1 /*---------------------------------------------------------------------------*
2   Project:  Horizon
3   File:     test_XmlOutput.cpp
4 
5   Copyright (C)2009-2012 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: 46347 $
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