/*---------------------------------------------------------------------------* Project: Horizon File: test_XmlOutput.cpp Copyright (C)2009 Nintendo Co., Ltd. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Rev: 18955 $ *---------------------------------------------------------------------------*/ #include #include #include #include #define NN_PRINT_TEST_LOG(...) (void)::nn::dbg::detail::TPrintf(__VA_ARGS__) using namespace nn::dbg; namespace nn{ namespace test{ namespace { void PrintXmlHeader() { NN_PRINT_TEST_LOG("\n"); } void PrintNnTestHeader() { NN_PRINT_TEST_LOG("\n"); } void PrintNnTestFooter() { NN_PRINT_TEST_LOG("\n"); } void PrintCunitEndMarker(bool isSuccess) { NN_PRINT_TEST_LOG("CU_TEST_EXIT:%s\n", isSuccess ? "SUCCESS" : "FAIL"); } // 特殊文字を変換 // & ---> & // < ---> < // > ---> > // " ---> " // ' ---> ' String ConvertToXmlString(const char* string) { const int STR_LEN = 383; char buf[STR_LEN + 1]; const char specials[] = {'&', '<', '>', '"', '\''}; const char* converts[] = {"&", "<", ">", """, "'"}; const int copyLens[] = {5, 4, 4, 6, 6}; const int SPECIALS_NUM = sizeof(specials); const char* pSrc = string; char* pDst = buf; while(*pSrc != '\0') { int index = 0; for(index = 0; index < SPECIALS_NUM; ++index) { if(*pSrc == specials[index]) { if(pDst + copyLens[index] - buf > STR_LEN) { NN_TPANIC_("Failed convert '%s', too long.\n", string); } ::std::memcpy(pDst, converts[index], copyLens[index]); pDst += copyLens[index]; break; } } if(index == SPECIALS_NUM) // 特殊文字でない { if(pDst + 1 - buf > STR_LEN) { NN_TPANIC_("Failed convert '%s', too long.\n", string); } *pDst++ = *pSrc; } pSrc++; } *pDst = '\0'; return String(buf); } } // -------------------------------------------------------- void Attribute::Print() { NN_PRINT_TEST_LOG(" %s=\"", m_Name.GetBuffer()); NN_PRINT_TEST_LOG(ConvertToXmlString(m_Value.GetBuffer()).GetBuffer()); NN_PRINT_TEST_LOG("\""); } // -------------------------------------------------------- Node::~Node() { // 属性の破棄 for(Attribute* pAttr = m_Attributes.PopFront(); pAttr; pAttr = m_Attributes.PopFront()) { delete pAttr; } // 子要素の破棄 for(Node* pNode = m_Children.PopFront(); pNode; pNode = m_Children.PopFront()) { delete pNode; } } Node* Node::CreateChildNode(const char* name) { Node* pChild = new Node(); if(pChild == NULL) { return NULL; } pChild->m_Name = name; AddChildNode(pChild); return pChild; } void Node::SetAttribute(const char* name, const char* value) { Attribute* pAttr = new Attribute(name, value); if(pAttr == NULL) { NN_TPANIC_("Failed set attribute"); } m_Attributes.PushBack(pAttr); } void Node::PrintIndent(int depth) { // インデントの出力 for(int i = 0; i < depth * INDENT; ++i) { NN_PRINT_TEST_LOG(" "); } } void Node::PrintBeginTag() { // 名前出力 NN_PRINT_TEST_LOG("<"); NN_PRINT_TEST_LOG(m_Name.GetBuffer()); // 属性出力 for(Attribute* pAttr = m_Attributes.GetFront(); pAttr; pAttr = m_Attributes.GetNext(pAttr)) { pAttr->Print(); } // タグ終了 NN_PRINT_TEST_LOG(">"); } void Node::PrintEndTag() { NN_PRINT_TEST_LOG("", m_Name.GetBuffer()); } void Node::Print(int depth) { // インデント PrintIndent(depth); // 開始タグの出力 PrintBeginTag(); // TORIAEZU: // 値がある 且つ 子要素を持つなら // とりあえずパニック if(m_Value != "" && !m_Children.IsEmpty()) { NN_TPANIC_("<%s> has value and childs", m_Name.GetBuffer()); } // 値の出力 if(m_Value != "") { // OutputDebugString("%s", m_Value.GetBuffer()); NN_PRINT_TEST_LOG(ConvertToXmlString(m_Value.GetBuffer()).GetBuffer()); } // 子要素の出力 else if(!m_Children.IsEmpty()) { NN_PRINT_TEST_LOG("\n"); for(Node* pNode = m_Children.GetFront(); pNode; pNode = m_Children.GetNext(pNode)) { pNode->Print(depth + 1); } PrintIndent(depth); } // 終了タグの出力 PrintEndTag(); // 改行 NN_PRINT_TEST_LOG("\n"); } int Node::GetSpecificChildrenNum(const char* name) { int num = 0; if(!m_Children.IsEmpty()) { for(Node* pNode = m_Children.GetFront(); pNode; pNode = m_Children.GetNext(pNode)) { if(String(name) == pNode->GetName()) { ++num; } num += pNode->GetSpecificChildrenNum(name); } } return num; } // -------------------------------------------------------- XmlOutput::XmlOutput() : m_pCurrentSuiteResult(NULL), m_pCurrentCaseResult(NULL), m_OutputMutex(false) { } XmlOutput::~XmlOutput() { for(Node* pNode = m_Results.PopFront(); pNode; pNode = m_Results.PopFront()) { delete pNode; } } void XmlOutput::OnInitialize(int testsNum, int suitesNum) { NN_TASSERT_(m_Results.IsEmpty()); NN_PRINT_TEST_LOG("%d tests exist\n", testsNum); NN_PRINT_TEST_LOG("%d suites exist\n", suitesNum); m_IsSuccess = true; } void XmlOutput::OnFinished(int testsNum, const Time& time) { NN_UNUSED_VAR(testsNum); NN_UNUSED_VAR(time); for(Node* pResult = m_Results.GetFront(); pResult; pResult = m_Results.GetNext(pResult)) { NN_PRINT_TEST_LOG("----------------------------------\n"); PrintNnTestHeader(); PrintXmlHeader(); pResult->Print(); PrintNnTestFooter(); } m_Results.Clear(); PrintCunitEndMarker(m_IsSuccess); } void XmlOutput::OnSuiteStart(int testsNum, const String testName) { NN_PRINT_TEST_LOG("Suite: %s\n", testName.GetBuffer()); // このスイートの結果を格納する XML のルートノードの作成 m_pCurrentSuiteResult = new Node("testsuite"); NN_TASSERT_(m_pCurrentSuiteResult); m_pCurrentSuiteResult->SetAttribute("name", testName.GetBuffer()); m_pCurrentSuiteResult->SetAttribute("tests", testsNum); m_Results.PushBack(m_pCurrentSuiteResult); } void XmlOutput::OnSuiteEnd(int testsNum, const String suiteName, const Time& time) { NN_UNUSED_VAR(suiteName); NN_UNUSED_VAR(time); NN_UNUSED_VAR(testsNum); int errorNum = m_pCurrentSuiteResult->GetSpecificChildrenNum("error"); m_pCurrentSuiteResult->SetAttribute("errors", errorNum); m_pCurrentSuiteResult->SetAttribute("time", time.GetElapsedTime()); } void XmlOutput::OnTestStart(const String testName) { NN_PRINT_TEST_LOG(" Test: %s ...\n", testName.GetBuffer()); m_CurrentTestName = testName; NN_TASSERT_(m_pCurrentCaseResult == NULL); NN_TASSERT_(m_pCurrentSuiteResult); m_pCurrentCaseResult = m_pCurrentSuiteResult->CreateChildNode("testcase"); m_pCurrentCaseResult->SetAttribute("classname", "nn"); m_pCurrentCaseResult->SetAttribute("name", m_CurrentTestName.GetBuffer()); } void XmlOutput::OnTestEnd(const String testName, bool isSuccess, const Time& time) { NN_UNUSED_VAR(time); NN_UNUSED_VAR(testName); NN_UNUSED_VAR(isSuccess); m_pCurrentCaseResult->SetAttribute("time", time.GetElapsedTime()); m_pCurrentCaseResult = NULL; } void XmlOutput::PrintAssertInfo(const AssertInfo& info) { const String& fileName = info.GetFilename(); const String& message = info.GetMessage(); NN_PRINT_TEST_LOG(" - %s, line:%d, ", fileName.GetBuffer(), info.GetLine()); NN_PRINT_TEST_LOG(message.GetBuffer()); NN_PRINT_TEST_LOG("\n"); } void XmlOutput::OnAssert(const AssertInfo& info) { nn::os::Mutex::ScopedLock lk(m_OutputMutex); PrintAssertInfo(info); Node* pErrorNode = m_pCurrentCaseResult->CreateChildNode("error"); pErrorNode->SetAttribute("type", ""); pErrorNode->SetAttribute("message", info.GetMessage().GetBuffer()); String value = "File: "; value += info.GetFilename(); value += " Line:"; value += String(info.GetLine()); pErrorNode->SetValue(value.GetBuffer()); m_IsSuccess = false; // 1回でもアサートが呼ばれたら失敗 } }}