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         // & ---> &amp;
49         // < ---> &lt;
50         // > ---> &gt;
51         // " ---> &quot;
52         // ' ---> &apos;
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[] = {"&amp;", "&lt;", "&gt;", "&quot;", "&apos;"};
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