OpenLayers OpenLayers

root/branches/openlayers/2.5/tests/run-tests.html

Revision 4224, 87.6 kB (checked in by crschmidt, 1 year ago)

check boxes by default.

  • 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         return "";
349     }
350 }
351 Test.AnotherWay._is_array=function( x )
352 {
353     return Test.AnotherWay._constructor_name( x )=="Array";
354 }
355 Test.AnotherWay._is_value_type=function( x )
356 {
357     cn=Test.AnotherWay._constructor_name( x );
358     return cn=="Number" || cn=="String" || cn=="Boolean" || cn=="Date";
359 }
360 Test.AnotherWay._thing_eq=function( expected, got, path, msg )
361 {
362     if( expected==null && got==null ) {
363         return true;
364     }else if( (expected==null && got!=null) || (expected!=null && got==null) ) {
365         msg.msg=Test.AnotherWay._eq_fail_msg( path, "values", expected, got );
366         return false;
367     }else {
368         var expected_cn=Test.AnotherWay._constructor_name( expected );
369         var got_cn=Test.AnotherWay._constructor_name( got );
370         if( expected_cn!=got_cn ) {
371             msg.msg=Test.AnotherWay._eq_fail_msg( path, "types", expected_cn, got_cn );
372             return false;
373         }else {
374             if( Test.AnotherWay._is_array( expected ) ) {
375                 return Test.AnotherWay._array_eq( expected, got, path, msg );
376             }else if( Test.AnotherWay._is_value_type( expected ) ) {
377                 if( expected!=got ) {
378                     msg.msg=Test.AnotherWay._eq_fail_msg( path, "values", expected, got );
379                     return false;
380                 }else {
381                     return true;
382                 }
383             }else { // just a plain object
384                 return Test.AnotherWay._object_eq( expected, got, path, msg );
385             }
386         }
387     }
388 }
389 Test.AnotherWay._test_object_t.prototype.eq=function( got, expected, name )
390 {
391     var msg={};
392     if( Test.AnotherWay._thing_eq( expected, got, "", msg ) ) {
393         this.ok( 1, name );
394     }else {
395         this.fail( name+". "+msg.msg );
396     }
397 }
398 Test.AnotherWay._test_object_t.prototype.like=function( got, expected, name )
399 {
400     if( got.match( expected )!=null ) {
401         this.ok( 1, name );
402     }else {
403         this.fail( name+": got "+got+", but expected it to match: "+expected );
404     }
405 }
406 Test.AnotherWay._g_html_eq_span=null;
407 Test.AnotherWay._html_eq_string_to_node=function( string_or_node, what, msg )
408 {
409     if( string_or_node.nodeType!=null ) {
410         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
411     }
412     if( Test.AnotherWay._g_html_eq_span==null ) {
413         Test.AnotherWay._g_html_eq_span=document.createElement( "span" );
414     }
415     Test.AnotherWay._g_html_eq_span.innerHTML=string_or_node;
416     if( Test.AnotherWay._g_html_eq_span.childNodes.length!=1 ) {
417         msg.msg="bad "+what+" html string given (should contain exactly one outermost element): "+string_or_node;
418     }
419     return Test.AnotherWay._g_html_eq_span.childNodes[0].cloneNode( true );
420 }
421 Test.AnotherWay._html_eq_node_to_string=function( node ) {
422     if( Test.AnotherWay._g_html_eq_span==null ) {
423         Test.AnotherWay._g_html_eq_span=document.createElement( "span" );
424     }
425     Test.AnotherWay._g_html_eq_span.innerHTML="";
426     if( node.outerHTML!=null ) {
427         Test.AnotherWay._g_html_eq_span.innerHTML=node.outerHTML;
428     }else {
429         Test.AnotherWay._g_html_eq_span.appendChild( node.cloneNode( true ) );
430     }
431     return Test.AnotherWay._g_html_eq_span.innerHTML;
432 }
433 Test.AnotherWay._html_eq_path_msg=function( path )
434 {
435     var msg="";
436     for( var i=0; i<path.length; ++i ) {
437         msg+=" [node "+path[i].node;
438         if( path[i].id!=null && path[i].id!="" ) {
439             msg+=" id "+path[i].id;
440         }else if( path[i].index!=null ) {
441             msg+=" at index "+path[i].index;
442         }
443         msg+="] "
444     }
445     return msg;
446 }
447 Test.AnotherWay._html_eq_fail_msg=function( path, what, expected, got )
448 {
449     return Test.AnotherWay._html_eq_path_msg( path )+": "+what+" differ: got "+got+", but expected "+expected;
450 }
451 Test.AnotherWay._html_eq_remove_blank=function( text )
452 {
453     if( text==null ) {
454         return "";
455     }else if( text.match( "^(\\s*)(.*\\S)(\\s*)$" ) ) {
456         return RegExp.$2;
457     }else if( text.match( "\s*" ) ) {
458         return "";
459     }
460     return text;
461 }
462 Test.AnotherWay._html_eq_remove_blank_nodes=function( node )
463 {
464     var to_remove=[];
465     for( var child=node.firstChild; child!=null; child=child.nextSibling ) {
466         if( child.nodeType==3 ) {
467             var value=Test.AnotherWay._html_eq_remove_blank( child.nodeValue );
468             if( value=="" ) {
469                 to_remove.push( child );
470             }else {
471                 child.nodeValue=value;
472             }
473         }
474     }
475     for( var i=0; i<to_remove.length; ++i ) {
476         node.removeChild( to_remove[i] );
477     }
478 }
479 Test.AnotherWay._html_node_type_text=function( node_type )
480 {
481     if( node_type==1 ) {
482         return "1 (html element)";
483     }else if( node_type==3 ) {
484         return "3 (text)";
485     }else {
486         return node_type;
487     }
488 }
489 Test.AnotherWay._html_eq_node=function( expected, got, path, msg, expected_loc_base, got_loc_base )
490 {
491     if( expected.nodeType!=got.nodeType ) {
492         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 ) );
493         return false;
494     }else if( expected.nodeType==3 ) {
495         if( expected.nodeValue!=got.nodeValue ) {
496             msg.msg=Test.AnotherWay._html_eq_fail_msg( path, "text", expected.nodeValue, got.nodeValue );
497             return false;
498         }
499     }else if( expected.nodeType==1 ) {
500         if( expected.nodeName!=got.nodeName ) {
501             msg.msg=Test.AnotherWay._html_eq_fail_msg( path, "node names", expected.nodeName, got.nodeName );
502             return false;
503         }
504         // compare attributes
505         var expected_attrs={};
506         var got_attrs={};
507         var i;
508         var a;
509         for( i=0; i<expected.attributes.length; ++i ) {
510             a=expected.attributes[i];
511             if( a.specified ) {
512                 expected_attrs[a.name]=1;
513             }
514         }
515         for( i=0; i<got.attributes.length; ++i ) {
516             a=got.attributes[i];
517             if( a.specified ) {
518                 got_attrs[a.name]=1;
519             }
520         }
521         for( a in expected_attrs ) {
522             if( ! (a in got_attrs) ) {
523                 msg.msg=Test.AnotherWay._html_eq_path_msg( path )+": attribute sets differ: expected attribute "+a+" is missing";
524                 return false;
525             }
526         }
527         for( a in got_attrs ) {
528             if( ! (a in expected_attrs) ) {
529                 msg.msg=Test.AnotherWay._html_eq_path_msg( path )+": attribute sets differ: got extra attribute "+a;
530                 return false;
531             }
532         }
533         for( a in expected_attrs ) {
534             var expected_value=expected.getAttribute( a );
535             var got_value=got.getAttribute( a );
536             if( typeof( expected_value )=="string" && typeof( got_value )=="string" ) {
537                 expected_value=Test.AnotherWay._html_eq_remove_blank( expected_value );
538                 got_value=Test.AnotherWay._html_eq_remove_blank( got_value );
539                 var ok=expected_value==got_value;
540                 if( !ok && (a=="href" || a=="HREF" )  ) { // try relative hrefs
541                     var expected_relative_value=expected_value;
542                     if( expected_loc_base!=null && expected_value.substring( 0, expected_loc_base.length )==expected_loc_base ) {
543                         expected_relative_value=expected_value.substring( expected_loc_base.length );
544                     }
545                     var got_relative_value=got_value;
546                     if( got_loc_base!=null && got_value.substring( 0, got_loc_base.length )==got_loc_base ) {
547                         got_relative_value=got_value.substring( got_loc_base.length );
548                     }
549                     ok=expected_relative_value==got_relative_value;
550                 }
551                 if( !ok ) {
552                     msg.msg=Test.AnotherWay._html_eq_fail_msg( path, "attribute "+a+" values", expected_value, got_value );
553                     return false;
554                 }
555             }else if( typeof( expected_value )=="function" && typeof( got_value )=="function" ) {
556                 expected_value=expected_value.toString();
557                 got_value=got_value.toString();
558                 if( expected_value!=got_value ) {
559                     msg.msg=Test.AnotherWay._html_eq_fail_msg( path, "attribute "+a+" values", expected_value, got_value );
560                     return false;
561                 }
562             }else {
563                 var value_msg={};
564                 if( !Test.AnotherWay._thing_eq( expected_value, got_value, "", value_msg ) ) {
565                     msg.msg=Test.AnotherWay._html_eq_path_msg( path )+": attribute "+a+" values differ: "+value_msg.msg;
566                     return false;
567                 }
568             }
569         }
570         // compare child nodes
571         Test.AnotherWay._html_eq_remove_blank_nodes( expected );
572         Test.AnotherWay._html_eq_remove_blank_nodes( got );
573         var expected_length=expected.childNodes.length;
574         var got_length=got.childNodes.length;
575         if( expected_length<got_length ) {
576             msg.msg=Test.AnotherWay._html_eq_path_msg( path )+": got "+(got_length-expected_length)+" extra child nodes";
577             return false;
578         }else if( expected_length>got_length ) {
579             msg.msg=Test.AnotherWay._html_eq_path_msg( path )+": expected "+(expected_length-got_length)+" more child nodes";
580             return false;
581         }else {
582             for( i=0; i<expected_length; ++i ) {
583                 var expected_node=expected.childNodes[i];
584                 path.push( { node: expected_node.nodeName, id: expected_node.id, index: i } );
585                 var eq=Test.AnotherWay._html_eq_node( expected_node, got.childNodes[i], path, msg, expected_loc_base, got_loc_base );
586                 path.pop();
587                 if( !eq ) {
588                     return false;
589                 }
590             }
591         }
592     }
593     return true;
594 }
595 Test.AnotherWay._html_eq_get_loc_base=function( node )
596 {
597     var loc_base=document.location;
598     if( node.ownerDocument!=null ) {
599         loc_base=node.ownerDocument.location;
600     }
601     if( loc_base!=null ) {
602         loc_base=loc_base.href;
603         var slash_pos=loc_base.lastIndexOf( "/" );
604         if( slash_pos!=-1 ) {
605             loc_base=loc_base.substring( 0, slash_pos+1 );
606         }
607     }
608     return loc_base;
609 }
610 Test.AnotherWay._test_object_t.prototype.html_eq=function( got, expected, name )
611 {
612     var msg={};
613     var expected_node=Test.AnotherWay._html_eq_string_to_node( expected, "expected", msg );
614     if( msg.msg!=null ) {
615         this.fail( name+" html_eq: "+msg.msg );
616     }else {
617         var got_node=Test.AnotherWay._html_eq_string_to_node( got, "got", msg );
618         if( msg.msg!=null ) {
619             this.fail( name+" html_eq: "+msg.msg );
620         }else {
621             var expected_loc_base=Test.AnotherWay._html_eq_get_loc_base( expected );
622             var got_loc_base=Test.AnotherWay._html_eq_get_loc_base( got );
623             if( Test.AnotherWay._html_eq_node( expected_node, got_node, [], msg, expected_loc_base, got_loc_base ) ) {
624                 this.ok( 1, name );
625             }else {
626                 var msg=name+" html_eq "+msg.msg;
627                 var expected_str=Test.AnotherWay._html_eq_node_to_string( expected_node );
628                 var got_str=Test.AnotherWay._html_eq_node_to_string( got_node );
629                 msg+=".\n got html: "+got_str;
630                 msg+=".\n expected html: "+expected_str;
631                 this.fail( msg );
632             }
633         }
634     }
635 }
636 Test.AnotherWay._debug_pane_print=function( msg )
637 {
638     var d=new Date();
639     var p=document.createElement( "p" );
640     p.appendChild( document.createTextNode( d.toLocaleTimeString()+" "+msg ) );
641     var debug_pane=document.getElementById( "debug" );
642     debug_pane.appendChild( p );
643     var debug_tab=document.getElementById( "debug_tab" );
644     var results_tab=document.getElementById( "results_tab" );
645     debug_tab.style.visibility="visible";
646     results_tab.style.visibility="visible";
647 }
648 Test.AnotherWay._test_object_t.prototype.debug_print=function( msg )
649 {
650     Test.AnotherWay._debug_pane_print( this.name+": "+msg );
651 }
652 Test.AnotherWay._test_object_t.prototype.delay_call=function()
653 {
654     var timeout_ms=200;
655     for( var i=0; i<arguments.length; ++i ) {
656         if( typeof( arguments[i] )!="function" ) {
657             timeout_ms=1000*arguments[i];
658         }else {
659             var action={ action_kind: "call", call_delay_milliseconds: timeout_ms, call_fn: arguments[i] };
660             this.delay_total_milliseconds_left+=Test.AnotherWay._action_estimate_milliseconds( action );
661             this.delay_actions.push( action );
662         }
663     }
664 }
665 Test.AnotherWay._test_object_t.prototype.open_window=function( url, fn, timeout_seconds )
666 {
667     if( timeout_seconds==null ) {
668         timeout_seconds=4;
669     }
670     var no_close=document.getElementById( "dont_close_test_windows" );
671     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 };
672     this.delay_total_milliseconds_left+=Test.AnotherWay._action_estimate_milliseconds( action );
673     this.delay_actions.push( action );
674 }
675 Test.AnotherWay._test_object_t.prototype.replay_events=function( wnd, events )
676 {
677     if( Test.AnotherWay._g_no_record_msg!=null ) {
678         this.fail( "replay_events: "+Test.AnotherWay._g_no_record_msg );
679     }else {
680         var action={ action_kind: "replay", replay_wnd: wnd, replay_events: events.events, replay_event_i: null, replay_checkpoints: events.checkpoints };
681         this.delay_total_milliseconds_left+=Test.AnotherWay._action_estimate_milliseconds( action );
682         this.delay_actions.push( action );
683     }
684 }
685 Test.AnotherWay._action_estimate_milliseconds=function( action )
686 {
687     var ms=0;
688     if( action.action_kind=="call" ) {
689         ms=action.call_delay_milliseconds;
690     }else if( action.action_kind=="window" ) {
691         ms=0;
692     }else if( action.action_kind=="replay" ) {
693         ms=0;
694         for( var i=0; i<action.replay_events.length; ++i ) {
695             ms+=action.replay_events[i]["time"]-0;
696         }
697     }
698     return ms;
699 }
700
701 Test.AnotherWay._g_timeout_granularity=200;
702 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 }
703
704 // load one html page, schedule further processing
705 Test.AnotherWay._run_test_page=function( id, called_from_outside )
706 {
707     if( id.match( /^test(\d+)/ ) ) {
708         id=RegExp.$1;
709         Test.AnotherWay._g_tests_queue.push( {
710             url: Test.AnotherWay._g_test_page_urls[id].url,
711             convention: Test.AnotherWay._g_test_page_urls[id].convention,
712             test_objects: []
713             } );
714         if( Test.AnotherWay._g_tests_queue.length==1 ) {
715             if( !called_from_outside ) {
716                 // Crap. Be careful stepping around.
717                 // For Mozilla and Opera, when this file is included into the frameset page that is in another directory (and _g_outside_path_correction!=null)
718                 // but the test pages are started from within it (by "run" buttons), then:
719                 // depending on whether the page is the first one loaded into the test frame or not,
720                 // the base url for relative test pages differs.
721                 // Crap, like I said.
722                 Test.AnotherWay._g_tests_queue[0].suppress_outside_path_correction=true;
723             }
724             Test.AnotherWay._start_loading_page();
725         }
726     }
727 }
728 Test.AnotherWay._load_next_page=function()
729 {
730     Test.AnotherWay._g_tests_queue.splice( 0, 1 );
731     if( Test.AnotherWay._g_tests_queue.length>0 ) {
732         Test.AnotherWay._start_loading_page();
733     }else {
734         if( !Test.AnotherWay._g_test_frame_no_clear ) {
735             Test.AnotherWay._g_test_iframe.location.replace( "about:blank" );
736         }
737     }
738 }
739 Test.AnotherWay._g_opera_path_correction=null; // ugly wart to support opera
740 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
741 Test.AnotherWay._set_iframe_location=function( iframe, loc, outside_path_correction )
742 {
743     // allow to load only locations with the same origin
744     var proto_end=loc.indexOf( "://" );
745     if( proto_end!=-1 ) { // otherwise, it's safe to assume (for Opera, Mozilla and IE ) that loc will be treated as relative
746         var main_loc=window.location.href;
747         var host_end=loc.substring( proto_end+3 ).indexOf( "/" );
748         var ok=false;
749         if( host_end!=-1 ) {
750             var loc_origin=loc.substring( 0, proto_end+3+host_end+1 );
751             if( main_loc.length>=loc_origin.length && main_loc.substring( 0, loc_origin.length )==loc_origin ) {
752                 ok=true;
753             }
754         }
755         if( !ok ) {
756             return { msg: "test pages may have only urls with the same origin as "+main_loc };
757         }
758     }
759     // opera cannot handle urls relative to file:// without assistance
760     if( window.opera!=null && window.location.protocol=="file:" && loc.indexOf( ":" )==-1 ) {
761         var base=window.location.href;
762         var q_pos=base.indexOf( "?" );
763         if( q_pos!=-1 ) {
764             base=base.substring( 0, q_pos );
765         }
766         var slash_pos=base.lastIndexOf( "/" );
767         if( slash_pos!=-1 ) {
768             base=base.substring( 0, slash_pos+1 );
769             Test.AnotherWay._g_opera_path_correction=base;
770             loc=base+loc;
771         }
772     }
773     // if this function is called from another page, and if that page is in another directory, correction is needed
774     if( outside_path_correction!=null ) {
775         var pos=loc.indexOf( outside_path_correction );
776         if( pos==0 ) {
777             loc=loc.substring( outside_path_correction.length+1 );
778         }
779     }
780     if( iframe.location!=null ) {
781         iframe.location.replace( loc );
782     }else {
783         iframe.src=loc;
784     }
785     return {};
786 }
787 Test.AnotherWay._start_loading_page=function()
788 {
789     var test_page=Test.AnotherWay._g_tests_queue[0];
790     test_page.loading_timeout_milliseconds=6000;
791     test_page.timeout_id=setTimeout( Test.AnotherWay._loading_timeout, Test.AnotherWay._g_timeout_granularity );
792     test_page.wait_msg=Test.AnotherWay._print_counter_result( test_page.url, "loading...", test_page.loading_timeout_milliseconds, "loading" );
793     if( test_page.convention=="jsan" ) {
794         // the tests in that page will run when it's loading, so the test object must be ready
795         Test.AnotherWay._g_test_object_for_jsan=new Test.AnotherWay._test_object_t( test_page.url );
796     }
797     var outside_path_correction=null;
798     if( Test.AnotherWay._g_outside_path_correction!=null && !test_page.suppress_outside_path_correction ) {
799         outside_path_correction=Test