/* * Copyright (c) 2007, Michael Feathers, James Grenning and Bas Vodde * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE EARLIER MENTIONED AUTHORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "CppUTest/TestHarness.h" #include "CppUTest/JUnitTestOutput.h" #include "CppUTest/TestResult.h" #include "CppUTest/PlatformSpecificFunctions.h" #include "CppUTest/SimpleString.h" class FileForJUnitOutputTests { SimpleString name_; bool isOpen_; SimpleString buffer_; FileForJUnitOutputTests* next_; SimpleStringCollection linesOfFile_; public: FileForJUnitOutputTests(SimpleString filename, FileForJUnitOutputTests* next) : name_(filename), isOpen_(true), next_(next) {} FileForJUnitOutputTests* nextFile() { return next_; } SimpleString name() { return name_; } void write(const SimpleString& buffer) { buffer_ += buffer; } void close() { isOpen_ = false; } const char* line(size_t lineNumber) { buffer_.split("\n", linesOfFile_); return linesOfFile_[lineNumber-1].asCharString(); } const char* lineFromTheBack(size_t lineNumberFromTheBack) { return line(amountOfLines() - (lineNumberFromTheBack - 1)); } size_t amountOfLines() { buffer_.split("\n", linesOfFile_); return linesOfFile_.size(); } SimpleString content() { return buffer_; } }; class FileSystemForJUnitTestOutputTests { FileForJUnitOutputTests* firstFile_; public: FileSystemForJUnitTestOutputTests() : firstFile_(0) {} ~FileSystemForJUnitTestOutputTests() { clear(); } void clear(void) { while (firstFile_) { FileForJUnitOutputTests* fileToBeDeleted = firstFile_; firstFile_ = firstFile_->nextFile(); delete fileToBeDeleted; } } FileForJUnitOutputTests* openFile(const SimpleString& filename) { firstFile_ = new FileForJUnitOutputTests(filename, firstFile_); return firstFile_; } int amountOfFiles() { int totalAmountOfFiles = 0; for (FileForJUnitOutputTests* current = firstFile_; current != NULL; current = current->nextFile()) totalAmountOfFiles++; return totalAmountOfFiles; } bool fileExists(const char* filename) { FileForJUnitOutputTests *searchedFile = file(filename); return (searchedFile != NULL); } FileForJUnitOutputTests* file(const char* filename) { for (FileForJUnitOutputTests* current = firstFile_; current != NULL; current = current->nextFile()) if (current->name() == filename) return current; return NULL; } }; extern "C" { static long millisTime = 0; static const char* theTime = ""; static long MockGetPlatformSpecificTimeInMillis() { return millisTime; } static const char* MockGetPlatformSpecificTimeString() { return theTime; } } class JUnitTestOutputTestRunner { TestResult result_; const char* currentGroupName_; UtestShell* currentTest_; bool firstTestInGroup_; int timeTheTestTakes_; TestFailure* testFailure_; public: JUnitTestOutputTestRunner(TestResult result) : result_(result), currentGroupName_(0), currentTest_(0), firstTestInGroup_(true), timeTheTestTakes_(0), testFailure_(0) { millisTime = 0; theTime = "1978-10-03T00:00:00"; UT_PTR_SET(GetPlatformSpecificTimeInMillis, MockGetPlatformSpecificTimeInMillis); UT_PTR_SET(GetPlatformSpecificTimeString, MockGetPlatformSpecificTimeString); } JUnitTestOutputTestRunner& start() { result_.testsStarted(); return *this; } JUnitTestOutputTestRunner& end() { endOfPreviousTestGroup(); delete currentTest_; result_.testsEnded(); return *this; } void endOfPreviousTestGroup() { runPreviousTest(); if (currentTest_) { result_.currentGroupEnded(currentTest_); firstTestInGroup_ = true; } currentGroupName_ = 0; } JUnitTestOutputTestRunner& withGroup(const char* groupName) { runPreviousTest(); endOfPreviousTestGroup(); currentGroupName_ = groupName; return *this; } JUnitTestOutputTestRunner& withTest(const char* testName) { runPreviousTest(); delete currentTest_; currentTest_ = new UtestShell(currentGroupName_, testName, "file", 1); return *this; } JUnitTestOutputTestRunner& withIgnoredTest(const char* testName) { runPreviousTest(); delete currentTest_; currentTest_ = new IgnoredUtestShell(currentGroupName_, testName, "file", 1); return *this; } void runPreviousTest() { if (currentTest_ == 0) return; if (firstTestInGroup_) { result_.currentGroupStarted(currentTest_); firstTestInGroup_ = false; } result_.currentTestStarted(currentTest_); millisTime += timeTheTestTakes_; if (testFailure_) { result_.addFailure(*testFailure_); delete testFailure_; testFailure_ = 0; } result_.currentTestEnded(currentTest_); } JUnitTestOutputTestRunner& thatTakes(int timeElapsed) { timeTheTestTakes_ = timeElapsed; return *this; } JUnitTestOutputTestRunner& seconds() { return *this; } JUnitTestOutputTestRunner& thatFails(const char* message, const char* file, int line) { testFailure_ = new TestFailure( currentTest_, file, line, message); return *this; } JUnitTestOutputTestRunner& atTime(const char* newTime) { theTime = newTime; return *this; } }; extern "C" { static FileSystemForJUnitTestOutputTests fileSystem; static PlatformSpecificFile mockFOpen(const char* filename, const char*) { return fileSystem.openFile(filename); } static void mockFPuts(const char* str, PlatformSpecificFile file) { ((FileForJUnitOutputTests*)file)->write(str); } static void mockFClose(PlatformSpecificFile file) { ((FileForJUnitOutputTests*)file)->close(); } } TEST_GROUP(JUnitOutputTest) { JUnitTestOutput *junitOutput; TestResult *result; JUnitTestOutputTestRunner *testCaseRunner; FileForJUnitOutputTests* outputFile; void setup() { UT_PTR_SET(PlatformSpecificFOpen, mockFOpen); UT_PTR_SET(PlatformSpecificFPuts, mockFPuts); UT_PTR_SET(PlatformSpecificFClose, mockFClose); junitOutput = new JUnitTestOutput(); result = new TestResult(*junitOutput); testCaseRunner = new JUnitTestOutputTestRunner(*result); } void teardown() { delete testCaseRunner; delete result; delete junitOutput; fileSystem.clear(); } }; TEST(JUnitOutputTest, withOneTestGroupAndOneTestOnlyWriteToOneFile) { testCaseRunner->start() .withGroup("groupname").withTest("testname") .end(); LONGS_EQUAL(1, fileSystem.amountOfFiles()); CHECK(fileSystem.fileExists("cpputest_groupname.xml")); } TEST(JUnitOutputTest, withOneTestGroupAndOneTestOutputsValidXMLFiles) { testCaseRunner->start() .withGroup("groupname").withTest("testname") .end(); outputFile = fileSystem.file("cpputest_groupname.xml"); STRCMP_EQUAL("\n", outputFile->line(1)); } TEST(JUnitOutputTest, withOneTestGroupAndOneTestoutputsTestSuiteStartAndEndBlocks) { testCaseRunner->start() .withGroup("groupname").withTest("testname") .end(); outputFile = fileSystem.file("cpputest_groupname.xml"); STRCMP_EQUAL("\n", outputFile->line(2)); STRCMP_EQUAL("", outputFile->lineFromTheBack(1)); } TEST(JUnitOutputTest, withOneTestGroupAndOneTestFileShouldContainAnEmptyPropertiesBlock) { testCaseRunner->start() .withGroup("groupname").withTest("testname") .end(); outputFile = fileSystem.file("cpputest_groupname.xml"); STRCMP_EQUAL("\n", outputFile->line(3)); STRCMP_EQUAL("\n", outputFile->line(4)); } TEST(JUnitOutputTest, withOneTestGroupAndOneTestFileShouldContainAnEmptyStdoutBlock) { testCaseRunner->start() .withGroup("groupname").withTest("testname") .end(); outputFile = fileSystem.file("cpputest_groupname.xml"); STRCMP_EQUAL("\n", outputFile->lineFromTheBack(3)); } TEST(JUnitOutputTest, withOneTestGroupAndOneTestFileShouldContainAnEmptyStderrBlock) { testCaseRunner->start() .withGroup("groupname").withTest("testname") .end(); outputFile = fileSystem.file("cpputest_groupname.xml"); STRCMP_EQUAL("\n", outputFile->lineFromTheBack(2)); } TEST(JUnitOutputTest, withOneTestGroupAndOneTestFileShouldContainsATestCaseBlock) { testCaseRunner->start() .withGroup("groupname").withTest("testname") .end(); outputFile = fileSystem.file("cpputest_groupname.xml"); STRCMP_EQUAL("\n", outputFile->line(5)); STRCMP_EQUAL("\n", outputFile->line(6)); } TEST(JUnitOutputTest, withOneTestGroupAndTwoTestCasesCreateCorrectTestgroupBlockAndCorrectTestCaseBlock) { testCaseRunner->start() .withGroup("twoTestsGroup").withTest("firstTestName").withTest("secondTestName") .end(); outputFile = fileSystem.file("cpputest_twoTestsGroup.xml"); STRCMP_EQUAL("\n", outputFile->line(2)); STRCMP_EQUAL("\n", outputFile->line(5)); STRCMP_EQUAL("\n", outputFile->line(6)); STRCMP_EQUAL("\n", outputFile->line(7)); STRCMP_EQUAL("\n", outputFile->line(8)); } TEST(JUnitOutputTest, withOneTestGroupAndTimeHasElapsedAndTimestampChanged) { testCaseRunner->start().atTime("2013-07-04T22:28:00") .withGroup("timeGroup").withTest("Dummy").thatTakes(10).seconds() .end(); outputFile = fileSystem.file("cpputest_timeGroup.xml"); STRCMP_EQUAL("\n", outputFile->line(2)); } TEST(JUnitOutputTest, withOneTestGroupAndMultipleTestCasesWithElapsedTime) { testCaseRunner->start() .withGroup("twoTestsGroup") .withTest("firstTestName").thatTakes(10).seconds() .withTest("secondTestName").thatTakes(50).seconds() .end(); outputFile = fileSystem.file("cpputest_twoTestsGroup.xml"); STRCMP_EQUAL("\n", outputFile->line(2)); STRCMP_EQUAL("\n", outputFile->line(5)); STRCMP_EQUAL("\n", outputFile->line(6)); STRCMP_EQUAL("\n", outputFile->line(7)); STRCMP_EQUAL("\n", outputFile->line(8)); } TEST(JUnitOutputTest, withOneTestGroupAndOneFailingTest) { testCaseRunner->start() .withGroup("testGroupWithFailingTest") .withTest("FailingTestName").thatFails("Test failed", "thisfile", 10) .end(); outputFile = fileSystem.file("cpputest_testGroupWithFailingTest.xml"); STRCMP_EQUAL("\n", outputFile->line(2)); STRCMP_EQUAL("\n", outputFile->line(5)); STRCMP_EQUAL("\n", outputFile->line(6)); STRCMP_EQUAL("\n", outputFile->line(7)); STRCMP_EQUAL("\n", outputFile->line(8)); } TEST(JUnitOutputTest, withTwoTestGroupAndOneFailingTest) { testCaseRunner->start() .withGroup("testGroupWithFailingTest") .withTest("FirstTest") .withTest("FailingTestName").thatFails("Test failed", "thisfile", 10) .end(); outputFile = fileSystem.file("cpputest_testGroupWithFailingTest.xml"); STRCMP_EQUAL("\n", outputFile->line(2)); STRCMP_EQUAL("\n", outputFile->line(7)); STRCMP_EQUAL("\n", outputFile->line(8)); } TEST(JUnitOutputTest, testFailureWithLessThanAndGreaterThanInsideIt) { testCaseRunner->start() .withGroup("testGroupWithFailingTest") .withTest("FailingTestName").thatFails("Test ", "thisfile", 10) .end(); outputFile = fileSystem.file("cpputest_testGroupWithFailingTest.xml"); STRCMP_EQUAL("\n", outputFile->line(6)); } TEST(JUnitOutputTest, testFailureWithQuotesInIt) { testCaseRunner->start() .withGroup("testGroupWithFailingTest") .withTest("FailingTestName").thatFails("Test \"failed\"", "thisfile", 10) .end(); outputFile = fileSystem.file("cpputest_testGroupWithFailingTest.xml"); STRCMP_EQUAL("\n", outputFile->line(6)); } TEST(JUnitOutputTest, testFailureWithNewlineInIt) { testCaseRunner->start() .withGroup("testGroupWithFailingTest") .withTest("FailingTestName").thatFails("Test \nfailed", "thisfile", 10) .end(); outputFile = fileSystem.file("cpputest_testGroupWithFailingTest.xml"); STRCMP_EQUAL("\n", outputFile->line(6)); } TEST(JUnitOutputTest, testFailureWithDifferentFileAndLine) { testCaseRunner->start() .withGroup("testGroupWithFailingTest") .withTest("FailingTestName").thatFails("Test failed", "importantFile", 999) .end(); outputFile = fileSystem.file("cpputest_testGroupWithFailingTest.xml"); STRCMP_EQUAL("\n", outputFile->line(6)); } TEST(JUnitOutputTest, testFailureWithAmpersands) { testCaseRunner->start() .withGroup("testGroupWithFailingTest") .withTest("FailingTestName").thatFails("&object1 != &object2", "importantFile", 999) .end(); outputFile = fileSystem.file("cpputest_testGroupWithFailingTest.xml"); STRCMP_EQUAL("\n", outputFile->line(6)); } TEST(JUnitOutputTest, aCoupleOfTestFailures) { testCaseRunner->start() .withGroup("testGroup") .withTest("passingOne") .withTest("FailingTest").thatFails("Failure", "file", 99) .withTest("passingTwo") .withTest("passingThree") .withTest("AnotherFailingTest").thatFails("otherFailure", "anotherFile", 10) .end(); outputFile = fileSystem.file("cpputest_testGroup.xml"); STRCMP_EQUAL("\n", outputFile->line(8)); STRCMP_EQUAL("\n", outputFile->line(16)); } TEST(JUnitOutputTest, testFailuresInSeparateGroups) { testCaseRunner->start() .withGroup("testGroup") .withTest("passingOne") .withTest("FailingTest").thatFails("Failure", "file", 99) .withGroup("AnotherGroup") .withTest("AnotherFailingTest").thatFails("otherFailure", "anotherFile", 10) .end(); outputFile = fileSystem.file("cpputest_testGroup.xml"); STRCMP_EQUAL("\n", outputFile->line(8)); outputFile = fileSystem.file("cpputest_AnotherGroup.xml"); STRCMP_EQUAL("\n", outputFile->line(8)); } TEST(JUnitOutputTest, twoTestGroupsWriteToTwoDifferentFiles) { testCaseRunner->start() .withGroup("firstTestGroup") .withTest("testName") .withGroup("secondTestGroup") .withTest("testName") .end(); CHECK(fileSystem.file("cpputest_firstTestGroup.xml")); CHECK(fileSystem.file("cpputest_secondTestGroup.xml")); } TEST(JUnitOutputTest, testGroupWithWeirdName) { STRCMP_EQUAL("cpputest_group_weird_name.xml", junitOutput->createFileName("group/weird/name").asCharString()); } TEST(JUnitOutputTest, TestCaseBlockWithAPackageName) { junitOutput->setPackageName("packagename"); testCaseRunner->start() .withGroup("groupname").withTest("testname") .end(); outputFile = fileSystem.file("cpputest_groupname.xml"); STRCMP_EQUAL("\n", outputFile->line(5)); STRCMP_EQUAL("\n", outputFile->line(6)); } TEST(JUnitOutputTest, TestCaseBlockForIgnoredTest) { junitOutput->setPackageName("packagename"); testCaseRunner->start() .withGroup("groupname").withIgnoredTest("testname") .end(); outputFile = fileSystem.file("cpputest_groupname.xml"); STRCMP_EQUAL("\n", outputFile->line(5)); STRCMP_EQUAL("\n", outputFile->line(6)); STRCMP_EQUAL("\n", outputFile->line(7)); }