| /* The contents of this file are subject to the Netscape Public |
| * License Version 1.1 (the "License"); you may not use this file |
| * except in compliance with the License. You may obtain a copy of |
| * the License at http://www.mozilla.org/NPL/ |
| * |
| * Software distributed under the License is distributed on an "AS |
| * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or |
| * implied. See the License for the specific language governing |
| * rights and limitations under the License. |
| * |
| * The Original Code is Mozilla Communicator client code, released March |
| * 31, 1998. |
| * |
| * The Initial Developer of the Original Code is Netscape Communications |
| * Corporation. Portions created by Netscape are |
| * Copyright (C) 1998 Netscape Communications Corporation. All |
| * Rights Reserved. |
| * |
| * Contributor(s): |
| * |
| */ |
| /* |
| * JavaScript shared functions file for running the tests in either |
| * stand-alone JavaScript engine. To run a test, first load this file, |
| * then load the test script. |
| */ |
| |
| var completed = false; |
| var testcases; |
| var tc = 0; |
| |
| SECTION = ""; |
| VERSION = ""; |
| BUGNUMBER = ""; |
| |
| /* |
| * constant strings |
| */ |
| var GLOBAL = "[object global]"; |
| var PASSED = " PASSED!" |
| var FAILED = " FAILED! expected: "; |
| |
| var DEBUG = false; |
| |
| |
| |
| /* wrapper for test cas constructor that doesn't require the SECTION |
| * argument. |
| */ |
| |
| function AddTestCase( description, expect, actual ) { |
| testcases[tc++] = new TestCase( SECTION, description, expect, actual ); |
| } |
| |
| /* |
| * TestCase constructor |
| * |
| */ |
| |
| function TestCase( n, d, e, a ) { |
| this.name = n; |
| this.description = d; |
| this.expect = e; |
| this.actual = a; |
| this.passed = true; |
| this.reason = ""; |
| this.bugnumber = BUGNUMBER; |
| |
| this.passed = getTestCaseResult( this.expect, this.actual ); |
| if ( DEBUG ) { |
| writeLineToLog( "added " + this.description ); |
| } |
| } |
| |
| /* |
| * Set up test environment. |
| * |
| */ |
| function startTest() { |
| if ( version ) { |
| // JavaScript 1.3 is supposed to be compliant ecma version 1.0 |
| if ( VERSION == "ECMA_1" ) { |
| version ( "130" ); |
| } |
| if ( VERSION == "JS_1.3" ) { |
| version ( "130" ); |
| } |
| if ( VERSION == "JS_1.2" ) { |
| version ( "120" ); |
| } |
| if ( VERSION == "JS_1.1" ) { |
| version ( "110" ); |
| } |
| // for ecma version 2.0, we will leave the javascript version to |
| // the default ( for now ). |
| } |
| |
| // print out bugnumber |
| |
| if ( BUGNUMBER ) { |
| writeLineToLog ("BUGNUMBER: " + BUGNUMBER ); |
| } |
| |
| testcases = new Array(); |
| tc = 0; |
| } |
| |
| |
| function test() { |
| for ( tc=0; tc < testcases.length; tc++ ) { |
| testcases[tc].passed = writeTestCaseResult( |
| testcases[tc].expect, |
| testcases[tc].actual, |
| testcases[tc].description +" = "+ testcases[tc].actual ); |
| testcases[tc].reason += ( testcases[tc].passed ) ? "" : "wrong value "; |
| } |
| stopTest(); |
| return ( testcases ); |
| } |
| |
| /* |
| * Compare expected result to the actual result and figure out whether |
| * the test case passed. |
| */ |
| function getTestCaseResult( expect, actual ) { |
| // because ( NaN == NaN ) always returns false, need to do |
| // a special compare to see if we got the right result. |
| if ( actual != actual ) { |
| if ( typeof actual == "object" ) { |
| actual = "NaN object"; |
| } else { |
| actual = "NaN number"; |
| } |
| } |
| if ( expect != expect ) { |
| if ( typeof expect == "object" ) { |
| expect = "NaN object"; |
| } else { |
| expect = "NaN number"; |
| } |
| } |
| |
| var passed = ( expect == actual ) ? true : false; |
| |
| // if both objects are numbers |
| // need to replace w/ IEEE standard for rounding |
| if ( !passed |
| && typeof(actual) == "number" |
| && typeof(expect) == "number" |
| ) { |
| if ( Math.abs(actual-expect) < 0.0000001 ) { |
| passed = true; |
| } |
| } |
| |
| // verify type is the same |
| if ( typeof(expect) != typeof(actual) ) { |
| passed = false; |
| } |
| |
| return passed; |
| } |
| |
| /* |
| * Begin printing functions. These functions use the shell's |
| * print function. When running tests in the browser, these |
| * functions, override these functions with functions that use |
| * document.write. |
| */ |
| |
| function writeTestCaseResult( expect, actual, string ) { |
| var passed = getTestCaseResult( expect, actual ); |
| writeFormattedResult( expect, actual, string, passed ); |
| return passed; |
| } |
| function writeFormattedResult( expect, actual, string, passed ) { |
| var s = string ; |
| s += ( passed ) ? PASSED : FAILED + expect; |
| writeLineToLog( s); |
| return passed; |
| } |
| function writeLineToLog( string ) { |
| print( string ); |
| } |
| function writeHeaderToLog( string ) { |
| print( string ); |
| } |
| /* end of print functions */ |
| |
| |
| /* |
| * When running in the shell, run the garbage collector after the |
| * test has completed. |
| */ |
| |
| function stopTest() { |
| var gc; |
| if ( gc != undefined ) { |
| gc(); |
| } |
| } |
| |
| /* |
| * Convenience function for displaying failed test cases. Useful |
| * when running tests manually. |
| * |
| */ |
| function getFailedCases() { |
| for ( var i = 0; i < testcases.length; i++ ) { |
| if ( ! testcases[i].passed ) { |
| print( testcases[i].description +" = " +testcases[i].actual +" expected: "+ testcases[i].expect ); |
| } |
| } |
| } |
| /* |
| * Date functions used by tests in Date suite |
| * |
| */ |
| var msPerDay = 86400000; |
| var HoursPerDay = 24; |
| var MinutesPerHour = 60; |
| var SecondsPerMinute = 60; |
| var msPerSecond = 1000; |
| var msPerMinute = 60000; // msPerSecond * SecondsPerMinute |
| var msPerHour = 3600000; // msPerMinute * MinutesPerHour |
| var TZ_DIFF = getTimeZoneDiff(); // offset of tester's timezone from UTC |
| var TZ_PST = -8; // offset of Pacific Standard Time from UTC |
| var PST_DIFF = TZ_DIFF - TZ_PST; // offset of tester's timezone from PST |
| var TIME_1970 = 0; |
| var TIME_2000 = 946684800000; |
| var TIME_1900 = -2208988800000; |
| var TIME_YEAR_0 = -62167219200000; |
| |
| |
| /* |
| * Originally, the test suite used a hard-coded value TZ_DIFF = -8. |
| * But that was only valid for testers in the Pacific Standard Time Zone! |
| * We calculate the proper number dynamically for any tester. We just |
| * have to be careful not to use a date subject to Daylight Savings Time... |
| */ |
| function getTimeZoneDiff() |
| { |
| return -((new Date(2000, 1, 1)).getTimezoneOffset())/60; |
| } |
| |
| |
| /* |
| * Date test "ResultArrays" are hard-coded for Pacific Standard Time. |
| * We must adjust them for the tester's own timezone - |
| */ |
| function adjustResultArray(ResultArray, msMode) |
| { |
| // If the tester's system clock is in PST, no need to continue - |
| if (!PST_DIFF) {return;} |
| |
| /* The date testcases instantiate Date objects in two different ways: |
| * |
| * millisecond mode: e.g. dt = new Date(10000000); |
| * year-month-day mode: dt = new Date(2000, 5, 1, ...); |
| * |
| * In the first case, the date is measured from Time 0 in Greenwich (i.e. UTC). |
| * In the second case, it is measured with reference to the tester's local timezone. |
| * |
| * In the first case we must correct those values expected for local measurements, |
| * like dt.getHours() etc. No correction is necessary for dt.getUTCHours() etc. |
| * |
| * In the second case, it is exactly the other way around - |
| */ |
| if (msMode) |
| { |
| // The hard-coded UTC milliseconds from Time 0 derives from a UTC date. |
| // Shift to the right by the offset between UTC and the tester. |
| var t = ResultArray[TIME] + TZ_DIFF*msPerHour; |
| |
| // Use our date arithmetic functions to determine the local hour, day, etc. |
| ResultArray[HOURS] = HourFromTime(t); |
| ResultArray[DAY] = WeekDay(t); |
| ResultArray[DATE] = DateFromTime(t); |
| ResultArray[MONTH] = MonthFromTime(t); |
| ResultArray[YEAR] = YearFromTime(t); |
| } |
| else |
| { |
| // The hard-coded UTC milliseconds from Time 0 derives from a PST date. |
| // Shift to the left by the offset between PST and the tester. |
| var t = ResultArray[TIME] - PST_DIFF*msPerHour; |
| |
| // Use our date arithmetic functions to determine the UTC hour, day, etc. |
| ResultArray[TIME] = t; |
| ResultArray[UTC_HOURS] = HourFromTime(t); |
| ResultArray[UTC_DAY] = WeekDay(t); |
| ResultArray[UTC_DATE] = DateFromTime(t); |
| ResultArray[UTC_MONTH] = MonthFromTime(t); |
| ResultArray[UTC_YEAR] = YearFromTime(t); |
| } |
| } |
| |
| |
| function Day( t ) { |
| return ( Math.floor(t/msPerDay ) ); |
| } |
| function DaysInYear( y ) { |
| if ( y % 4 != 0 ) { |
| return 365; |
| } |
| if ( (y % 4 == 0) && (y % 100 != 0) ) { |
| return 366; |
| } |
| if ( (y % 100 == 0) && (y % 400 != 0) ) { |
| return 365; |
| } |
| if ( (y % 400 == 0) ){ |
| return 366; |
| } else { |
| return "ERROR: DaysInYear(" + y + ") case not covered"; |
| } |
| } |
| function TimeInYear( y ) { |
| return ( DaysInYear(y) * msPerDay ); |
| } |
| function DayNumber( t ) { |
| return ( Math.floor( t / msPerDay ) ); |
| } |
| function TimeWithinDay( t ) { |
| if ( t < 0 ) { |
| return ( (t % msPerDay) + msPerDay ); |
| } else { |
| return ( t % msPerDay ); |
| } |
| } |
| function YearNumber( t ) { |
| } |
| function TimeFromYear( y ) { |
| return ( msPerDay * DayFromYear(y) ); |
| } |
| function DayFromYear( y ) { |
| return ( 365*(y-1970) + |
| Math.floor((y-1969)/4) - |
| Math.floor((y-1901)/100) + |
| Math.floor((y-1601)/400) ); |
| } |
| function InLeapYear( t ) { |
| if ( DaysInYear(YearFromTime(t)) == 365 ) { |
| return 0; |
| } |
| if ( DaysInYear(YearFromTime(t)) == 366 ) { |
| return 1; |
| } else { |
| return "ERROR: InLeapYear("+ t + ") case not covered"; |
| } |
| } |
| function YearFromTime( t ) { |
| t = Number( t ); |
| var sign = ( t < 0 ) ? -1 : 1; |
| var year = ( sign < 0 ) ? 1969 : 1970; |
| for ( var timeToTimeZero = t; ; ) { |
| // subtract the current year's time from the time that's left. |
| timeToTimeZero -= sign * TimeInYear(year) |
| |
| // if there's less than the current year's worth of time left, then break. |
| if ( sign < 0 ) { |
| if ( sign * timeToTimeZero <= 0 ) { |
| break; |
| } else { |
| year += sign; |
| } |
| } else { |
| if ( sign * timeToTimeZero < 0 ) { |
| break; |
| } else { |
| year += sign; |
| } |
| } |
| } |
| return ( year ); |
| } |
| function MonthFromTime( t ) { |
| // i know i could use switch but i'd rather not until it's part of ECMA |
| var day = DayWithinYear( t ); |
| var leap = InLeapYear(t); |
| |
| if ( (0 <= day) && (day < 31) ) { |
| return 0; |
| } |
| if ( (31 <= day) && (day < (59+leap)) ) { |
| return 1; |
| } |
| if ( ((59+leap) <= day) && (day < (90+leap)) ) { |
| return 2; |
| } |
| if ( ((90+leap) <= day) && (day < (120+leap)) ) { |
| return 3; |
| } |
| if ( ((120+leap) <= day) && (day < (151+leap)) ) { |
| return 4; |
| } |
| if ( ((151+leap) <= day) && (day < (181+leap)) ) { |
| return 5; |
| } |
| if ( ((181+leap) <= day) && (day < (212+leap)) ) { |
| return 6; |
| } |
| if ( ((212+leap) <= day) && (day < (243+leap)) ) { |
| return 7; |
| } |
| if ( ((243+leap) <= day) && (day < (273+leap)) ) { |
| return 8; |
| } |
| if ( ((273+leap) <= day) && (day < (304+leap)) ) { |
| return 9; |
| } |
| if ( ((304+leap) <= day) && (day < (334+leap)) ) { |
| return 10; |
| } |
| if ( ((334+leap) <= day) && (day < (365+leap)) ) { |
| return 11; |
| } else { |
| return "ERROR: MonthFromTime("+t+") not known"; |
| } |
| } |
| function DayWithinYear( t ) { |
| return( Day(t) - DayFromYear(YearFromTime(t))); |
| } |
| function DateFromTime( t ) { |
| var day = DayWithinYear(t); |
| var month = MonthFromTime(t); |
| |
| if ( month == 0 ) { |
| return ( day + 1 ); |
| } |
| if ( month == 1 ) { |
| return ( day - 30 ); |
| } |
| if ( month == 2 ) { |
| return ( day - 58 - InLeapYear(t) ); |
| } |
| if ( month == 3 ) { |
| return ( day - 89 - InLeapYear(t)); |
| } |
| if ( month == 4 ) { |
| return ( day - 119 - InLeapYear(t)); |
| } |
| if ( month == 5 ) { |
| return ( day - 150- InLeapYear(t)); |
| } |
| if ( month == 6 ) { |
| return ( day - 180- InLeapYear(t)); |
| } |
| if ( month == 7 ) { |
| return ( day - 211- InLeapYear(t)); |
| } |
| if ( month == 8 ) { |
| return ( day - 242- InLeapYear(t)); |
| } |
| if ( month == 9 ) { |
| return ( day - 272- InLeapYear(t)); |
| } |
| if ( month == 10 ) { |
| return ( day - 303- InLeapYear(t)); |
| } |
| if ( month == 11 ) { |
| return ( day - 333- InLeapYear(t)); |
| } |
| |
| return ("ERROR: DateFromTime("+t+") not known" ); |
| } |
| function WeekDay( t ) { |
| var weekday = (Day(t)+4) % 7; |
| return( weekday < 0 ? 7 + weekday : weekday ); |
| } |
| |
| // missing daylight savins time adjustment |
| |
| function HourFromTime( t ) { |
| var h = Math.floor( t / msPerHour ) % HoursPerDay; |
| return ( (h<0) ? HoursPerDay + h : h ); |
| } |
| function MinFromTime( t ) { |
| var min = Math.floor( t / msPerMinute ) % MinutesPerHour; |
| return( ( min < 0 ) ? MinutesPerHour + min : min ); |
| } |
| function SecFromTime( t ) { |
| var sec = Math.floor( t / msPerSecond ) % SecondsPerMinute; |
| return ( (sec < 0 ) ? SecondsPerMinute + sec : sec ); |
| } |
| function msFromTime( t ) { |
| var ms = t % msPerSecond; |
| return ( (ms < 0 ) ? msPerSecond + ms : ms ); |
| } |
| function LocalTZA() { |
| return ( TZ_DIFF * msPerHour ); |
| } |
| function UTC( t ) { |
| return ( t - LocalTZA() - DaylightSavingTA(t - LocalTZA()) ); |
| } |
| |
| function DaylightSavingTA( t ) { |
| t = t - LocalTZA(); |
| |
| var dst_start = GetSecondSundayInMarch(t) + 2*msPerHour; |
| var dst_end = GetFirstSundayInNovember(t)+ 2*msPerHour; |
| |
| if ( t >= dst_start && t < dst_end ) { |
| return msPerHour; |
| } else { |
| return 0; |
| } |
| |
| // Daylight Savings Time starts on the first Sunday in April at 2:00AM in |
| // PST. Other time zones will need to override this function. |
| |
| print( new Date( UTC(dst_start + LocalTZA())) ); |
| |
| return UTC(dst_start + LocalTZA()); |
| } |
| |
| function GetFirstSundayInApril( t ) { |
| var year = YearFromTime(t); |
| var leap = InLeapYear(t); |
| |
| var april = TimeFromYear(year) + TimeInMonth(0, leap) + TimeInMonth(1,leap) + |
| TimeInMonth(2,leap); |
| |
| for ( var first_sunday = april; WeekDay(first_sunday) > 0; |
| first_sunday += msPerDay ) |
| { |
| ; |
| } |
| |
| return first_sunday; |
| } |
| function GetLastSundayInOctober( t ) { |
| var year = YearFromTime(t); |
| var leap = InLeapYear(t); |
| |
| for ( var oct = TimeFromYear(year), m = 0; m < 9; m++ ) { |
| oct += TimeInMonth(m, leap); |
| } |
| for ( var last_sunday = oct + 30*msPerDay; WeekDay(last_sunday) > 0; |
| last_sunday -= msPerDay ) |
| { |
| ; |
| } |
| return last_sunday; |
| } |
| |
| // Added these two functions because DST rules changed for the US. |
| function GetSecondSundayInMarch( t ) { |
| var year = YearFromTime(t); |
| var leap = InLeapYear(t); |
| |
| var march = TimeFromYear(year) + TimeInMonth(0, leap) + TimeInMonth(1,leap); |
| |
| var sundayCount = 0; |
| var flag = true; |
| for ( var second_sunday = march; flag; second_sunday += msPerDay ) |
| { |
| if (WeekDay(second_sunday) == 0) { |
| if(++sundayCount == 2) |
| flag = false; |
| } |
| } |
| |
| return second_sunday; |
| } |
| function GetFirstSundayInNovember( t ) { |
| var year = YearFromTime(t); |
| var leap = InLeapYear(t); |
| |
| for ( var nov = TimeFromYear(year), m = 0; m < 10; m++ ) { |
| nov += TimeInMonth(m, leap); |
| } |
| for ( var first_sunday = nov; WeekDay(first_sunday) > 0; |
| first_sunday += msPerDay ) |
| { |
| ; |
| } |
| return first_sunday; |
| } |
| function LocalTime( t ) { |
| return ( t + LocalTZA() + DaylightSavingTA(t) ); |
| } |
| function MakeTime( hour, min, sec, ms ) { |
| if ( isNaN( hour ) || isNaN( min ) || isNaN( sec ) || isNaN( ms ) ) { |
| return Number.NaN; |
| } |
| |
| hour = ToInteger(hour); |
| min = ToInteger( min); |
| sec = ToInteger( sec); |
| ms = ToInteger( ms ); |
| |
| return( (hour*msPerHour) + (min*msPerMinute) + |
| (sec*msPerSecond) + ms ); |
| } |
| function MakeDay( year, month, date ) { |
| if ( isNaN(year) || isNaN(month) || isNaN(date) ) { |
| return Number.NaN; |
| } |
| year = ToInteger(year); |
| month = ToInteger(month); |
| date = ToInteger(date ); |
| |
| var sign = ( year < 1970 ) ? -1 : 1; |
| var t = ( year < 1970 ) ? 1 : 0; |
| var y = ( year < 1970 ) ? 1969 : 1970; |
| |
| var result5 = year + Math.floor( month/12 ); |
| var result6 = month % 12; |
| |
| if ( year < 1970 ) { |
| for ( y = 1969; y >= year; y += sign ) { |
| t += sign * TimeInYear(y); |
| } |
| } else { |
| for ( y = 1970 ; y < year; y += sign ) { |
| t += sign * TimeInYear(y); |
| } |
| } |
| |
| var leap = InLeapYear( t ); |
| |
| for ( var m = 0; m < month; m++ ) { |
| t += TimeInMonth( m, leap ); |
| } |
| |
| if ( YearFromTime(t) != result5 ) { |
| return Number.NaN; |
| } |
| if ( MonthFromTime(t) != result6 ) { |
| return Number.NaN; |
| } |
| if ( DateFromTime(t) != 1 ) { |
| return Number.NaN; |
| } |
| |
| return ( (Day(t)) + date - 1 ); |
| } |
| function TimeInMonth( month, leap ) { |
| // september april june november |
| // jan 0 feb 1 mar 2 apr 3 may 4 june 5 jul 6 |
| // aug 7 sep 8 oct 9 nov 10 dec 11 |
| |
| if ( month == 3 || month == 5 || month == 8 || month == 10 ) { |
| return ( 30*msPerDay ); |
| } |
| |
| // all the rest |
| if ( month == 0 || month == 2 || month == 4 || month == 6 || |
| month == 7 || month == 9 || month == 11 ) { |
| return ( 31*msPerDay ); |
| } |
| |
| // save february |
| return ( (leap == 0) ? 28*msPerDay : 29*msPerDay ); |
| } |
| function MakeDate( day, time ) { |
| if ( day == Number.POSITIVE_INFINITY || |
| day == Number.NEGATIVE_INFINITY || |
| day == Number.NaN ) { |
| return Number.NaN; |
| } |
| if ( time == Number.POSITIVE_INFINITY || |
| time == Number.POSITIVE_INFINITY || |
| day == Number.NaN) { |
| return Number.NaN; |
| } |
| return ( day * msPerDay ) + time; |
| } |
| function TimeClip( t ) { |
| if ( isNaN( t ) ) { |
| return ( Number.NaN ); |
| } |
| if ( Math.abs( t ) > 8.64e15 ) { |
| return ( Number.NaN ); |
| } |
| |
| return ( ToInteger( t ) ); |
| } |
| function ToInteger( t ) { |
| t = Number( t ); |
| |
| if ( isNaN( t ) ){ |
| return ( Number.NaN ); |
| } |
| if ( t == 0 || t == -0 || |
| t == Number.POSITIVE_INFINITY || t == Number.NEGATIVE_INFINITY ) { |
| return 0; |
| } |
| |
| var sign = ( t < 0 ) ? -1 : 1; |
| |
| return ( sign * Math.floor( Math.abs( t ) ) ); |
| } |
| function Enumerate ( o ) { |
| var p; |
| for ( p in o ) { |
| print( p +": " + o[p] ); |
| } |
| } |
| |
| /* these functions are useful for running tests manually in Rhino */ |
| |
| function GetContext() { |
| return Packages.com.netscape.javascript.Context.getCurrentContext(); |
| } |
| function OptLevel( i ) { |
| i = Number(i); |
| var cx = GetContext(); |
| cx.setOptimizationLevel(i); |
| } |
| /* end of Rhino functions */ |