mirror of https://github.com/tc39/test262.git
Import SpiderMonkey Temporal tests
Temporal tests written for the SpiderMonkey implementation. Mostly covers edge cases around mathematical operations and regression tests for reported spec bugs.
This commit is contained in:
parent
46c3823117
commit
e292fb80de
|
@ -0,0 +1,77 @@
|
|||
// Copyright (C) 2022 André Bargull. All rights reserved.
|
||||
// This code is governed by the BSD license found in the LICENSE file.
|
||||
|
||||
/*---
|
||||
esid: sec-temporal.zoneddatetime.prototype.round
|
||||
description: >
|
||||
NanosecondsToDays computes with precise mathematical integers.
|
||||
info: |
|
||||
NanosecondsToDays ( nanoseconds, relativeTo )
|
||||
...
|
||||
14. If sign is 1, then
|
||||
a. Repeat, while days > 0 and intermediateNs > endNs,
|
||||
i. Set days to days - 1.
|
||||
ii. ...
|
||||
|
||||
Ensure |days = days - 1| is exact and doesn't loose precision.
|
||||
features: [Temporal]
|
||||
---*/
|
||||
|
||||
var expectedDurationDays = [
|
||||
Number.MAX_SAFE_INTEGER + 4, // 9007199254740996
|
||||
Number.MAX_SAFE_INTEGER + 3, // 9007199254740994
|
||||
Number.MAX_SAFE_INTEGER + 2, // 9007199254740992
|
||||
Number.MAX_SAFE_INTEGER + 1, // 9007199254740992
|
||||
Number.MAX_SAFE_INTEGER + 0, // 9007199254740991
|
||||
Number.MAX_SAFE_INTEGER - 1, // 9007199254740990
|
||||
Number.MAX_SAFE_INTEGER - 2, // 9007199254740989
|
||||
Number.MAX_SAFE_INTEGER - 3, // 9007199254740988
|
||||
Number.MAX_SAFE_INTEGER - 4, // 9007199254740987
|
||||
Number.MAX_SAFE_INTEGER - 5, // 9007199254740986
|
||||
];
|
||||
|
||||
// Intentionally not Test262Error to ensure assertions errors are propagated.
|
||||
class StopExecution extends Error {}
|
||||
|
||||
var cal = new class extends Temporal.Calendar {
|
||||
#dateUntil = 0;
|
||||
|
||||
dateUntil(one, two, options) {
|
||||
if (++this.#dateUntil === 1) {
|
||||
return Temporal.Duration.from({days: Number.MAX_SAFE_INTEGER + 4});
|
||||
}
|
||||
return super.dateUntil(one, two, options);
|
||||
}
|
||||
|
||||
#dateAdd = 0;
|
||||
|
||||
dateAdd(date, duration, options) {
|
||||
// Ensure we don't add too many days which would lead to creating an invalid date.
|
||||
if (++this.#dateAdd === 3) {
|
||||
assert.sameValue(duration.days, Number.MAX_SAFE_INTEGER + 4);
|
||||
|
||||
// The added days must be larger than 5 for the |intermediateNs > endNs| condition.
|
||||
return super.dateAdd(date, "P6D", options);
|
||||
}
|
||||
|
||||
// Ensure the duration days are exact.
|
||||
if (this.#dateAdd > 3) {
|
||||
if (!expectedDurationDays.length) {
|
||||
throw new StopExecution();
|
||||
}
|
||||
assert.sameValue(duration.days, expectedDurationDays.shift());
|
||||
|
||||
// Add more than 5 for the |intermediateNs > endNs| condition.
|
||||
return super.dateAdd(date, "P6D", options);
|
||||
}
|
||||
|
||||
// Otherwise call the default implementation.
|
||||
return super.dateAdd(date, duration, options);
|
||||
}
|
||||
}("iso8601");
|
||||
|
||||
var zoned = new Temporal.ZonedDateTime(0n, "UTC", cal);
|
||||
var duration = Temporal.Duration.from({days: 5});
|
||||
var options = {smallestUnit: "days", relativeTo: zoned};
|
||||
|
||||
assert.throws(StopExecution, () => duration.round(options));
|
|
@ -0,0 +1,45 @@
|
|||
// Copyright (C) 2022 André Bargull. All rights reserved.
|
||||
// This code is governed by the BSD license found in the LICENSE file.
|
||||
|
||||
/*---
|
||||
esid: sec-temporal.zoneddatetime.prototype.round
|
||||
description: >
|
||||
NanosecondsToDays computes with precise mathematical integers.
|
||||
info: |
|
||||
NanosecondsToDays ( nanoseconds, relativeTo )
|
||||
...
|
||||
17. Repeat, while done is false,
|
||||
...
|
||||
c. If (nanoseconds - dayLengthNs) × sign ≥ 0, then
|
||||
...
|
||||
iii. Set days to days + sign.
|
||||
|
||||
Ensure |days = days + sign| is exact and doesn't loose precision.
|
||||
features: [Temporal]
|
||||
---*/
|
||||
|
||||
var cal = new class extends Temporal.Calendar {
|
||||
#dateUntil = 0;
|
||||
|
||||
dateUntil(one, two, options) {
|
||||
if (++this.#dateUntil === 1) {
|
||||
return Temporal.Duration.from({days: Number.MAX_SAFE_INTEGER + 10});
|
||||
}
|
||||
return super.dateUntil(one, two, options);
|
||||
}
|
||||
|
||||
#dateAdd = 0;
|
||||
|
||||
dateAdd(date, duration, options) {
|
||||
if (++this.#dateAdd === 3) {
|
||||
return super.dateAdd(date, "P1D", options);
|
||||
}
|
||||
return super.dateAdd(date, duration, options);
|
||||
}
|
||||
}("iso8601");
|
||||
|
||||
var zoned = new Temporal.ZonedDateTime(0n, "UTC", cal);
|
||||
var duration = Temporal.Duration.from({days: 5});
|
||||
var result = duration.round({smallestUnit: "days", relativeTo: zoned});
|
||||
|
||||
assert.sameValue(result.days, Number(BigInt(Number.MAX_SAFE_INTEGER + 10) + 5n));
|
96
test/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-1.js
vendored
Normal file
96
test/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-1.js
vendored
Normal file
|
@ -0,0 +1,96 @@
|
|||
// Copyright (C) 2022 André Bargull. All rights reserved.
|
||||
// This code is governed by the BSD license found in the LICENSE file.
|
||||
|
||||
/*---
|
||||
esid: sec-temporal.duration.prototype.total
|
||||
description: >
|
||||
RoundDuration computes on exact mathematical values.
|
||||
features: [Temporal]
|
||||
---*/
|
||||
|
||||
// Return the next Number value in direction to +Infinity.
|
||||
function nextUp(num) {
|
||||
if (!Number.isFinite(num)) {
|
||||
return num;
|
||||
}
|
||||
if (num === 0) {
|
||||
return Number.MIN_VALUE;
|
||||
}
|
||||
|
||||
var f64 = new Float64Array([num]);
|
||||
var u64 = new BigUint64Array(f64.buffer);
|
||||
u64[0] += (num < 0 ? -1n : 1n);
|
||||
return f64[0];
|
||||
}
|
||||
|
||||
// Return the next Number value in direction to -Infinity.
|
||||
function nextDown(num) {
|
||||
if (!Number.isFinite(num)) {
|
||||
return num;
|
||||
}
|
||||
if (num === 0) {
|
||||
return -Number.MIN_VALUE;
|
||||
}
|
||||
|
||||
var f64 = new Float64Array([num]);
|
||||
var u64 = new BigUint64Array(f64.buffer);
|
||||
u64[0] += (num < 0 ? 1n : -1n);
|
||||
return f64[0];
|
||||
}
|
||||
|
||||
let duration = Temporal.Duration.from({
|
||||
hours: 4000,
|
||||
nanoseconds: 1,
|
||||
});
|
||||
|
||||
let total = duration.total({unit: "hours"});
|
||||
|
||||
// From RoundDuration():
|
||||
//
|
||||
// 7. Let fractionalSeconds be nanoseconds × 10^-9 + microseconds × 10^-6 + milliseconds × 10^-3 + seconds.
|
||||
// = nanoseconds × 10^-9
|
||||
// = 1 × 10^-9
|
||||
// = 10^-9
|
||||
// = 0.000000001
|
||||
//
|
||||
// 13.a. Let fractionalHours be (fractionalSeconds / 60 + minutes) / 60 + hours.
|
||||
// = (fractionalSeconds / 60) / 60 + 4000
|
||||
// = 0.000000001 / 3600 + 4000
|
||||
//
|
||||
// 13.b. Set hours to RoundNumberToIncrement(fractionalHours, increment, roundingMode).
|
||||
// = trunc(fractionalHours)
|
||||
// = trunc(0.000000001 / 3600 + 4000)
|
||||
// = 4000
|
||||
//
|
||||
// 13.c. Set remainder to fractionalHours - hours.
|
||||
// = fractionalHours - hours
|
||||
// = 0.000000001 / 3600 + 4000 - 4000
|
||||
// = 0.000000001 / 3600
|
||||
//
|
||||
// From Temporal.Duration.prototype.total ( options ):
|
||||
//
|
||||
// 18. If unit is "hours", then let whole be roundResult.[[Hours]].
|
||||
// ...
|
||||
// 24. Return whole + roundResult.[[Remainder]].
|
||||
//
|
||||
// |whole| is 4000 and the remainder is (0.000000001 / 3600).
|
||||
//
|
||||
// 0.000000001 / 3600
|
||||
// = (1 / 10^9) / 3600
|
||||
// = (1 / 36) / 10^11
|
||||
// = 0.02777.... / 10^11
|
||||
// = 0.0000000000002777...
|
||||
//
|
||||
// 4000.0000000000002777... can't be represented exactly, the next best approximation
|
||||
// is 4000.0000000000005.
|
||||
|
||||
const expected = 4000.0000000000005;
|
||||
assert.sameValue(expected, 4000.0000000000002777);
|
||||
|
||||
// The next Number in direction -Infinity is less precise.
|
||||
assert.sameValue(nextDown(expected), 4000);
|
||||
|
||||
// The next Number in direction +Infinity is less precise.
|
||||
assert.sameValue(nextUp(expected), 4000.000000000001);
|
||||
|
||||
assert.sameValue(total, expected);
|
98
test/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-2.js
vendored
Normal file
98
test/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-2.js
vendored
Normal file
|
@ -0,0 +1,98 @@
|
|||
// Copyright (C) 2022 André Bargull. All rights reserved.
|
||||
// This code is governed by the BSD license found in the LICENSE file.
|
||||
|
||||
/*---
|
||||
esid: sec-temporal.duration.prototype.total
|
||||
description: >
|
||||
RoundDuration computes on exact mathematical values.
|
||||
features: [Temporal]
|
||||
---*/
|
||||
|
||||
// Return the next Number value in direction to +Infinity.
|
||||
function nextUp(num) {
|
||||
if (!Number.isFinite(num)) {
|
||||
return num;
|
||||
}
|
||||
if (num === 0) {
|
||||
return Number.MIN_VALUE;
|
||||
}
|
||||
|
||||
var f64 = new Float64Array([num]);
|
||||
var u64 = new BigUint64Array(f64.buffer);
|
||||
u64[0] += (num < 0 ? -1n : 1n);
|
||||
return f64[0];
|
||||
}
|
||||
|
||||
// Return the next Number value in direction to -Infinity.
|
||||
function nextDown(num) {
|
||||
if (!Number.isFinite(num)) {
|
||||
return num;
|
||||
}
|
||||
if (num === 0) {
|
||||
return -Number.MIN_VALUE;
|
||||
}
|
||||
|
||||
var f64 = new Float64Array([num]);
|
||||
var u64 = new BigUint64Array(f64.buffer);
|
||||
u64[0] += (num < 0 ? 1n : -1n);
|
||||
return f64[0];
|
||||
}
|
||||
|
||||
let duration = Temporal.Duration.from({
|
||||
hours: 4000,
|
||||
minutes: 59,
|
||||
seconds: 59,
|
||||
milliseconds: 999,
|
||||
microseconds: 999,
|
||||
nanoseconds: 999,
|
||||
});
|
||||
|
||||
let total = duration.total({unit: "hours"});
|
||||
|
||||
// From RoundDuration():
|
||||
//
|
||||
// 7. Let fractionalSeconds be nanoseconds × 10^-9 + microseconds × 10^-6 + milliseconds × 10^-3 + seconds.
|
||||
// = 999 × 10^-9 + 999 × 10^-6 + 999 × 10^-3 + 59
|
||||
// = 59.999'999'999
|
||||
//
|
||||
// 13.a. Let fractionalHours be (fractionalSeconds / 60 + minutes) / 60 + hours.
|
||||
// = (59.999'999'999 / 60 + 59) / 60 + 4000
|
||||
// = 1 - 0.000000001 / 3600 + 4000
|
||||
//
|
||||
// 13.b. Set hours to RoundNumberToIncrement(fractionalHours, increment, roundingMode).
|
||||
// = trunc(fractionalHours)
|
||||
// = trunc(1 - 0.000000001 / 3600 + 4000)
|
||||
// = 4000
|
||||
//
|
||||
// 13.c. Set remainder to fractionalHours - hours.
|
||||
// = fractionalHours - hours
|
||||
// = 1 - 0.000000001 / 3600 + 4000 - 4000
|
||||
// = 1 - 0.000000001 / 3600
|
||||
//
|
||||
// From Temporal.Duration.prototype.total ( options ):
|
||||
//
|
||||
// 18. If unit is "hours", then let whole be roundResult.[[Hours]].
|
||||
// ...
|
||||
// 24. Return whole + roundResult.[[Remainder]].
|
||||
//
|
||||
// |whole| is 4000 and the remainder is (1 - 0.000000001 / 3600).
|
||||
//
|
||||
// 1 - 0.000000001 / 3600
|
||||
// = 1 - (1 / 10^9) / 3600
|
||||
// = 1 - (1 / 36) / 10^11
|
||||
// = 1 - 0.02777.... / 10^11
|
||||
// = 0.9999999999997222...
|
||||
//
|
||||
// 4000.9999999999997222... can't be represented exactly, the next best approximation
|
||||
// is 4000.9999999999995.
|
||||
|
||||
const expected = 4000.9999999999995;
|
||||
assert.sameValue(expected, 4000.9999999999997222);
|
||||
|
||||
// The next Number in direction -Infinity is less precise.
|
||||
assert.sameValue(nextDown(expected), 4000.999999999999);
|
||||
|
||||
// The next Number in direction +Infinity is less precise.
|
||||
assert.sameValue(nextUp(expected), 4001);
|
||||
|
||||
assert.sameValue(total, expected);
|
130
test/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/precision-exact-mathematical-values.js
vendored
Normal file
130
test/built-ins/Temporal/ZonedDateTime/prototype/hoursInDay/precision-exact-mathematical-values.js
vendored
Normal file
|
@ -0,0 +1,130 @@
|
|||
// Copyright (C) 2022 André Bargull. All rights reserved.
|
||||
// This code is governed by the BSD license found in the LICENSE file.
|
||||
|
||||
/*---
|
||||
esid: sec-get-temporal.zoneddatetime.prototype.hoursinday
|
||||
description: >
|
||||
Hours in day is correctly rounded using precise mathematical values.
|
||||
info: |
|
||||
get Temporal.ZonedDateTime.prototype.hoursInDay
|
||||
|
||||
...
|
||||
15. Let diffNs be tomorrowInstant.[[Nanoseconds]] - todayInstant.[[Nanoseconds]].
|
||||
16. Return 𝔽(diffNs / (3.6 × 10^12)).
|
||||
features: [Temporal]
|
||||
---*/
|
||||
|
||||
const maxInstant = 86_40000_00000_00000_00000n;
|
||||
|
||||
function nextUp(num) {
|
||||
if (!Number.isFinite(num)) {
|
||||
return num;
|
||||
}
|
||||
if (num === 0) {
|
||||
return Number.MIN_VALUE;
|
||||
}
|
||||
|
||||
var f64 = new Float64Array([num]);
|
||||
var u64 = new BigUint64Array(f64.buffer);
|
||||
u64[0] += (num < 0 ? -1n : 1n);
|
||||
return f64[0];
|
||||
}
|
||||
|
||||
function nextDown(num) {
|
||||
if (!Number.isFinite(num)) {
|
||||
return num;
|
||||
}
|
||||
if (num === 0) {
|
||||
return -Number.MIN_VALUE;
|
||||
}
|
||||
|
||||
var f64 = new Float64Array([num]);
|
||||
var u64 = new BigUint64Array(f64.buffer);
|
||||
u64[0] += (num < 0 ? 1n : -1n);
|
||||
return f64[0];
|
||||
}
|
||||
|
||||
function BigIntAbs(n) {
|
||||
return n >= 0 ? n : -n;
|
||||
}
|
||||
|
||||
function test(todayInstant, tomorrowInstant, expected) {
|
||||
let timeZone = new class extends Temporal.TimeZone {
|
||||
#getPossibleInstantsFor = 0;
|
||||
|
||||
getPossibleInstantsFor() {
|
||||
if (++this.#getPossibleInstantsFor === 1) {
|
||||
return [new Temporal.Instant(todayInstant)];
|
||||
}
|
||||
assert.sameValue(this.#getPossibleInstantsFor, 2);
|
||||
return [new Temporal.Instant(tomorrowInstant)];
|
||||
}
|
||||
}("UTC");
|
||||
|
||||
let zdt = new Temporal.ZonedDateTime(0n, timeZone);
|
||||
let zdt_hoursInDay = zdt.hoursInDay;
|
||||
|
||||
assert.sameValue(zdt_hoursInDay, expected);
|
||||
|
||||
// Ensure the |expected| value is actually correctly rounded.
|
||||
|
||||
const nsPerSec = 1000n * 1000n * 1000n;
|
||||
const secPerHour = 60n * 60n;
|
||||
const nsPerHour = secPerHour * nsPerSec;
|
||||
|
||||
function toNanoseconds(hours) {
|
||||
let wholeHours = BigInt(Math.trunc(hours)) * nsPerHour;
|
||||
let fractionalHours = BigInt(Math.trunc(hours % 1 * Number(nsPerHour)));
|
||||
return wholeHours + fractionalHours;
|
||||
}
|
||||
|
||||
let diff = tomorrowInstant - todayInstant;
|
||||
let nanosInDay = toNanoseconds(zdt_hoursInDay);
|
||||
|
||||
// The next number gives a less precise result.
|
||||
let next = toNanoseconds(nextUp(zdt_hoursInDay));
|
||||
assert(BigIntAbs(diff - nanosInDay) <= BigIntAbs(diff - next));
|
||||
|
||||
// The previous number gives a less precise result.
|
||||
let prev = toNanoseconds(nextDown(zdt_hoursInDay));
|
||||
assert(BigIntAbs(diff - nanosInDay) <= BigIntAbs(diff - prev));
|
||||
|
||||
// This computation can be inaccurate.
|
||||
let inaccurate = Number(diff) / Number(nsPerHour);
|
||||
|
||||
// Doing it component-wise can produce more accurate results.
|
||||
let hours = Number(diff / nsPerSec) / Number(secPerHour);
|
||||
let fractionalHours = Number(diff % nsPerSec) / Number(nsPerHour);
|
||||
assert.sameValue(hours + fractionalHours, expected);
|
||||
|
||||
// Ensure the result is more precise than the inaccurate result.
|
||||
let inaccurateNanosInDay = toNanoseconds(inaccurate);
|
||||
assert(BigIntAbs(diff - nanosInDay) <= BigIntAbs(diff - inaccurateNanosInDay));
|
||||
}
|
||||
|
||||
test(-maxInstant, 0n, 2400000000);
|
||||
test(-maxInstant, 1n, 2400000000);
|
||||
test(-maxInstant, 10n, 2400000000);
|
||||
test(-maxInstant, 100n, 2400000000);
|
||||
|
||||
test(-maxInstant, 1_000n, 2400000000);
|
||||
test(-maxInstant, 10_000n, 2400000000);
|
||||
test(-maxInstant, 100_000n, 2400000000);
|
||||
|
||||
test(-maxInstant, 1_000_000n, 2400000000.0000005);
|
||||
test(-maxInstant, 10_000_000n, 2400000000.000003);
|
||||
test(-maxInstant, 100_000_000n, 2400000000.0000277);
|
||||
|
||||
test(-maxInstant, 1_000_000_000n, 2400000000.000278);
|
||||
test(-maxInstant, 10_000_000_000n, 2400000000.0027776);
|
||||
test(-maxInstant, 100_000_000_000n, 2400000000.0277777);
|
||||
|
||||
test(-maxInstant, maxInstant, 4800000000);
|
||||
test(-maxInstant, maxInstant - 10_000_000_000n, 4799999999.997222);
|
||||
test(-maxInstant, maxInstant - 10_000_000_000n + 1n, 4799999999.997222);
|
||||
test(-maxInstant, maxInstant - 10_000_000_000n - 1n, 4799999999.997222);
|
||||
|
||||
test(maxInstant, -maxInstant, -4800000000);
|
||||
test(maxInstant, -(maxInstant - 10_000_000_000n), -4799999999.997222);
|
||||
test(maxInstant, -(maxInstant - 10_000_000_000n + 1n), -4799999999.997222);
|
||||
test(maxInstant, -(maxInstant - 10_000_000_000n - 1n), -4799999999.997222);
|
Loading…
Reference in New Issue