1 // =================================
  2 // Copyright (c) 2021 Seppo Laakko
  3 // Distributed under the MIT license
  4 // =================================
  5 
  6 #include <cmajor/rt/UnitTest.hpp>
  7 #include <cmajor/rt/InitDone.hpp>
  8 #include <sngxml/dom/Document.hpp>
  9 #include <sngxml/dom/Element.hpp>
 10 #include <soulng/util/CodeFormatter.hpp>
 11 #include <soulng/util/Unicode.hpp>
 12 #include <fstream>
 13 #include <memory>
 14 #include <string>
 15 #include <vector>
 16 
 17 namespace cmajor { namespace rt {
 18 
 19 using namespace soulng::unicode;
 20 
 21 const int assertionResultEmpty = 0;
 22 const int assertionResultPassed = 1;
 23 const int assertionResulFailed = 2;
 24 
 25 struct AssertionResult 
 26 {
 27     AssertionResult(int result_int32_t line_) : result(result_)line(line_) {}
 28     int result;
 29     int32_t line;
 30 };
 31 
 32 class UnitTestEngine 
 33 {
 34 public:
 35     static void Init();
 36     static void Done();
 37     static UnitTestEngine& Instance() { return *instance; }
 38     void StartUnitTest(int32_t numAssertions_const char* unitTestFilePath_);
 39     void EndUnitTest(const char* testNameint32_t exitCode);
 40     void SetUnitTestAssertionResult(int32_t assertionIndexbool assertionResultint32_t line);
 41     void SetUnitTestException(const std::string& exceptionStr_) { exceptionStr = exceptionStr_; }
 42 private:
 43     static std::unique_ptr<UnitTestEngine> instance;
 44     int numAssertions;
 45     std::string unitTestFilePath;
 46     std::string exceptionStr;
 47     std::vector<AssertionResult> assertionResults;
 48 };
 49 
 50 std::unique_ptr<UnitTestEngine> UnitTestEngine::instance;
 51 
 52 void UnitTestEngine::Init()
 53 {
 54     instance.reset(new UnitTestEngine());
 55 }
 56 
 57 void UnitTestEngine::Done()
 58 {
 59     instance.reset();
 60 }
 61 
 62 void UnitTestEngine::StartUnitTest(int32_t numAssertions_const char* unitTestFilePath_)
 63 {
 64     numAssertions = numAssertions_;
 65     unitTestFilePath = unitTestFilePath_;
 66     for (int32_t i = 0; i < numAssertions; ++i)
 67     {
 68         assertionResults.push_back(AssertionResult(assertionResultEmpty0));
 69     }
 70 }
 71 
 72 void UnitTestEngine::EndUnitTest(const char* testNameint32_t exitCode)
 73 {
 74     std::ofstream testXmlFile(unitTestFilePath);
 75     sngxml::dom::Document document;
 76     std::unique_ptr<sngxml::dom::Element> testElement(new sngxml::dom::Element(U"test"));
 77     testElement->SetAttribute(U"name"ToUtf32(testName));
 78     for (int32_t i = 0; i < numAssertions; ++i)
 79     {
 80         std::unique_ptr<sngxml::dom::Element> assertionElement(new sngxml::dom::Element(U"assertion"));
 81         assertionElement->SetAttribute(U"index"ToUtf32(std::to_string(i)));
 82         const AssertionResult& assertionResult = assertionResults[i];
 83         std::u32string assertionResultStr = U"empty";
 84         if (assertionResult.result == assertionResultPassed)
 85         {
 86             assertionResultStr = U"passed";
 87         }
 88         else if (assertionResult.result == assertionResulFailed)
 89         {
 90             assertionResultStr = U"failed";
 91         }
 92         assertionElement->SetAttribute(U"result"assertionResultStr);
 93         assertionElement->SetAttribute(U"line"ToUtf32(std::to_string(assertionResult.line)));
 94         testElement->AppendChild(std::unique_ptr<sngxml::dom::Node>(assertionElement.release()));
 95     }
 96     testElement->SetAttribute(U"exitCode"ToUtf32(std::to_string(exitCode)));
 97     testElement->SetAttribute(U"count"ToUtf32(std::to_string(numAssertions)));
 98     if (!exceptionStr.empty())
 99     {
100         testElement->SetAttribute(U"exception"ToUtf32(exceptionStr));
101     }
102     document.AppendChild(std::unique_ptr<sngxml::dom::Node>(testElement.release()));
103     soulng::util::CodeFormatter formatter(testXmlFile);
104     formatter.SetIndentSize(2);
105     document.Write(formatter);
106 }
107 
108 void UnitTestEngine::SetUnitTestAssertionResult(int32_t assertionIndexbool assertionResultint32_t line)
109 {
110     AssertionResult& ar = assertionResults[assertionIndex];
111     if (assertionResult)
112     {
113         if (ar.result == assertionResultEmpty || ar.result == assertionResultPassed)
114         {
115             assertionResults[assertionIndex] = AssertionResult(assertionResultPassedline);
116         }
117         else
118         {
119             assertionResults[assertionIndex] = AssertionResult(assertionResulFailedline);
120         }
121     }
122     else
123     {
124         assertionResults[assertionIndex] = AssertionResult(assertionResulFailedline);
125     }
126 }
127 
128 } } // namespace cmajor::rt
129 
130 extern "C" void RtStartUnitTest(int32_t numAssertions, const char* unitTestFilePath, int64_t numberOfPolymorphicClassIds, const uint64_t* polymorphicClassIdArray,
131     int64_t numberOfStaticClassIds, const uint64_t* staticClassIdArray)
132 {
133     RtInit(numberOfPolymorphicClassIds, polymorphicClassIdArray, numberOfStaticClassIds, staticClassIdArray, nullptr);
134     cmajor::rt::UnitTestEngine::Init();
135     cmajor::rt::UnitTestEngine::Instance().StartUnitTest(numAssertions, unitTestFilePath);
136 }
137 
138 extern "C" void RtEndUnitTest(const char* testName, int32_t exitCode)
139 {
140     cmajor::rt::UnitTestEngine::Instance().EndUnitTest(testName, exitCode);
141     cmajor::rt::UnitTestEngine::Done();
142     RtDone();
143     RtExit(exitCode);
144 }
145 
146 extern "C" void RtSetUnitTestAssertionResult(int32_t assertionIndex, bool assertionResult, int32_t line)
147 {
148     cmajor::rt::UnitTestEngine::Instance().SetUnitTestAssertionResult(assertionIndex, assertionResult, line);
149 }
150 
151 extern "C" void RtSetUnitTestException(const char* exceptionStr)
152 {
153     cmajor::rt::UnitTestEngine::Instance().SetUnitTestException(exceptionStr);
154 }