1 /*---------------------------------------------------------------------------* 2 Project: Horizon 3 File: test_XmlOutput.cpp 4 5 Copyright (C)2009 Nintendo Co., Ltd. All rights reserved. 6 7 These coded instructions, statements, and computer programs contain 8 proprietary information of Nintendo of America Inc. and/or Nintendo 9 Company Ltd., and are protected by Federal copyright law. They may 10 not be disclosed to third parties or copied or duplicated in any form, 11 in whole or in part, without the prior written consent of Nintendo. 12 13 $Rev: 18955 $ 14 *---------------------------------------------------------------------------*/ 15 16 #include <nn/test/test_XmlOutput.h> 17 #include <nn/test/test_Time.h> 18 #include <cstring> 19 #include <cstdlib> 20 21 #define NN_PRINT_TEST_LOG(...) (void)::nn::dbg::detail::TPrintf(__VA_ARGS__) 22 23 using namespace nn::dbg; 24 25 namespace nn{ namespace test{ 26 27 namespace { PrintXmlHeader()28 void PrintXmlHeader() { 29 NN_PRINT_TEST_LOG("<?xml version=\"1.0\"?>\n"); 30 } 31 PrintNnTestHeader()32 void PrintNnTestHeader() 33 { 34 NN_PRINT_TEST_LOG("<!-- begin: nntest-Results.xml -->\n"); 35 } 36 PrintNnTestFooter()37 void PrintNnTestFooter() 38 { 39 NN_PRINT_TEST_LOG("<!-- end: nntest-Results.xml -->\n"); 40 } 41 PrintCunitEndMarker(bool isSuccess)42 void PrintCunitEndMarker(bool isSuccess) 43 { 44 NN_PRINT_TEST_LOG("CU_TEST_EXIT:%s\n", isSuccess ? "SUCCESS" : "FAIL"); 45 } 46 47 // 特殊文字を変換 48 // & ---> & 49 // < ---> < 50 // > ---> > 51 // " ---> " 52 // ' ---> ' ConvertToXmlString(const char * string)53 String ConvertToXmlString(const char* string) 54 { 55 const int STR_LEN = 383; 56 char buf[STR_LEN + 1]; 57 58 const char specials[] = {'&', '<', '>', '"', '\''}; 59 const char* converts[] = {"&", "<", ">", """, "'"}; 60 const int copyLens[] = {5, 4, 4, 6, 6}; 61 const int SPECIALS_NUM = sizeof(specials); 62 const char* pSrc = string; 63 char* pDst = buf; 64 while(*pSrc != '\0') 65 { 66 int index = 0; 67 for(index = 0; index < SPECIALS_NUM; ++index) 68 { 69 if(*pSrc == specials[index]) 70 { 71 if(pDst + copyLens[index] - buf > STR_LEN) 72 { 73 NN_TPANIC_("Failed convert '%s', too long.\n", string); 74 } 75 ::std::memcpy(pDst, converts[index], copyLens[index]); 76 pDst += copyLens[index]; 77 break; 78 } 79 } 80 if(index == SPECIALS_NUM) // 特殊文字でない 81 { 82 if(pDst + 1 - buf > STR_LEN) 83 { 84 NN_TPANIC_("Failed convert '%s', too long.\n", string); 85 } 86 *pDst++ = *pSrc; 87 } 88 pSrc++; 89 } 90 *pDst = '\0'; 91 return String(buf); 92 } 93 94 } 95 // -------------------------------------------------------- 96 Print()97 void Attribute::Print() 98 { 99 NN_PRINT_TEST_LOG(" %s=\"", m_Name.GetBuffer()); 100 NN_PRINT_TEST_LOG(ConvertToXmlString(m_Value.GetBuffer()).GetBuffer()); 101 NN_PRINT_TEST_LOG("\""); 102 } 103 // -------------------------------------------------------- 104 ~Node()105 Node::~Node() 106 { 107 // 属性の破棄 108 for(Attribute* pAttr = m_Attributes.PopFront(); 109 pAttr; 110 pAttr = m_Attributes.PopFront()) 111 { 112 delete pAttr; 113 } 114 // 子要素の破棄 115 for(Node* pNode = m_Children.PopFront(); 116 pNode; 117 pNode = m_Children.PopFront()) 118 { 119 delete pNode; 120 } 121 } 122 CreateChildNode(const char * name)123 Node* Node::CreateChildNode(const char* name) 124 { 125 Node* pChild = new Node(); 126 if(pChild == NULL) 127 { 128 return NULL; 129 } 130 pChild->m_Name = name; 131 132 AddChildNode(pChild); 133 return pChild; 134 } 135 SetAttribute(const char * name,const char * value)136 void Node::SetAttribute(const char* name, const char* value) 137 { 138 Attribute* pAttr = new Attribute(name, value); 139 if(pAttr == NULL) 140 { 141 NN_TPANIC_("Failed set attribute"); 142 } 143 m_Attributes.PushBack(pAttr); 144 } 145 PrintIndent(int depth)146 void Node::PrintIndent(int depth) 147 { 148 // インデントの出力 149 for(int i = 0; i < depth * INDENT; ++i) 150 { 151 NN_PRINT_TEST_LOG(" "); 152 } 153 } 154 PrintBeginTag()155 void Node::PrintBeginTag() 156 { 157 // 名前出力 158 NN_PRINT_TEST_LOG("<"); 159 NN_PRINT_TEST_LOG(m_Name.GetBuffer()); 160 161 // 属性出力 162 for(Attribute* pAttr = m_Attributes.GetFront(); 163 pAttr; 164 pAttr = m_Attributes.GetNext(pAttr)) 165 { 166 pAttr->Print(); 167 } 168 169 // タグ終了 170 NN_PRINT_TEST_LOG(">"); 171 172 } 173 PrintEndTag()174 void Node::PrintEndTag() 175 { 176 NN_PRINT_TEST_LOG("</%s>", m_Name.GetBuffer()); 177 } 178 Print(int depth)179 void Node::Print(int depth) 180 { 181 // インデント 182 PrintIndent(depth); 183 184 // 開始タグの出力 185 PrintBeginTag(); 186 187 // TORIAEZU: 188 // 値がある 且つ 子要素を持つなら 189 // とりあえずパニック 190 if(m_Value != "" && !m_Children.IsEmpty()) 191 { 192 NN_TPANIC_("<%s> has value and childs", m_Name.GetBuffer()); 193 } 194 195 // 値の出力 196 if(m_Value != "") 197 { 198 // OutputDebugString("%s", m_Value.GetBuffer()); 199 NN_PRINT_TEST_LOG(ConvertToXmlString(m_Value.GetBuffer()).GetBuffer()); 200 } 201 // 子要素の出力 202 else if(!m_Children.IsEmpty()) 203 { 204 NN_PRINT_TEST_LOG("\n"); 205 for(Node* pNode = m_Children.GetFront(); 206 pNode; 207 pNode = m_Children.GetNext(pNode)) 208 { 209 pNode->Print(depth + 1); 210 } 211 PrintIndent(depth); 212 } 213 214 // 終了タグの出力 215 PrintEndTag(); 216 217 // 改行 218 NN_PRINT_TEST_LOG("\n"); 219 } 220 221 222 GetSpecificChildrenNum(const char * name)223 int Node::GetSpecificChildrenNum(const char* name) 224 { 225 int num = 0; 226 if(!m_Children.IsEmpty()) 227 { 228 for(Node* pNode = m_Children.GetFront(); 229 pNode; 230 pNode = m_Children.GetNext(pNode)) 231 { 232 if(String(name) == pNode->GetName()) 233 { 234 ++num; 235 } 236 237 num += pNode->GetSpecificChildrenNum(name); 238 } 239 } 240 241 return num; 242 } 243 244 // -------------------------------------------------------- 245 XmlOutput()246 XmlOutput::XmlOutput() : m_pCurrentSuiteResult(NULL), m_pCurrentCaseResult(NULL), m_OutputMutex(false) 247 { 248 } 249 ~XmlOutput()250 XmlOutput::~XmlOutput() 251 { 252 for(Node* pNode = m_Results.PopFront(); 253 pNode; 254 pNode = m_Results.PopFront()) 255 { 256 delete pNode; 257 } 258 } 259 260 OnInitialize(int testsNum,int suitesNum)261 void XmlOutput::OnInitialize(int testsNum, int suitesNum) 262 { 263 NN_TASSERT_(m_Results.IsEmpty()); 264 265 NN_PRINT_TEST_LOG("%d tests exist\n", testsNum); 266 NN_PRINT_TEST_LOG("%d suites exist\n", suitesNum); 267 268 m_IsSuccess = true; 269 } 270 OnFinished(int testsNum,const Time & time)271 void XmlOutput::OnFinished(int testsNum, const Time& time) 272 { 273 NN_UNUSED_VAR(testsNum); 274 NN_UNUSED_VAR(time); 275 276 for(Node* pResult = m_Results.GetFront(); 277 pResult; 278 pResult = m_Results.GetNext(pResult)) 279 { 280 NN_PRINT_TEST_LOG("----------------------------------\n"); 281 PrintNnTestHeader(); 282 PrintXmlHeader(); 283 pResult->Print(); 284 PrintNnTestFooter(); 285 } 286 m_Results.Clear(); 287 288 PrintCunitEndMarker(m_IsSuccess); 289 } 290 291 OnSuiteStart(int testsNum,const String testName)292 void XmlOutput::OnSuiteStart(int testsNum, const String testName) 293 { 294 NN_PRINT_TEST_LOG("Suite: %s\n", testName.GetBuffer()); 295 296 // このスイートの結果を格納する XML のルートノードの作成 297 m_pCurrentSuiteResult = new Node("testsuite"); 298 NN_TASSERT_(m_pCurrentSuiteResult); 299 m_pCurrentSuiteResult->SetAttribute("name", testName.GetBuffer()); 300 m_pCurrentSuiteResult->SetAttribute("tests", testsNum); 301 302 m_Results.PushBack(m_pCurrentSuiteResult); 303 } 304 OnSuiteEnd(int testsNum,const String suiteName,const Time & time)305 void XmlOutput::OnSuiteEnd(int testsNum, const String suiteName, 306 const Time& time) 307 { 308 NN_UNUSED_VAR(suiteName); 309 NN_UNUSED_VAR(time); 310 NN_UNUSED_VAR(testsNum); 311 int errorNum = m_pCurrentSuiteResult->GetSpecificChildrenNum("error"); 312 m_pCurrentSuiteResult->SetAttribute("errors", errorNum); 313 m_pCurrentSuiteResult->SetAttribute("time", time.GetElapsedTime()); 314 } 315 OnTestStart(const String testName)316 void XmlOutput::OnTestStart(const String testName) 317 { 318 NN_PRINT_TEST_LOG(" Test: %s ...\n", testName.GetBuffer()); 319 m_CurrentTestName = testName; 320 321 NN_TASSERT_(m_pCurrentCaseResult == NULL); 322 NN_TASSERT_(m_pCurrentSuiteResult); 323 m_pCurrentCaseResult = m_pCurrentSuiteResult->CreateChildNode("testcase"); 324 m_pCurrentCaseResult->SetAttribute("classname", "nn"); 325 m_pCurrentCaseResult->SetAttribute("name", m_CurrentTestName.GetBuffer()); 326 } 327 OnTestEnd(const String testName,bool isSuccess,const Time & time)328 void XmlOutput::OnTestEnd(const String testName, bool isSuccess, 329 const Time& time) 330 { 331 NN_UNUSED_VAR(time); 332 NN_UNUSED_VAR(testName); 333 NN_UNUSED_VAR(isSuccess); 334 335 m_pCurrentCaseResult->SetAttribute("time", time.GetElapsedTime()); 336 m_pCurrentCaseResult = NULL; 337 } 338 PrintAssertInfo(const AssertInfo & info)339 void XmlOutput::PrintAssertInfo(const AssertInfo& info) 340 { 341 const String& fileName = info.GetFilename(); 342 const String& message = info.GetMessage(); 343 344 NN_PRINT_TEST_LOG(" - %s, line:%d, ", fileName.GetBuffer(), info.GetLine()); 345 NN_PRINT_TEST_LOG(message.GetBuffer()); 346 NN_PRINT_TEST_LOG("\n"); 347 } 348 OnAssert(const AssertInfo & info)349 void XmlOutput::OnAssert(const AssertInfo& info) 350 { 351 nn::os::Mutex::ScopedLock lk(m_OutputMutex); 352 PrintAssertInfo(info); 353 354 Node* pErrorNode = m_pCurrentCaseResult->CreateChildNode("error"); 355 pErrorNode->SetAttribute("type", ""); 356 pErrorNode->SetAttribute("message", info.GetMessage().GetBuffer()); 357 String value = "File: "; 358 value += info.GetFilename(); 359 value += " Line:"; 360 value += String(info.GetLine()); 361 pErrorNode->SetValue(value.GetBuffer()); 362 363 m_IsSuccess = false; // 1回でもアサートが呼ばれたら失敗 364 } 365 366 }} 367