blob: 424c7ed2f23800e48ae60e7625d9d7d5ee3f7979 [file] [log] [blame]
/*
* 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 "c.h"
#include "../ha_config.c"
#include "ha_config_mock.c"
#include "lib/stringinfo.h"
#include "utils/builtins.h"
/* Helper functions signature */
#define format_string_release(p) (pfree(p))
static char *format_string_create(char *f,...);
static void handle_pg_excp(char *msg, int errcode);
/*
* Unitest for GPHD_HA_load_nodes() in ../access/external/ha_config.c
* GPHD_HA_load_nodes() discovers the active Namnode from an HA Namenodes pair.
* It does this by interacting with the API exposed by hdfs.h, from which it uses
* 2 functions:
* a. Namenode * hdfsGetHANamenodes(const char * nameservice, int * size);
* b. void hdfsFreeNamenodeInformation(Namenode * namenodes, int size);
* This unitest verifies the correct interaction between GPHD_HA_load_nodes() implementation
* and the 2 hdfs.h APIs. It looks at the standard flows with expected input configuration
* and also at limit cases with corrupted input configuration.
* The mock functions for the two(2) hdfs.h APIs are in ha_config_mock.c.
*/
/*
* SUT function: NNHAConf* GPHD_HA_load_nodes(const char *nameservice);
* Mock function: Namenode * hdfsGetHANamenodes(const char * nameservice, int * size)
* Negative test: GPHD_HA_load_nodes() receives an unexistent namesrvice
*/
void
test__GPHD_HA_load_nodes__UnknownNameservice(void **state)
{
/*
* In case it receives an unknown nameservice string, the real function hdfsGetHANamenodes()
* will return NULL. We instruct our mock function to return NULL. In this way we simulate
* an unknown_service scenario and verify that our SUT function GPHD_HA_load_nodes() handles
* correctly the NULL returned by hdfsGetHANamenodes.
*/
will_return(hdfsGetHANamenodes, NULL);
PG_TRY();
{
NNHAConf *hac = GPHD_HA_load_nodes("UNKNOWN_SERVICE");
}
PG_CATCH();
{
char *msg = "nameservice UNKNOWN_SERVICE not found in client configuration. No HA namenodes provided";
handle_pg_excp(msg, ERRCODE_SYNTAX_ERROR);
return;
}
PG_END_TRY();
assert_true(false);
}
/*
* SUT function: NNHAConf* GPHD_HA_load_nodes(const char *nameservice);
* Mock function: Namenode * hdfsGetHANamenodes(const char * nameservice, int * size)
* Negative test: hdfsGetHANamenodes() returns just one namenode.
* This is not an HA sustainable.
*/
void
test__GPHD_HA_load_nodes__OneNN(void **state)
{
unsigned int numn = 1;
Namenode nns[1];
will_return(hdfsGetHANamenodes, nns);
will_assign_value(hdfsGetHANamenodes, size, numn);
PG_TRY();
{
NNHAConf *hac = GPHD_HA_load_nodes("NAMESERVICE");
}
PG_CATCH();
{
char *msg = format_string_create("High availability for nameservice %s was configured with only one node. A high availability scheme requires at least two nodes ",
"NAMESERVICE");
handle_pg_excp(msg, ERRCODE_INTERNAL_ERROR);
format_string_release(msg); /* if we trip on assert_string_equal we don't free but it doesen't matter because process stops*/
return;
}
PG_END_TRY();
assert_true(false);
}
/*
* SUT function: NNHAConf* GPHD_HA_load_nodes(const char *nameservice);
* Mock function: Namenode * hdfsGetHANamenodes(const char * nameservice, int * size)
* Negative test: hdfsGetHANamenodes() returns a Namenode.rpc_address field without ":"
* the host:port delimiter.
*/
void
test__GPHD_HA_load_nodes__RpcDelimMissing(void **state)
{
unsigned int numn = 2;
Namenode nns[] = { {"mdw2080", "mdw:50070"}, {"smdw:2080", "smdw:50070"}};
will_return(hdfsGetHANamenodes, nns);
will_assign_value(hdfsGetHANamenodes, size, numn);
PG_TRY();
{
NNHAConf *hac = GPHD_HA_load_nodes("NAMESERVICE");
}
PG_CATCH();
{
char *msg = "dfs.namenode.rpc-address was set incorrectly in the configuration. ':' missing";
handle_pg_excp(msg, ERRCODE_SYNTAX_ERROR);
return;
}
PG_END_TRY();
assert_true(false);
}
/*
* SUT function: NNHAConf* GPHD_HA_load_nodes(const char *nameservice)
* Mock functions: Namenode * hdfsGetHANamenodes(const char * nameservice, int * size)
* void port_to_str(char **port, int new_port)
* Positive test: port_to_str() assigns pxf_service_port correctly
*/
void
test__GPHD_HA_load_nodes__PxfServicePortIsAssigned(void **state)
{
unsigned int numn = 2;
Namenode nns[] = { {"mdw:2080", "mdw:50070"}, {"smdw:2080", "smdw:50070"}};
char strPort[30] = {0};
pg_ltoa(pxf_service_port, strPort);
will_return(hdfsGetHANamenodes, nns);
will_assign_value(hdfsGetHANamenodes, size, numn);
will_be_called(port_to_str);
expect_not_value(port_to_str, port, NULL);
expect_value(port_to_str, new_port, pxf_service_port);
will_assign_string(port_to_str, port, strPort);
will_be_called(port_to_str);
expect_not_value(port_to_str, port, NULL);
expect_value(port_to_str, new_port, pxf_service_port);
will_assign_string(port_to_str, port, strPort);
will_be_called(hdfsFreeNamenodeInformation);
NNHAConf *hac = GPHD_HA_load_nodes("NAMESERVICE");
}
/*
* SUT function: NNHAConf* GPHD_HA_load_nodes(const char *nameservice)
* Mock functions: Namenode * hdfsGetHANamenodes(const char * nameservice, int * size)
* void port_to_str(char **port, int new_port)
* Negative test: hdfsGetHANamenodes() returns a Namenode.http_address field without
* the host - ":port".
*/
void
test__GPHD_HA_load_nodes__HostMissing(void **state)
{
unsigned int numn = 2;
Namenode nns[] = { {":2080", "mdw:50070"}, {"smdw:2080", "smdw:50070"}};
char strPort[30] = {0};
pg_ltoa(pxf_service_port, strPort);
will_return(hdfsGetHANamenodes, nns);
will_assign_value(hdfsGetHANamenodes, size, numn);
will_be_called(port_to_str);
expect_not_value(port_to_str, port, NULL);
expect_value(port_to_str, new_port, pxf_service_port);
will_assign_string(port_to_str, port, strPort);
will_be_called(port_to_str);
expect_not_value(port_to_str, port, NULL);
expect_value(port_to_str, new_port, pxf_service_port);
will_assign_string(port_to_str, port, strPort);
will_be_called(hdfsFreeNamenodeInformation);
PG_TRY();
{
NNHAConf *hac = GPHD_HA_load_nodes("NAMESERVICE");
}
PG_CATCH();
{
char *msg = "HA Namenode host number 1 is NULL value";
handle_pg_excp(msg, ERRCODE_SYNTAX_ERROR);
return;
}
PG_END_TRY();
}
/*
* SUT function: NNHAConf* GPHD_HA_load_nodes(const char *nameservice)
* Mock functions: Namenode * hdfsGetHANamenodes(const char * nameservice, int * size)
* void port_to_str(char **port, int new_port)
* Negative test: port_to_str() does not set the port
* the port - "host:".
*/
void
test__GPHD_HA_load_nodes__PortMissing(void **state)
{
unsigned int numn = 2;
Namenode nns[] = { {"mdw:", "mdw:50070"}, {"smdw:2080", "smdw:50070"}};
will_return(hdfsGetHANamenodes, nns);
will_assign_value(hdfsGetHANamenodes, size, numn);
will_be_called(port_to_str);
expect_not_value(port_to_str, port, NULL);
expect_value(port_to_str, new_port, pxf_service_port);
will_be_called(port_to_str);
expect_not_value(port_to_str, port, NULL);
expect_value(port_to_str, new_port, pxf_service_port);
will_be_called(hdfsFreeNamenodeInformation);
PG_TRY();
{
NNHAConf *hac = GPHD_HA_load_nodes("NAMESERVICE");
}
PG_CATCH();
{
char *msg = "HA Namenode RPC port number 1 is NULL value";
handle_pg_excp(msg, ERRCODE_SYNTAX_ERROR);
return;
}
PG_END_TRY();
}
/*
* SUT function: NNHAConf* GPHD_HA_load_nodes(const char *nameservice)
* Mock functions: Namenode * hdfsGetHANamenodes(const char * nameservice, int * size)
* void port_to_str(char **port, int new_port)
* Negative test: port_to_str() returns a port outside the valid range
* - a number higher than 65535
*/
void
test__GPHD_HA_load_nodes__PortIsInvalidNumber(void **state)
{
unsigned int numn = 2;
Namenode nns[] = { {"mdw:2080", "mdw:65550"}, {"smdw:2080", "smdw:50070"}};
will_return(hdfsGetHANamenodes, nns);
will_assign_value(hdfsGetHANamenodes, size, numn);
will_be_called(port_to_str);
expect_not_value(port_to_str, port, NULL);
expect_value(port_to_str, new_port, pxf_service_port);
will_assign_string(port_to_str, port, "65550");
will_be_called(port_to_str);
expect_not_value(port_to_str, port, NULL);
expect_value(port_to_str, new_port, pxf_service_port);
will_assign_string(port_to_str, port, "65550");
will_be_called(hdfsFreeNamenodeInformation);
PG_TRY();
{
NNHAConf *hac = GPHD_HA_load_nodes("NAMESERVICE");
}
PG_CATCH();
{
char *msg = "Invalid port <65550> detected in nameservice configuration";
handle_pg_excp(msg, ERRCODE_SYNTAX_ERROR);
return;
}
PG_END_TRY();
}
/*
* SUT function: NNHAConf* GPHD_HA_load_nodes(const char *nameservice)
* Mock functions: Namenode * hdfsGetHANamenodes(const char * nameservice, int * size)
* void port_to_str(char **port, int new_port)
* Negative test: port_to_str() returns a port that is not a number
*/
void
test__GPHD_HA_load_nodes__PortIsNotNumber_TakeOne(void **state)
{
NNHAConf *hac;
unsigned int numn = 2;
Namenode nns[] = { {"mdw:2080", "mdw:50070"}, {"smdw:2080", "smdw:50070"}};
will_return(hdfsGetHANamenodes, nns);
will_assign_value(hdfsGetHANamenodes, size, numn);
will_be_called(port_to_str);
expect_not_value(port_to_str, port, NULL);
expect_value(port_to_str, new_port, pxf_service_port);
will_assign_string(port_to_str, port, "melon");
will_be_called(port_to_str);
expect_not_value(port_to_str, port, NULL);
expect_value(port_to_str, new_port, pxf_service_port);
will_be_called(hdfsFreeNamenodeInformation);
PG_TRY();
{
hac = GPHD_HA_load_nodes("NAMESERVICE");
}
PG_CATCH();
{
char *msg = "Invalid port <melon> detected in nameservice configuration";
handle_pg_excp(msg, ERRCODE_SYNTAX_ERROR);
return;
}
PG_END_TRY();
}
/*
* SUT function: NNHAConf* GPHD_HA_load_nodes(const char *nameservice)
* Mock functions: Namenode * hdfsGetHANamenodes(const char * nameservice, int * size)
* void port_to_str(char **port, int new_port)
* Negative test: port_to_str() returns a port that is not a number
*/
void
test__GPHD_HA_load_nodes__PortIsNotNumber_TakeTwo(void **state)
{
NNHAConf *hac;
unsigned int numn = 2;
Namenode nns[] = { {"mdw:2080", "mdw:50070"}, {"smdw:2080", "smdw:50070"}};
will_return(hdfsGetHANamenodes, nns);
will_assign_value(hdfsGetHANamenodes, size, numn);
will_be_called(port_to_str);
expect_not_value(port_to_str, port, NULL);
expect_value(port_to_str, new_port, pxf_service_port);
will_assign_string(port_to_str, port, "100ab");
will_be_called(port_to_str);
expect_not_value(port_to_str, port, NULL);
expect_value(port_to_str, new_port, pxf_service_port);
will_be_called(hdfsFreeNamenodeInformation);
PG_TRY();
{
hac = GPHD_HA_load_nodes("NAMESERVICE");
}
PG_CATCH();
{
char *msg = "Invalid port <100ab> detected in nameservice configuration";
handle_pg_excp(msg, ERRCODE_SYNTAX_ERROR);
return;
}
PG_END_TRY();
}
int
main(int argc, char *argv[])
{
cmockery_parse_arguments(argc, argv);
const UnitTest tests[] = {
unit_test(test__GPHD_HA_load_nodes__UnknownNameservice),
unit_test(test__GPHD_HA_load_nodes__OneNN),
unit_test(test__GPHD_HA_load_nodes__RpcDelimMissing),
unit_test(test__GPHD_HA_load_nodes__PxfServicePortIsAssigned),
unit_test(test__GPHD_HA_load_nodes__HostMissing),
unit_test(test__GPHD_HA_load_nodes__PortMissing),
unit_test(test__GPHD_HA_load_nodes__PortIsInvalidNumber),
unit_test(test__GPHD_HA_load_nodes__PortIsNotNumber_TakeOne),
unit_test(test__GPHD_HA_load_nodes__PortIsNotNumber_TakeTwo)
};
return run_tests(tests);
}
/*
* Helper function to format strings that need to be passed to assert macros
*/
static char*
format_string_create(char *f,...)
{
StringInfoData s;
va_list vl;
initStringInfo(&s);
va_start(vl,f);
appendStringInfoVA(&s, f, vl);
va_end(vl);
return s.data;
}
/*
* Encapsulates exception unpackaging
*/
static void
handle_pg_excp(char *msg, int errcode)
{
CurrentMemoryContext = 1;
ErrorData *edata = CopyErrorData();
assert_true(edata->sqlerrcode == errcode);
assert_true(edata->elevel == ERROR);
assert_string_equal(edata->message, msg);
/* Clean the internal error data stack. Otherwise errordata_stack_depth in elog.c,
* keeps growing from test to test with each ereport we issue in our SUT function
* until we reach errordata_stack_depth >= ERRORDATA_STACK_SIZE and our tests
* start failing
*/
elog_dismiss(INFO);
}