diff options
Diffstat (limited to 'test')
43 files changed, 6456 insertions, 0 deletions
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..6489fdb --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,90 @@ +cmake_minimum_required(VERSION 2.8.11) +project(MrsWatsonTest) + +include(${cmake_SCRIPTS_DIR}/ConfigureTarget.cmake) + +set(mrswatsontest_SOURCES + MrsWatsonTestMain.c + analysis/AnalysisClipping.c + analysis/AnalysisClippingTest.c + analysis/AnalysisDistortion.c + analysis/AnalysisDistortionTest.c + analysis/AnalysisSilence.c + analysis/AnalysisSilenceTest.c + analysis/AnalyzeFile.c + app/ProgramOptionTest.c + audio/AudioSettingsTest.c + audio/SampleBufferTest.c + base/CharStringTest.c + base/EndianTest.c + base/FileTest.c + base/LinkedListTest.c + base/PlatformInfoTest.c + io/SampleSourceTest.c + midi/MidiSequenceTest.c + midi/MidiSourceTest.c + plugin/PluginChainTest.c + plugin/PluginMock.c + plugin/PluginPresetMock.c + plugin/PluginPresetTest.c + plugin/PluginTest.c + plugin/PluginVst2xIdTest.c + time/AudioClockTest.c + time/TaskTimerTest.c + unit/ApplicationRunner.c + unit/IntegrationTests.c + unit/TestRunner.c + unit/UnitTests.c +) + +set(mrswatsontest_HEADERS + MrsWatsonTestMain.h + analysis/AnalysisClipping.h + analysis/AnalysisDistortion.h + analysis/AnalysisSilence.h + analysis/AnalyzeFile.h + plugin/PluginMock.h + plugin/PluginPresetMock.h + unit/ApplicationRunner.h + unit/TestRunner.h +) + +source_group(analysis ".*/analysis/.*") +source_group(app ".*/app/.*") +source_group(audio ".*/audio/.*") +source_group(base ".*/base/.*") +source_group(io ".*/io/.*") +source_group(midi ".*/midi/.*") +source_group(plugin ".*/plugin/.*") +source_group(time ".*/time/.*") +source_group(unit ".*/unit/.*") + +set(mrswatsontest_LIBS mrswatsoncore) +set(mrswatsontest_64_LIBS mrswatsoncore64) + +if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") + set(mrswatsontest_LIBS ${mrswatsontest_LIBS} dl) + set(mrswatsontest_64_LIBS ${mrswatsontest_64_LIBS} dl) +endif() + +if(WITH_AUDIOFILE) + set(mrswatsontest_LIBS ${mrswatsontest_LIBS} audiofile) + set(mrswatsontest_64_LIBS ${mrswatsontest_64_LIBS} audiofile64) + + if(WITH_FLAC) + set(mrswatsontest_LIBS ${mrswatsontest_LIBS} flac) + set(mrswatsontest_64_LIBS ${mrswatsontest_64_LIBS} flac64) + endif() +endif() + +add_executable(mrswatsontest ${mrswatsontest_SOURCES} ${mrswatsontest_HEADERS}) +target_link_libraries(mrswatsontest ${mrswatsontest_LIBS}) +add_executable(mrswatsontest64 ${mrswatsontest_SOURCES} ${mrswatsontest_HEADERS}) +target_link_libraries(mrswatsontest64 ${mrswatsontest_64_LIBS}) + +configure_target(mrswatsontest 32) +configure_target(mrswatsontest64 64) + +# The main executable must be built to correctly run integration tests +add_dependencies(mrswatsontest mrswatson) +add_dependencies(mrswatsontest64 mrswatson64) diff --git a/test/MrsWatsonTestMain.c b/test/MrsWatsonTestMain.c new file mode 100644 index 0000000..980cbfd --- /dev/null +++ b/test/MrsWatsonTestMain.c @@ -0,0 +1,348 @@ +// +// MrsWatsonTestMain.c +// MrsWatson +// +// Created by Nik Reiman on 8/9/12. +// Copyright (c) 2012 Teragon Audio. All rights reserved. +// + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include "app/ProgramOption.h" +#include "base/File.h" +#include "base/PlatformInfo.h" +#include "unit/ApplicationRunner.h" + +#include "MrsWatsonTestMain.h" + +extern LinkedList getTestSuites(void); +extern TestSuite findTestSuite(LinkedList testSuites, const CharString testSuiteName); +extern TestCase findTestCase(TestSuite testSuite, char *testName); +extern void printUnitTestSuites(void); +extern TestSuite runUnitTests(LinkedList testSuites, boolByte onlyPrintFailing); +extern TestSuite runIntegrationTests(TestEnvironment testEnvironment); + +static const char *DEFAULT_TEST_SUITE_NAME = "all"; + +#if UNIX +static const char *MRSWATSON_EXE_NAME = "mrswatson"; +#elif WINDOWS +static const char *MRSWATSON_EXE_NAME = "mrswatson.exe"; +#else +static const char *MRSWATSON_EXE_NAME = "mrswatson"; +#endif + +static ProgramOptions _newTestProgramOptions(void) +{ + ProgramOptions programOptions = newProgramOptions(NUM_TEST_OPTIONS); + srand((unsigned int)time(NULL)); + + programOptionsAdd(programOptions, newProgramOptionWithName(OPTION_TEST_SUITE, "suite", + "Choose a test suite to run. Current suites include:\n\ +\t- Integration: run audio quality tests against actual executable\n\ +\t- Unit: run all internal unit tests\n\ +\t- All: run all tests (default)\n\ +\t- A suite name (use '--list' to see all suite names)", + true, kProgramOptionTypeString, kProgramOptionArgumentTypeRequired)); + programOptionsSetCString(programOptions, OPTION_TEST_SUITE, DEFAULT_TEST_SUITE_NAME); + + programOptionsAdd(programOptions, newProgramOptionWithName(OPTION_TEST_NAME, "test", + "Run a single test. Tests are named 'Suite:Name', for example:\n\ +\t-t 'LinkedList:AppendItem'", + true, kProgramOptionTypeString, kProgramOptionArgumentTypeRequired)); + + programOptionsAdd(programOptions, newProgramOptionWithName(OPTION_TEST_PRINT_TESTS, "list-tests", + "List all unit tests in the same format required by --test", + true, kProgramOptionTypeEmpty, kProgramOptionArgumentTypeNone)); + + programOptionsAdd(programOptions, newProgramOptionWithName(OPTION_TEST_MRSWATSON_PATH, "mrswatson-path", + "Path to mrswatson executable. By default, mrswatson is assumed to be in the same \ +directory as mrswatsontest. Only required for running integration tests.", + true, kProgramOptionTypeString, kProgramOptionArgumentTypeRequired)); + + programOptionsAdd(programOptions, newProgramOptionWithName(OPTION_TEST_RESOURCES_PATH, "resources", + "Path to resources directory. Only required for running integration tests.", + true, kProgramOptionTypeString, kProgramOptionArgumentTypeRequired)); + + programOptionsAdd(programOptions, newProgramOptionWithName(OPTION_TEST_PRINT_ONLY_FAILING, "quiet", + "Print only failing tests. Note that if a test causes the suite to crash, the \ +bad test's name will not be printed. In this case, re-run without this option, as \ +the test names will be printed before the tests are executed.", + true, kProgramOptionTypeEmpty, kProgramOptionArgumentTypeNone)); + + programOptionsAdd(programOptions, newProgramOptionWithName(OPTION_TEST_KEEP_FILES, "keep-files", + "Keep files generated by integration tests (such as log files, audio output, \ +etc.). Normally these files are automatically removed if a test succeeds.", + true, kProgramOptionTypeEmpty, kProgramOptionArgumentTypeNone)); + + programOptionsAdd(programOptions, newProgramOptionWithName(OPTION_TEST_HELP, "help", + "Print full program help (this screen), or just the help for a single argument.", + true, kProgramOptionTypeString, kProgramOptionArgumentTypeOptional)); + + programOptionsAdd(programOptions, newProgramOptionWithName(OPTION_TEST_VERBOSE, "verbose", + "Show logging output from tests", + true, kProgramOptionTypeEmpty, kProgramOptionArgumentTypeNone)); + + return programOptions; +} + +void _printTestSummary(int testsRun, int testsPassed, int testsFailed, int testsSkipped) +{ + CharString numberBuffer = newCharStringWithCapacity(kCharStringLengthShort); + + printToLog(getLogColor(kTestLogEventReset), NULL, "Ran "); + sprintf(numberBuffer->data, "%d", testsRun); + printToLog(getLogColor(kTestLogEventSection), NULL, numberBuffer->data); + printToLog(getLogColor(kTestLogEventReset), NULL, " tests: "); + sprintf(numberBuffer->data, "%d", testsPassed); + printToLog(getLogColor(kTestLogEventPass), NULL, numberBuffer->data); + printToLog(getLogColor(kTestLogEventReset), NULL, " passed, "); + + sprintf(numberBuffer->data, "%d", testsFailed); + + if (testsFailed > 0) { + printToLog(getLogColor(kTestLogEventFail), NULL, numberBuffer->data); + } else { + printToLog(getLogColor(kTestLogEventReset), NULL, numberBuffer->data); + } + + printToLog(getLogColor(kTestLogEventReset), NULL, " failed, "); + + sprintf(numberBuffer->data, "%d", testsSkipped); + + if (testsSkipped > 0) { + printToLog(getLogColor(kTestLogEventSkip), NULL, numberBuffer->data); + } else { + printToLog(getLogColor(kTestLogEventReset), NULL, numberBuffer->data); + } + + printToLog(getLogColor(kTestLogEventReset), NULL, " skipped"); + flushLog(NULL); + + freeCharString(numberBuffer); +} + +File _findMrsWatsonExe(CharString mrsWatsonExeArg) +{ + CharString currentExecutableFilename = NULL; + CharString mrsWatsonExeName = NULL; + File currentExecutablePath = NULL; + File currentExecutableDir = NULL; + File mrsWatsonExe = NULL; + + if (mrsWatsonExeArg != NULL && !charStringIsEmpty(mrsWatsonExeArg)) { + mrsWatsonExe = newFileWithPath(mrsWatsonExeArg); + } else { + currentExecutableFilename = fileGetExecutablePath(); + currentExecutablePath = newFileWithPath(currentExecutableFilename); + + if (currentExecutablePath != NULL) { + currentExecutableDir = fileGetParent(currentExecutablePath); + + if (currentExecutableDir != NULL) { + mrsWatsonExeName = newCharStringWithCString(MRSWATSON_EXE_NAME); + + if (platformInfoIsRuntime64Bit()) { + charStringAppendCString(mrsWatsonExeName, "64"); + } + + mrsWatsonExe = newFileWithParent(currentExecutableDir, mrsWatsonExeName); + } + } + } + + freeCharString(currentExecutableFilename); + freeCharString(mrsWatsonExeName); + freeFile(currentExecutablePath); + freeFile(currentExecutableDir); + return mrsWatsonExe; +} + +int main(int argc, char *argv[]) +{ + ProgramOptions programOptions; + int totalTestsRun = 0; + int totalTestsPassed = 0; + int totalTestsFailed = 0; + int totalTestsSkipped = 0; + CharString testSuiteToRun = NULL; + CharString testSuiteName = NULL; + CharString mrsWatsonExeName = NULL; + CharString totalTimeString = NULL; + CharString executablePath = NULL; + File mrsWatsonExe = NULL; + File resourcesPath = NULL; + boolByte shouldRunUnitTests = false; + boolByte shouldRunIntegrationTests = false; + TestCase testCase = NULL; + TestSuite testSuite = NULL; + LinkedList testSuites = NULL; + TestSuite unitTestResults = NULL; + TestEnvironment testEnvironment = NULL; + TaskTimer timer; + char *testArgument; + char *colon; + char *testCaseName; + + timer = newTaskTimer(NULL, NULL); + taskTimerStart(timer); + + programOptions = _newTestProgramOptions(); + + if (!programOptionsParseArgs(programOptions, argc, argv)) { + printf("Or run with --help (option) to see help for a single option\n"); + return -1; + } + + if (programOptions->options[OPTION_TEST_HELP]->enabled) { + printf("Run with '--help full' to see extended help for all options.\n"); + + if (charStringIsEmpty(programOptionsGetString(programOptions, OPTION_TEST_HELP))) { + printf("All options, where <argument> is required and [argument] is optional\n"); + programOptionsPrintHelp(programOptions, false, DEFAULT_INDENT_SIZE); + } else { + programOptionsPrintHelp(programOptions, true, DEFAULT_INDENT_SIZE); + } + + return -1; + } else if (programOptions->options[OPTION_TEST_PRINT_TESTS]->enabled) { + printUnitTestSuites(); + return -1; + } + + if (programOptions->options[OPTION_TEST_VERBOSE]->enabled) { + initEventLogger(); + setLogLevel(LOG_DEBUG); + } + + testSuiteToRun = programOptionsGetString(programOptions, OPTION_TEST_SUITE); + + if (programOptions->options[OPTION_TEST_NAME]->enabled) { + shouldRunUnitTests = false; + shouldRunIntegrationTests = false; + + testArgument = programOptionsGetString(programOptions, OPTION_TEST_NAME)->data; + colon = strchr(testArgument, ':'); + + if (colon == NULL) { + printf("ERROR: Invalid test name"); + programOptionPrintHelp(programOptions->options[OPTION_TEST_NAME], true, DEFAULT_INDENT_SIZE, 0); + return -1; + } + + testCaseName = strdup(colon + 1); + *colon = '\0'; + + testSuiteName = programOptionsGetString(programOptions, OPTION_TEST_NAME); + testSuites = getTestSuites(); + testSuite = findTestSuite(testSuites, testSuiteName); + + if (testSuite == NULL) { + printf("ERROR: Could not find test suite '%s'\n", testSuiteName->data); + freeLinkedListAndItems(testSuites, (LinkedListFreeItemFunc)freeTestSuite); + return -1; + } + + testCase = findTestCase(testSuite, testCaseName); + + if (testCase == NULL) { + printf("ERROR: Could not find test case '%s'\n", testCaseName); + freeLinkedListAndItems(testSuites, (LinkedListFreeItemFunc)freeTestSuite); + return -1; + } else { + printf("Running test in %s:\n", testSuite->name); + runTestCase(testCase, testSuite); + freeLinkedListAndItems(testSuites, (LinkedListFreeItemFunc)freeTestSuite); + } + } else if (charStringIsEqualToCString(testSuiteToRun, "all", true)) { + shouldRunUnitTests = true; + shouldRunIntegrationTests = true; + } else if (charStringIsEqualToCString(testSuiteToRun, "unit", true)) { + shouldRunUnitTests = true; + } else if (charStringIsEqualToCString(testSuiteToRun, "integration", true)) { + shouldRunIntegrationTests = true; + } else { + testSuites = getTestSuites(); + testSuite = findTestSuite(testSuites, testSuiteToRun); + + if (testSuite == NULL) { + printf("ERROR: Invalid test suite '%s'\n", testSuiteToRun->data); + printf("Run with '--list' suite to show possible test suites\n"); + freeLinkedListAndItems(testSuites, (LinkedListFreeItemFunc)freeTestSuite); + return -1; + } else { + testSuite->onlyPrintFailing = programOptions->options[OPTION_TEST_PRINT_ONLY_FAILING]->enabled; + runTestSuite(testSuite, NULL); + totalTestsRun = testSuite->numSuccess + testSuite->numFail; + totalTestsPassed = testSuite->numSuccess; + totalTestsFailed = testSuite->numFail; + totalTestsSkipped = testSuite->numSkips; + freeLinkedListAndItems(testSuites, (LinkedListFreeItemFunc)freeTestSuite); + } + } + + if (shouldRunUnitTests) { + printf("=== Unit tests ===\n"); + testSuites = getTestSuites(); + unitTestResults = runUnitTests(testSuites, + programOptions->options[OPTION_TEST_PRINT_ONLY_FAILING]->enabled); + + totalTestsRun += unitTestResults->numSuccess + unitTestResults->numFail; + totalTestsPassed += unitTestResults->numSuccess; + totalTestsFailed += unitTestResults->numFail; + totalTestsSkipped += unitTestResults->numSkips; + + freeLinkedListAndItems(testSuites, (LinkedListFreeItemFunc)freeTestSuite); + freeTestSuite(unitTestResults); + } + + mrsWatsonExe = _findMrsWatsonExe(programOptionsGetString(programOptions, OPTION_TEST_MRSWATSON_PATH)); + + if (shouldRunIntegrationTests && mrsWatsonExe == NULL) { + printf("Could not find mrswatson, skipping integration tests\n"); + shouldRunIntegrationTests = false; + } + + if (programOptions->options[OPTION_TEST_RESOURCES_PATH]->enabled) { + resourcesPath = newFileWithPath(programOptionsGetString(programOptions, OPTION_TEST_RESOURCES_PATH)); + } + + if (shouldRunIntegrationTests && !fileExists(resourcesPath)) { + printf("Could not find test resources, skipping integration tests\n"); + shouldRunIntegrationTests = false; + } + + if (shouldRunIntegrationTests) { + printf("\n=== Integration tests ===\n"); + testEnvironment = newTestEnvironment(mrsWatsonExe->absolutePath->data, resourcesPath->absolutePath->data); + testEnvironment->results->onlyPrintFailing = programOptions->options[OPTION_TEST_PRINT_ONLY_FAILING]->enabled; + testEnvironment->results->keepFiles = programOptions->options[OPTION_TEST_KEEP_FILES]->enabled; + runIntegrationTests(testEnvironment); + totalTestsRun += testEnvironment->results->numSuccess + testEnvironment->results->numFail; + totalTestsPassed += testEnvironment->results->numSuccess; + totalTestsFailed += testEnvironment->results->numFail; + totalTestsSkipped += testEnvironment->results->numSkips; + } + + taskTimerStop(timer); + + if (totalTestsRun > 0) { + printf("\n=== Finished ===\n"); + _printTestSummary(totalTestsRun, totalTestsPassed, totalTestsFailed, totalTestsSkipped); + totalTimeString = taskTimerHumanReadbleString(timer); + printf("Total time: %s\n", totalTimeString->data); + } + + freeTestEnvironment(testEnvironment); + freeProgramOptions(programOptions); + freeCharString(executablePath); + freeCharString(mrsWatsonExeName); + freeCharString(totalTimeString); + freeFile(mrsWatsonExe); + freeFile(resourcesPath); + freeTaskTimer(timer); + return totalTestsFailed; +} diff --git a/test/MrsWatsonTestMain.h b/test/MrsWatsonTestMain.h new file mode 100644 index 0000000..8d8e94e --- /dev/null +++ b/test/MrsWatsonTestMain.h @@ -0,0 +1,20 @@ +// +// TestSuite.h +// MrsWatson +// +// Created by Nik Reiman on 8/9/12. +// Copyright (c) 2012 Teragon Audio. All rights reserved. +// + +typedef enum { + OPTION_TEST_SUITE, + OPTION_TEST_NAME, + OPTION_TEST_PRINT_TESTS, + OPTION_TEST_MRSWATSON_PATH, + OPTION_TEST_RESOURCES_PATH, + OPTION_TEST_PRINT_ONLY_FAILING, + OPTION_TEST_KEEP_FILES, + OPTION_TEST_HELP, + OPTION_TEST_VERBOSE, + NUM_TEST_OPTIONS +} TestProgramOptionIndex; diff --git a/test/analysis/AnalysisClipping.c b/test/analysis/AnalysisClipping.c new file mode 100644 index 0000000..55229ae --- /dev/null +++ b/test/analysis/AnalysisClipping.c @@ -0,0 +1,25 @@ +#include <math.h> +#include "AnalysisClipping.h" + +boolByte analysisClipping(const SampleBuffer sampleBuffer, AnalysisFunctionData data) +{ + for (ChannelCount i = 0; i < sampleBuffer->numChannels; i++) { + for (SampleCount j = 0; j < sampleBuffer->blocksize; j++) { + if (fabs(sampleBuffer->samples[i][j]) >= 1.0f) { + if (data->consecutiveFailCounter > data->failTolerance) { + data->failedChannel = i; + data->failedSample = j; + return false; + } else { + data->consecutiveFailCounter++; + } + } else { + if (data->consecutiveFailCounter > 0) { + data->consecutiveFailCounter--; + } + } + } + } + + return true; +} diff --git a/test/analysis/AnalysisClipping.h b/test/analysis/AnalysisClipping.h new file mode 100644 index 0000000..6dc56ef --- /dev/null +++ b/test/analysis/AnalysisClipping.h @@ -0,0 +1,3 @@ +#include "AnalyzeFile.h" + +boolByte analysisClipping(const SampleBuffer sampleBuffer, AnalysisFunctionData data); diff --git a/test/analysis/AnalysisClippingTest.c b/test/analysis/AnalysisClippingTest.c new file mode 100644 index 0000000..bd062a3 --- /dev/null +++ b/test/analysis/AnalysisClippingTest.c @@ -0,0 +1,37 @@ +#include "unit/TestRunner.h" +#include "AnalysisClipping.h" + +static int _testAnalysisClipping(void) +{ + SampleBuffer s = newSampleBuffer(1, 128); + AnalysisFunctionData d = newAnalysisFunctionData(); + unsigned long i; + + for (i = 0; i < s->blocksize; i++) { + s->samples[0][i] = 1.0f; + } + + assertFalse(analysisClipping(s, d)); + freeAnalysisFunctionData(d); + freeSampleBuffer(s); + return 0; +} + +static int _testAnalysisNotClipping(void) +{ + SampleBuffer s = newSampleBuffer(1, 8); + AnalysisFunctionData d = newAnalysisFunctionData(); + assert(analysisClipping(s, d)); + freeAnalysisFunctionData(d); + freeSampleBuffer(s); + return 0; +} + +TestSuite addAnalysisClippingTests(void); +TestSuite addAnalysisClippingTests(void) +{ + TestSuite testSuite = newTestSuite("AnalysisClipping", NULL, NULL); + addTest(testSuite, "AnalysisClipping", _testAnalysisClipping); + addTest(testSuite, "AnalysisNotClipping", _testAnalysisNotClipping); + return testSuite; +} diff --git a/test/analysis/AnalysisDistortion.c b/test/analysis/AnalysisDistortion.c new file mode 100644 index 0000000..27b8492 --- /dev/null +++ b/test/analysis/AnalysisDistortion.c @@ -0,0 +1,33 @@ +#include <stdlib.h> +#include "AnalysisDistortion.h" + +// If two samples differ by more than this amount, then we call it distortion +static const Sample kAnalysisDistortionTolerance = 0.5f; + +boolByte analysisDistortion(const SampleBuffer sampleBuffer, AnalysisFunctionData data) +{ + Sample difference; + + for (ChannelCount channelIndex = 0; channelIndex < sampleBuffer->numChannels; channelIndex++) { + for (SampleCount sampleIndex = 0; sampleIndex < sampleBuffer->blocksize; sampleIndex++) { + if (sampleBuffer->samples[channelIndex][sampleIndex] > data->lastSample[channelIndex]) { + difference = sampleBuffer->samples[channelIndex][sampleIndex] - data->lastSample[channelIndex]; + } else { + difference = data->lastSample[channelIndex] - sampleBuffer->samples[channelIndex][sampleIndex]; + } + + if (difference >= kAnalysisDistortionTolerance) { + // In this test, we don't care about the consecutive sample count. That is because + // we also want to detect harsh clicks which occur by a jump in the amplitude, which + // is a common error in many plugins. + data->failedChannel = channelIndex; + data->failedSample = sampleIndex; + return false; + } + + data->lastSample[channelIndex] = sampleBuffer->samples[channelIndex][sampleIndex]; + } + } + + return true; +} diff --git a/test/analysis/AnalysisDistortion.h b/test/analysis/AnalysisDistortion.h new file mode 100644 index 0000000..a517777 --- /dev/null +++ b/test/analysis/AnalysisDistortion.h @@ -0,0 +1,3 @@ +#include "AnalyzeFile.h" + +boolByte analysisDistortion(const SampleBuffer sampleBuffer, AnalysisFunctionData data); diff --git a/test/analysis/AnalysisDistortionTest.c b/test/analysis/AnalysisDistortionTest.c new file mode 100644 index 0000000..cb9441c --- /dev/null +++ b/test/analysis/AnalysisDistortionTest.c @@ -0,0 +1,37 @@ +#include "AnalysisDistortion.h" +#include "unit/TestRunner.h" + +static int _testAnalysisDistortion(void) +{ + SampleBuffer s = newSampleBuffer(1, 8); + AnalysisFunctionData d = newAnalysisFunctionData(); + unsigned int i; + + for (i = 0; i < s->blocksize; i++) { + s->samples[0][i] = 0.9f * (i % 2 ? 1.0f : -1.0f); + } + + assertFalse(analysisDistortion(s, d)); + freeAnalysisFunctionData(d); + freeSampleBuffer(s); + return 0; +} + +static int _testAnalysisNotDistortion(void) +{ + SampleBuffer s = newSampleBuffer(1, 8); + AnalysisFunctionData d = newAnalysisFunctionData(); + assert(analysisDistortion(s, d)); + freeAnalysisFunctionData(d); + freeSampleBuffer(s); + return 0; +} + +TestSuite addAnalysisDistortionTests(void); +TestSuite addAnalysisDistortionTests(void) +{ + TestSuite testSuite = newTestSuite("AnalysisDistortion", NULL, NULL); + addTest(testSuite, "AnalysisDistortion", _testAnalysisDistortion); + addTest(testSuite, "AnalysisNotDistortion", _testAnalysisNotDistortion); + return testSuite; +} diff --git a/test/analysis/AnalysisSilence.c b/test/analysis/AnalysisSilence.c new file mode 100644 index 0000000..0876245 --- /dev/null +++ b/test/analysis/AnalysisSilence.c @@ -0,0 +1,25 @@ +#include "AnalysisSilence.h" +#include "AnalyzeFile.h" + +boolByte analysisSilence(const SampleBuffer sampleBuffer, AnalysisFunctionData data) +{ + for (ChannelCount i = 0; i < sampleBuffer->numChannels; ++i) { + for (SampleCount j = 0; j < sampleBuffer->blocksize; ++j) { + if (sampleBuffer->samples[i][j] == 0.0f) { + data->consecutiveFailCounter++; + + if (data->consecutiveFailCounter > data->failTolerance) { + data->failedChannel = i; + data->failedSample = j; + return false; + } + } else { + if (data->consecutiveFailCounter > 0) { + data->consecutiveFailCounter = 0; + } + } + } + } + + return true; +} diff --git a/test/analysis/AnalysisSilence.h b/test/analysis/AnalysisSilence.h new file mode 100644 index 0000000..4e56c6c --- /dev/null +++ b/test/analysis/AnalysisSilence.h @@ -0,0 +1,3 @@ +#include "AnalyzeFile.h" + +boolByte analysisSilence(const SampleBuffer sampleBuffer, AnalysisFunctionData data); diff --git a/test/analysis/AnalysisSilenceTest.c b/test/analysis/AnalysisSilenceTest.c new file mode 100644 index 0000000..968bc37 --- /dev/null +++ b/test/analysis/AnalysisSilenceTest.c @@ -0,0 +1,57 @@ +#include "AnalysisSilence.h" +#include "unit/TestRunner.h" + +static int _testAnalysisSilence(void) +{ + SampleBuffer s = newSampleBuffer(1, 64); + AnalysisFunctionData d = newAnalysisFunctionData(); + assertFalse(analysisSilence(s, d)); + freeAnalysisFunctionData(d); + freeSampleBuffer(s); + return 0; +} + +static int _testAnalysisNotSilence(void) +{ + SampleBuffer s = newSampleBuffer(2, 64); + AnalysisFunctionData d = newAnalysisFunctionData(); + unsigned long i; + unsigned int j; + + for (i = 0; i < s->blocksize; i++) { + for (j = 0; j < s->numChannels; j++) { + s->samples[j][i] = 32767.0; + } + } + + assert(analysisSilence(s, d)); + freeAnalysisFunctionData(d); + freeSampleBuffer(s); + return 0; +} + +static int _testAnalysisNotSilenceInOneChannel(void) +{ + SampleBuffer s = newSampleBuffer(1, 64); + AnalysisFunctionData d = newAnalysisFunctionData(); + unsigned long i; + + for (i = 0; i < s->blocksize; i++) { + s->samples[0][i] = 32767.0; + } + + assert(analysisSilence(s, d)); + freeAnalysisFunctionData(d); + freeSampleBuffer(s); + return 0; +} + +TestSuite addAnalysisSilenceTests(void); +TestSuite addAnalysisSilenceTests(void) +{ + TestSuite testSuite = newTestSuite("AnalysisSilence", NULL, NULL); + addTest(testSuite, "AnalysisSilence", _testAnalysisSilence); + addTest(testSuite, "AnalysisNotSilence", _testAnalysisNotSilence); + addTest(testSuite, "AnalysisNotSilenceInOneChannel", _testAnalysisNotSilenceInOneChannel); + return testSuite; +} diff --git a/test/analysis/AnalyzeFile.c b/test/analysis/AnalyzeFile.c new file mode 100644 index 0000000..11e020f --- /dev/null +++ b/test/analysis/AnalyzeFile.c @@ -0,0 +1,123 @@ +#include <stdlib.h> +#include "audio/AudioSettings.h" +#include "io/SampleSource.h" +#include "AnalysisClipping.h" +#include "AnalysisDistortion.h" +#include "AnalysisSilence.h" + +// Number of consecutive samples which need to fail in order for the test to fail +static const int kAnalysisDefaultFailTolerance = 16; +// Use a blocksize of the default * 2 in order to avoid false positives of the +// silence detection algorithm, since the last block is likely to be silent. +static const int kAnalysisBlocksize = DEFAULT_BLOCKSIZE * 2; + +static LinkedList _getAnalysisFunctions(void) +{ + AnalysisFunctionData data; + LinkedList functionsList = newLinkedList(); + + data = newAnalysisFunctionData(); + data->analysisName = "clipping"; + data->functionPtr = (void *)analysisClipping; + linkedListAppend(functionsList, data); + + data = newAnalysisFunctionData(); + data->analysisName = "distortion"; + data->functionPtr = (void *)analysisDistortion; + linkedListAppend(functionsList, data); + + data = newAnalysisFunctionData(); + data->analysisName = "silence"; + data->functionPtr = (void *)analysisSilence; + data->failTolerance = kAnalysisBlocksize; + linkedListAppend(functionsList, data); + + return functionsList; +} + +static void _runAnalysisFunction(void *item, void *userData) +{ + AnalysisFunctionData functionData = (AnalysisFunctionData)item; + AnalysisFuncPtr analysisFuncPtr = (AnalysisFuncPtr)(functionData->functionPtr); + AnalysisData analysisData = (AnalysisData)userData; + + if (!analysisFuncPtr(analysisData->sampleBuffer, functionData)) { + charStringCopyCString(analysisData->failedAnalysisFunctionName, functionData->analysisName); + *(analysisData->failedAnalysisFrame) = *(analysisData->currentFrame) + functionData->failedSample; + *(analysisData->failedAnalysisChannel) = functionData->failedChannel; + *(analysisData->result) = false; + } +} + +boolByte analyzeFile(const char *filename, CharString failedAnalysisFunctionName, + ChannelCount *failedAnalysisChannel, SampleCount *failedAnalysisFrame) +{ + boolByte result; + CharString analysisFilename; + SampleSource sampleSource; + LinkedList analysisFunctions; + AnalysisData analysisData = (AnalysisData)malloc(sizeof(AnalysisDataMembers)); + SampleCount currentFrame = 0; + + // Needed to initialize new sample sources + initAudioSettings(); + analysisFunctions = _getAnalysisFunctions(); + analysisFilename = newCharStringWithCString(filename); + sampleSource = sampleSourceFactory(analysisFilename); + + if (sampleSource == NULL) { + freeCharString(analysisFilename); + free(analysisData); + freeAudioSettings(); + return false; + } + + result = sampleSource->openSampleSource(sampleSource, SAMPLE_SOURCE_OPEN_READ); + + if (!result) { + free(analysisData); + return result; + } + + analysisData->failedAnalysisFunctionName = failedAnalysisFunctionName; + analysisData->failedAnalysisChannel = failedAnalysisChannel; + analysisData->failedAnalysisFrame = failedAnalysisFrame; + analysisData->sampleBuffer = newSampleBuffer(DEFAULT_NUM_CHANNELS, kAnalysisBlocksize); + analysisData->currentFrame = ¤tFrame; + analysisData->result = &result; + + while (sampleSource->readSampleBlock(sampleSource, analysisData->sampleBuffer) && result) { + linkedListForeach(analysisFunctions, _runAnalysisFunction, analysisData); + currentFrame += kAnalysisBlocksize; + } + + freeSampleSource(sampleSource); + freeCharString(analysisFilename); + freeAudioSettings(); + freeSampleBuffer(analysisData->sampleBuffer); + freeLinkedListAndItems(analysisFunctions, (LinkedListFreeItemFunc)freeAnalysisFunctionData); + free(analysisData); + return result; +} + +void freeAnalysisFunctionData(AnalysisFunctionData self) +{ + free(self->lastSample); + free(self); +} + +AnalysisFunctionData newAnalysisFunctionData(void) +{ + AnalysisFunctionData result = (AnalysisFunctionData)malloc(sizeof(AnalysisFunctionDataMembers)); + result->analysisName = NULL; + result->consecutiveFailCounter = 0; + result->failedSample = 0; + result->functionPtr = NULL; + // TODO: Should use max channels, when we get that + result->lastSample = (Sample*)malloc(sizeof(Sample) * 2); + result->lastSample[0] = 0.0f; + result->lastSample[1] = 0.0f; + result->failTolerance = kAnalysisDefaultFailTolerance; + return result; +} + diff --git a/test/analysis/AnalyzeFile.h b/test/analysis/AnalyzeFile.h new file mode 100644 index 0000000..d47b3ba --- /dev/null +++ b/test/analysis/AnalyzeFile.h @@ -0,0 +1,37 @@ +#include <stdio.h> +#include "base/Types.h" +#include "audio/SampleBuffer.h" +#include "base/CharString.h" + +#ifndef MrsWatson_AnalyzeFile_h +#define MrsWatson_AnalyzeFile_h + +typedef struct { + const char *analysisName; + void *functionPtr; + int consecutiveFailCounter; + Sample *lastSample; + SampleCount failedSample; + ChannelCount failedChannel; + int failTolerance; +} AnalysisFunctionDataMembers; +typedef AnalysisFunctionDataMembers *AnalysisFunctionData; +typedef boolByte (*AnalysisFuncPtr)(const SampleBuffer sampleBuffer, AnalysisFunctionData data); + +typedef struct { + CharString failedAnalysisFunctionName; + SampleCount *failedAnalysisFrame; + ChannelCount *failedAnalysisChannel; + SampleCount *currentFrame; + boolByte *result; + SampleBuffer sampleBuffer; + AnalysisFunctionData functionData; +} AnalysisDataMembers; +typedef AnalysisDataMembers *AnalysisData; + +AnalysisFunctionData newAnalysisFunctionData(void); +boolByte analyzeFile(const char *filename, CharString failedAnalysisFunctionName, + ChannelCount *failedAnalysisChannel, SampleCount *failedAnalysisFrame); +void freeAnalysisFunctionData(AnalysisFunctionData self); + +#endif diff --git a/test/app/ProgramOptionTest.c b/test/app/ProgramOptionTest.c new file mode 100644 index 0000000..1361349 --- /dev/null +++ b/test/app/ProgramOptionTest.c @@ -0,0 +1,505 @@ +#include "unit/TestRunner.h" +#include "app/ProgramOption.h" +#include "base/File.h" + +#if UNIX +#define TEST_CONFIG_FILE "/tmp/mrswatsontest-config.txt" +#elif WINDOWS +#define TEST_CONFIG_FILE "C:\\Temp\\mrswatsontest-config.txt" +#else +#define TEST_CONFIG_FILE "mrswatsontest-config.txt" +#endif + +static void _programOptionTeardown(void) +{ + CharString configFilePath = newCharStringWithCString(TEST_CONFIG_FILE); + File configFile = newFileWithPath(configFilePath); + + if (fileExists(configFile)) { + fileRemove(configFile); + } + + freeCharString(configFilePath); + freeFile(configFile); +} + +static ProgramOption _getTestOption(void) +{ + return newProgramOptionWithName(0, "test", "test help", true, + kProgramOptionTypeNumber, kProgramOptionArgumentTypeOptional); +} + +static int _testNewProgramOptions(void) +{ + ProgramOptions p = newProgramOptions(4); + assertIntEquals(4, p->numOptions); + freeProgramOptions(p); + return 0; +} + +static int _testAddNewProgramOption(void) +{ + ProgramOptions p = newProgramOptions(1); + ProgramOption o; + assert(programOptionsAdd(p, _getTestOption())); + assertIntEquals(1, p->numOptions); + o = p->options[0]; + assertNotNull(o); + assertCharStringEquals("test", o->name); + freeProgramOptions(p); + return 0; +} + +static int _testAddNullProgramOption(void) +{ + ProgramOptions p = newProgramOptions(1); + assertFalse(programOptionsAdd(p, NULL)); + freeProgramOptions(p); + return 0; +} + +static int _testAddNewProgramOptionOutsideRange(void) +{ + ProgramOptions p = newProgramOptions(1); + ProgramOption o = _getTestOption(); + o->index++; + assertFalse(programOptionsAdd(p, o)); + assertIntEquals(1, p->numOptions); + freeProgramOption(o); + freeProgramOptions(p); + return 0; +} + +static int _testParseCommandLineShortOption(void) +{ + ProgramOptions p = newProgramOptions(1); + char *argv[2]; + argv[0] = "exe"; + argv[1] = "-t"; + assert(programOptionsAdd(p, _getTestOption())); + assertFalse(p->options[0]->enabled); + assert(programOptionsParseArgs(p, 2, argv)); + assert(p->options[0]->enabled); + freeProgramOptions(p); + return 0; +} + +static int _testParseCommandLineLongOption(void) +{ + ProgramOptions p = newProgramOptions(1); + char *argv[2]; + argv[0] = "exe"; + argv[1] = "--test"; + assert(programOptionsAdd(p, _getTestOption())); + assertFalse(p->options[0]->enabled); + assert(programOptionsParseArgs(p, 2, argv)); + assert(p->options[0]->enabled); + freeProgramOptions(p); + return 0; +} + +static int _testParseCommandLineInvalidOption(void) +{ + ProgramOptions p = newProgramOptions(1); + char *argv[2]; + argv[0] = "exe"; + argv[1] = "invalid"; + assert(programOptionsAdd(p, _getTestOption())); + assertFalse(p->options[0]->enabled); + assertFalse(programOptionsParseArgs(p, 2, argv)); + assertFalse(p->options[0]->enabled); + freeProgramOptions(p); + return 0; +} + +static int _testParseCommandLineRequiredOption(void) +{ + ProgramOptions p = newProgramOptions(1); + ProgramOption o = _getTestOption(); + char *argv[3]; + argv[0] = "exe"; + argv[1] = "--test"; + argv[2] = "1.23"; + o->argumentType = kProgramOptionArgumentTypeRequired; + assert(programOptionsAdd(p, o)); + + assertFalse(p->options[0]->enabled); + assertDoubleEquals(0.0, programOptionsGetNumber(p, 0), 0.0f); + assert(programOptionsParseArgs(p, 3, argv)); + assertDoubleEquals(1.23, programOptionsGetNumber(p, 0), TEST_DEFAULT_TOLERANCE); + assert(p->options[0]->enabled); + + freeProgramOptions(p); + return 0; +} + +static int _testParseCommandLineRequiredOptionMissing(void) +{ + ProgramOptions p = newProgramOptions(1); + ProgramOption o = _getTestOption(); + char *argv[2]; + argv[0] = "exe"; + argv[1] = "--test"; + o->argumentType = kProgramOptionArgumentTypeRequired; + assert(programOptionsAdd(p, o)); + + assertFalse(p->options[0]->enabled); + assertDoubleEquals(0.0, programOptionsGetNumber(p, 0), TEST_EXACT_TOLERANCE); + assertFalse(programOptionsParseArgs(p, 2, argv)); + assertFalse(p->options[0]->enabled); + + freeProgramOptions(p); + return 0; +} + +static ProgramOptions _getTestProgramOptionsForConfigFile(void) +{ + ProgramOptions p = newProgramOptions(2); + ProgramOption o1, o2; + o1 = newProgramOptionWithName(0, "test", "test help", true, kProgramOptionTypeString, kProgramOptionArgumentTypeNone); + programOptionsAdd(p, o1); + o2 = newProgramOptionWithName(1, "other", "test help", true, kProgramOptionTypeString, kProgramOptionArgumentTypeRequired); + programOptionsAdd(p, o2); + return p; +} + +static FILE *_openTestProgramConfigFile(void) +{ + FILE *fp = fopen(TEST_CONFIG_FILE, "w"); + return fp; +} + +static int _testParseConfigFile(void) +{ + ProgramOptions p = _getTestProgramOptionsForConfigFile(); + CharString filename = newCharStringWithCString(TEST_CONFIG_FILE); + FILE *fp = _openTestProgramConfigFile(); + fprintf(fp, "--test\n-o\nfoo\n"); + fclose(fp); + assert(programOptionsParseConfigFile(p, filename)); + assert(p->options[0]->enabled); + assert(p->options[1]->enabled); + assertCharStringEquals("foo", programOptionsGetString(p, 1)); + + unlink(TEST_CONFIG_FILE); + freeProgramOptions(p); + freeCharString(filename); + return 0; +} + +static int _testParseInvalidConfigFile(void) +{ + ProgramOptions p = newProgramOptions(1); + CharString filename = newCharStringWithCString("invalid"); + assertFalse(programOptionsParseConfigFile(p, filename)); + freeProgramOptions(p); + freeCharString(filename); + return 0; +} + +static int _testParseNullConfigFile(void) +{ + ProgramOptions p = newProgramOptions(1); + assertFalse(programOptionsParseConfigFile(p, NULL)); + freeProgramOptions(p); + return 0; +} + +static int _testParseConfigFileWithInvalidOptions(void) +{ + ProgramOptions p = _getTestProgramOptionsForConfigFile(); + CharString filename = newCharStringWithCString(TEST_CONFIG_FILE); + FILE *fp = _openTestProgramConfigFile(); + + fprintf(fp, "--test\n-s\n"); + fclose(fp); + assertFalse(programOptionsParseConfigFile(p, filename)); + assert(p->options[0]->enabled); + assertFalse(p->options[1]->enabled); + + unlink(TEST_CONFIG_FILE); + freeProgramOptions(p); + freeCharString(filename); + return 0; +} + +static int _testFindProgramOptionFromString(void) +{ + ProgramOptions p = newProgramOptions(1); + CharString c = newCharStringWithCString("test"); + ProgramOption o; + + assert(programOptionsAdd(p, _getTestOption())); + assertIntEquals(p->numOptions, 1); + o = programOptionsFind(p, c); + assertNotNull(o); + assertCharStringEquals("test", o->name); + + freeProgramOptions(p); + freeCharString(c); + return 0; +} + +static int _testFindProgramOptionFromStringInvalid(void) +{ + ProgramOptions p = newProgramOptions(1); + CharString c = newCharStringWithCString("invalid"); + ProgramOption o; + + assert(programOptionsAdd(p, _getTestOption())); + assertIntEquals(1, p->numOptions); + o = programOptionsFind(p, c); + assertIsNull(o); + + freeProgramOptions(p); + freeCharString(c); + return 0; +} + +static ProgramOptions _getTestOptionMultipleTypes(void) +{ + ProgramOptions p = newProgramOptions(3); + programOptionsAdd(p, newProgramOptionWithName(0, "string", "help", true, + kProgramOptionTypeString, kProgramOptionArgumentTypeRequired)); + programOptionsAdd(p, newProgramOptionWithName(1, "numeric", "help", true, + kProgramOptionTypeNumber, kProgramOptionArgumentTypeRequired)); + programOptionsAdd(p, newProgramOptionWithName(2, "list", "help", true, + kProgramOptionTypeList, kProgramOptionArgumentTypeRequired)); + return p; +} + +static int _testGetString(void) +{ + ProgramOptions p = _getTestOptionMultipleTypes(); + CharString s = programOptionsGetString(p, 0); + assertCharStringEquals("", s); + freeProgramOptions(p); + return 0; +} + +static int _testGetStringForWrongType(void) +{ + ProgramOptions p = _getTestOptionMultipleTypes(); + CharString s = programOptionsGetString(p, 1); + assertIsNull(s); + freeProgramOptions(p); + return 0; +} + +static int _testGetStringForInvalidOption(void) +{ + ProgramOptions p = _getTestOptionMultipleTypes(); + CharString s = programOptionsGetString(p, 4); + assertIsNull(s); + freeProgramOptions(p); + return 0; +} + +static int _testGetNumeric(void) +{ + ProgramOptions p = _getTestOptionMultipleTypes(); + float f = programOptionsGetNumber(p, 1); + assertDoubleEquals(0.0, f, TEST_DEFAULT_TOLERANCE); + freeProgramOptions(p); + return 0; +} + +static int _testGetNumericForWrongType(void) +{ + ProgramOptions p = _getTestOptionMultipleTypes(); + float f = programOptionsGetNumber(p, 0); + assertDoubleEquals(-1.0, f, TEST_DEFAULT_TOLERANCE); + freeProgramOptions(p); + return 0; +} + +static int _testGetNumericForInvalidOption(void) +{ + ProgramOptions p = _getTestOptionMultipleTypes(); + float f = programOptionsGetNumber(p, 4); + assertDoubleEquals(-1.0, f, TEST_DEFAULT_TOLERANCE); + freeProgramOptions(p); + return 0; +} + +static int _testGetList(void) +{ + ProgramOptions p = _getTestOptionMultipleTypes(); + LinkedList l = programOptionsGetList(p, 2); + assertIntEquals(0, l->_numItems); + freeProgramOptions(p); + return 0; +} + +static int _testGetListForWrongType(void) +{ + ProgramOptions p = _getTestOptionMultipleTypes(); + LinkedList l = programOptionsGetList(p, 0); + assertIsNull(l); + freeProgramOptions(p); + return 0; +} + +static int _testGetListForInvalidOption(void) +{ + ProgramOptions p = _getTestOptionMultipleTypes(); + LinkedList l = programOptionsGetList(p, 4); + assertIsNull(l); + freeProgramOptions(p); + return 0; +} + +static int _testSetString(void) +{ + ProgramOptions p = _getTestOptionMultipleTypes(); + CharString s; + programOptionsSetCString(p, 0, "test"); + s = programOptionsGetString(p, 0); + assertNotNull(s); + assertCharStringEquals("test", s); + freeProgramOptions(p); + return 0; +} + +static int _testSetStringForWrongType(void) +{ + ProgramOptions p = _getTestOptionMultipleTypes(); + programOptionsSetCString(p, 1, "test"); + freeProgramOptions(p); + return 0; +} + +static int _testSetStringForInvalidOption(void) +{ + ProgramOptions p = _getTestOptionMultipleTypes(); + CharString s; + programOptionsSetCString(p, 4, "test"); + s = programOptionsGetString(p, 0); + assertNotNull(s); + assertCharStringEquals("", s); + freeProgramOptions(p); + return 0; +} + +static int _testSetStringWithNull(void) +{ + ProgramOptions p = _getTestOptionMultipleTypes(); + CharString s; + programOptionsSetCString(p, 0, NULL); + s = programOptionsGetString(p, 0); + assertNotNull(s); + assertCharStringEquals("", s); + freeProgramOptions(p); + return 0; +} + +static int _testSetNumeric(void) +{ + ProgramOptions p = _getTestOptionMultipleTypes(); + float f; + programOptionsSetNumber(p, 1, 1.23f); + f = programOptionsGetNumber(p, 1); + assertDoubleEquals(1.23, f, TEST_DEFAULT_TOLERANCE); + freeProgramOptions(p); + return 0; +} + +static int _testSetNumericForWrongType(void) +{ + ProgramOptions p = _getTestOptionMultipleTypes(); + programOptionsSetNumber(p, 0, 1.23f); + freeProgramOptions(p); + return 0; +} + +static int _testSetNumericForInvalidOption(void) +{ + ProgramOptions p = _getTestOptionMultipleTypes(); + programOptionsSetNumber(p, 4, 1.23f); + freeProgramOptions(p); + return 0; +} + +static int _testSetListItem(void) +{ + ProgramOptions p = _getTestOptionMultipleTypes(); + CharString s; + LinkedList l; + CharString r; + + s = newCharStringWithCString("test"); + programOptionsSetListItem(p, 2, s); + l = programOptionsGetList(p, 2); + assertIntEquals(1, linkedListLength(l)); + r = l->item; + assertCharStringEquals("test", r); + freeCharString(s); + freeProgramOptions(p); + return 0; +} + +static int _testSetListItemForWrongType(void) +{ + ProgramOptions p = _getTestOptionMultipleTypes(); + CharString s = newCharStringWithCString("test"); + programOptionsSetListItem(p, 1, s); + freeCharString(s); + freeProgramOptions(p); + return 0; +} + +static int _testSetListItemForInvalidOption(void) +{ + ProgramOptions p = _getTestOptionMultipleTypes(); + CharString s = newCharStringWithCString("test"); + programOptionsSetListItem(p, 4, s); + freeCharString(s); + freeProgramOptions(p); + return 0; +} + +TestSuite addProgramOptionTests(void); +TestSuite addProgramOptionTests(void) +{ + TestSuite testSuite = newTestSuite("ProgramOption", NULL, _programOptionTeardown); + addTest(testSuite, "NewObject", _testNewProgramOptions); + addTest(testSuite, "AddNewProgramOption", _testAddNewProgramOption); + addTest(testSuite, "AddNullProgramOption", _testAddNullProgramOption); + addTest(testSuite, "AddNewProgramOptionOutsideRange", _testAddNewProgramOptionOutsideRange); + + addTest(testSuite, "ParseCommandLineShortOption", _testParseCommandLineShortOption); + addTest(testSuite, "ParseCommandLineLongOption", _testParseCommandLineLongOption); + addTest(testSuite, "ParseCommandLineInvalidOption", _testParseCommandLineInvalidOption); + addTest(testSuite, "ParseCommandLineRequiredOption", _testParseCommandLineRequiredOption); + addTest(testSuite, "ParseCommandLineRequiredOptionMissing", _testParseCommandLineRequiredOptionMissing); + + addTest(testSuite, "ParseConfigFile", _testParseConfigFile); + addTest(testSuite, "ParseInvalidConfigFile", _testParseInvalidConfigFile); + addTest(testSuite, "ParseNullConfigFile", _testParseNullConfigFile); + addTest(testSuite, "ParseConfigFileWithInvalidOptions", _testParseConfigFileWithInvalidOptions); + + addTest(testSuite, "FindProgramOptionFromString", _testFindProgramOptionFromString); + addTest(testSuite, "FindProgramOptionFromStringInvalid", _testFindProgramOptionFromStringInvalid); + + addTest(testSuite, "GetString", _testGetString); + addTest(testSuite, "GetStringForWrongType", _testGetStringForWrongType); + addTest(testSuite, "GetStringForInvalidOption", _testGetStringForInvalidOption); + addTest(testSuite, "GetNumeric", _testGetNumeric); + addTest(testSuite, "GetNumericForWrongType", _testGetNumericForWrongType); + addTest(testSuite, "GetNumericForInvalidOption", _testGetNumericForInvalidOption); + addTest(testSuite, "GetList", _testGetList); + addTest(testSuite, "GetListForWrongType", _testGetListForWrongType); + addTest(testSuite, "GetListForInvalidOption", _testGetListForInvalidOption); + addTest(testSuite, "SetString", _testSetString); + addTest(testSuite, "SetStringForWrongType", _testSetStringForWrongType); + addTest(testSuite, "SetStringForInvalidOption", _testSetStringForInvalidOption); + addTest(testSuite, "SetStringWithNull", _testSetStringWithNull); + addTest(testSuite, "SetNumeric", _testSetNumeric); + addTest(testSuite, "SetNumericForWrongType", _testSetNumericForWrongType); + addTest(testSuite, "SetNumericForInvalidOption", _testSetNumericForInvalidOption); + addTest(testSuite, "SetListItem", _testSetListItem); + addTest(testSuite, "SetListItemForWrongType", _testSetListItemForWrongType); + addTest(testSuite, "SetListItemForInvalidOption", _testSetListItemForInvalidOption); + + return testSuite; +} diff --git a/test/audio/AudioSettingsTest.c b/test/audio/AudioSettingsTest.c new file mode 100644 index 0000000..bbdbe62 --- /dev/null +++ b/test/audio/AudioSettingsTest.c @@ -0,0 +1,213 @@ +#include "audio/AudioSettings.h" +#include "unit/TestRunner.h" + +static void _audioSettingsSetup(void) +{ + initAudioSettings(); +} + +static void _audioSettingsTeardown(void) +{ + freeAudioSettings(); +} + +static int _testInitAudioSettings(void) +{ + assertDoubleEquals(DEFAULT_SAMPLE_RATE, getSampleRate(), TEST_DEFAULT_TOLERANCE); + assertIntEquals(DEFAULT_NUM_CHANNELS, getNumChannels()); + assertUnsignedLongEquals(DEFAULT_BLOCKSIZE, getBlocksize()); + assertDoubleEquals(DEFAULT_TEMPO, getTempo(), TEST_DEFAULT_TOLERANCE); + assertIntEquals(DEFAULT_TIMESIG_BEATS_PER_MEASURE, getTimeSignatureBeatsPerMeasure()); + assertIntEquals(DEFAULT_TIMESIG_NOTE_VALUE, getTimeSignatureNoteValue()); + return 0; +} + +static int _testSetSampleRate(void) +{ + setSampleRate(22050.0); + assertDoubleEquals(22050.0, getSampleRate(), TEST_DEFAULT_TOLERANCE); + return 0; +} + +static int _testSetInvalidSampleRate(void) +{ + setSampleRate(22050.0); + assertDoubleEquals(22050.0, getSampleRate(), TEST_DEFAULT_TOLERANCE); + assertFalse(setSampleRate(0.0)); + assertDoubleEquals(22050.0, getSampleRate(), TEST_DEFAULT_TOLERANCE); + return 0; +} + +static int _testSetNumChannels(void) +{ + setNumChannels(4); + assertIntEquals(4, getNumChannels()); + return 0; +} + +static int _testSetInvalidNumChannels(void) +{ + setNumChannels(2); + assertIntEquals(2, getNumChannels()); + assertFalse(setNumChannels(0)); + assertIntEquals(2, getNumChannels()); + return 0; +} + +static int _testSetBlocksize(void) +{ + setBlocksize(123); + assertUnsignedLongEquals(123l, getBlocksize()); + return 0; +} + +static int _testSetInvalidBlocksize(void) +{ + setBlocksize(123); + assertUnsignedLongEquals(123l, getBlocksize()); + assertFalse(setBlocksize(0)); + assertUnsignedLongEquals(123l, getBlocksize()); + return 0; +} + +static int _testSetTempo(void) +{ + setTempo(123.45f); + assertDoubleEquals(123.45, getTempo(), 0.1); + return 0; +} + +static int _testSetInvalidTempo(void) +{ + setTempo(100.0); + assertDoubleEquals(100.0, getTempo(), TEST_DEFAULT_TOLERANCE); + assertFalse(setTempo(-666.0)); + assertDoubleEquals(100.0, getTempo(), TEST_DEFAULT_TOLERANCE); + assertFalse(setTempo(0.0)); + assertDoubleEquals(100.0, getTempo(), TEST_DEFAULT_TOLERANCE); + return 0; +} + +static int _testSetTempoWithMidiBytes(void) +{ + byte bytes[3]; + bytes[0] = 0x13; + bytes[1] = 0xe7; + bytes[2] = 0x1b; + setTempoFromMidiBytes(bytes); + assertDoubleEquals(46.0, getTempo(), TEST_DEFAULT_TOLERANCE); + return 0; +} + +static int _testSetTempoWithMidiBytesNull(void) +{ + setTempo(100.0); + assertDoubleEquals(100.0, getTempo(), TEST_DEFAULT_TOLERANCE); + setTempoFromMidiBytes(NULL); + assertDoubleEquals(100.0, getTempo(), TEST_DEFAULT_TOLERANCE); + return 0; +} + +static int _testSetTimeSigBeatsPerMeasure(void) +{ + assert(setTimeSignatureBeatsPerMeasure(8)); + assertIntEquals(8, getTimeSignatureBeatsPerMeasure()); + return 0; +} + +static int _testSetTimeSigNoteValue(void) +{ + assert(setTimeSignatureNoteValue(2)); + assertIntEquals(2, getTimeSignatureNoteValue()); + return 0; +} + +static int _testSetTimeSignatureWithMidiBytes(void) +{ + byte bytes[2]; + // Corresponds to a time signature of 3/8 + bytes[0] = 3; + bytes[1] = 3; + assert(setTimeSignatureFromMidiBytes(bytes)); + assertIntEquals(3, getTimeSignatureBeatsPerMeasure()); + assertIntEquals(8, getTimeSignatureNoteValue()); + return 0; +} + +static int _testSetTimeSignatureWithMidiBytesNull(void) +{ + assert(setTimeSignatureBeatsPerMeasure(3)); + assert(setTimeSignatureNoteValue(8)); + assertIntEquals(3, getTimeSignatureBeatsPerMeasure()); + assertIntEquals(8, getTimeSignatureNoteValue()); + assertFalse(setTimeSignatureFromMidiBytes(NULL)); + assertIntEquals(3, getTimeSignatureBeatsPerMeasure()); + assertIntEquals(8, getTimeSignatureNoteValue()); + return 0; +} + +static int _testSetTimeSignatureFromString(void) +{ + CharString s = newCharStringWithCString("2/16"); + assert(setTimeSignatureBeatsPerMeasure(3)); + assert(setTimeSignatureNoteValue(8)); + assertIntEquals(3, getTimeSignatureBeatsPerMeasure()); + assertIntEquals(8, getTimeSignatureNoteValue()); + assert(setTimeSignatureFromString(s)); + assertIntEquals(2, getTimeSignatureBeatsPerMeasure()); + assertIntEquals(16, getTimeSignatureNoteValue()); + freeCharString(s); + return 0; +} + +static int _testSetTimeSignatureFromInvalidString(void) +{ + CharString s = newCharStringWithCString("invalid/none"); + assert(setTimeSignatureBeatsPerMeasure(3)); + assert(setTimeSignatureNoteValue(8)); + assertIntEquals(3, getTimeSignatureBeatsPerMeasure()); + assertIntEquals(8, getTimeSignatureNoteValue()); + assertFalse(setTimeSignatureFromString(s)); + assertIntEquals(3, getTimeSignatureBeatsPerMeasure()); + assertIntEquals(8, getTimeSignatureNoteValue()); + freeCharString(s); + return 0; +} + +static int _testSetTimeSignatureFromNullString(void) +{ + assert(setTimeSignatureBeatsPerMeasure(3)); + assert(setTimeSignatureNoteValue(8)); + assertIntEquals(3, getTimeSignatureBeatsPerMeasure()); + assertIntEquals(8, getTimeSignatureNoteValue()); + assertFalse(setTimeSignatureFromString(NULL)); + assertIntEquals(3, getTimeSignatureBeatsPerMeasure()); + assertIntEquals(8, getTimeSignatureNoteValue()); + return 0; +} + +TestSuite addAudioSettingsTests(void); +TestSuite addAudioSettingsTests(void) +{ + TestSuite testSuite = newTestSuite("AudioSettings", _audioSettingsSetup, _audioSettingsTeardown); + addTest(testSuite, "Initialization", _testInitAudioSettings); + addTest(testSuite, "SetSampleRate", _testSetSampleRate); + addTest(testSuite, "SetInvalidSampleRate", _testSetInvalidSampleRate); + addTest(testSuite, "SetNumChannels", _testSetNumChannels); + addTest(testSuite, "SetInvalidNumChannels", _testSetInvalidNumChannels); + addTest(testSuite, "SetBlocksize", _testSetBlocksize); + addTest(testSuite, "SetInvalidBlocksize", _testSetInvalidBlocksize); + addTest(testSuite, "SetTempo", _testSetTempo); + addTest(testSuite, "SetInvalidTempo", _testSetInvalidTempo); + addTest(testSuite, "SetTempoWithMidiBytes", _testSetTempoWithMidiBytes); + addTest(testSuite, "SetTempoWithMidiBytesNull", _testSetTempoWithMidiBytesNull); + + addTest(testSuite, "SetTimeSignatureBeatsPerMeasure", _testSetTimeSigBeatsPerMeasure); + addTest(testSuite, "SetTimeSignatureNoteValue", _testSetTimeSigNoteValue); + addTest(testSuite, "SetTimeSignatureWithMidiBytes", _testSetTimeSignatureWithMidiBytes); + addTest(testSuite, "SetTimeSignatureWithMidiBytesNull", _testSetTimeSignatureWithMidiBytesNull); + addTest(testSuite, "SetTimeSignatureFromString", _testSetTimeSignatureFromString); + addTest(testSuite, "SetTimeSignatureFromInvalidString", _testSetTimeSignatureFromInvalidString); + addTest(testSuite, "SetTimeSignatureFromNullString", _testSetTimeSignatureFromNullString); + return testSuite; +} diff --git a/test/audio/SampleBufferTest.c b/test/audio/SampleBufferTest.c new file mode 100644 index 0000000..9909f0f --- /dev/null +++ b/test/audio/SampleBufferTest.c @@ -0,0 +1,134 @@ +#include "audio/AudioSettings.h" +#include "audio/SampleBuffer.h" +#include "unit/TestRunner.h" + +static SampleBuffer _newMockSampleBuffer(void) +{ + return newSampleBuffer(1, 1); +} + +static int _testNewSampleBuffer(void) +{ + SampleBuffer s = _newMockSampleBuffer(); + assertIntEquals(1, s->numChannels); + assertUnsignedLongEquals(1l, s->blocksize); + freeSampleBuffer(s); + return 0; +} + +static int _testNewSampleBufferMultichannel(void) +{ + SampleBuffer s = newSampleBuffer(8, 128); + unsigned int i, j; + assertNotNull(s); + assertIntEquals(8, s->numChannels); + + // Actually write a bunch of samples to expose memory corruption + for (i = 0; i < s->blocksize; ++i) { + for (j = 0; j < s->numChannels; ++j) { + s->samples[j][i] = 0.5f; + } + } + + freeSampleBuffer(s); + return 0; +} + +static int _testClearSampleBuffer(void) +{ + SampleBuffer s = _newMockSampleBuffer(); + s->samples[0][0] = 123; + sampleBufferClear(s); + assertDoubleEquals(0.0, s->samples[0][0], TEST_DEFAULT_TOLERANCE); + freeSampleBuffer(s); + return 0; +} + +static int _testCopyAndMapChannelsSampleBuffers(void) +{ + SampleBuffer s1 = _newMockSampleBuffer(); + SampleBuffer s2 = _newMockSampleBuffer(); + s1->samples[0][0] = 123.0; + assert(sampleBufferCopyAndMapChannels(s2, s1)); + assertDoubleEquals(123.0, s2->samples[0][0], TEST_DEFAULT_TOLERANCE); + freeSampleBuffer(s1); + freeSampleBuffer(s2); + return 0; +} + +static int _testCopyAndMapChannelsSampleBuffersDifferentBlocksizes(void) +{ + SampleBuffer s1 = newSampleBuffer(1, DEFAULT_BLOCKSIZE); + SampleBuffer s2 = _newMockSampleBuffer(); + + s1->samples[0][0] = 123.0; + assertFalse(sampleBufferCopyAndMapChannels(s2, s1)); + // Contents should not change; copying with different sizes is invalid + assertDoubleEquals(123.0, s1->samples[0][0], TEST_DEFAULT_TOLERANCE); + + freeSampleBuffer(s1); + freeSampleBuffer(s2); + return 0; +} + +static int _testCopyAndMapChannelsSampleBuffersDifferentChannelsBigger(void) +{ + SampleBuffer s1 = newSampleBuffer(4, 1); + SampleBuffer s2 = newSampleBuffer(2, 1); + + s2->samples[0][0] = 1.0; + s2->samples[1][0] = 2.0; + + assert(sampleBufferCopyAndMapChannels(s1, s2)); + assertDoubleEquals(1.0, s1->samples[0][0], TEST_DEFAULT_TOLERANCE); + assertDoubleEquals(2.0, s1->samples[1][0], TEST_DEFAULT_TOLERANCE); + assertDoubleEquals(1.0, s1->samples[2][0], TEST_DEFAULT_TOLERANCE); + assertDoubleEquals(2.0, s1->samples[3][0], TEST_DEFAULT_TOLERANCE); + + freeSampleBuffer(s1); + freeSampleBuffer(s2); + return 0; +} + +static int _testCopyAndMapChannelsSampleBuffersDifferentChannelsSmaller(void) +{ + SampleBuffer s1 = newSampleBuffer(1, 1); + SampleBuffer s2 = newSampleBuffer(4, 1); + unsigned int i; + + for (i = 0; i < s1->numChannels; i++) { + s1->samples[i][0] = 1.0; + } + + for (i = 0; i < s2->numChannels; i++) { + s2->samples[i][0] = 2.0; + } + + assert(sampleBufferCopyAndMapChannels(s1, s2)); + assertDoubleEquals(2.0, s1->samples[0][0], TEST_DEFAULT_TOLERANCE); + + freeSampleBuffer(s1); + freeSampleBuffer(s2); + return 0; +} + +static int _testFreeNullSampleBuffer(void) +{ + freeSampleBuffer(NULL); + return 0; +} + +TestSuite addSampleBufferTests(void); +TestSuite addSampleBufferTests(void) +{ + TestSuite testSuite = newTestSuite("SampleBuffer", NULL, NULL); + addTest(testSuite, "NewObject", _testNewSampleBuffer); + addTest(testSuite, "NewSampleBufferMultichannel", _testNewSampleBufferMultichannel); + addTest(testSuite, "ClearSampleBuffer", _testClearSampleBuffer); + addTest(testSuite, "CopyAndMapChannelsSampleBuffers", _testCopyAndMapChannelsSampleBuffers); + addTest(testSuite, "CopyAndMapChannelsSampleBuffersDifferentSizes", _testCopyAndMapChannelsSampleBuffersDifferentBlocksizes); + addTest(testSuite, "CopyAndMapChannelsSampleBuffersDifferentChannelsBigger", _testCopyAndMapChannelsSampleBuffersDifferentChannelsBigger); + addTest(testSuite, "CopyAndMapChannelsSampleBuffersDifferentChannelsSmaller", _testCopyAndMapChannelsSampleBuffersDifferentChannelsSmaller); + addTest(testSuite, "FreeNullSampleBuffer", _testFreeNullSampleBuffer); + return testSuite; +} diff --git a/test/base/CharStringTest.c b/test/base/CharStringTest.c new file mode 100644 index 0000000..aabf206 --- /dev/null +++ b/test/base/CharStringTest.c @@ -0,0 +1,422 @@ +#include "unit/TestRunner.h" + +static char *const TEST_STRING = "test string"; +static char *const TEST_STRING_CAPS = "TEST STRING"; +static char *const OTHER_TEST_STRING = "other test string"; + +static int _testNewCharString(void) +{ + CharString c = newCharString(); + assertSizeEquals(kCharStringLengthDefault, c->capacity); + assertCharStringEquals(EMPTY_STRING, c); + freeCharString(c); + return 0; +} + +static int _testNewCharStringWithCapacity(void) +{ + size_t testSize = 123; + CharString c = newCharStringWithCapacity(testSize); + assertSizeEquals(testSize, c->capacity); + assertCharStringEquals(EMPTY_STRING, c); + freeCharString(c); + return 0; +} + +static int _testNewObjectWithNullCString(void) +{ + CharString c = newCharStringWithCString(NULL); + assertSizeEquals(kCharStringLengthDefault, c->capacity); + assertCharStringEquals(EMPTY_STRING, c); + freeCharString(c); + return 0; +} + +static int _testNewObjectWithEmptyCString(void) +{ + CharString c = newCharStringWithCString(EMPTY_STRING); + assertSizeEquals(kCharStringLengthDefault, c->capacity); + assertCharStringEquals(EMPTY_STRING, c); + freeCharString(c); + return 0; +} + +static int _testClearCharString(void) +{ + CharString c = newCharString(); + charStringCopyCString(c, TEST_STRING); + charStringClear(c); + assertSizeEquals(kCharStringLengthDefault, c->capacity); + assertCharStringEquals(EMPTY_STRING, c); + freeCharString(c); + return 0; +} + +static int _testCopyToCharString(void) +{ + CharString c = newCharString(); + charStringCopyCString(c, TEST_STRING); + assertSizeEquals(kCharStringLengthDefault, c->capacity); + assertCharStringEquals(TEST_STRING, c); + freeCharString(c); + return 0; +} + +static int _testCopyCharStrings(void) +{ + CharString c, c2; + c = newCharString(); + c2 = newCharString(); + charStringCopyCString(c, TEST_STRING); + charStringCopy(c2, c); + assertCharStringEquals(c2->data, c); + freeCharString(c); + freeCharString(c2); + return 0; +} + +static int _testIsEmptyStringEmpty(void) +{ + CharString c = newCharString(); + assert(charStringIsEmpty(c)); + freeCharString(c); + return 0; +} + +static int _testIsNullEmptyString(void) +{ + assert(charStringIsEmpty(NULL)); + return 0; +} + +static int _testIsRegularStringNotEmpty(void) +{ + CharString c = newCharString(); + charStringCopyCString(c, TEST_STRING); + assertFalse(charStringIsEmpty(c)); + freeCharString(c); + return 0; +} + +static int _testAppendCharStrings(void) +{ + CharString a = newCharStringWithCString("a"); + CharString b = newCharStringWithCString("b"); + charStringAppend(a, b); + assertCharStringEquals("ab", a); + freeCharString(a); + freeCharString(b); + return 0; +} + +static int _testAppendCharStringsOverCapacity(void) +{ + CharString a = newCharStringWithCString("a"); + CharString b = newCharStringWithCString("1234567890"); + assertSizeEquals((size_t)2, a->capacity); + charStringAppend(a, b); + assertCharStringEquals("a1234567890", a); + assertSizeEquals((size_t)12, a->capacity); + freeCharString(a); + freeCharString(b); + return 0; +} + +static int _testCharStringEqualsSameString(void) +{ + CharString c, c2; + c = newCharString(); + c2 = newCharString(); + charStringCopyCString(c, TEST_STRING); + charStringCopyCString(c2, TEST_STRING); + assert(charStringIsEqualTo(c, c2, false)); + freeCharString(c); + freeCharString(c2); + return 0; +} + +static int _testCharStringDoesEqualDifferentString(void) +{ + CharString c, c2; + c = newCharString(); + c2 = newCharString(); + charStringCopyCString(c, TEST_STRING); + charStringCopyCString(c2, OTHER_TEST_STRING); + assertFalse(charStringIsEqualTo(c, c2, false)); + freeCharString(c); + freeCharString(c2); + return 0; +} + +static int _testCharStringEqualsSameStringInsensitive(void) +{ + CharString c, c2; + c = newCharString(); + c2 = newCharString(); + charStringCopyCString(c, TEST_STRING); + charStringCopyCString(c2, TEST_STRING_CAPS); + assert(charStringIsEqualTo(c, c2, true)); + freeCharString(c); + freeCharString(c2); + return 0; +} + +static int _testCharStringDoesNotEqualSameStringInsensitive(void) +{ + CharString c, c2; + c = newCharString(); + c2 = newCharString(); + charStringCopyCString(c, TEST_STRING); + charStringCopyCString(c2, TEST_STRING_CAPS); + assertFalse(charStringIsEqualTo(c, c2, false)); + freeCharString(c); + freeCharString(c2); + return 0; +} + +static int _testCharStringEqualsNull(void) +{ + CharString c = newCharString(); + assertFalse(charStringIsEqualTo(c, NULL, false)); + freeCharString(c); + return 0; +} + +static int _testCharStringEqualsSameCString(void) +{ + CharString c = newCharString(); + charStringCopyCString(c, TEST_STRING); + assert(charStringIsEqualToCString(c, TEST_STRING, false)); + freeCharString(c); + return 0; +} + +static int _testCharStringNotEqualToDifferentCString(void) +{ + CharString c = newCharString(); + charStringCopyCString(c, TEST_STRING); + assertFalse(charStringIsEqualToCString(c, OTHER_TEST_STRING, false)); + freeCharString(c); + return 0; +} + +static int _testCharStringEqualsSameCStringInsensitive(void) +{ + CharString c = newCharString(); + charStringCopyCString(c, TEST_STRING); + assert(charStringIsEqualToCString(c, TEST_STRING_CAPS, true)); + freeCharString(c); + return 0; +} + +static int _testCharStringNotEqualsCStringInsensitive(void) +{ + CharString c = newCharString(); + charStringCopyCString(c, TEST_STRING); + assertFalse(charStringIsEqualToCString(c, TEST_STRING_CAPS, false)); + freeCharString(c); + return 0; +} + +static int _testCharStringEqualsCStringNull(void) +{ + CharString c = newCharString(); + assertFalse(charStringIsEqualToCString(c, NULL, false)); + freeCharString(c); + return 0; +} + +static int _testIsLetter(void) +{ + CharString c = newCharStringWithCString("a"); + assert(charStringIsLetter(c, 0)); + freeCharString(c); + return 0; +} + +static int _testIsNotLetter(void) +{ + CharString c = newCharStringWithCString("0"); + assertFalse(charStringIsLetter(c, 0)); + freeCharString(c); + return 0; +} + +static int _testIsNumber(void) +{ + CharString c = newCharStringWithCString("0"); + assert(charStringIsNumber(c, 0)); + freeCharString(c); + return 0; +} + +static int _testIsNotNumber(void) +{ + CharString c = newCharStringWithCString("a"); + assertFalse(charStringIsNumber(c, 0)); + freeCharString(c); + return 0; +} + +static int _testSplitString(void) +{ + CharString c = newCharStringWithCString("abc,def,ghi,"); + LinkedList l = charStringSplit(c, ','); + CharString *items = NULL; + + assertNotNull(l); + assertIntEquals(3, linkedListLength(l)); + items = (CharString *)linkedListToArray(l); + assertNotNull(items); + assertCharStringEquals("abc", items[0]); + assertCharStringEquals("def", items[1]); + assertCharStringEquals("ghi", items[2]); + + freeLinkedListAndItems(l, (LinkedListFreeItemFunc)freeCharString); + freeCharString(c); + free(items); + return 0; +} + +static int _testSplitStringWithoutDelimiter(void) +{ + const char *expected = "abcdefg"; + CharString c = newCharStringWithCString(expected); + CharString c2 = NULL; + LinkedList l = charStringSplit(c, ','); + + assertNotNull(l); + assertIntEquals(1, linkedListLength(l)); + c2 = l->item; + assertCharStringEquals(expected, c2); + + freeLinkedListAndItems(l, (LinkedListFreeItemFunc)freeCharString); + freeCharString(c); + return 0; +} + +static int _testSplitStringNULLDelimiter(void) +{ + CharString c = newCharStringWithCString("abcdefh"); + LinkedList l = charStringSplit(c, 0); + assertIsNull(l); + freeCharString(c); + return 0; +} + +static int _testSplitStringEmptyString(void) +{ + CharString c = newCharString(); + LinkedList l = charStringSplit(c, ','); + assertNotNull(l); + assertIntEquals(0, linkedListLength(l)); + freeCharString(c); + freeLinkedList(l); + return 0; +} + +// This function is not technically public, but we test against it instead of +// the public version in order to set a shorter line length. This makes test +// cases much easier to construct. +extern boolByte _charStringWrap(const char *srcString, char *destString, + size_t destStringSize, int indentSize, int lineLength); + +static int _testWrapNullSourceString(void) +{ + assertFalse(charStringWrap(NULL, 0)); + return 0; +} + +static int _testWrapString(void) +{ + CharString src = newCharStringWithCString("1234 6789 bcde 01"); + // Create dest string the same way as in wrapString(), cheap I know... + CharString dest = newCharStringWithCapacity(src->capacity * 2); + _charStringWrap(src->data, dest->data, dest->capacity, 0, 0x10); + assertCharStringEquals("1234 6789 bcde\n01", dest); + freeCharString(src); + freeCharString(dest); + return 0; +} + +static int _testWrapStringWithIndent(void) +{ + CharString src = newCharStringWithCString("1234 6789 bcde 01"); + // Create dest string the same way as in wrapString(), cheap I know... + CharString dest = newCharStringWithCapacity(src->capacity * 2); + _charStringWrap(src->data, dest->data, dest->capacity, 1, 0xe); + assertCharStringEquals(" 1234 6789\n bcde 01", dest); + freeCharString(src); + freeCharString(dest); + return 0; +} + +static int _testWrapStringLongerThanLine(void) +{ + CharString src = newCharStringWithCString("123456789abcdef12"); + // Create dest string the same way as in wrapString(), cheap I know... + CharString dest = newCharStringWithCapacity(src->capacity * 2); + _charStringWrap(src->data, dest->data, dest->capacity, 0, 0xf); + assertCharStringEquals("123456789abcde-\nf12", dest); + freeCharString(src); + freeCharString(dest); + return 0; +} + +static int _testFreeNullCharString(void) +{ + freeCharString(NULL); + return 0; +} + +TestSuite addCharStringTests(void); +TestSuite addCharStringTests(void) +{ + TestSuite testSuite = newTestSuite("CharString", NULL, NULL); + + addTest(testSuite, "NewObject", _testNewCharString); + addTest(testSuite, "NewObjectWithCapacity", _testNewCharStringWithCapacity); + addTest(testSuite, "NewObjectWithNullCString", _testNewObjectWithNullCString); + addTest(testSuite, "NewObjectWithEmptyCString", _testNewObjectWithEmptyCString); + + addTest(testSuite, "ClearString", _testClearCharString); + addTest(testSuite, "CopyToCharString", _testCopyToCharString); + addTest(testSuite, "CopyCharStrings", _testCopyCharStrings); + addTest(testSuite, "EmptyStringIsEmpty", _testIsEmptyStringEmpty); + addTest(testSuite, "NullIsEmpty", _testIsNullEmptyString); + addTest(testSuite, "RegularStringIsNotEmpty", _testIsRegularStringNotEmpty); + + addTest(testSuite, "AppendCharStrings", _testAppendCharStrings); + addTest(testSuite, "AppendCharStringsOverCapacity", _testAppendCharStringsOverCapacity); + + addTest(testSuite, "EqualsSameString", _testCharStringEqualsSameString); + addTest(testSuite, "DoesNotEqualDifferentString", _testCharStringDoesEqualDifferentString); + addTest(testSuite, "EqualsSameStringWithCaseInsensitive", _testCharStringEqualsSameStringInsensitive); + addTest(testSuite, "DoesNotEqualSameStringWithDifferentCase", _testCharStringDoesNotEqualSameStringInsensitive); + addTest(testSuite, "EqualsNull", _testCharStringEqualsNull); + + addTest(testSuite, "EqualsSameCString", _testCharStringEqualsSameCString); + addTest(testSuite, "DoesNotEqualDifferentCString", _testCharStringNotEqualToDifferentCString); + addTest(testSuite, "EqualsSameCStringWithCaseInsensitive", _testCharStringEqualsSameCStringInsensitive); + addTest(testSuite, "DoesNotEqualSameCStringWithDifferentCase", _testCharStringNotEqualsCStringInsensitive); + addTest(testSuite, "EqualsCStringNull", _testCharStringEqualsCStringNull); + + addTest(testSuite, "IsLetter", _testIsLetter); + addTest(testSuite, "IsNotLetter", _testIsNotLetter); + addTest(testSuite, "IsNumber", _testIsNumber); + addTest(testSuite, "IsNotNumber", _testIsNotNumber); + + addTest(testSuite, "SplitString", _testSplitString); + addTest(testSuite, "SplitStringWithoutDelimiter", _testSplitStringWithoutDelimiter); + addTest(testSuite, "SplitStringNULLDelimiter", _testSplitStringNULLDelimiter); + addTest(testSuite, "SplitStringEmptyString", _testSplitStringEmptyString); + + addTest(testSuite, "WrapNullSourceString", _testWrapNullSourceString); + addTest(testSuite, "WrapString", _testWrapString); + addTest(testSuite, "WrapStringWithIndent", _testWrapStringWithIndent); + addTest(testSuite, "WrapStringLongerThanLine", _testWrapStringLongerThanLine); + + addTest(testSuite, "FreeNullCharString", _testFreeNullCharString); + + return testSuite; +} diff --git a/test/base/EndianTest.c b/test/base/EndianTest.c new file mode 100644 index 0000000..5b64fb2 --- /dev/null +++ b/test/base/EndianTest.c @@ -0,0 +1,155 @@ +#include "unit/TestRunner.h" +#include "base/Endian.h" + +#include "unit/TestRunner.h" + +#if LINUX +#if defined(__BYTE_ORDER__) +#define HOST_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) +#define HOST_LITTLE_ENDIAN (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) +#endif +#elif MACOSX +#include <machine/endian.h> +#define HOST_BIG_ENDIAN (__DARWIN_BYTE_ORDER == __DARWIN_BIG_ENDIAN) +#define HOST_LITTLE_ENDIAN (__DARWIN_BYTE_ORDER == __DARWIN_LITTLE_ENDIAN) +#elif WINDOWS +// Windows is always little-endian, see: http://support.microsoft.com/kb/102025 +#define HOST_BIG_ENDIAN 0 +#define HOST_LITTLE_ENDIAN 1 +#endif + +#if !defined(HOST_BIG_ENDIAN) || !defined(HOST_LITTLE_ENDIAN) +#error Host platform endian-ness not known +#error Please define either HOST_BIG_ENDIAN or HOST_LITTLE_ENDIAN +#elif HOST_BIG_ENDIAN && HOST_LITTLE_ENDIAN +#error Both HOST_BIG_ENDIAN and HOST_LITTLE_ENDIAN cannot be defined to 1! +#endif + +static int _testFlipShortEndian(void) +{ + unsigned short s = 0xabcd; + unsigned short r = flipShortEndian(s); + assertUnsignedLongEquals(0xcdabul, r); + assertIntEquals(s, flipShortEndian(r)); + return 0; +} + +static int _testConvertBigEndianShortToPlatform(void) +{ + unsigned short s = 0xabcd; + unsigned short r = convertBigEndianShortToPlatform(s); +#if HOST_BIG_ENDIAN + assertUnsignedLongEquals(s, (unsigned long)r); +#elif HOST_LITTLE_ENDIAN + assertUnsignedLongEquals((unsigned long)s, (unsigned long)flipShortEndian(r)); +#endif + return 0; +} + +static int _testConvertBigEndianIntToPlatform(void) +{ + unsigned int i = 0xdeadbeef; + unsigned int r = convertBigEndianIntToPlatform(i); +#if HOST_BIG_ENDIAN + assertUnsignedLongEquals((unsigned long)i, r); +#elif HOST_LITTLE_ENDIAN + assertUnsignedLongEquals(0xefbeaddeul, r); +#endif + return 0; +} + +static int _testConvertLittleEndianIntToPlatform(void) +{ + unsigned int i = 0xdeadbeef; + unsigned int r = convertLittleEndianIntToPlatform(i); +#if HOST_BIG_ENDIAN + assertUnsignedLongEquals(0xefbeaddeul, r); +#elif HOST_LITTLE_ENDIAN + assertUnsignedLongEquals((unsigned long)i, r); +#endif + return 0; +} + +static int _testConvertBigEndianFloatToPlatform(void) +{ + // Generate an integer with a known value and convert it to a float using pointer trickery + int i = 0xdeadbeef; + int i2 = 0xefbeadbe; + float *f = (float *)&i; + float *f2 = (float *)&i2; + float r = convertBigEndianFloatToPlatform(*f); + // Unfortunately, the above pointer trickery will result in a really huge number, so the + // standard comparison tolerance is *way* too low. This number is the lowest possible + // result that we should accept for this test. + const double bigFloatTolerance = 3.02231e+24; + +#if HOST_BIG_ENDIAN + assertDoubleEquals(*f, r); +#elif HOST_LITTLE_ENDIAN + // Sanity check to make sure that the actual result is the really huge number which we + // are expecting. + assert(fabs(r) > bigFloatTolerance); + assertDoubleEquals(*f2, r, bigFloatTolerance); +#endif + return 0; +} + +static int _testConvertByteArrayToUnsignedShort(void) +{ + byte *b = (byte *)malloc(sizeof(byte) * 2); + int i; + unsigned short s; + + for (i = 0; i < 2; i++) { + b[i] = (byte)(0xaa + i); + } + + s = convertByteArrayToUnsignedShort(b); + +#if HOST_BIG_ENDIAN + assertUnsignedLongEquals(0xaaabul, s); +#elif HOST_LITTLE_ENDIAN + assertUnsignedLongEquals(0xabaaul, s); +#endif + + free(b); + return 0; +} + +static int _testConvertByteArrayToUnsignedInt(void) +{ + byte *b = (byte *)malloc(sizeof(byte) * 4); + unsigned int s; + + for (size_t i = 0; i < 4; i++) { + b[i] = (byte)(0xaa + i); + } + s = convertByteArrayToUnsignedInt(b); + + +#if HOST_BIG_ENDIAN + assertUnsignedLongEquals(0xaaabacadul, s); +#elif HOST_LITTLE_ENDIAN + assertUnsignedLongEquals(0xadacabaaul, s); +#endif + + free(b); + return 0; +} + +TestSuite addEndianTests(void); +TestSuite addEndianTests(void) +{ + TestSuite testSuite = newTestSuite("Endian", NULL, NULL); + + addTest(testSuite, "FlipShortEndian", _testFlipShortEndian); + addTest(testSuite, "ConvertBigEndianShortToPlatform", _testConvertBigEndianShortToPlatform); + addTest(testSuite, "ConvertBigEndianIntToPlatform", _testConvertBigEndianIntToPlatform); + addTest(testSuite, "ConvertLittleEndianIntToPlatform", _testConvertLittleEndianIntToPlatform); + addTest(testSuite, "ConvertBigEndianFloatToPlatform", _testConvertBigEndianFloatToPlatform); + + addTest(testSuite, "ConvertByteArrayToUnsignedShort", _testConvertByteArrayToUnsignedShort); + addTest(testSuite, "ConvertByteArrayToUnsignedInt", _testConvertByteArrayToUnsignedInt); + + return testSuite; +} diff --git a/test/base/FileTest.c b/test/base/FileTest.c new file mode 100644 index 0000000..45977c1 --- /dev/null +++ b/test/base/FileTest.c @@ -0,0 +1,1526 @@ +#include "unit/TestRunner.h" +#include "base/File.h" + +#define TEST_DIRNAME "test_dir" +#define TEST_DIRNAME_WITH_DOT "test.dir" +#define TEST_DIRNAME_COPY_DEST "test_dir_dest" +#define TEST_FILENAME "test_file.txt" + +static void _fileTestTeardown(void) +{ + CharString testFilePath = newCharStringWithCString(TEST_FILENAME); + File testFile = newFileWithPath(testFilePath); + CharString testDirPath = newCharStringWithCString(TEST_DIRNAME); + File testDir = newFileWithPath(testDirPath); + CharString testDirWithDotPath = newCharStringWithCString(TEST_DIRNAME_WITH_DOT); + File testDirWithDot = newFileWithPath(testDirWithDotPath); + CharString testDirCopyPath = newCharStringWithCString(TEST_DIRNAME_COPY_DEST); + File testDirCopy = newFileWithPath(testDirCopyPath); + + if (fileExists(testFile)) { + fileRemove(testFile); + } + + if (fileExists(testDir)) { + fileRemove(testDir); + } + + if (fileExists(testDirWithDot)) { + fileRemove(testDirWithDot); + } + + if (fileExists(testDirCopy)) { + fileRemove(testDirCopy); + } + + freeFile(testFile); + freeFile(testDir); + freeFile(testDirWithDot); + freeFile(testDirCopy); + freeCharString(testFilePath); + freeCharString(testDirPath); + freeCharString(testDirWithDotPath); + freeCharString(testDirCopyPath); +} + +static int _testNewFileDefault(void) +{ + File f = newFile(); + + assertNotNull(f); + assertCharStringEquals(EMPTY_STRING, f->absolutePath); + assertIntEquals(kFileTypeInvalid, f->fileType); + assertIntEquals(kFileOpenModeClosed, f->_openMode); + assertIsNull(f->_fileHandle); + assertFalse(fileExists(f)); + + freeFile(f); + return 0; +} + +static int _testNewFileAlreadyExists(void) +{ + CharString p = newCharStringWithCString(TEST_FILENAME); + File f = newFileWithPath(p); + File ftest = NULL; + + assertFalse(fileExists(f)); + assert(fileCreate(f, kFileTypeFile)); + assert(fileExists(f)); + + ftest = newFileWithPath(p); + assertIntEquals(kFileTypeFile, ftest->fileType); + assertIntEquals(kFileOpenModeClosed, ftest->_openMode); + assertIsNull(ftest->_fileHandle); + + freeCharString(p); + freeFile(f); + freeFile(ftest); + return 0; +} + +static int _testNewFileWithRelativePath(void) +{ + CharString p = newCharString(); + CharString pAbs = newCharString(); + CharString pwd = fileGetCurrentDirectory(); + File f = NULL; + + sprintf(p->data, "%s%c%s", TEST_DIRNAME, PATH_DELIMITER, TEST_FILENAME); + f = newFileWithPath(p); + assertNotNull(f); + sprintf(pAbs->data, "%s%c%s", pwd->data, PATH_DELIMITER, p->data); + assertCharStringEquals(pAbs->data, f->absolutePath); + assertFalse(fileExists(f)); + assertIntEquals(kFileTypeInvalid, f->fileType); + assertIntEquals(kFileOpenModeClosed, f->_openMode); + assertIsNull(f->_fileHandle); + + freeFile(f); + freeCharString(p); + freeCharString(pAbs); + freeCharString(pwd); + return 0; +} + +static int _testNewFileWithAbsolutePath(void) +{ + CharString p = newCharString(); + CharString pAbs = newCharString(); + File f = NULL; + + sprintf(p->data, "%s%c%s%c%s", ROOT_DIRECTORY, PATH_DELIMITER, TEST_DIRNAME, PATH_DELIMITER, TEST_FILENAME); + f = newFileWithPath(p); + assertNotNull(f); + // Mostly just testing to make sure that absolute paths are not incorrectly + // translated from relative ones + sprintf(pAbs->data, "%s%c%s%c%s", ROOT_DIRECTORY, PATH_DELIMITER, TEST_DIRNAME, PATH_DELIMITER, TEST_FILENAME); + assert(charStringIsEqualTo(pAbs, f->absolutePath, false)); + assertFalse(fileExists(f)); + assertIntEquals(kFileTypeInvalid, f->fileType); + assertIntEquals(kFileOpenModeClosed, f->_openMode); + assertIsNull(f->_fileHandle); + + freeFile(f); + freeCharString(p); + freeCharString(pAbs); + return 0; +} + +static int _testNewFileWithNetworkPath(void) +{ +#if WINDOWS + CharString p = newCharString(); + CharString pAbs = newCharString(); + CharString pwd = fileGetCurrentDirectory(); + File f; + + sprintf(p->data, "\\\\%s%c%s", TEST_DIRNAME, PATH_DELIMITER, TEST_FILENAME); + f = newFileWithPath(p); + assertNotNull(f); + pAbs = newCharString(); + // Mostly just testing to make sure that network paths are not incorrectly + // translated from relative ones + sprintf(pAbs->data, "\\\\%s%c%s", TEST_DIRNAME, PATH_DELIMITER, TEST_FILENAME); + assert(charStringIsEqualTo(pAbs, f->absolutePath, false)); + assertFalse(fileExists(f)); + assertIntEquals(kFileTypeInvalid, f->fileType); + assertIntEquals(kFileOpenModeClosed, f->_openMode); + assertIsNull(f->_fileHandle); + + freeFile(f); + freeCharString(p); + freeCharString(pAbs); + freeCharString(pwd); +#elif UNIX + // Unix systems mount network drives as regular filesystem paths, this test + // isn't necessary on that platform +#else + assert(false); +#endif + return 0; +} + +static int _testNewFileWithInvalidPath(void) +{ + CharString p; + File f; + + // These characters are not permitted in filenames on Windows or Unix. The + // file interface should refuse to create an object with such a path, + // regardless of type. + + p = newCharStringWithCString("*"); + f = newFileWithPath(p); + assertIsNull(f); + freeCharString(p); + + p = newCharStringWithCString(":"); + f = newFileWithPath(p); + assertIsNull(f); + freeCharString(p); + + p = newCharStringWithCString("?"); + f = newFileWithPath(p); + assertIsNull(f); + freeCharString(p); + + p = newCharStringWithCString("<"); + f = newFileWithPath(p); + assertIsNull(f); + freeCharString(p); + + p = newCharStringWithCString(">"); + f = newFileWithPath(p); + assertIsNull(f); + freeCharString(p); + + p = newCharStringWithCString("|"); + f = newFileWithPath(p); + assertIsNull(f); + freeCharString(p); + + freeFile(f); + return 0; +} + +static int _testNewFileWithNullPath(void) +{ + File f; + + // Should yield the same result as newFile() + f = newFileWithPath(NULL); + assertNotNull(f); + assertCharStringEquals(EMPTY_STRING, f->absolutePath); + assertIntEquals(kFileTypeInvalid, f->fileType); + assertFalse(fileExists(f)); + + freeFile(f); + return 0; +} + +static int _testNewFileWithCStringPath(void) +{ + File f = newFileWithPathCString(TEST_FILENAME); + assertNotNull(f); + assertFalse(fileExists(f)); + freeFile(f); + return 0; +} + +static int _testNewFileWithCStringPathNull(void) +{ + File f = newFileWithPathCString(NULL); + assertIsNull(f); + return 0; +} + +static int _testNewFileWithParent(void) +{ + CharString pdir = newCharStringWithCString(TEST_DIRNAME); + CharString pfile = newCharStringWithCString(TEST_FILENAME); + File dir = newFileWithPath(pdir); + File f = NULL; + CharString pwd = fileGetCurrentDirectory(); + CharString pAbs = newCharString(); + + assertFalse(fileExists(dir)); + assert(fileCreate(dir, kFileTypeDirectory)); + f = newFileWithParent(dir, pfile); + assertNotNull(f); + assertFalse(fileExists(f)); + sprintf(pAbs->data, "%s%c%s%c%s", pwd->data, PATH_DELIMITER, pdir->data, PATH_DELIMITER, pfile->data); + assertCharStringEquals(pAbs->data, f->absolutePath); + assertIntEquals(kFileOpenModeClosed, f->_openMode); + assertIsNull(f->_fileHandle); + + freeCharString(pAbs); + freeCharString(pdir); + freeCharString(pfile); + freeCharString(pwd); + freeFile(f); + freeFile(dir); + return 0; +} + +static int _testNewFileWithParentNullParent(void) +{ + CharString pfile = newCharStringWithCString(TEST_FILENAME); + File f = newFileWithParent(NULL, pfile); + assertIsNull(f); + freeCharString(pfile); + return 0; +} + +static int _testNewFileWithParentNullPath(void) +{ + CharString pdir = newCharStringWithCString(TEST_DIRNAME); + File d = newFileWithPath(pdir); + File f = NULL; + + assertFalse(fileExists(d)); + assert(fileCreate(d, kFileTypeDirectory)); + assert(fileExists(d)); + newFileWithParent(d, NULL); + assertIsNull(f); + + freeCharString(pdir); + freeFile(d); + return 0; +} + +static int _testNewFileWithParentEmptyPath(void) +{ + CharString pdir = newCharStringWithCString(TEST_DIRNAME); + CharString empty = newCharString(); + File d = newFileWithPath(pdir); + File f = NULL; + + assertFalse(fileExists(d)); + assert(fileCreate(d, kFileTypeDirectory)); + assert(fileExists(d)); + newFileWithParent(d, empty); + assertIsNull(f); + + freeCharString(pdir); + freeCharString(empty); + freeFile(d); + return 0; +} + +static int _testNewFileWithParentAlreadyExists(void) +{ + CharString pdir = newCharStringWithCString(TEST_DIRNAME); + CharString pfile = newCharStringWithCString(TEST_FILENAME); + File dir = newFileWithPath(pdir); + File f = NULL; + File ftest = NULL; + + assertFalse(fileExists(dir)); + assert(fileCreate(dir, kFileTypeDirectory)); + f = newFileWithParent(dir, pfile); + assertNotNull(f); + assertFalse(fileExists(f)); + assert(fileCreate(f, kFileTypeFile)); + ftest = newFileWithParent(dir, pfile); + assert(fileExists(ftest)); + assertIntEquals(kFileTypeFile, ftest->fileType); + + freeCharString(pdir); + freeCharString(pfile); + freeFile(f); + freeFile(ftest); + freeFile(dir); + return 0; +} + +static int _testNewFileWithParentNotDirectory(void) +{ + CharString pfile = newCharStringWithCString(TEST_FILENAME); + File f = newFileWithPath(pfile); + File ftest = NULL; + + assertFalse(fileExists(f)); + assert(fileCreate(f, kFileTypeFile)); + ftest = newFileWithParent(f, pfile); + assertIsNull(ftest); + + freeCharString(pfile); + freeFile(f); + freeFile(ftest); + return 0; +} + +static int _testNewFileWithParentInvalid(void) +{ + CharString pdir = newCharStringWithCString(TEST_DIRNAME); + CharString pfile = newCharStringWithCString(TEST_FILENAME); + File dir = newFileWithPath(pdir); + File f = NULL; + + assertFalse(fileExists(dir)); + f = newFileWithParent(dir, pfile); + assertIsNull(f); + + freeCharString(pdir); + freeCharString(pfile); + freeFile(f); + freeFile(dir); + return 0; +} + +static int _testNewFileWithParentAbsolutePath(void) +{ + CharString pdir = newCharStringWithCString(TEST_DIRNAME); + File dir = newFileWithPath(pdir); + File f = NULL; + CharString p = newCharString(); + + assertFalse(fileExists(dir)); + assert(fileCreate(dir, kFileTypeDirectory)); + assert(fileExists(dir)); + sprintf(p->data, "%s%c%s", ROOT_DIRECTORY, PATH_DELIMITER, TEST_FILENAME); + f = newFileWithParent(dir, p); + assertIsNull(f); + + freeCharString(p); + freeCharString(pdir); + freeFile(f); + freeFile(dir); + return 0; +} + +static int _testFileExists(void) +{ + File f; + FILE *fp = fopen(TEST_FILENAME, "w"); + CharString c = newCharStringWithCString(TEST_FILENAME); + assert(fp != NULL); + fclose(fp); + + f = newFileWithPath(c); + assert(fileExists(f)); + + freeFile(f); + freeCharString(c); + return 0; +} + +static int _testFileExistsInvalid(void) +{ + CharString c = newCharStringWithCString("invalid"); + File f = newFileWithPath(c); + + assertFalse(fileExists(f)); + + freeFile(f); + freeCharString(c); + return 0; +} + +static int _testFileCreateFile(void) +{ + CharString p = newCharStringWithCString(TEST_FILENAME); + File f, f2; + + f = newFileWithPath(p); + assertNotNull(f); + // Might fail because a previous test didn't clean up properly + assertFalse(fileExists(f)); + assert(fileCreate(f, kFileTypeFile)); + assert(fileExists(f)); + assertIntEquals(kFileOpenModeWrite, f->_openMode); + assertNotNull(f->_fileHandle); + + // Just to make sure... + f2 = newFileWithPath(p); + assert(fileExists(f2)); + assertIntEquals(kFileTypeFile, f2->fileType); + assertIntEquals(kFileOpenModeClosed, f2->_openMode); + assertIsNull(f2->_fileHandle); + + freeFile(f); + freeFile(f2); + freeCharString(p); + return 0; +} + +static int _testFileCreateDir(void) +{ + CharString p = newCharStringWithCString(TEST_DIRNAME); + File f = newFileWithPath(p); + File f2; + + assertNotNull(f); + // Might fail because a previous test didn't clean up properly + assertFalse(fileExists(f)); + assert(fileCreate(f, kFileTypeDirectory)); + assert(fileExists(f)); + // Just to make sure... + f2 = newFileWithPath(p); + assert(fileExists(f2)); + assertIntEquals(kFileTypeDirectory, f2->fileType); + assertIntEquals(kFileOpenModeClosed, f->_openMode); + assertIsNull(f->_fileHandle); + + freeFile(f); + freeFile(f2); + freeCharString(p); + return 0; +} + +static int _testFileCreateInvalidType(void) +{ + CharString p = newCharStringWithCString(TEST_FILENAME); + File f = newFileWithPath(p); + + assertNotNull(f); + // Might fail because a previous test didn't clean up properly + assertFalse(fileExists(f)); + assertFalse(fileCreate(f, kFileTypeInvalid)); + assertFalse(fileExists(f)); + + freeFile(f); + freeCharString(p); + return 0; +} + +static int _testFileCreateAlreadyExists(void) +{ + CharString p = newCharStringWithCString(TEST_FILENAME); + File f = newFileWithPath(p); + + assertNotNull(f); + // Might fail because a previous test didn't clean up properly + assertFalse(fileExists(f)); + assert(fileCreate(f, kFileTypeFile)); + assert(fileExists(f)); + assertFalse(fileCreate(f, kFileTypeFile)); + + freeFile(f); + freeCharString(p); + return 0; +} + +static int _testFileCopyToWithFile(void) +{ + CharString psrc = newCharStringWithCString(TEST_FILENAME); + CharString pdest = newCharStringWithCString(TEST_DIRNAME); + File src = newFileWithPath(psrc); + File dest = newFileWithPath(pdest); + File copy = NULL; + + assertNotNull(src); + assertNotNull(dest); + assertFalse(fileExists(src)); + assertFalse(fileExists(dest)); + + assert(fileCreate(dest, kFileTypeDirectory)); + assert(fileExists(dest)); + assert(fileCreate(src, kFileTypeFile)); + assert(fileExists(src)); + copy = fileCopyTo(src, dest); + assertNotNull(copy); + assert(fileExists(copy)); + + freeFile(src); + freeFile(dest); + freeFile(copy); + freeCharString(psrc); + freeCharString(pdest); + return 0; +} + +static int _testFileCopyToWithDirectory(void) +{ + CharString psrc = newCharStringWithCString(TEST_DIRNAME); + CharString psrcFile = newCharStringWithCString(TEST_FILENAME); + CharString pdest = newCharStringWithCString(TEST_DIRNAME_COPY_DEST); + File src = newFileWithPath(psrc); + File srcFile = NULL; + File dest = newFileWithPath(pdest); + File copy = NULL; + + assertNotNull(src); + assertNotNull(dest); + assertFalse(fileExists(src)); + assertFalse(fileExists(dest)); + + assert(fileCreate(dest, kFileTypeDirectory)); + assert(fileExists(dest)); + + assert(fileCreate(src, kFileTypeDirectory)); + assert(fileExists(src)); + srcFile = newFileWithParent(src, psrcFile); + assertNotNull(srcFile); + assertFalse(fileExists(srcFile)); + assert(fileCreate(srcFile, kFileTypeFile)); + assert(fileExists(srcFile)); + + copy = fileCopyTo(src, dest); + assertNotNull(copy); + assert(fileExists(copy)); + + freeFile(src); + freeFile(srcFile); + freeFile(dest); + freeFile(copy); + freeCharString(psrc); + freeCharString(psrcFile); + freeCharString(pdest); + return 0; +} + +static int _testFileCopyToInvalidDestination(void) +{ + CharString psrc = newCharStringWithCString(TEST_FILENAME); + CharString pdest = newCharStringWithCString("invalid"); + File src = newFileWithPath(psrc); + File dest = newFileWithPath(pdest); + File copy = NULL; + + assertNotNull(src); + assertNotNull(dest); + assertFalse(fileExists(src)); + assertFalse(fileExists(dest)); + + assert(fileCreate(src, kFileTypeFile)); + assert(fileExists(src)); + copy = fileCopyTo(src, dest); + assertIsNull(copy); + + freeFile(src); + freeFile(dest); + freeFile(copy); + freeCharString(psrc); + freeCharString(pdest); + return 0; +} + +static int _testFileCopyInvalidTo(void) +{ + CharString psrc = newCharStringWithCString("invalid"); + CharString pdest = newCharStringWithCString(TEST_DIRNAME); + File src = newFileWithPath(psrc); + File dest = newFileWithPath(pdest); + File copy = NULL; + + assertNotNull(src); + assertNotNull(dest); + assertFalse(fileExists(src)); + assertFalse(fileExists(dest)); + + assert(fileCreate(dest, kFileTypeDirectory)); + assert(fileExists(dest)); + copy = fileCopyTo(src, dest); + assertIsNull(copy); + + freeFile(src); + freeFile(dest); + freeFile(copy); + freeCharString(psrc); + freeCharString(pdest); + return 0; +} + +static int _testFileCopyToNull(void) +{ + CharString psrc = newCharStringWithCString(TEST_FILENAME); + File src = newFileWithPath(psrc); + File copy = NULL; + + assertNotNull(src); + assertFalse(fileExists(src)); + + assert(fileCreate(src, kFileTypeFile)); + assert(fileExists(src)); + copy = fileCopyTo(src, NULL); + assertIsNull(copy); + + freeFile(src); + freeFile(copy); + freeCharString(psrc); + return 0; +} + +static int _testFileRemoveDir(void) +{ + CharString pdest = newCharStringWithCString(TEST_DIRNAME); + File dir = newFileWithPath(pdest); + + assertFalse(fileExists(dir)); + assert(fileCreate(dir, kFileTypeDirectory)); + assert(fileExists(dir)); + assert(fileRemove(dir)); + assertFalse(fileExists(dir)); + + freeFile(dir); + freeCharString(pdest); + return 0; +} + +static int _testFileRemoveDirWithContents(void) +{ + CharString pdest = newCharStringWithCString(TEST_DIRNAME); + CharString psubdir = newCharStringWithCString(TEST_DIRNAME); + CharString psubfile = newCharStringWithCString(TEST_FILENAME); + File dir = newFileWithPath(pdest); + File subdir = NULL; + File subfile = NULL; + + assertFalse(fileExists(dir)); + assert(fileCreate(dir, kFileTypeDirectory)); + assert(fileExists(dir)); + + // Must be created after parent directory is created + subdir = newFileWithParent(dir, psubdir); + assertNotNull(subdir); + assertFalse(fileExists(subdir)); + assert(fileCreate(subdir, kFileTypeDirectory)); + assert(fileExists(subdir)); + + // Must be created after parent directory is created + subfile = newFileWithParent(dir, psubfile); + assertNotNull(subfile); + assertFalse(fileExists(subfile)); + assert(fileCreate(subfile, kFileTypeFile)); + assert(fileExists(subfile)); + // Windows will not be able to delete this directory unless all files in it are + // closed and unlocked. + fileClose(subfile); + + // Remove the parent directory and assert that all children are gone + assert(fileRemove(dir)); + assertFalse(fileExists(dir)); + assertFalse(fileExists(subdir)); + assertFalse(fileExists(subfile)); + + freeFile(dir); + freeFile(subdir); + freeFile(subfile); + freeCharString(pdest); + freeCharString(psubdir); + freeCharString(psubfile); + return 0; +} + +static int _testFileRemoveFile(void) +{ + CharString pfile = newCharStringWithCString(TEST_FILENAME); + File f = newFileWithPath(pfile); + + assertFalse(fileExists(f)); + assert(fileCreate(f, kFileTypeFile)); + assert(fileExists(f)); + assert(fileRemove(f)); + assertFalse(fileExists(f)); + + freeCharString(pfile); + freeFile(f); + return 0; +} + +static int _testFileRemoveInvalid(void) +{ + CharString pfile = newCharStringWithCString(TEST_FILENAME); + File f = newFileWithPath(pfile); + + assertFalse(fileExists(f)); + assertFalse(fileRemove(f)); + + freeCharString(pfile); + freeFile(f); + return 0; +} + +static int _testFileListDirectory(void) +{ + CharString pdest = newCharStringWithCString(TEST_DIRNAME); + CharString psubdir = newCharStringWithCString(TEST_DIRNAME); + CharString psubfile = newCharStringWithCString(TEST_FILENAME); + File dir = newFileWithPath(pdest); + File subdir = NULL; + File subfile = NULL; + LinkedList list; + File item; + + assertFalse(fileExists(dir)); + assert(fileCreate(dir, kFileTypeDirectory)); + assert(fileExists(dir)); + + subdir = newFileWithParent(dir, psubdir); + assertNotNull(subdir); + assertFalse(fileExists(subdir)); + assert(fileCreate(subdir, kFileTypeDirectory)); + assertNotNull(subdir); + + subfile = newFileWithParent(dir, psubfile); + assertNotNull(subfile); + assertFalse(fileExists(subfile)); + assert(fileCreate(subfile, kFileTypeFile)); + assert(fileExists(subfile)); + + list = fileListDirectory(dir); + assertNotNull(list); + item = (File)list->item; + assertNotNull(item); + + if (item->fileType == kFileTypeFile) { + assertCharStringContains(TEST_FILENAME, item->absolutePath); + } else if (item->fileType == kFileTypeDirectory) { + assertCharStringContains(TEST_DIRNAME, item->absolutePath); + } else { + assert(false); + } + + item = (File)(((LinkedList)list->nextItem)->item); + assertNotNull(item); + + if (item->fileType == kFileTypeFile) { + assertCharStringContains(TEST_FILENAME, item->absolutePath); + } else if (item->fileType == kFileTypeDirectory) { + assertCharStringContains(TEST_DIRNAME, item->absolutePath); + } else { + assert(false); + } + + freeLinkedListAndItems(list, (LinkedListFreeItemFunc)freeFile); + freeFile(dir); + freeFile(subfile); + freeFile(subdir); + freeCharString(pdest); + freeCharString(psubdir); + freeCharString(psubfile); + return 0; +} + +static int _testFileListDirectoryInvalid(void) +{ + CharString pdest = newCharStringWithCString(TEST_FILENAME); + File file = newFileWithPath(pdest); + LinkedList list; + + assertFalse(fileExists(file)); + assert(fileCreate(file, kFileTypeFile)); + assert(fileExists(file)); + list = fileListDirectory(file); + assertIsNull(list); + + freeFile(file); + freeCharString(pdest); + return 0; +} + +static int _testFileListDirectoryNotExists(void) +{ + CharString pdest = newCharStringWithCString(TEST_DIRNAME); + File dir = newFileWithPath(pdest); + LinkedList list; + + assertFalse(fileExists(dir)); + list = fileListDirectory(dir); + assertIsNull(list); + + freeFile(dir); + freeCharString(pdest); + return 0; +} + +static int _testFileListDirectoryEmpty(void) +{ + CharString pdest = newCharStringWithCString(TEST_DIRNAME); + File dir = newFileWithPath(pdest); + LinkedList list; + + assertFalse(fileExists(dir)); + assert(fileCreate(dir, kFileTypeDirectory)); + assert(fileExists(dir)); + list = fileListDirectory(dir); + assertNotNull(list); + assertIntEquals(0, linkedListLength(list)); + + freeLinkedList(list); + freeFile(dir); + freeCharString(pdest); + return 0; +} + +static int _testFileGetSize(void) +{ + CharString p = newCharStringWithCString(TEST_FILENAME); + File f = newFileWithPath(p); + + assertFalse(fileExists(f)); + assert(fileCreate(f, kFileTypeFile)); + assert(fileWrite(f, p)); + fileClose(f); // force flush and close to be called + assertSizeEquals(strlen(p->data), fileGetSize(f)); + + freeCharString(p); + freeFile(f); + return 0; +} + +static int _testFileGetSizeNotExists(void) +{ + CharString p = newCharStringWithCString(TEST_FILENAME); + File f = newFileWithPath(p); + + assertFalse(fileExists(f)); + assertSizeEquals((size_t)0, fileGetSize(f)); + + freeCharString(p); + freeFile(f); + return 0; +} + +static int _testFileGetSizeDirectory(void) +{ + CharString p = newCharStringWithCString(TEST_DIRNAME); + File d = newFileWithPath(p); + + assertFalse(fileExists(d)); + assert(fileCreate(d, kFileTypeDirectory)); + assert(fileExists(d)); + assertSizeEquals((size_t)0, fileGetSize(d)); + + freeCharString(p); + freeFile(d); + return 0; +} + +static int _testFileReadContents(void) +{ + CharString p = newCharStringWithCString(TEST_FILENAME); + File f = newFileWithPath(p); + CharString result = NULL; + + assertFalse(fileExists(f)); + assert(fileCreate(f, kFileTypeFile)); + assert(fileExists(f)); + assert(fileWrite(f, p)); + result = fileReadContents(f); + assertNotNull(result); + assertCharStringEquals(p->data, result); + + freeCharString(result); + freeCharString(p); + freeFile(f); + return 0; +} + +static int _testFileReadContentsNotExists(void) +{ + CharString p = newCharStringWithCString(TEST_FILENAME); + File f = newFileWithPath(p); + CharString result = NULL; + + assertFalse(fileExists(f)); + result = fileReadContents(f); + assertIsNull(result); + + freeCharString(result); + freeCharString(p); + freeFile(f); + return 0; +} + +static int _testFileReadContentsDirectory(void) +{ + CharString p = newCharStringWithCString(TEST_DIRNAME); + File d = newFileWithPath(p); + CharString result = NULL; + + assertFalse(fileExists(d)); + assert(fileCreate(d, kFileTypeDirectory)); + assert(fileExists(d)); + result = fileReadContents(d); + assertIsNull(result); + + freeCharString(result); + freeCharString(p); + freeFile(d); + return 0; +} + +static int _testFileReadLines(void) +{ + CharString p = newCharStringWithCString(TEST_FILENAME); + File f = newFileWithPath(p); + LinkedList lines = NULL; + CharString *items = NULL; + int i; + + assertFalse(fileExists(f)); + assert(fileCreate(f, kFileTypeFile)); + assert(fileExists(f)); + assert(fileWrite(f, p)); + assert(fileWriteBytes(f, "\n", 1)); + assert(fileWrite(f, p)); + lines = fileReadLines(f); + assertNotNull(lines); + assertIntEquals(2, linkedListLength(lines)); + items = (CharString *)linkedListToArray(lines); + assertNotNull(items); + + for (i = 0; i < linkedListLength(lines); i++) { + assertCharStringEquals(p->data, items[i]); + } + + freeLinkedListAndItems(lines, (LinkedListFreeItemFunc)freeCharString); + freeCharString(p); + freeFile(f); + free(items); + return 0; +} + +static int _testFileReadLinesEmpty(void) +{ + CharString p = newCharStringWithCString(TEST_FILENAME); + File f = newFileWithPath(p); + LinkedList lines = NULL; + + assertFalse(fileExists(f)); + assert(fileCreate(f, kFileTypeFile)); + assert(fileExists(f)); + lines = fileReadLines(f); + assertNotNull(lines); + assertIntEquals(0, linkedListLength(lines)); + + freeLinkedList(lines); + freeCharString(p); + freeFile(f); + return 0; +} + +static int _testFileReadLinesNotExists(void) +{ + CharString p = newCharStringWithCString(TEST_FILENAME); + File f = newFileWithPath(p); + LinkedList result = NULL; + + assertFalse(fileExists(f)); + result = fileReadLines(f); + assertIsNull(result); + + freeCharString(p); + freeFile(f); + return 0; +} + +static int _testFileReadLinesDirectory(void) +{ + CharString p = newCharStringWithCString(TEST_DIRNAME); + File d = newFileWithPath(p); + LinkedList result = NULL; + + assertFalse(fileExists(d)); + assert(fileCreate(d, kFileTypeDirectory)); + assert(fileExists(d)); + result = fileReadLines(d); + assertIsNull(result); + + freeCharString(p); + freeFile(d); + return 0; +} + +static int _testFileReadBytes(void) +{ + CharString p = newCharStringWithCString(TEST_FILENAME); + File f = newFileWithPath(p); + size_t s = 0; + CharString result = NULL; + + assertFalse(fileExists(f)); + assert(fileCreate(f, kFileTypeFile)); + assert(fileExists(f)); + assert(fileWrite(f, p)); + fileClose(f); + s = fileGetSize(f); + assert(s > 0); + char *fileData = fileReadBytes(f, s); + result = newCharStringWithCString(fileData); + assertNotNull(result); + assertCharStringEquals(TEST_FILENAME, result); + + freeCharString(result); + freeCharString(p); + freeFile(f); + free(fileData); + return 0; +} + +static int _testFileReadBytesNotExists(void) +{ + CharString p = newCharStringWithCString(TEST_FILENAME); + File f = newFileWithPath(p); + void *result = NULL; + + assertFalse(fileExists(f)); + // Note the fake size here + result = fileReadBytes(f, (size_t)100); + assertIsNull(result); + + freeCharString(result); + freeCharString(p); + freeFile(f); + return 0; +} + +static int _testFileReadBytesDirectory(void) +{ + CharString p = newCharStringWithCString(TEST_FILENAME); + File d = newFileWithPath(p); + void *result = NULL; + + assertFalse(fileExists(d)); + assert(fileCreate(d, kFileTypeDirectory)); + assert(fileExists(d)); + result = fileReadBytes(d, (size_t)100); + assertIsNull(result); + + freeCharString(result); + freeCharString(p); + freeFile(d); + return 0; +} + +static int _testFileReadBytesZeroSize(void) +{ + CharString p = newCharStringWithCString(TEST_FILENAME); + File f = newFileWithPath(p); + size_t s = 0; + char *result = NULL; + + assertFalse(fileExists(f)); + assert(fileCreate(f, kFileTypeFile)); + assert(fileExists(f)); + assert(fileWrite(f, p)); + fileClose(f); + s = fileGetSize(f); + assert(s > 0); + result = (char *)fileReadBytes(f, 0); + assertIsNull(result); + + free(result); + freeCharString(p); + freeFile(f); + return 0; +} + +static int _testFileReadBytesGreaterSize(void) +{ + CharString p = newCharStringWithCString(TEST_FILENAME); + File f = newFileWithPath(p); + size_t s = 0; + CharString result = NULL; + + assertFalse(fileExists(f)); + assert(fileCreate(f, kFileTypeFile)); + assert(fileExists(f)); + assert(fileWrite(f, p)); + fileClose(f); + s = fileGetSize(f); + assert(s > 0); + char *fileData = (char*)fileReadBytes(f, s * 2); + result = newCharStringWithCString(fileData); + assertNotNull(result); + assertCharStringEquals(TEST_FILENAME, result); + + freeCharString(result); + freeCharString(p); + freeFile(f); + free(fileData); + return 0; +} + +static int _testFileWrite(void) +{ + CharString p = newCharStringWithCString(TEST_FILENAME); + File f = newFileWithPath(p); + CharString result = NULL; + + assertFalse(fileExists(f)); + assert(fileCreate(f, kFileTypeFile)); + assert(fileExists(f)); + assert(fileWrite(f, p)); + result = fileReadContents(f); + assertNotNull(result); + assertCharStringEquals(TEST_FILENAME, result); + + freeCharString(result); + freeCharString(p); + freeFile(f); + return 0; +} + +static int _testFileWriteMultiple(void) +{ + CharString p = newCharStringWithCString(TEST_FILENAME); + CharString p2 = newCharStringWithCString(p->data); + File f = newFileWithPath(p); + CharString result = NULL; + + assertFalse(fileExists(f)); + assert(fileCreate(f, kFileTypeFile)); + assert(fileExists(f)); + assert(fileWrite(f, p)); + assert(fileWrite(f, p)); + result = fileReadContents(f); + assertNotNull(result); + charStringAppend(p, p2); + assertCharStringEquals(p->data, result); + + freeCharString(result); + freeCharString(p); + freeCharString(p2); + freeFile(f); + return 0; +} + +static int _testFileWriteInvalid(void) +{ + CharString p = newCharStringWithCString(TEST_FILENAME); + File f = newFileWithPath(p); + + assertFalse(fileExists(f)); + assertFalse(fileWrite(f, p)); + + freeCharString(p); + freeFile(f); + return 0; +} + +static int _testFileWriteDirectory(void) +{ + CharString p = newCharStringWithCString(TEST_FILENAME); + File d = newFileWithPath(p); + + assertFalse(fileExists(d)); + assert(fileCreate(d, kFileTypeDirectory)); + assertFalse(fileWrite(d, p)); + + freeCharString(p); + freeFile(d); + return 0; +} + +static int _testFileWriteBytes(void) +{ + CharString p = newCharStringWithCString(TEST_FILENAME); + File f = newFileWithPath(p); + size_t s = 0; + CharString result = NULL; + + assertFalse(fileExists(f)); + assert(fileCreate(f, kFileTypeFile)); + assert(fileExists(f)); + assert(fileWriteBytes(f, p->data, strlen(p->data))); + fileClose(f); + s = fileGetSize(f); + assert(s > 0); + char *fileData = (char *)fileReadBytes(f, s); + result = newCharStringWithCString(fileData); + assertNotNull(result); + assertCharStringEquals(TEST_FILENAME, result); + + freeCharString(result); + freeCharString(p); + freeFile(f); + free(fileData); + return 0; +} + +static int _testFileWriteBytesInvalid(void) +{ + CharString p = newCharStringWithCString(TEST_FILENAME); + File f = newFileWithPath(p); + + assertFalse(fileExists(f)); + assertFalse(fileWriteBytes(f, p->data, strlen(p->data))); + + freeCharString(p); + freeFile(f); + return 0; +} + +static int _testFileWriteBytesDirectory(void) +{ + CharString p = newCharStringWithCString(TEST_FILENAME); + File d = newFileWithPath(p); + + assertFalse(fileExists(d)); + assert(fileCreate(d, kFileTypeDirectory)); + assertFalse(fileWriteBytes(d, p->data, strlen(p->data))); + + freeCharString(p); + freeFile(d); + return 0; +} + +static int _testFileGetBasename(void) +{ + CharString p = newCharStringWithCString(TEST_FILENAME); + File f = newFileWithPath(p); + CharString b = fileGetBasename(f); + + assertNotNull(b); + assertCharStringEquals(TEST_FILENAME, b); + + freeCharString(b); + freeCharString(p); + freeFile(f); + return 0; +} + +static int _testFileGetBasenameInvalid(void) +{ + CharString p = newCharStringWithCString(TEST_FILENAME); + File f = newFile(); + CharString b = fileGetBasename(f); + + assertIsNull(b); + + freeCharString(p); + freeFile(f); + return 0; +} + +static int _testFileGetBasenameDirectory(void) +{ + CharString p = newCharStringWithCString(TEST_DIRNAME); + File d = newFileWithPath(p); + CharString b = NULL; + + assertFalse(fileExists(d)); + assert(fileCreate(d, kFileTypeDirectory)); + b = fileGetBasename(d); + assertNotNull(b); + assertCharStringEquals(TEST_DIRNAME, b); + + freeCharString(b); + freeCharString(p); + freeFile(d); + return 0; +} + +static int _testFileGetParent(void) +{ + CharString pdir = newCharStringWithCString(TEST_DIRNAME); + File dir = newFileWithPath(pdir); + CharString pfile = newCharStringWithCString(TEST_FILENAME); + File f = NULL; + File result = NULL; + + assertFalse(fileExists(dir)); + assert(fileCreate(dir, kFileTypeDirectory)); + assert(fileExists(dir)); + + f = newFileWithParent(dir, pfile); + assertNotNull(f); + assertFalse(fileExists(f)); + assert(fileCreate(f, kFileTypeFile)); + assert(fileExists(f)); + + result = fileGetParent(f); + assertNotNull(result); + assertCharStringEquals(dir->absolutePath->data, result->absolutePath); + + freeCharString(pdir); + freeCharString(pfile); + freeFile(f); + freeFile(dir); + freeFile(result); + return 0; +} + +static int _testFileGetParentInvalid(void) +{ + File f = newFile(); + File result = fileGetParent(f); + assertIsNull(result); + freeFile(f); + return 0; +} + +static int _testFileGetExtension(void) +{ + CharString p = newCharStringWithCString(TEST_FILENAME); + File f = newFileWithPath(p); + CharString extension = fileGetExtension(f); + + assertNotNull(extension); + assertCharStringEquals("txt", extension); + + freeCharString(p); + freeCharString(extension); + freeFile(f); + return 0; +} + +static int _testFileGetExtensionDirectory(void) +{ + CharString p = newCharStringWithCString(TEST_DIRNAME); + File d = newFileWithPath(p); + CharString result = NULL; + + assertFalse(fileExists(d)); + assert(fileCreate(d, kFileTypeDirectory)); + assert(fileExists(d)); + result = fileGetExtension(d); + assertIsNull(result); + + freeCharString(p); + freeFile(d); + return 0; +} + +static int _testFileGetExtensionInvalid(void) +{ + File f = newFile(); + CharString result = fileGetExtension(f); + assertIsNull(result); + freeFile(f); + return 0; +} + +static int _testFileGetExtensionNone(void) +{ + CharString p = newCharStringWithCString(TEST_DIRNAME); + File d = newFileWithPath(p); + CharString result = NULL; + + result = fileGetExtension(d); + assertIsNull(result); + + freeCharString(p); + freeFile(d); + return 0; +} + +static int _testFileGetExtensionWithDotInPath(void) +{ + CharString p = newCharStringWithCString(TEST_DIRNAME_WITH_DOT); + File d = newFileWithPath(p); + CharString pfile = newCharStringWithCString(TEST_DIRNAME_COPY_DEST); + File f = NULL; + CharString result = NULL; + + assertFalse(fileExists(d)); + assert(fileCreate(d, kFileTypeDirectory)); + + f = newFileWithParent(d, pfile); + assertNotNull(f); + result = fileGetExtension(f); + // The parent directory has a dot, but the file doesn't so this call + // should return null. + assertIsNull(result); + + freeCharString(p); + freeCharString(pfile); + freeFile(f); + freeFile(d); + return 0; +} + +static int _testFileFreeNull(void) +{ + freeFile(NULL); + return 0; +} + +TestSuite addFileTests(void); +TestSuite addFileTests(void) +{ + TestSuite testSuite = newTestSuite("File", NULL, _fileTestTeardown); + addTest(testSuite, "NewFileDefault", _testNewFileDefault); + + addTest(testSuite, "NewFileAlreadyExists", _testNewFileAlreadyExists); + addTest(testSuite, "NewFileWithRelativePath", _testNewFileWithRelativePath); + addTest(testSuite, "NewFileWithAbsolutePath", _testNewFileWithAbsolutePath); + addTest(testSuite, "NewFileWithNetworkPath", _testNewFileWithNetworkPath); + addTest(testSuite, "NewFileWithInvalidPath", _testNewFileWithInvalidPath); + addTest(testSuite, "NewFileWithNullPath", _testNewFileWithNullPath); + + addTest(testSuite, "NewFileWithCStringPath", _testNewFileWithCStringPath); + addTest(testSuite, "NewFileWithCStringPathNull", _testNewFileWithCStringPathNull); + + addTest(testSuite, "NewFileWithParent", _testNewFileWithParent); + addTest(testSuite, "NewFileWithParentNullParent", _testNewFileWithParentNullParent); + addTest(testSuite, "NewFileWithParentNullPath", _testNewFileWithParentNullPath); + addTest(testSuite, "NewFileWithParentEmptyPath", _testNewFileWithParentEmptyPath); + addTest(testSuite, "NewFileWithParentAlreadyExists", _testNewFileWithParentAlreadyExists); + addTest(testSuite, "NewFileWithParentNotDirectory", _testNewFileWithParentNotDirectory); + addTest(testSuite, "NewFileWithParentInvalid", _testNewFileWithParentInvalid); + addTest(testSuite, "NewFileWithParentAbsolutePath", _testNewFileWithParentAbsolutePath); + + addTest(testSuite, "FileExists", _testFileExists); + addTest(testSuite, "FileExistsInvalid", _testFileExistsInvalid); + + addTest(testSuite, "FileCreateFile", _testFileCreateFile); + addTest(testSuite, "FileCreateDir", _testFileCreateDir); + addTest(testSuite, "FileCreateInvalidType", _testFileCreateInvalidType); + addTest(testSuite, "FileCreateAlreadyExists", _testFileCreateAlreadyExists); + + addTest(testSuite, "FileCopyToWithFile", _testFileCopyToWithFile); + addTest(testSuite, "FileCopyToWithDirectory", _testFileCopyToWithDirectory); + addTest(testSuite, "FileCopyToInvalidDestination", _testFileCopyToInvalidDestination); + addTest(testSuite, "FileCopyNullTo", _testFileCopyInvalidTo); + addTest(testSuite, "FileCopyToNull", _testFileCopyToNull); + + addTest(testSuite, "FileRemoveDir", _testFileRemoveDir); + addTest(testSuite, "FileRemoveDirWithContents", _testFileRemoveDirWithContents); + addTest(testSuite, "FileRemoveFile", _testFileRemoveFile); + addTest(testSuite, "FileRemoveInvalid", _testFileRemoveInvalid); + + addTest(testSuite, "FileListDirectory", _testFileListDirectory); + addTest(testSuite, "FileListDirectoryInvalid", _testFileListDirectoryInvalid); + addTest(testSuite, "FileListDirectoryNotExists", _testFileListDirectoryNotExists); + addTest(testSuite, "FileListDirectoryEmpty", _testFileListDirectoryEmpty); + + addTest(testSuite, "FileGetSize", _testFileGetSize); + addTest(testSuite, "FileGetSizeNotExists", _testFileGetSizeNotExists); + addTest(testSuite, "FileGetSizeDirectory", _testFileGetSizeDirectory); + + addTest(testSuite, "FileReadContents", _testFileReadContents); + addTest(testSuite, "FileReadContentsNotExists", _testFileReadContentsNotExists); + addTest(testSuite, "FileReadContentsDirectory", _testFileReadContentsDirectory); + + addTest(testSuite, "FileReadLines", _testFileReadLines); + addTest(testSuite, "FileReadLinesEmpty", _testFileReadLinesEmpty); + addTest(testSuite, "FileReadLinesNotExists", _testFileReadLinesNotExists); + addTest(testSuite, "FileReadLinesDirectory", _testFileReadLinesDirectory); + + addTest(testSuite, "FileReadBytes", _testFileReadBytes); + addTest(testSuite, "FileReadBytesNotExists", _testFileReadBytesNotExists); + addTest(testSuite, "FileReadBytesDirectory", _testFileReadBytesDirectory); + addTest(testSuite, "FileReadBytesZeroSize", _testFileReadBytesZeroSize); + addTest(testSuite, "FileReadBytesGreaterSize", _testFileReadBytesGreaterSize); + + addTest(testSuite, "FileWrite", _testFileWrite); + addTest(testSuite, "FileWriteMulitple", _testFileWriteMultiple); + addTest(testSuite, "FileWriteInvalid", _testFileWriteInvalid); + addTest(testSuite, "FileWriteDirectory", _testFileWriteDirectory); + + addTest(testSuite, "FileWriteBytes", _testFileWriteBytes); + addTest(testSuite, "FileWriteBytesInvalid", _testFileWriteBytesInvalid); + addTest(testSuite, "FileWriteBytesDirectory", _testFileWriteBytesDirectory); + + addTest(testSuite, "FileGetBasename", _testFileGetBasename); + addTest(testSuite, "FileGetBasenameInvalid", _testFileGetBasenameInvalid); + addTest(testSuite, "FileGetBasenameDirectory", _testFileGetBasenameDirectory); + + addTest(testSuite, "FileGetParent", _testFileGetParent); + addTest(testSuite, "FileGetParentInvalid", _testFileGetParentInvalid); + + addTest(testSuite, "FileGetExtension", _testFileGetExtension); + addTest(testSuite, "FileGetExtensionDirectory", _testFileGetExtensionDirectory); + addTest(testSuite, "FileGetExtensionInvalid", _testFileGetExtensionInvalid); + addTest(testSuite, "FileGetExtensionNone", _testFileGetExtensionNone); + addTest(testSuite, "FileGetExtensionWithDotInPath", _testFileGetExtensionWithDotInPath); + + addTest(testSuite, "FileFreeNull", _testFileFreeNull); + + return testSuite; +} diff --git a/test/base/LinkedListTest.c b/test/base/LinkedListTest.c new file mode 100644 index 0000000..15b4290 --- /dev/null +++ b/test/base/LinkedListTest.c @@ -0,0 +1,245 @@ +#include <stdlib.h> +#include "unit/TestRunner.h" + +static char *const TEST_ITEM_STRING = "test string"; +static char *const OTHER_TEST_ITEM_STRING = "other test string"; + +static boolByte _gNumForeachCallbacksMade; +static boolByte _gForeachCallbackOk; + +static void _linkedListTestSetup(void) +{ + _gNumForeachCallbacksMade = 0; + _gForeachCallbackOk = false; +} + +static int _testNewLinkedList(void) +{ + LinkedList l = newLinkedList(); + assertNotNull(l); + assertIsNull(l->nextItem); + assertIntEquals(0, linkedListLength(l)); + assert(l->item == NULL); + freeLinkedList(l); + return 0; +} + +static int _testAppendItemToList(void) +{ + LinkedList l = newLinkedList(); + CharString c = newCharString(); + charStringCopyCString(c, TEST_ITEM_STRING); + linkedListAppend(l, c); + assertNotNull(l->item); + assertCharStringEquals(TEST_ITEM_STRING, ((CharString)l->item)); + assertIsNull(l->nextItem); + freeLinkedListAndItems(l, (LinkedListFreeItemFunc)freeCharString); + return 0; +} + +static int _testAppendMultipleItemsToList(void) +{ + LinkedListIterator i; + LinkedList l = newLinkedList(); + CharString c = newCharString(); + CharString c2 = newCharString(); + + charStringCopyCString(c, TEST_ITEM_STRING); + charStringCopyCString(c2, OTHER_TEST_ITEM_STRING); + linkedListAppend(l, c); + linkedListAppend(l, c2); + assertNotNull(l->item); + assertCharStringEquals(TEST_ITEM_STRING, ((CharString)l->item)); + assertNotNull(l->nextItem); + i = (LinkedListIterator)(l->nextItem); + assertNotNull(i->item); + assertCharStringEquals(OTHER_TEST_ITEM_STRING, ((CharString)i->item)); + assertIsNull(i->nextItem); + assertIntEquals(2, linkedListLength(l)); + + freeLinkedListAndItems(l, (LinkedListFreeItemFunc)freeCharString); + return 0; +} + +static int _testAppendNullItemToList(void) +{ + LinkedList l = newLinkedList(); + linkedListAppend(l, NULL); + assertIsNull(l->item); + assertIsNull(l->nextItem); + assertIntEquals(0, linkedListLength(l)); + freeLinkedList(l); + return 0; +} + +static int _testAppendItemToNullList(void) +{ + CharString c = newCharString(); + // The test here is not to crash + linkedListAppend(NULL, c); + freeCharString(c); + return 0; +} + +static int _testNumItemsInList(void) +{ + CharString c; + int i; + LinkedList l = newLinkedList(); + + for (i = 0; i < 100; i++) { + c = newCharString(); + charStringCopyCString(c, TEST_ITEM_STRING); + linkedListAppend(l, c); + } + + assertIntEquals(100, linkedListLength(l)); + freeLinkedListAndItems(l, (LinkedListFreeItemFunc)freeCharString); + return 0; +} + +static int _testNumItemsInNullList(void) +{ + assertIntEquals(0, linkedListLength(NULL)); + return 0; +} + +static int _testLinkedListToArray(void) +{ + LinkedList l = newLinkedList(); + CharString *arr; + CharString c; + + linkedListAppend(l, newCharStringWithCString("one")); + linkedListAppend(l, newCharStringWithCString("two")); + + arr = (CharString *)linkedListToArray(l); + assertNotNull(arr); + c = (CharString)arr[0]; + assertCharStringEquals("one", c); + c = (CharString)arr[1]; + assertCharStringEquals("two", c); + assertIsNull(arr[2]); + + free(arr); + freeLinkedListAndItems(l, (LinkedListFreeItemFunc)freeCharString); + return 0; +} + +static int _testLinkedListToArrayWithNull(void) +{ + CharString **arr; + arr = (CharString **)linkedListToArray(NULL); + assertIsNull(arr); + return 0; +} + +static int _testLinkedListWithEmptyList(void) +{ + CharString **arr; + LinkedList l = newLinkedList(); + arr = (CharString **)linkedListToArray(l); + assertIsNull(arr); + freeLinkedList(l); + return 0; +} + + +static void _linkedListEmptyCallback(void *item, void *userData) +{ + _gNumForeachCallbacksMade++; +} + +static int _testForeachOverNullList(void) +{ + linkedListForeach(NULL, _linkedListEmptyCallback, NULL); + assertIntEquals(0, _gNumForeachCallbacksMade); + return 0; +} + +static int _testForeachOverEmptyList(void) +{ + LinkedList l = newLinkedList(); + linkedListForeach(l, _linkedListEmptyCallback, NULL); + assertIntEquals(0, _gNumForeachCallbacksMade); + freeLinkedList(l); + return 0; +} + +static void _linkedListTestStringCallback(void *item, void *userData) +{ + CharString charString = (CharString)item; + _gForeachCallbackOk = charStringIsEqualToCString(charString, TEST_ITEM_STRING, false); + _gNumForeachCallbacksMade++; +} + +static int _testForeachOverList(void) +{ + LinkedList l = newLinkedList(); + CharString c = newCharString(); + + charStringCopyCString(c, TEST_ITEM_STRING); + linkedListAppend(l, c); + linkedListForeach(l, _linkedListTestStringCallback, NULL); + assertIntEquals(1, _gNumForeachCallbacksMade); + assert(_gForeachCallbackOk); + + freeLinkedListAndItems(l, (LinkedListFreeItemFunc)freeCharString); + return 0; +} + +static void _linkedListUserDataCallback(void *item, void *userData) +{ + CharString charString = (CharString)userData; + _gForeachCallbackOk = charStringIsEqualToCString(charString, TEST_ITEM_STRING, false); + _gNumForeachCallbacksMade++; +} + +static int _testForeachOverUserData(void) +{ + LinkedList l = newLinkedList(); + CharString c = newCharString(); + + charStringCopyCString(c, TEST_ITEM_STRING); + linkedListAppend(l, c); + linkedListForeach(l, _linkedListUserDataCallback, c); + assertIntEquals(1, _gNumForeachCallbacksMade); + assert(_gForeachCallbackOk); + + freeLinkedListAndItems(l, (LinkedListFreeItemFunc)freeCharString); + return 0; +} + +static int _testFreeNullLinkedList(void) +{ + freeLinkedList(NULL); + return 0; +} + +TestSuite addLinkedListTests(void); +TestSuite addLinkedListTests(void) +{ + TestSuite testSuite = newTestSuite("LinkedList", _linkedListTestSetup, NULL); + addTest(testSuite, "NewObject", _testNewLinkedList); + + addTest(testSuite, "AppendItem", _testAppendItemToList); + addTest(testSuite, "AppendMultipleItems", _testAppendMultipleItemsToList); + addTest(testSuite, "AppendNullItem", _testAppendNullItemToList); + addTest(testSuite, "AppendItemToNullList", _testAppendItemToNullList); + + addTest(testSuite, "NumItemsInList", _testNumItemsInList); + addTest(testSuite, "NumItemsInNullList", _testNumItemsInNullList); + + addTest(testSuite, "LinkedListToArray", _testLinkedListToArray); + addTest(testSuite, "LinkedListToArrayWithNull", _testLinkedListToArrayWithNull); + addTest(testSuite, "LinkedListWithEmptyList", _testLinkedListWithEmptyList); + + addTest(testSuite, "ForeachOverNullList", _testForeachOverNullList); + addTest(testSuite, "ForeachOverEmptyList", _testForeachOverEmptyList); + addTest(testSuite, "ForeachOverList", _testForeachOverList); + addTest(testSuite, "ForeachWithUserData", _testForeachOverUserData); + + addTest(testSuite, "FreeNullLinkedList", _testFreeNullLinkedList); + + return testSuite; +} diff --git a/test/base/PlatformInfoTest.c b/test/base/PlatformInfoTest.c new file mode 100644 index 0000000..4744602 --- /dev/null +++ b/test/base/PlatformInfoTest.c @@ -0,0 +1,86 @@ +#include "unit/TestRunner.h" +#include "base/PlatformInfo.h" + +static int _testGetPlatformType(void) +{ + PlatformInfo platform = newPlatformInfo(); +#if LINUX + assertIntEquals(PLATFORM_LINUX, platform->type); +#elif MACOSX + assertIntEquals(PLATFORM_MACOSX, platform->type); +#elif WINDOWS + assertIntEquals(PLATFORM_WINDOWS, platform->type); +#else + assertIntEquals(PLATFORM_UNSUPPORTED, platform->type); +#endif + freePlatformInfo(platform); + return 0; +} + +static int _testGetPlatformName(void) +{ + PlatformInfo platform = newPlatformInfo(); +#if LINUX + assertCharStringContains("Linux", platform->name); +#elif MACOSX + assertCharStringContains("Mac OS X", platform->name); +#elif WINDOWS + assertCharStringContains("Windows", platform->name); +#else + assertCharStringEquals("Unsupported platform", platform->name); +#endif + freePlatformInfo(platform); + return 0; +} + +static int _testGetShortPlatformName(void) +{ + PlatformInfo platform = newPlatformInfo(); +#if LINUX + + if (platformInfoIsHost64Bit() && platformInfoIsRuntime64Bit()) { + assertCharStringEquals("Linux-x86_64", platform->shortName); + } else { + assertCharStringEquals("Linux-i686", platform->shortName); + } + +#elif MACOSX + assertCharStringEquals("Mac OS X", platform->shortName); +#elif WINDOWS + + if (platformInfoIsHost64Bit() && platformInfoIsRuntime64Bit()) { + assertCharStringEquals("Windows 64-bit", platform->shortName); + } else { + assertCharStringEquals("Windows 32-bit", platform->shortName); + } + +#else + assertCharStringEquals("Unsupported", platform->shortName); +#endif + freePlatformInfo(platform); + return 0; +} + +static int _testIsHostLittleEndian(void) +{ +#if HOST_BIG_ENDIAN + assertFalse(isHostLittleEndian()); +#elif HOST_LITTLE_ENDIAN + assert(isHostLittleEndian()); +#endif + return 0; +} + +TestSuite addPlatformInfoTests(void); +TestSuite addPlatformInfoTests(void) +{ + TestSuite testSuite = newTestSuite("PlatformInfo", NULL, NULL); + + addTest(testSuite, "GetPlatformType", _testGetPlatformType); + addTest(testSuite, "GetPlatformName", _testGetPlatformName); + addTest(testSuite, "GetShortPlatformName", _testGetShortPlatformName); + + addTest(testSuite, "IsHostLittleEndian", _testIsHostLittleEndian); + + return testSuite; +} diff --git a/test/io/SampleSourceTest.c b/test/io/SampleSourceTest.c new file mode 100644 index 0000000..999c90b --- /dev/null +++ b/test/io/SampleSourceTest.c @@ -0,0 +1,55 @@ +#include "unit/TestRunner.h" +#include "io/SampleSource.h" +#include "audio/AudioSettings.h" + +const char *TEST_SAMPLESOURCE_FILENAME = "test.pcm"; + +static void _sampleSourceSetup(void) +{ + initAudioSettings(); +} + +static void _sampleSourceTeardown(void) +{ + freeAudioSettings(); +} + +static int _testGuessSampleSourceTypePcm(void) +{ + CharString c = newCharStringWithCString(TEST_SAMPLESOURCE_FILENAME); + SampleSource s = sampleSourceFactory(c); + assertIntEquals(SAMPLE_SOURCE_TYPE_PCM, s->sampleSourceType); + freeSampleSource(s); + freeCharString(c); + return 0; +} + +static int _testGuessSampleSourceTypeEmpty(void) +{ + CharString empty = newCharString(); + SampleSource s = sampleSourceFactory(empty); + assertIntEquals(SAMPLE_SOURCE_TYPE_SILENCE, s->sampleSourceType); + freeSampleSource(s); + freeCharString(empty); + return 0; +} + +static int _testGuessSampleSourceTypeWrongCase(void) +{ + CharString c = newCharStringWithCString("TEST.PCM"); + SampleSource s = sampleSourceFactory(c); + assertIntEquals(SAMPLE_SOURCE_TYPE_PCM, s->sampleSourceType); + freeSampleSource(s); + freeCharString(c); + return 0; +} + +TestSuite addSampleSourceTests(void); +TestSuite addSampleSourceTests(void) +{ + TestSuite testSuite = newTestSuite("SampleSource", _sampleSourceSetup, _sampleSourceTeardown); + addTest(testSuite, "GuessSampleSourceTypePcm", _testGuessSampleSourceTypePcm); + addTest(testSuite, "GuessSampleSourceTypeEmpty", _testGuessSampleSourceTypeEmpty); + addTest(testSuite, "GuessSampleSourceTypeWrongCase", _testGuessSampleSourceTypeWrongCase); + return testSuite; +} diff --git a/test/midi/MidiSequenceTest.c b/test/midi/MidiSequenceTest.c new file mode 100644 index 0000000..6b306ba --- /dev/null +++ b/test/midi/MidiSequenceTest.c @@ -0,0 +1,138 @@ +#include "unit/TestRunner.h" +#include "midi/MidiSequence.h" + +static int _testNewMidiSequence(void) +{ + MidiSequence m = newMidiSequence(); + assertNotNull(m); + assertIntEquals(0, linkedListLength(m->midiEvents)); + freeMidiSequence(m); + return 0; +} + +static int _testAppendMidiEventToSequence(void) +{ + MidiSequence m = newMidiSequence(); + MidiEvent e = newMidiEvent(); + appendMidiEventToSequence(m, e); + assertIntEquals(1, linkedListLength(m->midiEvents)); + freeMidiSequence(m); + return 0; +} + +static int _testAppendNullMidiEventToSequence(void) +{ + MidiSequence m = newMidiSequence(); + appendMidiEventToSequence(m, NULL); + assertIntEquals(0, linkedListLength(m->midiEvents)); + freeMidiSequence(m); + return 0; +} + +static int _testAppendEventToNullSequence(void) +{ + // Test is not crashing + MidiEvent e = newMidiEvent(); + appendMidiEventToSequence(NULL, e); + freeMidiEvent(e); + return 0; +} + +static int _testFillMidiEventsFromRangeStart(void) +{ + MidiSequence m = newMidiSequence(); + MidiEvent e = newMidiEvent(); + LinkedList l = newLinkedList(); + + e->status = 0xf7; + e->timestamp = 100; + appendMidiEventToSequence(m, e); + assertFalse(fillMidiEventsFromRange(m, 0, 256, l)); + assertIntEquals(1, linkedListLength(l)); + assertIntEquals(0xf7, ((MidiEvent)l->item)->status); + + freeMidiSequence(m); + freeLinkedList(l); + return 0; +} + +static int _testFillEventsFromEmptyRange(void) +{ + MidiSequence m = newMidiSequence(); + MidiEvent e = newMidiEvent(); + LinkedList l = newLinkedList(); + + e->status = 0xf7; + e->timestamp = 100; + appendMidiEventToSequence(m, e); + assert(fillMidiEventsFromRange(m, 0, 0, l)); + assertIntEquals(0, linkedListLength(l)); + + freeMidiSequence(m); + freeLinkedList(l); + return 0; +} + +static int _testFillEventsSequentially(void) +{ + MidiSequence m = newMidiSequence(); + MidiEvent e = newMidiEvent(); + MidiEvent e2 = newMidiEvent(); + LinkedList l = newLinkedList(); + + e->status = 0xf7; + e->timestamp = 100; + e2->status = 0xf7; + e2->timestamp = 300; + appendMidiEventToSequence(m, e); + appendMidiEventToSequence(m, e2); + assert(fillMidiEventsFromRange(m, 0, 256, l)); + assertIntEquals(1, linkedListLength(l)); + freeLinkedList(l); + l = newLinkedList(); + assertFalse(fillMidiEventsFromRange(m, 256, 256, l)); + assertIntEquals(1, linkedListLength(l)); + + freeMidiSequence(m); + freeLinkedList(l); + return 0; +} + +static int _testFillEventsFromRangePastSequence(void) +{ + MidiSequence m = newMidiSequence(); + MidiEvent e = newMidiEvent(); + LinkedList l = newLinkedList(); + + e->status = 0xf7; + e->timestamp = 100; + appendMidiEventToSequence(m, e); + // Should return false since this is the last event in the sequence + assertFalse(fillMidiEventsFromRange(m, 0, 200, l)); + assertIntEquals(1, linkedListLength(l)); + freeLinkedList(l); + l = newLinkedList(); + assertFalse(fillMidiEventsFromRange(m, 200, 256, l)); + assertIntEquals(0, linkedListLength(l)); + + freeMidiSequence(m); + freeLinkedList(l); + return 0; +} + +TestSuite addMidiSequenceTests(void); +TestSuite addMidiSequenceTests(void) +{ + TestSuite testSuite = newTestSuite("MidiSequence", NULL, NULL); + + addTest(testSuite, "Initialization", _testNewMidiSequence); + addTest(testSuite, "AppendEvent", _testAppendMidiEventToSequence); + addTest(testSuite, "AppendNullEvent", _testAppendNullMidiEventToSequence); + addTest(testSuite, "AppendEventToNullSequence", _testAppendEventToNullSequence); + addTest(testSuite, "FillEventsFromRangeStart", _testFillMidiEventsFromRangeStart); + addTest(testSuite, "FillEventsFromEmptyRange", _testFillEventsFromEmptyRange); + addTest(testSuite, "FillEventsSequentially", _testFillEventsSequentially); + addTest(testSuite, "FillEventsFromRangePastSequenceEnd", _testFillEventsFromRangePastSequence); + + return testSuite; +} diff --git a/test/midi/MidiSourceTest.c b/test/midi/MidiSourceTest.c new file mode 100644 index 0000000..c64eb65 --- /dev/null +++ b/test/midi/MidiSourceTest.c @@ -0,0 +1,41 @@ +#include "unit/TestRunner.h" +#include "midi/MidiSource.h" + +const char *TEST_MIDI_FILENAME = "test.mid"; + +static int _testGuessMidiSourceType(void) +{ + CharString c = newCharStringWithCString(TEST_MIDI_FILENAME); + assertIntEquals(MIDI_SOURCE_TYPE_FILE, guessMidiSourceType(c)); + freeCharString(c); + return 0; +} + +static int _testGuessMidiSourceTypeInvalid(void) +{ + CharString c = newCharStringWithCString("invalid"); + assertIntEquals(MIDI_SOURCE_TYPE_INVALID, guessMidiSourceType(c)); + freeCharString(c); + return 0; +} + +static int _testNewMidiSource(void) +{ + CharString c = newCharStringWithCString(TEST_MIDI_FILENAME); + MidiSource m = newMidiSource(MIDI_SOURCE_TYPE_FILE, c); + assertCharStringEquals(TEST_MIDI_FILENAME, m->sourceName); + assertIntEquals(MIDI_SOURCE_TYPE_FILE, m->midiSourceType); + freeMidiSource(m); + freeCharString(c); + return 0; +} + +TestSuite addMidiSourceTests(void); +TestSuite addMidiSourceTests(void) +{ + TestSuite testSuite = newTestSuite("MidiSource", NULL, NULL); + addTest(testSuite, "GuessMidiSourceType", _testGuessMidiSourceType); + addTest(testSuite, "GuessMidiSourceTypeInvalid", _testGuessMidiSourceTypeInvalid); + addTest(testSuite, "NewObject", _testNewMidiSource); + return testSuite; +} diff --git a/test/mrswatsontest b/test/mrswatsontest Binary files differnew file mode 100755 index 0000000..8a90eb0 --- /dev/null +++ b/test/mrswatsontest diff --git a/test/mrswatsontest64 b/test/mrswatsontest64 Binary files differnew file mode 100755 index 0000000..74b31af --- /dev/null +++ b/test/mrswatsontest64 diff --git a/test/plugin/PluginChainTest.c b/test/plugin/PluginChainTest.c new file mode 100644 index 0000000..cb07f71 --- /dev/null +++ b/test/plugin/PluginChainTest.c @@ -0,0 +1,311 @@ +#include "unit/TestRunner.h" +#include "audio/AudioSettings.h" +#include "midi/MidiEvent.h" +#include "plugin/PluginChain.h" +#include "plugin/PluginPassthru.h" + +#include "PluginMock.h" +#include "PluginPresetMock.h" + +static void _pluginChainTestSetup(void) +{ + initPluginChain(); +} + +static void _pluginChainTestTeardown(void) +{ + freePluginChain(getPluginChain()); +} + +static int _testInitPluginChain(void) +{ + PluginChain p = getPluginChain(); + assertIntEquals(0, p->numPlugins); + assertNotNull(p->plugins); + assertNotNull(p->presets); + return 0; +} + +static int _testAddFromArgumentStringNull(void) +{ + PluginChain p = getPluginChain(); + CharString c = newCharStringWithCString("/"); + + assertFalse(pluginChainAddFromArgumentString(p, NULL, c)); + assertIntEquals(0, p->numPlugins); + + freeCharString(c); + return 0; +} + +static int _testAddFromArgumentStringEmpty(void) +{ + PluginChain p = getPluginChain(); + CharString c = newCharStringWithCString("/"); + CharString empty = newCharString(); + + assertFalse(pluginChainAddFromArgumentString(p, empty, c)); + assertIntEquals(0, p->numPlugins); + + freeCharString(c); + freeCharString(empty); + return 0; +} + +static int _testAddFromArgumentStringEmptyLocation(void) +{ + PluginChain p = getPluginChain(); + CharString c = newCharStringWithCString(kInternalPluginPassthruName); + CharString empty = newCharString(); + + assert(pluginChainAddFromArgumentString(p, c, empty)); + assertIntEquals(1, p->numPlugins); + + freeCharString(c); + freeCharString(empty); + return 0; +} + +static int _testAddFromArgumentStringNullLocation(void) +{ + PluginChain p = getPluginChain(); + CharString c = newCharStringWithCString(kInternalPluginPassthruName); + + assert(pluginChainAddFromArgumentString(p, c, NULL)); + assertIntEquals(1, p->numPlugins); + + freeCharString(c); + return 0; +} + +static int _testAddFromArgumentString(void) +{ + PluginChain p = getPluginChain(); + CharString testArgs = newCharStringWithCString(kInternalPluginPassthruName); + + assert(pluginChainAddFromArgumentString(p, testArgs, NULL)); + assertIntEquals(1, p->numPlugins); + assertNotNull(p->plugins[0]); + assertIntEquals(PLUGIN_TYPE_INTERNAL, p->plugins[0]->pluginType); + assertCharStringEquals(kInternalPluginPassthruName, p->plugins[0]->pluginName); + + freeCharString(testArgs); + return 0; +} + +static int _testAddFromArgumentStringMultiple(void) +{ + PluginChain p = getPluginChain(); + CharString testArgs = newCharStringWithCString(kInternalPluginPassthruName); + unsigned int i; + + assert(pluginChainAddFromArgumentString(p, testArgs, NULL)); + assert(pluginChainAddFromArgumentString(p, testArgs, NULL)); + assertIntEquals(2, p->numPlugins); + + for (i = 0; i < p->numPlugins; i++) { + assertNotNull(p->plugins[i]); + assertIntEquals(PLUGIN_TYPE_INTERNAL, p->plugins[i]->pluginType); + assertCharStringEquals(kInternalPluginPassthruName, p->plugins[i]->pluginName); + } + + freeCharString(testArgs); + return 0; +} + +static int _testAddPluginWithPresetFromArgumentString(void) +{ + PluginChain p = getPluginChain(); + CharString testArgs = newCharStringWithCString("mrs_passthru,testPreset.fxp"); + + assert(pluginChainAddFromArgumentString(p, testArgs, NULL)); + assertIntEquals(1, p->numPlugins); + assertIntEquals(PLUGIN_TYPE_INTERNAL, p->plugins[0]->pluginType); + assertCharStringEquals(kInternalPluginPassthruName, p->plugins[0]->pluginName); + assertNotNull(p->presets[0]); + assertCharStringEquals("testPreset.fxp", p->presets[0]->presetName); + + freeCharString(testArgs); + return 0; +} + +static int _testAddFromArgumentStringWithPresetSpaces(void) +{ + PluginChain p = getPluginChain(); + CharString testArgs = newCharStringWithCString("mrs_passthru,test preset.fxp"); + + assert(pluginChainAddFromArgumentString(p, testArgs, NULL)); + assertIntEquals(1, p->numPlugins); + assertIntEquals(PLUGIN_TYPE_INTERNAL, p->plugins[0]->pluginType); + assertCharStringEquals(kInternalPluginPassthruName, p->plugins[0]->pluginName); + assertNotNull(p->presets[0]); + assertCharStringEquals("test preset.fxp", p->presets[0]->presetName); + + freeCharString(testArgs); + return 0; +} + +static int _testAppendPlugin(void) +{ + Plugin mock = newPluginMock(); + PluginChain p = getPluginChain(); + assert(pluginChainAppend(p, mock, NULL)); + return 0; +} + +static int _testAppendWithNullPlugin(void) +{ + PluginChain p = getPluginChain(); + assertFalse(pluginChainAppend(p, NULL, NULL)); + return 0; +} + +static int _testAppendWithPreset(void) +{ + Plugin mock = newPluginMock(); + PluginChain p = getPluginChain(); + PluginPreset mockPreset = newPluginPresetMock(); + assert(pluginChainAppend(p, mock, mockPreset)); + return 0; +} + +static int _testInitializePluginChain(void) +{ + Plugin mock = newPluginMock(); + PluginChain p = getPluginChain(); + PluginPreset mockPreset = newPluginPresetMock(); + + assert(pluginChainAppend(p, mock, mockPreset)); + pluginChainInitialize(p); + assert(((PluginMockData)mock->extraData)->isOpen); + assert(((PluginPresetMockData)mockPreset->extraData)->isOpen); + assert(((PluginPresetMockData)mockPreset->extraData)->isLoaded); + + return 0; +} + +static int _testGetMaximumTailTime(void) +{ + Plugin mock = newPluginMock(); + PluginChain p = getPluginChain(); + int maxTailTime = 0; + + assert(pluginChainAppend(p, mock, NULL)); + maxTailTime = pluginChainGetMaximumTailTimeInMs(p); + assertIntEquals(kPluginMockTailTime, maxTailTime); + + return 0; +} + +static int _testPrepareForProcessing(void) +{ + Plugin mock = newPluginMock(); + PluginChain p = getPluginChain(); + + assert(pluginChainAppend(p, mock, NULL)); + assertIntEquals(RETURN_CODE_SUCCESS, pluginChainInitialize(p)); + pluginChainPrepareForProcessing(p); + assert(((PluginMockData)mock->extraData)->isPrepared); + + return 0; +} + +static int _testProcessPluginChainAudio(void) +{ + Plugin mock = newPluginMock(); + PluginChain p = getPluginChain(); + SampleBuffer inBuffer = newSampleBuffer(DEFAULT_NUM_CHANNELS, DEFAULT_BLOCKSIZE); + SampleBuffer outBuffer = newSampleBuffer(DEFAULT_NUM_CHANNELS, DEFAULT_BLOCKSIZE); + + assert(pluginChainAppend(p, mock, NULL)); + pluginChainProcessAudio(p, inBuffer, outBuffer); + assert(((PluginMockData)mock->extraData)->processAudioCalled); + + freeSampleBuffer(inBuffer); + freeSampleBuffer(outBuffer); + return 0; +} + +static int _testProcessPluginChainAudioRealtime(void) +{ + Plugin mock = newPluginMock(); + PluginChain p = getPluginChain(); + SampleBuffer inBuffer = newSampleBuffer(DEFAULT_NUM_CHANNELS, DEFAULT_BLOCKSIZE); + SampleBuffer outBuffer = newSampleBuffer(DEFAULT_NUM_CHANNELS, DEFAULT_BLOCKSIZE); + TaskTimer t = newTaskTimerWithCString("test", "test"); + + assert(pluginChainAppend(p, mock, NULL)); + pluginChainSetRealtime(p, true); + taskTimerStart(t); + pluginChainProcessAudio(p, inBuffer, outBuffer); + assertTimeEquals(1000 * DEFAULT_BLOCKSIZE / getSampleRate(), taskTimerStop(t), 0.1); + assert(((PluginMockData)mock->extraData)->processAudioCalled); + + freeTaskTimer(t); + freeSampleBuffer(inBuffer); + freeSampleBuffer(outBuffer); + return 0; +} + +static int _testProcessPluginChainMidiEvents(void) +{ + Plugin mock = newPluginMock(); + PluginChain p = getPluginChain(); + SampleBuffer inBuffer = newSampleBuffer(DEFAULT_NUM_CHANNELS, DEFAULT_BLOCKSIZE); + SampleBuffer outBuffer = newSampleBuffer(DEFAULT_NUM_CHANNELS, DEFAULT_BLOCKSIZE); + LinkedList list = newLinkedList(); + MidiEvent midi = newMidiEvent(); + + linkedListAppend(list, midi); + assert(pluginChainAppend(p, mock, NULL)); + pluginChainProcessMidi(p, list); + assert(((PluginMockData)mock->extraData)->processMidiCalled); + + freeMidiEvent(midi); + freeLinkedList(list); + freeSampleBuffer(inBuffer); + freeSampleBuffer(outBuffer); + return 0; +} + +static int _testShutdown(void) +{ + Plugin mock = newPluginMock(); + PluginChain p = getPluginChain(); + + assert(pluginChainAppend(p, mock, NULL)); + pluginChainShutdown(p); + assertFalse(((PluginMockData)mock->extraData)->isOpen); + + return 0; +} + +TestSuite addPluginChainTests(void); +TestSuite addPluginChainTests(void) +{ + TestSuite testSuite = newTestSuite("PluginChain", _pluginChainTestSetup, _pluginChainTestTeardown); + addTest(testSuite, "Initialization", _testInitPluginChain); + addTest(testSuite, "AddFromArgumentStringNull", _testAddFromArgumentStringNull); + addTest(testSuite, "AddFromArgumentStringEmpty", _testAddFromArgumentStringEmpty); + addTest(testSuite, "AddFromArgumentStringEmptyLocation", _testAddFromArgumentStringEmptyLocation); + addTest(testSuite, "AddFromArgumentStringNullLocation", _testAddFromArgumentStringNullLocation); + addTest(testSuite, "AddFromArgumentString", _testAddFromArgumentString); + addTest(testSuite, "AddFromArgumentStringMultiple", _testAddFromArgumentStringMultiple); + addTest(testSuite, "AddPluginWithPresetFromArgumentString", _testAddPluginWithPresetFromArgumentString); + addTest(testSuite, "AddFromArgumentStringWithPresetSpaces", _testAddFromArgumentStringWithPresetSpaces); + addTest(testSuite, "AppendPlugin", _testAppendPlugin); + addTest(testSuite, "AppendWithNullPlugin", _testAppendWithNullPlugin); + addTest(testSuite, "AppendWithPreset", _testAppendWithPreset); + addTest(testSuite, "InitializePluginChain", _testInitializePluginChain); + + addTest(testSuite, "GetMaximumTailTime", _testGetMaximumTailTime); + + addTest(testSuite, "PrepareForProcessing", _testPrepareForProcessing); + addTest(testSuite, "ProcessPluginChainAudio", _testProcessPluginChainAudio); + addTest(testSuite, "ProcessPluginChainAudioRealtime", _testProcessPluginChainAudioRealtime); + addTest(testSuite, "ProcessPluginChainMidiEvents", _testProcessPluginChainMidiEvents); + + addTest(testSuite, "Shutdown", _testShutdown); + + return testSuite; +} diff --git a/test/plugin/PluginMock.c b/test/plugin/PluginMock.c new file mode 100644 index 0000000..249a722 --- /dev/null +++ b/test/plugin/PluginMock.c @@ -0,0 +1,95 @@ +#include "PluginMock.h" + + +static void _pluginMockEmpty(void *pluginPtr) +{ + // Nothing to do here +} + +static boolByte _pluginMockOpen(void *pluginPtr) +{ + Plugin self = (Plugin)pluginPtr; + PluginMockData extraData = (PluginMockData)self->extraData; + extraData->isOpen = true; + return true; +} + +static int _pluginMockGetSetting(void *pluginPtr, PluginSetting pluginSetting) +{ + switch (pluginSetting) { + case PLUGIN_SETTING_TAIL_TIME_IN_MS: + return kPluginMockTailTime; + + case PLUGIN_NUM_INPUTS: + return 2; + + case PLUGIN_NUM_OUTPUTS: + return 2; + + case PLUGIN_INITIAL_DELAY: + return 0; + + default: + return 0; + } +} + +static void _pluginMockPrepareForProcessing(void *pluginPtr) +{ + Plugin self = (Plugin)pluginPtr; + PluginMockData extraData = (PluginMockData)self->extraData; + extraData->isPrepared = true; +} + +static void _pluginMockProcessAudio(void *pluginPtr, SampleBuffer inputs, SampleBuffer outputs) +{ + Plugin self = (Plugin)pluginPtr; + PluginMockData extraData = (PluginMockData)self->extraData; + extraData->processAudioCalled = true; + sampleBufferClear(outputs); +} + +static void _pluginMockProcessMidiEvents(void *pluginPtr, LinkedList midiEvents) +{ + Plugin self = (Plugin)pluginPtr; + PluginMockData extraData = (PluginMockData)self->extraData; + extraData->processMidiCalled = true; +} + +static boolByte _pluginMockSetParameter(void *pluginPtr, unsigned int i, float value) +{ + return false; +} + +static void _pluginMockClose(void *pluginPtr) +{ + Plugin self = (Plugin)pluginPtr; + PluginMockData extraData = (PluginMockData)self->extraData; + extraData->isOpen = false; +} + +Plugin newPluginMock(void) +{ + Plugin plugin = _newPlugin(PLUGIN_TYPE_INTERNAL, PLUGIN_TYPE_INSTRUMENT); + charStringCopyCString(plugin->pluginName, "Mock"); + charStringCopyCString(plugin->pluginLocation, "Internal"); + + plugin->openPlugin = _pluginMockOpen; + plugin->displayInfo = _pluginMockEmpty; + plugin->getSetting = _pluginMockGetSetting; + plugin->prepareForProcessing = _pluginMockPrepareForProcessing; + plugin->processAudio = _pluginMockProcessAudio; + plugin->processMidiEvents = _pluginMockProcessMidiEvents; + plugin->setParameter = _pluginMockSetParameter; + plugin->closePlugin = _pluginMockClose; + plugin->freePluginData = _pluginMockEmpty; + + PluginMockData extraData = (PluginMockData)malloc(sizeof(PluginMockDataMembers)); + extraData->isOpen = false; + extraData->isPrepared = false; + extraData->processAudioCalled = false; + extraData->processMidiCalled = false; + plugin->extraData = extraData; + + return plugin; +} diff --git a/test/plugin/PluginMock.h b/test/plugin/PluginMock.h new file mode 100644 index 0000000..e5eaa15 --- /dev/null +++ b/test/plugin/PluginMock.h @@ -0,0 +1,19 @@ +#ifndef MrsWatson_PluginMock_h +#define MrsWatson_PluginMock_h + +#include "base/Types.h" +#include "plugin/Plugin.h" + +static const int kPluginMockTailTime = 123; + +typedef struct { + boolByte isOpen; + boolByte isPrepared; + boolByte processAudioCalled; + boolByte processMidiCalled; +} PluginMockDataMembers; +typedef PluginMockDataMembers *PluginMockData; + +Plugin newPluginMock(void); + +#endif diff --git a/test/plugin/PluginPresetMock.c b/test/plugin/PluginPresetMock.c new file mode 100644 index 0000000..8911c75 --- /dev/null +++ b/test/plugin/PluginPresetMock.c @@ -0,0 +1,42 @@ +#include "PluginPresetMock.h" + +static boolByte _openPluginPresetMock(void *pluginPresetPtr) +{ + PluginPreset pluginPreset = (PluginPreset)pluginPresetPtr; + PluginPresetMockData extraData = (PluginPresetMockData)pluginPreset->extraData; + extraData->isOpen = true; + return true; +} + +static boolByte _loadPluginPresetMock(void *pluginPresetPtr, Plugin plugin) +{ + PluginPreset pluginPreset = (PluginPreset)pluginPresetPtr; + PluginPresetMockData extraData = (PluginPresetMockData)pluginPreset->extraData; + extraData->isLoaded = true; + return true; +} + +static void _freePluginPresetMock(void *extraDataPtr) +{ +} + +PluginPreset newPluginPresetMock(void) +{ + PluginPreset pluginPreset = (PluginPreset)malloc(sizeof(PluginPresetMembers)); + PluginPresetMockData extraData = (PluginPresetMockData)malloc(sizeof(PluginPresetMockDataMembers)); + + pluginPreset->presetType = PRESET_TYPE_INTERNAL_PROGRAM; + pluginPreset->presetName = newCharString(); + pluginPreset->compatiblePluginTypes = 0; + pluginPresetSetCompatibleWith(pluginPreset, PLUGIN_TYPE_INTERNAL); + + pluginPreset->openPreset = _openPluginPresetMock; + pluginPreset->loadPreset = _loadPluginPresetMock; + pluginPreset->freePresetData = _freePluginPresetMock; + + extraData->isOpen = false; + extraData->isLoaded = false; + pluginPreset->extraData = extraData; + + return pluginPreset; +} diff --git a/test/plugin/PluginPresetMock.h b/test/plugin/PluginPresetMock.h new file mode 100644 index 0000000..70c9089 --- /dev/null +++ b/test/plugin/PluginPresetMock.h @@ -0,0 +1,15 @@ +#ifndef MrsWaston_PluginPresetMock_h +#define MrsWaston_PluginPresetMock_h + +#include "base/Types.h" +#include "plugin/PluginPreset.h" + +typedef struct { + boolByte isOpen; + boolByte isLoaded; +} PluginPresetMockDataMembers; +typedef PluginPresetMockDataMembers *PluginPresetMockData; + +PluginPreset newPluginPresetMock(void); + +#endif diff --git a/test/plugin/PluginPresetTest.c b/test/plugin/PluginPresetTest.c new file mode 100644 index 0000000..e16024f --- /dev/null +++ b/test/plugin/PluginPresetTest.c @@ -0,0 +1,78 @@ +#include "unit/TestRunner.h" +#include "plugin/PluginPreset.h" +#include "PluginMock.h" + +const char *TEST_PRESET_FILENAME = "test.fxp"; + +static int _testGuessPluginPresetType(void) +{ + CharString c = newCharStringWithCString(TEST_PRESET_FILENAME); + PluginPreset p = pluginPresetFactory(c); + assertIntEquals(PRESET_TYPE_FXP, p->presetType); + freePluginPreset(p); + freeCharString(c); + return 0; +} + +static int _testGuessPluginPresetTypeInvalid(void) +{ + CharString c = newCharStringWithCString("invalid"); + PluginPreset p = pluginPresetFactory(c); + assertIsNull(p); + freePluginPreset(p); + freeCharString(c); + return 0; +} + +static int _testNewObject(void) +{ + CharString c = newCharStringWithCString(TEST_PRESET_FILENAME); + PluginPreset p = pluginPresetFactory(c); + assertIntEquals(p->presetType, PRESET_TYPE_FXP); + assertCharStringEquals(TEST_PRESET_FILENAME, p->presetName); + freePluginPreset(p); + freeCharString(c); + return 0; +} + +static int _testIsPresetCompatibleWithPlugin(void) +{ + CharString c = newCharStringWithCString(TEST_PRESET_FILENAME); + PluginPreset p = pluginPresetFactory(c); + Plugin mockPlugin = newPluginMock(); + + pluginPresetSetCompatibleWith(p, PLUGIN_TYPE_INTERNAL); + assert(pluginPresetIsCompatibleWith(p, mockPlugin)); + + freePlugin(mockPlugin); + freeCharString(c); + freePluginPreset(p); + return 0; +} + +static int _testIsPresetNotCompatibleWithPlugin(void) +{ + CharString c = newCharStringWithCString(TEST_PRESET_FILENAME); + PluginPreset p = pluginPresetFactory(c); + Plugin mockPlugin = newPluginMock(); + + pluginPresetSetCompatibleWith(p, PLUGIN_TYPE_VST_2X); + assertFalse(pluginPresetIsCompatibleWith(p, mockPlugin)); + + freePlugin(mockPlugin); + freeCharString(c); + freePluginPreset(p); + return 0; +} + +TestSuite addPluginPresetTests(void); +TestSuite addPluginPresetTests(void) +{ + TestSuite testSuite = newTestSuite("PluginPreset", NULL, NULL); + addTest(testSuite, "GuessPluginPresetType", _testGuessPluginPresetType); + addTest(testSuite, "GuessPluginPresetTypeInvalid", _testGuessPluginPresetTypeInvalid); + addTest(testSuite, "NewObject", _testNewObject); + addTest(testSuite, "IsPresetCompatibleWithPlugin", _testIsPresetCompatibleWithPlugin); + addTest(testSuite, "IsPresetNotCompatibleWithPlugin", _testIsPresetNotCompatibleWithPlugin); + return testSuite; +} diff --git a/test/plugin/PluginTest.c b/test/plugin/PluginTest.c new file mode 100644 index 0000000..7c2a575 --- /dev/null +++ b/test/plugin/PluginTest.c @@ -0,0 +1,91 @@ +#include "unit/TestRunner.h" +#include "plugin/Plugin.h" + +static int _testPluginFactory(void) +{ + CharString silence = newCharStringWithCString("mrs_silence"); + CharString pluginRoot = newCharString(); + Plugin p = pluginFactory(silence, pluginRoot); + + assertNotNull(p); + assertIntEquals(PLUGIN_TYPE_INTERNAL, p->interfaceType); + assertCharStringEquals(silence->data, p->pluginName); + + freeCharString(silence); + freeCharString(pluginRoot); + freePlugin(p); + return 0; +} + +static int _testPluginFactoryInvalidPlugin(void) +{ + CharString invalid = newCharStringWithCString("invalid"); + CharString pluginRoot = newCharString(); + Plugin p = pluginFactory(invalid, pluginRoot); + + assertIsNull(p); + + freeCharString(invalid); + freeCharString(pluginRoot); + freePlugin(p); + return 0; +} + +static int _testPluginFactoryNullPluginName(void) +{ + CharString pluginRoot = newCharString(); + Plugin p = pluginFactory(NULL, pluginRoot); + + assertIsNull(p); + + freeCharString(pluginRoot); + freePlugin(p); + return 0; +} + +static int _testPluginFactoryEmptyPluginName(void) +{ + CharString invalid = newCharString(); + CharString pluginRoot = newCharString(); + Plugin p = pluginFactory(invalid, pluginRoot); + + assertIsNull(p); + + freeCharString(invalid); + freeCharString(pluginRoot); + freePlugin(p); + return 0; +} + +static int _testPluginFactoryNullRoot(void) +{ + CharString silence = newCharStringWithCString("mrs_silence"); + Plugin p = pluginFactory(silence, NULL); + + assertNotNull(p); + assertIntEquals(PLUGIN_TYPE_INTERNAL, p->interfaceType); + assertCharStringEquals(silence->data, p->pluginName); + + freeCharString(silence); + freePlugin(p); + return 0; +} + +static int _testFreeNullPlugin(void) +{ + freePlugin(NULL); + return 0; +} + +TestSuite addPluginTests(void); +TestSuite addPluginTests(void) +{ + TestSuite testSuite = newTestSuite("Plugin", NULL, NULL); + addTest(testSuite, "PluginFactory", _testPluginFactory); + addTest(testSuite, "PluginFactoryInvalidPlugin", _testPluginFactoryInvalidPlugin); + addTest(testSuite, "PluginFactoryNullPluginName", _testPluginFactoryNullPluginName); + addTest(testSuite, "PluginFactoryEmptyPluginName", _testPluginFactoryEmptyPluginName); + addTest(testSuite, "PluginFactoryNullRoot", _testPluginFactoryNullRoot); + addTest(testSuite, "FreeNullPlugin", _testFreeNullPlugin); + return testSuite; +} diff --git a/test/plugin/PluginVst2xIdTest.c b/test/plugin/PluginVst2xIdTest.c new file mode 100644 index 0000000..a065a55 --- /dev/null +++ b/test/plugin/PluginVst2xIdTest.c @@ -0,0 +1,87 @@ +#include "unit/TestRunner.h" +#include "plugin/PluginVst2xId.h" + +static int _testNewPluginVst2xId(void) +{ + PluginVst2xId id = newPluginVst2xId(); + assertUnsignedLongEquals(ZERO_UNSIGNED_LONG, id->id); + assertCharStringEquals(PLUGIN_VST2X_ID_UNKNOWN, id->idString); + freePluginVst2xId(id); + return 0; +} + +static int _testNewPluginVst2xIdWithIntId(void) +{ + PluginVst2xId id = newPluginVst2xIdWithId(0x61626364); + assertUnsignedLongEquals(0x61626364l, id->id); + assertCharStringEquals("abcd", id->idString); + freePluginVst2xId(id); + return 0; +} + +static int _testNewPluginVst2xIdWithZeroIntId(void) +{ + PluginVst2xId id = newPluginVst2xIdWithId(0); + assertUnsignedLongEquals(ZERO_UNSIGNED_LONG, id->id); + assertCharStringEquals(EMPTY_STRING, id->idString); + freePluginVst2xId(id); + return 0; +} + +static int _testNewPluginVst2xIdWithStringId(void) +{ + CharString c = newCharStringWithCString("abcd"); + PluginVst2xId id = newPluginVst2xIdWithStringId(c); + assertUnsignedLongEquals(0x61626364l, id->id); + assertCharStringEquals(c->data, id->idString); + freePluginVst2xId(id); + freeCharString(c); + return 0; +} + +static int _testNewPluginVst2xIdWithEmptyStringId(void) +{ + CharString empty = newCharStringWithCString(EMPTY_STRING); + PluginVst2xId id = newPluginVst2xIdWithStringId(empty); + assertUnsignedLongEquals(ZERO_UNSIGNED_LONG, id->id); + assertCharStringEquals(PLUGIN_VST2X_ID_UNKNOWN, id->idString); + freePluginVst2xId(id); + freeCharString(empty); + return 0; +} + +static int _testNewPluginVst2xIdWithNullStringId(void) +{ + PluginVst2xId id = newPluginVst2xIdWithStringId(NULL); + assertUnsignedLongEquals(ZERO_UNSIGNED_LONG, id->id); + assertCharStringEquals(PLUGIN_VST2X_ID_UNKNOWN, id->idString); + freePluginVst2xId(id); + return 0; +} + +static int _testNewPluginVst2xIdWithInvalidStringId(void) +{ + CharString c = newCharStringWithCString("a"); + PluginVst2xId id = newPluginVst2xIdWithStringId(c); + assertUnsignedLongEquals(ZERO_UNSIGNED_LONG, id->id); + assertCharStringEquals(PLUGIN_VST2X_ID_UNKNOWN, id->idString); + freePluginVst2xId(id); + freeCharString(c); + return 0; +} + +TestSuite addPluginVst2xIdTests(void); +TestSuite addPluginVst2xIdTests(void) +{ + TestSuite testSuite = newTestSuite("PluginVst2xId", NULL, NULL); + + addTest(testSuite, "NewPluginVst2xId", _testNewPluginVst2xId); + addTest(testSuite, "NewPluginVst2xIdWithIntId", _testNewPluginVst2xIdWithIntId); + addTest(testSuite, "NewPluginVst2xIdWithZeroIntId", _testNewPluginVst2xIdWithZeroIntId); + addTest(testSuite, "NewPluginVst2xIdWithStringId", _testNewPluginVst2xIdWithStringId); + addTest(testSuite, "NewPluginVst2xIdWithNullStringId", _testNewPluginVst2xIdWithNullStringId); + addTest(testSuite, "NewPluginVst2xIdWithEmptyStringId", _testNewPluginVst2xIdWithEmptyStringId); + addTest(testSuite, "NewPluginVst2xIdWithInvalidStringId", _testNewPluginVst2xIdWithInvalidStringId); + + return testSuite; +} diff --git a/test/time/AudioClockTest.c b/test/time/AudioClockTest.c new file mode 100644 index 0000000..8f29a8c --- /dev/null +++ b/test/time/AudioClockTest.c @@ -0,0 +1,82 @@ +#include "unit/TestRunner.h" +#include "time/AudioClock.h" + +static const unsigned long kAudioClockTestBlocksize = 256; + +static void _audioClockTestSetup(void) +{ + initAudioClock(); +} + +static void _audioClockTestTeardown(void) +{ + freeAudioClock(getAudioClock()); +} + +static int _testInitAudioClock(void) +{ + AudioClock audioClock = getAudioClock(); + assertUnsignedLongEquals(ZERO_UNSIGNED_LONG, audioClock->currentFrame); + assertFalse(audioClock->isPlaying); + assertFalse(audioClock->transportChanged); + return 0; +} + +static int _testAdvanceAudioClock(void) +{ + AudioClock audioClock = getAudioClock(); + advanceAudioClock(audioClock, kAudioClockTestBlocksize); + assertUnsignedLongEquals(kAudioClockTestBlocksize, audioClock->currentFrame); + assert(audioClock->isPlaying); + assert(audioClock->transportChanged); + return 0; +} + +static int _testStopAudioClock(void) +{ + AudioClock audioClock = getAudioClock(); + advanceAudioClock(audioClock, kAudioClockTestBlocksize); + audioClockStop(audioClock); + assertFalse(audioClock->isPlaying); + assert(audioClock->transportChanged) + return 0; +} + +static int _testRestartAudioClock(void) +{ + AudioClock audioClock = getAudioClock(); + advanceAudioClock(audioClock, kAudioClockTestBlocksize); + audioClockStop(audioClock); + advanceAudioClock(audioClock, kAudioClockTestBlocksize); + assert(audioClock->isPlaying); + assert(audioClock->transportChanged); + assertUnsignedLongEquals(kAudioClockTestBlocksize * 2, audioClock->currentFrame); + return 0; +} + +static int _testAdvanceClockMulitpleTimes(void) +{ + AudioClock audioClock = getAudioClock(); + int i; + + for (i = 0; i < 100; i++) { + advanceAudioClock(audioClock, kAudioClockTestBlocksize); + } + + assert(audioClock->isPlaying); + assertFalse(audioClock->transportChanged); + assertUnsignedLongEquals(kAudioClockTestBlocksize * 100, audioClock->currentFrame); + return 0; +} + +TestSuite addAudioClockTests(void); +TestSuite addAudioClockTests(void) +{ + TestSuite testSuite = newTestSuite("AudioClock", _audioClockTestSetup, _audioClockTestTeardown); + addTest(testSuite, "Initialization", _testInitAudioClock); + addTest(testSuite, "AdvanceClock", _testAdvanceAudioClock); + addTest(testSuite, "StopClock", _testStopAudioClock); + addTest(testSuite, "RestartClock", _testRestartAudioClock); + addTest(testSuite, "MultipleAdvance", _testAdvanceClockMulitpleTimes); + return testSuite; +} diff --git a/test/time/TaskTimerTest.c b/test/time/TaskTimerTest.c new file mode 100644 index 0000000..10ae41e --- /dev/null +++ b/test/time/TaskTimerTest.c @@ -0,0 +1,279 @@ +#include <math.h> + +#include "unit/TestRunner.h" +#include "time/TaskTimer.h" + +#define SLEEP_DURATION_MS 10.0 +// Timer testing is a bit unreliable, so we just check to see that each sleep +// call (see below) is recorded off no more than this amount of milliseconds. +#define MAX_TIMER_TOLERANCE_MS 1.5f +#define TEST_COMPONENT_NAME "component" +#define TEST_SUBCOMPONENT_NAME "subcomponent" + +// Keep a static task timer which can easily be destroyed in the teardown. The +// reason for this is that when running the test suite in valgrind, the timing +// tests often fail, which will cuase the suite to leak. Having failed timing +// tests is probably ok when running in valgrind, but leaking memory isn't, as +// that's the entire point of running it there. :) +static TaskTimer _testTaskTimer; + +static void _testTaskTimerSetup(void) +{ + _testTaskTimer = newTaskTimerWithCString(TEST_COMPONENT_NAME, TEST_SUBCOMPONENT_NAME); +} + +static void _testTaskTimerTeardown(void) +{ + freeTaskTimer(_testTaskTimer); +} + +static int _testNewTaskTimer(void) +{ + CharString c = newCharStringWithCString(TEST_COMPONENT_NAME); + TaskTimer t = newTaskTimer(c, TEST_SUBCOMPONENT_NAME); + + assert(t->enabled); + assertCharStringEquals(TEST_COMPONENT_NAME, t->component); + assertCharStringEquals(TEST_SUBCOMPONENT_NAME, t->subcomponent); + assertDoubleEquals(0.0, t->totalTaskTime, TEST_DEFAULT_TOLERANCE); + + freeCharString(c); + freeTaskTimer(t); + return 0; +} + +static int _testNewObjectWithEmptyComponent(void) +{ + CharString c = newCharStringWithCString(EMPTY_STRING); + TaskTimer t = newTaskTimer(c, TEST_SUBCOMPONENT_NAME); + + assertNotNull(t); + assertCharStringEquals(EMPTY_STRING, t->component); + assertCharStringEquals(TEST_SUBCOMPONENT_NAME, t->subcomponent); + + freeCharString(c); + freeTaskTimer(t); + return 0; +} + +static int _testNewObjectWithEmptySubcomponent(void) +{ + CharString c = newCharStringWithCString(TEST_COMPONENT_NAME); + TaskTimer t = newTaskTimer(c, EMPTY_STRING); + + assertNotNull(t); + assertCharStringEquals(TEST_COMPONENT_NAME, t->component); + assertCharStringEquals(EMPTY_STRING, t->subcomponent); + + freeCharString(c); + freeTaskTimer(t); + return 0; +} + +static int _testNewObjectWithNullComponent(void) +{ + TaskTimer t = newTaskTimer(NULL, NULL); + + assertNotNull(t); + assertCharStringEquals(EMPTY_STRING, t->component); + assertCharStringEquals(EMPTY_STRING, t->subcomponent); + + freeTaskTimer(t); + return 0; +} + +static int _testNewObjectWithNullComponentCString(void) +{ + TaskTimer t = newTaskTimerWithCString(NULL, NULL); + + assertNotNull(t); + assertCharStringEquals(EMPTY_STRING, t->component); + assertCharStringEquals(EMPTY_STRING, t->subcomponent); + + freeTaskTimer(t); + return 0; +} + +static int _testNewObjectWithNullSubcomponent(void) +{ + CharString c = newCharStringWithCString(TEST_COMPONENT_NAME); + TaskTimer t = newTaskTimer(c, NULL); + + assertNotNull(t); + assertCharStringEquals(TEST_COMPONENT_NAME, t->component); + assertCharStringEquals(EMPTY_STRING, t->subcomponent); + + freeCharString(c); + freeTaskTimer(t); + return 0; +} + +static int _testNewObjectWithCStrings(void) +{ + TaskTimer t = newTaskTimerWithCString(TEST_COMPONENT_NAME, TEST_SUBCOMPONENT_NAME); + + assert(t->enabled); + assertCharStringEquals(TEST_COMPONENT_NAME, t->component); + assertCharStringEquals(TEST_SUBCOMPONENT_NAME, t->subcomponent); + assertDoubleEquals(0.0, t->totalTaskTime, TEST_DEFAULT_TOLERANCE); + + freeTaskTimer(t); + return 0; +} + +static void _testSleep(void) +{ + taskTimerSleep(SLEEP_DURATION_MS); +} + +static int _testTaskTimerDuration(void) +{ + double elapsedTime = 0.0; + taskTimerStart(_testTaskTimer); + _testSleep(); + elapsedTime = taskTimerStop(_testTaskTimer); + assertTimeEquals(SLEEP_DURATION_MS, _testTaskTimer->totalTaskTime, MAX_TIMER_TOLERANCE_MS); + assertTimeEquals(SLEEP_DURATION_MS, elapsedTime, MAX_TIMER_TOLERANCE_MS); + return 0; +} + +static int _testTaskTimerDurationMultipleTimes(void) +{ + double elapsedTime = 0.0; + int i; + + for (i = 0; i < 5; i++) { + taskTimerStart(_testTaskTimer); + _testSleep(); + elapsedTime = taskTimerStop(_testTaskTimer); + assertTimeEquals(SLEEP_DURATION_MS, elapsedTime, MAX_TIMER_TOLERANCE_MS); + elapsedTime = 0.0; + } + + assertTimeEquals(5.0 * SLEEP_DURATION_MS, _testTaskTimer->totalTaskTime, MAX_TIMER_TOLERANCE_MS * 5.0); + + return 0; +} + +static int _testTaskTimerCallStartTwice(void) +{ + taskTimerStart(_testTaskTimer); + taskTimerStart(_testTaskTimer); + _testSleep(); + taskTimerStop(_testTaskTimer); + assertTimeEquals(SLEEP_DURATION_MS, _testTaskTimer->totalTaskTime, MAX_TIMER_TOLERANCE_MS); + return 0; +} + +static int _testTaskTimerCallStopTwice(void) +{ + taskTimerStart(_testTaskTimer); + _testSleep(); + taskTimerStop(_testTaskTimer); + taskTimerStop(_testTaskTimer); + assertTimeEquals(SLEEP_DURATION_MS, _testTaskTimer->totalTaskTime, MAX_TIMER_TOLERANCE_MS); + return 0; +} + +static int _testCallStopBeforeStart(void) +{ + taskTimerStop(_testTaskTimer); + taskTimerStart(_testTaskTimer); + _testSleep(); + taskTimerStop(_testTaskTimer); + assertTimeEquals(SLEEP_DURATION_MS, _testTaskTimer->totalTaskTime, MAX_TIMER_TOLERANCE_MS); + return 0; +} + +static int _testHumanReadableTimeMs(void) +{ + CharString s; + _testTaskTimer->totalTaskTime = 230; + s = taskTimerHumanReadbleString(_testTaskTimer); + assertCharStringEquals("230ms", s); + freeCharString(s); + return 0; +} + +static int _testHumanReadableTimeSec(void) +{ + CharString s; + // 23 seconds + _testTaskTimer->totalTaskTime = 23000; + s = taskTimerHumanReadbleString(_testTaskTimer); + assertCharStringEquals("23sec", s); + freeCharString(s); + return 0; +} + +static int _testHumanReadableTimeMinSec(void) +{ + CharString s; + // 10 minutes, 23 seconds + _testTaskTimer->totalTaskTime = 600000 + 23000; + s = taskTimerHumanReadbleString(_testTaskTimer); + assertCharStringEquals("10:23sec", s); + freeCharString(s); + return 0; +} + +static int _testHumanReadableTimeHoursMinSec(void) +{ + CharString s; + // 2 hours, 10 minutes, 23 seconds + _testTaskTimer->totalTaskTime = (1000 * 60 * 60 * 2) + 600000 + 23000; + s = taskTimerHumanReadbleString(_testTaskTimer); + assertCharStringEquals("2:10:23sec", s); + freeCharString(s); + return 0; +} + +static int _testHumanReadableTimeNotStarted(void) +{ + CharString s = taskTimerHumanReadbleString(_testTaskTimer); + assertCharStringEquals("0ms", s); + freeCharString(s); + return 0; +} + +static int _testSleepMilliseconds(void) +{ + double elapsedTime; + TaskTimer t = newTaskTimerWithCString("test", "test"); + taskTimerStart(t); + taskTimerSleep(12); + elapsedTime = taskTimerStop(t); + assertTimeEquals(12, elapsedTime, 0.1); + freeTaskTimer(t); + return 0; +} + +TestSuite addTaskTimerTests(void); +TestSuite addTaskTimerTests(void) +{ + TestSuite testSuite = newTestSuite("TaskTimer", _testTaskTimerSetup, _testTaskTimerTeardown); + addTest(testSuite, "NewObject", _testNewTaskTimer); + addTest(testSuite, "NewObjectWithEmptyComponent", _testNewObjectWithEmptyComponent); + addTest(testSuite, "NewObjectWithEmptySubcomponent", _testNewObjectWithEmptySubcomponent); + addTest(testSuite, "NewObjectWithNullComponent", _testNewObjectWithNullComponent); + addTest(testSuite, "NewObjectWithNullComponentCString", _testNewObjectWithNullComponentCString); + addTest(testSuite, "NewObjectWithNullSubcomponent", _testNewObjectWithNullSubcomponent); + addTest(testSuite, "NewObjectWithCStrings", _testNewObjectWithCStrings); + + addTest(testSuite, "TaskDuration", _testTaskTimerDuration); + addTest(testSuite, "TaskDurationMultipleTimes", _testTaskTimerDurationMultipleTimes); + + addTest(testSuite, "CallStartTwice", _testTaskTimerCallStartTwice); + addTest(testSuite, "CallStopTwice", _testTaskTimerCallStopTwice); + addTest(testSuite, "CallStartTwice", _testTaskTimerCallStartTwice); + addTest(testSuite, "CallStopBeforeStart", _testCallStopBeforeStart); + + addTest(testSuite, "HumanReadableTimeMs", _testHumanReadableTimeMs); + addTest(testSuite, "HumanReadableTimeSec", _testHumanReadableTimeSec); + addTest(testSuite, "HumanReadableTimeMinSec", _testHumanReadableTimeMinSec); + addTest(testSuite, "HumanReadableTimeHoursMinSec", _testHumanReadableTimeHoursMinSec); + addTest(testSuite, "HumanReadableTimeNotStarted", _testHumanReadableTimeNotStarted); + + addTest(testSuite, "SleepMilliseconds", _testSleepMilliseconds); + return testSuite; +} diff --git a/test/unit/ApplicationRunner.c b/test/unit/ApplicationRunner.c new file mode 100644 index 0000000..54b4812 --- /dev/null +++ b/test/unit/ApplicationRunner.c @@ -0,0 +1,297 @@ +#include <stdio.h> +#include <stdarg.h> + +#include "ApplicationRunner.h" +#include "base/File.h" +#include "base/PlatformInfo.h" +#include "analysis/AnalyzeFile.h" + +const char *kDefaultTestOutputFileType = "pcm"; +static const char *kApplicationRunnerOutputFolder = "out"; +static const int kApplicationRunnerWaitTimeoutInMs = 1000; + +CharString buildTestArgumentString(const char *arguments, ...) +{ + CharString formattedArguments; + va_list argumentList; + va_start(argumentList, arguments); + formattedArguments = newCharStringWithCapacity(kCharStringLengthLong); + vsnprintf(formattedArguments->data, formattedArguments->capacity, arguments, argumentList); + va_end(argumentList); + return formattedArguments; +} + +CharString getTestResourceFilename(const char *resourcesPath, const char *resourceType, const char *resourceName) +{ + CharString filename = newCharString(); + snprintf(filename->data, filename->capacity, "%s%c%s%c%s", + resourcesPath, PATH_DELIMITER, resourceType, PATH_DELIMITER, resourceName); + return filename; +} + +CharString getTestOutputFilename(const char *testName, const char *fileExtension) +{ + CharString filename = newCharString(); + char *space; + char *spacePtr; + + snprintf(filename->data, filename->capacity, "%s%c%s.%s", + kApplicationRunnerOutputFolder, PATH_DELIMITER, testName, fileExtension); + spacePtr = filename->data; + + do { + space = strchr(spacePtr + 1, ' '); + + if (space == NULL || (unsigned int)(space - filename->data) > strlen(filename->data)) { + break; + } else { + *space = '-'; + } + } while (true); + + return filename; +} + +static CharString _getTestPluginResourcesPath(const char *resourcesPath) +{ + CharString pluginRoot = newCharString(); + PlatformInfo platform = newPlatformInfo(); + snprintf(pluginRoot->data, pluginRoot->capacity, "%s%cvst%c%s", + resourcesPath, PATH_DELIMITER, PATH_DELIMITER, platform->shortName->data); + freePlatformInfo(platform); + return pluginRoot; +} + +static CharString _getDefaultArguments(TestEnvironment testEnvironment, const char *testName, const char *outputFilename) +{ + CharString outString = newCharStringWithCapacity(kCharStringLengthLong); + CharString logfileName = getTestOutputFilename(testName, "txt"); + CharString resourcesPath = _getTestPluginResourcesPath(testEnvironment->resourcesPath); + snprintf(outString->data, outString->capacity, + "--log-file \"%s\" --verbose --output \"%s\" --plugin-root \"%s\"", + logfileName->data, outputFilename, resourcesPath->data); + freeCharString(logfileName); + freeCharString(resourcesPath); + return outString; +} + +static void _removeOutputFile(const char *argument) +{ + CharString outputFilename = newCharStringWithCString(argument); + File outputFile = newFileWithPath(outputFilename); + + if (fileExists(outputFile)) { + fileRemove(outputFile); + } + + freeCharString(outputFilename); + freeFile(outputFile); +} + +static void _removeOutputFiles(const char *testName) +{ + // Remove all possible output files generated during testing + CharString outputFilename; + + outputFilename = getTestOutputFilename(testName, "aif"); + _removeOutputFile(outputFilename->data); + freeCharString(outputFilename); + outputFilename = getTestOutputFilename(testName, "flac"); + _removeOutputFile(outputFilename->data); + freeCharString(outputFilename); + outputFilename = getTestOutputFilename(testName, "pcm"); + _removeOutputFile(outputFilename->data); + freeCharString(outputFilename); + outputFilename = getTestOutputFilename(testName, "wav"); + _removeOutputFile(outputFilename->data); + freeCharString(outputFilename); + outputFilename = getTestOutputFilename(testName, "txt"); + _removeOutputFile(outputFilename->data); + freeCharString(outputFilename); +} + +static const char *_getResultCodeString(const int resultCode) +{ + switch (resultCode) { + case RETURN_CODE_SUCCESS: + return "Success"; + + case RETURN_CODE_NOT_RUN: + return "Not run"; + + case RETURN_CODE_INVALID_ARGUMENT: + return "Invalid argument"; + + case RETURN_CODE_MISSING_REQUIRED_OPTION: + return "Missing required option"; + + case RETURN_CODE_IO_ERROR: + return "I/O error"; + + case RETURN_CODE_PLUGIN_ERROR: + return "Plugin error"; + + case RETURN_CODE_INVALID_PLUGIN_CHAIN: + return "Invalid plugin chain"; + + case RETURN_CODE_UNSUPPORTED_FEATURE: + return "Unsupported feature"; + + case RETURN_CODE_INTERNAL_ERROR: + return "Internal error"; + + case RETURN_CODE_SIGNAL: + return "Caught signal"; + + default: + return "Unknown"; + } +} + +void runIntegrationTest(const TestEnvironment testEnvironment, + const char *testName, CharString testArguments, + ReturnCodes expectedResultCode, const char *outputFileType) +{ + int result = -1; + ReturnCodes resultCode = (ReturnCodes)result; + CharString arguments = newCharStringWithCapacity(kCharStringLengthLong); + CharString defaultArguments; + CharString failedAnalysisFunctionName = newCharString(); + ChannelCount failedAnalysisChannel; + SampleCount failedAnalysisFrame; + File outputFolder = NULL; + CharString outputFilename = getTestOutputFilename(testName, + outputFileType == NULL ? kDefaultTestOutputFileType : outputFileType); + +#if WINDOWS + STARTUPINFOA startupInfo; + PROCESS_INFORMATION processInfo; +#endif + + // Remove files from a previous test run + outputFolder = newFileWithPathCString(kApplicationRunnerOutputFolder); + + if (fileExists(outputFolder)) { + _removeOutputFiles(testName); + } else { + fileCreate(outputFolder, kFileTypeDirectory); + } + + // Create the command line argument + charStringAppendCString(arguments, "\""); + charStringAppendCString(arguments, testEnvironment->applicationPath); + charStringAppendCString(arguments, "\""); + charStringAppendCString(arguments, " "); + defaultArguments = _getDefaultArguments(testEnvironment, testName, outputFilename->data); + charStringAppend(arguments, defaultArguments); + charStringAppendCString(arguments, " "); + charStringAppend(arguments, testArguments); + + if (!testEnvironment->results->onlyPrintFailing) { + printTestName(testName); + } + +#if WINDOWS + memset(&startupInfo, 0, sizeof(startupInfo)); + memset(&processInfo, 0, sizeof(processInfo)); + startupInfo.cb = sizeof(startupInfo); + result = CreateProcessA((LPCSTR)(testEnvironment->applicationPath), (LPSTR)(arguments->data), + 0, 0, false, CREATE_DEFAULT_ERROR_MODE, 0, 0, &startupInfo, &processInfo); + + if (result) { + // TODO: Check return codes for these calls + WaitForSingleObject(processInfo.hProcess, kApplicationRunnerWaitTimeoutInMs); + GetExitCodeProcess(processInfo.hProcess, (LPDWORD)&resultCode); + CloseHandle(processInfo.hProcess); + CloseHandle(processInfo.hThread); + } else { + logCritical("Could not launch process, got error %s", stringForLastError(GetLastError())); + return; + } + +#else + result = system(arguments->data); + resultCode = (ReturnCodes)WEXITSTATUS(result); +#endif + + if (resultCode == RETURN_CODE_FORK_FAILED || + resultCode == RETURN_CODE_SHELL_FAILED || + resultCode == RETURN_CODE_LAUNCH_FAILED_OTHER) { + if (testEnvironment->results->onlyPrintFailing) { + printTestName(testName); + } + + printTestFail(); + logCritical("Could not launch shell, got return code %d\n\ +Please check the executable path specified in the --mrswatson-path argument.", + resultCode); + testEnvironment->results->numFail++; + } else if (resultCode == expectedResultCode) { + if (outputFileType != NULL) { + if (analyzeFile(outputFilename->data, failedAnalysisFunctionName, + &failedAnalysisChannel, &failedAnalysisFrame)) { + testEnvironment->results->numSuccess++; + + if (!testEnvironment->results->keepFiles) { + _removeOutputFiles(testName); + } + + if (!testEnvironment->results->onlyPrintFailing) { + printTestSuccess(); + } + } else { + if (testEnvironment->results->onlyPrintFailing) { + printTestName(testName); + } + + fprintf(stderr, "Audio analysis check for %s failed at channel %d, frame %lu. ", + failedAnalysisFunctionName->data, failedAnalysisChannel, failedAnalysisFrame); + printTestFail(); + testEnvironment->results->numFail++; + } + } else { + testEnvironment->results->numSuccess++; + + if (!testEnvironment->results->keepFiles) { + _removeOutputFiles(testName); + } + + if (!testEnvironment->results->onlyPrintFailing) { + printTestSuccess(); + } + } + } else { + if (testEnvironment->results->onlyPrintFailing) { + printTestName(testName); + } + + fprintf(stderr, "Expected result code %d (%s), got %d (%s). ", + expectedResultCode, _getResultCodeString(expectedResultCode), + resultCode, _getResultCodeString(resultCode)); + printTestFail(); + testEnvironment->results->numFail++; + } + + freeCharString(outputFilename); + freeCharString(arguments); + freeCharString(defaultArguments); + freeCharString(testArguments); + freeCharString(failedAnalysisFunctionName); +} + +void freeTestEnvironment(TestEnvironment self) +{ + if (self != NULL) { + freeTestSuite(self->results); + free(self); + } +} + +TestEnvironment newTestEnvironment(char *applicationPath, char *resourcesPath) +{ + TestEnvironment testEnvironment = (TestEnvironment)malloc(sizeof(TestEnvironmentMembers)); + testEnvironment->applicationPath = applicationPath; + testEnvironment->resourcesPath = resourcesPath; + testEnvironment->results = newTestSuite("Results", NULL, NULL); + return testEnvironment; +} diff --git a/test/unit/ApplicationRunner.h b/test/unit/ApplicationRunner.h new file mode 100644 index 0000000..7682f01 --- /dev/null +++ b/test/unit/ApplicationRunner.h @@ -0,0 +1,39 @@ +#ifndef MrsWatson_ApplicationRunner_h +#define MrsWatson_ApplicationRunner_h + +#if HAVE_UNISTD_H +#include <unistd.h> +#endif +#include <stdlib.h> +#include "unit/TestRunner.h" +#include "base/LinkedList.h" +#include "logging/EventLogger.h" +#include "base/CharString.h" +#include "MrsWatson.h" + +extern const char *kDefaultTestOutputFileType; + +typedef struct { + int currentIndex; + char **outArray; +} ArgumentsCopyData; + +typedef struct { + char *applicationPath; + char *resourcesPath; + TestSuite results; +} TestEnvironmentMembers; +typedef TestEnvironmentMembers *TestEnvironment; +TestEnvironment newTestEnvironment(char *applicationPath, char *resourcesPath); + +void runIntegrationTest(const TestEnvironment testEnvironment, + const char *testName, CharString testArguments, + ReturnCodes expectedResultCode, const char *outputFileType); + +CharString buildTestArgumentString(const char *arguments, ...); +CharString getTestResourceFilename(const char *resourcesPath, const char *resourceType, const char *resourceName); +CharString getTestOutputFilename(const char *testName, const char *fileExtension); + +void freeTestEnvironment(TestEnvironment testEnvironment); + +#endif diff --git a/test/unit/IntegrationTests.c b/test/unit/IntegrationTests.c new file mode 100644 index 0000000..b2180ba --- /dev/null +++ b/test/unit/IntegrationTests.c @@ -0,0 +1,175 @@ +#include "ApplicationRunner.h" + +extern void _printTestSummary(int testsRun, int testsPassed, int testsFailed, int testsSkipped); + +void runIntegrationTests(TestEnvironment environment); +void runIntegrationTests(TestEnvironment environment) +{ + // Test resource paths + const char *resourcesPath = environment->resourcesPath; + CharString _a440_mono_pcm = getTestResourceFilename(resourcesPath, "audio", "a440-mono.pcm"); + CharString _a440_stereo_aiff = getTestResourceFilename(resourcesPath, "audio", "a440-stereo.aif"); + CharString _a440_stereo_flac = getTestResourceFilename(resourcesPath, "audio", "a440-stereo.flac"); + CharString _a440_stereo_pcm = getTestResourceFilename(resourcesPath, "audio", "a440-stereo.pcm"); + CharString _a440_stereo_wav = getTestResourceFilename(resourcesPath, "audio", "a440-stereo.wav"); + CharString _c_scale_mid = getTestResourceFilename(resourcesPath, "midi", "c-scale.mid"); + CharString _again_test_fxp = getTestResourceFilename(resourcesPath, "presets", "again-test.fxp"); + const char *a440_stereo_aiff = _a440_stereo_aiff->data; + const char *a440_stereo_flac = _a440_stereo_flac->data; + const char *a440_mono_pcm = _a440_mono_pcm->data; + const char *a440_stereo_pcm = _a440_stereo_pcm->data; + const char *a440_stereo_wav = _a440_stereo_wav->data; + const char *c_scale_mid = _c_scale_mid->data; + const char *again_test_fxp = _again_test_fxp->data; + + // Basic non-processing operations + runIntegrationTest(environment, "List plugins", + newCharStringWithCString("--list-plugins"), + RETURN_CODE_NOT_RUN, NULL); + runIntegrationTest(environment, "List file types", + newCharStringWithCString("--list-file-types"), + RETURN_CODE_NOT_RUN, NULL); + runIntegrationTest(environment, "Invalid argument", + newCharStringWithCString("--invalid"), + RETURN_CODE_INVALID_ARGUMENT, NULL); + + // Invalid configurations + runIntegrationTest(environment, "Run with no plugins", + newCharString(), + RETURN_CODE_INVALID_PLUGIN_CHAIN, NULL); + runIntegrationTest(environment, "Effect with no input source", + newCharStringWithCString("--plugin again"), + RETURN_CODE_MISSING_REQUIRED_OPTION, NULL); + runIntegrationTest(environment, "Instrument with no MIDI source", + newCharStringWithCString("--plugin vstxsynth"), + RETURN_CODE_MISSING_REQUIRED_OPTION, NULL); + runIntegrationTest(environment, "Plugin chain with instrument not at head", + buildTestArgumentString("--plugin \"again;vstxsynth\" --input \"%s\"", a440_stereo_pcm), + RETURN_CODE_INVALID_PLUGIN_CHAIN, NULL); + runIntegrationTest(environment, "Plugin with invalid preset", + buildTestArgumentString("--plugin \"again,invalid.fxp\" --input \"%s\"", a440_stereo_pcm), + RETURN_CODE_INVALID_ARGUMENT, NULL); + runIntegrationTest(environment, "Preset for wrong plugin", + buildTestArgumentString("--plugin \"vstxsynth,%s\" --midi-file \"%s\"", again_test_fxp, c_scale_mid), + RETURN_CODE_INVALID_ARGUMENT, NULL); + runIntegrationTest(environment, "Set invalid parameter", + buildTestArgumentString("--plugin again --input \"%s\" --parameter 1,0.5", a440_stereo_pcm), + RETURN_CODE_INVALID_ARGUMENT, NULL); + runIntegrationTest(environment, "Set invalid time signature", + buildTestArgumentString("--plugin again --input \"%s\" --time-signature invalid", a440_stereo_pcm), + RETURN_CODE_INVALID_ARGUMENT, NULL); + runIntegrationTest(environment, "Set invalid tempo", + buildTestArgumentString("--plugin again --input \"%s\" --tempo 0", a440_stereo_pcm), + RETURN_CODE_INVALID_ARGUMENT, NULL); + runIntegrationTest(environment, "Set invalid blocksize", + buildTestArgumentString("--plugin again --input \"%s\" --blocksize 0", a440_stereo_pcm), + RETURN_CODE_INVALID_ARGUMENT, NULL); + runIntegrationTest(environment, "Set invalid bit depth", + buildTestArgumentString("--plugin again --input \"%s\" --bit-depth 5", a440_stereo_pcm), + RETURN_CODE_INVALID_ARGUMENT, NULL); + runIntegrationTest(environment, "Set invalid channel count", + buildTestArgumentString("--plugin again --input \"%s\" --channels 0", a440_stereo_pcm), + RETURN_CODE_INVALID_ARGUMENT, NULL); + runIntegrationTest(environment, "Set invalid sample rate", + buildTestArgumentString("--plugin again --input \"%s\" --sample-rate 0", a440_stereo_pcm), + RETURN_CODE_INVALID_ARGUMENT, NULL); + + // Audio file types + runIntegrationTest(environment, "Read PCM file", + buildTestArgumentString("--plugin again --input \"%s\"", a440_stereo_pcm), + RETURN_CODE_SUCCESS, kDefaultTestOutputFileType); + runIntegrationTest(environment, "Write PCM file", + buildTestArgumentString("--plugin again --input \"%s\"", a440_stereo_pcm), + RETURN_CODE_SUCCESS, "pcm"); + runIntegrationTest(environment, "Read WAV file", + buildTestArgumentString("--plugin again --input \"%s\"", a440_stereo_wav), + RETURN_CODE_SUCCESS, kDefaultTestOutputFileType); + runIntegrationTest(environment, "Write WAV file", + buildTestArgumentString("--plugin again --input \"%s\"", a440_stereo_pcm), + RETURN_CODE_SUCCESS, "wav"); + +#if USE_AUDIOFILE + runIntegrationTest(environment, "Read AIFF file", + buildTestArgumentString("--plugin again --input \"%s\"", a440_stereo_aiff), + RETURN_CODE_SUCCESS, kDefaultTestOutputFileType); + runIntegrationTest(environment, "Write AIFF file", + buildTestArgumentString("--plugin again --input \"%s\"", a440_stereo_pcm), + RETURN_CODE_SUCCESS, "aif"); +#endif + +#if USE_FLAC + runIntegrationTest(environment, "Read FLAC file", + buildTestArgumentString("--plugin again --input \"%s\"", a440_stereo_flac), + RETURN_CODE_SUCCESS, kDefaultTestOutputFileType); + runIntegrationTest(environment, "Write FLAC file", + buildTestArgumentString("--plugin again --input \"%s\"", a440_stereo_pcm), + RETURN_CODE_SUCCESS, "flac"); +#endif + + // Configuration tests + runIntegrationTest(environment, "Read mono input source", + buildTestArgumentString("--plugin again --input \"%s\" --channels 1", a440_mono_pcm), + RETURN_CODE_SUCCESS, kDefaultTestOutputFileType); + runIntegrationTest(environment, "Read with user-defined sample rate", + buildTestArgumentString("--plugin again --input \"%s\" --sample-rate 48000", a440_stereo_pcm), + RETURN_CODE_SUCCESS, kDefaultTestOutputFileType); + runIntegrationTest(environment, "Read with user-defined blocksize", + buildTestArgumentString("--plugin again --input \"%s\" --blocksize 128", a440_stereo_pcm), + RETURN_CODE_SUCCESS, kDefaultTestOutputFileType); + runIntegrationTest(environment, "Set parameter", + buildTestArgumentString("--plugin again --input \"%s\" --parameter 0,0.5", a440_stereo_pcm), + RETURN_CODE_SUCCESS, kDefaultTestOutputFileType); + runIntegrationTest(environment, "Set time signature", + buildTestArgumentString("--plugin again --input \"%s\" --time-signature 3/4", a440_stereo_pcm), + RETURN_CODE_SUCCESS, kDefaultTestOutputFileType); + + // Internal plugins + runIntegrationTest(environment, "Process with internal limiter", + buildTestArgumentString("--plugin mrs_limiter --input \"%s\"", a440_stereo_pcm), + RETURN_CODE_SUCCESS, kDefaultTestOutputFileType); + runIntegrationTest(environment, "Process with internal gain plugin", + buildTestArgumentString("--plugin mrs_gain --parameter 0,0.5 --input \"%s\"", a440_stereo_pcm), + RETURN_CODE_SUCCESS, kDefaultTestOutputFileType); + runIntegrationTest(environment, "Process with internal gain plugin and invalid parameter", + buildTestArgumentString("--plugin mrs_gain --parameter 1,0.5 --input \"%s\"", a440_stereo_pcm), + RETURN_CODE_INVALID_ARGUMENT, NULL); + runIntegrationTest(environment, "Process with internal passthru plugin", + buildTestArgumentString("--plugin mrs_passthru --input \"%s\"", a440_stereo_pcm), + RETURN_CODE_SUCCESS, kDefaultTestOutputFileType); +#if 0 + // This test case works, but fails the analysis check for silence (obviously). + // It will remain disabled until we have a smarter way to specify which analysis + // functions should be run for each integration test. + runIntegrationTest(environment, "Process with silence generator", + newCharStringWithCString("--plugin mrs_silence --max-time 1000"), + RETURN_CODE_SUCCESS, kDefaultTestOutputFileType); +#endif + + // Plugin processing tests + runIntegrationTest(environment, "Process audio with again plugin", + buildTestArgumentString("--plugin again --input \"%s\"", a440_stereo_pcm), + RETURN_CODE_SUCCESS, kDefaultTestOutputFileType); + runIntegrationTest(environment, "Process MIDI with vstxsynth plugin", + buildTestArgumentString("--plugin vstxsynth --midi-file \"%s\"", c_scale_mid), + RETURN_CODE_SUCCESS, kDefaultTestOutputFileType); + runIntegrationTest(environment, "Process effect chain", + buildTestArgumentString("--plugin vstxsynth,again --midi-file \"%s\"", c_scale_mid), + RETURN_CODE_SUCCESS, kDefaultTestOutputFileType); + runIntegrationTest(environment, "Load FXP preset to VST", + buildTestArgumentString("--plugin \"again,%s\" --input \"%s\"", again_test_fxp, a440_stereo_pcm), + RETURN_CODE_SUCCESS, kDefaultTestOutputFileType); + runIntegrationTest(environment, "Load internal program to VST", + buildTestArgumentString("--plugin vstxsynth,2 --midi-file \"%s\"", c_scale_mid), + RETURN_CODE_SUCCESS, kDefaultTestOutputFileType); + + _printTestSummary(environment->results->numSuccess + environment->results->numFail + environment->results->numSkips, + environment->results->numSuccess, environment->results->numFail, environment->results->numSkips); + + freeCharString(_a440_stereo_aiff); + freeCharString(_a440_stereo_flac); + freeCharString(_a440_mono_pcm); + freeCharString(_a440_stereo_pcm); + freeCharString(_a440_stereo_wav); + freeCharString(_c_scale_mid); + freeCharString(_again_test_fxp); +} diff --git a/test/unit/TestRunner.c b/test/unit/TestRunner.c new file mode 100644 index 0000000..4c90cb3 --- /dev/null +++ b/test/unit/TestRunner.c @@ -0,0 +1,152 @@ +#include <stdlib.h> +#if WINDOWS +#include <io.h> +#endif + +#include "unit/TestRunner.h" + +void addTestToTestSuite(TestSuite testSuite, TestCase testCase) +{ + linkedListAppend(testSuite->testCases, testCase); +} + +const LogColor getLogColor(TestLogEventType eventType) +{ + switch (eventType) { + case kTestLogEventSection: + return isatty(1) ? COLOR_FG_CYAN : COLOR_NONE; + + case kTestLogEventPass: + return isatty(1) ? COLOR_FG_GREEN : COLOR_NONE; + + case kTestLogEventFail: + return isatty(1) ? COLOR_BG_MAROON : COLOR_NONE; + + case kTestLogEventSkip: + return isatty(1) ? COLOR_FG_YELLOW : COLOR_NONE; + + case kTestLogEventReset: + return isatty(1) ? COLOR_RESET : COLOR_NONE; + + default: + return COLOR_NONE; + } +} + +void printTestSuccess(void) +{ + printToLog(getLogColor(kTestLogEventPass), NULL, "OK"); + flushLog(NULL); +} + +void printTestFail(void) +{ + printToLog(getLogColor(kTestLogEventFail), NULL, "FAIL"); + flushLog(NULL); +} + +static void _printTestSkipped(void) +{ + printToLog(getLogColor(kTestLogEventSkip), NULL, "Skipped"); + flushLog(NULL); +} + +void printTestName(const char *testName) +{ + fprintf(stderr, " %s: ", testName); + // Flush standard output in case the test crashes. That way at least the + // crashing test name is seen. + fflush(stderr); +} + +void runTestCase(void *item, void *extraData) +{ + TestCase testCase = (TestCase)item; + TestSuite testSuite = (TestSuite)extraData; + int result; + + if (!testSuite->onlyPrintFailing) { + printTestName(testCase->name); + } + + if (testCase->testCaseFunc != NULL) { + if (testSuite->setup != NULL) { + testSuite->setup(); + } + + result = testCase->testCaseFunc(); + + if (result == 0) { + if (!testSuite->onlyPrintFailing) { + printTestSuccess(); + } + + testSuite->numSuccess++; + } else { + printTestFail(); + testSuite->numFail++; + } + + if (testSuite->teardown != NULL) { + testSuite->teardown(); + } + } else { + if (!testSuite->onlyPrintFailing) { + _printTestSkipped(); + } + + testSuite->numSkips++; + } +} + +void runTestSuite(void *testSuitePtr, void *extraData) +{ + TestSuite testSuite = (TestSuite)testSuitePtr; + + printToLog(getLogColor(kTestLogEventReset), NULL, "Running tests in "); + printToLog(getLogColor(kTestLogEventSection), NULL, testSuite->name); + flushLog(NULL); + + linkedListForeach(testSuite->testCases, runTestCase, testSuite); +} + +// In both the TestSuite and TestCase objects we assume that we do not need ownership of +// the strings passed in, since they should be allocated on the heap and live for the +// lifetime of the program. +TestSuite newTestSuite(char *name, TestCaseSetupFunc setup, TestCaseTeardownFunc teardown) +{ + TestSuite testSuite = (TestSuite)malloc(sizeof(TestSuiteMembers)); + testSuite->name = name; + testSuite->numSuccess = 0; + testSuite->numFail = 0; + testSuite->numSkips = 0; + testSuite->testCases = newLinkedList(); + testSuite->setup = setup; + testSuite->teardown = teardown; + testSuite->onlyPrintFailing = false; + testSuite->keepFiles = false; + return testSuite; +} + +TestCase newTestCase(char *name, char *filename, int lineNumber, TestCaseExecFunc testCaseFunc) +{ + TestCase testCase = (TestCase)malloc(sizeof(TestCaseMembers)); + testCase->name = name; + testCase->filename = filename; + testCase->lineNumber = lineNumber; + testCase->testCaseFunc = testCaseFunc; + return testCase; +} + +void freeTestCase(TestCase self) +{ + free(self); +} + +void freeTestSuite(TestSuite self) +{ + if (self != NULL) { + freeLinkedListAndItems(self->testCases, (LinkedListFreeItemFunc)freeTestCase); + free(self); + } +} diff --git a/test/unit/TestRunner.h b/test/unit/TestRunner.h new file mode 100644 index 0000000..597a80a --- /dev/null +++ b/test/unit/TestRunner.h @@ -0,0 +1,173 @@ +#ifndef MrsWatsonTest_TestRunner_h +#define MrsWatsonTest_TestRunner_h + +#include <stdio.h> +#include <string.h> +#include <math.h> + +#include "base/CharString.h" +#include "base/File.h" +#include "logging/LogPrinter.h" + +#if UNIX +#include <unistd.h> +#endif + +#ifndef __func__ +#define __func__ __FUNCTION__ +#endif + +typedef enum { + kTestLogEventSection, + kTestLogEventPass, + kTestLogEventFail, + kTestLogEventSkip, + kTestLogEventReset, + kTestLogEventInvalid +} TestLogEventType; +const LogColor getLogColor(TestLogEventType eventType); + +typedef int (*TestCaseExecFunc)(void); +typedef void (*TestCaseSetupFunc)(void); +typedef void (*TestCaseTeardownFunc)(void); + +typedef struct { + char *name; + char *filename; + int lineNumber; + TestCaseExecFunc testCaseFunc; +} TestCaseMembers; +typedef TestCaseMembers *TestCase; + +typedef struct { + char *name; + int numSuccess; + int numFail; + int numSkips; + LinkedList testCases; + TestCaseSetupFunc setup; + TestCaseTeardownFunc teardown; + boolByte onlyPrintFailing; + boolByte keepFiles; +} TestSuiteMembers; +typedef TestSuiteMembers *TestSuite; + +void addTestToTestSuite(TestSuite testSuite, TestCase testCase); +void runTestSuite(void *testSuitePtr, void *extraData); +void runTestCase(void *item, void *extraData); +void printTestName(const char *testName); +void printTestSuccess(void); +void printTestFail(void); + +TestSuite newTestSuite(char *name, TestCaseSetupFunc setup, TestCaseTeardownFunc teardown); +TestCase newTestCase(char *name, char *filename, int lineNumber, TestCaseExecFunc testCaseFunc); + +void freeTestCase(TestCase self); +void freeTestSuite(TestSuite self); + +static const char *_getFileBasename(const char *filename) +{ + const char *lastDelimiter; + + if (filename == NULL) { + return NULL; + } + + lastDelimiter = strrchr(filename, PATH_DELIMITER); + + if (lastDelimiter == NULL) { + return (char *)filename; + } else { + return lastDelimiter + 1; + } +} + +#define addTest(testSuite, name, testCaseFunc) { \ + addTestToTestSuite(testSuite, newTestCase(name, __FILE__, __LINE__, testCaseFunc)); \ + } + +#define assert(_result) { \ + if(!(_result)) { \ + fprintf(stderr, "\nAssertion failed at %s:%d. ", _getFileBasename(__FILE__), __LINE__); \ + return 1; \ + } \ + } + +#define assertFalse(_result) assert((_result) == false) +#define assertIsNull(_result) assert((_result) == NULL) +#define assertNotNull(_result) assert((_result) != NULL) + +#define assertIntEquals(expected, _result) { \ + int _resultInt = _result; \ + if(_resultInt != expected) { \ + fprintf(stderr, "Assertion failed at %s:%d. Expected %d, got %d. ", _getFileBasename(__FILE__), __LINE__, expected, _resultInt); \ + return 1; \ + } \ + } + +#define assertLongEquals(expected, _result) { \ + long _resultLong = _result; \ + if(_resultLong != expected) { \ + fprintf(stderr, "Assertion failed at %s:%d. Expected %lu, got %lu. ", _getFileBasename(__FILE__), __LINE__, expected, _resultLong); \ + return 1; \ + } \ + } + +#define ZERO_UNSIGNED_LONG (unsigned long)0 +#define assertUnsignedLongEquals(expected, _result) { \ + unsigned long _resultULong = _result; \ + if(_resultULong != expected) { \ + fprintf(stderr, "Assertion failed at %s:%d. Expected %ld, got %ld. ", _getFileBasename(__FILE__), __LINE__, expected, _resultULong); \ + return 1; \ + } \ + } + +#define assertSizeEquals(expected, _result) { \ + size_t _resultSize = _result; \ + if(_result != expected) { \ + fprintf(stderr, "Assertion failed at %s:%d. Expected %zu, got %zu. ", _getFileBasename(__FILE__), __LINE__, expected, _resultSize); \ + return 1; \ + } \ + } + +#define TEST_DEFAULT_TOLERANCE 0.01 +#define TEST_EXACT_TOLERANCE 0.0 + +#define assertDoubleEquals(expected, _result, tolerance) { \ + double resultRounded = floor(_result * 100.0) / 100.0; \ + double expectedRounded = floor(expected * 100.0) / 100.0; \ + double _resultDiff = fabs(resultRounded - expectedRounded); \ + if(_resultDiff > tolerance) { \ + fprintf(stderr, "Assertion failed at %s:%d. Expected %g, got %g. ", _getFileBasename(__FILE__), __LINE__, expectedRounded, resultRounded); \ + return 1; \ + } \ + } + + // Timing assertions fail all the time in debug mode, because the binary is + // running in the debugger, is not optimized, is being profiled, etc. So for + // debug builds, we should not return early here, or else that will cause + // valgrind to go crazy and report a bunch of leaks. +#define assertTimeEquals(expected, _result, tolerance) { \ + double resultRounded = floor(_result * 100.0) / 100.0; \ + double expectedRounded = floor(expected * 100.0) / 100.0; \ + double _resultDiff = fabs(resultRounded - expectedRounded); \ + if(_resultDiff > tolerance) { \ + fprintf(stderr, "Warning: timing assertion failed at %s:%d. Expected %gms, got %gms. ", _getFileBasename(__FILE__), __LINE__, expectedRounded, resultRounded); \ + } \ + } + +#define assertCharStringEquals(expected, _result) { \ + if(!charStringIsEqualToCString(_result, expected, false)) { \ + fprintf(stderr, "Assertion failed at %s:%d. Expected '%s', got '%s'. ", _getFileBasename(__FILE__), __LINE__, expected, _result->data); \ + return 1; \ + } \ + } + +#define assertCharStringContains(expected, _result) { \ + if(strstr(_result->data, expected) == NULL) { \ + fprintf(stderr, "Assertion failed at %s:%d. Expected '%s' to contain '%s'. ", _getFileBasename(__FILE__), __LINE__, _result->data, expected); \ + return 1; \ + } \ + } + +#endif diff --git a/test/unit/UnitTests.c b/test/unit/UnitTests.c new file mode 100644 index 0000000..b2bae69 --- /dev/null +++ b/test/unit/UnitTests.c @@ -0,0 +1,160 @@ +#include <stdlib.h> +#include "base/LinkedList.h" +#include "unit/TestRunner.h" + +extern TestSuite addAudioClockTests(void); +extern TestSuite addAudioSettingsTests(void); +extern TestSuite addCharStringTests(void); +extern TestSuite addEndianTests(void); +extern TestSuite addFileTests(void); +extern TestSuite addLinkedListTests(void); +extern TestSuite addMidiSequenceTests(void); +extern TestSuite addMidiSourceTests(void); +extern TestSuite addPlatformInfoTests(void); +extern TestSuite addPluginTests(void); +extern TestSuite addPluginChainTests(void); +extern TestSuite addPluginPresetTests(void); +extern TestSuite addPluginVst2xIdTests(void); +extern TestSuite addProgramOptionTests(void); +extern TestSuite addSampleBufferTests(void); +extern TestSuite addSampleSourceTests(void); +extern TestSuite addTaskTimerTests(void); + +extern TestSuite addAnalysisClippingTests(void); +extern TestSuite addAnalysisDistortionTests(void); +extern TestSuite addAnalysisSilenceTests(void); + +extern void _printTestSummary(int testsRun, int testsPassed, int testsFailed, int testsSkipped); + +static void _sumTestSuiteResults(void *item, void *extraData) +{ + TestSuite testSuite = (TestSuite)item; + TestSuite result = (TestSuite)extraData; + result->numSuccess += testSuite->numSuccess; + result->numFail += testSuite->numFail; + result->numSkips += testSuite->numSkips; +} + +LinkedList getTestSuites(void); +LinkedList getTestSuites(void) +{ + LinkedList unitTestSuites = newLinkedList(); + linkedListAppend(unitTestSuites, addAudioClockTests()); + linkedListAppend(unitTestSuites, addAudioSettingsTests()); + linkedListAppend(unitTestSuites, addCharStringTests()); + linkedListAppend(unitTestSuites, addEndianTests()); + linkedListAppend(unitTestSuites, addFileTests()); + linkedListAppend(unitTestSuites, addLinkedListTests()); + linkedListAppend(unitTestSuites, addMidiSequenceTests()); + linkedListAppend(unitTestSuites, addMidiSourceTests()); + linkedListAppend(unitTestSuites, addPlatformInfoTests()); + linkedListAppend(unitTestSuites, addPluginTests()); + linkedListAppend(unitTestSuites, addPluginChainTests()); + linkedListAppend(unitTestSuites, addPluginPresetTests()); + linkedListAppend(unitTestSuites, addPluginVst2xIdTests()); + linkedListAppend(unitTestSuites, addProgramOptionTests()); + linkedListAppend(unitTestSuites, addSampleBufferTests()); + linkedListAppend(unitTestSuites, addSampleSourceTests()); + linkedListAppend(unitTestSuites, addTaskTimerTests()); + + linkedListAppend(unitTestSuites, addAnalysisClippingTests()); + linkedListAppend(unitTestSuites, addAnalysisDistortionTests()); + linkedListAppend(unitTestSuites, addAnalysisSilenceTests()); + + return unitTestSuites; +} + +static void _setTestSuiteOnlyPrintFailing(void *item, void *userData) +{ + TestSuite testSuite = (TestSuite)item; + testSuite->onlyPrintFailing = true; +} + +TestSuite runUnitTests(LinkedList testSuites, boolByte onlyPrintFailing); +TestSuite runUnitTests(LinkedList testSuites, boolByte onlyPrintFailing) +{ + TestSuite suiteResults; + + if (onlyPrintFailing) { + linkedListForeach(testSuites, _setTestSuiteOnlyPrintFailing, NULL); + } + + linkedListForeach(testSuites, runTestSuite, NULL); + // Create a new test suite to be used as the userData passed to the foreach loop + suiteResults = newTestSuite("Suite results", NULL, NULL); + linkedListForeach(testSuites, _sumTestSuiteResults, suiteResults); + + _printTestSummary(suiteResults->numSuccess + suiteResults->numFail + suiteResults->numSkips, + suiteResults->numSuccess, suiteResults->numFail, suiteResults->numSkips); + + return suiteResults; +} + +TestCase findTestCase(TestSuite testSuite, char *testName); +TestCase findTestCase(TestSuite testSuite, char *testName) +{ + LinkedList iterator = testSuite->testCases; + TestCase currentTestCase = NULL; + + while (iterator != NULL) { + if (iterator->item != NULL) { + currentTestCase = (TestCase)iterator->item; + + if (!strncasecmp(currentTestCase->name, testName, strlen(currentTestCase->name))) { + return currentTestCase; + } + } + + iterator = iterator->nextItem; + } + + return NULL; +} + +TestSuite findTestSuite(LinkedList testSuites, const CharString testSuiteName); +TestSuite findTestSuite(LinkedList testSuites, const CharString testSuiteName) +{ + LinkedList iterator = testSuites; + TestSuite testSuite = NULL; + + if (testSuiteName == NULL || charStringIsEmpty(testSuiteName)) { + return NULL; + } + + while (iterator != NULL) { + if (iterator->item != NULL) { + testSuite = (TestSuite)iterator->item; + + if (charStringIsEqualToCString(testSuiteName, testSuite->name, true)) { + break; + } else { + testSuite = NULL; + } + } + + iterator = iterator->nextItem; + } + + return testSuite; +} + +static void _printTestCases(void *item, void *userData) +{ + TestCase testCase = (TestCase)item; + char *testSuiteName = (char *)userData; + printf("%s:%s\n", testSuiteName, testCase->name); +} + +static void _printTestsInSuite(void *item, void *userData) +{ + TestSuite testSuite = (TestSuite)item; + linkedListForeach(testSuite->testCases, _printTestCases, testSuite->name); +} + +void printUnitTestSuites(void); +void printUnitTestSuites(void) +{ + LinkedList unitTestSuites = getTestSuites(); + linkedListForeach(unitTestSuites, _printTestsInSuite, NULL); + freeLinkedListAndItems(unitTestSuites, (LinkedListFreeItemFunc)freeTestSuite); +} |
