Updated icon.png and config.xml for all widget related file in WRT TCs
[test/tct/web/wrt.git] / tct-ext02-wrt-tests / setting-install-location-prefer-external-no-card / resources / testharnessreport.js
1 /*
2  * This file is intended for vendors to implement
3  * code needed to integrate testharness.js tests with their own test systems.
4  *
5  * The default implementation extracts metadata from the tests and validates 
6  * it against the cached version that should be present in the test source 
7  * file. If the cache is not found or is out of sync, source code suitable for
8  * caching the metadata is optionally generated.
9  *
10  * The cached metadata is present for extraction by test processing tools that
11  * are unable to execute javascript.
12  *
13  * Metadata is attached to tests via the properties parameter in the test
14  * constructor. See testharness.js for details.
15  *
16  * Typically test system integration will attach callbacks when each test has
17  * run, using add_result_callback(callback(test)), or when the whole test file
18  * has completed, using 
19  * add_completion_callback(callback(tests, harness_status)).
20  *
21  * For more documentation about the callback functions and the
22  * parameters they are called with see testharness.js
23  */
24
25
26
27 var metadata_generator = {
28
29     currentMetadata: {},
30     cachedMetadata: false,
31     metadataProperties: ['help', 'assert', 'author'],
32     
33     error: function(message) {
34         var messageElement = document.createElement('p');
35         messageElement.setAttribute('class', 'error');
36         this.appendText(messageElement, message);
37         
38         var summary = document.getElementById('summary');
39         if (summary) {
40             summary.parentNode.insertBefore(messageElement, summary);
41         }
42         else {
43             document.body.appendChild(messageElement);
44         }
45     },
46
47     /**
48      * Ensure property value has contact information
49      */
50     validateContact: function(test, propertyName) {
51         var result = true;
52         var value = test.properties[propertyName];
53         var values = Array.isArray(value) ? value : [value];
54         for (var index = 0; index < values.length; index++) {
55             value = values[index];
56             var re = /(\S+)(\s*)<(.*)>(.*)/;
57             if (! re.test(value)) {
58                 re = /(\S+)(\s+)(http[s]?:\/\/)(.*)/
59                 if (! re.test(value)) {
60                     this.error('Metadata property "' + propertyName + 
61                         '" for test: "' + test.name +
62                         '" must have name and contact information ' +
63                         '("name <email>" or "name http(s)://")');
64                     result = false;
65                 }
66             }
67         }
68         return result;
69     },
70     
71     /**
72      * Extract metadata from test object
73      */
74     extractFromTest: function(test) {
75         var testMetadata = {};
76         // filter out metadata from other properties in test
77         for (var metaIndex = 0; metaIndex < this.metadataProperties.length;
78              metaIndex++) {
79             var meta = this.metadataProperties[metaIndex];
80             if (test.properties.hasOwnProperty(meta)) {
81                 if ('author' == meta) {
82                     this.validateContact(test, meta);
83                 }
84                 testMetadata[meta] = test.properties[meta];
85             }
86         }
87         return testMetadata;
88     },
89     
90     /**
91      * Compare cached metadata to extracted metadata
92      */
93     validateCache: function() {
94         for (var testName in this.currentMetadata) {
95             if (! this.cachedMetadata.hasOwnProperty(testName)) {
96                 return false;
97             }
98             var testMetadata = this.currentMetadata[testName];
99             var cachedTestMetadata = this.cachedMetadata[testName];
100             delete this.cachedMetadata[testName];
101             
102             for (var metaIndex = 0; metaIndex < this.metadataProperties.length;
103                  metaIndex++) {
104                 var meta = this.metadataProperties[metaIndex];
105                 if (cachedTestMetadata.hasOwnProperty(meta) && 
106                     testMetadata.hasOwnProperty(meta)) {
107                     if (Array.isArray(cachedTestMetadata[meta])) {
108                       if (! Array.isArray(testMetadata[meta])) {
109                           return false;
110                       }
111                       if (cachedTestMetadata[meta].length == 
112                           testMetadata[meta].length) {
113                           for (var index = 0; 
114                                index < cachedTestMetadata[meta].length; 
115                                index++) {
116                               if (cachedTestMetadata[meta][index] != 
117                                   testMetadata[meta][index]) {
118                                   return false;
119                               }
120                           }
121                       }
122                       else {
123                           return false;
124                       }
125                     }
126                     else {
127                       if (Array.isArray(testMetadata[meta])) {
128                         return false;
129                       }
130                       if (cachedTestMetadata[meta] != testMetadata[meta]) {
131                         return false;
132                       }
133                     }
134                 }
135                 else if (cachedTestMetadata.hasOwnProperty(meta) || 
136                          testMetadata.hasOwnProperty(meta)) {
137                     return false;
138                 }
139             }
140         }
141         for (var testName in this.cachedMetadata) {
142             return false;
143         }
144         return true;
145     },
146   
147     appendText: function(elemement, text) {
148         elemement.appendChild(document.createTextNode(text));
149     },
150   
151     jsonifyArray: function(arrayValue, indent) {
152         var output = '[';
153
154         if (1 == arrayValue.length) {
155             output += JSON.stringify(arrayValue[0]);
156         }
157         else {
158             for (var index = 0; index < arrayValue.length; index++) {
159                 if (0 < index) {
160                     output += ',\n  ' + indent;
161                 }
162                 output += JSON.stringify(arrayValue[index]);
163             }
164         }
165         output += ']';
166         return output;
167     },
168     
169     jsonifyObject: function(objectValue, indent) {
170         var output = '{';
171         
172         var count = 0;
173         for (var property in objectValue) {
174             ++count;
175             if (Array.isArray(objectValue[property]) || 
176                 ('object' == typeof(value))) {
177                 ++count;
178             }
179         }
180         if (1 == count) {
181             for (var property in objectValue) {
182                 output += ' "' + property + '": '
183                           + JSON.stringify(objectValue[property])
184                           + ' ';
185             }
186         }
187         else {
188             var first = true;
189             for (var property in objectValue) {
190                 if (! first) {
191                     output += ',';
192                 }
193                 first = false;
194                 output += '\n  ' + indent + '"' + property + '": ';
195                 var value = objectValue[property];
196                 if (Array.isArray(value)) {
197                     output += this.jsonifyArray(value, indent + 
198                         '                '.substr(0, 5 + property.length));
199                 }
200                 else if ('object' == typeof(value)) {
201                     output += this.jsonifyObject(value, indent + '  ');
202                 }
203                 else {
204                     output += JSON.stringify(value);
205                 }
206             }
207             if (1 < output.length) {
208                 output += '\n' + indent;
209             }
210         }
211         output += '}';
212         return output;
213     },
214   
215     /**
216      * Generate javascript source code for captured metadata
217      * Metadata is in pretty-printed JSON format
218      */
219     generateSource: function() {
220         var source = 
221             '<script id="metadata_cache">/*\n' + 
222             this.jsonifyObject(this.currentMetadata, '') + '\n' + 
223             '*/</script>\n';
224         return source;
225     },
226     
227     /**
228      * Add element containing metadata source code
229      */
230     addSourceElement: function(event) {
231         var sourceWrapper = document.createElement('div');
232         sourceWrapper.setAttribute('id', 'metadata_source');
233
234         var instructions = document.createElement('p');
235         if (this.cachedMetadata) {
236             this.appendText(instructions, 
237                 'Replace the existing <script id="metadata_cache"> element ' + 
238                 'in the test\'s <head> with the following:');
239         }
240         else {
241             this.appendText(instructions, 
242                 'Copy the following into the <head> element of the test ' +
243                 'or the test\'s metadata sidecar file:');
244         }
245         sourceWrapper.appendChild(instructions);
246         
247         var sourceElement = document.createElement('pre');
248         this.appendText(sourceElement, this.generateSource());
249
250         sourceWrapper.appendChild(sourceElement);
251         
252         var messageElement = document.getElementById('metadata_issue');
253         messageElement.parentNode.insertBefore(sourceWrapper, 
254                                                messageElement.nextSibling);
255         messageElement.parentNode.removeChild(messageElement);
256
257         (event.preventDefault) ? event.preventDefault() : 
258                                  event.returnValue = false;
259     },
260     
261     /**
262      * Extract the metadata cache from the cache element if present
263      */
264     getCachedMetadata: function() {
265         var cacheElement = document.getElementById('metadata_cache');
266         
267         if (cacheElement) {
268             var cacheText = cacheElement.firstChild.nodeValue;
269             var openBrace = cacheText.indexOf('{');
270             var closeBrace = cacheText.lastIndexOf('}');
271             if ((-1 < openBrace) && (-1 < closeBrace)) {
272                 cacheText = cacheText.slice(openBrace, closeBrace + 1);
273                 try {
274                     this.cachedMetadata = JSON.parse(cacheText);
275                 }
276                 catch (exc) {
277                     this.cachedMetadata = 'Invalid JSON in Cached metadata. ';
278                 }
279             }
280             else {
281                 this.cachedMetadata = 'Metadata not found in cache element. ';
282             }
283         }
284     },
285     
286     /**
287      * Main entry point, extract metadata from tests, compare to cached version
288      * if present.
289      * If cache not present or differs from extrated metadata, generate an error
290      */
291     process: function(tests, harness_status) {
292         for (var index = 0; index < tests.length; index++) {
293             var test = tests[index];
294             if (this.currentMetadata.hasOwnProperty(test.name)) {
295                 this.error('Duplicate test name: ' + test.name);
296             }
297             else {
298                 this.currentMetadata[test.name] = this.extractFromTest(test);
299             }
300         }
301
302         this.getCachedMetadata();
303         
304         var message = null;
305         var messageClass = 'warning';
306         var showSource = false;
307         
308         if (0 == tests.length) {
309             if (this.cachedMetadata) {
310                 message = 'Cached metadata present but no tests. ';
311             }
312         }
313         else if (1 == tests.length) {
314             if (this.cachedMetadata) {
315                 message = 'Single test files should not have cached metadata. ';
316             }
317             else {
318                 var testMetadata = this.currentMetadata[tests[0].name];
319                 var hasMetadata = false;
320                 for (var meta in testMetadata) {
321                     hasMetadata |= testMetadata.hasOwnProperty(meta);
322                 }
323                 if (hasMetadata) {
324                     message = 'Single tests should not have metadata. ' +
325                               'Move metadata to <head>. ';
326                 }
327             }
328         }
329         else {
330             if (this.cachedMetadata) {
331                 messageClass = 'error';
332                 if ('string' == typeof(this.cachedMetadata)) {
333                     message = this.cachedMetadata;
334                     showSource = true;
335                 }
336                 else if (! this.validateCache()) {
337                     message = 'Cached metadata out of sync. ';
338                     showSource = true;
339                 }
340             }
341         }
342         
343         if (message) {
344             var messageElement = document.createElement('p');
345             messageElement.setAttribute('id', 'metadata_issue');
346             messageElement.setAttribute('class', messageClass);
347             this.appendText(messageElement, message);
348             
349             if (showSource) {
350                 var link = document.createElement('a');
351                 this.appendText(link, 'Click for source code.');
352                 link.setAttribute('href', '#');
353                 link.setAttribute('onclick', 
354                                   'metadata_generator.addSourceElement(event)');
355                 messageElement.appendChild(link);
356             }
357             
358             var summary = document.getElementById('summary');
359             if (summary) {
360                 summary.parentNode.insertBefore(messageElement, summary);
361             }
362             else {
363                 var log = document.getElementById('log');
364                 if (log) {
365                     log.appendChild(messageElement);
366                 }
367             }
368         }
369     },
370
371     setup: function() {
372         add_completion_callback(
373             function (tests, harness_status) { 
374                 metadata_generator.process(tests, harness_status)
375             });
376     }
377 }
378
379 metadata_generator.setup();
380 // vim: set expandtab shiftwidth=4 tabstop=4: