blob: f19e6aebde4d579d8f14de8a55ac419847f07b9d [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include "cmockery.h"
#include "postgres.h"
#include "utils/memutils.h"
/* Define UNIT_TESTING so that the extension can skip declaring PG_MODULE_MAGIC */
#define UNIT_TESTING
/* include unit under test */
#include "../src/pxfprotocol.c"
/* include mock files */
#include "mock/pxfbridge_mock.c"
#include "mock/pxfuriparser_mock.c"
#include "mock/pxffragment_mock.c"
#include "mock/pxffilters_mock.c"
char *uri_no_profile = "pxf://default/tmp/dummy1?FRAGMENTER=xxx&RESOLVER=yyy&ACCESSOR=zzz";
char *uri_param = "pxf://localhost:5888/tmp/dummy1";
static void
test_pxfprotocol_validate_urls(void **state)
{
/* setup call info with no call context */
PG_FUNCTION_ARGS = palloc0(sizeof(FunctionCallInfoData));
fcinfo->context = palloc0(sizeof(ExtProtocolValidatorData));
fcinfo->context->type = T_ExtProtocolValidatorData;
Value *v = makeString(uri_no_profile);
List *list = list_make1(v);
((ExtProtocolValidatorData *) fcinfo->context)->url_list = list;
/* set mock behavior for uri parsing */
GPHDUri *gphd_uri = palloc0(sizeof(GPHDUri));
expect_string(parseGPHDUri, uri_str, uri_no_profile);
will_return(parseGPHDUri, gphd_uri);
expect_value(GPHDUri_verify_no_duplicate_options, uri, gphd_uri);
will_be_called(GPHDUri_verify_no_duplicate_options);
expect_value(GPHDUri_opt_exists, uri, gphd_uri);
expect_string(GPHDUri_opt_exists, key, PXF_PROFILE);
will_return(GPHDUri_opt_exists, false);
expect_value(GPHDUri_verify_core_options_exist, uri, gphd_uri);
expect_any(GPHDUri_verify_core_options_exist, coreoptions);
will_be_called(GPHDUri_verify_core_options_exist);
expect_value(freeGPHDUri, uri, gphd_uri);
will_be_called(freeGPHDUri);
Datum d = pxfprotocol_validate_urls(fcinfo);
assert_int_equal(DatumGetInt32(d), 0);
/* cleanup */
list_free_deep(list);
pfree(fcinfo->context);
pfree(fcinfo);
}
static void
test_pxfprotocol_import_first_call(void **state)
{
/* setup call info with no call context */
PG_FUNCTION_ARGS = palloc0(sizeof(FunctionCallInfoData));
fcinfo->context = palloc0(sizeof(ExtProtocolData));
fcinfo->context->type = T_ExtProtocolData;
EXTPROTOCOL_GET_DATALEN(fcinfo) = 100;
EXTPROTOCOL_GET_DATABUF(fcinfo) = palloc0(EXTPROTOCOL_GET_DATALEN(fcinfo));
((ExtProtocolData *) fcinfo->context)->prot_last_call = false;
((ExtProtocolData *) fcinfo->context)->prot_url = uri_param;
ExternalSelectDesc desc = (ExternalSelectDesc) palloc0(sizeof(ExternalSelectDesc));
desc->projInfo = NULL;
((ExtProtocolData*) fcinfo->context)->desc = desc;
Relation relation = (Relation) palloc0(sizeof(Relation));
((ExtProtocolData *) fcinfo->context)->prot_relation = relation;
/* set mock behavior for uri parsing */
GPHDUri *gphd_uri = palloc0(sizeof(GPHDUri));
expect_string(parseGPHDUri, uri_str, uri_param);
will_return(parseGPHDUri, gphd_uri);
/* set mock behavior for set fragments */
gphd_uri->fragments = palloc0(sizeof(List));
expect_value(get_fragments, uri, gphd_uri);
expect_value(get_fragments, relation, relation);
will_assign_memory(get_fragments, uri, gphd_uri, sizeof(GPHDUri));
will_be_called(get_fragments);
/* set mock behavior for bridge import start -- nothing here */
expect_any(gpbridge_import_start, context);
will_be_called(gpbridge_import_start);
/* set mock behavior for bridge read */
const int EXPECTED_SIZE = 31;
/* expected number of bytes read from the bridge */
expect_any(gpbridge_read, context);
expect_value(gpbridge_read, databuf, EXTPROTOCOL_GET_DATABUF(fcinfo));
expect_value(gpbridge_read, datalen, EXTPROTOCOL_GET_DATALEN(fcinfo));
will_return(gpbridge_read, EXPECTED_SIZE);
Datum d = pxfprotocol_import(fcinfo);
/* return number of bytes read from the bridge */
assert_int_equal(DatumGetInt32(d), EXPECTED_SIZE);
gphadoop_context *context = (gphadoop_context *) EXTPROTOCOL_GET_USER_CTX(fcinfo);
/* context has been created */
assert_true(context != NULL);
/* gphduri has been parsed */
assert_true(context->gphd_uri != NULL);
/* uri has been initialized */
assert_true(context->uri.data != NULL);
/* no write file name for import case */
assert_true(context->relation != NULL);
/* relation pointer is copied */
assert_int_equal(context->relation, relation);
/* cleanup */
pfree(desc);
pfree(relation);
pfree(gphd_uri);
pfree(EXTPROTOCOL_GET_USER_CTX(fcinfo));
pfree(EXTPROTOCOL_GET_DATABUF(fcinfo));
pfree(fcinfo->context);
pfree(fcinfo);
}
static void
test_pxfprotocol_import_second_call(void **state)
{
/* setup call info with call context */
PG_FUNCTION_ARGS = palloc0(sizeof(FunctionCallInfoData));
fcinfo->context = palloc0(sizeof(ExtProtocolData));
fcinfo->context->type = T_ExtProtocolData;
EXTPROTOCOL_GET_DATALEN(fcinfo) = 100;
EXTPROTOCOL_GET_DATABUF(fcinfo) = palloc0(EXTPROTOCOL_GET_DATALEN(fcinfo));
((ExtProtocolData *) fcinfo->context)->prot_last_call = false;
gphadoop_context *call_context = palloc0(sizeof(gphadoop_context));
EXTPROTOCOL_SET_USER_CTX(fcinfo, call_context);
/* set mock behavior for bridge read */
const int EXPECTED_SIZE = 0;
/* expected number of bytes read from the bridge */
expect_value(gpbridge_read, context, call_context);
expect_value(gpbridge_read, databuf, EXTPROTOCOL_GET_DATABUF(fcinfo));
expect_value(gpbridge_read, datalen, EXTPROTOCOL_GET_DATALEN(fcinfo));
will_return(gpbridge_read, EXPECTED_SIZE);
Datum d = pxfprotocol_import(fcinfo);
assert_int_equal(DatumGetInt32(d), EXPECTED_SIZE);
/* return number of bytes read from the bridge */
assert_true(EXTPROTOCOL_GET_USER_CTX(fcinfo) == call_context);
/* context is still the same */
/* cleanup */
pfree(call_context);
pfree(EXTPROTOCOL_GET_DATABUF(fcinfo));
pfree(fcinfo->context);
pfree(fcinfo);
}
static void
test_pxfprotocol_import_last_call(void **state)
{
/* setup call info with a call context and last call indicator */
PG_FUNCTION_ARGS = palloc0(sizeof(FunctionCallInfoData));
fcinfo->context = palloc0(sizeof(ExtProtocolData));
fcinfo->context->type = T_ExtProtocolData;
gphadoop_context *call_context = palloc0(sizeof(gphadoop_context));
EXTPROTOCOL_SET_USER_CTX(fcinfo, call_context);
EXTPROTOCOL_SET_LAST_CALL(fcinfo);
/* init data in context that will be cleaned up */
initStringInfo(&call_context->uri);
/* set mock behavior for bridge cleanup */
expect_value(gpbridge_cleanup, context, call_context);
will_be_called(gpbridge_cleanup);
Datum d = pxfprotocol_import(fcinfo);
assert_int_equal(DatumGetInt32(d), 0);
/* 0 is returned from function */
assert_true(EXTPROTOCOL_GET_USER_CTX(fcinfo) == NULL);
/* call context is cleaned up */
/* cleanup */
pfree(fcinfo->context);
pfree(fcinfo);
}
static void
test_pxfprotocol_export_first_call(void **state)
{
/* setup call info with no call context */
PG_FUNCTION_ARGS = palloc0(sizeof(FunctionCallInfoData));
fcinfo->context = palloc0(sizeof(ExtProtocolData));
fcinfo->context->type = T_ExtProtocolData;
EXTPROTOCOL_GET_DATALEN(fcinfo) = 100;
EXTPROTOCOL_GET_DATABUF(fcinfo) = palloc0(EXTPROTOCOL_GET_DATALEN(fcinfo));
((ExtProtocolData *) fcinfo->context)->prot_last_call = false;
((ExtProtocolData *) fcinfo->context)->prot_url = uri_param;
ExternalSelectDesc desc = (ExternalSelectDesc) palloc0(sizeof(ExternalSelectDesc));
desc->projInfo = NULL;
((ExtProtocolData*) fcinfo->context)->desc = desc;
Relation relation = (Relation) palloc0(sizeof(Relation));
((ExtProtocolData *) fcinfo->context)->prot_relation = relation;
/* set mock behavior for uri parsing */
GPHDUri *gphd_uri = palloc0(sizeof(GPHDUri));
expect_string(parseGPHDUri, uri_str, uri_param);
will_return(parseGPHDUri, gphd_uri);
/* set mock behavior for bridge export start -- nothing here */
expect_any(gpbridge_export_start, context);
will_be_called(gpbridge_export_start);
/* set mock behavior for bridge write */
const int EXPECTED_SIZE = 31;
/* expected number of bytes written to the bridge */
expect_any(gpbridge_write, context);
expect_value(gpbridge_write, databuf, EXTPROTOCOL_GET_DATABUF(fcinfo));
expect_value(gpbridge_write, datalen, EXTPROTOCOL_GET_DATALEN(fcinfo));
will_return(gpbridge_write, EXPECTED_SIZE);
Datum d = pxfprotocol_export(fcinfo);
/* return number of bytes written to the bridge */
assert_int_equal(DatumGetInt32(d), EXPECTED_SIZE);
gphadoop_context *context = (gphadoop_context *) EXTPROTOCOL_GET_USER_CTX(fcinfo);
/* context has been created */
assert_true(context != NULL);
/* gphduri has been parsed */
assert_true(context->gphd_uri != NULL);
/* uri has been initialized */
assert_true(context->uri.data != NULL);
/* write file name initialized, but empty, since it is filled by another component */
assert_true(context->relation != NULL);
/* relation pointer is copied */
assert_int_equal(context->relation, relation);
/* cleanup */
pfree(desc);
pfree(relation);
pfree(gphd_uri);
pfree(EXTPROTOCOL_GET_USER_CTX(fcinfo));
pfree(EXTPROTOCOL_GET_DATABUF(fcinfo));
pfree(fcinfo->context);
pfree(fcinfo);
}
static void
test_pxfprotocol_export_second_call(void **state)
{
/* setup call info with call context */
PG_FUNCTION_ARGS = palloc0(sizeof(FunctionCallInfoData));
fcinfo->context = palloc0(sizeof(ExtProtocolData));
fcinfo->context->type = T_ExtProtocolData;
EXTPROTOCOL_GET_DATALEN(fcinfo) = 100;
EXTPROTOCOL_GET_DATABUF(fcinfo) = palloc0(EXTPROTOCOL_GET_DATALEN(fcinfo));
((ExtProtocolData *) fcinfo->context)->prot_last_call = false;
gphadoop_context *call_context = palloc0(sizeof(gphadoop_context));
EXTPROTOCOL_SET_USER_CTX(fcinfo, call_context);
/* set mock behavior for bridge write */
const int EXPECTED_SIZE = 0;
/* expected number of bytes written to the bridge */
expect_value(gpbridge_write, context, call_context);
expect_value(gpbridge_write, databuf, EXTPROTOCOL_GET_DATABUF(fcinfo));
expect_value(gpbridge_write, datalen, EXTPROTOCOL_GET_DATALEN(fcinfo));
will_return(gpbridge_write, EXPECTED_SIZE);
Datum d = pxfprotocol_export(fcinfo);
assert_int_equal(DatumGetInt32(d), EXPECTED_SIZE);
/* return number of bytes written to the bridge */
assert_true(EXTPROTOCOL_GET_USER_CTX(fcinfo) == call_context);
/* context is still the same */
/* cleanup */
pfree(call_context);
pfree(EXTPROTOCOL_GET_DATABUF(fcinfo));
pfree(fcinfo->context);
pfree(fcinfo);
}
static void
test_pxfprotocol_export_last_call(void **state)
{
/* setup call info with a call context and last call indicator */
PG_FUNCTION_ARGS = palloc0(sizeof(FunctionCallInfoData));
fcinfo->context = palloc0(sizeof(ExtProtocolData));
fcinfo->context->type = T_ExtProtocolData;
gphadoop_context *call_context = palloc0(sizeof(gphadoop_context));
EXTPROTOCOL_SET_USER_CTX(fcinfo, call_context);
EXTPROTOCOL_SET_LAST_CALL(fcinfo);
/* init data in context that will be cleaned up */
initStringInfo(&call_context->uri);
/* set mock behavior for bridge cleanup */
expect_value(gpbridge_cleanup, context, call_context);
will_be_called(gpbridge_cleanup);
Datum d = pxfprotocol_export(fcinfo);
assert_int_equal(DatumGetInt32(d), 0);
/* 0 is returned from function */
assert_true(EXTPROTOCOL_GET_USER_CTX(fcinfo) == NULL);
/* call context is cleaned up */
/* cleanup */
pfree(fcinfo->context);
pfree(fcinfo);
}
/* test setup and teardown methods */
static void
before_test(void **state)
{
/* set global variables */
GpIdentity.segindex = 0;
}
static void
after_test(void **state)
{
/*
* no-op, but the teardown seems to be required when the test fails,
* otherwise CMockery issues a mismatch error
*/
}
int
main(int argc, char *argv[])
{
cmockery_parse_arguments(argc, argv);
const UnitTest tests[] = {
unit_test(test_pxfprotocol_validate_urls),
unit_test_setup_teardown(test_pxfprotocol_import_first_call, before_test, after_test),
unit_test_setup_teardown(test_pxfprotocol_import_second_call, before_test, after_test),
unit_test_setup_teardown(test_pxfprotocol_import_last_call, before_test, after_test),
unit_test_setup_teardown(test_pxfprotocol_export_first_call, before_test, after_test),
unit_test_setup_teardown(test_pxfprotocol_export_second_call, before_test, after_test),
unit_test_setup_teardown(test_pxfprotocol_export_last_call, before_test, after_test)
};
MemoryContextInit();
return run_tests(tests);
}