mirror of
https://github.com/tc39/test262.git
synced 2025-07-27 07:54:41 +02:00
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…
x
Reference in New Issue
Block a user