summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/CMakeLists.txt90
-rw-r--r--test/MrsWatsonTestMain.c348
-rw-r--r--test/MrsWatsonTestMain.h20
-rw-r--r--test/analysis/AnalysisClipping.c25
-rw-r--r--test/analysis/AnalysisClipping.h3
-rw-r--r--test/analysis/AnalysisClippingTest.c37
-rw-r--r--test/analysis/AnalysisDistortion.c33
-rw-r--r--test/analysis/AnalysisDistortion.h3
-rw-r--r--test/analysis/AnalysisDistortionTest.c37
-rw-r--r--test/analysis/AnalysisSilence.c25
-rw-r--r--test/analysis/AnalysisSilence.h3
-rw-r--r--test/analysis/AnalysisSilenceTest.c57
-rw-r--r--test/analysis/AnalyzeFile.c123
-rw-r--r--test/analysis/AnalyzeFile.h37
-rw-r--r--test/app/ProgramOptionTest.c505
-rw-r--r--test/audio/AudioSettingsTest.c213
-rw-r--r--test/audio/SampleBufferTest.c134
-rw-r--r--test/base/CharStringTest.c422
-rw-r--r--test/base/EndianTest.c155
-rw-r--r--test/base/FileTest.c1526
-rw-r--r--test/base/LinkedListTest.c245
-rw-r--r--test/base/PlatformInfoTest.c86
-rw-r--r--test/io/SampleSourceTest.c55
-rw-r--r--test/midi/MidiSequenceTest.c138
-rw-r--r--test/midi/MidiSourceTest.c41
-rwxr-xr-xtest/mrswatsontestbin0 -> 334168 bytes
-rwxr-xr-xtest/mrswatsontest64bin0 -> 292172 bytes
-rw-r--r--test/plugin/PluginChainTest.c311
-rw-r--r--test/plugin/PluginMock.c95
-rw-r--r--test/plugin/PluginMock.h19
-rw-r--r--test/plugin/PluginPresetMock.c42
-rw-r--r--test/plugin/PluginPresetMock.h15
-rw-r--r--test/plugin/PluginPresetTest.c78
-rw-r--r--test/plugin/PluginTest.c91
-rw-r--r--test/plugin/PluginVst2xIdTest.c87
-rw-r--r--test/time/AudioClockTest.c82
-rw-r--r--test/time/TaskTimerTest.c279
-rw-r--r--test/unit/ApplicationRunner.c297
-rw-r--r--test/unit/ApplicationRunner.h39
-rw-r--r--test/unit/IntegrationTests.c175
-rw-r--r--test/unit/TestRunner.c152
-rw-r--r--test/unit/TestRunner.h173
-rw-r--r--test/unit/UnitTests.c160
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 = &currentFrame;
+ 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
new file mode 100755
index 0000000..8a90eb0
--- /dev/null
+++ b/test/mrswatsontest
Binary files differ
diff --git a/test/mrswatsontest64 b/test/mrswatsontest64
new file mode 100755
index 0000000..74b31af
--- /dev/null
+++ b/test/mrswatsontest64
Binary files differ
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);
+}