OpenLayers OpenLayers

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

Revision 8277, 89.3 kB (checked in by crschmidt, 4 weeks ago)

Update auto-tests from run-tests.

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