1 /*---------------------------------------------------------------------------* 2 Project: Horizon 3 File: test_XmlOutput.cpp 4 Copyright (C)2009 Nintendo Co., Ltd. All rights reserved. 5 These coded instructions, statements, and computer programs contain 6 proprietary information of Nintendo of America Inc. and/or Nintendo 7 Company Ltd., and are protected by Federal copyright law. They may 8 not be disclosed to third parties or copied or duplicated in any form, 9 in whole or in part, without the prior written consent of Nintendo. 10 $Rev: 18955 $ 11 *--------------------------------------------------------------------------- 12 13 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 // Converts special characters 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 to 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) // Not a special character 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 // Destroy attribute 108 for(Attribute* pAttr = m_Attributes.PopFront(); 109 pAttr; 110 pAttr = m_Attributes.PopFront()) 111 { 112 delete pAttr; 113 } 114 // Destroy child element 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 to set attribute"); 142 } 143 m_Attributes.PushBack(pAttr); 144 } 145 PrintIndent(int depth)146 void Node::PrintIndent(int depth) 147 { 148 // Indent output 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 // Name output 158 NN_PRINT_TEST_LOG("<"); 159 NN_PRINT_TEST_LOG(m_Name.GetBuffer()); 160 161 // Attribute output 162 for(Attribute* pAttr = m_Attributes.GetFront(); 163 pAttr; 164 pAttr = m_Attributes.GetNext(pAttr)) 165 { 166 pAttr->Print(); 167 } 168 169 // Tag finish 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 // Indent 182 PrintIndent(depth); 183 184 // Output start tag 185 PrintBeginTag(); 186 187 // TODO: 188 // If there is a value AND child element 189 // Panic for now 190 if(m_Value != "" && !m_Children.IsEmpty()) 191 { 192 NN_TPANIC_("<%s> has value and children", m_Name.GetBuffer()); 193 } 194 195 // Output value 196 if(m_Value != "") 197 { 198 // OutputDebugString("%s", m_Value.GetBuffer()); 199 NN_PRINT_TEST_LOG(ConvertToXmlString(m_Value.GetBuffer()).GetBuffer()); 200 } 201 // Child element output 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 // Output end tag 215 PrintEndTag(); 216 217 // Newline 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 //Create root node for XML to store the result of this suite 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; // Fails even if one assert is called 364 } 365 366 }} 367