1/* The contents of this file are subject to the Netscape Public
2 * License Version 1.1 (the "License"); you may not use this file
3 * except in compliance with the License. You may obtain a copy of
4 * the License at http://www.mozilla.org/NPL/
5 *
6 * Software distributed under the License is distributed on an "AS
7 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
8 * implied. See the License for the specific language governing
9 * rights and limitations under the License.
10 *
11 * The Original Code is Mozilla Communicator client code, released March
12 * 31, 1998.
13 *
14 * The Initial Developer of the Original Code is Netscape Communications
15 * Corporation. Portions created by Netscape are
16 * Copyright (C) 1998 Netscape Communications Corporation. All
17 * Rights Reserved.
18 *
19 * Contributor(s):
20 *
21 */
22/*
23 * JavaScript shared functions file for running the tests in either
24 * stand-alone JavaScript engine.  To run a test, first load this file,
25 * then load the test script.
26 */
27
28var	completed =	false;
29var	testcases;
30var tc = 0;
31
32SECTION	= "";
33VERSION	= "";
34BUGNUMBER =	"";
35
36/*
37 * constant strings
38 */
39var	GLOBAL = "[object global]";
40var PASSED = " PASSED!"
41var FAILED = " FAILED! expected: ";
42
43var	DEBUG =	false;
44
45
46
47/* wrapper for test cas constructor that doesn't require the SECTION
48 * argument.
49 */
50
51function AddTestCase( description, expect, actual ) {
52    testcases[tc++] = new TestCase( SECTION, description, expect, actual );
53}
54
55/*
56 * TestCase constructor
57 *
58 */
59
60function TestCase( n, d, e,	a )	{
61	this.name		 = n;
62	this.description = d;
63	this.expect		 = e;
64	this.actual		 = a;
65	this.passed		 = true;
66	this.reason		 = "";
67	this.bugnumber	  =	BUGNUMBER;
68
69	this.passed	= getTestCaseResult( this.expect, this.actual );
70	if ( DEBUG ) {
71		writeLineToLog(	"added " + this.description	);
72	}
73}
74
75/*
76 * Set up test environment.
77 *
78 */
79function startTest() {
80    if ( version ) {
81    	//	JavaScript 1.3 is supposed to be compliant ecma	version	1.0
82	    if ( VERSION ==	"ECMA_1" ) {
83		    version	( "130"	);
84    	}
85	    if ( VERSION ==	"JS_1.3" ) {
86		    version	( "130"	);
87    	}
88	    if ( VERSION ==	"JS_1.2" ) {
89		    version	( "120"	);
90    	}
91	    if ( VERSION  == "JS_1.1" )	{
92		    version	( "110"	);
93    	}
94	    // for ecma	version	2.0, we	will leave the javascript version to
95    	// the default ( for now ).
96    }
97
98    // print out bugnumber
99
100    if ( BUGNUMBER ) {
101            writeLineToLog ("BUGNUMBER: " + BUGNUMBER );
102    }
103
104    testcases = new Array();
105    tc = 0;
106}
107
108
109function test() {
110    for ( tc=0; tc < testcases.length; tc++ ) {
111        testcases[tc].passed = writeTestCaseResult(
112                            testcases[tc].expect,
113                            testcases[tc].actual,
114                            testcases[tc].description +" = "+ testcases[tc].actual );
115        testcases[tc].reason += ( testcases[tc].passed ) ? "" : "wrong value ";
116    }
117    stopTest();
118    return ( testcases );
119}
120
121/*
122 * Compare expected result to the actual result and figure out whether
123 * the test case passed.
124 */
125function getTestCaseResult(	expect,	actual ) {
126	//	because	( NaN == NaN ) always returns false, need to do
127	//	a special compare to see if	we got the right result.
128		if ( actual	!= actual )	{
129			if ( typeof	actual == "object" ) {
130				actual = "NaN object";
131			} else {
132				actual = "NaN number";
133			}
134		}
135		if ( expect	!= expect )	{
136			if ( typeof	expect == "object" ) {
137				expect = "NaN object";
138			} else {
139				expect = "NaN number";
140			}
141		}
142
143		var	passed = ( expect == actual	) ?	true : false;
144
145	//	if both	objects	are	numbers
146	// need	to replace w/ IEEE standard	for	rounding
147		if (	!passed
148				&& typeof(actual) == "number"
149				&& typeof(expect) == "number"
150			) {
151				if ( Math.abs(actual-expect) < 0.0000001 ) {
152					passed = true;
153				}
154		}
155
156	//	verify type	is the same
157		if ( typeof(expect)	!= typeof(actual) )	{
158			passed = false;
159		}
160
161		return passed;
162}
163
164/*
165 * Begin printing functions.  These functions use the shell's
166 * print function.  When running tests in the browser, these
167 * functions, override these functions with functions that use
168 * document.write.
169 */
170
171function writeTestCaseResult( expect, actual, string ) {
172		var	passed = getTestCaseResult(	expect,	actual );
173		writeFormattedResult( expect, actual, string, passed );
174		return passed;
175}
176function writeFormattedResult( expect, actual, string, passed ) {
177        var s = string ;
178        s += ( passed ) ? PASSED : FAILED + expect;
179        writeLineToLog( s);
180        return passed;
181}
182function writeLineToLog( string	) {
183	print( string );
184}
185function writeHeaderToLog( string )	{
186	print( string );
187}
188/* end of print functions */
189
190
191/*
192 * When running in the shell, run the garbage collector after the
193 * test has completed.
194 */
195
196function stopTest()	{
197 	var gc;
198	if ( gc != undefined ) {
199		gc();
200	}
201}
202
203/*
204 * Convenience function for displaying failed test cases.  Useful
205 * when running tests manually.
206 *
207 */
208function getFailedCases() {
209  for (	var	i =	0; i < testcases.length; i++ ) {
210	 if	( !	testcases[i].passed	) {
211		print( testcases[i].description	+" = " +testcases[i].actual	+" expected: "+	testcases[i].expect	);
212	 }
213  }
214}
215 /*
216  *	Date functions used	by tests in	Date suite
217  *
218  */
219var	msPerDay =			86400000;
220var	HoursPerDay	=		24;
221var	MinutesPerHour =	60;
222var	SecondsPerMinute =	60;
223var	msPerSecond	=		1000;
224var	msPerMinute	=		60000;		//	msPerSecond	* SecondsPerMinute
225var	msPerHour =			3600000;	//	msPerMinute	* MinutesPerHour
226var             TZ_DIFF	= getTimeZoneDiff();  // offset of tester's timezone from UTC
227var             TZ_PST = -8;  // offset of Pacific Standard Time from UTC
228var             PST_DIFF = TZ_DIFF - TZ_PST;  // offset of tester's timezone from PST
229var	TIME_1970	 = 0;
230var	TIME_2000	 = 946684800000;
231var	TIME_1900	 = -2208988800000;
232var     TIME_YEAR_0      = -62167219200000;
233
234
235/*
236 * Originally, the test suite used a hard-coded value TZ_DIFF = -8.
237 * But that was only valid for testers in the Pacific Standard Time Zone!
238 * We calculate the proper number dynamically for any tester. We just
239 * have to be careful not to use a date subject to Daylight Savings Time...
240*/
241function getTimeZoneDiff()
242{
243  return -((new Date(2000, 1, 1)).getTimezoneOffset())/60;
244}
245
246
247/*
248 * Date test "ResultArrays" are hard-coded for Pacific Standard Time.
249 * We must adjust them for the tester's own timezone -
250 */
251function adjustResultArray(ResultArray, msMode)
252{
253  // If the tester's system clock is in PST, no need to continue -
254  if (!PST_DIFF) {return;}
255
256  /* The date testcases instantiate Date objects in two different ways:
257   *
258   *        millisecond mode: e.g.   dt = new Date(10000000);
259   *        year-month-day mode:  dt = new Date(2000, 5, 1, ...);
260   *
261   * In the first case, the date is measured from Time 0 in Greenwich (i.e. UTC).
262   * In the second case, it is measured with reference to the tester's local timezone.
263   *
264   * In the first case we must correct those values expected for local measurements,
265   * like dt.getHours() etc. No correction is necessary for dt.getUTCHours() etc.
266   *
267   * In the second case, it is exactly the other way around -
268  */
269  if (msMode)
270  {
271    // The hard-coded UTC milliseconds from Time 0 derives from a UTC date.
272    // Shift to the right by the offset between UTC and the tester.
273    var t = ResultArray[TIME]  +  TZ_DIFF*msPerHour;
274
275    // Use our date arithmetic functions to determine the local hour, day, etc.
276    ResultArray[HOURS] = HourFromTime(t);
277    ResultArray[DAY] = WeekDay(t);
278    ResultArray[DATE] = DateFromTime(t);
279    ResultArray[MONTH] = MonthFromTime(t);
280    ResultArray[YEAR] = YearFromTime(t);
281  }
282  else
283  {
284    // The hard-coded UTC milliseconds from Time 0 derives from a PST date.
285    // Shift to the left by the offset between PST and the tester.
286    var t = ResultArray[TIME]  -  PST_DIFF*msPerHour;
287
288    // Use our date arithmetic functions to determine the UTC hour, day, etc.
289    ResultArray[TIME] = t;
290    ResultArray[UTC_HOURS] = HourFromTime(t);
291    ResultArray[UTC_DAY] = WeekDay(t);
292    ResultArray[UTC_DATE] = DateFromTime(t);
293    ResultArray[UTC_MONTH] = MonthFromTime(t);
294    ResultArray[UTC_YEAR] = YearFromTime(t);
295  }
296}
297
298
299function Day( t	) {
300	return ( Math.floor(t/msPerDay ) );
301}
302function DaysInYear( y ) {
303	if ( y % 4 != 0	) {
304		return 365;
305	}
306	if ( (y	% 4	== 0) && (y	% 100 != 0)	) {
307		return 366;
308	}
309	if ( (y	% 100 == 0)	&&	(y % 400 !=	0) ) {
310		return 365;
311	}
312	if ( (y	% 400 == 0)	){
313		return 366;
314	} else {
315		return "ERROR: DaysInYear("	+ y	+ ") case not covered";
316	}
317}
318function TimeInYear( y ) {
319	return ( DaysInYear(y) * msPerDay );
320}
321function DayNumber(	t )	{
322	return ( Math.floor( t / msPerDay )	);
323}
324function TimeWithinDay(	t )	{
325	if ( t < 0 ) {
326		return ( (t	% msPerDay)	+ msPerDay );
327	} else {
328		return ( t % msPerDay );
329	}
330}
331function YearNumber( t ) {
332}
333function TimeFromYear( y ) {
334	return ( msPerDay *	DayFromYear(y) );
335}
336function DayFromYear( y	) {
337	return (	365*(y-1970) +
338				Math.floor((y-1969)/4) -
339				Math.floor((y-1901)/100) +
340				Math.floor((y-1601)/400) );
341}
342function InLeapYear( t ) {
343	if ( DaysInYear(YearFromTime(t)) ==	365	) {
344		return 0;
345	}
346	if ( DaysInYear(YearFromTime(t)) ==	366	) {
347		return 1;
348	} else {
349		return "ERROR:  InLeapYear("+ t + ") case not covered";
350	}
351}
352function YearFromTime( t ) {
353	t =	Number(	t );
354	var	sign = ( t < 0 ) ? -1 :	1;
355	var	year = ( sign <	0 )	? 1969 : 1970;
356	for	(	var	timeToTimeZero = t;	;  ) {
357	//	subtract the current year's	time from the time that's left.
358		timeToTimeZero -= sign * TimeInYear(year)
359
360	//	if there's less	than the current year's	worth of time left,	then break.
361		if ( sign <	0 )	{
362			if ( sign *	timeToTimeZero <= 0	) {
363				break;
364			} else {
365				year +=	sign;
366			}
367		} else {
368			if ( sign *	timeToTimeZero < 0 ) {
369				break;
370			} else {
371				year +=	sign;
372			}
373		}
374	}
375	return ( year );
376}
377function MonthFromTime(	t )	{
378	//	i know i could use switch but i'd rather not until it's	part of	ECMA
379	var	day	= DayWithinYear( t );
380	var	leap = InLeapYear(t);
381
382	if ( (0	<= day)	&& (day	< 31) )	{
383		return 0;
384	}
385	if ( (31 <=	day) &&	(day < (59+leap)) )	{
386		return 1;
387	}
388	if ( ((59+leap)	<= day)	&& (day	< (90+leap)) ) {
389		return 2;
390	}
391	if ( ((90+leap)	<= day)	&& (day	< (120+leap)) )	{
392		return 3;
393	}
394	if ( ((120+leap) <=	day) &&	(day < (151+leap)) ) {
395		return 4;
396	}
397	if ( ((151+leap) <=	day) &&	(day < (181+leap)) ) {
398		return 5;
399	}
400	if ( ((181+leap) <=	day) &&	(day < (212+leap)) ) {
401		return 6;
402	}
403	if ( ((212+leap) <=	day) &&	(day < (243+leap)) ) {
404		return 7;
405	}
406	if ( ((243+leap) <=	day) &&	(day < (273+leap)) ) {
407		return 8;
408	}
409	if ( ((273+leap) <=	day) &&	(day < (304+leap)) ) {
410		return 9;
411	}
412	if ( ((304+leap) <=	day) &&	(day < (334+leap)) ) {
413		return 10;
414	}
415	if ( ((334+leap) <=	day) &&	(day < (365+leap)) ) {
416		return 11;
417	} else {
418		return "ERROR:	MonthFromTime("+t+") not known";
419	}
420}
421function DayWithinYear(	t )	{
422		return(	Day(t) - DayFromYear(YearFromTime(t)));
423}
424function DateFromTime( t ) {
425	var	day	= DayWithinYear(t);
426	var	month =	MonthFromTime(t);
427
428	if ( month == 0	) {
429		return ( day + 1 );
430	}
431	if ( month == 1	) {
432		return ( day - 30 );
433	}
434	if ( month == 2	) {
435		return ( day - 58 -	InLeapYear(t) );
436	}
437	if ( month == 3	) {
438		return ( day - 89 -	InLeapYear(t));
439	}
440	if ( month == 4	) {
441		return ( day - 119 - InLeapYear(t));
442	}
443	if ( month == 5	) {
444		return ( day - 150-	InLeapYear(t));
445	}
446	if ( month == 6	) {
447		return ( day - 180-	InLeapYear(t));
448	}
449	if ( month == 7	) {
450		return ( day - 211-	InLeapYear(t));
451	}
452	if ( month == 8	) {
453		return ( day - 242-	InLeapYear(t));
454	}
455	if ( month == 9	) {
456		return ( day - 272-	InLeapYear(t));
457	}
458	if ( month == 10 ) {
459		return ( day - 303-	InLeapYear(t));
460	}
461	if ( month == 11 ) {
462		return ( day - 333-	InLeapYear(t));
463	}
464
465	return ("ERROR:	 DateFromTime("+t+") not known"	);
466}
467function WeekDay( t	) {
468	var	weekday	= (Day(t)+4) % 7;
469	return(	weekday	< 0	? 7	+ weekday :	weekday	);
470}
471
472// missing daylight	savins time	adjustment
473
474function HourFromTime( t ) {
475	var	h =	Math.floor(	t /	msPerHour )	% HoursPerDay;
476	return ( (h<0) ? HoursPerDay + h : h  );
477}
478function MinFromTime( t	) {
479	var	min	= Math.floor( t	/ msPerMinute )	% MinutesPerHour;
480	return(	( min <	0 )	? MinutesPerHour + min : min  );
481}
482function SecFromTime( t	) {
483	var	sec	= Math.floor( t	/ msPerSecond )	% SecondsPerMinute;
484	return ( (sec <	0 )	? SecondsPerMinute + sec : sec );
485}
486function msFromTime( t ) {
487	var	ms = t % msPerSecond;
488	return ( (ms < 0 ) ? msPerSecond + ms :	ms );
489}
490function LocalTZA()	{
491	return ( TZ_DIFF * msPerHour );
492}
493function UTC( t	) {
494	return ( t - LocalTZA()	- DaylightSavingTA(t - LocalTZA()) );
495}
496
497function DaylightSavingTA( t ) {
498	t =	t -	LocalTZA();
499
500	var	dst_start = GetSecondSundayInMarch(t) + 2*msPerHour;
501	var	dst_end	  = GetFirstSundayInNovember(t)+ 2*msPerHour;
502
503	if ( t >= dst_start	&& t < dst_end ) {
504		return msPerHour;
505	} else {
506		return 0;
507	}
508
509	// Daylight	Savings	Time starts	on the first Sunday	in April at	2:00AM in
510	// PST.	 Other time	zones will need	to override	this function.
511
512	print( new Date( UTC(dst_start + LocalTZA())) );
513
514	return UTC(dst_start  +	LocalTZA());
515}
516
517function GetFirstSundayInApril( t ) {
518    var year = YearFromTime(t);
519    var leap = InLeapYear(t);
520
521    var april = TimeFromYear(year) + TimeInMonth(0, leap) + TimeInMonth(1,leap) +
522    TimeInMonth(2,leap);
523
524    for ( var first_sunday = april; WeekDay(first_sunday) > 0;
525        first_sunday += msPerDay )
526    {
527        ;
528    }
529
530    return first_sunday;
531}
532function GetLastSundayInOctober( t ) {
533    var year = YearFromTime(t);
534    var leap = InLeapYear(t);
535
536    for ( var oct = TimeFromYear(year), m = 0; m < 9; m++ ) {
537        oct += TimeInMonth(m, leap);
538    }
539    for ( var last_sunday = oct + 30*msPerDay; WeekDay(last_sunday) > 0;
540        last_sunday -= msPerDay )
541    {
542        ;
543    }
544    return last_sunday;
545}
546
547// Added these two functions because DST rules changed for the US.
548function GetSecondSundayInMarch( t ) {
549	var	year = YearFromTime(t);
550	var	leap = InLeapYear(t);
551
552	var	march =	TimeFromYear(year) + TimeInMonth(0, leap) + TimeInMonth(1,leap);
553
554	var sundayCount = 0;
555	var flag = true;
556	for ( var second_sunday = march; flag; second_sunday += msPerDay )
557	{
558		if (WeekDay(second_sunday) == 0) {
559			if(++sundayCount == 2)
560				flag = false;
561		}
562	}
563
564	return second_sunday;
565}
566function GetFirstSundayInNovember( t ) {
567	var year = YearFromTime(t);
568	var leap = InLeapYear(t);
569
570	for ( var nov = TimeFromYear(year), m =	0; m < 10; m++ ) {
571		nov += TimeInMonth(m, leap);
572	}
573	for ( var first_sunday = nov; WeekDay(first_sunday) > 0;
574		first_sunday += msPerDay	)
575	{
576		;
577	}
578	return first_sunday;
579}
580function LocalTime(	t )	{
581	return ( t + LocalTZA()	+ DaylightSavingTA(t) );
582}
583function MakeTime( hour, min, sec, ms )	{
584	if ( isNaN(	hour ) || isNaN( min ) || isNaN( sec ) || isNaN( ms	) )	{
585		return Number.NaN;
586	}
587
588	hour = ToInteger(hour);
589	min	 = ToInteger( min);
590	sec	 = ToInteger( sec);
591	ms	 = ToInteger( ms );
592
593	return(	(hour*msPerHour) + (min*msPerMinute) +
594			(sec*msPerSecond) +	ms );
595}
596function MakeDay( year,	month, date	) {
597	if ( isNaN(year) ||	isNaN(month) ||	isNaN(date)	) {
598		return Number.NaN;
599	}
600	year = ToInteger(year);
601	month =	ToInteger(month);
602	date = ToInteger(date );
603
604	var	sign = ( year <	1970 ) ? -1	: 1;
605	var	t =	   ( year <	1970 ) ? 1 :  0;
606	var	y =	   ( year <	1970 ) ? 1969 :	1970;
607
608	var	result5	= year + Math.floor( month/12 );
609	var	result6	= month	% 12;
610
611	if ( year <	1970 ) {
612	   for ( y = 1969; y >=	year; y	+= sign	) {
613		 t += sign * TimeInYear(y);
614	   }
615	} else {
616		for	( y	= 1970 ; y < year; y +=	sign ) {
617			t += sign *	TimeInYear(y);
618		}
619	}
620
621	var	leap = InLeapYear( t );
622
623	for	( var m	= 0; m < month;	m++	) {
624		t += TimeInMonth( m, leap );
625	}
626
627	if ( YearFromTime(t) !=	result5	) {
628		return Number.NaN;
629	}
630	if ( MonthFromTime(t) != result6 ) {
631		return Number.NaN;
632	}
633	if ( DateFromTime(t) !=	1 )	{
634		return Number.NaN;
635	}
636
637	return ( (Day(t)) +	date - 1 );
638}
639function TimeInMonth( month, leap )	{
640	// september april june	november
641	// jan 0  feb 1	 mar 2	apr	3	may	4  june	5  jul 6
642	// aug 7  sep 8	 oct 9	nov	10	dec	11
643
644	if ( month == 3	|| month ==	5 || month == 8	|| month ==	10 ) {
645		return ( 30*msPerDay );
646	}
647
648	// all the rest
649	if ( month == 0	|| month ==	2 || month == 4	|| month ==	6 ||
650		 month == 7	|| month ==	9 || month == 11 ) {
651		return ( 31*msPerDay );
652	 }
653
654	// save	february
655	return ( (leap == 0) ? 28*msPerDay : 29*msPerDay );
656}
657function MakeDate( day,	time ) {
658	if (	day	== Number.POSITIVE_INFINITY	||
659			day	== Number.NEGATIVE_INFINITY	||
660			day	== Number.NaN )	{
661		return Number.NaN;
662	}
663	if (	time ==	Number.POSITIVE_INFINITY ||
664			time ==	Number.POSITIVE_INFINITY ||
665			day	== Number.NaN) {
666		return Number.NaN;
667	}
668	return ( day * msPerDay	) +	time;
669}
670function TimeClip( t ) {
671	if ( isNaN(	t )	) {
672		return ( Number.NaN	);
673	}
674	if ( Math.abs( t ) > 8.64e15 ) {
675		return ( Number.NaN	);
676	}
677
678	return ( ToInteger(	t )	);
679}
680function ToInteger(	t )	{
681	t =	Number(	t );
682
683	if ( isNaN(	t )	){
684		return ( Number.NaN	);
685	}
686	if ( t == 0	|| t ==	-0 ||
687		 t == Number.POSITIVE_INFINITY || t	== Number.NEGATIVE_INFINITY	) {
688		 return	0;
689	}
690
691	var	sign = ( t < 0 ) ? -1 :	1;
692
693	return ( sign *	Math.floor(	Math.abs( t	) )	);
694}
695function Enumerate ( o ) {
696	var	p;
697	for	( p	in o ) {
698		print( p +": " + o[p] );
699	}
700}
701
702/* these functions are useful for running tests manually in Rhino */
703
704function GetContext() {
705	return Packages.com.netscape.javascript.Context.getCurrentContext();
706}
707function OptLevel( i ) {
708	i =	Number(i);
709	var	cx = GetContext();
710	cx.setOptimizationLevel(i);
711}
712/* end of Rhino functions */
713