OpenLayers OpenLayers

root/trunk/openlayers/tests/run-tests.html

Revision 7452, 88.2 kB (checked in by tschaub, 3 weeks ago)

Patching Test.AnotherWay to use importNode in html_eq. r=crschmidt (closes #1376)

  • Property svn:eol-style set to native
Line 
1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
2 <html><head><title> Run the testsuite</title>
3 <noscript>Javascript is disabled in your browser. This page cannot be displayed correctly without Javascript. Sorry. <br/> If you want to view this page, please change your browser settings so that Javascript is enabled.</noscript>
4 <!--
5 Test.AnotherWay version 0.5
6
7 Copyright (c) 2005 Artem Khodush, http://straytree.org
8
9 Permission is hereby granted, free of charge, to any person obtaining
10 a copy of this software and associated documentation files (the
11 "Software"), to deal in the Software without restriction, including
12 without limitation the rights to use, copy, modify, merge, publish,
13 distribute, sublicense, and/or sell copies of the Software, and to
14 permit persons to whom the Software is furnished to do so, subject to
15 the following conditions:
16
17 The above copyright notice and this permission notice shall be
18 included in all copies or substantial portions of the Software.
19
20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 -->
28 <style type="text/css">
29 * { padding: 0; margin: 0; }
30 html { height: 99%; }
31 body { height: 98%; font: normal normal 10pt sans-serif }
32 #col1 {  float: left; width: 27em; margin: 0 0 0 1em; overflow: visible; }
33 #col2 {  position: relative; height: 98%; margin: 0 0.5em 0 28em; }
34 #col1_header { margin-top: 0.5em; }
35 #scroller { height: 550px; overflow: auto;}
36 #testtable { margin: 0 0 2em 0; width: 97%; }
37 #run_buttons { margin-bottom: 4em; }
38
39 #right_header { padding-top: 0.8em; }
40 #results_count { float: left; }
41 .active_tab             { float: right; padding: 0 1em 0.2em 1em; background: #0af; border: 1px solid #048; border-bottom: none; cursor: pointer; cursor: hand;
42                      position: relative; top: -0.2em; }
43 .inactive_tab           { float: right; padding: 0 1em 0 1em; background: #9bb; color: #444; border: 1px solid #9bb; border-bottom: none; cursor: pointer; cursor: hand; }
44 .inactive_mouseover_tab     { float: right; padding: 0 1em 0 1em; background: #9bb; color: #062; border: 1px solid #062; border-bottom: none; cursor: pointer; cursor: hand; }
45
46 #right_frame { overflow: auto; position: relative; top: -0.2em; clear: right; height: 95%; border: 1px solid #048; }
47
48 #debug { display: none; }
49 #debug p { margin: 2px 0 0 5em; text-indent: -4.8em; }
50
51 #error { display: none; color: #c22; }
52
53 #results p { margin: 0 0 2px 0; }
54 /* cursor indicating that detailed results may be expanded/contracted */
55 #results p.badtest { cursor: text; }
56 #results p.ok, #results p.fail { cursor: pointer; cursor: hand; }
57
58 /* colored squares in the results window at the left of test page names */
59 #results p.ok .bullet { background: #6d6; }
60 #results p.fail .bullet { background:  #d46; }
61 #results p.badtest .bullet { background: #ea3; }
62 #results p.loading .bullet { background: #48f; }
63 #results p.running .bullet { background: #26e; }
64 #results p.waiting .bullet { background: #04d; }
65 /* highlight in the results line */
66 #results p .warning { background: #ffc; }
67
68 /* layout of the detailed results */
69 .result_detail { padding-left: 3em; }
70 .result_exception_detail { padding-left: 4em; }
71 .result_exception_stack_detail { padding-left: 5em;  }
72 .result_micro_detail { padding-left: 6em; }
73 /* colouring in the detailed results */
74 .result_detail .fail, .result_exception_detail .fail,  .result_micro_detail .fail { background: #ffd8d8; }
75
76 /* "start recording" controls*/
77 #record_div { margin-top: 3em; }
78 #record_div p { margin-bottom: 0.5em; }
79 #record_select { width: 88%; }
80 #record_input { width: 53%; }
81 </style>
82 <script type="text/javascript">
83 <!--
84 if( typeof( Test )=="undefined" ) {
85     Test={};
86 }
87 Test.AnotherWay={};
88
89 Test.AnotherWay._g_test_iframe=null; // frame where to load test pages
90 Test.AnotherWay._g_test_frame_no_clear=false; // true - leave last page displayed after tests end
91 Test.AnotherWay._g_test_page_urls=[]; // array of: { url: url, convention: "anotherway" or "jsan" }
92 Test.AnotherWay._g_test_object_for_jsan=null; // test object for filling by tests that adhere to jsan Test.Simple calling convention
93 Test.AnotherWay._g_pages_to_run=null; // list of pages to run automatically after loading
94 Test.AnotherWay._g_run_on_main_load=false; // special handling for run_pages_to_run when it might be called before onload or before list of test pages is known.
95 Test.AnotherWay._g_run_on_list_load=false;
96 Test.AnotherWay._g_main_loaded=false;
97
98 Test.AnotherWay._run_pages_to_run=function( called_from_outside )
99 {
100     if( !Test.AnotherWay._g_main_loaded ) {
101         Test.AnotherWay._g_run_on_main_load=true;
102     }else {
103         var a_pages=Test.AnotherWay._g_pages_to_run;
104         if( a_pages=="all" ) {
105             for( var i=0; i<Test.AnotherWay._g_test_page_urls.length; ++i ) {
106                 Test.AnotherWay._run_test_page( "test"+i );
107             }
108         }else if( a_pages!=null ) {
109             for( var run_i=0; run_i<a_pages.length; ++run_i ) {
110                 var run_page=a_pages[run_i];
111                 var found=false;
112                 for( var all_i=0; all_i<Test.AnotherWay._g_test_page_urls.length; ++all_i ) {
113                     if( run_page==Test.AnotherWay._g_test_page_urls[all_i].url ) {
114                         Test.AnotherWay._run_test_page( "test"+all_i, called_from_outside );
115                         found=true;
116                         break;
117                     }
118                 }
119                 if( !found ) {
120                     Test.AnotherWay._show_error( "page specified to run is not found in the page list: "+run_page );
121                     break;
122                 }
123             }
124         }
125     }
126 }
127
128 Test.AnotherWay._add_test_page_url=function( test_url, convention )
129 {
130     var table=document.getElementById( "testtable" );
131     var record_select=document.getElementById( "record_select" );
132     var index=Test.AnotherWay._g_test_page_urls.length;
133
134     // trim spaces.
135     if( test_url.match( "^(\\s*)(.*\\S)(\\s*)$" ) ) {
136         test_url=RegExp.$2;
137     }
138
139     Test.AnotherWay._g_test_page_urls[index]={ url: test_url, convention: convention };
140     var row=table.insertRow( -1 );
141
142     var cell;
143     var cell_child;
144     cell=row.insertCell( -1 );
145     cell_child=document.createElement( "input" );
146     cell_child.type="checkbox";
147     cell_child.id="checkbox"+index;
148     cell_child.checked='checked';
149     cell_child.defaultChecked='checked';
150     cell.appendChild( cell_child );
151
152     cell=row.insertCell( -1 );
153     cell.setAttribute( "width", "75%" );
154     cell.appendChild( document.createTextNode( test_url ) );
155
156     cell=row.insertCell( -1 );
157     cell_child=document.createElement( "input" );
158     cell_child.type="button";
159     cell_child.id="test"+index;
160     cell_child.value=" run ";
161     cell_child.onclick=Test.AnotherWay._run_one_onclick;
162     cell.appendChild( cell_child );
163
164     cell=row.insertCell( -1 );
165     cell.setAttribute( "width", "8em" );
166     cell_child=document.createElement( "span" );
167     cell.appendChild( cell_child );
168
169     var option=document.createElement( "option" );
170     option.appendChild( document.createTextNode( test_url ) );
171     record_select.appendChild( option );
172 }
173 Test.AnotherWay._show_error=function( msg )
174 {
175     var error_div=document.getElementById( "error" );
176     error_div.innerHTML="";
177     error_div.appendChild( document.createTextNode( msg ) );
178     error_div.style.display="block";
179 }
180
181 // read urls from the list in the html file inside the list_iframe
182 // fill on-screen list with urls and "run" buttons, and fill the g_test_page_urls object.
183 Test.AnotherWay._list_iframe_onload=function()
184 {
185     if( window.frames.list_iframe!=null && window.frames.list_iframe.location!="" && window.frames.list_iframe.location!="about:blank" ) {
186         var list_doc=window.frames.list_iframe.document;
187         var list=list_doc.getElementById( "testlist" );
188         if( list!=null ) {
189             for( var i=0; i<list.childNodes.length; ++i ) {
190                 var item=list.childNodes[i];
191                 if( item.nodeName=="LI" || item.nodeName=="li" ) {
192                     var convention="anotherway";
193                     if( Test.AnotherWay._get_css_class( item )=="jsan" ) {
194                         convention="jsan";
195                     }
196                     Test.AnotherWay._add_test_page_url( item.innerHTML, convention );
197                 }
198             }
199             if( Test.AnotherWay._g_run_on_list_load ) {
200                 Test.AnotherWay._g_run_on_list_load=false;
201                 Test.AnotherWay._run_pages_to_run();
202             }
203         }else {
204             Test.AnotherWay._show_error( "no list with id 'testlist' in a list file "+window.frames.list_iframe.location );
205         }
206     }
207 }
208
209 Test.AnotherWay._map_checkboxes=function( f )
210 {
211     var table=document.getElementById( "testtable" );
212     var checks=table.getElementsByTagName( "INPUT" );
213     for( var i=0; i<checks.length; ++i ) {
214         if( checks[i].type=="checkbox" && checks[i].id.match( /^checkbox(\d+)$/ ) ) {
215             f( checks[i], RegExp.$1 );
216         }
217     }
218 }
219 Test.AnotherWay._run_all_onclick=function()
220 {
221     Test.AnotherWay._map_checkboxes( function( c, id ) { Test.AnotherWay._run_test_page( "test"+id ); } );
222 }
223 Test.AnotherWay._run_selected_onclick=function()
224 {
225     Test.AnotherWay._map_checkboxes( function( c, id ) { if( c.checked ) Test.AnotherWay._run_test_page( "test"+id ); } );
226 }
227 Test.AnotherWay._unselect_all_onclick=function()
228 {
229     Test.AnotherWay._map_checkboxes( function( c, id ) { c.checked=false; } );
230 }
231 Test.AnotherWay._run_one_onclick=function()
232 {
233     Test.AnotherWay._run_test_page( this.id );
234 }
235
236 // construct an object that will gather results of running one test function
237 Test.AnotherWay._test_object_t=function( fun_name )
238 {
239     this.name=fun_name; // name of the test function
240     this.n_plan=null; // planned number of assertions
241     this.n_ok=0; // # of ok assertions
242     this.n_fail=0; // # of failed assertions
243     this.exception=""; // if the function throwed an exception, it's its message
244     this.exception_stack=[]; // strings: function call stack from the exception
245     this.assertions=[]; // assertion results: array of { ok: 1 or 0, name: string }
246     this.wait_result_milliseconds=0; // how long to wait before collecting results from the test
247     this.second_wait_msg=null; // <p> status message (in addition to the page wait_msg)
248     this.delay_actions=[]; // array of actions to be perfomed after the test function returns
249                 //  action : { acton_kind: "call" | "window" | "replay"
250                 //              when "call":        { call_fn call_delay_milliseconds } call_fn takes nothing
251                 //              when "window" :     { wnd_url wnd_wnd wnd_fn wnd_timeout_milliseconds wnd_dont_close } wnd_fn takes wnd
252                 //              wnen "replay" :     { replay_wnd replay_events replay_event_i replay_checkpoints } checkpoint_fn takes this, wnd
253                 //  }
254     this.delay_action_i=null; // index of delay action currently being performed
255     this.delay_prev_timer_time=0;   // for counting time while performing delay_actions
256     this.delay_current_milliseconds_left=0; // time left before the next action, runs down
257     this.delay_total_milliseconds_left=0;   // for indication: total estimated time for all actions, runs up and down
258 }
259
260 Test.AnotherWay._test_object_t.prototype.ok=function( cond, name )
261 {
262     if( cond ) {
263         ++this.n_ok;
264         cond=1;
265     }else {
266         ++this.n_fail;
267         cond=0;
268     }
269     this.assertions.push( { ok: cond, name: name } );
270 }
271 Test.AnotherWay._test_object_t.prototype.fail=function( name )
272 {
273     this.ok( false, name );
274 }
275 Test.AnotherWay._test_object_t.prototype.plan=function( n )
276 {
277     this.n_plan=n;
278 }
279 Test.AnotherWay._test_object_t.prototype.wait_result=function( seconds )
280 {
281     this.wait_result_milliseconds=1000*seconds;
282 }
283 Test.AnotherWay._eq_fail_msg=function( path, what, expected, got )
284 {
285     return "eq: "+path+" "+what+" differ: got "+got+", but expected "+expected;
286 }
287 Test.AnotherWay._array_eq=function( expected, got, path, msg )
288 {
289     if( expected.length!=got.length ) {
290         msg.msg=Test.AnotherWay._eq_fail_msg( path, "array length", expected.length, got.length );
291         return false;
292     }
293     for( var i=0; i<expected.length; ++i ) {
294         if( !Test.AnotherWay._thing_eq( expected[i], got[i], path+"["+i+"]", msg ) ) {
295             return false;
296         }
297     }
298     return true;
299 }
300 Test.AnotherWay._object_eq=function( expected, got, path, msg )
301 {
302     var v;
303     for( v in expected ) {
304         if( ! (v in got) ) {
305             msg.msg=Test.AnotherWay._eq_fail_msg( path+"."+v, "properties", expected[v], "undefined" );
306             return false;
307         }
308         if( !Test.AnotherWay._thing_eq( expected[v], got[v], path+"."+v, msg ) ) {
309             return false;
310         }
311     }
312     for( v in got ) {
313         if( ! (v in expected) ) {
314             msg.msg=Test.AnotherWay._eq_fail_msg( path+"."+v, "properties", "undefined", got[v] );
315             return false;
316         }
317     }
318     return true;
319 }
320 Test.AnotherWay._constructor_name=function( x )
321 {
322     if( x==null ) {
323         return "";
324     }
325     var s="unknown";
326     try {
327         s=typeof( x.constructor );
328         if( s!="unknown" ) {
329             s=x.constructor.toString();
330         }
331     }catch( e ) {
332         s="unknown";
333     }
334     if( s=="unknown" ) {
335         // hackish attempt to guess a type
336         var is_array=true;
337         var index=0;
338         for( i in x ) {
339             if( i!=index ) {
340                 is_array=false;
341             }
342             ++index;
343         }
344         return is_array ? "Array" : "Object"; // for empty arrays/objects, this will be wrong half the time
345     }else if( s.match( /^\s*function\s+(\w+)\s*\(/ ) ) {
346         return RegExp.$1;
347     }else {
348       var c = '';
349       switch(typeof x) {
350         case 'string':
351           c = 'String';
352           break;
353         case 'object':
354           c = 'Object';
355           break;
356         default:
357           c = '';
358       }
359         return c;
360     }
361 }
362 Test.AnotherWay._is_array=function( x )
363 {
364     return Test.AnotherWay._constructor_name( x )=="Array";
365 }
366 Test.AnotherWay._is_value_type=function( x )
367 {
368     cn=Test.AnotherWay._constructor_name( x );
369     return cn=="Number" || cn=="String" || cn=="Boolean" || cn=="Date";
370 }
371 Test.AnotherWay._thing_eq=function( expected, got, path, msg )
372 {
373     if( expected==null && got==null ) {
374         return true;
375     }else if( (expected==null && got!=null) || (expected!=null && got==null) ) {
376         msg.msg=Test.AnotherWay._eq_fail_msg( path, "values", expected, got );
377         return false;
378     }else {
379         var expected_cn=Test.AnotherWay._constructor_name( expected );
380         var got_cn=Test.AnotherWay._constructor_name( got );
381         if( expected_cn!=got_cn ) {
382             msg.msg=Test.AnotherWay._eq_fail_msg( path, "types", expected_cn, got_cn );
383             return false;
384         }else {
385             if( Test.AnotherWay._is_array( expected ) ) {
386                 return Test.AnotherWay._array_eq( expected, got, path, msg );
387             }else if( Test.AnotherWay._is_value_type( expected ) ) {
388                 if( expected!=got ) {
389                     msg.msg=Test.AnotherWay._eq_fail_msg( path, "values", expected, got );
390                     return false;
391                 }else {
392                     return true;
393                 }
394             }else { // just a plain object
395                 return Test.AnotherWay._object_eq( expected, got, path, msg );
396             }
397         }
398     }
399 }
400 Test.AnotherWay._test_object_t.prototype.eq=function( got, expected, name )
401 {
402     var msg={};
403     if( Test.AnotherWay._thing_eq( expected, got, "", msg ) ) {
404         this.ok( 1, name );
405     }else {
406         this.fail( name+". "+msg.msg );
407     }
408 }
409 Test.AnotherWay._test_object_t.prototype.like=function( got, expected, name )
410 {
411     if( got.match( expected )!=null ) {
412         this.ok( 1, name );
413     }else {
414         this.fail( name+": got "+got+", but expected it to match: "+expected );
415     }
416 }
417 Test.AnotherWay._g_html_eq_span=null;
418 Test.AnotherWay._html_eq_string_to_node=function( string_or_node, what, msg )
419 {
420     if( string_or_node.nodeType!=null ) {
421         string_or_node=Test.AnotherWay._html_eq_node_to_string( string_or_node ); // double trip - to make properties assigned in scripts available as html node attributes
422     }
423     if( Test.AnotherWay._g_html_eq_span==null ) {
424         Test.AnotherWay._g_html_eq_span=document.createElement( "span" );
425     }
426     Test.AnotherWay._g_html_eq_span.innerHTML=string_or_node;
427     if( Test.AnotherWay._g_html_eq_span.childNodes.length!=1 ) {
428         msg.msg="bad "+what+" html string given (should contain exactly one outermost element): "+string_or_node;
429     }
430     return Test.AnotherWay._g_html_eq_span.childNodes[0].cloneNode( true );
431 }
432 Test.AnotherWay._html_eq_node_to_string=function( node ) {
433     if( Test.AnotherWay._g_html_eq_span==null ) {
434         Test.AnotherWay._g_html_eq_span=document.createElement( "span" );
435     }
436     Test.AnotherWay._g_html_eq_span.innerHTML="";
437     if( node.outerHTML!=null ) {
438         Test.AnotherWay._g_html_eq_span.innerHTML=node.outerHTML;
439     }else {
440             var clone = node.cloneNode(true);
441             var node = Test.AnotherWay._g_html_eq_span;
442             if(node.ownerDocument && node.ownerDocument.importNode) {
443                 if(node.ownerDocument != clone.ownerDocument) {
444                     clone = node.ownerDocument.importNode(clone, true);
445                 }
446             }
447             node.appendChild(clone);
448     }
449     return Test.AnotherWay._g_html_eq_span.innerHTML;
450 }
451 Test.AnotherWay._html_eq_path_msg=function( path )
452 {
453     var msg="";
454     for( var i=0; i<path.length; ++i ) {
455         msg+=" [node "+path[i].node;
456         if( path[i].id!=null && path[i].id!="" ) {
457             msg+=" id "+path[i].id;
458         }else if( path[i].index!=null ) {
459             msg+=" at index "+path[i].index;
460         }
461         msg+="] "
462     }
463     return msg;
464 }
465 Test.AnotherWay._html_eq_fail_msg=function( path, what, expected, got )
466 {
467     return Test.AnotherWay._html_eq_path_msg( path )+": "+what+" differ: got "+got+", but expected "+expected;
468 }
469 Test.AnotherWay._html_eq_remove_blank=function( text )
470 {
471     if( text==null ) {
472         return "";
473     }else if( text.match( "^(\\s*)(.*\\S)(\\s*)$" ) ) {
474         return RegExp.$2;
475     }else if( text.match( "\s*" ) ) {
476         return "";
477     }
478     return text;
479 }
480 Test.AnotherWay._html_eq_remove_blank_nodes=function( node )
481 {
482     var to_remove=[];
483     for( var child=node.firstChild; child!=null; child=child.nextSibling ) {
484         if( child.nodeType==3 ) {
485             var value=Test.AnotherWay._html_eq_remove_blank( child.nodeValue );
486             if( value=="" ) {
487                 to_remove.push( child );
488             }else {
489                 child.nodeValue=value;
490             }
491         }
492     }
493     for( var i=0; i<to_remove.length; ++i ) {
494         node.removeChild( to_remove[i] );
495     }
496 }
497 Test.AnotherWay._html_node_type_text=function( node_type )
498 {
499     if( node_type==1 ) {
500         return "1 (html element)";
501     }else if( node_type==3 ) {
502         return "3 (text)";
503     }else {
504         return node_type;
505     }
506 }
507 Test.AnotherWay._html_eq_node=function( expected, got, path, msg, expected_loc_base, got_loc_base )
508 {
509     if( expected.nodeType!=got.nodeType ) {
510         msg.msg=Test.AnotherWay._html_eq_fail_msg( path, "node types", Test.AnotherWay._html_node_type_text( expected.nodeType ), Test.AnotherWay._html_node_type_text( got.nodeType ) );
511         return false;
512     }else if( expected.nodeType==3 ) {
513         if( expected.nodeValue!=got.nodeValue ) {
514             msg.msg=Test.AnotherWay._html_eq_fail_msg( path, "text", expected.nodeValue, got.nodeValue );
515             return false;
516         }
517     }else if( expected.nodeType==1 ) {
518         if( expected.nodeName!=got.nodeName ) {
519             msg.msg=Test.AnotherWay._html_eq_fail_msg( path, "node names", expected.nodeName, got.nodeName );
520             return false;
521         }
522         // compare attributes
523         var expected_attrs={};
524         var got_attrs={};
525         var i;
526         var a;
527         for( i=0; i<expected.attributes.length; ++i ) {
528             a=expected.attributes[i];
529             if( a.specified ) {
530                 expected_attrs[a.name]=1;
531             }
532         }
533         for( i=0; i<got.attributes.length; ++i ) {
534             a=got.attributes[i];
535             if( a.specified ) {
536                 got_attrs[a.name]=1;
537             }
538         }
539         for( a in expected_attrs ) {
540             if( ! (a in got_attrs) ) {
541                 msg.msg=Test.AnotherWay._html_eq_path_msg( path )+": attribute sets differ: expected attribute "+a+" is missing";
542                 return false;
543             }
544         }
545         for( a in got_attrs ) {
546             if( ! (a in expected_attrs) ) {
547                 msg.msg=Test.AnotherWay._html_eq_path_msg( path )+": attribute sets differ: got extra attribute "+a;
548                 return false;
549             }
550         }
551         for( a in expected_attrs ) {
552             var expected_value=expected.getAttribute( a );
553             var got_value=got.getAttribute( a );
554             if( typeof( expected_value )=="string" && typeof( got_value )=="string" ) {
555                 expected_value=Test.AnotherWay._html_eq_remove_blank( expected_value );
556                 got_value=Test.AnotherWay._html_eq_remove_blank( got_value );
557                 var ok=expected_value==got_value;
558                 if( !ok && (a=="href" || a=="HREF" )  ) { // try relative hrefs
559                     var expected_relative_value=expected_value;
560                     if( expected_loc_base!=null && expected_value.substring( 0, expected_loc_base.length )==expected_loc_base ) {
561                         expected_relative_value=expected_value.substring( expected_loc_base.length );
562                     }
563                     var got_relative_value=got_value;
564                     if( got_loc_base!=null && got_value.substring( 0, got_loc_base.length )==got_loc_base ) {
565                         got_relative_value=got_value.substring( got_loc_base.length );
566                     }
567                     ok=expected_relative_value==got_relative_value;
568                 }
569                 if( !ok ) {
570                     msg.msg=Test.AnotherWay._html_eq_fail_msg( path, "attribute "+a+" values", expected_value, got_value );
571                     return false;
572                 }
573             }else if( typeof( expected_value )=="function" && typeof( got_value )=="function" ) {
574                 expected_value=expected_value.toString();
575                 got_value=got_value.toString();
576                 if( expected_value!=got_value ) {
577                     msg.msg=Test.AnotherWay._html_eq_fail_msg( path, "attribute "+a+" values", expected_value, got_value );
578                     return false;
579                 }
580             }else {
581                 var value_msg={};
582                 if( !Test.AnotherWay._thing_eq( expected_value, got_value, "", value_msg ) ) {
583                     msg.msg=Test.AnotherWay._html_eq_path_msg( path )+": attribute "+a+" values differ: "+value_msg.msg;
584                     return false;
585                 }
586             }
587         }
588         // compare child nodes
589         Test.AnotherWay._html_eq_remove_blank_nodes( expected );
590         Test.AnotherWay._html_eq_remove_blank_nodes( got );
591         var expected_length=expected.childNodes.length;
592         var got_length=got.childNodes.length;
593         if( expected_length<got_length ) {
594             msg.msg=Test.AnotherWay._html_eq_path_msg( path )+": got "+(got_length-expected_length)+" extra child nodes";
595             return false;
596         }else if( expected_length>got_length ) {
597             msg.msg=Test.AnotherWay._html_eq_path_msg( path )+": expected "+(expected_length-got_length)+" more child nodes";
598             return false;
599         }else {
600             for( i=0; i<expected_length; ++i ) {
601                 var expected_node=expected.childNodes[i];
602                 path.push( { node: expected_node.nodeName, id: expected_node.id, index: i } );
603                 var eq=Test.AnotherWay._html_eq_node( expected_node, got.childNodes[i], path, msg, expected_loc_base, got_loc_base );
604                 path.pop();
605                 if( !eq ) {
606                     return false;
607                 }
608             }
609         }
610     }
611     return true;
612 }
613 Test.AnotherWay._html_eq_get_loc_base=function( node )
614 {
615     var loc_base=document.location;
616     if( node.ownerDocument!=null ) {
617         loc_base=node.ownerDocument.location;
618     }
619     if( loc_base!=null ) {
620         loc_base=loc_base.href;
621         var slash_pos=loc_base.lastIndexOf( "/" );
622         if( slash_pos!=-1 ) {
623             loc_base=loc_base.substring( 0, slash_pos+1 );
624         }
625     }
626     return loc_base;
627 }
628 Test.AnotherWay._test_object_t.prototype.html_eq=function( got, expected, name )
629 {
630     var msg={};
631     var expected_node=Test.AnotherWay._html_eq_string_to_node( expected, "expected", msg );
632     if( msg.msg!=null ) {
633         this.fail( name+" html_eq: "+msg.msg );
634     }else {
635         var got_node=Test.AnotherWay._html_eq_string_to_node( got, "got", msg );
636         if( msg.msg!=null ) {
637             this.fail( name+" html_eq: "+msg.msg );
638         }else {
639             var expected_loc_base=Test.AnotherWay._html_eq_get_loc_base( expected );
640             var got_loc_base=Test.AnotherWay._html_eq_get_loc_base( got );
641             if( Test.AnotherWay._html_eq_node( expected_node, got_node, [], msg, expected_loc_base, got_loc_base ) ) {
642                 this.ok( 1, name );
643             }else {
644                 var msg=name+" html_eq "+msg.msg;
645                 var expected_str=Test.AnotherWay._html_eq_node_to_string( expected_node );
646                 var got_str=Test.AnotherWay._html_eq_node_to_string( got_node );
647                 msg+=".\n got html: "+got_str;
648                 msg+=".\n expected html: "+expected_str;
649                 this.fail( msg );
650             }
651         }
652     }
653 }
654 Test.AnotherWay._debug_pane_print=function( msg )
655 {
656     var d=new Date();
657     var p=document.createElement( "p" );
658     p.appendChild( document.createTextNode( d.toLocaleTimeString()+" "+msg ) );
659     var debug_pane=document.getElementById( "debug" );
660     debug_pane.appendChild( p );
661     var debug_tab=document.getElementById( "debug_tab" );
662     var results_tab=document.getElementById( "results_tab" );
663     debug_tab.style.visibility="visible";
664     results_tab.style.visibility="visible";
665 }
666 Test.AnotherWay._test_object_t.prototype.debug_print=function( msg )
667 {
668     Test.AnotherWay._debug_pane_print( this.name+": "+msg );
669 }
670 Test.AnotherWay._test_object_t.prototype.delay_call=function()
671 {
672     var timeout_ms=200;
673     for( var i=0; i<arguments.length; ++i ) {
674         if( typeof( arguments[i] )!="function" ) {
675             timeout_ms=1000*arguments[i];
676         }else {
677             var action={ action_kind: "call", call_delay_milliseconds: timeout_ms, call_fn: arguments[i] };
678             this.delay_total_milliseconds_left+=Test.AnotherWay._action_estimate_milliseconds( action );
679             this.delay_actions.push( action );
680         }
681     }
682 }
683 Test.AnotherWay._test_object_t.prototype.open_window=function( url, fn, timeout_seconds )
684 {
685     if( timeout_seconds==null ) {
686         timeout_seconds=4;
687     }
688     var no_close=document.getElementById( "dont_close_test_windows" );
689     var action={ action_kind: "window", wnd_url: url.toString(), wnd_wnd: null, wnd_fn: fn, wnd_timeout_milliseconds: timeout_seconds*1000, wnd_no_close: no_close.checked };
690     this.delay_total_milliseconds_left+=Test.AnotherWay._action_estimate_milliseconds( action );
691     this.delay_actions.push( action );
692 }
693 Test.AnotherWay._test_object_t.prototype.replay_events=function( wnd, events )
694 {
695     if( Test.AnotherWay._g_no_record_msg!=null ) {
696         this.fail( "replay_events: "+Test.AnotherWay._g_no_record_msg );
697     }else {
698         var action={ action_kind: "replay", replay_wnd: wnd, replay_events: events.events, replay_event_i: null, replay_checkpoints: events.checkpoints };
699         this.delay_total_milliseconds_left+=Test.AnotherWay._action_estimate_milliseconds( action );
700         this.delay_actions.push( action );
701     }
702 }
703 Test.AnotherWay._action_estimate_milliseconds=function( action )
704 {
705     var ms=0;
706     if( action.action_kind=="call" ) {
707         ms=action.call_delay_milliseconds;
708     }else if( action.action_kind=="window" ) {
709         ms=0;
710     }else if( action.action_kind=="replay" ) {
711         ms=0;
712         for( var i=0; i<action.replay_events.length; ++i ) {
713             ms+=action.replay_events[i]["time"]-0;
714         }
715     }
716     return ms;
717 }
718
719 Test.AnotherWay._g_timeout_granularity=200;
720 Test.AnotherWay._g_tests_queue=[]; // vector of { url: string, test_objects : array of test_object_t, test_object_i: int, wait_msg: <p> object, loading_timeout_milliseconds: int, timeout_id: id }
721
722 // load one html page, schedule further processing
723 Test.AnotherWay._run_test_page=function( id, called_from_outside )
724 {
725     if( id.match( /^test(\d+)/ ) ) {
726         id=RegExp.$1;
727         Test.AnotherWay._g_tests_queue.push( {
728             url: Test.AnotherWay._g_test_page_urls[id].url,
729             convention: Test.AnotherWay._g_test_page_urls[id].convention,
730             test_objects: []
731             } );
732         if( Test.AnotherWay._g_tests_queue.length==1 ) {
733             if( !called_from_outside ) {
734                 // Crap. Be careful stepping around.
735                 // For Mozilla and Opera, when this file is included into the frameset page that is in another directory (and _g_outside_path_correction!=null)
736                 // but the test pages are started from within it (by "run" buttons), then:
737                 // depending on whether the page is the first one loaded into the test frame or not,
738                 // the base url for relative test pages differs.
739                 // Crap, like I said.
740                 Test.AnotherWay._g_tests_queue[0].suppress_outside_path_correction=true;
741             }
742             Test.AnotherWay._start_loading_page();
743         }
744     }
745 }
746 Test.AnotherWay._load_next_page=function()
747 {
748     Test.AnotherWay._g_tests_queue.splice( 0, 1 );
749     if( Test.AnotherWay._g_tests_queue.length>0 ) {
750         Test.AnotherWay._start_loading_page();
751     }else {
752         if( !Test.AnotherWay._g_test_frame_no_clear ) {
753             Test.AnotherWay._g_test_iframe.location.replace( "about:blank" );
754         }
755     }
756 }
757 Test.AnotherWay._g_opera_path_correction=null; // ugly wart to support opera
758 Test.AnotherWay._g_outside_path_correction=null; // ugly wart to accomodate Opera and Mozilla, where relative url relates to the directory where the page that calls this function is located
759 Test.AnotherWay._set_iframe_location=function( iframe, loc, outside_path_correction )
760 {
761     // allow to load only locations with the same origin
762     var proto_end=loc.indexOf( "://" );
763     if( proto_end!=-1 ) { // otherwise, it's safe to assume (for Opera, Mozilla and IE ) that loc will be treated as relative
764         var main_loc=window.location.href;
765         var host_end=loc.substring( proto_end+3 ).indexOf( "/" );
766         var ok=false;
767         if( host_end!=-1 ) {
768             var loc_origin=loc.substring( 0, proto_end+3+host_end+1 );
769             if( main_loc.length>=loc_origin.length && main_loc.substring( 0, loc_origin.length )==loc_origin ) {
770                 ok=true;
771             }
772         }
773         if( !ok ) {
774             return { msg: "test pages may have only urls with the same origin as "+main_loc };
775         }
776     }
777     // opera cannot handle urls relative to file:// without assistance
778     if( window.opera!=null && window.location.protocol=="file:" && loc.indexOf( ":" )==-1 ) {
779         var base=window.location.href;
780         var q_pos=base.indexOf( "?" );
781         if( q_pos!=-1 ) {
782             base=base.substring( 0, q_pos );
783         }
784         var slash_pos=base.lastIndexOf( "/" );
785         if( slash_pos!=-1 ) {
786             base=base.substring( 0, slash_pos+1 );
787             Test.AnotherWay._g_opera_path_correction=base;
788             loc=base+loc;
789         }
790     }
791     // if this function is called from another page, and if that page is in another directory, correction is needed
792     if( outside_path_correction!=null ) {
793         var pos=loc.indexOf( outside_path_correction );
794         if( pos==0 ) {
795             loc=loc.substring( outside_path_correction.length+1 );
796         }
797     }
798     if( iframe.location!=null ) {
799         iframe.location.replace( loc );