/*********************************************************************************************************************************** Test Run ***********************************************************************************************************************************/ void testRun(void) { FUNCTION_HARNESS_VOID(); // ***************************************************************************************************************************** if (testBegin("DEBUG")) { #ifdef DEBUG bool debug = true; #else bool debug = false; #endif TEST_RESULT_BOOL(debug, false, "DEBUG is not defined"); } // ***************************************************************************************************************************** if (testBegin("DEBUG_UNIT_EXTERN")) { const char *debugUnitExtern = MACRO_TO_STR(DEBUG_UNIT_EXTERN); TEST_RESULT_STR(debugUnitExtern, "static", "DEBUG_UNIT_EXTERN is static"); } FUNCTION_HARNESS_RESULT_VOID(); }
/*********************************************************************************************************************************** Get time at beginning of current run ***********************************************************************************************************************************/ uint64_t testTimeMSecBegin(void) { FUNCTION_HARNESS_VOID(); FUNCTION_HARNESS_RESULT(UINT64, timeMSecBegin); }
/*********************************************************************************************************************************** Get the time in milliseconds ***********************************************************************************************************************************/ uint64_t testTimeMSec(void) { FUNCTION_HARNESS_VOID(); struct timeval currentTime; gettimeofday(¤tTime, NULL); FUNCTION_HARNESS_RESULT(UINT64, ((uint64_t)currentTime.tv_sec * 1000) + (uint64_t)currentTime.tv_usec / 1000); }
/*********************************************************************************************************************************** Test Run ***********************************************************************************************************************************/ void testRun(void) { FUNCTION_HARNESS_VOID(); // Create default storage object for testing Storage *storageTest = storagePosixNew( strNew(testPath()), STORAGE_MODE_FILE_DEFAULT, STORAGE_MODE_PATH_DEFAULT, true, NULL); // ***************************************************************************************************************************** if (testBegin("lockStopFileName()")) { // Load configuration so lock path is set StringList *argList = strLstNew(); strLstAddZ(argList, "pgbackrest"); strLstAddZ(argList, "--stanza=db"); strLstAddZ(argList, "--lock-path=/path/to/lock"); strLstAddZ(argList, "archive-get"); harnessCfgLoad(strLstSize(argList), strLstPtr(argList)); TEST_RESULT_STR(strPtr(lockStopFileName(NULL)), "/path/to/lock/all.stop", "stop file for all stanzas"); TEST_RESULT_STR(strPtr(lockStopFileName(strNew("db"))), "/path/to/lock/db.stop", "stop file for on stanza"); } // ***************************************************************************************************************************** if (testBegin("lockStopTest()")) { StringList *argList = strLstNew(); strLstAddZ(argList, "pgbackrest"); strLstAdd(argList, strNewFmt("--lock-path=%s", testPath())); strLstAddZ(argList, "start"); harnessCfgLoad(strLstSize(argList), strLstPtr(argList)); TEST_RESULT_VOID(lockStopTest(), "no stop files without stanza"); // ------------------------------------------------------------------------------------------------------------------------- argList = strLstNew(); strLstAddZ(argList, "pgbackrest"); strLstAddZ(argList, "--stanza=db"); strLstAdd(argList, strNewFmt("--lock-path=%s", testPath())); strLstAddZ(argList, "start"); harnessCfgLoad(strLstSize(argList), strLstPtr(argList)); TEST_RESULT_VOID(lockStopTest(), "no stop files with stanza"); storagePutNP(storageNewWriteNP(storageTest, strNew("all.stop")), NULL); TEST_ERROR(lockStopTest(), StopError, "stop file exists for all stanzas"); storagePutNP(storageNewWriteNP(storageTest, strNew("db.stop")), NULL); TEST_ERROR(lockStopTest(), StopError, "stop file exists for stanza db"); } FUNCTION_HARNESS_RESULT_VOID(); }
/*********************************************************************************************************************************** Make sure nothing is left in the log after all tests have completed ***********************************************************************************************************************************/ void harnessLogFinal(void) { FUNCTION_HARNESS_VOID(); harnessLogLoad(logFile); if (strcmp(harnessLogBuffer, "") != 0) THROW_FMT(AssertError, "\n\nexpected log to be empty but actual log was:\n\n%s\n\n", harnessLogBuffer); FUNCTION_HARNESS_RESULT_VOID(); }
/*********************************************************************************************************************************** Test Run ***********************************************************************************************************************************/ void testRun(void) { FUNCTION_HARNESS_VOID(); // ***************************************************************************************************************************** if (testBegin("perlMain()")) { // ------------------------------------------------------------------------------------------------------------------------- cfgInit(); cfgCommandSet(cfgCmdInfo); cfgExeSet(strNew("/path/to/pgbackrest")); TEST_RESULT_STR( strPtr(perlMain()), "($iResult, $bErrorC, $strMessage) = pgBackRest::Main::main('info')", "command with no options"); // ------------------------------------------------------------------------------------------------------------------------- cfgOptionValidSet(cfgOptCompress, true); cfgOptionSet(cfgOptCompress, cfgSourceParam, varNewBool(true)); StringList *commandParamList = strLstNew(); strLstAdd(commandParamList, strNew("A")); strLstAdd(commandParamList, strNew("B")); cfgCommandParamSet(commandParamList); TEST_RESULT_STR( strPtr(perlMain()), "($iResult, $bErrorC, $strMessage) = pgBackRest::Main::main('info','A','B')", "command with one option and params"); } // ***************************************************************************************************************************** if (testBegin("perlInit(), perlExec(), and perlFree()")) { StringList *argList = strLstNew(); strLstAdd(argList, strNew("pgbackrest")); strLstAdd(argList, strNew("--stanza=db")); strLstAdd(argList, strNew("--log-level-console=off")); strLstAdd(argList, strNew("--log-level-stderr=off")); strLstAdd(argList, strNew("--log-level-file=off")); strLstAdd(argList, strNew("archive-push")); TEST_RESULT_VOID(cfgLoad(strLstSize(argList), strLstPtr(argList)), "load archive-push config"); TEST_RESULT_VOID(perlFree(0), "free Perl before it is init'd"); TEST_RESULT_VOID(perlInit(), "init Perl"); TEST_ERROR(perlExec(), PathMissingError, PERL_EMBED_ERROR); TEST_RESULT_VOID(perlFree(0), "free Perl"); } FUNCTION_HARNESS_RESULT_VOID(); }
/*********************************************************************************************************************************** Initialize log for testing ***********************************************************************************************************************************/ void harnessLogInit(void) { FUNCTION_HARNESS_VOID(); logInit(logLevelTestDefault, logLevelOff, logLevelInfo, false, 99); logFileBanner = true; snprintf(logFile, sizeof(logFile), "%s/expect.log", testExpectPath()); logHandleFile = harnessLogOpen(logFile, O_WRONLY | O_CREAT | O_TRUNC, 0640); logAnySet(); FUNCTION_HARNESS_RESULT_VOID(); }
/*********************************************************************************************************************************** testComplete - make sure all expected tests ran ***********************************************************************************************************************************/ void testComplete(void) { FUNCTION_HARNESS_VOID(); #ifndef NO_LOG // Make sure there is nothing untested left in the log harnessLogFinal(); #endif // Check that all tests ran if (testRun != testTotal) { fprintf(stderr, "ERROR: expected %d tests but %d were run\n", testTotal, testRun); fflush(stderr); exit(255); } FUNCTION_HARNESS_RESULT_VOID(); }
/*********************************************************************************************************************************** Test Run ***********************************************************************************************************************************/ void testRun(void) { FUNCTION_HARNESS_VOID(); // Additional coverage not provided by testing with actual certificates // ***************************************************************************************************************************** if (testBegin("asn1ToStr(), tlsClientHostVerify(), and tlsClientHostVerifyName()")) { TEST_ERROR(asn1ToStr(NULL), CryptoError, "TLS certificate name entry is missing"); TEST_ERROR( tlsClientHostVerifyName( strNew("host"), strNewN("ab\0cd", 5)), CryptoError, "TLS certificate name contains embedded null"); TEST_ERROR(tlsClientHostVerify(strNew("host"), NULL), CryptoError, "No certificate presented by the TLS server"); TEST_RESULT_BOOL(tlsClientHostVerifyName(strNew("host"), strNew("**")), false, "invalid pattern"); TEST_RESULT_BOOL(tlsClientHostVerifyName(strNew("host"), strNew("*.")), false, "invalid pattern"); TEST_RESULT_BOOL(tlsClientHostVerifyName(strNew("a.bogus.host.com"), strNew("*.host.com")), false, "invalid host"); } // ***************************************************************************************************************************** if (testBegin("TlsClient verification")) { TlsClient *client = NULL; // Connection errors // ------------------------------------------------------------------------------------------------------------------------- TEST_ASSIGN(client, tlsClientNew(strNew("99.99.99.99.99"), 9443, 0, true, NULL, NULL), "new client"); TEST_ERROR( tlsClientOpen(client), HostConnectError, "unable to get address for '99.99.99.99.99': [-2] Name or service not known"); TEST_ASSIGN(client, tlsClientNew(strNew("localhost"), 9443, 100, true, NULL, NULL), "new client"); TEST_ERROR(tlsClientOpen(client), HostConnectError, "unable to connect to 'localhost:9443': [111] Connection refused"); // Certificate location and validation errors // ------------------------------------------------------------------------------------------------------------------------- // Add test hosts if (system( // {uncoverable_branch} "echo \"127.0.0.1 test.pgbackrest.org host.test2.pgbackrest.org test3.pgbackrest.org\" |" " sudo tee -a /etc/hosts > /dev/null") != 0) { THROW(AssertError, "unable to add test hosts to /etc/hosts"); // {uncovered+} } // Start server to test various certificate errors testTlsServerAltName(); TEST_ERROR( tlsClientOpen(tlsClientNew(strNew("localhost"), 9443, 500, true, strNew("bogus.crt"), strNew("/bogus"))), CryptoError, "unable to set user-defined CA certificate location: [33558530] No such file or directory"); TEST_ERROR( tlsClientOpen(tlsClientNew(strNew("localhost"), 9443, 500, true, NULL, strNew("/bogus"))), CryptoError, "unable to verify certificate presented by 'localhost:9443': [20] unable to get local issuer certificate"); TEST_RESULT_VOID( tlsClientOpen( tlsClientNew(strNew("test.pgbackrest.org"), 9443, 500, true, strNewFmt("%s/" TEST_CERTIFICATE_PREFIX "-ca.crt", testRepoPath()), NULL)), "success on valid ca file and match common name"); TEST_RESULT_VOID( tlsClientOpen( tlsClientNew(strNew("host.test2.pgbackrest.org"), 9443, 500, true, strNewFmt("%s/" TEST_CERTIFICATE_PREFIX "-ca.crt", testRepoPath()), NULL)), "success on valid ca file and match alt name"); TEST_ERROR( tlsClientOpen( tlsClientNew(strNew("test3.pgbackrest.org"), 9443, 500, true, strNewFmt("%s/" TEST_CERTIFICATE_PREFIX "-ca.crt", testRepoPath()), NULL)), CryptoError, "unable to find hostname 'test3.pgbackrest.org' in certificate common name or subject alternative names"); TEST_ERROR( tlsClientOpen( tlsClientNew(strNew("localhost"), 9443, 500, true, strNewFmt("%s/" TEST_CERTIFICATE_PREFIX ".crt", testRepoPath()), NULL)), CryptoError, "unable to verify certificate presented by 'localhost:9443': [20] unable to get local issuer certificate"); TEST_RESULT_VOID( tlsClientOpen(tlsClientNew(strNew("localhost"), 9443, 500, false, NULL, NULL)), "success on no verify"); } // ***************************************************************************************************************************** if (testBegin("TlsClient general usage")) { TlsClient *client = NULL; // Reset statistics tlsClientStatLocal = (TlsClientStat){0}; TEST_RESULT_STR(tlsClientStatStr(), NULL, "no stats yet"); testTlsServer(); ioBufferSizeSet(12); TEST_ASSIGN(client, tlsClientNew(strNew(TLS_TEST_HOST), 9443, 500, true, NULL, NULL), "new client"); TEST_RESULT_VOID(tlsClientOpen(client), "open client"); const Buffer *input = BUFSTRDEF("some protocol info"); TEST_RESULT_VOID(ioWrite(tlsClientIoWrite(client), input), "write input"); ioWriteFlush(tlsClientIoWrite(client)); TEST_RESULT_STR(strPtr(ioReadLine(tlsClientIoRead(client))), "something:0", "read line"); TEST_RESULT_BOOL(ioReadEof(tlsClientIoRead(client)), false, " check eof = false"); Buffer *output = bufNew(12); TEST_RESULT_INT(ioRead(tlsClientIoRead(client), output), 12, "read output"); TEST_RESULT_STR(strPtr(strNewBuf(output)), "some content", " check output"); TEST_RESULT_BOOL(ioReadEof(tlsClientIoRead(client)), false, " check eof = false"); output = bufNew(8); TEST_RESULT_INT(ioRead(tlsClientIoRead(client), output), 8, "read output"); TEST_RESULT_STR(strPtr(strNewBuf(output)), "AND MORE", " check output"); TEST_RESULT_BOOL(ioReadEof(tlsClientIoRead(client)), false, " check eof = false"); output = bufNew(12); TEST_ERROR( ioRead(tlsClientIoRead(client), output), FileReadError, "unable to read data from 'tls.test.pgbackrest.org:9443' after 500ms"); // ------------------------------------------------------------------------------------------------------------------------- input = BUFSTRDEF("more protocol info"); TEST_RESULT_VOID(tlsClientOpen(client), "open client again (it is already open)"); TEST_RESULT_VOID(ioWrite(tlsClientIoWrite(client), input), "write input"); ioWriteFlush(tlsClientIoWrite(client)); output = bufNew(12); TEST_RESULT_INT(ioRead(tlsClientIoRead(client), output), 12, "read output"); TEST_RESULT_STR(strPtr(strNewBuf(output)), "0123456789AB", " check output"); TEST_RESULT_BOOL(ioReadEof(tlsClientIoRead(client)), false, " check eof = false"); output = bufNew(12); TEST_RESULT_INT(ioRead(tlsClientIoRead(client), output), 0, "read no output after eof"); TEST_RESULT_BOOL(ioReadEof(tlsClientIoRead(client)), true, " check eof = true"); TEST_RESULT_BOOL(tlsClientStatStr() != NULL, true, "check statistics exist"); TEST_RESULT_VOID(tlsClientFree(client), "free client"); } FUNCTION_HARNESS_RESULT_VOID(); }
/*********************************************************************************************************************************** Test run ***********************************************************************************************************************************/ void testRun(void) { FUNCTION_HARNESS_VOID(); // Static tests against known values -- these may break as options change so will need to be kept up to date. The tests have // generally been selected to favor values that are not expected to change but adjustments are welcome as long as the type of // test is not drastically changed. // ***************************************************************************************************************************** if (testBegin("check known values")) { TEST_ERROR(cfgCommandId(BOGUS_STR), AssertError, "invalid command 'BOGUS'"); TEST_RESULT_INT(cfgCommandId("archive-push"), cfgCmdArchivePush, "command id from name"); TEST_ERROR( cfgCommandDefIdFromId(CFG_COMMAND_TOTAL), AssertError, "assertion 'commandId < cfgCmdNone' failed"); TEST_RESULT_INT(cfgCommandDefIdFromId(cfgCmdBackup), cfgDefCmdBackup, "command id to def id"); TEST_RESULT_STR(cfgCommandName(cfgCmdBackup), "backup", "command name from id"); TEST_RESULT_INT(cfgOptionDefIdFromId(cfgOptPgHost + 6), cfgDefOptPgHost, "option id to def id"); TEST_RESULT_INT(cfgOptionId("target"), cfgOptTarget, "option id from name"); TEST_RESULT_INT(cfgOptionId(BOGUS_STR), -1, "option id from invalid option name"); TEST_ERROR( cfgOptionIdFromDefId(cfgDefOptionTotal(), 6), AssertError, "assertion 'optionDefId < cfgDefOptionTotal()' failed"); TEST_ERROR( cfgOptionIdFromDefId(0, 999999), AssertError, "assertion 'index < cfgDefOptionIndexTotal(optionDefId)' failed"); TEST_RESULT_INT(cfgOptionIdFromDefId(cfgDefOptPgHost, 6), cfgOptPgHost + 6, "option def id to id"); TEST_ERROR(cfgOptionIndex(CFG_OPTION_TOTAL), AssertError, "assertion 'optionId < CFG_OPTION_TOTAL' failed"); TEST_RESULT_INT(cfgOptionIndex(cfgOptPgHostCmd + 6), 6, "option index"); TEST_RESULT_INT(cfgOptionIndex(cfgOptCompressLevel), 0, "option index"); TEST_RESULT_INT(cfgOptionIndexTotal(cfgOptPgPath), 8, "option index total"); TEST_RESULT_INT(cfgOptionIndexTotal(cfgOptLogLevelConsole), 1, "option index total"); TEST_RESULT_STR(cfgOptionName(cfgOptBackupStandby), "backup-standby", "option id from name"); } // ***************************************************************************************************************************** if (testBegin("configuration")) { TEST_RESULT_VOID(cfgInit(), "config init"); // ------------------------------------------------------------------------------------------------------------------------- TEST_RESULT_INT(cfgCommand(), cfgCmdNone, "command begins as none"); TEST_RESULT_VOID(cfgCommandSet(cfgCmdBackup), "command set to backup"); TEST_RESULT_INT(cfgCommand(), cfgCmdBackup, "command is backup"); // ------------------------------------------------------------------------------------------------------------------------- TEST_RESULT_VOID(cfgCommandSet(cfgCmdBackup), "command set to backup"); TEST_RESULT_INT(cfgLogLevelDefault(), logLevelInfo, "default log level is info"); TEST_RESULT_BOOL(cfgLogFile(), true, "log file is on"); TEST_RESULT_BOOL(cfgLockRequired(), true, "lock is required"); TEST_RESULT_BOOL(cfgLockRemoteRequired(cfgCmdBackup), true, "remote lock is required"); TEST_RESULT_INT(cfgLockType(), lockTypeBackup, "lock is type backup"); TEST_RESULT_INT(cfgLockRemoteType(cfgCmdBackup), lockTypeBackup, "remote lock is type backup"); TEST_RESULT_BOOL(cfgParameterAllowed(), false, "parameters not allowed"); TEST_RESULT_VOID(cfgCommandSet(cfgCmdInfo), "command set to info"); TEST_RESULT_INT(cfgLogLevelDefault(), logLevelDebug, "default log level is debug"); TEST_RESULT_INT(cfgLogLevelStdErrMax(), logLevelTrace, "max stderr log level is trace"); TEST_RESULT_BOOL(cfgLogFile(), false, "log file is off"); TEST_RESULT_BOOL(cfgLockRequired(), false, "lock is not required"); TEST_RESULT_BOOL(cfgLockRemoteRequired(cfgCmdInfo), false, "remote lock is not required"); TEST_RESULT_INT(cfgLockType(), lockTypeNone, "lock is type none"); TEST_RESULT_INT(cfgLockRemoteType(cfgCmdInfo), lockTypeNone, "remote lock is type none"); TEST_RESULT_VOID(cfgCommandSet(cfgCmdStanzaCreate), "command set to stanza-create"); TEST_RESULT_BOOL(cfgLockRequired(), true, "lock is required"); TEST_RESULT_INT(cfgLockType(), lockTypeAll, "lock is type all"); TEST_RESULT_VOID(cfgCommandSet(cfgCmdLocal), "command set to local"); TEST_RESULT_INT(cfgLogLevelStdErrMax(), logLevelError, "max stderr log level is error"); TEST_RESULT_BOOL(cfgLogFile(), true, "log file is on"); // ------------------------------------------------------------------------------------------------------------------------- TEST_RESULT_BOOL(cfgCommandHelp(), false, "command help defaults to false"); TEST_RESULT_VOID(cfgCommandHelpSet(true), "set command help"); TEST_RESULT_BOOL(cfgCommandHelp(), true, "command help is set"); // ------------------------------------------------------------------------------------------------------------------------- TEST_RESULT_INT(strLstSize(cfgCommandParam()), 0, "command param list defaults to empty"); TEST_RESULT_VOID(cfgCommandParamSet(strLstAddZ(strLstNew(), "param")), "set command param list"); TEST_RESULT_INT(strLstSize(cfgCommandParam()), 1, "command param list is set"); // ------------------------------------------------------------------------------------------------------------------------- TEST_RESULT_PTR(cfgExe(), NULL, "exe defaults to null"); TEST_RESULT_VOID(cfgExeSet(strNew("/path/to/exe")), "set exe"); TEST_RESULT_STR(strPtr(cfgExe()), "/path/to/exe", "exe is set"); // ------------------------------------------------------------------------------------------------------------------------- TEST_RESULT_BOOL(cfgOptionNegate(cfgOptConfig), false, "negate defaults to false"); TEST_RESULT_VOID(cfgOptionNegateSet(cfgOptConfig, true), "set negate"); TEST_RESULT_BOOL(cfgOptionNegate(cfgOptConfig), true, "negate is set"); // ------------------------------------------------------------------------------------------------------------------------- TEST_RESULT_BOOL(cfgOptionReset(cfgOptConfig), false, "reset defaults to false"); TEST_RESULT_VOID(cfgOptionResetSet(cfgOptConfig, true), "set reset"); TEST_RESULT_BOOL(cfgOptionReset(cfgOptConfig), true, "reset is set"); // ------------------------------------------------------------------------------------------------------------------------- TEST_RESULT_BOOL(cfgOptionValid(cfgOptConfig), false, "valid defaults to false"); TEST_RESULT_BOOL(cfgOptionTest(cfgOptConfig), false, "option not valid for the command"); TEST_RESULT_VOID(cfgOptionValidSet(cfgOptConfig, true), "set valid"); TEST_RESULT_BOOL(cfgOptionValid(cfgOptConfig), true, "valid is set"); TEST_RESULT_BOOL(cfgOptionTest(cfgOptConfig), false, "option valid but value is null"); TEST_RESULT_VOID(cfgOptionSet(cfgOptConfig, cfgSourceParam, varNewStrZ("cfg")), "set option config"); TEST_RESULT_BOOL(cfgOptionTest(cfgOptConfig), true, "option valid and value not null"); // ------------------------------------------------------------------------------------------------------------------------- TEST_RESULT_PTR(cfgOption(cfgOptOnline), NULL, "online is null"); TEST_RESULT_VOID(cfgOptionSet(cfgOptOnline, cfgSourceParam, varNewBool(false)), "set online"); TEST_RESULT_BOOL(cfgOptionBool(cfgOptOnline), false, "online is set"); TEST_RESULT_VOID(cfgOptionSet(cfgOptOnline, cfgSourceParam, varNewStrZ("1")), "set online"); TEST_RESULT_BOOL(cfgOptionBool(cfgOptOnline), true, "online is set"); TEST_RESULT_INT(cfgOptionSource(cfgOptOnline), cfgSourceParam, "online source is set"); TEST_ERROR( cfgOptionDbl(cfgOptOnline), AssertError, "assertion 'varType(configOptionValue[optionId].value) == varTypeDouble' failed"); TEST_ERROR( cfgOptionInt64(cfgOptOnline), AssertError, "assertion 'varType(configOptionValue[optionId].value) == varTypeInt64' failed"); TEST_RESULT_VOID(cfgOptionSet(cfgOptCompressLevel, cfgSourceParam, varNewInt64(1)), "set compress-level"); TEST_RESULT_INT(cfgOptionInt(cfgOptCompressLevel), 1, "compress-level is set"); TEST_RESULT_VOID(cfgOptionSet(cfgOptCompressLevel, cfgSourceDefault, varNewStrZ("3")), "set compress-level"); TEST_RESULT_INT(cfgOptionUInt(cfgOptCompressLevel), 3, "compress-level is set"); TEST_RESULT_INT(cfgOptionSource(cfgOptCompressLevel), cfgSourceDefault, "compress source is set"); TEST_ERROR( cfgOptionBool(cfgOptCompressLevel), AssertError, "assertion 'varType(configOptionValue[optionId].value) == varTypeBool' failed"); TEST_RESULT_VOID( cfgOptionSet(cfgOptArchivePushQueueMax, cfgSourceParam, varNewInt64(999999999999)), "set archive-push-queue-max"); TEST_RESULT_INT(cfgOptionInt64(cfgOptArchivePushQueueMax), 999999999999, "archive-push-queue-max is set"); TEST_RESULT_INT(cfgOptionUInt64(cfgOptArchivePushQueueMax), 999999999999, "archive-push-queue-max is set"); TEST_RESULT_VOID(cfgOptionSet(cfgOptProtocolTimeout, cfgSourceParam, varNewDbl(1.1)), "set protocol-timeout"); TEST_RESULT_DOUBLE(cfgOptionDbl(cfgOptProtocolTimeout), 1.1, "protocol-timeout is set"); TEST_RESULT_VOID(cfgOptionSet(cfgOptProtocolTimeout, cfgSourceConfig, varNewStrZ("3.3")), "set protocol-timeout"); TEST_RESULT_DOUBLE(cfgOptionDbl(cfgOptProtocolTimeout), 3.3, "protocol-timeout is set"); TEST_RESULT_INT(cfgOptionSource(cfgOptProtocolTimeout), cfgSourceConfig, "protocol-timeout source is set"); TEST_ERROR( cfgOptionKv(cfgOptProtocolTimeout), AssertError, "assertion 'varType(configOptionValue[optionId].value) == varTypeKeyValue' failed"); TEST_RESULT_VOID(cfgOptionSet(cfgOptProtocolTimeout, cfgSourceConfig, NULL), "set protocol-timeout to NULL"); TEST_RESULT_PTR(cfgOption(cfgOptProtocolTimeout), NULL, "protocol-timeout is not set"); TEST_ERROR( cfgOptionSet(cfgOptRecoveryOption, cfgSourceParam, varNewDbl(1.1)), AssertError, "option 'recovery-option' must be set with KeyValue variant"); TEST_RESULT_VOID(cfgOptionSet(cfgOptRecoveryOption, cfgSourceConfig, varNewKv(kvNew())), "set recovery-option"); TEST_RESULT_INT(varLstSize(kvKeyList(cfgOptionKv(cfgOptRecoveryOption))), 0, "recovery-option is set"); TEST_ERROR( cfgOptionLst(cfgOptRecoveryOption), AssertError, "assertion 'configOptionValue[optionId].value == NULL" " || varType(configOptionValue[optionId].value) == varTypeVariantList' failed"); TEST_RESULT_INT(varLstSize(cfgOptionLst(cfgOptDbInclude)), 0, "db-include defaults to empty"); TEST_ERROR( cfgOptionSet(cfgOptDbInclude, cfgSourceParam, varNewDbl(1.1)), AssertError, "option 'db-include' must be set with VariantList variant"); TEST_RESULT_VOID(cfgOptionSet(cfgOptDbInclude, cfgSourceConfig, varNewVarLst(varLstNew())), "set db-include"); TEST_RESULT_INT(varLstSize(cfgOptionLst(cfgOptDbInclude)), 0, "db-include is set"); TEST_ERROR( cfgOptionStr(cfgOptDbInclude), AssertError, "assertion 'configOptionValue[optionId].value == NULL" " || varType(configOptionValue[optionId].value) == varTypeString' failed"); TEST_RESULT_PTR(cfgOptionStr(cfgOptStanza), NULL, "stanza defaults to null"); TEST_ERROR( cfgOptionSet(cfgOptStanza, cfgSourceParam, varNewDbl(1.1)), AssertError, "option 'stanza' must be set with String variant"); TEST_RESULT_VOID(cfgOptionSet(cfgOptStanza, cfgSourceConfig, varNewStrZ("db")), "set stanza"); TEST_RESULT_STR(strPtr(cfgOptionStr(cfgOptStanza)), "db", "stanza is set"); TEST_ERROR( cfgOptionInt(cfgOptStanza), AssertError, "assertion 'varType(configOptionValue[optionId].value) == varTypeInt64' failed"); // ------------------------------------------------------------------------------------------------------------------------- TEST_RESULT_VOID(cfgInit(), "config init resets value"); TEST_RESULT_INT(cfgCommand(), cfgCmdNone, "command begins as none"); } // ***************************************************************************************************************************** if (testBegin("cfgOptionHostPort()")) { unsigned int port = 55555; cfgInit(); cfgCommandSet(cfgCmdBackup); cfgOptionValidSet(cfgOptRepoS3Host, true); cfgOptionSet(cfgOptRepoS3Host, cfgSourceConfig, varNewStrZ("host.com")) ; TEST_RESULT_STR(strPtr(cfgOptionHostPort(cfgOptRepoS3Host, &port)), "host.com", "check plain host"); TEST_RESULT_UINT(port, 55555, "check that port was not updated"); cfgOptionSet(cfgOptRepoS3Host, cfgSourceConfig, varNewStrZ("myhost.com:777")) ; TEST_RESULT_STR(strPtr(cfgOptionHostPort(cfgOptRepoS3Host, &port)), "myhost.com", "check host with port"); TEST_RESULT_UINT(port, 777, "check that port was updated"); TEST_RESULT_STR(strPtr(cfgOptionHostPort(cfgOptRepoS3Endpoint, &port)), NULL, "check null host"); TEST_RESULT_UINT(port, 777, "check that port was not updated"); cfgOptionSet(cfgOptRepoS3Host, cfgSourceConfig, varNewStrZ("myhost.com:777:888")) ; TEST_ERROR( cfgOptionHostPort(cfgOptRepoS3Host, &port), OptionInvalidError, "'myhost.com:777:888' is not valid for option 'repo1-s3-host'" "\nHINT: is more than one port specified?"); TEST_RESULT_UINT(port, 777, "check that port was not updated"); cfgOptionValidSet(cfgOptRepoS3Endpoint, true); cfgOptionSet(cfgOptRepoS3Endpoint, cfgSourceConfig, varNewStrZ("myendpoint.com:ZZZ")) ; TEST_ERROR( cfgOptionHostPort(cfgOptRepoS3Endpoint, &port), OptionInvalidError, "'myendpoint.com:ZZZ' is not valid for option 'repo1-s3-endpoint'" "\nHINT: port is not a positive integer."); TEST_RESULT_UINT(port, 777, "check that port was not updated"); } // ***************************************************************************************************************************** if (testBegin("cfgOptionDefault() and cfgOptionDefaultSet()")) { TEST_RESULT_VOID(cfgInit(), "config init"); TEST_RESULT_VOID(cfgCommandSet(cfgCmdBackup), "backup command"); TEST_RESULT_STR(strPtr(varStr(cfgOptionDefault(cfgOptType))), "incr", "backup type default"); TEST_RESULT_BOOL(varBool(cfgOptionDefault(cfgOptCompress)), "true", "backup compress default"); TEST_RESULT_DOUBLE(varDbl(cfgOptionDefault(cfgOptProtocolTimeout)), 1830, "backup protocol-timeout default"); TEST_RESULT_INT(varIntForce(cfgOptionDefault(cfgOptCompressLevel)), 6, "backup compress-level default"); TEST_RESULT_PTR(cfgOptionDefault(cfgOptDbInclude), NULL, "backup db-include default is null"); TEST_RESULT_VOID(cfgOptionSet(cfgOptPgHost, cfgSourceParam, varNewStrZ("backup")), "backup host set"); TEST_RESULT_VOID(cfgOptionDefaultSet(cfgOptPgHost, varNewStrZ("backup-default")), "backup host default"); TEST_RESULT_VOID(cfgOptionDefaultSet(cfgOptPgHost, varNewStrZ("backup-default2")), "reset backup host default"); TEST_RESULT_STR(strPtr(varStr(cfgOption(cfgOptPgHost))), "backup", "backup host value"); TEST_RESULT_STR(strPtr(varStr(cfgOptionDefault(cfgOptPgHost))), "backup-default2", "backup host default"); TEST_RESULT_VOID(cfgOptionSet(cfgOptPgSocketPath, cfgSourceDefault, NULL), "backup pg-socket-path set"); TEST_RESULT_VOID(cfgOptionDefaultSet(cfgOptPgSocketPath, varNewStrZ("/to/socket")), "backup pg-socket-path default"); TEST_RESULT_VOID(cfgOptionDefaultSet(cfgOptPgSocketPath, varNewStrZ("/to/socket2")), "reset backup pg-socket-path default"); TEST_RESULT_STR(strPtr(varStr(cfgOption(cfgOptPgSocketPath))), "/to/socket2", "backup pg-socket-path value"); TEST_RESULT_STR(strPtr(varStr(cfgOptionDefault(cfgOptPgSocketPath))), "/to/socket2", "backup pg-socket-path value default"); } FUNCTION_HARNESS_RESULT_VOID(); }
/*********************************************************************************************************************************** Test Run ***********************************************************************************************************************************/ void testRun(void) { FUNCTION_HARNESS_VOID(); // ***************************************************************************************************************************** if (testBegin("gzipError")) { TEST_RESULT_INT(gzipError(Z_OK), Z_OK, "check ok"); TEST_RESULT_INT(gzipError(Z_STREAM_END), Z_STREAM_END, "check stream end"); TEST_ERROR(gzipError(Z_NEED_DICT), AssertError, "zlib threw error: [2] need dictionary"); TEST_ERROR(gzipError(Z_ERRNO), AssertError, "zlib threw error: [-1] file error"); TEST_ERROR(gzipError(Z_STREAM_ERROR), FormatError, "zlib threw error: [-2] stream error"); TEST_ERROR(gzipError(Z_DATA_ERROR), FormatError, "zlib threw error: [-3] data error"); TEST_ERROR(gzipError(Z_MEM_ERROR), MemoryError, "zlib threw error: [-4] insufficient memory"); TEST_ERROR(gzipError(Z_BUF_ERROR), AssertError, "zlib threw error: [-5] no space in buffer"); TEST_ERROR(gzipError(Z_VERSION_ERROR), FormatError, "zlib threw error: [-6] incompatible version"); TEST_ERROR(gzipError(999), AssertError, "zlib threw error: [999] unknown error"); } // ***************************************************************************************************************************** if (testBegin("gzipWindowBits")) { TEST_RESULT_INT(gzipWindowBits(true), -15, "raw window bits"); TEST_RESULT_INT(gzipWindowBits(false), 31, "gzip window bits"); } // ***************************************************************************************************************************** if (testBegin("GzipCompress and GzipDecompress")) { const char *simpleData = "A simple string"; Buffer *compressed = NULL; Buffer *decompressed = bufNewC(simpleData, strlen(simpleData)); TEST_ASSIGN( compressed, testCompress(gzipCompressNew(3, false), decompressed, 1024, 1024), "simple data - compress large in/large out buffer"); TEST_RESULT_BOOL( bufEq(compressed, testCompress(gzipCompressNew(3, false), decompressed, 1024, 1)), true, "simple data - compress large in/small out buffer"); TEST_RESULT_BOOL( bufEq(compressed, testCompress(gzipCompressNew(3, false), decompressed, 1, 1024)), true, "simple data - compress small in/large out buffer"); TEST_RESULT_BOOL( bufEq(compressed, testCompress(gzipCompressNew(3, false), decompressed, 1, 1)), true, "simple data - compress small in/small out buffer"); TEST_RESULT_BOOL( bufEq(decompressed, testDecompress(gzipDecompressNew(false), compressed, 1024, 1024)), true, "simple data - decompress large in/large out buffer"); TEST_RESULT_BOOL( bufEq(decompressed, testDecompress(gzipDecompressNew(false), compressed, 1024, 1)), true, "simple data - decompress large in/small out buffer"); TEST_RESULT_BOOL( bufEq(decompressed, testDecompress(gzipDecompressNew(false), compressed, 1, 1024)), true, "simple data - decompress small in/large out buffer"); TEST_RESULT_BOOL( bufEq(decompressed, testDecompress(gzipDecompressNew(false), compressed, 1, 1)), true, "simple data - decompress small in/small out buffer"); // Compress a large zero input buffer into small output buffer // ------------------------------------------------------------------------------------------------------------------------- decompressed = bufNew(1024 * 1024 - 1); memset(bufPtr(decompressed), 0, bufSize(decompressed)); bufUsedSet(decompressed, bufSize(decompressed)); TEST_ASSIGN( compressed, testCompress(gzipCompressNew(3, true), decompressed, bufSize(decompressed), 1024), "zero data - compress large in/small out buffer"); TEST_RESULT_BOOL( bufEq(decompressed, testDecompress(gzipDecompressNew(true), compressed, bufSize(compressed), 1024 * 256)), true, "zero data - decompress large in/small out buffer"); } // ***************************************************************************************************************************** if (testBegin("gzipDecompressToLog() and gzipCompressToLog()")) { GzipDecompress *decompress = (GzipDecompress *)ioFilterDriver(gzipDecompressNew(false)); TEST_RESULT_STR(strPtr(gzipDecompressToLog(decompress)), "{inputSame: false, done: false, availIn: 0}", "format object"); decompress->inputSame = true; decompress->done = true; TEST_RESULT_STR(strPtr(gzipDecompressToLog(decompress)), "{inputSame: true, done: true, availIn: 0}", "format object"); } FUNCTION_HARNESS_RESULT_VOID(); }
/*********************************************************************************************************************************** Test Run ***********************************************************************************************************************************/ void testRun(void) { FUNCTION_HARNESS_VOID(); // ***************************************************************************************************************************** if (testBegin("stackTraceFmt()")) { char buffer[8]; TEST_RESULT_INT(stackTraceFmt(buffer, 8, 0, "%s", "1234567"), 7, "fill buffer"); TEST_RESULT_STR(buffer, "1234567", " check buffer"); TEST_RESULT_INT(stackTraceFmt(buffer, 8, 7, "%s", "1234567"), 7, "try to fill buffer - at end"); TEST_RESULT_STR(buffer, "1234567", " check buffer is unmodified"); TEST_RESULT_INT(stackTraceFmt(buffer, 8, 8, "%s", "1234567"), 7, "try to fill buffer - past end"); TEST_RESULT_STR(buffer, "1234567", " check buffer is unmodified"); } // ***************************************************************************************************************************** if (testBegin("stackTraceInit() and stackTraceError()")) { #ifdef WITH_BACKTRACE stackTraceInit(BOGUS_STR); // This time does nothing stackTraceInit(BOGUS_STR); // This will call the error routine since we passed a bogus exe assert(stackTracePush("file1.c", "function1", logLevelDebug) == logLevelDebug); stackTracePop("file1.c", "function1"); backTraceState = NULL; #endif } // ***************************************************************************************************************************** if (testBegin("stackTraceTestStart(), stackTraceTestStop(), and stackTraceTest()")) { #ifndef NDEBUG assert(stackTraceTest()); stackTraceTestStop(); assert(!stackTraceTest()); stackTraceTestStart(); assert(stackTraceTest()); #endif } // ***************************************************************************************************************************** if (testBegin("stackTracePush(), stackTracePop(), and stackTraceClean()")) { char buffer[4096]; #ifdef WITH_BACKTRACE stackTraceInit(testExe()); #endif TEST_ERROR(stackTracePop("file1", "function1", false), AssertError, "assertion 'stackSize > 0' failed"); assert(stackTracePush("file1", "function1", logLevelDebug) == logLevelDebug); assert(stackSize == 1); TEST_ERROR( stackTracePop("file2", "function2", false), AssertError, "popping file2:function2 but expected file1:function1"); assert(stackTracePush("file1", "function1", logLevelDebug) == logLevelDebug); TEST_ERROR( stackTracePop("file1", "function2", false), AssertError, "popping file1:function2 but expected file1:function1"); TRY_BEGIN() { assert(stackTracePush("file1.c", "function1", logLevelDebug) == logLevelDebug); stackTraceParamLog(); assert(strcmp(stackTraceParam(), "void") == 0); stackTraceToZ(buffer, sizeof(buffer), "file1.c", "function2", 99); #ifdef WITH_BACKTRACE TEST_RESULT_STR( buffer, "file1:function2:99:(test build required for parameters)\n" " ... function(s) ommitted ...\n" "file1:function1:0:(void)", " check stack trace"); #else TEST_RESULT_STR( buffer, "file1:function2:99:(test build required for parameters)\n" " ... function(s) ommitted ...\n" "file1:function1:(void)", " check stack trace"); #endif assert(stackTracePush("file1.c", "function2", logLevelTrace) == logLevelTrace); stackTrace[stackSize - 2].fileLine = 7777; assert(strcmp(stackTraceParam(), "trace log level required for parameters") == 0); stackTrace[stackSize - 1].functionLogLevel = logLevelDebug; TRY_BEGIN() { // Function with one param assert(stackTracePush("file2.c", "function2", logLevelDebug) == logLevelDebug); stackTrace[stackSize - 2].fileLine = 7777; stackTraceParamAdd((size_t)snprintf(stackTraceParamBuffer("param1"), STACK_TRACE_PARAM_MAX, "value1")); stackTraceParamLog(); assert(strcmp(stackTraceParam(), "param1: value1") == 0); // Function with multiple params assert(stackTracePush("file3.c", "function3", logLevelTrace) == logLevelTrace); stackTrace[stackSize - 2].fileLine = 7777; stackTraceParamLog(); stackTraceParamAdd((size_t)snprintf(stackTraceParamBuffer("param1"), STACK_TRACE_PARAM_MAX, "value1")); stackTraceParamAdd((size_t)snprintf(stackTraceParamBuffer("param2"), STACK_TRACE_PARAM_MAX, "value2")); assert(strcmp(stackTraceParam(), "param1: value1, param2: value2") == 0); // Calculate exactly where the buffer will overflow (4 is for the separators) size_t bufferOverflow = sizeof(functionParamBuffer) - (STACK_TRACE_PARAM_MAX * 2) - strlen("param1") - 4 - (size_t)(stackTrace[stackSize - 1].param - functionParamBuffer); // Munge the previous previous param in the stack so that the next one will just barely fit stackTrace[stackSize - 1].paramSize = bufferOverflow - 1; assert(stackTracePush("file4.c", "function4", logLevelDebug) == logLevelTrace); stackTrace[stackSize - 2].fileLine = 7777; stackTraceParamLog(); assert(stackSize == 5); // This param will fit exactly stackTraceParamAdd((size_t)snprintf(stackTraceParamBuffer("param1"), STACK_TRACE_PARAM_MAX, "value1")); assert(strcmp(stackTraceParam(), "param1: value1") == 0); // But when we increment the param pointer by one there will be overflow stackTrace[stackSize - 1].param += 1; stackTrace[stackSize - 1].paramSize = 0; stackTraceParamAdd((size_t)snprintf(stackTraceParamBuffer("param1"), STACK_TRACE_PARAM_MAX, "value1")); assert(strcmp(stackTraceParam(), "buffer full - parameters not available") == 0); stackTraceToZ(buffer, sizeof(buffer), "file4.c", "function4", 99); #ifdef WITH_BACKTRACE TEST_RESULT_STR( buffer, "file4:function4:99:(buffer full - parameters not available)\n" "file3:function3:7777:(param1: value1, param2: value2)\n" "file2:function2:7777:(param1: value1)\n" "file1:function2:7777:(debug log level required for parameters)\n" "file1:function1:7777:(void)", "stack trace"); #else TEST_RESULT_STR( buffer, "file4:function4:99:(buffer full - parameters not available)\n" "file3:function3:(param1: value1, param2: value2)\n" "file2:function2:(param1: value1)\n" "file1:function2:(debug log level required for parameters)\n" "file1:function1:(void)", "stack trace"); #endif stackTracePop("file4.c", "function4", false); assert(stackSize == 4); // Check that stackTracePop() works with test tracing stackTracePush("file_test.c", "function_test", logLevelDebug); stackTracePop("file_test.c", "function_test", true); // Check that stackTracePop() does nothing when test tracing is disabled stackTraceTestStop(); stackTracePop("bogus.c", "bogus", true); stackTraceTestStart(); THROW(ConfigError, "test"); } CATCH(ConfigError) { // Ignore the error since we are just testing stack cleaup } TRY_END(); assert(stackSize == 2); THROW(ConfigError, "test"); } CATCH(ConfigError) { // Ignore the error since we are just testing stack cleaup } TRY_END(); assert(stackSize == 0); }
/*********************************************************************************************************************************** Test run ***********************************************************************************************************************************/ void testRun(void) { FUNCTION_HARNESS_VOID(); // Static tests against known values -- these may break as options change so will need to be kept up to date. The tests have // generally been selected to favor values that are not expected to change but adjustments are welcome as long as the type of // test is not drastically changed. // ***************************************************************************************************************************** if (testBegin("check known values")) { TEST_RESULT_STR(cfgDefOptionName(cfgDefOptConfig), "config", "option name"); TEST_RESULT_INT(cfgDefOptionId("repo-host"), cfgDefOptRepoHost, "define id"); TEST_RESULT_INT(cfgDefOptionId(BOGUS_STR), -1, "invalid define id"); TEST_RESULT_BOOL(cfgDefOptionAllowList(cfgDefCmdBackup, cfgDefOptLogLevelConsole), true, "allow list valid"); TEST_RESULT_BOOL(cfgDefOptionAllowList(cfgDefCmdBackup, cfgDefOptPgHost), false, "allow list not valid"); TEST_RESULT_BOOL(cfgDefOptionAllowList(cfgDefCmdBackup, cfgDefOptType), true, "command allow list valid"); TEST_RESULT_INT(cfgDefOptionAllowListValueTotal(cfgDefCmdBackup, cfgDefOptChecksumPage), 0, "allow list total = 0"); TEST_RESULT_INT(cfgDefOptionAllowListValueTotal(cfgDefCmdBackup, cfgDefOptType), 3, "allow list total"); TEST_RESULT_STR(cfgDefOptionAllowListValue(cfgDefCmdBackup, cfgDefOptType, 0), "full", "allow list value 0"); TEST_RESULT_STR(cfgDefOptionAllowListValue(cfgDefCmdBackup, cfgDefOptType, 1), "diff", "allow list value 1"); TEST_RESULT_STR(cfgDefOptionAllowListValue(cfgDefCmdBackup, cfgDefOptType, 2), "incr", "allow list value 2"); TEST_ERROR( cfgDefOptionAllowListValue(cfgDefCmdBackup, cfgDefOptType, 3), AssertError, "assertion 'valueId < cfgDefOptionAllowListValueTotal(commandDefId, optionDefId)' failed"); TEST_RESULT_BOOL( cfgDefOptionAllowListValueValid(cfgDefCmdBackup, cfgDefOptType, "diff"), true, "allow list value valid"); TEST_RESULT_BOOL( cfgDefOptionAllowListValueValid(cfgDefCmdBackup, cfgDefOptType, BOGUS_STR), false, "allow list value not valid"); TEST_RESULT_BOOL(cfgDefOptionAllowRange(cfgDefCmdBackup, cfgDefOptCompressLevel), true, "range allowed"); TEST_RESULT_BOOL(cfgDefOptionAllowRange(cfgDefCmdBackup, cfgDefOptRepoHost), false, "range not allowed"); TEST_RESULT_DOUBLE(cfgDefOptionAllowRangeMin(cfgDefCmdBackup, cfgDefOptDbTimeout), 0.1, "range min"); TEST_RESULT_DOUBLE(cfgDefOptionAllowRangeMax(cfgDefCmdBackup, cfgDefOptCompressLevel), 9, "range max"); TEST_RESULT_DOUBLE(cfgDefOptionAllowRangeMin(cfgDefCmdArchivePush, cfgDefOptArchivePushQueueMax), 0, "range min"); TEST_RESULT_DOUBLE( cfgDefOptionAllowRangeMax(cfgDefCmdArchivePush, cfgDefOptArchivePushQueueMax), 4503599627370496, "range max"); TEST_ERROR( cfgDefOptionDefault(cfgDefCommandTotal(), cfgDefOptCompressLevel), AssertError, "assertion 'commandDefId < cfgDefCommandTotal()' failed"); TEST_ERROR(cfgDefOptionDefault( cfgDefCmdBackup, cfgDefOptionTotal()), AssertError, "assertion 'optionDefId < cfgDefOptionTotal()' failed"); TEST_RESULT_STR(cfgDefOptionDefault(cfgDefCmdBackup, cfgDefOptCompressLevel), "6", "option default exists"); TEST_RESULT_STR(cfgDefOptionDefault(cfgDefCmdRestore, cfgDefOptType), "default", "command default exists"); TEST_RESULT_STR(cfgDefOptionDefault(cfgDefCmdLocal, cfgDefOptType), NULL, "command default does not exist"); TEST_RESULT_STR(cfgDefOptionDefault(cfgDefCmdBackup, cfgDefOptRepoHost), NULL, "default does not exist"); TEST_RESULT_BOOL(cfgDefOptionDepend(cfgDefCmdRestore, cfgDefOptRepoS3Key), true, "has depend option"); TEST_RESULT_BOOL(cfgDefOptionDepend(cfgDefCmdRestore, cfgDefOptType), false, "does not have depend option"); TEST_RESULT_INT(cfgDefOptionDependOption(cfgDefCmdBackup, cfgDefOptPgHostUser), cfgDefOptPgHost, "depend option id"); TEST_RESULT_INT(cfgDefOptionDependOption(cfgDefCmdBackup, cfgDefOptRepoHostCmd), cfgDefOptRepoHost, "depend option id"); TEST_RESULT_INT(cfgDefOptionDependValueTotal(cfgDefCmdRestore, cfgDefOptTarget), 3, "depend option value total"); TEST_RESULT_STR(cfgDefOptionDependValue(cfgDefCmdRestore, cfgDefOptTarget, 0), "name", "depend option value 0"); TEST_RESULT_STR(cfgDefOptionDependValue(cfgDefCmdRestore, cfgDefOptTarget, 1), "time", "depend option value 1"); TEST_RESULT_STR(cfgDefOptionDependValue(cfgDefCmdRestore, cfgDefOptTarget, 2), "xid", "depend option value 2"); TEST_ERROR( cfgDefOptionDependValue(cfgDefCmdRestore, cfgDefOptTarget, 3), AssertError, "assertion 'valueId < cfgDefOptionDependValueTotal(commandDefId, optionDefId)' failed"); TEST_RESULT_BOOL( cfgDefOptionDependValueValid(cfgDefCmdRestore, cfgDefOptTarget, "time"), true, "depend option value valid"); TEST_RESULT_BOOL( cfgDefOptionDependValueValid(cfgDefCmdRestore, cfgDefOptTarget, BOGUS_STR), false, "depend option value not valid"); TEST_ERROR( cfgDefOptionIndexTotal(cfgDefOptionTotal()), AssertError, "assertion 'optionDefId < cfgDefOptionTotal()' failed"); TEST_RESULT_INT(cfgDefOptionIndexTotal(cfgDefOptPgPath), 8, "index total > 1"); TEST_RESULT_INT(cfgDefOptionIndexTotal(cfgDefOptRepoPath), 1, "index total == 1"); TEST_RESULT_BOOL(cfgDefOptionInternal(cfgDefCmdRestore, cfgDefOptSet), false, "option set is not internal"); TEST_RESULT_BOOL(cfgDefOptionInternal(cfgDefCmdRestore, cfgDefOptPgHost), true, "option pg-host is internal"); TEST_RESULT_BOOL(cfgDefOptionInternal(cfgDefCmdRestore, cfgDefOptTest), true, "option test is internal"); TEST_RESULT_BOOL(cfgDefOptionMulti(cfgDefOptRecoveryOption), true, "recovery-option is multi"); TEST_RESULT_BOOL(cfgDefOptionMulti(cfgDefOptStartFast), false, "start-fast is not multi"); TEST_RESULT_STR(cfgDefOptionPrefix(cfgDefOptPgHost), "pg", "option prefix"); TEST_RESULT_STR(cfgDefOptionPrefix(cfgDefOptType), NULL, "option has no prefix"); TEST_RESULT_BOOL(cfgDefOptionRequired(cfgDefCmdBackup, cfgDefOptConfig), true, "option required"); TEST_RESULT_BOOL(cfgDefOptionRequired(cfgDefCmdRestore, cfgDefOptRepoHost), false, "option not required"); TEST_RESULT_BOOL(cfgDefOptionRequired(cfgDefCmdInfo, cfgDefOptStanza), false, "command option not required"); TEST_RESULT_INT(cfgDefOptionSection(cfgDefOptRepoS3Key), cfgDefSectionGlobal, "global section"); TEST_RESULT_INT(cfgDefOptionSection(cfgDefOptPgPath), cfgDefSectionStanza, "stanza section"); TEST_RESULT_INT(cfgDefOptionSection(cfgDefOptType), cfgDefSectionCommandLine, "command line only"); TEST_RESULT_BOOL(cfgDefOptionSecure(cfgDefOptRepoS3Key), true, "option secure"); TEST_RESULT_BOOL(cfgDefOptionSecure(cfgDefOptRepoHost), false, "option not secure"); TEST_RESULT_INT(cfgDefOptionType(cfgDefOptType), cfgDefOptTypeString, "string type"); TEST_RESULT_INT(cfgDefOptionType(cfgDefOptCompress), cfgDefOptTypeBoolean, "boolean type"); TEST_ERROR( cfgDefOptionValid(cfgDefCmdInfo, cfgDefOptionTotal()), AssertError, "assertion 'optionDefId < cfgDefOptionTotal()' failed"); TEST_RESULT_BOOL(cfgDefOptionValid(cfgDefCmdBackup, cfgDefOptType), true, "option valid"); TEST_RESULT_BOOL(cfgDefOptionValid(cfgDefCmdInfo, cfgDefOptType), false, "option not valid"); } // ***************************************************************************************************************************** if (testBegin("cfgDefCommandHelp*() and cfgDefOptionHelp*()")) { TEST_RESULT_BOOL(cfgDefOptionHelpNameAlt(cfgDefOptRepoHost), true, "name alt exists"); TEST_RESULT_BOOL(cfgDefOptionHelpNameAlt(cfgDefOptSet), false, "name alt not exists"); TEST_RESULT_INT(cfgDefOptionHelpNameAltValueTotal(cfgDefOptRepoHost), 1, "name alt value total"); TEST_RESULT_STR(cfgDefOptionHelpNameAltValue(cfgDefOptRepoHost, 0), "backup-host", "name alt value 0"); TEST_ERROR( cfgDefOptionHelpNameAltValue(cfgDefOptRepoHost, 1), AssertError, "assertion 'valueId < cfgDefOptionHelpNameAltValueTotal(optionDefId)' failed"); TEST_RESULT_STR(cfgDefCommandHelpSummary(cfgDefCmdBackup), "Backup a database cluster.", "backup command help summary"); TEST_RESULT_STR( cfgDefCommandHelpDescription(cfgDefCmdBackup), "pgBackRest does not have a built-in scheduler so it's best to run it from cron or some other scheduling mechanism.", "backup command help description"); TEST_RESULT_STR(cfgDefOptionHelpSection(cfgDefOptCompress), "general", "compress option help section"); TEST_RESULT_STR( cfgDefOptionHelpSummary(cfgDefCmdBackup, cfgDefOptCompress), "Use gzip file compression.", "backup command, compress option help summary"); TEST_RESULT_STR( cfgDefOptionHelpDescription(cfgDefCmdBackup, cfgDefOptCompress), "Backup files are compatible with command-line gzip tools.", "backup command, compress option help description"); TEST_RESULT_STR( cfgDefOptionHelpSummary(cfgDefCmdBackup, cfgDefOptType), "Backup type.", "backup command, type option help summary"); TEST_RESULT_STR( cfgDefOptionHelpDescription(cfgDefCmdBackup, cfgDefOptType), "The following backup types are supported:\n" "\n" "* full - all database cluster files will be copied and there will be no dependencies on previous backups.\n" "* incr - incremental from the last successful backup.\n" "* diff - like an incremental backup but always based on the last full backup.", "backup command, type option help description"); } FUNCTION_HARNESS_RESULT_VOID(); }
/*********************************************************************************************************************************** Test Run ***********************************************************************************************************************************/ void testRun(void) { FUNCTION_HARNESS_VOID(); // ***************************************************************************************************************************** if (testBegin("strNew(), strNewBuf(), strNewN(), strEmpty(), strPtr(), strSize(), and strFree()")) { // We don't want this struct to grow since there are generally a lot of strings, so make sure it doesn't grow without us // knowing about it TEST_RESULT_UINT(sizeof(StringConst), TEST_64BIT() ? 16 : 12, "check StringConst struct size"); // Test the size macro TEST_RESULT_VOID(CHECK_SIZE(555), "valid size"); TEST_ERROR(CHECK_SIZE(STRING_SIZE_MAX + 1), AssertError, "string size must be <= 1073741824 bytes"); String *string = strNew("static string"); TEST_RESULT_STR(strPtr(string), "static string", "new with static string"); TEST_RESULT_INT(strSize(string), 13, "check size"); TEST_RESULT_BOOL(strEmpty(string), false, "is not empty"); TEST_RESULT_INT(strlen(strPtr(string)), 13, "check size with strlen()"); TEST_RESULT_CHAR(strPtr(string)[2], 'a', "check character"); TEST_RESULT_VOID(strFree(string), "free string"); // ------------------------------------------------------------------------------------------------------------------------- TEST_RESULT_STR(strPtr(strNewN("testmorestring", 4)), "test", "new string with size limit"); // ------------------------------------------------------------------------------------------------------------------------- Buffer *buffer = bufNew(8); memcpy(bufPtr(buffer), "12345678", 8); bufUsedSet(buffer, 8); TEST_RESULT_STR(strPtr(strNewBuf(buffer)), "12345678", "new string from buffer"); // ------------------------------------------------------------------------------------------------------------------------- string = strNewFmt("formatted %s %04d", "string", 1); TEST_RESULT_STR(strPtr(string), "formatted string 0001", "new with formatted string"); TEST_RESULT_PTR(strPtr(NULL), NULL, "null string pointer"); TEST_RESULT_VOID(strFree(string), "free string"); TEST_RESULT_VOID(strFree(NULL), "free null string"); } // ***************************************************************************************************************************** if (testBegin("STRING_STATIC()")) { TEST_RESULT_STR(strPtr(TEST_STRING), "a very interesting string!", "check static string"); TEST_RESULT_STR(strPtr(strSubN(TEST_STRING, 0, 6)), "a very", "read-only strSub() works"); } // ***************************************************************************************************************************** if (testBegin("strBase() and strPath()")) { TEST_RESULT_STR(strPtr(strBase(STRDEF(""))), "", "empty string"); TEST_RESULT_STR(strPtr(strBase(STRDEF("/"))), "", "/ only"); TEST_RESULT_STR(strPtr(strBase(STRDEF("/file"))), "file", "root file"); TEST_RESULT_STR(strPtr(strBase(STRDEF("/dir1/dir2/file"))), "file", "subdirectory file"); TEST_RESULT_STR(strPtr(strPath(STRDEF(""))), "", "empty string"); TEST_RESULT_STR(strPtr(strPath(STRDEF("/"))), "/", "/ only"); TEST_RESULT_STR(strPtr(strPath(STRDEF("/file"))), "/", "root path"); TEST_RESULT_STR(strPtr(strPath(STRDEF("/dir1/dir2/file"))), "/dir1/dir2", "subdirectory file"); } // ***************************************************************************************************************************** if (testBegin("strCat(), strCatChr(), and strCatFmt()")) { String *string = strNew("XXXX"); String *string2 = strNew("ZZZZ"); TEST_RESULT_STR(strPtr(strCat(string, "YYYY")), "XXXXYYYY", "cat string"); TEST_RESULT_SIZE(string->extra, 4, "check extra"); TEST_RESULT_STR(strPtr(strCatFmt(string, "%05d", 777)), "XXXXYYYY00777", "cat formatted string"); TEST_RESULT_SIZE(string->extra, 6, "check extra"); TEST_RESULT_STR(strPtr(strCatChr(string, '!')), "XXXXYYYY00777!", "cat chr"); TEST_RESULT_SIZE(string->extra, 5, "check extra"); TEST_RESULT_STR(strPtr(string2), "ZZZZ", "check unaltered string"); } // ***************************************************************************************************************************** if (testBegin("strDup()")) { const String *string = STRDEF("duplicated string"); String *stringDup = strDup(string); TEST_RESULT_STR(strPtr(stringDup), strPtr(string), "duplicated strings match"); TEST_RESULT_PTR(strDup(NULL), NULL, "duplicate null string"); } // ***************************************************************************************************************************** if (testBegin("strBeginsWith() and strBeginsWithZ()")) { TEST_RESULT_BOOL(strBeginsWith(STRDEF(""), STRDEF("aaa")), false, "empty string"); TEST_RESULT_BOOL(strBeginsWith(STRDEF("astring"), STRDEF("")), true, "empty begins with"); TEST_RESULT_BOOL(strBeginsWithZ(STRDEF("astring"), "astr"), true, "partial begins with"); TEST_RESULT_BOOL(strBeginsWithZ(STRDEF("astring"), "astring"), true, "equal strings"); } // ***************************************************************************************************************************** if (testBegin("strEndsWith() and strEndsWithZ()")) { TEST_RESULT_BOOL(strEndsWith(STRDEF(""), STRDEF(".doc")), false, "empty string"); TEST_RESULT_BOOL(strEndsWith(STRDEF("astring"), STRDEF("")), true, "empty ends with"); TEST_RESULT_BOOL(strEndsWithZ(STRDEF("astring"), "ing"), true, "partial ends with"); TEST_RESULT_BOOL(strEndsWithZ(STRDEF("astring"), "astring"), true, "equal strings"); } // ***************************************************************************************************************************** if (testBegin("strEq(), strEqZ(), strCmp(), strCmpZ()")) { TEST_RESULT_BOOL(strEq(STRDEF("equalstring"), STRDEF("equalstring")), true, "strings equal"); TEST_RESULT_BOOL(strEq(STRDEF("astring"), STRDEF("anotherstring")), false, "strings not equal"); TEST_RESULT_BOOL(strEq(STRDEF("astring"), STRDEF("bstring")), false, "equal length strings not equal"); TEST_RESULT_INT(strCmp(STRDEF("equalstring"), STRDEF("equalstring")), 0, "strings equal"); TEST_RESULT_INT(strCmp(STRDEF("a"), STRDEF("b")), -1, "a < b"); TEST_RESULT_INT(strCmp(STRDEF("b"), STRDEF("a")), 1, "b > a"); TEST_RESULT_BOOL(strEqZ(STRDEF("equalstring"), "equalstring"), true, "strings equal"); TEST_RESULT_BOOL(strEqZ(STRDEF("astring"), "anotherstring"), false, "strings not equal"); TEST_RESULT_BOOL(strEqZ(STRDEF("astring"), "bstring"), false, "equal length strings not equal"); TEST_RESULT_INT(strCmpZ(STRDEF("equalstring"), "equalstring"), 0, "strings equal"); TEST_RESULT_INT(strCmpZ(STRDEF("a"), "b"), -1, "a < b"); TEST_RESULT_INT(strCmpZ(STRDEF("b"), "a"), 1, "b > a"); } // ***************************************************************************************************************************** if (testBegin("strFirstUpper(), strFirstLower(), strUpper(), strLower()")) { TEST_RESULT_STR(strPtr(strFirstUpper(strNew(""))), "", "empty first upper"); TEST_RESULT_STR(strPtr(strFirstUpper(strNew("aaa"))), "Aaa", "first upper"); TEST_RESULT_STR(strPtr(strFirstUpper(strNew("Aaa"))), "Aaa", "first already upper"); TEST_RESULT_STR(strPtr(strFirstLower(strNew(""))), "", "empty first lower"); TEST_RESULT_STR(strPtr(strFirstLower(strNew("AAA"))), "aAA", "first lower"); TEST_RESULT_STR(strPtr(strFirstLower(strNew("aAA"))), "aAA", "first already lower"); TEST_RESULT_STR(strPtr(strLower(strNew("K123aBc"))), "k123abc", "all lower"); TEST_RESULT_STR(strPtr(strLower(strNew("k123abc"))), "k123abc", "already lower"); TEST_RESULT_STR(strPtr(strLower(strNew("C"))), "c", "char lower"); TEST_RESULT_STR(strPtr(strLower(strNew(""))), "", "empty lower"); TEST_RESULT_STR(strPtr(strUpper(strNew("K123aBc"))), "K123ABC", "all upper"); TEST_RESULT_STR(strPtr(strUpper(strNew("K123ABC"))), "K123ABC", "already upper"); TEST_RESULT_STR(strPtr(strUpper(strNew("c"))), "C", "char upper"); TEST_RESULT_STR(strPtr(strUpper(strNew(""))), "", "empty upper"); } // ***************************************************************************************************************************** if (testBegin("strQuote()")) { TEST_RESULT_STR(strPtr(strQuote(STRDEF("abcd"), STRDEF("'"))), "'abcd'", "quote string"); } // ***************************************************************************************************************************** if (testBegin("strReplaceChr()")) { TEST_RESULT_STR(strPtr(strReplaceChr(strNew("ABCD"), 'B', 'R')), "ARCD", "replace chr"); } // ***************************************************************************************************************************** if (testBegin("strSub() and strSubN()")) { TEST_RESULT_STR(strPtr(strSub(STRDEF("ABCD"), 2)), "CD", "sub string"); TEST_RESULT_STR(strPtr(strSubN(STRDEF("ABCD"), 1, 2)), "BC", "sub string with length"); } // ***************************************************************************************************************************** if (testBegin("strTrim()")) { TEST_RESULT_STR(strPtr(strTrim(strNew(""))), "", "trim empty"); TEST_RESULT_STR(strPtr(strTrim(strNew("X"))), "X", "no trim (one char)"); TEST_RESULT_STR(strPtr(strTrim(strNew("no-trim"))), "no-trim", "no trim (string)"); TEST_RESULT_STR(strPtr(strTrim(strNew(" \t\r\n"))), "", "all whitespace"); TEST_RESULT_STR(strPtr(strTrim(strNew(" \tbegin-only"))), "begin-only", "trim begin"); TEST_RESULT_STR(strPtr(strTrim(strNew("end-only\t "))), "end-only", "trim end"); TEST_RESULT_STR(strPtr(strTrim(strNew("\n\rboth\r\n"))), "both", "trim both"); TEST_RESULT_STR(strPtr(strTrim(strNew("begin \r\n\tend"))), "begin \r\n\tend", "ignore whitespace in middle"); } // ***************************************************************************************************************************** if (testBegin("strChr() and strTrunc()")) { TEST_RESULT_INT(strChr(STRDEF("abcd"), 'c'), 2, "c found"); TEST_RESULT_INT(strChr(STRDEF("abcd"), 'C'), -1, "capital C not found"); TEST_RESULT_INT(strChr(STRDEF("abcd"), 'i'), -1, "i not found"); TEST_RESULT_INT(strChr(STRDEF(""), 'x'), -1, "empty string - x not found"); String *val = strNew("abcdef"); TEST_ERROR( strTrunc(val, (int)(strSize(val) + 1)), AssertError, "assertion 'idx >= 0 && (size_t)idx <= this->size' failed"); TEST_ERROR(strTrunc(val, -1), AssertError, "assertion 'idx >= 0 && (size_t)idx <= this->size' failed"); TEST_RESULT_STR(strPtr(strTrunc(val, strChr(val, 'd'))), "abc", "simple string truncated"); strCat(val, "\r\n to end"); TEST_RESULT_STR(strPtr(strTrunc(val, strChr(val, 'n'))), "abc\r\n to e", "complex string truncated"); TEST_RESULT_STR(strPtr(strTrunc(val, strChr(val, 'a'))), "", "complete string truncated - empty string"); TEST_RESULT_INT(strSize(val), 0, "0 size"); TEST_RESULT_STR(strPtr(strTrunc(val, 0)), "", "test coverage of empty string - no error thrown for index 0"); } // ***************************************************************************************************************************** if (testBegin("strToLog() and strObjToLog()")) { TEST_RESULT_STR(strPtr(strToLog(STRDEF("test"))), "{\"test\"}", "format string"); TEST_RESULT_STR(strPtr(strToLog(NULL)), "null", "format null string"); char buffer[256]; TEST_RESULT_UINT(strObjToLog(NULL, (StrObjToLogFormat)strToLog, buffer, sizeof(buffer)), 4, "format null string"); TEST_RESULT_STR(buffer, "null", "check null string"); TEST_RESULT_UINT(strObjToLog(STRDEF("teststr"), (StrObjToLogFormat)strToLog, buffer, sizeof(buffer)), 11, "format string"); TEST_RESULT_STR(buffer, "{\"teststr\"}", "check string"); } // ***************************************************************************************************************************** if (testBegin("strSizeFormat()")) { TEST_RESULT_STR(strPtr(strSizeFormat(0)), "0B", "zero bytes"); TEST_RESULT_STR(strPtr(strSizeFormat(1023)), "1023B", "1023 bytes"); TEST_RESULT_STR(strPtr(strSizeFormat(1024)), "1KB", "1 KB"); TEST_RESULT_STR(strPtr(strSizeFormat(2200)), "2.1KB", "2.1 KB"); TEST_RESULT_STR(strPtr(strSizeFormat(1048576)), "1MB", "1 MB"); TEST_RESULT_STR(strPtr(strSizeFormat(20162900)), "19.2MB", "19.2 MB"); TEST_RESULT_STR(strPtr(strSizeFormat(1073741824)), "1GB", "1 GB"); TEST_RESULT_STR(strPtr(strSizeFormat(1073741824 + 107374183)), "1.1GB", "1.1 GB"); TEST_RESULT_STR(strPtr(strSizeFormat(UINT64_MAX)), "17179869183GB", "uint64 max"); } // ***************************************************************************************************************************** if (testBegin("strLstNew(), strLstAdd*(), strLstGet(), strLstMove(), strLstSize(), and strLstFree()")) { // Add strings to the list // ------------------------------------------------------------------------------------------------------------------------- StringList *list = NULL; MEM_CONTEXT_TEMP_BEGIN() { list = strLstNew(); for (int listIdx = 0; listIdx <= LIST_INITIAL_SIZE; listIdx++) { if (listIdx == 0) { TEST_RESULT_PTR(strLstAdd(list, NULL), list, "add null item"); } else TEST_RESULT_PTR(strLstAdd(list, strNewFmt("STR%02d", listIdx)), list, "add item %d", listIdx); } strLstMove(list, MEM_CONTEXT_OLD()); } MEM_CONTEXT_TEMP_END(); TEST_RESULT_INT(strLstSize(list), 9, "list size"); // Read them back and check values // ------------------------------------------------------------------------------------------------------------------------- for (unsigned int listIdx = 0; listIdx < strLstSize(list); listIdx++) { if (listIdx == 0) { TEST_RESULT_PTR(strLstGet(list, listIdx), NULL, "check null item"); } else TEST_RESULT_STR(strPtr(strLstGet(list, listIdx)), strPtr(strNewFmt("STR%02u", listIdx)), "check item %u", listIdx); } TEST_RESULT_VOID(strLstFree(list), "free string list"); TEST_RESULT_VOID(strLstFree(NULL), "free null string list"); }
/*********************************************************************************************************************************** Test run ***********************************************************************************************************************************/ void testRun(void) { FUNCTION_HARNESS_VOID(); // ***************************************************************************************************************************** if (testBegin("cfgLoadLogSetting()")) { cfgInit(); TEST_RESULT_VOID(cfgLoadLogSetting(), "load log settings all defaults"); TEST_RESULT_INT(logLevelStdOut, logLevelOff, "console logging is off"); TEST_RESULT_INT(logLevelStdErr, logLevelOff, "stderr logging is off"); TEST_RESULT_INT(logLevelFile, logLevelOff, "file logging is off"); TEST_RESULT_BOOL(logTimestamp, true, "timestamp logging is on"); // ------------------------------------------------------------------------------------------------------------------------- cfgInit(); cfgCommandSet(cfgCmdLocal); cfgOptionValidSet(cfgOptLogLevelConsole, true); cfgOptionSet(cfgOptLogLevelConsole, cfgSourceParam, varNewStrZ("info")); cfgOptionValidSet(cfgOptLogLevelStderr, true); cfgOptionSet(cfgOptLogLevelStderr, cfgSourceParam, varNewStrZ("error")); cfgOptionValidSet(cfgOptLogLevelFile, true); cfgOptionSet(cfgOptLogLevelFile, cfgSourceParam, varNewStrZ("debug")); cfgOptionValidSet(cfgOptLogTimestamp, true); cfgOptionSet(cfgOptLogTimestamp, cfgSourceParam, varNewBool(false)); TEST_RESULT_VOID(cfgLoadLogSetting(), "load log settings no defaults"); TEST_RESULT_INT(logLevelStdOut, logLevelInfo, "console logging is info"); TEST_RESULT_INT(logLevelStdErr, logLevelError, "stderr logging is error"); TEST_RESULT_INT(logLevelFile, logLevelDebug, "file logging is debugging"); TEST_RESULT_BOOL(logTimestamp, false, "timestamp logging is off"); // ------------------------------------------------------------------------------------------------------------------------- cfgInit(); cfgCommandSet(cfgCmdLocal); cfgOptionValidSet(cfgOptLogLevelStderr, true); cfgOptionSet(cfgOptLogLevelStderr, cfgSourceParam, varNewStrZ("info")); TEST_RESULT_VOID(cfgLoadLogSetting(), "load log settings reset stderr"); TEST_RESULT_INT(logLevelStdErr, logLevelError, "stderr logging is error"); } // ***************************************************************************************************************************** if (testBegin("cfgLoadUpdateOption()")) { String *exe = strNew("/path/to/pgbackrest"); String *exeOther = strNew("/other/path/to/pgbackrest"); cfgInit(); cfgCommandSet(cfgCmdBackup); cfgExeSet(exe); cfgOptionValidSet(cfgOptRepoHost, true); cfgOptionValidSet(cfgOptPgHost, true); TEST_RESULT_VOID(cfgLoadUpdateOption(), "hosts are not set so don't update commands"); cfgOptionSet(cfgOptRepoHost, cfgSourceParam, varNewStrZ("repo-host")); TEST_RESULT_VOID(cfgLoadUpdateOption(), "repo remote command is updated"); TEST_RESULT_STR(strPtr(cfgOptionStr(cfgOptRepoHostCmd)), strPtr(exe), " check repo1-host-cmd"); cfgOptionSet(cfgOptRepoHostCmd, cfgSourceParam, varNewStr(exeOther)); TEST_RESULT_VOID(cfgLoadUpdateOption(), "repo remote command was already set"); TEST_RESULT_STR(strPtr(cfgOptionStr(cfgOptRepoHostCmd)), strPtr(exeOther), " check repo1-host-cmd"); cfgOptionSet(cfgOptRepoHost, cfgSourceParam, NULL); // ------------------------------------------------------------------------------------------------------------------------- cfgOptionValidSet(cfgOptPgHostCmd, true); cfgOptionSet(cfgOptPgHost, cfgSourceParam, varNewStrZ("pg1-host")); cfgOptionValidSet(cfgOptPgHost + 1, true); cfgOptionSet(cfgOptPgHost + 1, cfgSourceParam, varNewStrZ("pg2-host")); cfgOptionValidSet(cfgOptPgHostCmd + 1, true); cfgOptionSet(cfgOptPgHostCmd + 1, cfgSourceParam, varNewStr(exeOther)); cfgOptionValidSet(cfgOptPgHost + cfgOptionIndexTotal(cfgOptPgHost) - 1, true); cfgOptionSet(cfgOptPgHost + cfgOptionIndexTotal(cfgOptPgHost) - 1, cfgSourceParam, varNewStrZ("pgX-host")); TEST_RESULT_VOID(cfgLoadUpdateOption(), "pg remote command is updated"); TEST_RESULT_STR(strPtr(cfgOptionStr(cfgOptPgHostCmd)), strPtr(exe), " check pg1-host-cmd"); TEST_RESULT_STR(strPtr(cfgOptionStr(cfgOptPgHostCmd + 1)), strPtr(exeOther), " check pg2-host-cmd is already set"); TEST_RESULT_STR(strPtr(cfgOptionStr(cfgOptPgHostCmd + 2)), NULL, " check pg3-host-cmd is not set"); TEST_RESULT_STR( strPtr(cfgOptionStr(cfgOptPgHostCmd + cfgOptionIndexTotal(cfgOptPgHost) - 1)), strPtr(exe), " check pgX-host-cmd"); // ------------------------------------------------------------------------------------------------------------------------- cfgInit(); cfgOptionValidSet(cfgOptDbTimeout, true); cfgOptionSet(cfgOptDbTimeout, cfgSourceParam, varNewDbl(100)); TEST_RESULT_VOID(cfgLoadUpdateOption(), "pg timeout set but not protocol timeout"); cfgOptionValidSet(cfgOptProtocolTimeout, true); cfgOptionSet(cfgOptProtocolTimeout, cfgSourceDefault, varNewDbl(101)); TEST_RESULT_VOID(cfgLoadUpdateOption(), "protocol timeout > pg timeout"); cfgOptionSet(cfgOptDbTimeout, cfgSourceParam, varNewDbl(100000)); TEST_RESULT_VOID(cfgLoadUpdateOption(), "protocol timeout set automatically"); TEST_RESULT_DOUBLE(cfgOptionDbl(cfgOptProtocolTimeout), 100030, " check protocol timeout"); cfgOptionValidSet(cfgOptProtocolTimeout, true); cfgOptionSet(cfgOptProtocolTimeout, cfgSourceParam, varNewDbl(50.5)); TEST_ERROR( cfgLoadUpdateOption(), OptionInvalidValueError, "'50.5' is not valid for 'protocol-timeout' option\n" "HINT 'protocol-timeout' option (50.5) should be greater than 'db-timeout' option (100000)."); cfgOptionSet(cfgOptProtocolTimeout, cfgSourceParam, varNewDbl(45)); cfgOptionSet(cfgOptDbTimeout, cfgSourceDefault, varNewDbl(3600)); TEST_RESULT_VOID(cfgLoadUpdateOption(), "set default pg timeout to be less than protocol timeout"); TEST_RESULT_DOUBLE(cfgOptionDbl(cfgOptProtocolTimeout), 45, " check protocol timeout"); TEST_RESULT_DOUBLE(cfgOptionDbl(cfgOptDbTimeout), 15, " check db timeout"); cfgOptionSet(cfgOptProtocolTimeout, cfgSourceParam, varNewDbl(11)); cfgOptionSet(cfgOptDbTimeout, cfgSourceDefault, varNewDbl(3600)); TEST_RESULT_VOID(cfgLoadUpdateOption(), "set default pg timeout to be less than test protocol timeout"); TEST_RESULT_DOUBLE(cfgOptionDbl(cfgOptProtocolTimeout), 11, " check protocol timeout"); TEST_RESULT_DOUBLE(cfgOptionDbl(cfgOptDbTimeout), 5.5, " check db timeout"); // ------------------------------------------------------------------------------------------------------------------------- cfgInit(); cfgCommandSet(cfgCmdBackup); cfgExeSet(exe); cfgOptionValidSet(cfgOptPgHost, true); TEST_RESULT_VOID(cfgLoadUpdateOption(), "only repo-host is valid"); cfgOptionValidSet(cfgOptRepoHost, true); cfgOptionSet(cfgOptRepoHost, cfgSourceParam, varNewStrZ("repo-host")); cfgOptionValidSet(cfgOptPgHost + 4, true); cfgOptionSet(cfgOptPgHost + 4, cfgSourceParam, varNewStrZ("pg5-host")); TEST_ERROR(cfgLoadUpdateOption(), ConfigError, "pg and repo hosts cannot both be configured as remote"); // ------------------------------------------------------------------------------------------------------------------------- StringList *argList = strLstNew(); strLstAdd(argList, strNew("pgbackrest")); strLstAdd(argList, strNew("help")); strLstAdd(argList, strNew("backup")); strLstAdd(argList, strNew("process-max")); harnessLogLevelSet(logLevelWarn); TEST_RESULT_VOID(harnessCfgLoad(strLstSize(argList), strLstPtr(argList)), "load help config -- no retention warning"); TEST_RESULT_BOOL(cfgCommandHelp(), true, " command is help"); argList = strLstNew(); strLstAdd(argList, strNew("pgbackrest")); strLstAdd(argList, strNew("--stanza=db")); strLstAdd(argList, strNew("--no-log-timestamp")); strLstAdd(argList, strNew("expire")); harnessLogLevelSet(logLevelWarn); TEST_RESULT_VOID(harnessCfgLoad(strLstSize(argList), strLstPtr(argList)), "load config for retention warning"); harnessLogResult( "P00 WARN: option repo1-retention-full is not set, the repository may run out of space\n" " HINT: to retain full backups indefinitely (without warning), set option" " 'repo1-retention-full' to the maximum."); TEST_RESULT_BOOL(cfgOptionTest(cfgOptRepoRetentionArchive), false, " repo1-retention-archive not set"); strLstAdd(argList, strNew("--repo1-retention-full=1")); TEST_RESULT_VOID(harnessCfgLoad(strLstSize(argList), strLstPtr(argList)), "load config no retention warning"); TEST_RESULT_INT(cfgOptionInt(cfgOptRepoRetentionArchive), 1, " repo1-retention-archive set"); // Munge repo-type for coverage. This will go away when there are multiple repos. cfgOptionSet(cfgOptRepoType, cfgSourceParam, NULL); TEST_RESULT_VOID(cfgLoadUpdateOption(), "load config no repo-type"); argList = strLstNew(); strLstAdd(argList, strNew("pgbackrest")); strLstAdd(argList, strNew("--stanza=db")); strLstAdd(argList, strNew("--no-log-timestamp")); strLstAdd(argList, strNew("--repo1-retention-archive-type=incr")); strLstAdd(argList, strNew("expire")); TEST_RESULT_VOID(harnessCfgLoad(strLstSize(argList), strLstPtr(argList)), "load config for retention warning"); harnessLogResult( "P00 WARN: option repo1-retention-full is not set, the repository may run out of space\n" " HINT: to retain full backups indefinitely (without warning), set option 'repo1-retention-full'" " to the maximum.\n" "P00 WARN: WAL segments will not be expired: option 'repo1-retention-archive-type=incr' but option" " 'repo1-retention-archive' is not set"); TEST_RESULT_BOOL(cfgOptionTest(cfgOptRepoRetentionArchive), false, " repo1-retention-archive not set"); argList = strLstNew(); strLstAdd(argList, strNew("pgbackrest")); strLstAdd(argList, strNew("--stanza=db")); strLstAdd(argList, strNew("--no-log-timestamp")); strLstAdd(argList, strNew("--repo1-retention-archive-type=diff")); strLstAdd(argList, strNew("expire")); TEST_RESULT_VOID(harnessCfgLoad(strLstSize(argList), strLstPtr(argList)), "load config for retention warning"); harnessLogResult( "P00 WARN: option repo1-retention-full is not set, the repository may run out of space\n" " HINT: to retain full backups indefinitely (without warning), set option" " 'repo1-retention-full' to the maximum.\n" "P00 WARN: WAL segments will not be expired: option 'repo1-retention-archive-type=diff' but neither option" " 'repo1-retention-archive' nor option 'repo1-retention-diff' is set"); TEST_RESULT_BOOL(cfgOptionTest(cfgOptRepoRetentionArchive), false, " repo1-retention-archive not set"); strLstAdd(argList, strNew("--repo1-retention-diff=2")); TEST_RESULT_VOID(harnessCfgLoad(strLstSize(argList), strLstPtr(argList)), "load config for retention warning"); harnessLogResult( "P00 WARN: option repo1-retention-full is not set, the repository may run out of space\n" " HINT: to retain full backups indefinitely (without warning), set option" " 'repo1-retention-full' to the maximum."); TEST_RESULT_INT(cfgOptionInt(cfgOptRepoRetentionArchive), 2, " repo1-retention-archive set to retention-diff"); argList = strLstNew(); strLstAdd(argList, strNew("pgbackrest")); strLstAdd(argList, strNew("--stanza=db")); strLstAdd(argList, strNew("--no-log-timestamp")); strLstAdd(argList, strNew("--repo1-retention-archive-type=diff")); strLstAdd(argList, strNew("--repo1-retention-archive=3")); strLstAdd(argList, strNew("--repo1-retention-full=1")); strLstAdd(argList, strNew("expire")); TEST_RESULT_VOID(harnessCfgLoad(strLstSize(argList), strLstPtr(argList)), "load config for retention warning"); harnessLogResult( "P00 WARN: option 'repo1-retention-diff' is not set for 'repo1-retention-archive-type=diff'\n" " HINT: to retain differential backups indefinitely (without warning), set option 'repo1-retention-diff'" " to the maximum."); // ------------------------------------------------------------------------------------------------------------------------- setenv("PGBACKREST_REPO1_S3_KEY", "mykey", true); setenv("PGBACKREST_REPO1_S3_KEY_SECRET", "mysecretkey", true); // Invalid bucket name with verification enabled fails argList = strLstNew(); strLstAdd(argList, strNew("pgbackrest")); strLstAdd(argList, strNew("--stanza=db")); strLstAdd(argList, strNew("--repo1-type=s3")); strLstAdd(argList, strNew("--repo1-s3-bucket=bogus.bucket")); strLstAdd(argList, strNew("--repo1-s3-region=region")); strLstAdd(argList, strNew("--repo1-s3-endpoint=endpoint")); strLstAdd(argList, strNew("--repo1-path=/repo")); strLstAdd(argList, strNew("archive-get")); TEST_ERROR( harnessCfgLoad(strLstSize(argList), strLstPtr(argList)), OptionInvalidValueError, "'bogus.bucket' is not valid for option 'repo1-s3-bucket'" "\nHINT: RFC-2818 forbids dots in wildcard matches" "\nHINT: TLS/SSL verification cannot proceed with this bucket name" "\nHINT: remove dots from the bucket name"); // Invalid bucket name with verification disabled succeeds argList = strLstNew(); strLstAdd(argList, strNew("pgbackrest")); strLstAdd(argList, strNew("--stanza=db")); strLstAdd(argList, strNew("--repo1-type=s3")); strLstAdd(argList, strNew("--repo1-s3-bucket=bogus.bucket")); strLstAdd(argList, strNew("--repo1-s3-region=region")); strLstAdd(argList, strNew("--repo1-s3-endpoint=endpoint")); strLstAdd(argList, strNew("--no-repo1-s3-verify-ssl")); strLstAdd(argList, strNew("--repo1-path=/repo")); strLstAdd(argList, strNew("archive-get")); TEST_RESULT_VOID(harnessCfgLoad(strLstSize(argList), strLstPtr(argList)), "invalid bucket with no verification"); TEST_RESULT_STR(strPtr(cfgOptionStr(cfgOptRepoS3Bucket)), "bogus.bucket", " check bucket value"); // Valid bucket name argList = strLstNew(); strLstAdd(argList, strNew("pgbackrest")); strLstAdd(argList, strNew("--stanza=db")); strLstAdd(argList, strNew("--repo1-type=s3")); strLstAdd(argList, strNew("--repo1-s3-bucket=cool-bucket")); strLstAdd(argList, strNew("--repo1-s3-region=region")); strLstAdd(argList, strNew("--repo1-s3-endpoint=endpoint")); strLstAdd(argList, strNew("--repo1-path=/repo")); strLstAdd(argList, strNew("archive-get")); TEST_RESULT_VOID(harnessCfgLoad(strLstSize(argList), strLstPtr(argList)), "valid bucket name"); TEST_RESULT_STR(strPtr(cfgOptionStr(cfgOptRepoS3Bucket)), "cool-bucket", " check bucket value"); unsetenv("PGBACKREST_REPO1_S3_KEY"); unsetenv("PGBACKREST_REPO1_S3_KEY_SECRET"); } // ***************************************************************************************************************************** if (testBegin("cfgLoadLogFile()")) { cfgInit(); cfgOptionValidSet(cfgOptLogLevelFile, true); cfgOptionSet(cfgOptLogLevelFile, cfgSourceParam, varNewStrZ("detail")); // On the error case is tested here, success is tested in cfgLoad() TEST_RESULT_VOID(cfgLoadLogFile(strNew("/BOGUS")), "attempt to open bogus log file"); TEST_RESULT_STR(strPtr(cfgOptionStr(cfgOptLogLevelFile)), "off", "log-level-file should now be off"); } // ***************************************************************************************************************************** if (testBegin("cfgLoad()")) { // Command does not have umask // ------------------------------------------------------------------------------------------------------------------------- StringList *argList = strLstNew(); strLstAdd(argList, strNew("pgbackrest")); strLstAdd(argList, strNew("info")); TEST_RESULT_VOID(cfgLoad(strLstSize(argList), strLstPtr(argList)), "load config and don't set umask"); // Set a distinct umask value and test that the umask is reset by configLoad since default for neutral-umask=y // ------------------------------------------------------------------------------------------------------------------------- argList = strLstNew(); strLstAdd(argList, strNew("pgbackrest")); strLstAdd(argList, strNew("--stanza=db")); strLstAdd(argList, strNew("--log-level-console=off")); strLstAdd(argList, strNew("--log-level-stderr=off")); strLstAdd(argList, strNew("--log-level-file=off")); strLstAdd(argList, strNew("archive-get")); umask(0111); TEST_RESULT_VOID(cfgLoad(strLstSize(argList), strLstPtr(argList)), "load config for neutral-umask"); TEST_RESULT_INT(umask(0111), 0000, " umask was reset"); // Set a distinct umask value and test that the umask is not reset by configLoad with option --no-neutral-umask // ------------------------------------------------------------------------------------------------------------------------- argList = strLstNew(); strLstAdd(argList, strNew("pgbackrest")); strLstAdd(argList, strNew("--stanza=db")); strLstAdd(argList, strNew("--no-neutral-umask")); strLstAdd(argList, strNew("--log-level-console=off")); strLstAdd(argList, strNew("--log-level-stderr=off")); strLstAdd(argList, strNew("--log-level-file=off")); strLstAdd(argList, strNew("archive-get")); umask(0111); TEST_RESULT_VOID(cfgLoad(strLstSize(argList), strLstPtr(argList)), "load config for no-neutral-umask"); TEST_RESULT_INT(umask(0), 0111, " umask was not reset"); // No command // ------------------------------------------------------------------------------------------------------------------------- argList = strLstNew(); strLstAdd(argList, strNew("pgbackrest")); TEST_RESULT_VOID(cfgLoad(strLstSize(argList), strLstPtr(argList)), "no command"); // Help command only // ------------------------------------------------------------------------------------------------------------------------- argList = strLstNew(); strLstAdd(argList, strNew("pgbackrest")); strLstAdd(argList, strNew("help")); ioBufferSizeSet(333); TEST_RESULT_VOID(cfgLoad(strLstSize(argList), strLstPtr(argList)), "help command"); TEST_RESULT_SIZE(ioBufferSize(), 333, "buffer size not updated by help command"); // Help command for backup // ------------------------------------------------------------------------------------------------------------------------- argList = strLstNew(); strLstAdd(argList, strNew("pgbackrest")); strLstAdd(argList, strNew("help")); strLstAdd(argList, strNew("backup")); strLstAdd(argList, strNew("--log-level-console=off")); strLstAdd(argList, strNew("--log-level-stderr=off")); strLstAdd(argList, strNew("--log-level-file=off")); strLstAdd(argList, strNew("--repo1-retention-full=2")); TEST_RESULT_VOID(cfgLoad(strLstSize(argList), strLstPtr(argList)), "help command for backup"); TEST_RESULT_SIZE(ioBufferSize(), 4 * 1024 * 1024, "buffer size set to option default"); // Command takes lock and opens log file // ------------------------------------------------------------------------------------------------------------------------- struct stat statLog; argList = strLstNew(); strLstAdd(argList, strNew("pgbackrest")); strLstAdd(argList, strNew("--stanza=db")); strLstAdd(argList, strNew("--pg1-path=/path")); strLstAdd(argList, strNew("--repo1-retention-full=1")); strLstAdd(argList, strNewFmt("--log-path=%s", testPath())); strLstAdd(argList, strNew("--log-level-console=off")); strLstAdd(argList, strNew("--log-level-stderr=off")); strLstAdd(argList, strNew("--log-level-file=warn")); strLstAdd(argList, strNew("backup")); TEST_RESULT_VOID(cfgLoad(strLstSize(argList), strLstPtr(argList)), "lock and open log file"); TEST_RESULT_INT(lstat(strPtr(strNewFmt("%s/db-backup.log", testPath())), &statLog), 0, " check log file exists"); // Local command opens log file with special filename // ------------------------------------------------------------------------------------------------------------------------- argList = strLstNew(); strLstAdd(argList, strNew("pgbackrest")); strLstAdd(argList, strNew("--stanza=db")); strLstAdd(argList, strNew("--command=backup")); strLstAdd(argList, strNewFmt("--log-path=%s", testPath())); strLstAdd(argList, strNew("--process=1")); strLstAdd(argList, strNew("--host-id=1")); strLstAdd(argList, strNew("--type=backup")); strLstAdd(argList, strNew("--log-level-file=warn")); strLstAdd(argList, strNew("local")); TEST_RESULT_VOID(cfgLoad(strLstSize(argList), strLstPtr(argList)), "open log file"); TEST_RESULT_INT( lstat(strPtr(strNewFmt("%s/db-backup-local-001.log", testPath())), &statLog), 0, " check log file exists"); // Remote command opens log file with special filename // ------------------------------------------------------------------------------------------------------------------------- argList = strLstNew(); strLstAdd(argList, strNew("pgbackrest")); strLstAdd(argList, strNew("--command=backup")); strLstAdd(argList, strNewFmt("--log-path=%s", testPath())); strLstAdd(argList, strNew("--type=backup")); strLstAdd(argList, strNew("--log-level-file=warn")); strLstAdd(argList, strNew("--process=0")); strLstAdd(argList, strNew("remote")); TEST_RESULT_VOID(cfgLoad(strLstSize(argList), strLstPtr(argList)), "open log file"); TEST_RESULT_INT( lstat(strPtr(strNewFmt("%s/all-backup-remote-000.log", testPath())), &statLog), 0, " check log file exists"); } FUNCTION_HARNESS_RESULT_VOID(); }
/*********************************************************************************************************************************** Get and set the test path, i.e., the path where this test should write its files ***********************************************************************************************************************************/ const char * testPath(void) { FUNCTION_HARNESS_VOID(); FUNCTION_HARNESS_RESULT(STRINGZ, testPathData); }
/*********************************************************************************************************************************** Test Run ***********************************************************************************************************************************/ void testRun(void) { FUNCTION_HARNESS_VOID(); // ***************************************************************************************************************************** if (testBegin("cfgExecParam()")) { StringList *argList = strLstNew(); strLstAddZ(argList, "pgbackrest"); strLstAddZ(argList, "--stanza=test1"); strLstAdd(argList, strNewFmt("--repo1-path=%s/repo", testPath())); strLstAdd(argList, strNewFmt("--pg1-path=%s/db path", testPath())); strLstAddZ(argList, "--log-subprocess"); strLstAddZ(argList, "--no-config"); strLstAddZ(argList, "--reset-neutral-umask"); strLstAddZ(argList, "--repo-cipher-type=aes-256-cbc"); strLstAddZ(argList, "archive-get"); harnessCfgLoad(strLstSize(argList), strLstPtr(argList)); // Set repo1-cipher-pass to make sure it is not passed on the command line cfgOptionValidSet(cfgOptRepoCipherPass, true); cfgOptionSet(cfgOptRepoCipherPass, cfgSourceConfig, varNewStrZ("1234")); TEST_RESULT_STR( strPtr(strLstJoin(cfgExecParam(cfgCmdLocal, NULL), "|")), strPtr( strNewFmt( "--no-config|--log-subprocess|--pg1-path=\"%s/db path\"|--repo1-cipher-type=aes-256-cbc|--repo1-path=%s/repo" "|--stanza=test1|local", testPath(), testPath())), "exec archive-get -> local"); // ------------------------------------------------------------------------------------------------------------------------- argList = strLstNew(); strLstAddZ(argList, "pgbackrest"); strLstAddZ(argList, "--stanza=test1"); strLstAdd(argList, strNewFmt("--repo1-path=%s/repo", testPath())); strLstAdd(argList, strNewFmt("--pg1-path=%s/db", testPath())); strLstAddZ(argList, "--db-include=1"); strLstAddZ(argList, "--db-include=2"); strLstAddZ(argList, "--recovery-option=a=b"); strLstAddZ(argList, "--recovery-option=c=d"); strLstAddZ(argList, "restore"); harnessCfgLoad(strLstSize(argList), strLstPtr(argList)); KeyValue *optionReplace = kvNew(); kvPut(optionReplace, varNewStr(strNew("repo1-path")), varNewStr(strNew("/replace/path"))); kvPut(optionReplace, varNewStr(strNew("stanza")), NULL); TEST_RESULT_STR( strPtr(strLstJoin(cfgExecParam(cfgCmdRestore, optionReplace), "|")), strPtr( strNewFmt( "--db-include=1|--db-include=2|--pg1-path=%s/db|--recovery-option=a=b|--recovery-option=c=d" "|--repo1-path=/replace/path|restore", testPath())), "exec restore -> restore"); } FUNCTION_HARNESS_RESULT_VOID(); }