diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /js/src/tests/test262/built-ins/Temporal/Duration | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/tests/test262/built-ins/Temporal/Duration')
595 files changed, 18755 insertions, 0 deletions
diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/basic.js b/js/src/tests/test262/built-ins/Temporal/Duration/basic.js new file mode 100644 index 0000000000..7ef099c703 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/basic.js @@ -0,0 +1,19 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration +description: Basic constructor tests. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.assertDuration(new Temporal.Duration(5, 5, 5, 5, 5, 5, 5, 5, 5, 0), + 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, "positive"); +TemporalHelpers.assertDuration(new Temporal.Duration(-5, -5, -5, -5, -5, -5, -5, -5, -5, 0), + -5, -5, -5, -5, -5, -5, -5, -5, -5, 0, "negative"); +TemporalHelpers.assertDuration(new Temporal.Duration(-0, -0, -0, -0, -0, -0, -0, -0, -0, -0), + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "negative zero"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/builtin.js b/js/src/tests/test262/built-ins/Temporal/Duration/builtin.js new file mode 100644 index 0000000000..360eb17718 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/builtin.js @@ -0,0 +1,30 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration +description: Tests that Temporal.Duration meets the requirements for built-in objects +info: | + Unless specified otherwise, a built-in object that is callable as a function is a built-in + function object with the characteristics described in 10.3. Unless specified otherwise, the + [[Extensible]] internal slot of a built-in object initially has the value true. + + Unless otherwise specified every built-in function and every built-in constructor has the + Function prototype object [...] as the value of its [[Prototype]] internal slot. +features: [Temporal] +---*/ + +assert.sameValue(Object.isExtensible(Temporal.Duration), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.Duration), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.Duration), + Function.prototype, "prototype"); + +assert.sameValue(typeof Temporal.Duration.prototype, + "object", "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/call-builtin.js b/js/src/tests/test262/built-ins/Temporal/Duration/call-builtin.js new file mode 100644 index 0000000000..135a7a996a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/call-builtin.js @@ -0,0 +1,18 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration +description: Constructor should not call built-in functions. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +Number.isFinite = () => { throw new Test262Error("should not call Number.isFinite") }; +Math.sign = () => { throw new Test262Error("should not call Math.sign") }; + +const duration = new Temporal.Duration(1, 1); +TemporalHelpers.assertDuration(duration, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/argument-cast.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/argument-cast.js new file mode 100644 index 0000000000..0144de2127 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/argument-cast.js @@ -0,0 +1,29 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: Strings and objects are supported arguments. +features: [Temporal] +---*/ + +assert.sameValue(Temporal.Duration.compare("PT12H", new Temporal.Duration()), 1, + "first argument string"); +assert.sameValue(Temporal.Duration.compare({ hours: 12 }, new Temporal.Duration()), 1, + "first argument object"); +assert.throws(TypeError, () => Temporal.Duration.compare({ hour: 12 }, new Temporal.Duration()), + "first argument missing property"); + +assert.sameValue(Temporal.Duration.compare(new Temporal.Duration(), "PT12H"), -1, + "second argument string"); +assert.sameValue(Temporal.Duration.compare(new Temporal.Duration(), { hours: 12 }), -1, + "second argument object"); +assert.throws(TypeError, () => Temporal.Duration.compare(new Temporal.Duration(), { hour: 12 }), + "second argument missing property"); + +assert.sameValue(Temporal.Duration.compare({ hours: 12, minute: 5 }, { hours: 12, day: 5 }), 0, + "ignores incorrect properties"); + + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/argument-string-fractional-units-rounding-mode.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/argument-string-fractional-units-rounding-mode.js new file mode 100644 index 0000000000..ce1b172fc6 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/argument-string-fractional-units-rounding-mode.js @@ -0,0 +1,23 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: Strings with fractional duration units are rounded with the correct rounding mode +features: [Temporal] +---*/ + +const expectedPos = new Temporal.Duration(0, 0, 0, 0, 1, 1, 52, 500); +const expectedNeg = new Temporal.Duration(0, 0, 0, 0, -1, -1, -52, -500); + +assert.sameValue(Temporal.Duration.compare("PT1.03125H", expectedPos), 0, + "positive fractional units rounded with correct rounding mode (first argument)"); +assert.sameValue(Temporal.Duration.compare("-PT1.03125H", expectedNeg), 0, + "negative fractional units rounded with correct rounding mode (first argument)"); +assert.sameValue(Temporal.Duration.compare(expectedPos, "PT1.03125H"), 0, + "positive fractional units rounded with correct rounding mode (second argument)"); +assert.sameValue(Temporal.Duration.compare(expectedNeg, "-PT1.03125H"), 0, + "negative fractional units rounded with correct rounding mode (second argument)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/argument-string-negative-fractional-units.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/argument-string-negative-fractional-units.js new file mode 100644 index 0000000000..4b9b40a282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/argument-string-negative-fractional-units.js @@ -0,0 +1,23 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: Strings with fractional duration units are treated with the correct sign +features: [Temporal] +---*/ + +const expectedHours = new Temporal.Duration(0, 0, 0, 0, -24, -34, -4, -404, -442, -800); +const resultHours1 = Temporal.Duration.compare("-PT24.567890123H", expectedHours); +assert.sameValue(resultHours1, 0, "negative fractional hours (first argument)"); +const resultHours2 = Temporal.Duration.compare(expectedHours, "-PT24.567890123H"); +assert.sameValue(resultHours2, 0, "negative fractional hours (second argument)"); + +const expectedMinutes = new Temporal.Duration(0, 0, 0, 0, 0, -1440, -34, -73, -407, -380); +const resultMinutes1 = Temporal.Duration.compare("-PT1440.567890123M", expectedMinutes); +assert.sameValue(resultMinutes1, 0, "negative fractional minutes (first argument)"); +const resultMinutes2 = Temporal.Duration.compare("-PT1440.567890123M", expectedMinutes); +assert.sameValue(resultMinutes2, 0, "negative fractional minutes (second argument)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/basic.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/basic.js new file mode 100644 index 0000000000..b3d1fe8040 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/basic.js @@ -0,0 +1,56 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: Basic comparisons. +features: [Temporal] +---*/ + +const td1pos = new Temporal.Duration(0, 0, 0, 0, 5, 5, 5, 5, 5, 5); +const td2pos = new Temporal.Duration(0, 0, 0, 0, 5, 4, 5, 5, 5, 5); +const td1neg = new Temporal.Duration(0, 0, 0, 0, -5, -5, -5, -5, -5, -5); +const td2neg = new Temporal.Duration(0, 0, 0, 0, -5, -4, -5, -5, -5, -5); +assert.sameValue(Temporal.Duration.compare(td1pos, td1pos), 0, + "time units: equal"); +assert.sameValue(Temporal.Duration.compare(td2pos, td1pos), -1, + "time units: smaller/larger"); +assert.sameValue(Temporal.Duration.compare(td1pos, td2pos), 1, + "time units: larger/smaller"); +assert.sameValue(Temporal.Duration.compare(td1neg, td1neg), 0, + "time units: negative/negative equal"); +assert.sameValue(Temporal.Duration.compare(td2neg, td1neg), 1, + "time units: negative/negative smaller/larger"); +assert.sameValue(Temporal.Duration.compare(td1neg, td2neg), -1, + "time units: negative/negative larger/smaller"); +assert.sameValue(Temporal.Duration.compare(td1neg, td2pos), -1, + "time units: negative/positive"); +assert.sameValue(Temporal.Duration.compare(td1pos, td2neg), 1, + "time units: positive/negative"); + +const dd1pos = new Temporal.Duration(5, 5, 5, 5, 5, 5, 5, 5, 5, 5); +const dd2pos = new Temporal.Duration(5, 5, 5, 5, 5, 4, 5, 5, 5, 5); +const dd1neg = new Temporal.Duration(-5, -5, -5, -5, -5, -5, -5, -5, -5, -5); +const dd2neg = new Temporal.Duration(-5, -5, -5, -5, -5, -4, -5, -5, -5, -5); +const relativeTo = Temporal.PlainDate.from("2017-01-01"); +assert.throws(RangeError, () => Temporal.Duration.compare(dd1pos, dd2pos), + "date units: relativeTo is required"); +assert.sameValue(Temporal.Duration.compare(dd1pos, dd1pos, { relativeTo }), 0, + "date units: equal"); +assert.sameValue(Temporal.Duration.compare(dd2pos, dd1pos, { relativeTo }), -1, + "date units: smaller/larger"); +assert.sameValue(Temporal.Duration.compare(dd1pos, dd2pos, { relativeTo }), 1, + "date units: larger/smaller"); +assert.sameValue(Temporal.Duration.compare(dd1neg, dd1neg, { relativeTo }), 0, + "date units: negative/negative equal"); +assert.sameValue(Temporal.Duration.compare(dd2neg, dd1neg, { relativeTo }), 1, + "date units: negative/negative smaller/larger"); +assert.sameValue(Temporal.Duration.compare(dd1neg, dd2neg, { relativeTo }), -1, + "date units: negative/negative larger/smaller"); +assert.sameValue(Temporal.Duration.compare(dd1neg, dd2pos, { relativeTo }), -1, + "date units: negative/positive"); +assert.sameValue(Temporal.Duration.compare(dd1pos, dd2neg, { relativeTo }), 1, + "date units: positive/negative"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/builtin.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/builtin.js new file mode 100644 index 0000000000..b7563288a4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/builtin.js @@ -0,0 +1,33 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: Tests that Temporal.Duration.compare meets the requirements for built-in objects +info: | + Built-in functions that are not constructors do not have a "prototype" property unless + otherwise specified in the description of a particular function. + + Unless specified otherwise, a built-in object that is callable as a function is a built-in + function object with the characteristics described in 10.3. Unless specified otherwise, the + [[Extensible]] internal slot of a built-in object initially has the value true. + + Unless otherwise specified every built-in function and every built-in constructor has the + Function prototype object [...] as the value of its [[Prototype]] internal slot. +features: [Temporal] +---*/ + +assert.sameValue(Object.isExtensible(Temporal.Duration.compare), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.Duration.compare), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.Duration.compare), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.Duration.compare.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/calendar-dateadd-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/calendar-dateadd-called-with-options-undefined.js new file mode 100644 index 0000000000..1e1127233c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/calendar-dateadd-called-with-options-undefined.js @@ -0,0 +1,25 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: > + BuiltinTimeZoneGetInstantFor calls Calendar.dateAdd with undefined as the + options value +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarDateAddUndefinedOptions(); +const timeZone = TemporalHelpers.oneShiftTimeZone(new Temporal.Instant(0n), 3600e9); +const relativeTo = new Temporal.ZonedDateTime(0n, timeZone, calendar); + +const duration1 = new Temporal.Duration(0, 0, 1); +const duration2 = new Temporal.Duration(0, 0, 1); +Temporal.Duration.compare(duration1, duration2, { relativeTo }); +assert.sameValue(calendar.dateAddCallCount, 4); +// one call in CalculateOffsetShift for each duration argument, plus one in +// UnbalanceDurationRelative for each duration argument + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/calendar-dateadd-called-with-plaindate-instance.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/calendar-dateadd-called-with-plaindate-instance.js new file mode 100644 index 0000000000..5757d639f3 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/calendar-dateadd-called-with-plaindate-instance.js @@ -0,0 +1,20 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: > + relativeTo parameters that are not ZonedDateTime or undefined, are always + converted to PlainDate for observable calendar calls +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarDateAddPlainDateInstance(); +const relativeTo = new Temporal.PlainDate(2000, 1, 1, calendar); +calendar.specificPlainDate = relativeTo; +Temporal.Duration.compare(new Temporal.Duration(1, 1, 1, 1), new Temporal.Duration(1, 1, 1), { relativeTo }); +assert(calendar.dateAddCallCount > 0, "assertions in calendar.dateAdd() should have been tested"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/calendar-fields-iterable.js new file mode 100644 index 0000000000..91184fe4bd --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/calendar-fields-iterable.js @@ -0,0 +1,41 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: Verify the result of calendar.fields() is treated correctly. +info: | + sec-temporal.duration.compare step 4: + 4. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_). + sec-temporal-torelativetemporalobject step 4.c: + c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »). + sec-temporal-calendarfields step 4: + 4. Let _result_ be ? IterableToList(_fieldsArray_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "day", + "hour", + "microsecond", + "millisecond", + "minute", + "month", + "monthCode", + "nanosecond", + "second", + "year", +]; + +const calendar = TemporalHelpers.calendarFieldsIterable(); +const duration1 = new Temporal.Duration(1); +const duration2 = new Temporal.Duration(0, 12); +Temporal.Duration.compare(duration1, duration2, { relativeTo: { year: 2000, month: 1, day: 1, calendar } }); + +assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once"); +assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args"); +assert(calendar.iteratorExhausted[0], "iterated through the whole iterable"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/calendar-possibly-required.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/calendar-possibly-required.js new file mode 100644 index 0000000000..2931cdbdfc --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/calendar-possibly-required.js @@ -0,0 +1,51 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: relativeTo argument needed if days = 0 but years/months/weeks non-zero +features: [Temporal] +---*/ +const duration1 = new Temporal.Duration(1); +const duration2 = new Temporal.Duration(0, 12); +const duration3 = new Temporal.Duration(0, 0, 5); +const duration4 = new Temporal.Duration(0, 0, 0, 42); +const relativeTo = new Temporal.PlainDate(2021, 12, 15); +assert.throws( + RangeError, + () => { Temporal.Duration.compare(duration1, duration1); }, + "cannot compare Duration values without relativeTo if year is non-zero" +); +assert.sameValue(0, + Temporal.Duration.compare(duration1, duration1, { relativeTo }), + "compare succeeds for year-only Duration provided relativeTo is supplied"); +assert.throws( + RangeError, + () => { Temporal.Duration.compare(duration2, duration2); }, + "cannot compare Duration values without relativeTo if month is non-zero" +); +assert.sameValue(0, + Temporal.Duration.compare(duration2, duration2, { relativeTo }), + "compare succeeds for year-and-month Duration provided relativeTo is supplied"); +assert.throws( + RangeError, + () => { Temporal.Duration.compare(duration3, duration3); }, + "cannot compare Duration values without relativeTo if week is non-zero" +); +assert.sameValue(0, + Temporal.Duration.compare(duration3, duration3, { relativeTo }), + "compare succeeds for year-and-month-and-week Duration provided relativeTo is supplied" +); + +assert.sameValue(0, + Temporal.Duration.compare(duration4, duration4), + "compare succeeds for zero year-month-week non-zero day Duration even without relativeTo"); + +// Double-check that the comparison also works with a relative-to argument +assert.sameValue(0, + Temporal.Duration.compare(duration4, duration4, { relativeTo }), + "compare succeeds for zero year-month-week non-zero day Duration with relativeTo" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/calendar-temporal-object.js new file mode 100644 index 0000000000..1a3fec8431 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/calendar-temporal-object.js @@ -0,0 +1,30 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots +info: | + sec-temporal.duration.compare step 4: + 4. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_). + sec-temporal-torelativetemporalobject step 4.b: + b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_). + sec-temporal-gettemporalcalendarwithisodefault step 2: + 2. Return ? ToTemporalCalendarWithISODefault(_calendar_). + sec-temporal-totemporalcalendarwithisodefault step 2: + 3. Return ? ToTemporalCalendar(_temporalCalendarLike_). + sec-temporal-totemporalcalendar step 1.a: + a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then + i. Return _temporalCalendarLike_.[[Calendar]]. +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => { + const duration1 = new Temporal.Duration(1); + const duration2 = new Temporal.Duration(0, 12); + Temporal.Duration.compare(duration1, duration2, { relativeTo: { year: 2000, month: 1, day: 1, calendar: temporalObject } }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/length.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/length.js new file mode 100644 index 0000000000..c803a55f43 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/length.js @@ -0,0 +1,28 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: Temporal.Duration.compare.length is 2 +info: | + Every built-in function object, including constructors, has a "length" property whose value is + an integer. Unless otherwise specified, this value is equal to the largest number of named + arguments shown in the subclause headings for the function description. Optional parameters + (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form + «...name») are not included in the default argument count. + + Unless otherwise specified, the "length" property of a built-in function object has the + attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +verifyProperty(Temporal.Duration.compare, "length", { + value: 2, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/name.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/name.js new file mode 100644 index 0000000000..7d192b02fe --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/name.js @@ -0,0 +1,26 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: Temporal.Duration.compare.name is "compare". +info: | + Every built-in function object, including constructors, that is not identified as an anonymous + function has a "name" property whose value is a String. Unless otherwise specified, this value + is the name that is given to the function in this specification. + + Unless otherwise specified, the "name" property of a built-in function object, if it exists, + has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +verifyProperty(Temporal.Duration.compare, "name", { + value: "compare", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/not-a-constructor.js new file mode 100644 index 0000000000..59fc9cd6d9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/not-a-constructor.js @@ -0,0 +1,23 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: Temporal.Duration.compare does not implement [[Construct]], is not new-able +info: | + Built-in function objects that are not identified as constructors do not implement the + [[Construct]] internal method unless otherwise specified in the description of a particular + function. +includes: [isConstructor.js] +features: [Reflect.construct, Temporal] +---*/ + +assert.throws(TypeError, () => { + new Temporal.Duration.compare(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.Duration.compare), false, + "isConstructor(Temporal.Duration.compare)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/options-object.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/options-object.js new file mode 100644 index 0000000000..f917116457 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/options-object.js @@ -0,0 +1,21 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.compare +description: Empty object may be used as options +features: [Temporal] +---*/ + +assert.sameValue( + Temporal.Duration.compare({ hours: 1 }, { minutes: 60 }, {}), 0, + "options may be an empty plain object" +); + +assert.sameValue( + Temporal.Duration.compare({ hours: 1 }, { minutes:60 }, () => {}), 0, + "options may be an empty function object" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/options-undefined.js new file mode 100644 index 0000000000..2a611c1602 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/options-undefined.js @@ -0,0 +1,16 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: Verify that undefined options are handled correctly. +features: [Temporal] +---*/ + +const duration1 = new Temporal.Duration(1); +const duration2 = new Temporal.Duration(0, 12); +assert.throws(RangeError, () => Temporal.Duration.compare(duration1, duration2), "default relativeTo is undefined"); +assert.throws(RangeError, () => Temporal.Duration.compare(duration1, duration2, undefined), "default relativeTo is undefined"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/options-wrong-type.js new file mode 100644 index 0000000000..727bdc6ac3 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/options-wrong-type.js @@ -0,0 +1,25 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: TypeError thrown when options argument is a primitive +features: [BigInt, Symbol, Temporal] +---*/ + +const badOptions = [ + null, + true, + "some string", + Symbol(), + 1, + 2n, +]; + +for (const value of badOptions) { + assert.throws(TypeError, () => Temporal.Duration.compare({ hours: 1 }, { minutes: 60 }, value), + `TypeError on wrong options type ${typeof value}`); +}; + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/order-of-operations.js new file mode 100644 index 0000000000..ccb19e03b7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/order-of-operations.js @@ -0,0 +1,278 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: Properties on objects passed to compare() are accessed in the correct order +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + // ToTemporalDuration on first argument + "get one.days", + "get one.days.valueOf", + "call one.days.valueOf", + "get one.hours", + "get one.hours.valueOf", + "call one.hours.valueOf", + "get one.microseconds", + "get one.microseconds.valueOf", + "call one.microseconds.valueOf", + "get one.milliseconds", + "get one.milliseconds.valueOf", + "call one.milliseconds.valueOf", + "get one.minutes", + "get one.minutes.valueOf", + "call one.minutes.valueOf", + "get one.months", + "get one.months.valueOf", + "call one.months.valueOf", + "get one.nanoseconds", + "get one.nanoseconds.valueOf", + "call one.nanoseconds.valueOf", + "get one.seconds", + "get one.seconds.valueOf", + "call one.seconds.valueOf", + "get one.weeks", + "get one.weeks.valueOf", + "call one.weeks.valueOf", + "get one.years", + "get one.years.valueOf", + "call one.years.valueOf", + // ToTemporalDuration on second argument + "get two.days", + "get two.days.valueOf", + "call two.days.valueOf", + "get two.hours", + "get two.hours.valueOf", + "call two.hours.valueOf", + "get two.microseconds", + "get two.microseconds.valueOf", + "call two.microseconds.valueOf", + "get two.milliseconds", + "get two.milliseconds.valueOf", + "call two.milliseconds.valueOf", + "get two.minutes", + "get two.minutes.valueOf", + "call two.minutes.valueOf", + "get two.months", + "get two.months.valueOf", + "call two.months.valueOf", + "get two.nanoseconds", + "get two.nanoseconds.valueOf", + "call two.nanoseconds.valueOf", + "get two.seconds", + "get two.seconds.valueOf", + "call two.seconds.valueOf", + "get two.weeks", + "get two.weeks.valueOf", + "call two.weeks.valueOf", + "get two.years", + "get two.years.valueOf", + "call two.years.valueOf", + // ToRelativeTemporalObject + "get options.relativeTo", +]; +const actual = []; + +// basic order of observable operations with no relativeTo +Temporal.Duration.compare( + createDurationPropertyBagObserver("one", 0, 0, 0, 7), + createDurationPropertyBagObserver("two", 0, 0, 0, 6), + createOptionsObserver(undefined) +); +assert.compareArray(actual, expected, "order of operations"); +actual.splice(0); // clear + +const expectedOpsForPlainRelativeTo = expected.concat([ + // ToRelativeTemporalObject + "get options.relativeTo.calendar", + "has options.relativeTo.calendar.calendar", + "get options.relativeTo.calendar.fields", + "call options.relativeTo.calendar.fields", + "get options.relativeTo.day", + "get options.relativeTo.day.valueOf", + "call options.relativeTo.day.valueOf", + "get options.relativeTo.hour", + "get options.relativeTo.microsecond", + "get options.relativeTo.millisecond", + "get options.relativeTo.minute", + "get options.relativeTo.month", + "get options.relativeTo.month.valueOf", + "call options.relativeTo.month.valueOf", + "get options.relativeTo.monthCode", + "get options.relativeTo.monthCode.toString", + "call options.relativeTo.monthCode.toString", + "get options.relativeTo.nanosecond", + "get options.relativeTo.offset", + "get options.relativeTo.second", + "get options.relativeTo.timeZone", + "get options.relativeTo.year", + "get options.relativeTo.year.valueOf", + "call options.relativeTo.year.valueOf", + "get options.relativeTo.calendar.dateFromFields", + "call options.relativeTo.calendar.dateFromFields", +]); + +const plainRelativeTo = TemporalHelpers.propertyBagObserver(actual, { + year: 2001, + month: 5, + monthCode: "M05", + day: 2, + calendar: TemporalHelpers.calendarObserver(actual, "options.relativeTo.calendar"), +}, "options.relativeTo"); + +function createOptionsObserver(relativeTo = undefined) { + return TemporalHelpers.propertyBagObserver(actual, { relativeTo }, "options"); +} + +function createDurationPropertyBagObserver(name, y = 0, mon = 0, w = 0, d = 0, h = 0, min = 0, s = 0, ms = 0, µs = 0, ns = 0) { + return TemporalHelpers.propertyBagObserver(actual, { + years: y, + months: mon, + weeks: w, + days: d, + hours: h, + minutes: min, + seconds: s, + milliseconds: ms, + microseconds: µs, + nanoseconds: ns, + }, name); +} + +// order of observable operations with plain relativeTo and without calendar units +Temporal.Duration.compare( + createDurationPropertyBagObserver("one", 0, 0, 0, 7), + createDurationPropertyBagObserver("two", 0, 0, 0, 6), + createOptionsObserver(plainRelativeTo) +); +assert.compareArray(actual, expectedOpsForPlainRelativeTo, "order of operations with PlainDate relativeTo and no calendar units"); +actual.splice(0); // clear + +const expectedOpsForZonedRelativeTo = expected.concat([ + // ToRelativeTemporalObject + "get options.relativeTo.calendar", + "has options.relativeTo.calendar.calendar", + "get options.relativeTo.calendar.fields", + "call options.relativeTo.calendar.fields", + "get options.relativeTo.day", + "get options.relativeTo.day.valueOf", + "call options.relativeTo.day.valueOf", + "get options.relativeTo.hour", + "get options.relativeTo.hour.valueOf", + "call options.relativeTo.hour.valueOf", + "get options.relativeTo.microsecond", + "get options.relativeTo.microsecond.valueOf", + "call options.relativeTo.microsecond.valueOf", + "get options.relativeTo.millisecond", + "get options.relativeTo.millisecond.valueOf", + "call options.relativeTo.millisecond.valueOf", + "get options.relativeTo.minute", + "get options.relativeTo.minute.valueOf", + "call options.relativeTo.minute.valueOf", + "get options.relativeTo.month", + "get options.relativeTo.month.valueOf", + "call options.relativeTo.month.valueOf", + "get options.relativeTo.monthCode", + "get options.relativeTo.monthCode.toString", + "call options.relativeTo.monthCode.toString", + "get options.relativeTo.nanosecond", + "get options.relativeTo.nanosecond.valueOf", + "call options.relativeTo.nanosecond.valueOf", + "get options.relativeTo.offset", + "get options.relativeTo.offset.toString", + "call options.relativeTo.offset.toString", + "get options.relativeTo.second", + "get options.relativeTo.second.valueOf", + "call options.relativeTo.second.valueOf", + "get options.relativeTo.timeZone", + "get options.relativeTo.year", + "get options.relativeTo.year.valueOf", + "call options.relativeTo.year.valueOf", + "get options.relativeTo.calendar.dateFromFields", + "call options.relativeTo.calendar.dateFromFields", + "has options.relativeTo.timeZone.timeZone", + "get options.relativeTo.timeZone.getPossibleInstantsFor", + "call options.relativeTo.timeZone.getPossibleInstantsFor", + "get options.relativeTo.timeZone.getOffsetNanosecondsFor", + "call options.relativeTo.timeZone.getOffsetNanosecondsFor", + // CalculateOffsetShift on first argument + "get options.relativeTo.timeZone.getOffsetNanosecondsFor", + "call options.relativeTo.timeZone.getOffsetNanosecondsFor", + // ...in AddZonedDateTime + "get options.relativeTo.timeZone.getOffsetNanosecondsFor", + "call options.relativeTo.timeZone.getOffsetNanosecondsFor", + "get options.relativeTo.calendar.dateAdd", + "call options.relativeTo.calendar.dateAdd", + "get options.relativeTo.timeZone.getPossibleInstantsFor", + "call options.relativeTo.timeZone.getPossibleInstantsFor", + // ...done with AddZonedDateTime + "get options.relativeTo.timeZone.getOffsetNanosecondsFor", + "call options.relativeTo.timeZone.getOffsetNanosecondsFor", + // CalculateOffsetShift on second argument + "get options.relativeTo.timeZone.getOffsetNanosecondsFor", + "call options.relativeTo.timeZone.getOffsetNanosecondsFor", + "get options.relativeTo.timeZone.getOffsetNanosecondsFor", + "call options.relativeTo.timeZone.getOffsetNanosecondsFor", + "get options.relativeTo.calendar.dateAdd", + "call options.relativeTo.calendar.dateAdd", + "get options.relativeTo.timeZone.getPossibleInstantsFor", + "call options.relativeTo.timeZone.getPossibleInstantsFor", + "get options.relativeTo.timeZone.getOffsetNanosecondsFor", + "call options.relativeTo.timeZone.getOffsetNanosecondsFor", +]); + +const zonedRelativeTo = TemporalHelpers.propertyBagObserver(actual, { + year: 2001, + month: 5, + monthCode: "M05", + day: 2, + hour: 6, + minute: 54, + second: 32, + millisecond: 987, + microsecond: 654, + nanosecond: 321, + offset: "+00:00", + calendar: TemporalHelpers.calendarObserver(actual, "options.relativeTo.calendar"), + timeZone: TemporalHelpers.timeZoneObserver(actual, "options.relativeTo.timeZone"), +}, "options.relativeTo"); + +// order of observable operations with zoned relativeTo and without calendar units +Temporal.Duration.compare( + createDurationPropertyBagObserver("one", 0, 0, 0, 7), + createDurationPropertyBagObserver("two", 0, 0, 0, 6), + createOptionsObserver(zonedRelativeTo) +); +assert.compareArray(actual, expectedOpsForZonedRelativeTo, "order of operations with ZonedDateTime relativeTo and no calendar units"); +actual.splice(0); // clear + +// code path through UnbalanceDurationRelative that balances higher units down +// to days: +const expectedOpsForDayBalancing = expectedOpsForZonedRelativeTo.concat([ + // UnbalanceDurationRelative + "get options.relativeTo.timeZone.getOffsetNanosecondsFor", // 7.a ToTemporalDate + "call options.relativeTo.timeZone.getOffsetNanosecondsFor", + "get options.relativeTo.calendar.dateAdd", // 11.a.ii + "call options.relativeTo.calendar.dateAdd", // 11.a.iii.1 MoveRelativeDate + "call options.relativeTo.calendar.dateAdd", // 11.a.iv.1 MoveRelativeDate + "call options.relativeTo.calendar.dateAdd", // 11.a.v.1 MoveRelativeDate + // UnbalanceDurationRelative again for the second argument: + "get options.relativeTo.timeZone.getOffsetNanosecondsFor", // 7.a ToTemporalDate + "call options.relativeTo.timeZone.getOffsetNanosecondsFor", + "get options.relativeTo.calendar.dateAdd", // 11.a.ii + "call options.relativeTo.calendar.dateAdd", // 11.a.iii.1 MoveRelativeDate + "call options.relativeTo.calendar.dateAdd", // 11.a.iv.1 MoveRelativeDate + "call options.relativeTo.calendar.dateAdd", // 11.a.v.1 MoveRelativeDate +]); +Temporal.Duration.compare( + createDurationPropertyBagObserver("one", 1, 1, 1), + createDurationPropertyBagObserver("two", 1, 1, 1, 1), + createOptionsObserver(zonedRelativeTo) +); +assert.compareArray(actual, expectedOpsForDayBalancing, "order of operations with calendar units"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/precision-exact-mathematical-values-1.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/precision-exact-mathematical-values-1.js new file mode 100644 index 0000000000..c40f283c14 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/precision-exact-mathematical-values-1.js @@ -0,0 +1,71 @@ +// |reftest| skip -- Temporal is not supported +// 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.compare +description: > + Duration components are precise mathematical integers. +info: | + Temporal.Duration.compare ( one, two [ , options ] ) + + ... + 7. If any of one.[[Years]], two.[[Years]], one.[[Months]], two.[[Months]], one.[[Weeks]], or + two.[[Weeks]] are not 0, then + a. Let unbalanceResult1 be ? UnbalanceDurationRelative(one.[[Years]], one.[[Months]], + one.[[Weeks]], one.[[Days]], "day", relativeTo). + ... + 9. Let ns1 be ! TotalDurationNanoseconds(days1, one.[[Hours]], one.[[Minutes]], one.[[Seconds]], + one.[[Milliseconds]], one.[[Microseconds]], one.[[Nanoseconds]], shift1). + 10. Let ns2 be ! TotalDurationNanoseconds(days2, two.[[Hours]], two.[[Minutes]], two.[[Seconds]], + two.[[Milliseconds]], two.[[Microseconds]], two.[[Nanoseconds]], shift2). + 11. If ns1 > ns2, return 1𝔽. + 12. If ns1 < ns2, return -1𝔽. + 13. Return +0𝔽. + + UnbalanceDurationRelative ( years, months, weeks, days, largestUnit, relativeTo ) + + ... + 11. Else, + a. If any of years, months, and weeks are not zero, then + ... + iv. Repeat, while weeks ≠ 0, + 1. Let moveResult be ? MoveRelativeDate(calendar, relativeTo, oneWeek). + 2. Set relativeTo to moveResult.[[RelativeTo]]. + 3. Set days to days + moveResult.[[Days]]. + 4. Set weeks to weeks - sign. + 12. Return ? CreateDateDurationRecord(years, months, weeks, days). +features: [Temporal] +---*/ + +var one = Temporal.Duration.from({ + days: Number.MAX_SAFE_INTEGER, + weeks: 3, +}); + +var two = Temporal.Duration.from({ + days: Number.MAX_SAFE_INTEGER + 3, + weeks: 0, +}); + +var cal = new class extends Temporal.Calendar { + dateAdd(date, duration, options) { + // Add one day when one week was requested. + if (duration.toString() === "P1W") { + return super.dateAdd(date, "P1D", options); + } + + // Only expect to add one week. + throw new Test262Error("dateAdd called with unexpected value"); + } +}("iso8601"); + +var pd = new Temporal.PlainDate(1970, 1, 1, cal); + +// |Number.MAX_SAFE_INTEGER + 1 + 1 + 1| is unequal to |Number.MAX_SAFE_INTEGER + 3| +// when the addition is performed using IEEE-754 semantics. But for compare we have +// to ensure exact mathematical computations are performed. + +assert.sameValue(Temporal.Duration.compare(one, two, {relativeTo: pd}), 0); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/precision-exact-mathematical-values-2.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/precision-exact-mathematical-values-2.js new file mode 100644 index 0000000000..ef92cac371 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/precision-exact-mathematical-values-2.js @@ -0,0 +1,71 @@ +// |reftest| skip -- Temporal is not supported +// 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.compare +description: > + Duration components are precise mathematical integers. +info: | + Temporal.Duration.compare ( one, two [ , options ] ) + + ... + 7. If any of one.[[Years]], two.[[Years]], one.[[Months]], two.[[Months]], one.[[Weeks]], or + two.[[Weeks]] are not 0, then + a. Let unbalanceResult1 be ? UnbalanceDurationRelative(one.[[Years]], one.[[Months]], + one.[[Weeks]], one.[[Days]], "day", relativeTo). + ... + 9. Let ns1 be ! TotalDurationNanoseconds(days1, one.[[Hours]], one.[[Minutes]], one.[[Seconds]], + one.[[Milliseconds]], one.[[Microseconds]], one.[[Nanoseconds]], shift1). + 10. Let ns2 be ! TotalDurationNanoseconds(days2, two.[[Hours]], two.[[Minutes]], two.[[Seconds]], + two.[[Milliseconds]], two.[[Microseconds]], two.[[Nanoseconds]], shift2). + 11. If ns1 > ns2, return 1𝔽. + 12. If ns1 < ns2, return -1𝔽. + 13. Return +0𝔽. + + UnbalanceDurationRelative ( years, months, weeks, days, largestUnit, relativeTo ) + + ... + 11. Else, + a. If any of years, months, and weeks are not zero, then + ... + iv. Repeat, while weeks ≠ 0, + 1. Let moveResult be ? MoveRelativeDate(calendar, relativeTo, oneWeek). + 2. Set relativeTo to moveResult.[[RelativeTo]]. + 3. Set days to days + moveResult.[[Days]]. + 4. Set weeks to weeks - sign. + 12. Return ? CreateDateDurationRecord(years, months, weeks, days). +features: [Temporal] +---*/ + +var one = Temporal.Duration.from({ + days: Number.MAX_SAFE_INTEGER, + weeks: 3, +}); + +var two = Temporal.Duration.from({ + days: Number.MAX_SAFE_INTEGER + 3, + weeks: 0, +}); + +var cal = new class extends Temporal.Calendar { + dateAdd(date, duration, options) { + // Add one day when one week was requested. + if (duration.toString() === "P1W") { + return super.dateAdd(date, "P1D", options); + } + + // Use zero duration to avoid a RangeError during CalculateOffsetShift. + return super.dateAdd(date, "PT0S", options); + } +}("iso8601"); + +var zdt = new Temporal.ZonedDateTime(0n, "UTC", cal); + +// |Number.MAX_SAFE_INTEGER + 1 + 1 + 1| is unequal to |Number.MAX_SAFE_INTEGER + 3| +// when the addition is performed using IEEE-754 semantics. But for compare we have +// to ensure exact mathematical computations are performed. + +assert.sameValue(Temporal.Duration.compare(one, two, {relativeTo: zdt}), 0); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/prop-desc.js new file mode 100644 index 0000000000..1d547f6592 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: The "compare" property of Temporal.Duration +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.Duration.compare, + "function", + "`typeof Duration.compare` is `function`" +); + +verifyProperty(Temporal.Duration, "compare", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/read-time-fields-before-datefromfields.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/read-time-fields-before-datefromfields.js new file mode 100644 index 0000000000..e4572a6406 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/read-time-fields-before-datefromfields.js @@ -0,0 +1,25 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: The time fields are read from the object before being passed to dateFromFields(). +info: | + sec-temporal.duration.compare step 4: + 4. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_). + sec-temporal-torelativetemporalobject step 4.g: + g. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_). + sec-temporal-interprettemporaldatetimefields steps 1–2: + 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_). + 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarMakeInvalidGettersTime(); +const duration1 = new Temporal.Duration(1); +const duration2 = new Temporal.Duration(0, 12); +Temporal.Duration.compare(duration1, duration2, { relativeTo: { year: 2000, month: 1, day: 1, calendar } }); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-hour.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-hour.js new file mode 100644 index 0000000000..cb107450bf --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-hour.js @@ -0,0 +1,44 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: relativeTo with hours. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const oneDay = new Temporal.Duration(0, 0, 0, 1); +const hours24 = new Temporal.Duration(0, 0, 0, 0, 24); +assert.sameValue(Temporal.Duration.compare(oneDay, hours24), + 0, + "relativeTo not required for days"); +assert.sameValue( + Temporal.Duration.compare(oneDay, hours24, { relativeTo: Temporal.PlainDate.from("2017-01-01") }), + 0, + "relativeTo does not affect days if PlainDate"); +assert.sameValue(Temporal.Duration.compare(oneDay, hours24, { relativeTo: "2019-11-03" }), + 0, + "casts relativeTo to PlainDate from string"); +assert.sameValue(Temporal.Duration.compare(oneDay, hours24, { relativeTo: { year: 2019, month: 11, day: 3 } }), + 0, + "casts relativeTo to PlainDate from object"); + +const timeZone = TemporalHelpers.springForwardFallBackTimeZone(); +assert.sameValue( + Temporal.Duration.compare(oneDay, hours24, { relativeTo: new Temporal.ZonedDateTime(0n, timeZone) }), + 0, + 'relativeTo does not affect days if ZonedDateTime, and duration encompasses no DST change'); +assert.sameValue( + Temporal.Duration.compare(oneDay, hours24, { relativeTo: new Temporal.ZonedDateTime(972802800_000_000_000n, timeZone) }), + 1, + 'relativeTo does affect days if ZonedDateTime, and duration encompasses DST change'); +assert.sameValue( + Temporal.Duration.compare(oneDay, hours24, { + relativeTo: { year: 2000, month: 10, day: 29, timeZone } + }), + 1, + 'casts relativeTo to ZonedDateTime from object'); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-month.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-month.js new file mode 100644 index 0000000000..0ede90da7c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-month.js @@ -0,0 +1,20 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: relativeTo with months. +features: [Temporal] +---*/ + +const oneMonth = new Temporal.Duration(0, 1); +const days30 = new Temporal.Duration(0, 0, 0, 30); +assert.sameValue( + Temporal.Duration.compare(oneMonth, days30, { relativeTo: Temporal.PlainDate.from("2018-04-01") }), 0); +assert.sameValue( + Temporal.Duration.compare(oneMonth, days30, { relativeTo: Temporal.PlainDate.from("2018-03-01") }), 1); +assert.sameValue( + Temporal.Duration.compare(oneMonth, days30, { relativeTo: Temporal.PlainDate.from("2018-02-01") }), -1); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-infinity-throws-rangeerror.js new file mode 100644 index 0000000000..a069fd2587 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-infinity-throws-rangeerror.js @@ -0,0 +1,27 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Throws if any value in the relativeTo property bag is Infinity or -Infinity +esid: sec-temporal.duration.compare +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const duration1 = new Temporal.Duration(0, 0, 0, 1); +const duration2 = new Temporal.Duration(0, 0, 0, 0, 24); +const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321 }; + +[Infinity, -Infinity].forEach((inf) => { + ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => { + assert.throws(RangeError, () => Temporal.Duration.compare(duration1, duration2, { relativeTo: { ...base, [prop]: inf } }), `${prop} property cannot be ${inf} in relativeTo`); + + const calls = []; + const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop); + assert.throws(RangeError, () => Temporal.Duration.compare(duration1, duration2, { relativeTo: { ...base, [prop]: obj } })); + assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value"); + }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-invalid-offset-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-invalid-offset-string.js new file mode 100644 index 0000000000..7ef0d8a1b8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-invalid-offset-string.js @@ -0,0 +1,26 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: relativeTo property bag with offset property is rejected if offset is in the wrong format +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const d1 = new Temporal.Duration(0, 1, 0, 280); +const d2 = new Temporal.Duration(0, 1, 0, 281); + +const badOffsets = [ + "00:00", // missing sign + "+0", // too short + "-000:00", // too long + 0, // converts to a string that is invalid +]; +badOffsets.forEach((offset) => { + const relativeTo = { year: 2021, month: 10, day: 28, offset, timeZone }; + assert.throws(RangeError, () => Temporal.Duration.compare(d1, d2, { relativeTo }), `"${offset} is not a valid offset string`); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-invalid.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-invalid.js new file mode 100644 index 0000000000..b2f74d2f94 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-invalid.js @@ -0,0 +1,17 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: Throws if a value in the relativeTo property bag is missing. +features: [Temporal] +---*/ + +const oneDay = new Temporal.Duration(0, 0, 0, 1); +const hours24 = new Temporal.Duration(0, 0, 0, 0, 24); +assert.throws(TypeError, () => Temporal.Duration.compare(oneDay, hours24, { relativeTo: { month: 11, day: 3 } }), "missing year"); +assert.throws(TypeError, () => Temporal.Duration.compare(oneDay, hours24, { relativeTo: { year: 2019, month: 11 } }), "missing day"); +assert.throws(TypeError, () => Temporal.Duration.compare(oneDay, hours24, { relativeTo: { year: 2019, day: 3 } }), "missing month"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..d9f57ab527 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,19 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const duration1 = new Temporal.Duration(1); + const duration2 = new Temporal.Duration(1, 1); + assert.throws(RangeError, () => Temporal.Duration.compare(duration1, duration2, { relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..d13dce4000 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,22 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-temporal.duration.compare +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach(notCallable => { + const timeZone = new Temporal.TimeZone("UTC"); + const duration1 = new Temporal.Duration(1); + const duration2 = new Temporal.Duration(1, 1); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => Temporal.Duration.compare(duration1, duration2, { relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..b6956fd4d3 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,19 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_001, 86400_000_000_001, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const duration1 = new Temporal.Duration(1); + const duration2 = new Temporal.Duration(1, 1); + assert.throws(RangeError, () => Temporal.Duration.compare(duration1, duration2, { relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..98b7bbd6e9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,28 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const duration1 = new Temporal.Duration(1); + const duration2 = new Temporal.Duration(1, 1); + assert.throws(TypeError, () => Temporal.Duration.compare(duration1, duration2, { relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-instance-does-not-get-timeZone-property.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-instance-does-not-get-timeZone-property.js new file mode 100644 index 0000000000..e72308271c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-instance-does-not-get-timeZone-property.js @@ -0,0 +1,23 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: > + A Temporal.TimeZone instance passed to compare() does not have its + 'timeZone' property observably checked +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +Object.defineProperty(timeZone, "timeZone", { + get() { + throw new Test262Error("timeZone.timeZone should not be accessed"); + }, +}); + +Temporal.Duration.compare(new Temporal.Duration(), new Temporal.Duration(), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }); +Temporal.Duration.compare(new Temporal.Duration(), new Temporal.Duration(), { relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-string-datetime.js new file mode 100644 index 0000000000..cd16bed200 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-string-datetime.js @@ -0,0 +1,37 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: Conversion of ISO date-time strings to Temporal.TimeZone instances +features: [Temporal] +---*/ + +let timeZone = "2021-08-19T17:30"; +assert.throws(RangeError, () => Temporal.Duration.compare(new Temporal.Duration(), new Temporal.Duration(), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), "bare date-time string is not a time zone"); +assert.throws(RangeError, () => Temporal.Duration.compare(new Temporal.Duration(), new Temporal.Duration(), { relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }), "bare date-time string is not a time zone"); + +// The following are all valid strings so should not throw: + +[ + "2021-08-19T17:30Z", + "2021-08-19T1730Z", + "2021-08-19T17:30-07:00", + "2021-08-19T1730-07:00", + "2021-08-19T17:30-0700", + "2021-08-19T1730-0700", + "2021-08-19T17:30[UTC]", + "2021-08-19T1730[UTC]", + "2021-08-19T17:30Z[UTC]", + "2021-08-19T1730Z[UTC]", + "2021-08-19T17:30-07:00[UTC]", + "2021-08-19T1730-07:00[UTC]", + "2021-08-19T17:30-0700[UTC]", + "2021-08-19T1730-0700[UTC]", +].forEach((timeZone) => { + Temporal.Duration.compare(new Temporal.Duration(), new Temporal.Duration(), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }); + Temporal.Duration.compare(new Temporal.Duration(), new Temporal.Duration(), { relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-string-leap-second.js new file mode 100644 index 0000000000..bef606c98d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-string-leap-second.js @@ -0,0 +1,23 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: Leap second is a valid ISO string for TimeZone +features: [Temporal] +---*/ + +let timeZone = "2016-12-31T23:59:60+00:00[UTC]"; + +// A string with a leap second is a valid ISO string, so the following two +// operations should not throw + +Temporal.Duration.compare(new Temporal.Duration(), new Temporal.Duration(), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }); +Temporal.Duration.compare(new Temporal.Duration(), new Temporal.Duration(), { relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }); + +timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]"; +assert.throws(RangeError, () => Temporal.Duration.compare(new Temporal.Duration(), new Temporal.Duration(), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), "leap second in time zone name not valid"); +assert.throws(RangeError, () => Temporal.Duration.compare(new Temporal.Duration(), new Temporal.Duration(), { relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }), "leap second in time zone name not valid (nested property)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-string-year-zero.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-string-year-zero.js new file mode 100644 index 0000000000..021476c786 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-string-year-zero.js @@ -0,0 +1,28 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: Negative zero, as an extended year, is rejected +features: [Temporal, arrow-function] +---*/ + +const invalidStrings = [ + "-000000-10-31T17:45Z", + "-000000-10-31T17:45+00:00[UTC]", +]; +invalidStrings.forEach((timeZone) => { + assert.throws( + RangeError, + () => Temporal.Duration.compare(new Temporal.Duration(), new Temporal.Duration(), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), + "reject minus zero as extended year" + ); + assert.throws( + RangeError, + () => Temporal.Duration.compare(new Temporal.Duration(), new Temporal.Duration(), { relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }), + "reject minus zero as extended year (nested property)" + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-string.js new file mode 100644 index 0000000000..1b761c3f8e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-string.js @@ -0,0 +1,17 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: Time zone IDs are valid input for a time zone +features: [Temporal] +---*/ + +// The following are all valid strings so should not throw: + +["UTC", "+01:00"].forEach((timeZone) => { + Temporal.Duration.compare(new Temporal.Duration(), new Temporal.Duration(), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-wrong-type.js new file mode 100644 index 0000000000..dfe8e8d6a1 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-propertybag-timezone-wrong-type.js @@ -0,0 +1,40 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: > + Appropriate error thrown when argument cannot be converted to a valid string + or object for TimeZone +features: [BigInt, Symbol, Temporal] +---*/ + +const rangeErrorTests = [ + [null, "null"], + [true, "boolean"], + ["", "empty string"], + [1, "number that doesn't convert to a valid ISO string"], + [19761118, "number that would convert to a valid ISO string in other contexts"], + [1n, "bigint"], + [new Temporal.Calendar("iso8601"), "calendar instance"], +]; + +for (const [timeZone, description] of rangeErrorTests) { + assert.throws(RangeError, () => Temporal.Duration.compare(new Temporal.Duration(), new Temporal.Duration(), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), `${description} does not convert to a valid ISO string`); + assert.throws(RangeError, () => Temporal.Duration.compare(new Temporal.Duration(), new Temporal.Duration(), { relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }), `${description} does not convert to a valid ISO string (nested property)`); +} + +const typeErrorTests = [ + [Symbol(), "symbol"], +]; + +for (const [timeZone, description] of typeErrorTests) { + assert.throws(TypeError, () => Temporal.Duration.compare(new Temporal.Duration(), new Temporal.Duration(), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), `${description} is not a valid object and does not convert to a string`); + assert.throws(TypeError, () => Temporal.Duration.compare(new Temporal.Duration(), new Temporal.Duration(), { relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }), `${description} is not a valid object and does not convert to a string (nested property)`); +} + +const timeZone = undefined; +assert.throws(RangeError, () => Temporal.Duration.compare(new Temporal.Duration(), new Temporal.Duration(), { relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }), `undefined is always a RangeError as nested property`); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-string-invalid.js new file mode 100644 index 0000000000..884ca217e9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-string-invalid.js @@ -0,0 +1,17 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: RangeError thrown if relativeTo is a string with the wrong format +features: [Temporal] +---*/ + +['bad string', '15:30:45.123456', 'iso8601', 'UTC', 'P1YT1H'].forEach((relativeTo) => { + const duration1 = new Temporal.Duration(0, 0, 0, 31); + const duration2 = new Temporal.Duration(0, 1); + assert.throws(RangeError, () => Temporal.Duration.compare(duration1, duration2, { relativeTo })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-string-plaindatetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-string-plaindatetime.js new file mode 100644 index 0000000000..db3a52adf5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-string-plaindatetime.js @@ -0,0 +1,18 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: The relativeTo option accepts a PlainDateTime-like ISO 8601 string +features: [Temporal] +---*/ + +['2000-01-01', '2000-01-01T00:00', '2000-01-01T00:00[u-ca=iso8601]'].forEach((relativeTo) => { + const duration1 = new Temporal.Duration(0, 0, 0, 31); + const duration2 = new Temporal.Duration(0, 1); + const result = Temporal.Duration.compare(duration1, duration2, { relativeTo }); + assert.sameValue(result, 0); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-string-zoneddatetime-wrong-offset.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-string-zoneddatetime-wrong-offset.js new file mode 100644 index 0000000000..c3115d77a8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-string-zoneddatetime-wrong-offset.js @@ -0,0 +1,16 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: Throws if a ZonedDateTime-like relativeTo string has the wrong UTC offset +features: [Temporal] +---*/ + +const duration1 = new Temporal.Duration(0, 0, 0, 31); +const duration2 = new Temporal.Duration(0, 1); +const relativeTo = "2000-01-01T00:00+05:30[UTC]"; +assert.throws(RangeError, () => Temporal.Duration.compare(duration1, duration2, { relativeTo })); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-string-zoneddatetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-string-zoneddatetime.js new file mode 100644 index 0000000000..e99eec5399 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-string-zoneddatetime.js @@ -0,0 +1,23 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: The relativeTo option accepts a ZonedDateTime-like ISO 8601 string +features: [Temporal] +---*/ + +[ + '2000-01-01[UTC]', + '2000-01-01T00:00[UTC]', + '2000-01-01T00:00+00:00[UTC]', + '2000-01-01T00:00+00:00[UTC][u-ca=iso8601]', +].forEach((relativeTo) => { + const duration1 = new Temporal.Duration(0, 0, 0, 31); + const duration2 = new Temporal.Duration(0, 1); + const result = Temporal.Duration.compare(duration1, duration2, { relativeTo }); + assert.sameValue(result, 0); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-sub-minute-offset.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-sub-minute-offset.js new file mode 100644 index 0000000000..1b9ba35199 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-sub-minute-offset.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: relativeTo string accepts an inexact UTC offset rounded to hours and minutes +features: [Temporal] +---*/ + +const duration1 = new Temporal.Duration(0, 0, 0, 31); +const duration2 = new Temporal.Duration(0, 1); + +let relativeTo = "2000-01-01T00:00+00:45[+00:44:30.123456789]"; +assert.sameValue(Temporal.Duration.compare(duration1, duration2, { relativeTo }), 0, "rounded HH:MM is accepted in string"); + +relativeTo = "2000-01-01T00:00+00:44:30[+00:44:30.123456789]"; +assert.throws(RangeError, () => Temporal.Duration.compare(duration1, duration2, { relativeTo }), "no other rounding is accepted for offset"); + +const timeZone = new Temporal.TimeZone("+00:44:30.123456789"); +relativeTo = { year: 2000, month: 1, day: 1, offset: "+00:45", timeZone }; +assert.throws(RangeError, () => Temporal.Duration.compare(duration1, duration2, { relativeTo }), "rounded HH:MM not accepted as offset in property bag"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-undefined-throw-on-calendar-units.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-undefined-throw-on-calendar-units.js new file mode 100644 index 0000000000..74639412b1 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-undefined-throw-on-calendar-units.js @@ -0,0 +1,29 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: > + The relativeTo option is required when either Duration contains years, + months, or weeks +features: [Temporal] +---*/ + +const oneYear = new Temporal.Duration(1); +const oneMonth = new Temporal.Duration(0, 1); +const oneWeek = new Temporal.Duration(0, 0, 1); +const oneDay = new Temporal.Duration(0, 0, 0, 1); + +assert.sameValue(Temporal.Duration.compare(oneDay, oneDay), 0, "days do not require relativeTo"); + +assert.throws(RangeError, () => Temporal.Duration.compare(oneWeek, oneDay), "weeks in left operand require relativeTo"); +assert.throws(RangeError, () => Temporal.Duration.compare(oneDay, oneWeek), "weeks in right operand require relativeTo"); + +assert.throws(RangeError, () => Temporal.Duration.compare(oneMonth, oneDay), "months in left operand require relativeTo"); +assert.throws(RangeError, () => Temporal.Duration.compare(oneDay, oneMonth), "months in right operand require relativeTo"); + +assert.throws(RangeError, () => Temporal.Duration.compare(oneYear, oneDay), "years in left operand require relativeTo"); +assert.throws(RangeError, () => Temporal.Duration.compare(oneDay, oneYear), "years in right operand require relativeTo"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-year.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-year.js new file mode 100644 index 0000000000..3cb4f21a1d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-year.js @@ -0,0 +1,18 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: relativeTo with years. +features: [Temporal] +---*/ + +const oneYear = new Temporal.Duration(1); +const days365 = new Temporal.Duration(0, 0, 0, 365); +assert.sameValue( + Temporal.Duration.compare(oneYear, days365, { relativeTo: Temporal.PlainDate.from("2017-01-01") }), 0); +assert.sameValue( + Temporal.Duration.compare(oneYear, days365, { relativeTo: Temporal.PlainDate.from("2016-01-01") }), 1); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-negative-epochnanoseconds.js new file mode 100644 index 0000000000..ab1f81f9c5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-negative-epochnanoseconds.js @@ -0,0 +1,26 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch +info: | + sec-temporal-getisopartsfromepoch step 1: + 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 10<sup>6</sup>. + sec-temporal-builtintimezonegetplaindatetimefor step 2: + 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]). +features: [Temporal] +---*/ + +const relativeTo = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC"); +const duration = new Temporal.Duration(0, 0, 0, 1); + +// This code path shows up anywhere we convert an exact time, before the Unix +// epoch, with nonzero microseconds or nanoseconds, into a wall time; in this +// case via relativeTo. + +const result = Temporal.Duration.compare(duration, duration, { relativeTo }); +assert.sameValue(result, 0); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..36b4ca3c03 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,20 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + const duration1 = new Temporal.Duration(1); + const duration2 = new Temporal.Duration(1, 1); + assert.throws(RangeError, () => Temporal.Duration.compare(duration1, duration2, { relativeTo })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..ba9b466139 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,23 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-temporal.duration.compare +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach(notCallable => { + const timeZone = new Temporal.TimeZone("UTC"); + const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + const duration1 = new Temporal.Duration(1); + const duration2 = new Temporal.Duration(1, 1); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => Temporal.Duration.compare(duration1, duration2, { relativeTo }), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..29d19a0930 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,20 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_001, 86400_000_000_001, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + const duration1 = new Temporal.Duration(1); + const duration2 = new Temporal.Duration(1, 1); + assert.throws(RangeError, () => Temporal.Duration.compare(duration1, duration2, { relativeTo })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..93ebc4a5b3 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,29 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + const duration1 = new Temporal.Duration(1); + const duration2 = new Temporal.Duration(1, 1); + assert.throws(TypeError, () => Temporal.Duration.compare(duration1, duration2, { relativeTo })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/shell.js new file mode 100644 index 0000000000..eda1477282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/shell.js @@ -0,0 +1,24 @@ +// GENERATED, DO NOT EDIT +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/timezone-getpossibleinstantsfor-iterable.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/timezone-getpossibleinstantsfor-iterable.js new file mode 100644 index 0000000000..625c034b3b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/timezone-getpossibleinstantsfor-iterable.js @@ -0,0 +1,41 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call +info: | + sec-temporal.duration.compare steps 4–6: + 4. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_). + 5. Let _shift1_ be ! CalculateOffsetShift(_relativeTo_, _one_.[[Years]], [...], _one_.[[Nanoseconds]]). + 6. Let _shift2_ be ! CalculateOffsetShift(_relativeTo_, _two_.[[Years]], [...], _two_.[[Nanoseconds]]). + sec-temporal-torelativetemporalobject step 6.d: + d. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_result_.[[Year]], [...], _result_.[[Nanosecond]], _offsetNs_, _timeZone_, *"compatible"*, *"reject"*). + sec-temporal-interpretisodatetimeoffset step 7: + 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_). + sec-temporal-calculateoffsetshift step 4: + 4. Let _after_ be ? AddZonedDateTime(_relativeTo_.[[Nanoseconds]], _relativeTo_.[[TimeZone]], _relativeTo_.[[Calendar]], _y_, [...], _ns_). + sec-temporal-addzoneddatetime step 8: + 8. Let _intermediateInstant_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _intermediateDateTime_, *"compatible"*). + sec-temporal-builtintimezonegetinstantfor step 1: + 1. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_). + sec-temporal-getpossibleinstantsfor step 2: + 2. Let _list_ be ? IterableToList(_possibleInstants_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "2000-01-01T00:00:00", // called once on the input relativeTo object + "2001-01-01T00:00:00", // called once on relativeTo plus the first operand + "2001-02-01T00:00:00", // called once on relativeTo plus the second operand +]; + +TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => { + const duration1 = new Temporal.Duration(1); + const duration2 = new Temporal.Duration(0, 13); + Temporal.Duration.compare(duration1, duration2, { relativeTo: { year: 2000, month: 1, day: 1, timeZone } }); +}, expected); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/twenty-five-hour-day.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/twenty-five-hour-day.js new file mode 100644 index 0000000000..3017565091 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/twenty-five-hour-day.js @@ -0,0 +1,36 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: Unbalancing handles DST days with more than 24 hours +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +const tz = TemporalHelpers.springForwardFallBackTimeZone(); + +// 2000-10-29 is a 25-hour day according to this time zone... + +const relativeTo = new Temporal.ZonedDateTime(941187600_000_000_000n, tz); + +// confirm that we have rewound one year and one day: +assert.sameValue('1999-10-29T01:00:00-08:00[Custom/Spring_Fall]', relativeTo.toString()); + +const d1 = new Temporal.Duration(1, 0, 0, 1); +const d2 = new Temporal.Duration(1, 0, 0, 0, 25); + +// ...so the durations should be equal relative to relativeTo: + +assert.sameValue(0, + Temporal.Duration.compare(d1, d2, { relativeTo }), + "2000-10-29 is a 25-hour day" +); + +assert.sameValue(1, + Temporal.Duration.compare(d1, { years: 1, hours: 24 }, { relativeTo }), + "2020-10-29 has more than 24 hours" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/compare/year-zero.js b/js/src/tests/test262/built-ins/Temporal/Duration/compare/year-zero.js new file mode 100644 index 0000000000..23f05d0bba --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/compare/year-zero.js @@ -0,0 +1,21 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.compare +description: Negative zero, as an extended year, fails +features: [Temporal] +---*/ + +const duration1 = new Temporal.Duration(1); +const duration2 = new Temporal.Duration(2); +const bad = "-000000-11-01"; + +assert.throws( + RangeError, + () => Temporal.Duration.compare(duration1, duration2, { relativeTo: bad }), + "Cannot use negative zero as extended year" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/constructor.js b/js/src/tests/test262/built-ins/Temporal/Duration/constructor.js new file mode 100644 index 0000000000..a478692f82 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/constructor.js @@ -0,0 +1,15 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration +description: Temporal.Duration constructor cannot be called as a function +info: | + 1. If NewTarget is undefined, throw a TypeError exception. +features: [Temporal] +---*/ + +assert.throws(TypeError, () => Temporal.Duration()); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/days-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/days-undefined.js new file mode 100644 index 0000000000..022d52e3a1 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/days-undefined.js @@ -0,0 +1,20 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration +description: Undefined arguments should be treated as zero. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const args = [1, 1, 1]; + +const explicit = new Temporal.Duration(...args, undefined); +TemporalHelpers.assertDuration(explicit, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, "explicit"); + +const implicit = new Temporal.Duration(...args); +TemporalHelpers.assertDuration(implicit, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, "implicit"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/fractional-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/fractional-throws-rangeerror.js new file mode 100644 index 0000000000..bd34d56d72 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/fractional-throws-rangeerror.js @@ -0,0 +1,35 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Temporal.Duration throws a RangeError if any value is fractional +esid: sec-temporal.duration +features: [Temporal] +---*/ + +const descriptions = [ + 'years', + 'months', + 'weeks', + 'days', + 'hours', + 'minutes', + 'seconds', + 'milliseconds', + 'microseconds', + 'nanoseconds' +].map((time) => `Duration constructor throws RangeError with fractional value in the ${time} position`); + +assert.throws(RangeError, () => new Temporal.Duration(1.1), descriptions[0]); +assert.throws(RangeError, () => new Temporal.Duration(0, 1.1), descriptions[1]); +assert.throws(RangeError, () => new Temporal.Duration(0, 0, 1.1), descriptions[2]); +assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 1.1), descriptions[3]); +assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 1.1), descriptions[4]); +assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 1.1), descriptions[5]); +assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, 1.1), descriptions[6]); +assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 1.1), descriptions[7]); +assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 1.1), descriptions[8]); +assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 1.1), descriptions[9]); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-duration.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-duration.js new file mode 100644 index 0000000000..e723380c4b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-duration.js @@ -0,0 +1,27 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.from +description: A Duration object is copied, not returned directly +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const orig = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); +const result = Temporal.Duration.from(orig); + +TemporalHelpers.assertDuration( + result, + 1, 2, 3, 4, 5, 6, 7, 987, 654, 321, + "Duration is copied" +); + +assert.notSameValue( + result, + orig, + "When a Duration is given, the returned value is not the original Duration" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-existing-object.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-existing-object.js new file mode 100644 index 0000000000..bdbbba4276 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-existing-object.js @@ -0,0 +1,20 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2020 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.from +description: Property bag is converted to Duration; Duration is copied +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const d1 = Temporal.Duration.from({ milliseconds: 1000, month: 1 }); +TemporalHelpers.assertDuration(d1, 0, 0, 0, 0, 0, 0, 0, 1000, 0, 0); + +const d2 = Temporal.Duration.from(d1); +assert.notSameValue(d1, d2); +TemporalHelpers.assertDuration(d1, 0, 0, 0, 0, 0, 0, 0, 1000, 0, 0); +TemporalHelpers.assertDuration(d2, 0, 0, 0, 0, 0, 0, 0, 1000, 0, 0); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-non-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-non-string.js new file mode 100644 index 0000000000..c70979456e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-non-string.js @@ -0,0 +1,18 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2020 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.from +description: Appropriate error thrown if primitive input cannot convert to a valid string +features: [Temporal] +---*/ + +assert.throws(RangeError, () => Temporal.Duration.from(undefined), "undefined"); +assert.throws(RangeError, () => Temporal.Duration.from(null), "null"); +assert.throws(RangeError, () => Temporal.Duration.from(true), "boolean"); +assert.throws(TypeError, () => Temporal.Duration.from(Symbol()), "Symbol"); +assert.throws(RangeError, () => Temporal.Duration.from(5), "number"); +assert.throws(RangeError, () => Temporal.Duration.from(5n), "bigint"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-object-invalid.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-object-invalid.js new file mode 100644 index 0000000000..8977be7ffe --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-object-invalid.js @@ -0,0 +1,29 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.from +description: Invalid object arguments. +features: [Temporal] +---*/ + +const tests = [ + { years: 0.5 }, + { months: 0.5 }, + { weeks: 0.5 }, + { days: 0.5 }, + { hours: 0.5, minutes: 20 }, + { hours: 0.5, seconds: 15 }, + { minutes: 10.7, nanoseconds: 400 }, + { hours: 1, minutes: -30 }, +]; + +for (const input of tests) { + assert.throws(RangeError, () => Temporal.Duration.from(input)); +} + +assert.throws(TypeError, () => Temporal.Duration.from({})); +assert.throws(TypeError, () => Temporal.Duration.from({ month: 12 })); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-fractional-precision.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-fractional-precision.js new file mode 100644 index 0000000000..84d05762f0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-fractional-precision.js @@ -0,0 +1,59 @@ +// |reftest| skip -- Temporal is not supported +// 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.from +description: > + Fractional parts are computed using exact mathematical values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const tests = { + "PT0.999999999H": Temporal.Duration.from({ + minutes: 59, + seconds: 59, + milliseconds: 999, + microseconds: 996, + nanoseconds: 400, + }), + "PT0.000000011H": Temporal.Duration.from({ + minutes: 0, + seconds: 0, + milliseconds: 0, + microseconds: 39, + nanoseconds: 600, + }), + + "PT0.999999999M": Temporal.Duration.from({ + seconds: 59, + milliseconds: 999, + microseconds: 999, + nanoseconds: 940, + }), + "PT0.000000011M": Temporal.Duration.from({ + seconds: 0, + milliseconds: 0, + microseconds: 0, + nanoseconds: 660, + }), + + "PT0.999999999S": Temporal.Duration.from({ + milliseconds: 999, + microseconds: 999, + nanoseconds: 999, + }), + "PT0.000000011S": Temporal.Duration.from({ + milliseconds: 0, + microseconds: 0, + nanoseconds: 11, + }), +}; + +for (let [str, expected] of Object.entries(tests)) { + let actual = Temporal.Duration.from(str); + TemporalHelpers.assertDurationsEqual(actual, expected, str); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-fractional-units-rounding-mode.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-fractional-units-rounding-mode.js new file mode 100644 index 0000000000..92a58c77dc --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-fractional-units-rounding-mode.js @@ -0,0 +1,31 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.from +description: Strings with fractional duration units are rounded with the correct rounding mode +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const resultPosHours = Temporal.Duration.from("PT1.03125H"); +TemporalHelpers.assertDuration(resultPosHours, 0, 0, 0, 0, 1, 1, 52, 500, 0, 0, + "positive fractional hours rounded with correct rounding mode"); + +const resultNegHours = Temporal.Duration.from("-PT1.03125H"); +TemporalHelpers.assertDuration(resultNegHours, 0, 0, 0, 0, -1, -1, -52, -500, 0, 0, + "negative fractional hours rounded with correct rounding mode"); + +// The following input should not round, but may fail if an implementation does +// floating point arithmetic too early: + +const resultPosSeconds = Temporal.Duration.from("PT46H66M71.50040904S"); +TemporalHelpers.assertDuration(resultPosSeconds, 0, 0, 0, 0, 46, 66, 71, 500, 409, 40, + "positive fractional seconds not rounded"); + +const resultNegSeconds = Temporal.Duration.from("-PT46H66M71.50040904S"); +TemporalHelpers.assertDuration(resultNegSeconds, 0, 0, 0, 0, -46, -66, -71, -500, -409, -40, + "negative fractional seconds not rounded"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-fractional-with-zero-subparts.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-fractional-with-zero-subparts.js new file mode 100644 index 0000000000..b09ea2f5e7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-fractional-with-zero-subparts.js @@ -0,0 +1,36 @@ +// |reftest| skip -- Temporal is not supported +// 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.from +description: > + Throws when a fractional unit is present and a sub-part is zero. +features: [Temporal] +---*/ + +const invalid = [ + // Hours fraction with whole minutes. + "PT0.1H0M", + + // Hours fraction with fractional minutes. + "PT0.1H0.0M", + + // Hours fraction with whole seconds. + "PT0.1H0S", + + // Hours fraction with fractional seconds. + "PT0.1H0.0S", + + // Minutes fraction with whole seconds. + "PT0.1M0S", + + // Minutes fraction with fractional seconds. + "PT0.1M0.0S", +]; + +for (let string of invalid) { + assert.throws(RangeError, () => Temporal.Duration.from(string)); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-invalid.js new file mode 100644 index 0000000000..413c386971 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-invalid.js @@ -0,0 +1,35 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.from +description: Invalid string arguments. +features: [Temporal] +---*/ + +const tests = [ + "P1Y1M1W1DT1H1M1.123456789123S", + "P0.5Y", + "P1Y0,5M", + "P1Y1M0.5W", + "P1Y1M1W0,5D", + "P1Y1M1W1DT0.5H5S", + "P1Y1M1W1DT1.5H0,5M", + "P1Y1M1W1DT1H0.5M0.5S", + "P", + "PT", + "-P", + "-PT", + "+P", + "+PT", + "P1Y1M1W1DT1H1M1.01Sjunk", + "P-1Y1M", + "P1Y-1M" +]; + +for (const input of tests) { + assert.throws(RangeError, () => Temporal.Duration.from(input)); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-negative-fractional-units.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-negative-fractional-units.js new file mode 100644 index 0000000000..b660f55243 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string-negative-fractional-units.js @@ -0,0 +1,18 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: Strings with fractional duration units are treated with the correct sign +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const resultHours = Temporal.Duration.from("-PT24.567890123H"); +TemporalHelpers.assertDuration(resultHours, 0, 0, 0, 0, -24, -34, -4, -404, -442, -800, "negative fractional hours"); + +const resultMinutes = Temporal.Duration.from("-PT1440.567890123M"); +TemporalHelpers.assertDuration(resultMinutes, 0, 0, 0, 0, 0, -1440, -34, -73, -407, -380, "negative fractional minutes"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string.js new file mode 100644 index 0000000000..6d6b1d198a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/argument-string.js @@ -0,0 +1,51 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.from +description: Basic string arguments. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.assertDuration(Temporal.Duration.from("P1D"), + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0); +TemporalHelpers.assertDuration(Temporal.Duration.from("p1y1m1dt1h1m1s"), + 1, 1, 0, 1, 1, 1, 1, 0, 0, 0); +TemporalHelpers.assertDuration(Temporal.Duration.from("P1Y1M1W1DT1H1M1.1S"), + 1, 1, 1, 1, 1, 1, 1, 100, 0, 0); +TemporalHelpers.assertDuration(Temporal.Duration.from("P1Y1M1W1DT1H1M1.12S"), + 1, 1, 1, 1, 1, 1, 1, 120, 0, 0); +TemporalHelpers.assertDuration(Temporal.Duration.from("P1Y1M1W1DT1H1M1.123S"), + 1, 1, 1, 1, 1, 1, 1, 123, 0, 0); +TemporalHelpers.assertDuration(Temporal.Duration.from("P1Y1M1W1DT1H1M1.1234S"), + 1, 1, 1, 1, 1, 1, 1, 123, 400, 0); +TemporalHelpers.assertDuration(Temporal.Duration.from("P1Y1M1W1DT1H1M1.12345S"), + 1, 1, 1, 1, 1, 1, 1, 123, 450, 0); +TemporalHelpers.assertDuration(Temporal.Duration.from("P1Y1M1W1DT1H1M1.123456S"), + 1, 1, 1, 1, 1, 1, 1, 123, 456, 0); +TemporalHelpers.assertDuration(Temporal.Duration.from("P1Y1M1W1DT1H1M1.1234567S"), + 1, 1, 1, 1, 1, 1, 1, 123, 456, 700); +TemporalHelpers.assertDuration(Temporal.Duration.from("P1Y1M1W1DT1H1M1.12345678S"), + 1, 1, 1, 1, 1, 1, 1, 123, 456, 780); +TemporalHelpers.assertDuration(Temporal.Duration.from("P1Y1M1W1DT1H1M1.123456789S"), + 1, 1, 1, 1, 1, 1, 1, 123, 456, 789); +TemporalHelpers.assertDuration(Temporal.Duration.from("P1Y1M1W1DT1H1M1,12S"), + 1, 1, 1, 1, 1, 1, 1, 120, 0, 0); +TemporalHelpers.assertDuration(Temporal.Duration.from("P1DT0.5M"), + 0, 0, 0, 1, 0, 0, 30, 0, 0, 0); +TemporalHelpers.assertDuration(Temporal.Duration.from("P1DT0,5H"), + 0, 0, 0, 1, 0, 30, 0, 0, 0, 0); +TemporalHelpers.assertDuration(Temporal.Duration.from("+P1D"), + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0); +TemporalHelpers.assertDuration(Temporal.Duration.from("-P1D"), + 0, 0, 0, -1, 0, 0, 0, 0, 0, 0); +TemporalHelpers.assertDuration(Temporal.Duration.from("\u2212P1D"), + 0, 0, 0, -1, 0, 0, 0, 0, 0, 0); +TemporalHelpers.assertDuration(Temporal.Duration.from("-P1Y1M1W1DT1H1M1.123456789S"), + -1, -1, -1, -1, -1, -1, -1, -123, -456, -789); +TemporalHelpers.assertDuration(Temporal.Duration.from("PT100M"), + 0, 0, 0, 0, 0, 100, 0, 0, 0, 0); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/builtin.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/builtin.js new file mode 100644 index 0000000000..acec25b5b8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/builtin.js @@ -0,0 +1,33 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.from +description: Tests that Temporal.Duration.from meets the requirements for built-in objects +info: | + Built-in functions that are not constructors do not have a "prototype" property unless + otherwise specified in the description of a particular function. + + Unless specified otherwise, a built-in object that is callable as a function is a built-in + function object with the characteristics described in 10.3. Unless specified otherwise, the + [[Extensible]] internal slot of a built-in object initially has the value true. + + Unless otherwise specified every built-in function and every built-in constructor has the + Function prototype object [...] as the value of its [[Prototype]] internal slot. +features: [Temporal] +---*/ + +assert.sameValue(Object.isExtensible(Temporal.Duration.from), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.Duration.from), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.Duration.from), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.Duration.from.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/infinity-throws-rangeerror.js new file mode 100644 index 0000000000..f34e2dcc8f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/infinity-throws-rangeerror.js @@ -0,0 +1,31 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2020 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Temporal.Duration.from handles a property bag if any value is Infinity +esid: sec-temporal.duration.from +features: [Temporal] +---*/ + +const fields = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds', 'nanoseconds']; + +fields.forEach((field) => { + assert.throws(RangeError, () => Temporal.Duration.from({ [field]: Infinity })); +}); + +let calls = 0; +const obj = { + valueOf() { + calls++; + return Infinity; + } +}; + +fields.forEach((field) => { + calls = 0; + assert.throws(RangeError, () => Temporal.Duration.from({ [field]: obj })); + assert.sameValue(calls, 1, "it fails after fetching the primitive value"); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/length.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/length.js new file mode 100644 index 0000000000..12c629055c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/length.js @@ -0,0 +1,28 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2020 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.from +description: Temporal.Duration.from.length is 1 +info: | + Every built-in function object, including constructors, has a "length" property whose value is + an integer. Unless otherwise specified, this value is equal to the largest number of named + arguments shown in the subclause headings for the function description. Optional parameters + (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form + «...name») are not included in the default argument count. + + Unless otherwise specified, the "length" property of a built-in function object has the + attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +verifyProperty(Temporal.Duration.from, "length", { + value: 1, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/name.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/name.js new file mode 100644 index 0000000000..1c0450948e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/name.js @@ -0,0 +1,26 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2020 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.from +description: Temporal.Duration.from.name is "from" +info: | + Every built-in function object, including constructors, that is not identified as an anonymous + function has a "name" property whose value is a String. Unless otherwise specified, this value + is the name that is given to the function in this specification. + + Unless otherwise specified, the "name" property of a built-in function object, if it exists, + has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +verifyProperty(Temporal.Duration.from, "name", { + value: "from", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/negative-inifinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/negative-inifinity-throws-rangeerror.js new file mode 100644 index 0000000000..9a92251596 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/negative-inifinity-throws-rangeerror.js @@ -0,0 +1,31 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2020 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Temporal.Duration.from handles a property bag if any value is -Infinity +esid: sec-temporal.duration.from +features: [Temporal] +---*/ + +const fields = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds', 'nanoseconds']; + +fields.forEach((field) => { + assert.throws(RangeError, () => Temporal.Duration.from({ [field]: -Infinity })); +}); + +let calls = 0; +const obj = { + valueOf() { + calls++; + return -Infinity; + } +}; + +fields.forEach((field) => { + calls = 0; + assert.throws(RangeError, () => Temporal.Duration.from({ [field]: obj })); + assert.sameValue(calls, 1, "it fails after fetching the primitive value"); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/non-integer-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/non-integer-throws-rangeerror.js new file mode 100644 index 0000000000..0c7c5d3d70 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/non-integer-throws-rangeerror.js @@ -0,0 +1,28 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.with +description: A non-integer value for any recognized property in the property bag, throws a RangeError +features: [Temporal] +---*/ + +const fields = [ + "years", + "months", + "weeks", + "days", + "hours", + "minutes", + "seconds", + "milliseconds", + "microseconds", + "nanoseconds", +]; +fields.forEach((field) => { + assert.throws(RangeError, () => Temporal.Duration.from({ [field]: 1.5 })); + assert.throws(RangeError, () => Temporal.Duration.from({ [field]: -1.5 })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/not-a-constructor.js new file mode 100644 index 0000000000..9c63d7c25c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/not-a-constructor.js @@ -0,0 +1,23 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.from +description: Temporal.Duration.from does not implement [[Construct]], is not new-able +info: | + Built-in function objects that are not identified as constructors do not implement the + [[Construct]] internal method unless otherwise specified in the description of a particular + function. +includes: [isConstructor.js] +features: [Reflect.construct, Temporal] +---*/ + +assert.throws(TypeError, () => { + new Temporal.Duration.from(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.Duration.from), false, + "isConstructor(Temporal.Duration.from)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/order-of-operations.js new file mode 100644 index 0000000000..a934924bd9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/order-of-operations.js @@ -0,0 +1,61 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2020 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.from +description: Properties on an object passed to from() are accessed in the correct order +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "get fields.days", + "get fields.days.valueOf", + "call fields.days.valueOf", + "get fields.hours", + "get fields.hours.valueOf", + "call fields.hours.valueOf", + "get fields.microseconds", + "get fields.microseconds.valueOf", + "call fields.microseconds.valueOf", + "get fields.milliseconds", + "get fields.milliseconds.valueOf", + "call fields.milliseconds.valueOf", + "get fields.minutes", + "get fields.minutes.valueOf", + "call fields.minutes.valueOf", + "get fields.months", + "get fields.months.valueOf", + "call fields.months.valueOf", + "get fields.nanoseconds", + "get fields.nanoseconds.valueOf", + "call fields.nanoseconds.valueOf", + "get fields.seconds", + "get fields.seconds.valueOf", + "call fields.seconds.valueOf", + "get fields.weeks", + "get fields.weeks.valueOf", + "call fields.weeks.valueOf", + "get fields.years", + "get fields.years.valueOf", + "call fields.years.valueOf", +]; +const actual = []; +const fields = TemporalHelpers.propertyBagObserver(actual, { + years: 1, + months: 1, + weeks: 1, + days: 1, + hours: 1, + minutes: 1, + seconds: 1, + milliseconds: 1, + microseconds: 1, + nanoseconds: 1, +}, "fields"); +const result = Temporal.Duration.from(fields); +TemporalHelpers.assertDuration(result, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); +assert.compareArray(actual, expected, "order of operations"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/prop-desc.js new file mode 100644 index 0000000000..37979083e0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.from +description: The "from" property of Temporal.Duration +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.Duration.from, + "function", + "`typeof Duration.from` is `function`" +); + +verifyProperty(Temporal.Duration, "from", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/shell.js new file mode 100644 index 0000000000..eda1477282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/shell.js @@ -0,0 +1,24 @@ +// GENERATED, DO NOT EDIT +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/string-with-skipped-units.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/string-with-skipped-units.js new file mode 100644 index 0000000000..b26bb30fbd --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/string-with-skipped-units.js @@ -0,0 +1,34 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.from +description: | + Creating a Duration from an ISO 8601 string with an absent designator between + two other designators +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +// Date designators: years-weeks (months missing) + +const d1 = Temporal.Duration.from("P3Y4W"); +TemporalHelpers.assertDuration(d1, 3, 0, 4, 0, 0, 0, 0, 0, 0, 0, "years-weeks string"); + +// Date designators: years-days (months and weeks missing) + +const d2 = Temporal.Duration.from("P3Y4D"); +TemporalHelpers.assertDuration(d2, 3, 0, 0, 4, 0, 0, 0, 0, 0, 0, "years-days string"); + +// Date designators: months-days (weeks missing) + +const d3 = Temporal.Duration.from("P3M4D"); +TemporalHelpers.assertDuration(d3, 0, 3, 0, 4, 0, 0, 0, 0, 0, 0, "months-days string"); + +// Time designators: hours-seconds (minutes missing) + +const d4 = Temporal.Duration.from("PT3H4.123456789S"); +TemporalHelpers.assertDuration(d4, 0, 0, 0, 0, 3, 0, 4, 123, 456, 789, "hours-seconds string"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/from/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/Duration/from/subclassing-ignored.js new file mode 100644 index 0000000000..dee881c899 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/from/subclassing-ignored.js @@ -0,0 +1,19 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.from +description: The receiver is never called when calling from() +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.checkSubclassingIgnoredStatic( + Temporal.Duration, + "from", + ["P1Y2M3W4DT5H6M7.987654321S"], + (result) => TemporalHelpers.assertDuration(result, 1, 2, 3, 4, 5, 6, 7, 987, 654, 321), +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/hours-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/hours-undefined.js new file mode 100644 index 0000000000..60772a8567 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/hours-undefined.js @@ -0,0 +1,20 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration +description: Undefined arguments should be treated as zero. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const args = [1, 1, 1, 1]; + +const explicit = new Temporal.Duration(...args, undefined); +TemporalHelpers.assertDuration(explicit, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, "explicit"); + +const implicit = new Temporal.Duration(...args); +TemporalHelpers.assertDuration(implicit, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, "implicit"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/infinity-throws-rangeerror.js new file mode 100644 index 0000000000..d838026d25 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/infinity-throws-rangeerror.js @@ -0,0 +1,84 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2020 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Temporal.Duration throws a RangeError if any value is Infinity +esid: sec-temporal.duration +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +assert.throws(RangeError, () => new Temporal.Duration(Infinity)); +assert.throws(RangeError, () => new Temporal.Duration(0, Infinity)); +assert.throws(RangeError, () => new Temporal.Duration(0, 0, Infinity)); +assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, Infinity)); +assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, Infinity)); +assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, Infinity)); +assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, Infinity)); +assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, Infinity)); +assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, Infinity)); +assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, Infinity)); + +const O = (primitiveValue, propertyName) => (calls) => TemporalHelpers.toPrimitiveObserver(calls, primitiveValue, propertyName); +const tests = [ + [ + "infinite years", + [O(Infinity, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")], + ["get years.valueOf", "call years.valueOf"] + ], + [ + "infinite months", + [O(0, "years"), O(Infinity, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")], + ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf"] + ], + [ + "infinite weeks", + [O(0, "years"), O(0, "months"), O(Infinity, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")], + ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf"] + ], + [ + "infinite days", + [O(0, "years"), O(0, "months"), O(0, "weeks"), O(Infinity, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")], + ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf"] + ], + [ + "infinite hours", + [O(0, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(Infinity, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")], + ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf", "get hours.valueOf", "call hours.valueOf"] + ], + [ + "infinite minutes", + [O(0, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(Infinity, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")], + ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf", "get hours.valueOf", "call hours.valueOf", "get minutes.valueOf", "call minutes.valueOf"] + ], + [ + "infinite seconds", + [O(0, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(Infinity, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")], + ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf", "get hours.valueOf", "call hours.valueOf", "get minutes.valueOf", "call minutes.valueOf", "get seconds.valueOf", "call seconds.valueOf"] + ], + [ + "infinite milliseconds", + [O(0, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(Infinity, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")], + ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf", "get hours.valueOf", "call hours.valueOf", "get minutes.valueOf", "call minutes.valueOf", "get seconds.valueOf", "call seconds.valueOf", "get milliseconds.valueOf", "call milliseconds.valueOf"] + ], + [ + "infinite microseconds", + [O(0, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(Infinity, "microseconds"), O(0, "nanoseconds")], + ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf", "get hours.valueOf", "call hours.valueOf", "get minutes.valueOf", "call minutes.valueOf", "get seconds.valueOf", "call seconds.valueOf", "get milliseconds.valueOf", "call milliseconds.valueOf", "get microseconds.valueOf", "call microseconds.valueOf"] + ], + [ + "infinite nanoseconds", + [O(0, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(Infinity, "nanoseconds")], + ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf", "get hours.valueOf", "call hours.valueOf", "get minutes.valueOf", "call minutes.valueOf", "get seconds.valueOf", "call seconds.valueOf", "get milliseconds.valueOf", "call milliseconds.valueOf", "get microseconds.valueOf", "call microseconds.valueOf", "get nanoseconds.valueOf", "call nanoseconds.valueOf"] + ], +]; + +for (const [description, args, expected] of tests) { + const actual = []; + const args_ = args.map((o) => o(actual)); + assert.throws(RangeError, () => new Temporal.Duration(...args_), description); + assert.compareArray(actual, expected, `${description} order of operations`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/length.js b/js/src/tests/test262/built-ins/Temporal/Duration/length.js new file mode 100644 index 0000000000..1985509674 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/length.js @@ -0,0 +1,28 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration +description: Temporal.Duration.length is 0 +info: | + Every built-in function object, including constructors, has a "length" property whose value is + an integer. Unless otherwise specified, this value is equal to the largest number of named + arguments shown in the subclause headings for the function description. Optional parameters + (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form + «...name») are not included in the default argument count. + + Unless otherwise specified, the "length" property of a built-in function object has the + attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +verifyProperty(Temporal.Duration, "length", { + value: 0, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/microseconds-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/microseconds-undefined.js new file mode 100644 index 0000000000..83a5b1303c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/microseconds-undefined.js @@ -0,0 +1,20 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration +description: Undefined arguments should be treated as zero. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const args = [1, 1, 1, 1, 1, 1, 1, 1]; + +const explicit = new Temporal.Duration(...args, undefined); +TemporalHelpers.assertDuration(explicit, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, "explicit"); + +const implicit = new Temporal.Duration(...args); +TemporalHelpers.assertDuration(implicit, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, "implicit"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/milliseconds-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/milliseconds-undefined.js new file mode 100644 index 0000000000..d56a793a93 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/milliseconds-undefined.js @@ -0,0 +1,20 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration +description: Undefined arguments should be treated as zero. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const args = [1, 1, 1, 1, 1, 1, 1]; + +const explicit = new Temporal.Duration(...args, undefined); +TemporalHelpers.assertDuration(explicit, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, "explicit"); + +const implicit = new Temporal.Duration(...args); +TemporalHelpers.assertDuration(implicit, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, "implicit"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/minutes-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/minutes-undefined.js new file mode 100644 index 0000000000..615666ee30 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/minutes-undefined.js @@ -0,0 +1,20 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration +description: Undefined arguments should be treated as zero. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const args = [1, 1, 1, 1, 1]; + +const explicit = new Temporal.Duration(...args, undefined); +TemporalHelpers.assertDuration(explicit, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, "explicit"); + +const implicit = new Temporal.Duration(...args); +TemporalHelpers.assertDuration(implicit, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, "implicit"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/mixed.js b/js/src/tests/test262/built-ins/Temporal/Duration/mixed.js new file mode 100644 index 0000000000..e1754ccd2d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/mixed.js @@ -0,0 +1,22 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration +description: Constructor with mixed signs. +features: [Temporal] +---*/ + +assert.throws(RangeError, () => new Temporal.Duration(-1, 1, 1, 1, 1, 1, 1, 1, 1, 1)); +assert.throws(RangeError, () => new Temporal.Duration(1, -1, 1, 1, 1, 1, 1, 1, 1, 1)); +assert.throws(RangeError, () => new Temporal.Duration(1, 1, -1, 1, 1, 1, 1, 1, 1, 1)); +assert.throws(RangeError, () => new Temporal.Duration(1, 1, 1, -1, 1, 1, 1, 1, 1, 1)); +assert.throws(RangeError, () => new Temporal.Duration(1, 1, 1, 1, -1, 1, 1, 1, 1, 1)); +assert.throws(RangeError, () => new Temporal.Duration(1, 1, 1, 1, 1, -1, 1, 1, 1, 1)); +assert.throws(RangeError, () => new Temporal.Duration(1, 1, 1, 1, 1, 1, -1, 1, 1, 1)); +assert.throws(RangeError, () => new Temporal.Duration(1, 1, 1, 1, 1, 1, 1, -1, 1, 1)); +assert.throws(RangeError, () => new Temporal.Duration(1, 1, 1, 1, 1, 1, 1, 1, -1, 1)); +assert.throws(RangeError, () => new Temporal.Duration(1, 1, 1, 1, 1, 1, 1, 1, 1, -1)); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/months-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/months-undefined.js new file mode 100644 index 0000000000..1dbf28a4ca --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/months-undefined.js @@ -0,0 +1,20 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration +description: Undefined arguments should be treated as zero. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const args = [1]; + +const explicit = new Temporal.Duration(...args, undefined); +TemporalHelpers.assertDuration(explicit, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "explicit"); + +const implicit = new Temporal.Duration(...args); +TemporalHelpers.assertDuration(implicit, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "implicit"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/name.js b/js/src/tests/test262/built-ins/Temporal/Duration/name.js new file mode 100644 index 0000000000..f4ddc96580 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/name.js @@ -0,0 +1,26 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration +description: Temporal.Duration.name is "Duration" +info: | + Every built-in function object, including constructors, that is not identified as an anonymous + function has a "name" property whose value is a String. Unless otherwise specified, this value + is the name that is given to the function in this specification. + + Unless otherwise specified, the "name" property of a built-in function object, if it exists, + has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +verifyProperty(Temporal.Duration, "name", { + value: "Duration", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/nanoseconds-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/nanoseconds-undefined.js new file mode 100644 index 0000000000..381053d83d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/nanoseconds-undefined.js @@ -0,0 +1,20 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration +description: Undefined arguments should be treated as zero. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const args = [1, 1, 1, 1, 1, 1, 1, 1, 1]; + +const explicit = new Temporal.Duration(...args, undefined); +TemporalHelpers.assertDuration(explicit, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, "explicit"); + +const implicit = new Temporal.Duration(...args); +TemporalHelpers.assertDuration(implicit, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, "implicit"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/negative-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/negative-infinity-throws-rangeerror.js new file mode 100644 index 0000000000..ccf3a24a8a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/negative-infinity-throws-rangeerror.js @@ -0,0 +1,84 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2020 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Temporal.Duration throws a RangeError if any value is -Infinity +esid: sec-temporal.duration +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +assert.throws(RangeError, () => new Temporal.Duration(-Infinity)); +assert.throws(RangeError, () => new Temporal.Duration(0, -Infinity)); +assert.throws(RangeError, () => new Temporal.Duration(0, 0, -Infinity)); +assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, -Infinity)); +assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, -Infinity)); +assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, -Infinity)); +assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, -Infinity)); +assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, -Infinity)); +assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, -Infinity)); +assert.throws(RangeError, () => new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, -Infinity)); + +const O = (primitiveValue, propertyName) => (calls) => TemporalHelpers.toPrimitiveObserver(calls, primitiveValue, propertyName); +const tests = [ + [ + "infinite years", + [O(-Infinity, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")], + ["get years.valueOf", "call years.valueOf"] + ], + [ + "infinite months", + [O(0, "years"), O(-Infinity, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")], + ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf"] + ], + [ + "infinite weeks", + [O(0, "years"), O(0, "months"), O(-Infinity, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")], + ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf"] + ], + [ + "infinite days", + [O(0, "years"), O(0, "months"), O(0, "weeks"), O(-Infinity, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")], + ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf"] + ], + [ + "infinite hours", + [O(0, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(-Infinity, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")], + ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf", "get hours.valueOf", "call hours.valueOf"] + ], + [ + "infinite minutes", + [O(0, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(-Infinity, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")], + ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf", "get hours.valueOf", "call hours.valueOf", "get minutes.valueOf", "call minutes.valueOf"] + ], + [ + "infinite seconds", + [O(0, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(-Infinity, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")], + ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf", "get hours.valueOf", "call hours.valueOf", "get minutes.valueOf", "call minutes.valueOf", "get seconds.valueOf", "call seconds.valueOf"] + ], + [ + "infinite milliseconds", + [O(0, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(-Infinity, "milliseconds"), O(0, "microseconds"), O(0, "nanoseconds")], + ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf", "get hours.valueOf", "call hours.valueOf", "get minutes.valueOf", "call minutes.valueOf", "get seconds.valueOf", "call seconds.valueOf", "get milliseconds.valueOf", "call milliseconds.valueOf"] + ], + [ + "infinite microseconds", + [O(0, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(-Infinity, "microseconds"), O(0, "nanoseconds")], + ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf", "get hours.valueOf", "call hours.valueOf", "get minutes.valueOf", "call minutes.valueOf", "get seconds.valueOf", "call seconds.valueOf", "get milliseconds.valueOf", "call milliseconds.valueOf", "get microseconds.valueOf", "call microseconds.valueOf"] + ], + [ + "infinite nanoseconds", + [O(0, "years"), O(0, "months"), O(0, "weeks"), O(0, "days"), O(0, "hours"), O(0, "minutes"), O(0, "seconds"), O(0, "milliseconds"), O(0, "microseconds"), O(-Infinity, "nanoseconds")], + ["get years.valueOf", "call years.valueOf", "get months.valueOf", "call months.valueOf", "get weeks.valueOf", "call weeks.valueOf", "get days.valueOf", "call days.valueOf", "get hours.valueOf", "call hours.valueOf", "get minutes.valueOf", "call minutes.valueOf", "get seconds.valueOf", "call seconds.valueOf", "get milliseconds.valueOf", "call milliseconds.valueOf", "get microseconds.valueOf", "call microseconds.valueOf", "get nanoseconds.valueOf", "call nanoseconds.valueOf"] + ], +]; + +for (const [description, args, expected] of tests) { + const actual = []; + const args_ = args.map((o) => o(actual)); + assert.throws(RangeError, () => new Temporal.Duration(...args_), description); + assert.compareArray(actual, expected, `${description} order of operations`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prop-desc.js new file mode 100644 index 0000000000..bc99900a4d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration +description: The "Duration" property of Temporal +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.Duration, + "function", + "`typeof Duration` is `function`" +); + +verifyProperty(Temporal, "Duration", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/basic.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/basic.js new file mode 100644 index 0000000000..d0ca50416f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/basic.js @@ -0,0 +1,42 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.abs +description: Temporal.Duration.prototype.abs will return absolute value of the input duration. +info: | + 1. Let duration be the this value. + 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]). + 3. Return ? CreateTemporalDuration(abs(duration.[[Years]]), abs(duration.[[Months]]), abs(duration.[[Weeks]]), abs(duration.[[Days]]), abs(duration.[[Hours]]), abs(duration.[[Minutes]]), abs(duration.[[Seconds]]), abs(duration.[[Milliseconds]]), abs(duration.[[Microseconds]]), abs(duration.[[Nanoseconds]])). + +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +let d1 = new Temporal.Duration(); +TemporalHelpers.assertDuration( + d1.abs(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "empty"); + +let d2 = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); +TemporalHelpers.assertDuration( + d2.abs(), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, "positive"); + +let d3 = new Temporal.Duration(1e5, 2e5, 3e5, 4e5, 5e5, 6e5, 7e5, 8e5, 9e5, 10e5); +TemporalHelpers.assertDuration( + d3.abs(), 1e5, 2e5, 3e5, 4e5, 5e5, 6e5, 7e5, 8e5, 9e5, 10e5, "large positive"); + +let d4 = new Temporal.Duration(-1, -2, -3, -4, -5, -6, -7, -8, -9, -10); +TemporalHelpers.assertDuration( + d4.abs(), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, "negative"); + +// Test with some zeros +let d5 = new Temporal.Duration(1, 0, 3, 0, 5, 0, 7, 0, 9, 0); +TemporalHelpers.assertDuration( + d5.abs(), 1, 0, 3, 0, 5, 0, 7, 0, 9, 0, "some zeros"); + +let d6 = new Temporal.Duration(0, 2, 0, 4, 0, 6, 0, 8, 0, 10); +TemporalHelpers.assertDuration( + d6.abs(), 0, 2, 0, 4, 0, 6, 0, 8, 0, 10, "other zeros"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/branding.js new file mode 100644 index 0000000000..dc0fb0bd6f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.abs +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const abs = Temporal.Duration.prototype.abs; + +assert.sameValue(typeof abs, "function"); + +assert.throws(TypeError, () => abs.call(undefined), "undefined"); +assert.throws(TypeError, () => abs.call(null), "null"); +assert.throws(TypeError, () => abs.call(true), "true"); +assert.throws(TypeError, () => abs.call(""), "empty string"); +assert.throws(TypeError, () => abs.call(Symbol()), "symbol"); +assert.throws(TypeError, () => abs.call(1), "1"); +assert.throws(TypeError, () => abs.call({}), "plain object"); +assert.throws(TypeError, () => abs.call(Temporal.Duration), "Temporal.Duration"); +assert.throws(TypeError, () => abs.call(Temporal.Duration.prototype), "Temporal.Duration.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/builtin.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/builtin.js new file mode 100644 index 0000000000..a04fcfc7f1 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/builtin.js @@ -0,0 +1,36 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.abs +description: > + Tests that Temporal.Duration.prototype.abs + meets the requirements for built-in objects defined by the + introduction of chapter 17 of the ECMAScript Language Specification. +info: | + Built-in functions that are not constructors do not have a "prototype" property unless + otherwise specified in the description of a particular function. + + Unless specified otherwise, a built-in object that is callable as a function is a built-in + function object with the characteristics described in 10.3. Unless specified otherwise, the + [[Extensible]] internal slot of a built-in object initially has the value true. + + Unless otherwise specified every built-in function and every built-in constructor has the + Function prototype object [...] as the value of its [[Prototype]] internal slot. +features: [Temporal] +---*/ + +assert.sameValue(Object.isExtensible(Temporal.Duration.prototype.abs), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.Duration.prototype.abs), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.Duration.prototype.abs), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.Duration.prototype.abs.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/length.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/length.js new file mode 100644 index 0000000000..3258db321c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/length.js @@ -0,0 +1,28 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2020 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.abs +description: Temporal.Duration.prototype.abs.length is 0 +info: | + Every built-in function object, including constructors, has a "length" property whose value is + an integer. Unless otherwise specified, this value is equal to the largest number of named + arguments shown in the subclause headings for the function description. Optional parameters + (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form + «...name») are not included in the default argument count. + + Unless otherwise specified, the "length" property of a built-in function object has the + attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +verifyProperty(Temporal.Duration.prototype.abs, "length", { + value: 0, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/name.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/name.js new file mode 100644 index 0000000000..3828c26727 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/name.js @@ -0,0 +1,26 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.abs +description: Temporal.Duration.prototype.abs.name is "abs". +info: | + Every built-in function object, including constructors, that is not identified as an anonymous + function has a "name" property whose value is a String. Unless otherwise specified, this value + is the name that is given to the function in this specification. + + Unless otherwise specified, the "name" property of a built-in function object, if it exists, + has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +verifyProperty(Temporal.Duration.prototype.abs, "name", { + value: "abs", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/new-object.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/new-object.js new file mode 100644 index 0000000000..d03be3a575 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/new-object.js @@ -0,0 +1,30 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.abs +description: Temporal.Duration.prototype.abs returns a new object. +features: [Temporal] +---*/ + +let d1 = new Temporal.Duration(); +assert.notSameValue(d1.abs(), d1); + +let d2 = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); +assert.notSameValue(d2.abs(), d2); + +let d3 = new Temporal.Duration(1e5, 2e5, 3e5, 4e5, 5e5, 6e5, 7e5, 8e5, 9e5, 10e5); +assert.notSameValue(d3.abs(), d3); + +let d4 = new Temporal.Duration(-1, -2, -3, -4, -5, -6, -7, -8, -9, -10); +assert.notSameValue(d4.abs(), d4); + +// Test with some zeros +let d5 = new Temporal.Duration(1, 0, 3, 0, 5, 0, 7, 0, 9, 0); +assert.notSameValue(d5.abs(), d5); + +let d6 = new Temporal.Duration(0, 2, 0, 4, 0, 6, 0, 8, 0, 10); +assert.notSameValue(d6.abs(), d6); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/not-a-constructor.js new file mode 100644 index 0000000000..ccf9dfd8a9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/not-a-constructor.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.abs +description: > + Temporal.Duration.prototype.abs does not implement [[Construct]], is not new-able +info: | + Built-in function objects that are not identified as constructors do not implement the + [[Construct]] internal method unless otherwise specified in the description of a particular + function. +includes: [isConstructor.js] +features: [Reflect.construct, Temporal] +---*/ + +assert.throws(TypeError, () => { + new Temporal.Duration.prototype.abs(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.Duration.prototype.abs), false, + "isConstructor(Temporal.Duration.prototype.abs)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/prop-desc.js new file mode 100644 index 0000000000..a27f045381 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.abs +description: The "abs" property of Temporal.Duration.prototype +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.Duration.prototype.abs, + "function", + "`typeof Duration.prototype.abs` is `function`" +); + +verifyProperty(Temporal.Duration.prototype, "abs", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/shell.js new file mode 100644 index 0000000000..eda1477282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/shell.js @@ -0,0 +1,24 @@ +// GENERATED, DO NOT EDIT +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/subclassing-ignored.js new file mode 100644 index 0000000000..c72ff6d9e2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/abs/subclassing-ignored.js @@ -0,0 +1,20 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.abs +description: Objects of a subclass are never created as return values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.checkSubclassingIgnored( + Temporal.Duration, + [0, 0, 0, -4, -5, -6, -7, -987, -654, -321], + "abs", + [], + (result) => TemporalHelpers.assertDuration(result, 0, 0, 0, 4, 5, 6, 7, 987, 654, 321), +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-invalid-property.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-invalid-property.js new file mode 100644 index 0000000000..c4131ad363 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-invalid-property.js @@ -0,0 +1,31 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: temporalDurationLike object must contain at least one correctly spelled property +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321); + +assert.throws( + TypeError, + () => instance.add({}), + "Throws TypeError if no property is present" +); + +assert.throws( + TypeError, + () => instance.add({ nonsense: true }), + "Throws TypeError if no recognized property is present" +); + +assert.throws( + TypeError, + () => instance.add({ sign: 1 }), + "Sign property is not recognized" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-mixed-sign.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-mixed-sign.js new file mode 100644 index 0000000000..b4c7a8de6b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-mixed-sign.js @@ -0,0 +1,20 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: Positive and negative values in the temporalDurationLike argument are not acceptable +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321); + +assert.throws( + RangeError, + () => instance.add({ hours: 1, minutes: -30 }), + `mixed positive and negative values always throw` +); + + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-not-object.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-not-object.js new file mode 100644 index 0000000000..e162f9fd75 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-not-object.js @@ -0,0 +1,22 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: Passing a primitive other than string to add() throws +features: [Symbol, Temporal] +---*/ + +const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321); +assert.throws(RangeError, () => instance.add(undefined), "undefined"); +assert.throws(RangeError, () => instance.add(null), "null"); +assert.throws(RangeError, () => instance.add(true), "boolean"); +assert.throws(RangeError, () => instance.add(""), "empty string"); +assert.throws(TypeError, () => instance.add(Symbol()), "Symbol"); +assert.throws(RangeError, () => instance.add(7), "number"); +assert.throws(RangeError, () => instance.add(7n), "bigint"); +assert.throws(TypeError, () => instance.add([]), "array"); +assert.throws(TypeError, () => instance.add(() => {}), "function"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-singular-properties.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-singular-properties.js new file mode 100644 index 0000000000..5689f9315c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-singular-properties.js @@ -0,0 +1,30 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: Singular properties in the property bag are always ignored +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321); + +[ + { year: 1 }, + { month: 2 }, + { week: 3 }, + { day: 4 }, + { hour: 5 }, + { minute: 6 }, + { second: 7 }, + { millisecond: 8 }, + { microsecond: 9 }, + { nanosecond: 10 }, +].forEach((badObject) => { + assert.throws(TypeError, () => instance.add(badObject), + "Throw TypeError if temporalDurationLike is not valid"); +}); + + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-string-fractional-units-rounding-mode.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-string-fractional-units-rounding-mode.js new file mode 100644 index 0000000000..f4be115484 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-string-fractional-units-rounding-mode.js @@ -0,0 +1,19 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: Strings with fractional duration units are rounded with the correct rounding mode +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const blank = new Temporal.Duration(); + +TemporalHelpers.assertDuration(blank.add("PT1.03125H"), 0, 0, 0, 0, 1, 1, 52, 500, 0, 0, + "positive fractional units rounded with correct rounding mode"); +TemporalHelpers.assertDuration(blank.add("-PT1.03125H"), 0, 0, 0, 0, -1, -1, -52, -500, 0, 0, + "negative fractional units rounded with correct rounding mode"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-string-negative-fractional-units.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-string-negative-fractional-units.js new file mode 100644 index 0000000000..60b1b0a4e5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-string-negative-fractional-units.js @@ -0,0 +1,20 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: Strings with fractional duration units are treated with the correct sign +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(); + +const resultHours = instance.add("-PT24.567890123H"); +TemporalHelpers.assertDuration(resultHours, 0, 0, 0, 0, -24, -34, -4, -404, -442, -800, "negative fractional hours"); + +const resultMinutes = instance.add("-PT1440.567890123M"); +TemporalHelpers.assertDuration(resultMinutes, 0, 0, 0, 0, 0, -1440, -34, -73, -407, -380, "negative fractional minutes"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-string.js new file mode 100644 index 0000000000..e31ef8436b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/argument-string.js @@ -0,0 +1,17 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: String arguments are supported. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const duration = Temporal.Duration.from({ days: 1, minutes: 5 }); +const result = duration.add("P2DT5M"); +TemporalHelpers.assertDuration(result, 0, 0, 0, 3, 0, 10, 0, 0, 0, 0, "String argument should be supported"); +assert.throws(RangeError, () => duration.add("2DT5M"), "Invalid string argument should throw"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/balance-negative-result.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/balance-negative-result.js new file mode 100644 index 0000000000..37253e952d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/balance-negative-result.js @@ -0,0 +1,39 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: A negative duration result is balanced correctly by the modulo operation in NanosecondsToDays +info: | + sec-temporal-nanosecondstodays step 6: + 6. If Type(_relativeTo_) is not Object or _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot, then + a. Return the new Record { ..., [[Nanoseconds]]: abs(_nanoseconds_) modulo _dayLengthNs_ × _sign_, ... }. + sec-temporal-balanceduration step 4: + 4. If _largestUnit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then + a. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _relativeTo_). + sec-temporal-addduration steps 5–6: + 5. If _relativeTo_ is *undefined*, then + ... + b. Let _result_ be ! BalanceDuration(_d1_ + _d2_, _h1_ + _h2_, _min1_ + _min2_, _s1_ + _s2_, _ms1_ + _ms2_, _mus1_ + _mus2_, _ns1_ + _ns2_, _largestUnit_). + ... + 6. Else if _relativeTo_ has an [[InitializedTemporalPlainDateTime]] internal slot, then + ... + n. Let _result_ be ! BalanceDuration(_dateDifference_.[[Days]], _h1_ + _h2_, _min1_ + _min2_, _s1_ + _s2_, _ms1_ + _ms2_, _mus1_ + _mus2_, _ns1_ + _ns2_, _largestUnit_). + sec-temporal.duration.prototype.add step 6: + 6. Let _result_ be ? AddDuration(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _other_.[[Years]], _other_.[[Months]], _other_.[[Weeks]], _other_.[[Days]], _other_.[[Hours]], _other_.[[Minutes]], _other_.[[Seconds]], _other_.[[Milliseconds]], _other_.[[Microseconds]], _other_.[[Nanoseconds]], _relativeTo_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const duration1 = new Temporal.Duration(0, 0, 0, 0, -60); +const duration2 = new Temporal.Duration(0, 0, 0, -1); + +const resultNotRelative = duration1.add(duration2); +TemporalHelpers.assertDuration(resultNotRelative, 0, 0, 0, -3, -12, 0, 0, 0, 0, 0); + +const relativeTo = new Temporal.PlainDateTime(2000, 1, 1); +const resultRelative = duration1.add(duration2, { relativeTo }); +TemporalHelpers.assertDuration(resultRelative, 0, 0, 0, -3, -12, 0, 0, 0, 0, 0); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/balance-negative-time-units.js new file mode 100644 index 0000000000..674ddc71c6 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/balance-negative-time-units.js @@ -0,0 +1,63 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: Negative time fields in relativeTo are balanced upwards +info: | + sec-temporal-balancetime steps 3–14: + 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000). + 4. Set _nanosecond_ to _nanosecond_ modulo 1000. + 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000). + 6. Set _microsecond_ to _microsecond_ modulo 1000. + 7. Set _second_ to _second_ + floor(_millisecond_ / 1000). + 8. Set _millisecond_ to _millisecond_ modulo 1000. + 9. Set _minute_ to _minute_ + floor(_second_ / 60). + 10. Set _second_ to _second_ modulo 60. + 11. Set _hour_ to _hour_ + floor(_minute_ / 60). + 12. Set _minute_ to _minute_ modulo 60. + 13. Let _days_ be floor(_hour_ / 24). + 14. Set _hour_ to _hour_ modulo 24. + sec-temporal-differencetime step 8: + 8. Let _bt_ be ? BalanceTime(_hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_). + sec-temporal-differenceisodatetime step 2: + 2. Let _timeDifference_ be ? DifferenceTime(_h1_, _min1_, _s1_, _ms1_, _mus1_, _ns1_, _h2_, _min2_, _s2_, _ms2_, _mus2_, _ns2_). + sec-temporal-differencezoneddatetime step 7: + 7. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _calendar_, _largestUnit_, _options_). + sec-temporal-addduration step 7.g.i: + i. Let _result_ be ? DifferenceZonedDateTime(_relativeTo_.[[Nanoseconds]], _endNs_, _timeZone_, _calendar_, _largestUnit_). + sec-temporal.duration.prototype.add step 6: + 6. Let _result_ be ? AddDuration(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _other_.[[Years]], _other_.[[Months]], _other_.[[Weeks]], _other_.[[Days]], _other_.[[Hours]], _other_.[[Minutes]], _other_.[[Seconds]], _other_.[[Milliseconds]], _other_.[[Microseconds]], _other_.[[Nanoseconds]], _relativeTo_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(0, 0, 0, 0, 1, 1, 1, 1, 1, 1); + +const timeZone = new Temporal.TimeZone("UTC"); +const relativeTo = new Temporal.ZonedDateTime(830998861_000_000_000n, timeZone); +// This code path is encountered if largestUnit is years, months, weeks, or days +// and relativeTo is a ZonedDateTime +const options = { largestUnit: "days", relativeTo }; + +const result1 = duration.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, -2), options); +TemporalHelpers.assertDuration(result1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 999, "nanoseconds balance"); + +const result2 = duration.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, -2), options); +TemporalHelpers.assertDuration(result2, 0, 0, 0, 0, 1, 1, 1, 0, 999, 1, "microseconds balance"); + +const result3 = duration.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, -2), options); +TemporalHelpers.assertDuration(result3, 0, 0, 0, 0, 1, 1, 0, 999, 1, 1, "milliseconds balance"); + +const result4 = duration.add(new Temporal.Duration(0, 0, 0, 0, 0, 0, -2), options); +TemporalHelpers.assertDuration(result4, 0, 0, 0, 0, 1, 0, 59, 1, 1, 1, "seconds balance"); + +const result5 = duration.add(new Temporal.Duration(0, 0, 0, 0, 0, -2), options); +TemporalHelpers.assertDuration(result5, 0, 0, 0, 0, 0, 59, 1, 1, 1, 1, "minutes balance"); + +// This one is different because hours are later balanced again in BalanceDuration +const result6 = duration.add(new Temporal.Duration(0, 0, 0, 0, -2), options); +TemporalHelpers.assertDuration(result6, 0, 0, 0, 0, 0, -58, -58, -998, -998, -999, "hours balance"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/basic.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/basic.js new file mode 100644 index 0000000000..d6802b7146 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/basic.js @@ -0,0 +1,34 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: Basic behavior +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const duration1 = Temporal.Duration.from({ days: 1, minutes: 5 }); +TemporalHelpers.assertDuration(duration1.add({ days: 2, minutes: 5 }), + 0, 0, 0, 3, 0, 10, 0, 0, 0, 0, "positive same units"); +TemporalHelpers.assertDuration(duration1.add({ hours: 12, seconds: 30 }), + 0, 0, 0, 1, 12, 5, 30, 0, 0, 0, "positive different units"); +TemporalHelpers.assertDuration(Temporal.Duration.from("P3DT10M").add({ days: -2, minutes: -5 }), + 0, 0, 0, 1, 0, 5, 0, 0, 0, 0, "negative same units"); +TemporalHelpers.assertDuration(Temporal.Duration.from("P1DT12H5M30S").add({ hours: -12, seconds: -30 }), + 0, 0, 0, 1, 0, 5, 0, 0, 0, 0, "negative different units"); +const duration2 = Temporal.Duration.from("P50DT50H50M50.500500500S"); +TemporalHelpers.assertDuration(duration2.add(duration2), + 0, 0, 0, 104, 5, 41, 41, 1, 1, 0, "balancing positive"); +const duration3 = Temporal.Duration.from({ hours: -1, seconds: -60 }); +TemporalHelpers.assertDuration(duration3.add({ minutes: 122 }), + 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, "balancing flipped sign 1"); +const duration4 = Temporal.Duration.from({ hours: -1, seconds: -3721 }); +TemporalHelpers.assertDuration(duration4.add({ minutes: 61, nanoseconds: 3722000000001 }), + 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, "balancing flipped sign 2"); +TemporalHelpers.assertDuration(duration1.add({ month: 1, days: 1 }), + 0, 0, 0, 2, 0, 5, 0, 0, 0, 0, + "incorrectly-spelled properties are ignored"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/branding.js new file mode 100644 index 0000000000..0a048e603b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/branding.js @@ -0,0 +1,27 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const add = Temporal.Duration.prototype.add; + +assert.sameValue(typeof add, "function"); + +const args = [new Temporal.Duration(0, 0, 0, 0, 5)]; + +assert.throws(TypeError, () => add.apply(undefined, args), "undefined"); +assert.throws(TypeError, () => add.apply(null, args), "null"); +assert.throws(TypeError, () => add.apply(true, args), "true"); +assert.throws(TypeError, () => add.apply("", args), "empty string"); +assert.throws(TypeError, () => add.apply(Symbol(), args), "symbol"); +assert.throws(TypeError, () => add.apply(1, args), "1"); +assert.throws(TypeError, () => add.apply({}, args), "plain object"); +assert.throws(TypeError, () => add.apply(Temporal.Duration, args), "Temporal.Duration"); +assert.throws(TypeError, () => add.apply(Temporal.Duration.prototype, args), "Temporal.Duration.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/builtin.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/builtin.js new file mode 100644 index 0000000000..44de286bfa --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/builtin.js @@ -0,0 +1,36 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: > + Tests that Temporal.Duration.prototype.add + meets the requirements for built-in objects defined by the + introduction of chapter 17 of the ECMAScript Language Specification. +info: | + Built-in functions that are not constructors do not have a "prototype" property unless + otherwise specified in the description of a particular function. + + Unless specified otherwise, a built-in object that is callable as a function is a built-in + function object with the characteristics described in 10.3. Unless specified otherwise, the + [[Extensible]] internal slot of a built-in object initially has the value true. + + Unless otherwise specified every built-in function and every built-in constructor has the + Function prototype object [...] as the value of its [[Prototype]] internal slot. +features: [Temporal] +---*/ + +assert.sameValue(Object.isExtensible(Temporal.Duration.prototype.add), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.Duration.prototype.add), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.Duration.prototype.add), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.Duration.prototype.add.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-dateadd-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-dateadd-called-with-options-undefined.js new file mode 100644 index 0000000000..68b83ce258 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-dateadd-called-with-options-undefined.js @@ -0,0 +1,20 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: > + BuiltinTimeZoneGetInstantFor calls Calendar.dateAdd with undefined as the + options value +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarDateAddUndefinedOptions(); +const timeZone = TemporalHelpers.oneShiftTimeZone(new Temporal.Instant(0n), 3600e9); +const instance = new Temporal.Duration(1, 1, 1, 1); +instance.add(instance, { relativeTo: new Temporal.ZonedDateTime(0n, timeZone, calendar) }); +assert.sameValue(calendar.dateAddCallCount, 5); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-dateadd-called-with-plaindate-instance.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-dateadd-called-with-plaindate-instance.js new file mode 100644 index 0000000000..673f3cef84 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-dateadd-called-with-plaindate-instance.js @@ -0,0 +1,21 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: > + relativeTo parameters that are not ZonedDateTime or undefined, are always + converted to PlainDate for observable calendar calls +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarDateAddPlainDateInstance(); +const instance = new Temporal.Duration(1, 1, 1, 1); +const relativeTo = new Temporal.PlainDate(2000, 1, 1, calendar); +calendar.specificPlainDate = relativeTo; +instance.add(instance, { relativeTo }); +assert(calendar.dateAddCallCount > 0, "assertions in calendar.dateAdd() should have been tested"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-dateadd.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-dateadd.js new file mode 100644 index 0000000000..86cce23446 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-dateadd.js @@ -0,0 +1,43 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: Duration.prototype.add should call dateAdd with the appropriate values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +let calls = 0; +const expected = [ + { + plainDate: [1920, 5, "M05", 3], + duration: [2, 0, 0, 4, 0, 0, 0, 0, 0, 0], + }, + { + plainDate: [1922, 5, "M05", 7], + duration: [0, 10, 0, 0, 0, 0, 0, 0, 0, 0], + }, +]; +class CustomCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + dateAdd(plainDate, duration, options) { + TemporalHelpers.assertPlainDate(plainDate, ...expected[calls].plainDate, + `plainDate argument ${calls}`); + TemporalHelpers.assertDuration(duration, ...expected[calls].duration, + `duration argument ${calls}`); + assert.sameValue(options, undefined, "options argument"); + ++calls; + return super.dateAdd(plainDate, duration, options); + } +} +const relativeTo = new Temporal.PlainDate(1920, 5, 3, new CustomCalendar()); +const duration = new Temporal.Duration(2, 0, 0, 4, 2); +const result = duration.add({ months: 10, hours: 14 }, { relativeTo }); +TemporalHelpers.assertDuration(result, 2, 10, 0, 4, 16, 0, 0, 0, 0, 0, "result"); +assert.sameValue(calls, 2, "should have called dateAdd"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-dateuntil-called-with-singular-largestunit.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-dateuntil-called-with-singular-largestunit.js new file mode 100644 index 0000000000..44d2e160c2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-dateuntil-called-with-singular-largestunit.js @@ -0,0 +1,81 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form +info: | + sec-temporal.duration.prototype.add step 6: + 6. Let _result_ be ? AddDuration(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _other_.[[Years]], _other_.[[Months]], _other_.[[Weeks]], _other_.[[Days]], _other_.[[Hours]], _other_.[[Minutes]], _other_.[[Seconds]], _other_.[[Milliseconds]], _other_.[[Microseconds]], _other_.[[Nanoseconds]], _relativeTo_). + sec-temporal-addduration steps 6-7: + 6. If _relativeTo_ has an [[InitializedTemporalPlainDateTime]] internal slot, then + ... + j. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_). + k. Let _differenceOptions_ be ! OrdinaryObjectCreate(*null*). + l. Perform ! CreateDataPropertyOrThrow(_differenceOptions_, *"largestUnit"*, _dateLargestUnit_). + m. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _datePart_, _end_, _differenceOptions_). + ... + 7. Else, + a. Assert: _relativeTo_ has an [[IntializedTemporalZonedDateTime]] internal slot. + ... + f. If _largestUnit_ is not one of *"year"*, *"month"*, *"week"*, or *"day"*, then + ... + g. Else, + i. Let _result_ be ? DifferenceZonedDateTime(_relativeTo_.[[Nanoseconds]], _endNs_, _timeZone_, _calendar_, _largestUnit_). + sec-temporal-differencezoneddatetime steps 7 and 11: + 7. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _calendar_, _largestUnit_, _options_). + 11. Let _result_ be ? NanosecondsToDays(_timeRemainderNs_, _intermediate_). + sec-temporal-nanosecondstodays step 11: + 11. 1. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _relativeTo_.[[Calendar]], *"day"*). + sec-temporal-differenceisodatetime steps 9–11: + 9. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_). + 10. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _dateLargestUnit_). + 11. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.checkCalendarDateUntilLargestUnitSingular( + (calendar, largestUnit, index) => { + const one = new Temporal.Duration(...[...Array(index).fill(0), ...Array(10 - index).fill(1)]); + const two = new Temporal.Duration(...[...Array(index).fill(0), ...Array(10 - index).fill(2)]); + const relativeTo = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar); + one.add(two, { relativeTo, largestUnit }); + }, + { + years: ["year"], + months: ["month"], + weeks: ["week"], + days: ["day"], + hours: ["day"], + minutes: ["day"], + seconds: ["day"], + milliseconds: ["day"], + microseconds: ["day"], + nanoseconds: ["day"] + } +); + +TemporalHelpers.checkCalendarDateUntilLargestUnitSingular( + (calendar, largestUnit, index) => { + const one = new Temporal.Duration(...[...Array(index).fill(0), ...Array(10 - index).fill(1)]); + const two = new Temporal.Duration(...[...Array(index).fill(0), ...Array(10 - index).fill(2)]); + const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar); + one.add(two, { relativeTo, largestUnit }); + }, + { + years: ["year", "day"], + months: ["month", "day"], + weeks: ["week", "day"], + days: ["day", "day"], + hours: [], + minutes: [], + seconds: [], + milliseconds: [], + microseconds: [], + nanoseconds: [] + } +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-fields-iterable.js new file mode 100644 index 0000000000..82fa2a519f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-fields-iterable.js @@ -0,0 +1,41 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: Verify the result of calendar.fields() is treated correctly. +info: | + sec-temporal.duration.prototype.add step 5: + 5. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_). + sec-temporal-torelativetemporalobject step 4.c: + c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »). + sec-temporal-calendarfields step 4: + 4. Let _result_ be ? IterableToList(_fieldsArray_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "day", + "hour", + "microsecond", + "millisecond", + "minute", + "month", + "monthCode", + "nanosecond", + "second", + "year", +]; + +const calendar = TemporalHelpers.calendarFieldsIterable(); +const duration1 = new Temporal.Duration(1); +const duration2 = new Temporal.Duration(0, 12); +duration1.add(duration2, { relativeTo: { year: 2000, month: 1, day: 1, calendar } }); + +assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once"); +assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args"); +assert(calendar.iteratorExhausted[0], "iterated through the whole iterable"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-temporal-object.js new file mode 100644 index 0000000000..5a47578600 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/calendar-temporal-object.js @@ -0,0 +1,30 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots +info: | + sec-temporal.duration.prototype.add step 5: + 5. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_). + sec-temporal-torelativetemporalobject step 4.b: + b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_). + sec-temporal-gettemporalcalendarwithisodefault step 2: + 2. Return ? ToTemporalCalendarWithISODefault(_calendar_). + sec-temporal-totemporalcalendarwithisodefault step 2b + 3. Return ? ToTemporalCalendar(_temporalCalendarLike_). + sec-temporal-totemporalcalendar step 1.a: + a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then + i. Return _temporalCalendarLike_.[[Calendar]]. +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => { + const duration1 = new Temporal.Duration(1); + const duration2 = new Temporal.Duration(0, 12); + duration1.add(duration2, { relativeTo: { year: 2000, month: 1, day: 1, calendar: temporalObject } }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/days-is-number-max-value-with-zoneddatetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/days-is-number-max-value-with-zoneddatetime.js new file mode 100644 index 0000000000..abcddc50cb --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/days-is-number-max-value-with-zoneddatetime.js @@ -0,0 +1,21 @@ +// |reftest| skip -- Temporal is not supported +// 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.add +description: > + AddZonedDateTime throws a RangeError when the intermediate instant is too large. +features: [Temporal] +---*/ + +const plainDate = new Temporal.PlainDate(1970, 1, 1); +const zonedDateTime = new Temporal.ZonedDateTime(0n, "UTC", "iso8601"); + +var duration = Temporal.Duration.from({days: 1, nanoseconds: Number.MAX_VALUE}); + +var options = {relativeTo: zonedDateTime}; + +assert.throws(RangeError, () => duration.add(duration, options)); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/days-is-number-max-value.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/days-is-number-max-value.js new file mode 100644 index 0000000000..5568e53d14 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/days-is-number-max-value.js @@ -0,0 +1,16 @@ +// |reftest| skip -- Temporal is not supported +// 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.add +description: > + BalanceDuration throws a RangeError when the result is too large. +features: [Temporal] +---*/ + +var duration = Temporal.Duration.from({days: Number.MAX_VALUE}); + +assert.throws(RangeError, () => duration.add(duration)); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/infinity-throws-rangeerror.js new file mode 100644 index 0000000000..be90f77c69 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/infinity-throws-rangeerror.js @@ -0,0 +1,34 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2020 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Temporal.Duration.prototype.add throws a RangeError if any value in a property bag is Infinity +esid: sec-temporal.duration.prototype.add +features: [Temporal] +---*/ + +const fields = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds', 'nanoseconds']; + +const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); +const relativeTo = new Temporal.PlainDateTime(2000, 1, 1); + +fields.forEach((field) => { + assert.throws(RangeError, () => instance.add({ [field]: Infinity }, { relativeTo })); +}); + +let calls = 0; +const obj = { + valueOf() { + calls++; + return Infinity; + } +}; + +fields.forEach((field) => { + calls = 0; + assert.throws(RangeError, () => instance.add({ [field]: obj }, { relativeTo })); + assert.sameValue(calls, 1, "it fails after fetching the primitive value"); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/length.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/length.js new file mode 100644 index 0000000000..4f3dda7c0b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/length.js @@ -0,0 +1,28 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2020 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: Temporal.Duration.prototype.add.length is 1 +info: | + Every built-in function object, including constructors, has a "length" property whose value is + an integer. Unless otherwise specified, this value is equal to the largest number of named + arguments shown in the subclause headings for the function description. Optional parameters + (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form + «...name») are not included in the default argument count. + + Unless otherwise specified, the "length" property of a built-in function object has the + attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +verifyProperty(Temporal.Duration.prototype.add, "length", { + value: 1, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/name.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/name.js new file mode 100644 index 0000000000..ffb2252c49 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/name.js @@ -0,0 +1,26 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: Temporal.Duration.prototype.add.name is "add". +info: | + Every built-in function object, including constructors, that is not identified as an anonymous + function has a "name" property whose value is a String. Unless otherwise specified, this value + is the name that is given to the function in this specification. + + Unless otherwise specified, the "name" property of a built-in function object, if it exists, + has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +verifyProperty(Temporal.Duration.prototype.add, "name", { + value: "add", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/nanoseconds-is-number-max-safe-integer.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/nanoseconds-is-number-max-safe-integer.js new file mode 100644 index 0000000000..0853f71db1 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/nanoseconds-is-number-max-safe-integer.js @@ -0,0 +1,60 @@ +// |reftest| skip -- Temporal is not supported +// 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.add +description: > + AddDuration computes on exact mathematical number values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const plainDate = new Temporal.PlainDate(1970, 1, 1); +const zonedDateTime = new Temporal.ZonedDateTime(0n, "UTC", "iso8601"); + +// Largest temporal unit is "day". +const duration1 = Temporal.Duration.from({nanoseconds: Number.MAX_SAFE_INTEGER}); +const duration2 = Temporal.Duration.from({nanoseconds: 2, days: 1}); +const nanos = BigInt(Number.MAX_SAFE_INTEGER) + 2n; + +TemporalHelpers.assertDuration( + duration1.add(duration2), + 0, 0, 0, + 1 + Number((nanos / (24n * 60n * 60n * 1_000_000_000n))), + Number((nanos / (60n * 60n * 1_000_000_000n)) % 24n), + Number((nanos / (60n * 1_000_000_000n)) % 60n), + Number((nanos / 1_000_000_000n) % 60n), + Number((nanos / 1_000_000n) % 1000n), + Number((nanos / 1000n) % 1000n), + Number(nanos % 1000n), + "duration1.add(duration2)" +); + +TemporalHelpers.assertDuration( + duration1.add(duration2, {relativeTo: plainDate}), + 0, 0, 0, + 1 + Number((nanos / (24n * 60n * 60n * 1_000_000_000n))), + Number((nanos / (60n * 60n * 1_000_000_000n)) % 24n), + Number((nanos / (60n * 1_000_000_000n)) % 60n), + Number((nanos / 1_000_000_000n) % 60n), + Number((nanos / 1_000_000n) % 1000n), + Number((nanos / 1000n) % 1000n), + Number(nanos % 1000n), + "duration1.add(duration2, {relativeTo: plainDate})" +); + +TemporalHelpers.assertDuration( + duration1.add(duration2, {relativeTo: zonedDateTime}), + 0, 0, 0, + 1 + Number((nanos / (24n * 60n * 60n * 1_000_000_000n))), + Number((nanos / (60n * 60n * 1_000_000_000n)) % 24n), + Number((nanos / (60n * 1_000_000_000n)) % 60n), + Number((nanos / 1_000_000_000n) % 60n), + Number((nanos / 1_000_000n) % 1000n), + Number((nanos / 1000n) % 1000n), + Number(nanos % 1000n), + "duration1.add(duration2, {relativeTo: zonedDateTime})" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/nanoseconds-is-number-max-value-1.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/nanoseconds-is-number-max-value-1.js new file mode 100644 index 0000000000..f797dcf356 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/nanoseconds-is-number-max-value-1.js @@ -0,0 +1,30 @@ +// |reftest| skip -- Temporal is not supported +// 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.add +description: > + BalanceDuration throws a RangeError when the result is too large. +features: [Temporal] +---*/ + +const plainDate = new Temporal.PlainDate(1970, 1, 1); +const zonedDateTime = new Temporal.ZonedDateTime(0n, "UTC", "iso8601"); + +// Largest temporal unit is "nanosecond". +const duration = Temporal.Duration.from({nanoseconds: Number.MAX_VALUE}); + +assert.throws(RangeError, () => { + duration.add(duration); +}); + +assert.throws(RangeError, () => { + duration.add(duration, {relativeTo: plainDate}); +}); + +assert.throws(RangeError, () => { + duration.add(duration, {relativeTo: zonedDateTime}); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/nanoseconds-is-number-max-value-2.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/nanoseconds-is-number-max-value-2.js new file mode 100644 index 0000000000..9667ce1847 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/nanoseconds-is-number-max-value-2.js @@ -0,0 +1,52 @@ +// |reftest| skip -- Temporal is not supported +// 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.add +description: > + BalanceDuration computes on exact mathematical number values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const plainDate = new Temporal.PlainDate(1970, 1, 1); +const zonedDateTime = new Temporal.ZonedDateTime(0n, "UTC", "iso8601"); + +// Largest temporal unit is "day". +const duration1 = Temporal.Duration.from({nanoseconds: Number.MAX_VALUE}); +const duration2 = Temporal.Duration.from({nanoseconds: Number.MAX_VALUE, days: 1}); +const nanos = BigInt(Number.MAX_VALUE) * 2n; + +TemporalHelpers.assertDuration( + duration1.add(duration2), + 0, 0, 0, + 1 + Number((nanos / (24n * 60n * 60n * 1_000_000_000n))), + Number((nanos / (60n * 60n * 1_000_000_000n)) % 24n), + Number((nanos / (60n * 1_000_000_000n)) % 60n), + Number((nanos / 1_000_000_000n) % 60n), + Number((nanos / 1_000_000n) % 1000n), + Number((nanos / 1000n) % 1000n), + Number(nanos % 1000n), + "duration1.add(duration2)" +); + +TemporalHelpers.assertDuration( + duration1.add(duration2, {relativeTo: plainDate}), + 0, 0, 0, + 1 + Number((nanos / (24n * 60n * 60n * 1_000_000_000n))), + Number((nanos / (60n * 60n * 1_000_000_000n)) % 24n), + Number((nanos / (60n * 1_000_000_000n)) % 60n), + Number((nanos / 1_000_000_000n) % 60n), + Number((nanos / 1_000_000n) % 1000n), + Number((nanos / 1000n) % 1000n), + Number(nanos % 1000n), + "duration1.add(duration2, {relativeTo: plainDate})" +); + +// Throws a RangeError because the intermediate instant is too large. +assert.throws(RangeError, () => { + duration1.add(duration2, {relativeTo: zonedDateTime}); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/nanoseconds-to-days-loop-indefinitely-1.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/nanoseconds-to-days-loop-indefinitely-1.js new file mode 100644 index 0000000000..4d2a82af87 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/nanoseconds-to-days-loop-indefinitely-1.js @@ -0,0 +1,61 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: > + NanosecondsToDays can loop arbitrarily long, performing observable operations each iteration. +info: | + NanosecondsToDays ( nanoseconds, relativeTo ) + + ... + 15. If sign is 1, then + a. Repeat, while days > 0 and intermediateNs > endNs, + i. Set days to days - 1. + ii. Set intermediateNs to ℝ(? AddZonedDateTime(ℤ(startNs), relativeTo.[[TimeZone]], + relativeTo.[[Calendar]], 0, 0, 0, days, 0, 0, 0, 0, 0, 0)). + ... +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calls = []; +const duration = Temporal.Duration.from({ days: 1 }); + +function createRelativeTo(count) { + const tz = new Temporal.TimeZone("UTC"); + // Record calls in calls[] + TemporalHelpers.observeMethod(calls, tz, "getPossibleInstantsFor"); + const cal = new Temporal.Calendar("iso8601"); + // Return _count_ days for the second call to dateUntil, behaving normally after + TemporalHelpers.substituteMethod(cal, "dateUntil", [ + TemporalHelpers.SUBSTITUTE_SKIP, + Temporal.Duration.from({ days: count }), + ]); + return new Temporal.ZonedDateTime(0n, tz, cal); +} + +let zdt = createRelativeTo(200); +calls.splice(0); // Reset calls list after ZonedDateTime construction +duration.add(duration, { + relativeTo: zdt, +}); +assert.sameValue( + calls.length, + 200 + 2, + "Expected duration.add to call getPossibleInstantsFor correct number of times" +); + +zdt = createRelativeTo(300); +calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction +duration.add(duration, { + relativeTo: zdt, +}); +assert.sameValue( + calls.length, + 300 + 2, + "Expected duration.add to call getPossibleInstantsFor correct number of times" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/nanoseconds-to-days-loop-indefinitely-2.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/nanoseconds-to-days-loop-indefinitely-2.js new file mode 100644 index 0000000000..9d6477df95 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/nanoseconds-to-days-loop-indefinitely-2.js @@ -0,0 +1,74 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: > + NanosecondsToDays can loop infinitely. +info: | + NanosecondsToDays ( nanoseconds, relativeTo ) + + ... + 18. Repeat, while done is false, + a. Let oneDayFartherNs be ℝ(? AddZonedDateTime(ℤ(intermediateNs), relativeTo.[[TimeZone]], + relativeTo.[[Calendar]], 0, 0, 0, sign, 0, 0, 0, 0, 0, 0)). + b. Set dayLengthNs to oneDayFartherNs - intermediateNs. + c. If (nanoseconds - dayLengthNs) × sign ≥ 0, then + i. Set nanoseconds to nanoseconds - dayLengthNs. + ii. Set intermediateNs to oneDayFartherNs. + iii. Set days to days + sign. + d. Else, + i. Set done to true. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calls = []; +const duration = Temporal.Duration.from({ days: 1 }); + +function createRelativeTo(count) { + const dayLengthNs = 86400000000000n; + const dayInstant = new Temporal.Instant(dayLengthNs); + const substitutions = []; + const timeZone = new Temporal.TimeZone("UTC"); + // Return constant value for first _count_ calls + TemporalHelpers.substituteMethod( + timeZone, + "getPossibleInstantsFor", + substitutions + ); + substitutions.length = count; + let i = 0; + for (i = 0; i < substitutions.length; i++) { + // (this value) + substitutions[i] = [dayInstant]; + } + // Record calls in calls[] + TemporalHelpers.observeMethod(calls, timeZone, "getPossibleInstantsFor"); + return new Temporal.ZonedDateTime(0n, timeZone); +} + +let zdt = createRelativeTo(200); +calls.splice(0); // Reset calls list after ZonedDateTime construction +duration.add(duration, { + relativeTo: zdt, +}); +assert.sameValue( + calls.length, + 200 + 1, + "Expected duration.add to call getPossibleInstantsFor correct number of times" +); + +zdt = createRelativeTo(300); +calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction +duration.add(duration, { + relativeTo: zdt, +}); +assert.sameValue( + calls.length, + 300 + 1, + "Expected duration.add to call getPossibleInstantsFor correct number of times" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/negative-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/negative-infinity-throws-rangeerror.js new file mode 100644 index 0000000000..f21efe9b02 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/negative-infinity-throws-rangeerror.js @@ -0,0 +1,34 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2020 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Temporal.Duration.prototype.add throws a RangeError if any value in a property bag is -Infinity +esid: sec-temporal.duration.prototype.add +features: [Temporal] +---*/ + +const fields = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds', 'nanoseconds']; + +const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); +const relativeTo = new Temporal.PlainDateTime(2000, 1, 1); + +fields.forEach((field) => { + assert.throws(RangeError, () => instance.add({ [field]: -Infinity }, { relativeTo })); +}); + +let calls = 0; +const obj = { + valueOf() { + calls++; + return -Infinity; + } +}; + +fields.forEach((field) => { + calls = 0; + assert.throws(RangeError, () => instance.add({ [field]: obj }, { relativeTo })); + assert.sameValue(calls, 1, "it fails after fetching the primitive value"); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/non-integer-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/non-integer-throws-rangeerror.js new file mode 100644 index 0000000000..8b950ed69e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/non-integer-throws-rangeerror.js @@ -0,0 +1,29 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: A non-integer value for any recognized property in the property bag, throws a RangeError +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321); +const fields = [ + "years", + "months", + "weeks", + "days", + "hours", + "minutes", + "seconds", + "milliseconds", + "microseconds", + "nanoseconds", +]; +fields.forEach((field) => { + assert.throws(RangeError, () => instance.add({ [field]: 1.5 })); + assert.throws(RangeError, () => instance.add({ [field]: -1.5 })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/not-a-constructor.js new file mode 100644 index 0000000000..6ffce9bca4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/not-a-constructor.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: > + Temporal.Duration.prototype.add does not implement [[Construct]], is not new-able +info: | + Built-in function objects that are not identified as constructors do not implement the + [[Construct]] internal method unless otherwise specified in the description of a particular + function. +includes: [isConstructor.js] +features: [Reflect.construct, Temporal] +---*/ + +assert.throws(TypeError, () => { + new Temporal.Duration.prototype.add(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.Duration.prototype.add), false, + "isConstructor(Temporal.Duration.prototype.add)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/options-object.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/options-object.js new file mode 100644 index 0000000000..9729d5d29f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/options-object.js @@ -0,0 +1,26 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: Empty or a function object may be used as options +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(0, 0, 0, 0, 1); + +const result1 = instance.add({ hours: 1 }, {}); +TemporalHelpers.assertDuration( + result1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, + "options may be an empty plain object" +); + +const result2 = instance.add({ hours: 1 }, () => {}); +TemporalHelpers.assertDuration( + result2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, + "options may be a function object" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/options-undefined.js new file mode 100644 index 0000000000..a7c8517fff --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/options-undefined.js @@ -0,0 +1,35 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: Verify that undefined options are handled correctly. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const duration1 = new Temporal.Duration(1); +const duration2 = new Temporal.Duration(0, 12); +const duration3 = new Temporal.Duration(0, 0, 0, 1); +const duration4 = new Temporal.Duration(0, 0, 0, 0, 24); + +assert.throws(RangeError, () => duration1.add(duration2), "no options with years"); +TemporalHelpers.assertDuration(duration3.add(duration4), + 0, 0, 0, /* days = */ 2, 0, 0, 0, 0, 0, 0, + "no options with days"); + +const optionValues = [ + [undefined, "undefined"], + [{}, "plain object"], + [() => {}, "lambda"], +]; +for (const [options, description] of optionValues) { + assert.throws(RangeError, () => duration1.add(duration2, options), + `options ${description} with years`); + TemporalHelpers.assertDuration(duration3.add(duration4, options), + 0, 0, 0, /* days = */ 2, 0, 0, 0, 0, 0, 0, + `options ${description} with days`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/options-wrong-type.js new file mode 100644 index 0000000000..0be8538fd4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/options-wrong-type.js @@ -0,0 +1,26 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: TypeError thrown when options argument is a primitive +features: [BigInt, Symbol, Temporal] +---*/ + +const badOptions = [ + null, + true, + "some string", + Symbol(), + 1, + 2n, +]; + +const instance = new Temporal.Duration(0, 0, 0, 0, 1); +for (const value of badOptions) { + assert.throws(TypeError, () => instance.add({ hours: 1 }, value), + `TypeError on wrong options type ${typeof value}`); +}; + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/order-of-operations.js new file mode 100644 index 0000000000..7f13364fe8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/order-of-operations.js @@ -0,0 +1,260 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2020 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: Properties on an object passed to add() are accessed in the correct order +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + // ToTemporalDurationRecord + "get fields.days", + "get fields.days.valueOf", + "call fields.days.valueOf", + "get fields.hours", + "get fields.hours.valueOf", + "call fields.hours.valueOf", + "get fields.microseconds", + "get fields.microseconds.valueOf", + "call fields.microseconds.valueOf", + "get fields.milliseconds", + "get fields.milliseconds.valueOf", + "call fields.milliseconds.valueOf", + "get fields.minutes", + "get fields.minutes.valueOf", + "call fields.minutes.valueOf", + "get fields.months", + "get fields.months.valueOf", + "call fields.months.valueOf", + "get fields.nanoseconds", + "get fields.nanoseconds.valueOf", + "call fields.nanoseconds.valueOf", + "get fields.seconds", + "get fields.seconds.valueOf", + "call fields.seconds.valueOf", + "get fields.weeks", + "get fields.weeks.valueOf", + "call fields.weeks.valueOf", + "get fields.years", + "get fields.years.valueOf", + "call fields.years.valueOf", + // ToRelativeTemporalObject + "get options.relativeTo", +]; +const actual = []; + +const simpleFields = TemporalHelpers.propertyBagObserver(actual, { + years: 0, + months: 0, + weeks: 0, + days: 1, + hours: 1, + minutes: 1, + seconds: 1, + milliseconds: 1, + microseconds: 1, + nanoseconds: 1, +}, "fields"); + +function createOptionsObserver(relativeTo = undefined) { + return TemporalHelpers.propertyBagObserver(actual, { relativeTo }, "options"); +} + +// basic order of observable operations, without any calendar units: +const simpleInstance = new Temporal.Duration(0, 0, 0, 1, 1, 1, 1, 1, 1, 1); +simpleInstance.add(simpleFields, createOptionsObserver()); +assert.compareArray(actual, expected, "order of operations"); +actual.splice(0); // clear + +const expectedOpsForPlainRelativeTo = expected.concat([ + // ToRelativeTemporalObject + "get options.relativeTo.calendar", + "has options.relativeTo.calendar.calendar", + "get options.relativeTo.calendar.fields", + "call options.relativeTo.calendar.fields", + // PrepareTemporalFields + "get options.relativeTo.day", + "get options.relativeTo.day.valueOf", + "call options.relativeTo.day.valueOf", + "get options.relativeTo.hour", + "get options.relativeTo.microsecond", + "get options.relativeTo.millisecond", + "get options.relativeTo.minute", + "get options.relativeTo.month", + "get options.relativeTo.month.valueOf", + "call options.relativeTo.month.valueOf", + "get options.relativeTo.monthCode", + "get options.relativeTo.monthCode.toString", + "call options.relativeTo.monthCode.toString", + "get options.relativeTo.nanosecond", + "get options.relativeTo.offset", + "get options.relativeTo.second", + "get options.relativeTo.timeZone", + "get options.relativeTo.year", + "get options.relativeTo.year.valueOf", + "call options.relativeTo.year.valueOf", + // InterpretTemporalDateTimeFields + "get options.relativeTo.calendar.dateFromFields", + "call options.relativeTo.calendar.dateFromFields", + // AddDuration + "get options.relativeTo.calendar.dateAdd", + "call options.relativeTo.calendar.dateAdd", + "call options.relativeTo.calendar.dateAdd", + "get options.relativeTo.calendar.dateUntil", + "call options.relativeTo.calendar.dateUntil", +]); + +const instance = new Temporal.Duration(1, 2, 0, 4, 5, 6, 7, 987, 654, 321); + +const fields = TemporalHelpers.propertyBagObserver(actual, { + years: 1, + months: 1, + weeks: 1, + days: 1, + hours: 1, + minutes: 1, + seconds: 1, + milliseconds: 1, + microseconds: 1, + nanoseconds: 1, +}, "fields"); + +const plainRelativeTo = TemporalHelpers.propertyBagObserver(actual, { + year: 2000, + month: 1, + monthCode: "M01", + day: 1, + calendar: TemporalHelpers.calendarObserver(actual, "options.relativeTo.calendar"), +}, "options.relativeTo"); + +instance.add(fields, createOptionsObserver(plainRelativeTo)); +assert.compareArray(actual, expectedOpsForPlainRelativeTo, "order of operations with PlainDate relativeTo"); +actual.splice(0); // clear + +const expectedOpsForZonedRelativeTo = expected.concat([ + // ToRelativeTemporalObject + "get options.relativeTo.calendar", + "has options.relativeTo.calendar.calendar", + "get options.relativeTo.calendar.fields", + "call options.relativeTo.calendar.fields", + // PrepareTemporalFields + "get options.relativeTo.day", + "get options.relativeTo.day.valueOf", + "call options.relativeTo.day.valueOf", + "get options.relativeTo.hour", + "get options.relativeTo.hour.valueOf", + "call options.relativeTo.hour.valueOf", + "get options.relativeTo.microsecond", + "get options.relativeTo.microsecond.valueOf", + "call options.relativeTo.microsecond.valueOf", + "get options.relativeTo.millisecond", + "get options.relativeTo.millisecond.valueOf", + "call options.relativeTo.millisecond.valueOf", + "get options.relativeTo.minute", + "get options.relativeTo.minute.valueOf", + "call options.relativeTo.minute.valueOf", + "get options.relativeTo.month", + "get options.relativeTo.month.valueOf", + "call options.relativeTo.month.valueOf", + "get options.relativeTo.monthCode", + "get options.relativeTo.monthCode.toString", + "call options.relativeTo.monthCode.toString", + "get options.relativeTo.nanosecond", + "get options.relativeTo.nanosecond.valueOf", + "call options.relativeTo.nanosecond.valueOf", + "get options.relativeTo.offset", + "get options.relativeTo.offset.toString", + "call options.relativeTo.offset.toString", + "get options.relativeTo.second", + "get options.relativeTo.second.valueOf", + "call options.relativeTo.second.valueOf", + "get options.relativeTo.timeZone", + "get options.relativeTo.year", + "get options.relativeTo.year.valueOf", + "call options.relativeTo.year.valueOf", + // InterpretTemporalDateTimeFields + "get options.relativeTo.calendar.dateFromFields", + "call options.relativeTo.calendar.dateFromFields", + // ToRelativeTemporalObject again + "has options.relativeTo.timeZone.timeZone", + // InterpretISODateTimeOffset + "get options.relativeTo.timeZone.getPossibleInstantsFor", + "call options.relativeTo.timeZone.getPossibleInstantsFor", + "get options.relativeTo.timeZone.getOffsetNanosecondsFor", + "call options.relativeTo.timeZone.getOffsetNanosecondsFor", + // AddDuration → AddZonedDateTime 1 + "get options.relativeTo.timeZone.getOffsetNanosecondsFor", + "call options.relativeTo.timeZone.getOffsetNanosecondsFor", + "get options.relativeTo.calendar.dateAdd", + "call options.relativeTo.calendar.dateAdd", + "get options.relativeTo.timeZone.getPossibleInstantsFor", + "call options.relativeTo.timeZone.getPossibleInstantsFor", + // AddDuration → AddZonedDateTime 2 + "get options.relativeTo.timeZone.getOffsetNanosecondsFor", + "call options.relativeTo.timeZone.getOffsetNanosecondsFor", + "get options.relativeTo.calendar.dateAdd", + "call options.relativeTo.calendar.dateAdd", + "get options.relativeTo.timeZone.getPossibleInstantsFor", + "call options.relativeTo.timeZone.getPossibleInstantsFor", + // AddDuration → DifferenceZonedDateTime + "get options.relativeTo.timeZone.getOffsetNanosecondsFor", + "call options.relativeTo.timeZone.getOffsetNanosecondsFor", + "get options.relativeTo.timeZone.getOffsetNanosecondsFor", + "call options.relativeTo.timeZone.getOffsetNanosecondsFor", + // AddDuration → DifferenceZonedDateTime → DifferenceISODateTime + "get options.relativeTo.calendar.dateUntil", + "call options.relativeTo.calendar.dateUntil", + // AddDuration → DifferenceZonedDateTime → AddZonedDateTime + "get options.relativeTo.timeZone.getOffsetNanosecondsFor", + "call options.relativeTo.timeZone.getOffsetNanosecondsFor", + "get options.relativeTo.calendar.dateAdd", + "call options.relativeTo.calendar.dateAdd", + "get options.relativeTo.timeZone.getPossibleInstantsFor", + "call options.relativeTo.timeZone.getPossibleInstantsFor", + // AddDuration → DifferenceZonedDateTime → NanosecondsToDays + "get options.relativeTo.timeZone.getOffsetNanosecondsFor", + "call options.relativeTo.timeZone.getOffsetNanosecondsFor", + "get options.relativeTo.timeZone.getOffsetNanosecondsFor", + "call options.relativeTo.timeZone.getOffsetNanosecondsFor", + // AddDuration → DifferenceZonedDateTime → NanosecondsToDays → DifferenceISODateTime + "get options.relativeTo.calendar.dateUntil", + "call options.relativeTo.calendar.dateUntil", + // AddDuration → DifferenceZonedDateTime → NanosecondsToDays → AddZonedDateTime 1 + "get options.relativeTo.timeZone.getOffsetNanosecondsFor", + "call options.relativeTo.timeZone.getOffsetNanosecondsFor", + "get options.relativeTo.calendar.dateAdd", + "call options.relativeTo.calendar.dateAdd", + "get options.relativeTo.timeZone.getPossibleInstantsFor", + "call options.relativeTo.timeZone.getPossibleInstantsFor", + // AddDuration → DifferenceZonedDateTime → NanosecondsToDays → AddZonedDateTime 2 + "get options.relativeTo.timeZone.getOffsetNanosecondsFor", + "call options.relativeTo.timeZone.getOffsetNanosecondsFor", + "get options.relativeTo.calendar.dateAdd", + "call options.relativeTo.calendar.dateAdd", + "get options.relativeTo.timeZone.getPossibleInstantsFor", + "call options.relativeTo.timeZone.getPossibleInstantsFor", +]); + +const zonedRelativeTo = TemporalHelpers.propertyBagObserver(actual, { + year: 2000, + month: 1, + monthCode: "M01", + day: 1, + hour: 0, + minute: 0, + second: 0, + millisecond: 0, + microsecond: 0, + nanosecond: 0, + offset: "+00:00", + calendar: TemporalHelpers.calendarObserver(actual, "options.relativeTo.calendar"), + timeZone: TemporalHelpers.timeZoneObserver(actual, "options.relativeTo.timeZone"), +}, "options.relativeTo"); + +instance.add(fields, createOptionsObserver(zonedRelativeTo)); +assert.compareArray(actual, expectedOpsForZonedRelativeTo, "order of operations with ZonedDateTime relativeTo"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/precision-exact-mathematical-values.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/precision-exact-mathematical-values.js new file mode 100644 index 0000000000..0d7f29b9e6 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/precision-exact-mathematical-values.js @@ -0,0 +1,26 @@ +// |reftest| skip -- Temporal is not supported +// 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.add +description: > + AddDuration computes on exact mathematical number values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +// Largest temporal unit is "microsecond". +let duration1 = Temporal.Duration.from({microseconds: Number.MAX_SAFE_INTEGER + 1, nanoseconds: 0}); +let duration2 = Temporal.Duration.from({microseconds: 1, nanoseconds: 1000}); + +TemporalHelpers.assertDuration( + duration1.add(duration2), + 0, 0, 0, 0, + 0, 0, 0, 0, + 9007199254740994, + 0, + "duration1.add(duration2)" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/prop-desc.js new file mode 100644 index 0000000000..5fab787d47 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: The "add" property of Temporal.Duration.prototype +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.Duration.prototype.add, + "function", + "`typeof Duration.prototype.add` is `function`" +); + +verifyProperty(Temporal.Duration.prototype, "add", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/read-time-fields-before-datefromfields.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/read-time-fields-before-datefromfields.js new file mode 100644 index 0000000000..ee1abfc2e9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/read-time-fields-before-datefromfields.js @@ -0,0 +1,25 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: The time fields are read from the object before being passed to dateFromFields(). +info: | + sec-temporal.duration.prototype.add step 5: + 5. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_). + sec-temporal-torelativetemporalobject step 4.g: + g. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_). + sec-temporal-interprettemporaldatetimefields steps 1–2: + 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_). + 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarMakeInvalidGettersTime(); +const duration1 = new Temporal.Duration(1); +const duration2 = new Temporal.Duration(0, 12); +duration1.add(duration2, { relativeTo: { year: 2000, month: 1, day: 1, calendar } }); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-infinity-throws-rangeerror.js new file mode 100644 index 0000000000..0be7e08edb --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-infinity-throws-rangeerror.js @@ -0,0 +1,26 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Throws if any value in the property bag is Infinity or -Infinity +esid: sec-temporal.duration.prototype.add +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); +const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321 }; + +[Infinity, -Infinity].forEach((inf) => { + ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => { + assert.throws(RangeError, () => instance.add(instance, { relativeTo: { ...base, [prop]: inf } }), `${prop} property cannot be ${inf} in relativeTo`); + + const calls = []; + const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop); + assert.throws(RangeError, () => instance.add(instance, { relativeTo: { ...base, [prop]: obj } })); + assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value"); + }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-leap-second.js new file mode 100644 index 0000000000..d6602384e0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-leap-second.js @@ -0,0 +1,46 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: Leap second is constrained in both an ISO string and a property bag +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1, 0, 0, 1); + +let relativeTo = "2016-12-31T23:59:60"; +const result1 = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }); +TemporalHelpers.assertDuration( + result1, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "leap second is a valid ISO string for PlainDate relativeTo" +); + +relativeTo = "2016-12-31T23:59:60+00:00[UTC]"; +const result2 = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }); +TemporalHelpers.assertDuration( + result2, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "leap second is a valid ISO string for ZonedDateTime relativeTo" +); + +relativeTo = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 }; +const result3 = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }); +TemporalHelpers.assertDuration( + result3, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "second: 60 is valid in a property bag for PlainDate relativeTo" +); + +relativeTo = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60, timeZone: "UTC" }; +const result4 = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }); +TemporalHelpers.assertDuration( + result4, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "second: 60 is valid in a property bag for ZonedDateTime relativeTo" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-month.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-month.js new file mode 100644 index 0000000000..fc4b609848 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-month.js @@ -0,0 +1,21 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: relativeTo with months. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const oneMonth = new Temporal.Duration(0, 1); +const days30 = new Temporal.Duration(0, 0, 0, 30); +TemporalHelpers.assertDuration(oneMonth.add(days30, { relativeTo: Temporal.PlainDate.from('2018-01-01') }), + 0, 2, 0, 2, 0, 0, 0, 0, 0, 0, "January"); +TemporalHelpers.assertDuration(oneMonth.add(days30, { relativeTo: Temporal.PlainDate.from('2018-02-01') }), + 0, 1, 0, 30, 0, 0, 0, 0, 0, 0, "February"); +TemporalHelpers.assertDuration(oneMonth.add(days30, { relativeTo: Temporal.PlainDate.from('2018-03-01') }), + 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, "March"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-number.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-number.js new file mode 100644 index 0000000000..45e87cfce7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-number.js @@ -0,0 +1,33 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: A number as relativeTo option is converted to a string, then to Temporal.PlainDate +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1, 0, 0, 1); + +const relativeTo = 20191101; + +const result = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }); +TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "20191101 is a valid ISO string for relativeTo"); + +const numbers = [ + 1, + -20191101, + 1234567890, +]; + +for (const relativeTo of numbers) { + assert.throws( + RangeError, + () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }), + `Number ${relativeTo} does not convert to a valid ISO string for relativeTo` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-order.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-order.js new file mode 100644 index 0000000000..cffd653efc --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-order.js @@ -0,0 +1,19 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: relativeTo with years. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const d1 = new Temporal.Duration(0, 1, 0, 0); +const d2 = new Temporal.Duration(0, 0, 0, 30); +const relativeTo = new Temporal.PlainDate(2000, 1, 1); +TemporalHelpers.assertDuration(d1.add(d2, { relativeTo }), + 0, 2, 0, 1, 0, 0, 0, 0, 0, 0, + "first this is resolved against relativeTo, then the argument against relativeTo + this"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-calendar-datefromfields-called-with-null-prototype-fields.js new file mode 100644 index 0000000000..32ac7615d7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-calendar-datefromfields-called-with-null-prototype-fields.js @@ -0,0 +1,19 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: > + Calendar.dateFromFields method is called with a null-prototype fields object +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution(); +const instance = new Temporal.Duration(1, 0, 0, 1); +const relativeTo = { year: 2000, month: 5, day: 2, calendar }; +instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }); +assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-calendar-number.js new file mode 100644 index 0000000000..95ee3717ee --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-calendar-number.js @@ -0,0 +1,45 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: A number as calendar in relativeTo property bag is converted to a string, then to a calendar +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1, 0, 0, 1); + +const calendar = 19970327; + +let relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar }; +const result1 = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }); +TemporalHelpers.assertDuration(result1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "19970327 is a valid ISO string for relativeTo.calendar"); + +relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar: { calendar } }; +const result2 = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }); +TemporalHelpers.assertDuration(result2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "19970327 is a valid ISO string for relativeTo.calendar (nested property)"); + +const numbers = [ + 1, + -19970327, + 1234567890, +]; + +for (const calendar of numbers) { + let relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar }; + assert.throws( + RangeError, + () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }), + `Number ${calendar} does not convert to a valid ISO string for relativeTo.calendar` + ); + relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar: { calendar } }; + assert.throws( + RangeError, + () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }), + `Number ${calendar} does not convert to a valid ISO string for relativeTo.calendar (nested property)` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-calendar-wrong-type.js new file mode 100644 index 0000000000..8a70dfc10b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-calendar-wrong-type.js @@ -0,0 +1,53 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: > + Appropriate error thrown when relativeTo.calendar cannot be converted to a + calendar object or string +features: [BigInt, Symbol, Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.Duration(1, 0, 0, 1); + +const rangeErrorTests = [ + [null, "null"], + [true, "boolean"], + ["", "empty string"], + [1, "number that doesn't convert to a valid ISO string"], + [1n, "bigint"], + [new Temporal.TimeZone("UTC"), "time zone instance"], +]; + +for (const [calendar, description] of rangeErrorTests) { + let relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar }; + assert.throws(RangeError, () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }), `${description} does not convert to a valid ISO string`); + + relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar: { calendar } }; + assert.throws(RangeError, () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }), `${description} does not convert to a valid ISO string (nested property)`); +} + +const typeErrorTests = [ + [Symbol(), "symbol"], + [{}, "plain object"], + [Temporal.PlainDate, "Temporal.PlainDate, object"], + [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"], + [Temporal.ZonedDateTime, "Temporal.ZonedDateTime, object"], + [Temporal.ZonedDateTime.prototype, "Temporal.ZonedDateTime.prototype, object"], +]; + +for (const [calendar, description] of typeErrorTests) { + let relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar }; + assert.throws(TypeError, () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }), `${description} is not a valid property bag and does not convert to a string`); + + relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar: { calendar } }; + assert.throws(TypeError, () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }), `${description} is not a valid property bag and does not convert to a string (nested property)`); +} + +const relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar: { calendar: undefined } }; +assert.throws(RangeError, () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }), `nested undefined calendar property is always a RangeError`); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-invalid-offset-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-invalid-offset-string.js new file mode 100644 index 0000000000..c2b82f0dfa --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-invalid-offset-string.js @@ -0,0 +1,25 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: relativeTo property bag with offset property is rejected if offset is in the wrong format +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.Duration(1, 0, 0, 1); + +const badOffsets = [ + "00:00", // missing sign + "+0", // too short + "-000:00", // too long + 0, // converts to a string that is invalid +]; +badOffsets.forEach((offset) => { + const relativeTo = { year: 2021, month: 10, day: 28, offset, timeZone }; + assert.throws(RangeError, () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }), `"${offset} is not a valid offset string`); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-no-time-units.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-no-time-units.js new file mode 100644 index 0000000000..e4bed52a0f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-no-time-units.js @@ -0,0 +1,18 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: Missing time units in relativeTo property bag default to 0 +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1, 0, 0, 1); + +let relativeTo = { year: 2000, month: 1, day: 1 }; +const result = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }); +TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "missing time units default to 0"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..a9925c68d6 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,19 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); + const other = new Temporal.Duration(2); + assert.throws(RangeError, () => duration.add(other, { relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..2cf033caa3 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,23 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); + const other = new Temporal.Duration(2); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => duration.add(other, { relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..2cdcb994f5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,19 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); + const other = new Temporal.Duration(2); + assert.throws(RangeError, () => duration.add(other, { relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..1e45b733b9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,28 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); + const other = new Temporal.Duration(2); + assert.throws(TypeError, () => duration.add(other, { relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-instance-does-not-get-timeZone-property.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-instance-does-not-get-timeZone-property.js new file mode 100644 index 0000000000..04daf8cf3b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-instance-does-not-get-timeZone-property.js @@ -0,0 +1,25 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: > + A Temporal.TimeZone instance passed to add() does not have its + 'timeZone' property observably checked +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1); + +const timeZone = new Temporal.TimeZone("UTC"); +Object.defineProperty(timeZone, "timeZone", { + get() { + throw new Test262Error("timeZone.timeZone should not be accessed"); + }, +}); + +instance.add(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }); +instance.add(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-string-datetime.js new file mode 100644 index 0000000000..2b6e13c3e5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-string-datetime.js @@ -0,0 +1,39 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: Conversion of ISO date-time strings to Temporal.TimeZone instances +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1); + +let timeZone = "2021-08-19T17:30"; +assert.throws(RangeError, () => instance.add(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), "bare date-time string is not a time zone"); +assert.throws(RangeError, () => instance.add(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }), "bare date-time string is not a time zone"); + +// The following are all valid strings so should not throw: + +[ + "2021-08-19T17:30Z", + "2021-08-19T1730Z", + "2021-08-19T17:30-07:00", + "2021-08-19T1730-07:00", + "2021-08-19T17:30-0700", + "2021-08-19T1730-0700", + "2021-08-19T17:30[UTC]", + "2021-08-19T1730[UTC]", + "2021-08-19T17:30Z[UTC]", + "2021-08-19T1730Z[UTC]", + "2021-08-19T17:30-07:00[UTC]", + "2021-08-19T1730-07:00[UTC]", + "2021-08-19T17:30-0700[UTC]", + "2021-08-19T1730-0700[UTC]", +].forEach((timeZone) => { + instance.add(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }); + instance.add(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-string-leap-second.js new file mode 100644 index 0000000000..767929548f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-string-leap-second.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: Leap second is a valid ISO string for TimeZone +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1); +let timeZone = "2016-12-31T23:59:60+00:00[UTC]"; + +// A string with a leap second is a valid ISO string, so the following two +// operations should not throw + +instance.add(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }); +instance.add(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }); + +timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]"; +assert.throws(RangeError, () => instance.add(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), "leap second in time zone name not valid"); +assert.throws(RangeError, () => instance.add(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }), "leap second in time zone name not valid (nested property)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-string-year-zero.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-string-year-zero.js new file mode 100644 index 0000000000..e824f6ff3b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-string-year-zero.js @@ -0,0 +1,29 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: Negative zero, as an extended year, is rejected +features: [Temporal, arrow-function] +---*/ + +const invalidStrings = [ + "-000000-10-31T17:45Z", + "-000000-10-31T17:45+00:00[UTC]", +]; +const instance = new Temporal.Duration(1); +invalidStrings.forEach((timeZone) => { + assert.throws( + RangeError, + () => instance.add(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), + "reject minus zero as extended year" + ); + assert.throws( + RangeError, + () => instance.add(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }), + "reject minus zero as extended year (nested property)" + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-string.js new file mode 100644 index 0000000000..17f26354ae --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-string.js @@ -0,0 +1,19 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: Time zone IDs are valid input for a time zone +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1); + +// The following are all valid strings so should not throw: + +["UTC", "+01:00"].forEach((timeZone) => { + instance.add(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-wrong-type.js new file mode 100644 index 0000000000..1bd8646488 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-propertybag-timezone-wrong-type.js @@ -0,0 +1,42 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: > + Appropriate error thrown when argument cannot be converted to a valid string + or object for TimeZone +features: [BigInt, Symbol, Temporal] +---*/ + +const instance = new Temporal.Duration(1); + +const rangeErrorTests = [ + [null, "null"], + [true, "boolean"], + ["", "empty string"], + [1, "number that doesn't convert to a valid ISO string"], + [19761118, "number that would convert to a valid ISO string in other contexts"], + [1n, "bigint"], + [new Temporal.Calendar("iso8601"), "calendar instance"], +]; + +for (const [timeZone, description] of rangeErrorTests) { + assert.throws(RangeError, () => instance.add(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), `${description} does not convert to a valid ISO string`); + assert.throws(RangeError, () => instance.add(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }), `${description} does not convert to a valid ISO string (nested property)`); +} + +const typeErrorTests = [ + [Symbol(), "symbol"], +]; + +for (const [timeZone, description] of typeErrorTests) { + assert.throws(TypeError, () => instance.add(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), `${description} is not a valid object and does not convert to a string`); + assert.throws(TypeError, () => instance.add(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }), `${description} is not a valid object and does not convert to a string (nested property)`); +} + +const timeZone = undefined; +assert.throws(RangeError, () => instance.add(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }), `undefined is always a RangeError as nested property`); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-required.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-required.js new file mode 100644 index 0000000000..cea3361ca5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-required.js @@ -0,0 +1,36 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: relativeTo is required if the largest unit is at least weeks. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const d = Temporal.Duration.from({ hours: 1 }); +const dy = Temporal.Duration.from({ years: 1 }); +const dm = Temporal.Duration.from({ months: 1 }); +const dw = Temporal.Duration.from({ weeks: 1 }); +assert.throws(RangeError, () => d.add(dy)); +assert.throws(RangeError, () => d.add(dm)); +assert.throws(RangeError, () => d.add(dw)); +assert.throws(RangeError, () => dy.add(d)); +assert.throws(RangeError, () => dm.add(d)); +assert.throws(RangeError, () => dw.add(d)); +const relativeTo = Temporal.PlainDate.from("2000-01-01"); +TemporalHelpers.assertDuration(d.add(dy, { relativeTo }), + 1, 0, 0, 0, 1, 0, 0, 0, 0, 0); +TemporalHelpers.assertDuration(d.add(dm, { relativeTo }), + 0, 1, 0, 0, 1, 0, 0, 0, 0, 0); +TemporalHelpers.assertDuration(d.add(dw, { relativeTo }), + 0, 0, 1, 0, 1, 0, 0, 0, 0, 0); +TemporalHelpers.assertDuration(dy.add(d, { relativeTo }), + 1, 0, 0, 0, 1, 0, 0, 0, 0, 0); +TemporalHelpers.assertDuration(dm.add(d, { relativeTo }), + 0, 1, 0, 0, 1, 0, 0, 0, 0, 0); +TemporalHelpers.assertDuration(dw.add(d, { relativeTo }), + 0, 0, 1, 0, 1, 0, 0, 0, 0, 0); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-string-datetime.js new file mode 100644 index 0000000000..f68cf4848b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-string-datetime.js @@ -0,0 +1,41 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: > + Conversion of ISO date-time strings as relativeTo option to + Temporal.ZonedDateTime or Temporal.PlainDateTime instances +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1, 0, 0, 1); + +let relativeTo = "2019-11-01T00:00"; +const result1 = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }); +TemporalHelpers.assertDuration(result1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "bare date-time string is a plain relativeTo"); + +relativeTo = "2019-11-01T00:00-07:00"; +const result2 = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }); +TemporalHelpers.assertDuration(result2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "date-time + offset is a plain relativeTo"); + +relativeTo = "2019-11-01T00:00[-07:00]"; +const result3 = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }); +TemporalHelpers.assertDuration(result3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "date-time + IANA annotation is a zoned relativeTo"); + +relativeTo = "2019-11-01T00:00Z[-07:00]"; +const result4 = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }); +TemporalHelpers.assertDuration(result4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "date-time + Z + IANA annotation is a zoned relativeTo"); + +relativeTo = "2019-11-01T00:00+00:00[UTC]"; +const result5 = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }); +TemporalHelpers.assertDuration(result5, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "date-time + offset + IANA annotation is a zoned relativeTo"); + +relativeTo = "2019-11-01T00:00Z"; +assert.throws(RangeError, () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }), "date-time + Z throws without an IANA annotation"); +relativeTo = "2019-11-01T00:00+04:15[UTC]"; +assert.throws(RangeError, () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }), "date-time + offset + IANA annotation throws if wall time and exact time mismatch"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-string-invalid.js new file mode 100644 index 0000000000..4a913cf3cd --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-string-invalid.js @@ -0,0 +1,16 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: RangeError thrown if relativeTo is a string with the wrong format +features: [Temporal] +---*/ + +['bad string', '15:30:45.123456', 'iso8601', 'UTC', 'P1YT1H'].forEach((relativeTo) => { + const duration = new Temporal.Duration(1, 0, 0, 15); + assert.throws(RangeError, () => duration.add(new Temporal.Duration(0, 0, 0, 16), { relativeTo })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-string-plaindatetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-string-plaindatetime.js new file mode 100644 index 0000000000..f73d875240 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-string-plaindatetime.js @@ -0,0 +1,18 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: The relativeTo option accepts a PlainDateTime-like ISO 8601 string +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +['2000-01-01', '2000-01-01T00:00', '2000-01-01T00:00[u-ca=iso8601]'].forEach((relativeTo) => { + const duration = new Temporal.Duration(1, 0, 0, 15); + const result = duration.add(new Temporal.Duration(0, 0, 0, 16), { relativeTo }); + TemporalHelpers.assertDuration(result, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-string-zoneddatetime-wrong-offset.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-string-zoneddatetime-wrong-offset.js new file mode 100644 index 0000000000..b938483e5f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-string-zoneddatetime-wrong-offset.js @@ -0,0 +1,19 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: Throws if a ZonedDateTime-like relativeTo string has the wrong UTC offset +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1, 0, 0, 1); +const relativeTo = "2000-01-01T00:00+05:30[UTC]"; +assert.throws( + RangeError, + () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }), + "add should throw RangeError on a string with UTC offset mismatch" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-string-zoneddatetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-string-zoneddatetime.js new file mode 100644 index 0000000000..2513a6cc5d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-string-zoneddatetime.js @@ -0,0 +1,23 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: The relativeTo option accepts a ZonedDateTime-like ISO 8601 string +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + '2000-01-01[UTC]', + '2000-01-01T00:00[UTC]', + '2000-01-01T00:00+00:00[UTC]', + '2000-01-01T00:00+00:00[UTC][u-ca=iso8601]', +].forEach((relativeTo) => { + const duration = new Temporal.Duration(1, 0, 0, 15); + const result = duration.add(new Temporal.Duration(0, 0, 0, 16), { relativeTo }); + TemporalHelpers.assertDuration(result, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-sub-minute-offset.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-sub-minute-offset.js new file mode 100644 index 0000000000..fce5c9da4d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-sub-minute-offset.js @@ -0,0 +1,25 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: relativeTo string accepts an inexact UTC offset rounded to hours and minutes +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1, 0, 0, 1); + +let relativeTo = "2000-01-01T00:00+00:45[+00:44:30.123456789]"; +const result = instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }); +TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "rounded HH:MM is accepted in string"); + +relativeTo = "2000-01-01T00:00+00:44:30[+00:44:30.123456789]"; +assert.throws(RangeError, () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }), "no other rounding is accepted for offset"); + +const timeZone = new Temporal.TimeZone("+00:44:30.123456789"); +relativeTo = { year: 2000, month: 1, day: 1, offset: "+00:45", timeZone }; +assert.throws(RangeError, () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }), "rounded HH:MM not accepted as offset in property bag"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-wrong-type.js new file mode 100644 index 0000000000..e65cd25c40 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-wrong-type.js @@ -0,0 +1,42 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: > + Appropriate error thrown when relativeTo cannot be converted to a valid + relativeTo string or property bag +features: [BigInt, Symbol, Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.Duration(1, 0, 0, 1); + +const rangeErrorTests = [ + [undefined, "undefined"], + [null, "null"], + [true, "boolean"], + ["", "empty string"], + [1, "number that doesn't convert to a valid ISO string"], + [1n, "bigint"], +]; + +for (const [relativeTo, description] of rangeErrorTests) { + assert.throws(RangeError, () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }), `${description} does not convert to a valid ISO string`); +} + +const typeErrorTests = [ + [Symbol(), "symbol"], + [{}, "plain object"], + [Temporal.PlainDate, "Temporal.PlainDate, object"], + [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"], + [Temporal.ZonedDateTime, "Temporal.ZonedDateTime, object"], + [Temporal.ZonedDateTime.prototype, "Temporal.ZonedDateTime.prototype, object"], +]; + +for (const [relativeTo, description] of typeErrorTests) { + assert.throws(TypeError, () => instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }), `${description} is not a valid property bag and does not convert to a string`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-year.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-year.js new file mode 100644 index 0000000000..1556a07d00 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-year.js @@ -0,0 +1,19 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: relativeTo with years. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const oneYear = new Temporal.Duration(1); +const days365 = new Temporal.Duration(0, 0, 0, 365); +TemporalHelpers.assertDuration(oneYear.add(days365, { relativeTo: Temporal.PlainDate.from("2016-01-01") }), + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "non-leap year"); +TemporalHelpers.assertDuration(oneYear.add(days365, { relativeTo: Temporal.PlainDate.from("2015-01-01") }), + 1, 11, 0, 30, 0, 0, 0, 0, 0, 0, "leap year"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-nanoseconds-to-days-range-errors.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-nanoseconds-to-days-range-errors.js new file mode 100644 index 0000000000..e64c22f10f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-nanoseconds-to-days-range-errors.js @@ -0,0 +1,122 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-temporal.duration.prototype.add +description: > + Called abstract operation NanosecondsToDays can throw three different RangeErrors when paired with a ZonedDateTime. +info: | + 6.5.7 NanosecondsToDays ( nanoseconds, relativeTo ) + 19. If days < 0 and sign = 1, throw a RangeError exception. + 20. If days > 0 and sign = -1, throw a RangeError exception. + ... + 22. If nanoseconds > 0 and sign = -1, throw a RangeError exception. +features: [Temporal, BigInt] +includes: [temporalHelpers.js] +---*/ + +const dayNs = 86_400_000_000_000; +const dayDuration = Temporal.Duration.from({ days: 1 }); +const epochInstant = new Temporal.Instant(0n); + +function timeZoneSubstituteValues( + getPossibleInstantsFor, + getOffsetNanosecondsFor +) { + const tz = new Temporal.TimeZone("UTC"); + TemporalHelpers.substituteMethod( + tz, + "getPossibleInstantsFor", + getPossibleInstantsFor + ); + TemporalHelpers.substituteMethod( + tz, + "getOffsetNanosecondsFor", + getOffsetNanosecondsFor + ); + return tz; +} + +// NanosecondsToDays.19: days < 0 and sign = 1 +let zdt = new Temporal.ZonedDateTime( + -1n, // Set DifferenceZonedDateTime _ns1_ + timeZoneSubstituteValues( + [ + TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for first call, AddDuration step 9 + [epochInstant], // Returned for AddDuration step 10, setting _endNs_ -> DifferenceZonedDateTime _ns2_ + [epochInstant], // Returned for NanosecondsToDays step 14, setting _intermediateNs_ + ], + [ + // Behave normally in 4 calls made prior to NanosecondsToDays + TemporalHelpers.SUBSTITUTE_SKIP, + TemporalHelpers.SUBSTITUTE_SKIP, + TemporalHelpers.SUBSTITUTE_SKIP, + TemporalHelpers.SUBSTITUTE_SKIP, + dayNs - 1, // Returned for NanosecondsToDays step 7, setting _startDateTime_ + -dayNs + 1, // Returned for NanosecondsToDays step 11, setting _endDateTime_ + ] + ) +); +assert.throws(RangeError, () => + // Adding day to day sets largestUnit to 'day', avoids having any week/month/year components in differences + dayDuration.add(dayDuration, { + relativeTo: zdt, + }) +); + +// NanosecondsToDays.20: days > 0 and sign = -1 +zdt = new Temporal.ZonedDateTime( + 1n, // Set DifferenceZonedDateTime _ns1_ + timeZoneSubstituteValues( + [ + TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for first call, AddDuration step 9 + [epochInstant], // Returned for AddDuration step 10, setting _endNs_ -> DifferenceZonedDateTime _ns2_ + [epochInstant], // Returned for NanosecondsToDays step 14, setting _intermediateNs_ + ], + [ + // Behave normally in 4 calls made prior to NanosecondsToDays + TemporalHelpers.SUBSTITUTE_SKIP, + TemporalHelpers.SUBSTITUTE_SKIP, + TemporalHelpers.SUBSTITUTE_SKIP, + TemporalHelpers.SUBSTITUTE_SKIP, + -dayNs + 1, // Returned for NanosecondsToDays step 7, setting _startDateTime_ + dayNs - 1, // Returned for NanosecondsToDays step 11, setting _endDateTime_ + ] + ) +); +assert.throws(RangeError, () => + // Adding day to day sets largestUnit to 'day', avoids having any week/month/year components in differences + dayDuration.add(dayDuration, { + relativeTo: zdt, + }) +); + +// NanosecondsToDays.22: nanoseconds > 0 and sign = -1 +zdt = new Temporal.ZonedDateTime( + 0n, // Set DifferenceZonedDateTime _ns1_ + timeZoneSubstituteValues( + [ + TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for first call, AddDuration step 9 + [new Temporal.Instant(-1n)], // Returned for AddDuration step 10, setting _endNs_ -> DifferenceZonedDateTime _ns2_ + [new Temporal.Instant(-2n)], // Returned for NanosecondsToDays step 14, setting _intermediateNs_ + [new Temporal.Instant(-4n)], // Returned for NanosecondsToDays step 18.a, setting _oneDayFartherNs_ + ], + [ + // Behave normally in 4 calls made prior to NanosecondsToDays + TemporalHelpers.SUBSTITUTE_SKIP, + TemporalHelpers.SUBSTITUTE_SKIP, + TemporalHelpers.SUBSTITUTE_SKIP, + TemporalHelpers.SUBSTITUTE_SKIP, + dayNs - 1, // Returned for NanosecondsToDays step 7, setting _startDateTime_ + -dayNs + 1, // Returned for NanosecondsToDays step 11, setting _endDateTime_ + ] + ) +); +assert.throws(RangeError, () => + // Adding day to day sets largestUnit to 'day', avoids having any week/month/year components in differences + dayDuration.add(dayDuration, { + relativeTo: zdt, + }) +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-negative-epochnanoseconds.js new file mode 100644 index 0000000000..8b5ed47cab --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-negative-epochnanoseconds.js @@ -0,0 +1,27 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch +info: | + sec-temporal-getisopartsfromepoch step 1: + 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 10<sup>6</sup>. + sec-temporal-builtintimezonegetplaindatetimefor step 2: + 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]). +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +const relativeTo = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC"); +const duration = new Temporal.Duration(0, 0, 0, 1); + +// This code path shows up anywhere we convert an exact time, before the Unix +// epoch, with nonzero microseconds or nanoseconds, into a wall time; in this +// case via relativeTo. + +const result = duration.add(duration, { relativeTo }); +TemporalHelpers.assertDuration(result, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..11c47e07bb --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,20 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); + const other = new Temporal.Duration(2); + const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => duration.add(other, { relativeTo })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..2c442c1572 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); + const other = new Temporal.Duration(2); + const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => duration.add(other, { relativeTo }), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..2b76fe6dbb --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,20 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); + const other = new Temporal.Duration(2); + const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => duration.add(other, { relativeTo })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..ce4ea69868 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,29 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); + const other = new Temporal.Duration(2); + const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => duration.add(other, { relativeTo })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/shell.js new file mode 100644 index 0000000000..eda1477282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/shell.js @@ -0,0 +1,24 @@ +// GENERATED, DO NOT EDIT +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/subclassing-ignored.js new file mode 100644 index 0000000000..3ea2b3e2ea --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/subclassing-ignored.js @@ -0,0 +1,20 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: Objects of a subclass are never created as return values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.checkSubclassingIgnored( + Temporal.Duration, + [0, 0, 0, 4, 5, 6, 7, 987, 654, 321], + "add", + [{ nanoseconds: 1 }], + (result) => TemporalHelpers.assertDuration(result, 0, 0, 0, 4, 5, 6, 7, 987, 654, 322), +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/timezone-getpossibleinstantsfor-iterable.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/timezone-getpossibleinstantsfor-iterable.js new file mode 100644 index 0000000000..167f0ace71 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/timezone-getpossibleinstantsfor-iterable.js @@ -0,0 +1,46 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call +info: | + sec-temporal.duration.prototype.add steps 5–6: + 5. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_). + 6. Let _result_ be ? AddDuration(_duration_.[[Years]], [...], _duration_.[[Nanoseconds]], _other_.[[Years]], [...], _other_.[[Nanoseconds]], _relativeTo_). + sec-temporal-torelativetemporalobject step 6.d: + d. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_result_.[[Year]], [...], _result_.[[Nanosecond]], _offsetNs_, _timeZone_, *"compatible"*, *"reject"*). + sec-temporal-interpretisodatetimeoffset step 7: + 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_). + sec-temporal-addduration steps 7.d–e and 7.g.i: + d. Let _intermediateNs_ be ? AddZonedDateTime(_relativeTo_.[[Nanoseconds]], _timeZone_, _calendar_, _y1_, [...], _ns1_). + e. Let _endNs_ be ? AddZonedDateTime(_intermediateNs_, _timeZone_, _calendar_, _y2_, [...], _ns2_). + [...] + i. Let _result_ be ? DifferenceZonedDateTime(_relativeTo_.[[Nanoseconds]], _endNs_, _timeZone_, _calendar_, _largestUnit_). + sec-temporal-differencezoneddatetime step 8: + 8. Let _intermediateNs_ be ? AddZonedDateTime(_ns1_, _timeZone_, _calendar_, _dateDifference_.[[Years]], _dateDifference_.[[Months]], _dateDifference_.[[Weeks]], 0, 0, 0, 0, 0, 0, 0). + sec-temporal-addzoneddatetime step 8: + 8. Let _intermediateInstant_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _intermediateDateTime_, *"compatible"*). + sec-temporal-builtintimezonegetinstantfor step 1: + 1. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_). + sec-temporal-getpossibleinstantsfor step 2: + 2. Let _list_ be ? IterableToList(_possibleInstants_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "2000-01-01T09:00:00", // called once on the input relativeTo object + "2001-01-01T09:00:00", // called once on relativeTo plus the receiver + "2002-01-01T09:00:00", // called once on relativeTo plus the receiver plus the argument + "2002-01-01T09:00:00", // called once on relativeTo plus the years, months, and weeks from the difference of relativeTo minus endNs +]; + +TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => { + const duration1 = new Temporal.Duration(1); + const duration2 = new Temporal.Duration(0, 12); + duration1.add(duration2, { relativeTo: { year: 2000, month: 1, day: 1, hour: 9, timeZone } }); +}, expected); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/year-zero.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/year-zero.js new file mode 100644 index 0000000000..39f09c1132 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/add/year-zero.js @@ -0,0 +1,20 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.add +description: Negative zero, as an extended year, is rejected +features: [Temporal, arrow-function] +---*/ + +const instance = new Temporal.Duration(1, 0, 0, 1); + +let relativeTo = "-000000-11-04T00:00"; +assert.throws( + RangeError, + () => { instance.add(new Temporal.Duration(0, 0, 0, 0, -24), { relativeTo }); }, + "reject minus zero as extended year" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/blank/basic.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/blank/basic.js new file mode 100644 index 0000000000..6c4c1c49c7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/blank/basic.js @@ -0,0 +1,28 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.duration.prototype.blank +description: Basic tests for blank. +features: [Temporal] +---*/ + +assert.sameValue(Temporal.Duration.from("P3DT1H").blank, false); +assert.sameValue(Temporal.Duration.from("-PT2H20M30S").blank, false); +assert.sameValue(Temporal.Duration.from("PT0S").blank, true); +const zero = Temporal.Duration.from({ + years: 0, + months: 0, + weeks: 0, + days: 0, + hours: 0, + minutes: 0, + seconds: 0, + milliseconds: 0, + microseconds: 0, + nanoseconds: 0 +}); +assert.sameValue(zero.blank, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/blank/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/blank/branding.js new file mode 100644 index 0000000000..1f5f9c52a4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/blank/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.duration.prototype.blank +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const blank = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "blank").get; + +assert.sameValue(typeof blank, "function"); + +assert.throws(TypeError, () => blank.call(undefined), "undefined"); +assert.throws(TypeError, () => blank.call(null), "null"); +assert.throws(TypeError, () => blank.call(true), "true"); +assert.throws(TypeError, () => blank.call(""), "empty string"); +assert.throws(TypeError, () => blank.call(Symbol()), "symbol"); +assert.throws(TypeError, () => blank.call(1), "1"); +assert.throws(TypeError, () => blank.call({}), "plain object"); +assert.throws(TypeError, () => blank.call(Temporal.Duration), "Temporal.Duration"); +assert.throws(TypeError, () => blank.call(Temporal.Duration.prototype), "Temporal.Duration.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/blank/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/blank/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/blank/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/blank/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/blank/prop-desc.js new file mode 100644 index 0000000000..5e645da458 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/blank/prop-desc.js @@ -0,0 +1,17 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.duration.prototype.blank +description: The "blank" property of Temporal.Duration.prototype +features: [Temporal] +---*/ + +const descriptor = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "blank"); +assert.sameValue(typeof descriptor.get, "function"); +assert.sameValue(descriptor.set, undefined); +assert.sameValue(descriptor.enumerable, false); +assert.sameValue(descriptor.configurable, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/blank/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/blank/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/blank/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/constructor.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/constructor.js new file mode 100644 index 0000000000..09f558dc66 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/constructor.js @@ -0,0 +1,20 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.constructor +description: Test for Temporal.Duration.prototype.constructor. +info: The initial value of Temporal.Duration.prototype.constructor is %Temporal.Duration%. +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +verifyProperty(Temporal.Duration.prototype, "constructor", { + value: Temporal.Duration, + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/days/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/days/branding.js new file mode 100644 index 0000000000..40b430d5b5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/days/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.duration.prototype.days +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const days = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "days").get; + +assert.sameValue(typeof days, "function"); + +assert.throws(TypeError, () => days.call(undefined), "undefined"); +assert.throws(TypeError, () => days.call(null), "null"); +assert.throws(TypeError, () => days.call(true), "true"); +assert.throws(TypeError, () => days.call(""), "empty string"); +assert.throws(TypeError, () => days.call(Symbol()), "symbol"); +assert.throws(TypeError, () => days.call(1), "1"); +assert.throws(TypeError, () => days.call({}), "plain object"); +assert.throws(TypeError, () => days.call(Temporal.Duration), "Temporal.Duration"); +assert.throws(TypeError, () => days.call(Temporal.Duration.prototype), "Temporal.Duration.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/days/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/days/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/days/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/days/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/days/prop-desc.js new file mode 100644 index 0000000000..94237f21b9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/days/prop-desc.js @@ -0,0 +1,17 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.duration.prototype.days +description: The "days" property of Temporal.Duration.prototype +features: [Temporal] +---*/ + +const descriptor = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "days"); +assert.sameValue(typeof descriptor.get, "function"); +assert.sameValue(descriptor.set, undefined); +assert.sameValue(descriptor.enumerable, false); +assert.sameValue(descriptor.configurable, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/days/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/days/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/days/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/hours/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/hours/branding.js new file mode 100644 index 0000000000..bee2f4259b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/hours/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.duration.prototype.hours +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const hours = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "hours").get; + +assert.sameValue(typeof hours, "function"); + +assert.throws(TypeError, () => hours.call(undefined), "undefined"); +assert.throws(TypeError, () => hours.call(null), "null"); +assert.throws(TypeError, () => hours.call(true), "true"); +assert.throws(TypeError, () => hours.call(""), "empty string"); +assert.throws(TypeError, () => hours.call(Symbol()), "symbol"); +assert.throws(TypeError, () => hours.call(1), "1"); +assert.throws(TypeError, () => hours.call({}), "plain object"); +assert.throws(TypeError, () => hours.call(Temporal.Duration), "Temporal.Duration"); +assert.throws(TypeError, () => hours.call(Temporal.Duration.prototype), "Temporal.Duration.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/hours/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/hours/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/hours/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/hours/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/hours/prop-desc.js new file mode 100644 index 0000000000..b1a92b63c1 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/hours/prop-desc.js @@ -0,0 +1,17 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.duration.prototype.hours +description: The "hours" property of Temporal.Duration.prototype +features: [Temporal] +---*/ + +const descriptor = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "hours"); +assert.sameValue(typeof descriptor.get, "function"); +assert.sameValue(descriptor.set, undefined); +assert.sameValue(descriptor.enumerable, false); +assert.sameValue(descriptor.configurable, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/hours/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/hours/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/hours/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/microseconds/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/microseconds/branding.js new file mode 100644 index 0000000000..dc862fbdea --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/microseconds/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.duration.prototype.microseconds +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const microseconds = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "microseconds").get; + +assert.sameValue(typeof microseconds, "function"); + +assert.throws(TypeError, () => microseconds.call(undefined), "undefined"); +assert.throws(TypeError, () => microseconds.call(null), "null"); +assert.throws(TypeError, () => microseconds.call(true), "true"); +assert.throws(TypeError, () => microseconds.call(""), "empty string"); +assert.throws(TypeError, () => microseconds.call(Symbol()), "symbol"); +assert.throws(TypeError, () => microseconds.call(1), "1"); +assert.throws(TypeError, () => microseconds.call({}), "plain object"); +assert.throws(TypeError, () => microseconds.call(Temporal.Duration), "Temporal.Duration"); +assert.throws(TypeError, () => microseconds.call(Temporal.Duration.prototype), "Temporal.Duration.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/microseconds/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/microseconds/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/microseconds/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/microseconds/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/microseconds/prop-desc.js new file mode 100644 index 0000000000..5b97cea8a3 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/microseconds/prop-desc.js @@ -0,0 +1,17 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.duration.prototype.microseconds +description: The "microseconds" property of Temporal.Duration.prototype +features: [Temporal] +---*/ + +const descriptor = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "microseconds"); +assert.sameValue(typeof descriptor.get, "function"); +assert.sameValue(descriptor.set, undefined); +assert.sameValue(descriptor.enumerable, false); +assert.sameValue(descriptor.configurable, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/microseconds/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/microseconds/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/microseconds/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/milliseconds/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/milliseconds/branding.js new file mode 100644 index 0000000000..faa0462acf --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/milliseconds/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.duration.prototype.milliseconds +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const milliseconds = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "milliseconds").get; + +assert.sameValue(typeof milliseconds, "function"); + +assert.throws(TypeError, () => milliseconds.call(undefined), "undefined"); +assert.throws(TypeError, () => milliseconds.call(null), "null"); +assert.throws(TypeError, () => milliseconds.call(true), "true"); +assert.throws(TypeError, () => milliseconds.call(""), "empty string"); +assert.throws(TypeError, () => milliseconds.call(Symbol()), "symbol"); +assert.throws(TypeError, () => milliseconds.call(1), "1"); +assert.throws(TypeError, () => milliseconds.call({}), "plain object"); +assert.throws(TypeError, () => milliseconds.call(Temporal.Duration), "Temporal.Duration"); +assert.throws(TypeError, () => milliseconds.call(Temporal.Duration.prototype), "Temporal.Duration.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/milliseconds/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/milliseconds/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/milliseconds/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/milliseconds/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/milliseconds/prop-desc.js new file mode 100644 index 0000000000..cb5e1bc1da --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/milliseconds/prop-desc.js @@ -0,0 +1,17 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.duration.prototype.milliseconds +description: The "milliseconds" property of Temporal.Duration.prototype +features: [Temporal] +---*/ + +const descriptor = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "milliseconds"); +assert.sameValue(typeof descriptor.get, "function"); +assert.sameValue(descriptor.set, undefined); +assert.sameValue(descriptor.enumerable, false); +assert.sameValue(descriptor.configurable, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/milliseconds/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/milliseconds/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/milliseconds/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/minutes/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/minutes/branding.js new file mode 100644 index 0000000000..32d3f5a047 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/minutes/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.duration.prototype.minutes +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const minutes = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "minutes").get; + +assert.sameValue(typeof minutes, "function"); + +assert.throws(TypeError, () => minutes.call(undefined), "undefined"); +assert.throws(TypeError, () => minutes.call(null), "null"); +assert.throws(TypeError, () => minutes.call(true), "true"); +assert.throws(TypeError, () => minutes.call(""), "empty string"); +assert.throws(TypeError, () => minutes.call(Symbol()), "symbol"); +assert.throws(TypeError, () => minutes.call(1), "1"); +assert.throws(TypeError, () => minutes.call({}), "plain object"); +assert.throws(TypeError, () => minutes.call(Temporal.Duration), "Temporal.Duration"); +assert.throws(TypeError, () => minutes.call(Temporal.Duration.prototype), "Temporal.Duration.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/minutes/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/minutes/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/minutes/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/minutes/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/minutes/prop-desc.js new file mode 100644 index 0000000000..894e470d94 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/minutes/prop-desc.js @@ -0,0 +1,17 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.duration.prototype.minutes +description: The "minutes" property of Temporal.Duration.prototype +features: [Temporal] +---*/ + +const descriptor = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "minutes"); +assert.sameValue(typeof descriptor.get, "function"); +assert.sameValue(descriptor.set, undefined); +assert.sameValue(descriptor.enumerable, false); +assert.sameValue(descriptor.configurable, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/minutes/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/minutes/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/minutes/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/months/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/months/branding.js new file mode 100644 index 0000000000..86241f65d3 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/months/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.duration.prototype.months +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const months = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "months").get; + +assert.sameValue(typeof months, "function"); + +assert.throws(TypeError, () => months.call(undefined), "undefined"); +assert.throws(TypeError, () => months.call(null), "null"); +assert.throws(TypeError, () => months.call(true), "true"); +assert.throws(TypeError, () => months.call(""), "empty string"); +assert.throws(TypeError, () => months.call(Symbol()), "symbol"); +assert.throws(TypeError, () => months.call(1), "1"); +assert.throws(TypeError, () => months.call({}), "plain object"); +assert.throws(TypeError, () => months.call(Temporal.Duration), "Temporal.Duration"); +assert.throws(TypeError, () => months.call(Temporal.Duration.prototype), "Temporal.Duration.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/months/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/months/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/months/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/months/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/months/prop-desc.js new file mode 100644 index 0000000000..87eb38785b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/months/prop-desc.js @@ -0,0 +1,17 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.duration.prototype.months +description: The "months" property of Temporal.Duration.prototype +features: [Temporal] +---*/ + +const descriptor = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "months"); +assert.sameValue(typeof descriptor.get, "function"); +assert.sameValue(descriptor.set, undefined); +assert.sameValue(descriptor.enumerable, false); +assert.sameValue(descriptor.configurable, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/months/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/months/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/months/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/nanoseconds/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/nanoseconds/branding.js new file mode 100644 index 0000000000..48fb07d773 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/nanoseconds/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.duration.prototype.nanoseconds +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const nanoseconds = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "nanoseconds").get; + +assert.sameValue(typeof nanoseconds, "function"); + +assert.throws(TypeError, () => nanoseconds.call(undefined), "undefined"); +assert.throws(TypeError, () => nanoseconds.call(null), "null"); +assert.throws(TypeError, () => nanoseconds.call(true), "true"); +assert.throws(TypeError, () => nanoseconds.call(""), "empty string"); +assert.throws(TypeError, () => nanoseconds.call(Symbol()), "symbol"); +assert.throws(TypeError, () => nanoseconds.call(1), "1"); +assert.throws(TypeError, () => nanoseconds.call({}), "plain object"); +assert.throws(TypeError, () => nanoseconds.call(Temporal.Duration), "Temporal.Duration"); +assert.throws(TypeError, () => nanoseconds.call(Temporal.Duration.prototype), "Temporal.Duration.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/nanoseconds/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/nanoseconds/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/nanoseconds/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/nanoseconds/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/nanoseconds/prop-desc.js new file mode 100644 index 0000000000..84f337895d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/nanoseconds/prop-desc.js @@ -0,0 +1,17 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.duration.prototype.nanoseconds +description: The "nanoseconds" property of Temporal.Duration.prototype +features: [Temporal] +---*/ + +const descriptor = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "nanoseconds"); +assert.sameValue(typeof descriptor.get, "function"); +assert.sameValue(descriptor.set, undefined); +assert.sameValue(descriptor.enumerable, false); +assert.sameValue(descriptor.configurable, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/nanoseconds/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/nanoseconds/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/nanoseconds/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/basic.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/basic.js new file mode 100644 index 0000000000..2b22b3ea03 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/basic.js @@ -0,0 +1,54 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.negated +description: Temporal.Duration.prototype.negated will return negated value of the input duration. +info: | + 3. Return ? CreateTemporalDuration(abs(duration.[[Years]]), abs(duration.[[Months]]), abs(duration.[[Weeks]]), abs(duration.[[Days]]), abs(duration.[[Hours]]), abs(duration.[[Minutes]]), abs(duration.[[Seconds]]), abs(duration.[[Milliseconds]]), abs(duration.[[Microseconds]]), abs(duration.[[Nanoseconds]])). +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +let d1 = new Temporal.Duration(); +TemporalHelpers.assertDuration( + d1.negated(), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "zeros"); + +let d2 = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); +TemporalHelpers.assertDuration( + d2.negated(), -1, -2, -3, -4, -5, -6, -7, -8, -9, -10, + "positive values"); + +let d3 = new Temporal.Duration(1e5, 2e5, 3e5, 4e5, 5e5, 6e5, 7e5, 8e5, 9e5, 10e5); +TemporalHelpers.assertDuration( + d3.negated(), -1e5, -2e5, -3e5, -4e5, -5e5, -6e5, -7e5, -8e5, -9e5, -10e5, + "large positive values"); + +let d4 = new Temporal.Duration(-1, -2, -3, -4, -5, -6, -7, -8, -9, -10); +TemporalHelpers.assertDuration( + d4.negated(), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, + "negative values"); + +let d5 = new Temporal.Duration(-1e5, -2e5, -3e5, -4e5, -5e5, -6e5, -7e5, -8e5, -9e5, -10e5); +TemporalHelpers.assertDuration( + d5.negated(), 1e5, 2e5, 3e5, 4e5, 5e5, 6e5, 7e5, 8e5, 9e5, 10e5, + "large negative values"); + +let d6 = new Temporal.Duration(1, 0, 3, 0, 5, 0, 7, 0, 9, 0); +TemporalHelpers.assertDuration( + d6.negated(), -1, 0, -3, 0, -5, 0, -7, 0, -9, 0, + "some zeros with positive values"); + +let d7 = new Temporal.Duration(-1, 0, -3, 0, -5, 0, -7, 0, -9, 0); +TemporalHelpers.assertDuration( + d7.negated(), 1, 0, 3, 0, 5, 0, 7, 0, 9, 0, + "some zeros with negative values"); + +let d8 = new Temporal.Duration(0, -2, 0, -4, 0, -6, 0, -8, 0, -10); +TemporalHelpers.assertDuration( + d8.negated(), 0, 2, 0, 4, 0, 6, 0, 8, 0, 10, + "other zeros with negative values"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/branding.js new file mode 100644 index 0000000000..9f7432ed52 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.negated +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const negated = Temporal.Duration.prototype.negated; + +assert.sameValue(typeof negated, "function"); + +assert.throws(TypeError, () => negated.call(undefined), "undefined"); +assert.throws(TypeError, () => negated.call(null), "null"); +assert.throws(TypeError, () => negated.call(true), "true"); +assert.throws(TypeError, () => negated.call(""), "empty string"); +assert.throws(TypeError, () => negated.call(Symbol()), "symbol"); +assert.throws(TypeError, () => negated.call(1), "1"); +assert.throws(TypeError, () => negated.call({}), "plain object"); +assert.throws(TypeError, () => negated.call(Temporal.Duration), "Temporal.Duration"); +assert.throws(TypeError, () => negated.call(Temporal.Duration.prototype), "Temporal.Duration.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/builtin.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/builtin.js new file mode 100644 index 0000000000..f94e6dad8b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/builtin.js @@ -0,0 +1,36 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.negated +description: > + Tests that Temporal.Duration.prototype.negated + meets the requirements for built-in objects defined by the + introduction of chapter 17 of the ECMAScript Language Specification. +info: | + Built-in functions that are not constructors do not have a "prototype" property unless + otherwise specified in the description of a particular function. + + Unless specified otherwise, a built-in object that is callable as a function is a built-in + function object with the characteristics described in 10.3. Unless specified otherwise, the + [[Extensible]] internal slot of a built-in object initially has the value true. + + Unless otherwise specified every built-in function and every built-in constructor has the + Function prototype object [...] as the value of its [[Prototype]] internal slot. +features: [Temporal] +---*/ + +assert.sameValue(Object.isExtensible(Temporal.Duration.prototype.negated), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.Duration.prototype.negated), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.Duration.prototype.negated), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.Duration.prototype.negated.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/length.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/length.js new file mode 100644 index 0000000000..bd9ba67d0e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/length.js @@ -0,0 +1,28 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2020 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.negated +description: Temporal.Duration.prototype.negated.length is 0 +info: | + Every built-in function object, including constructors, has a "length" property whose value is + an integer. Unless otherwise specified, this value is equal to the largest number of named + arguments shown in the subclause headings for the function description. Optional parameters + (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form + «...name») are not included in the default argument count. + + Unless otherwise specified, the "length" property of a built-in function object has the + attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +verifyProperty(Temporal.Duration.prototype.negated, "length", { + value: 0, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/name.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/name.js new file mode 100644 index 0000000000..d9a8aa2a45 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/name.js @@ -0,0 +1,26 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.negated +description: Temporal.Duration.prototype.negated.name is "negated". +info: | + Every built-in function object, including constructors, that is not identified as an anonymous + function has a "name" property whose value is a String. Unless otherwise specified, this value + is the name that is given to the function in this specification. + + Unless otherwise specified, the "name" property of a built-in function object, if it exists, + has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +verifyProperty(Temporal.Duration.prototype.negated, "name", { + value: "negated", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/not-a-constructor.js new file mode 100644 index 0000000000..d612c47433 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/not-a-constructor.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.negated +description: > + Temporal.Duration.prototype.negated does not implement [[Construct]], is not new-able +info: | + Built-in function objects that are not identified as constructors do not implement the + [[Construct]] internal method unless otherwise specified in the description of a particular + function. +includes: [isConstructor.js] +features: [Reflect.construct, Temporal] +---*/ + +assert.throws(TypeError, () => { + new Temporal.Duration.prototype.negated(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.Duration.prototype.negated), false, + "isConstructor(Temporal.Duration.prototype.negated)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/prop-desc.js new file mode 100644 index 0000000000..483ce98215 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.negated +description: The "negated" property of Temporal.Duration.prototype +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.Duration.prototype.negated, + "function", + "`typeof Duration.prototype.negated` is `function`" +); + +verifyProperty(Temporal.Duration.prototype, "negated", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/shell.js new file mode 100644 index 0000000000..eda1477282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/shell.js @@ -0,0 +1,24 @@ +// GENERATED, DO NOT EDIT +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/subclassing-ignored.js new file mode 100644 index 0000000000..d436baa86b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/negated/subclassing-ignored.js @@ -0,0 +1,20 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.negated +description: Objects of a subclass are never created as return values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.checkSubclassingIgnored( + Temporal.Duration, + [0, 0, 0, 4, 5, 6, 7, 987, 654, 321], + "negated", + [], + (result) => TemporalHelpers.assertDuration(result, 0, 0, 0, -4, -5, -6, -7, -987, -654, -321), +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/prop-desc.js new file mode 100644 index 0000000000..d4cdeced21 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/prop-desc.js @@ -0,0 +1,21 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-duration-prototype +description: The "prototype" property of Temporal.Duration +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue(typeof Temporal.Duration.prototype, "object"); +assert.notSameValue(Temporal.Duration.prototype, null); + +verifyProperty(Temporal.Duration, "prototype", { + writable: false, + enumerable: false, + configurable: false, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/balance-negative-result.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/balance-negative-result.js new file mode 100644 index 0000000000..1200480136 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/balance-negative-result.js @@ -0,0 +1,25 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: A negative duration result is balanced correctly by the modulo operation in NanosecondsToDays +info: | + sec-temporal-nanosecondstodays step 6: + 6. If Type(_relativeTo_) is not Object or _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot, then + a. Return the new Record { ..., [[Nanoseconds]]: abs(_nanoseconds_) modulo _dayLengthNs_ × _sign_, ... }. + sec-temporal-balanceduration step 4: + 4. If _largestUnit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then + a. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _relativeTo_). + sec-temporal.duration.prototype.round step 25: + 25. Let _result_ be ? BalanceDuration(_balanceResult_.[[Days]], _adjustResult_.[[Hours]], _adjustResult_.[[Minutes]], _adjustResult_.[[Seconds]], _adjustResult_.[[Milliseconds]], _adjustResult_.[[Microseconds]], _adjustResult_.[[Nanoseconds]], _largestUnit_, _relativeTo_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(0, 0, 0, 0, -60); +const result = duration.round({ largestUnit: "days" }); +TemporalHelpers.assertDuration(result, 0, 0, 0, -2, -12, 0, 0, 0, 0, 0); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/branding.js new file mode 100644 index 0000000000..d3e6882fe5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/branding.js @@ -0,0 +1,27 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const round = Temporal.Duration.prototype.round; + +assert.sameValue(typeof round, "function"); + +const args = ["hour"]; + +assert.throws(TypeError, () => round.apply(undefined, args), "undefined"); +assert.throws(TypeError, () => round.apply(null, args), "null"); +assert.throws(TypeError, () => round.apply(true, args), "true"); +assert.throws(TypeError, () => round.apply("", args), "empty string"); +assert.throws(TypeError, () => round.apply(Symbol(), args), "symbol"); +assert.throws(TypeError, () => round.apply(1, args), "1"); +assert.throws(TypeError, () => round.apply({}, args), "plain object"); +assert.throws(TypeError, () => round.apply(Temporal.Duration, args), "Temporal.Duration"); +assert.throws(TypeError, () => round.apply(Temporal.Duration.prototype, args), "Temporal.Duration.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/builtin.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/builtin.js new file mode 100644 index 0000000000..144511a2e5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/builtin.js @@ -0,0 +1,36 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: > + Tests that Temporal.Duration.prototype.round + meets the requirements for built-in objects defined by the + introduction of chapter 17 of the ECMAScript Language Specification. +info: | + Built-in functions that are not constructors do not have a "prototype" property unless + otherwise specified in the description of a particular function. + + Unless specified otherwise, a built-in object that is callable as a function is a built-in + function object with the characteristics described in 10.3. Unless specified otherwise, the + [[Extensible]] internal slot of a built-in object initially has the value true. + + Unless otherwise specified every built-in function and every built-in constructor has the + Function prototype object [...] as the value of its [[Prototype]] internal slot. +features: [Temporal] +---*/ + +assert.sameValue(Object.isExtensible(Temporal.Duration.prototype.round), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.Duration.prototype.round), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.Duration.prototype.round), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.Duration.prototype.round.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-dateadd-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-dateadd-called-with-options-undefined.js new file mode 100644 index 0000000000..db506f8cbd --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-dateadd-called-with-options-undefined.js @@ -0,0 +1,76 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: > + BuiltinTimeZoneGetInstantFor calls Calendar.dateAdd with undefined as the + options value +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarDateAddUndefinedOptions(); +const timeZone = TemporalHelpers.oneShiftTimeZone(new Temporal.Instant(0n), 3600e9); +const relativeTo = new Temporal.ZonedDateTime(0n, timeZone, calendar); + +// Rounding with smallestUnit a calendar unit. +// The calls come from these paths: +// Duration.round() -> +// RoundDuration -> +// MoveRelativeZonedDateTime -> AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd() +// NanosecondsToDays -> AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd() +// BalanceDurationRelative -> +// MoveRelativeDate -> calendar.dateAdd() (2x) +// calendar.dateAdd() +// MoveRelativeZonedDateTime -> AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd() +// BalanceDuration -> +// AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd() +// NanosecondsToDays -> AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd() (2x) + +const instance1 = new Temporal.Duration(1, 1, 1, 1, 1); +instance1.round({ smallestUnit: "days", relativeTo }); +assert.sameValue(calendar.dateAddCallCount, 9, "rounding with calendar smallestUnit"); + +// Rounding with a non-default largestUnit to cover the path in +// UnbalanceDurationRelative where larger units are converted into smaller +// units; and with a smallestUnit larger than days to cover the path in +// RoundDuration where days are converted into larger units. +// The calls come from these paths: +// Duration.round() -> +// UnbalanceDurationRelative -> MoveRelativeDate -> calendar.dateAdd() +// RoundDuration -> +// MoveRelativeZonedDateTime -> AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd() +// MoveRelativeDate -> calendar.dateAdd() (5x) +// BalanceDurationRelative +// MoveRelativeDate -> calendar.dateAdd() +// MoveRelativeZonedDateTime -> AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd() + +calendar.dateAddCallCount = 0; + +const instance2 = new Temporal.Duration(0, 1, 1, 1); +instance2.round({ largestUnit: "weeks", smallestUnit: "weeks", relativeTo }); +assert.sameValue(calendar.dateAddCallCount, 9, "rounding with non-default largestUnit and calendar smallestUnit"); + +// Rounding with smallestUnit a non-calendar unit, and having the resulting time +// difference be longer than a calendar day, covering the paths that go through +// AdjustRoundedDurationDays. +// The calls come from these paths: +// Duration.round() -> +// AdjustRoundedDurationDays -> +// AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd() +// AddDuration -> +// AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd() +// NanosecondsToDays -> AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd() (2x) +// BalanceDuration -> +// AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd() +// NanosecondsToDays -> AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd() (2x) + +calendar.dateAddCallCount = 0; + +const instance3 = new Temporal.Duration(0, 0, 0, 0, 23, 59, 59, 999, 999, 999); +instance3.round({ largestUnit: "days", smallestUnit: "hours", roundingMode: "ceil", relativeTo }); +assert.sameValue(calendar.dateAddCallCount, 7, "rounding with time difference exceeding calendar day"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-dateadd-called-with-plaindate-instance.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-dateadd-called-with-plaindate-instance.js new file mode 100644 index 0000000000..7ef310c4d0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-dateadd-called-with-plaindate-instance.js @@ -0,0 +1,21 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: > + relativeTo parameters that are not ZonedDateTime or undefined, are always + converted to PlainDate for observable calendar calls +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarDateAddPlainDateInstance(); +const instance = new Temporal.Duration(1, 1, 1, 1); +const relativeTo = new Temporal.PlainDate(2000, 1, 1, calendar); +calendar.specificPlainDate = relativeTo; +instance.round({ largestUnit: "days", relativeTo }); +assert(calendar.dateAddCallCount > 0, "assertions in calendar.dateAdd() should have been tested"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-dateuntil-called-with-singular-largestunit.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-dateuntil-called-with-singular-largestunit.js new file mode 100644 index 0000000000..039cde4c25 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-dateuntil-called-with-singular-largestunit.js @@ -0,0 +1,163 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form +info: | + sec-temporal.duration.prototype.round steps 20–25: + 20. Let _unbalanceResult_ be ? UnbalanceDurationRelative(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _largestUnit_, _relativeTo_). + 21. Let _roundResult_ be ? RoundDuration(_unbalanceResult_.[[Years]], _unbalanceResult_.[[Months]], _unbalanceResult_.[[Weeks]], _unbalanceResult_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_[[Seconds]], _duration_[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _relativeTo_). + 22. Let _adjustResult_ be ? AdjustRoundedDurationDays(_roundResult_.[[Years]], _roundResult_.[[Months]], _roundResult_.[[Weeks]], _roundResult_.[[Days]], _roundResult_.[[Hours]], _roundResult_.[[Minutes]], _roundResult_.[[Seconds]], _roundResult_.[[Milliseconds]], _roundResult_.[[Microseconds]], _roundResult_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _relativeTo_). + 23. Let _balanceResult_ be ? BalanceDurationRelative(_adjustResult_.[[Years]], _adjustResult_.[[Months]], _adjustResult_.[[Weeks]], _adjustResult_.[[Days]], _largestUnit_, _relativeTo_). + 24. ... + 25. Let _result_ be ? BalanceDuration(_balanceResult_.[[Days]], _adjustResult_.[[Hours]], _adjustResult_.[[Minutes]], _adjustResult_.[[Seconds]], _adjustResult_.[[Milliseconds]], _adjustResult_.[[Microseconds]], _adjustResult.[[Nanoseconds]], _largestUnit_, _relativeTo_). + sec-temporal-unbalancedurationrelative steps 1 and 9.d.iii–v: + 1. If _largestUnit_ is *"year"*, or _years_, _months_, _weeks_, and _days_ are all 0, then + a. Return ... + ... + 9. If _largestUnit_ is *"month"*, then + ... + d. Repeat, while abs(_years_) > 0, + ... + iii. Let _untilOptions_ be ! OrdinaryObjectCreate(*null*). + iv. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"month"*). + v. Let _untilResult_ be ? CalendarDateUntil(_calendar_, _relativeTo_, _newRelativeTo_, _untilOptions_, _dateUntil_). + sec-temporal-roundduration steps 5.d and 8.n–p: + 5. If _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then + ... + d. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _intermediate_). + ... + 8. If _unit_ is *"year"*, then + ... + n. Let _untilOptions_ be ! OrdinaryObjectCreate(*null*). + o. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"year"*). + p. Let _timePassed_ be ? CalendarDateUntil(_calendar_, _relativeTo_, _daysLater_, _untilOptions_) + sec-temporal-adjustroundeddurationdays steps 1 and 9: + 1. If _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot; or _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*; or _unit_ is *"nanosecond"* and _increment_ is 1, then + a. Return ... + ... + 9. Let _adjustedDateDuration_ be ? AddDuration(_years_, _months_, _weeks_, _days_, 0, 0, 0, 0, 0, 0, 0, 0, 0, _direction_, 0, 0, 0, 0, 0, 0, _relativeTo_). + sec-temporal-addduration step 7.a–g: + a. Assert: _relativeTo_ has an [[IntializedTemporalZonedDateTime]] internal slot. + ... + f. If _largestUnit_ is not one of *"year"*, *"month"*, *"week"*, or *"day"*, then + ... + g. Else, + i. Let _result_ be ? DifferenceZonedDateTime(_relativeTo_.[[Nanoseconds]], _endNs_, _timeZone_, _calendar_, _largestUnit_). + sec-temporal-balancedurationrelative steps 1, 9.m–o, and 9.q.vi–viii: + 1. If _largestUnit_ is not one of *"year"*, *"month"*, or *"week"*, or _years_, _months_, _weeks_, and _days_ are all 0, then + a. Return ... + ... + 9. If _largestUnit_ is *"year"*, then + ... + m. Let _untilOptions_ be ! OrdinaryObjectCreate(*null*). + n. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"month"*). + o. Let _untilResult_ be ? CalendarDateUntil(_calendar_, _relativeTo_, _newRelativeTo_, _untilOptions_, _dateUntil_). + p. ... + q. Repeat, while abs(_months_) ≥ abs(_oneYearMonths_), + ... + vi. Let _untilOptions_ be ! OrdinaryObjectCreate(*null*). + vii. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"month"*). + viii. Let _untilResult_ be ? CalendarDateUntil(_calendar_, _relativeTo_, _newRelativeTo_, _untilOptions_, _dateUntil_). + sec-temporal-balanceduration step 3.a: + 3. If _largestUnit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then + a. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _relativeTo_). + sec-temporal-differencezoneddatetime steps 7 and 11: + 7. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _calendar_, _largestUnit_, _options_). + 11. Let _result_ be ? NanosecondsToDays(_timeRemainderNs_, _intermediate_). + sec-temporal-nanosecondstodays step 11: + 11. 1. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _relativeTo_.[[Calendar]], *"day"*). + sec-temporal-differenceisodatetime steps 9–11: + 9. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_). + 10. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _dateLargestUnit_). + 11. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +// Check with smallestUnit nanoseconds but roundingIncrement > 1; each call +// should result in two calls to dateUntil() originating from +// AdjustRoundedDurationDays, one with largestUnit equal to the largest unit in +// the duration higher than "day", and one with largestUnit: "day". +// Additionally one call with largestUnit: "month" in BalanceDurationRelative +// when the largestUnit given to round() is "year", and one call with +// largestUnit: "day" when the largestUnit given to round() is "year", "month", +// "week", or "day". + +const durations = [ + [1, 0, 0, 0, 0, 0, 0, 0, 0, 86399_999_999_999], + [0, 1, 0, 0, 0, 0, 0, 0, 0, 86399_999_999_999], + [0, 0, 1, 0, 0, 0, 0, 0, 0, 86399_999_999_999], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 86399_999_999_999], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 86399_999_999_999], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 86399_999_999_999], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 86399_999_999_999], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 86399_999_999_999], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 86399_999_999_999], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 86399_999_999_999], +].map((args) => new Temporal.Duration(...args)); + +TemporalHelpers.checkCalendarDateUntilLargestUnitSingular( + (calendar, largestUnit, index) => { + const duration = durations[index]; + const relativeTo = new Temporal.ZonedDateTime(0n, "UTC", calendar); + duration.round({ largestUnit, roundingIncrement: 2, roundingMode: 'ceil', relativeTo }); + }, + { + years: ["year", "day", "month", "day"], + months: ["month", "day", "day"], + weeks: ["week", "day", "day"], + days: ["day", "day", "day"], + hours: ["day", "day"], + minutes: ["day", "day"], + seconds: ["day", "day"], + milliseconds: ["day", "day"], + microseconds: ["day", "day"], + nanoseconds: ["day", "day"] + } +); + +// Check the path that converts months to years and vice versa in +// BalanceDurationRelative and UnbalanceDurationRelative. + +TemporalHelpers.checkCalendarDateUntilLargestUnitSingular( + (calendar, largestUnit) => { + const duration = new Temporal.Duration(5, 60); + const relativeTo = new Temporal.PlainDateTime(2000, 5, 2, 0, 0, 0, 0, 0, 0, calendar); + duration.round({ largestUnit, relativeTo }); + }, + { + years: ["month", "month", "month", "month", "month", "month"], + months: ["month", "month", "month", "month", "month"], + weeks: [], + days: [], + hours: [], + minutes: [], + seconds: [], + milliseconds: [], + microseconds: [], + nanoseconds: [] + } +); + +// Check the paths that call dateUntil() in RoundDuration. These paths do not +// call dateUntil() in AdjustRoundedDurationDays. Note that there is no +// largestUnit: "month" call in BalanceDurationRelative and no largestUnit: +// "day" call in BalanceDuration, because the durations have rounded down to 0. + +TemporalHelpers.checkCalendarDateUntilLargestUnitSingular( + (calendar, largestUnit) => { + const duration = new Temporal.Duration(0, 0, 0, 0, 1, 1, 1, 1, 1, 1); + const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_000_000_000n, "UTC", calendar); + duration.round({ largestUnit, smallestUnit: largestUnit, relativeTo }); + }, { + years: ["day", "year"], + months: ["day"], + weeks: ["day"], + days: ["day"] + } +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-fields-iterable.js new file mode 100644 index 0000000000..06519b36cc --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-fields-iterable.js @@ -0,0 +1,40 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Verify the result of calendar.fields() is treated correctly. +info: | + sec-temporal.duration.prototype.round step 19: + 19. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_). + sec-temporal-torelativetemporalobject step 4.c: + c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »). + sec-temporal-calendarfields step 4: + 4. Let _result_ be ? IterableToList(_fieldsArray_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "day", + "hour", + "microsecond", + "millisecond", + "minute", + "month", + "monthCode", + "nanosecond", + "second", + "year", +]; + +const calendar = TemporalHelpers.calendarFieldsIterable(); +const duration = new Temporal.Duration(1, 1, 1, 1, 1, 1, 1); +duration.round({ smallestUnit: 'months', relativeTo: { year: 2000, month: 1, day: 1, calendar } }); + +assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once"); +assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args"); +assert(calendar.iteratorExhausted[0], "iterated through the whole iterable"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-possibly-required.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-possibly-required.js new file mode 100644 index 0000000000..7dbc578d2d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-possibly-required.js @@ -0,0 +1,65 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: relativeTo argument required if days = 0 but years/months/weeks non-zero +features: [Temporal] +includes: [temporalHelpers.js] +---*/ +const duration1 = new Temporal.Duration(1); +const duration2 = new Temporal.Duration(0, 12); +const duration3 = new Temporal.Duration(0, 0, 5); +const duration4 = new Temporal.Duration(0, 0, 0, 42); +const pd = new Temporal.PlainDate(2021, 12, 15); +const relativeToYears = { relativeTo: pd, largestUnit: "years" }; +const relativeToMonths = { relativeTo: pd, largestUnit: "months" }; +const relativeToWeeks = { relativeTo: pd, largestUnit: "weeks" }; +const relativeToDays = { relativeTo: pd, largestUnit: "days" }; + +assert.throws( + RangeError, + () => { duration1.round({ relativeTo: pd }); }, + "rounding a year Duration fails without largest/smallest unit" +); + +TemporalHelpers.assertDuration(duration1.round(relativeToYears), 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "round year duration to years"); +TemporalHelpers.assertDuration(duration1.round(relativeToMonths), 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, "round year duration to months"); +TemporalHelpers.assertDuration(duration1.round(relativeToWeeks), 0, 0, 52, 1, 0, 0, 0, 0, 0, 0, "round year duration to weeks"); +TemporalHelpers.assertDuration(duration1.round(relativeToDays), 0, 0, 0, 365, 0, 0, 0, 0, 0, 0, "round year duration to days"); + +assert.throws( + RangeError, + () => { duration2.round({ relativeTo: pd }); }, + "rounding a month Duration fails without largest/smallest unit" +); + +TemporalHelpers.assertDuration(duration2.round(relativeToYears), 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "round month duration to years"); +TemporalHelpers.assertDuration(duration2.round(relativeToMonths), 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, "round month duration to months"); +TemporalHelpers.assertDuration(duration2.round(relativeToWeeks), 0, 0, 52, 1, 0, 0, 0, 0, 0, 0, "round month duration to weeks"); +TemporalHelpers.assertDuration(duration2.round(relativeToDays), 0, 0, 0, 365, 0, 0, 0, 0, 0, 0, "round month duration to days"); + +assert.throws( + RangeError, + () => { duration3.round({ relativeTo: pd }); }, + "rounding a week Duration fails without largest/smallest unit" +); + +TemporalHelpers.assertDuration(duration3.round(relativeToYears), 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, "round week duration to years"); +TemporalHelpers.assertDuration(duration3.round(relativeToMonths), 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, "round week duration to months"); +TemporalHelpers.assertDuration(duration3.round(relativeToWeeks), 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, "round week duration to weeks"); +TemporalHelpers.assertDuration(duration3.round(relativeToDays), 0, 0, 0, 35, 0, 0, 0, 0, 0, 0, "round week duration to days"); + +assert.throws( + RangeError, + () => { duration4.round({ relativeTo: pd }); }, + "rounding a day Duration fails without largest/smallest unit" +); + +TemporalHelpers.assertDuration(duration4.round(relativeToYears), 0, 1, 0, 11, 0, 0, 0, 0, 0, 0, "round day duration to years"); +TemporalHelpers.assertDuration(duration4.round(relativeToMonths), 0, 1, 0, 11, 0, 0, 0, 0, 0, 0, "round day duration to months"); +TemporalHelpers.assertDuration(duration4.round(relativeToWeeks), 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, "round day duration to weeks"); +TemporalHelpers.assertDuration(duration4.round(relativeToDays), 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, "round day duration to days"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-temporal-object.js new file mode 100644 index 0000000000..39efef0761 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/calendar-temporal-object.js @@ -0,0 +1,29 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots +info: | + sec-temporal.duration.prototype.round step 19: + 19. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_). + sec-temporal-torelativetemporalobject step 4.b: + b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_). + sec-temporal-gettemporalcalendarwithisodefault step 2: + 2. Return ? ToTemporalCalendarWithISODefault(_calendar_). + sec-temporal-totemporalcalendarwithisodefault step 2: + 3. Return ? ToTemporalCalendar(_temporalCalendarLike_). + sec-temporal-totemporalcalendar step 1.a: + a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then + i. Return _temporalCalendarLike_.[[Calendar]]. +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => { + const duration = new Temporal.Duration(1, 1, 1, 1, 1, 1, 1); + duration.round({ smallestUnit: 'months', relativeTo: { year: 2000, month: 1, day: 1, calendar: temporalObject } }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/dateuntil-field.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/dateuntil-field.js new file mode 100644 index 0000000000..57c0602c84 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/dateuntil-field.js @@ -0,0 +1,48 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: > + When consulting calendar.dateUntil() to calculate the number of months in a + year, the months property is not accessed on the result Duration +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +// One path, through UnbalanceDurationRelative, calls dateUntil() in a loop for +// each year in the duration + +const actual = []; +const expected1 = [ + "call dateUntil", + "call dateUntil", +]; +const duration = new Temporal.Duration(0, 12); +TemporalHelpers.observeProperty(actual, duration, "months", 1); + +const calendar = TemporalHelpers.calendarDateUntilObservable(actual, duration); +const relativeTo = new Temporal.PlainDateTime(2018, 10, 12, 0, 0, 0, 0, 0, 0, calendar); + +const years = new Temporal.Duration(2); +const result1 = years.round({ largestUnit: "months", relativeTo }); +TemporalHelpers.assertDuration(result1, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, "result"); +assert.compareArray(actual, expected1, "operations"); + +// There is a second path, through BalanceDurationRelative, that calls +// dateUntil() in a loop for each year in the duration plus one extra time + +actual.splice(0); // reset calls for next test +const expected2 = [ + "call dateUntil", + "call dateUntil", + "call dateUntil", +]; + +const months = new Temporal.Duration(0, 24); +const result2 = months.round({ largestUnit: "years", relativeTo }); +TemporalHelpers.assertDuration(result2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "result"); +assert.compareArray(actual, expected2, "operations"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/february-leap-year.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/february-leap-year.js new file mode 100644 index 0000000000..8092945ab7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/february-leap-year.js @@ -0,0 +1,43 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: RangeError thrown when largestUnit option not one of the allowed string values +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +// Based on a test case by André Bargull <andre.bargull@gmail.com> + +// Note: February in a leap year. +const relativeTo = new Temporal.PlainDate(1972, 2, 1); + +const options = { + largestUnit: "years", + relativeTo, +}; + +const twoDaysLessThanFourYears = new Temporal.Duration(3, 11, 0, 27); +TemporalHelpers.assertDuration( + twoDaysLessThanFourYears.round(options), + 3, 11, 0, 27, 0, 0, 0, 0, 0, 0, + "Two days less than four years starting in February in a leap year shouldn't balance up" +); + +const oneDayLessThanFourYears = new Temporal.Duration(3, 11, 0, 28); +TemporalHelpers.assertDuration( + oneDayLessThanFourYears.round(options), + 3, 11, 0, 28, 0, 0, 0, 0, 0, 0, + "One day less than four years starting in February in a leap year shouldn't balance up" +); + +const fourYears = new Temporal.Duration(3, 11, 0, 29); +TemporalHelpers.assertDuration( + fourYears.round(options), + 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "Four years starting in February in a leap year should balance up" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-invalid-string.js new file mode 100644 index 0000000000..95db46b114 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-invalid-string.js @@ -0,0 +1,14 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: RangeError thrown when largestUnit option not one of the allowed string values +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 987, 500); +assert.throws(RangeError, () => duration.round({ largestUnit: "other string" })); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-plurals-accepted.js new file mode 100644 index 0000000000..352cb88a5c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-plurals-accepted.js @@ -0,0 +1,28 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Plural units are accepted as well for the largestUnit option +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); +const relativeTo = new Temporal.PlainDate(2000, 1, 1); +const validUnits = [ + "year", + "month", + "week", + "day", + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +TemporalHelpers.checkPluralUnitsAccepted((largestUnit) => duration.round({ largestUnit, relativeTo }), validUnits); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-smallestunit-default.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-smallestunit-default.js new file mode 100644 index 0000000000..9ee8a5456f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-smallestunit-default.js @@ -0,0 +1,41 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: assumes a different default for largestUnit if smallestUnit is larger than the default +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const relativeTo = Temporal.PlainDate.from("2020-01-01"); +const almostYear = Temporal.Duration.from({ days: 364 }); +TemporalHelpers.assertDuration(almostYear.round({ smallestUnit: "years", relativeTo }), + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "years"); +const almostMonth = Temporal.Duration.from({ days: 27 }); +TemporalHelpers.assertDuration(almostMonth.round({ smallestUnit: "months", relativeTo }), + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, "months"); +const almostWeek = Temporal.Duration.from({ days: 6 }); +TemporalHelpers.assertDuration(almostWeek.round({ smallestUnit: "weeks", relativeTo }), + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, "weeks"); +const almostDay = Temporal.Duration.from({ seconds: 86399 }); +TemporalHelpers.assertDuration(almostDay.round({ smallestUnit: "days" }), + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, "days"); +const almostHour = Temporal.Duration.from({ seconds: 3599 }); +TemporalHelpers.assertDuration(almostHour.round({ smallestUnit: "hours" }), + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, "hours"); +const almostMinute = Temporal.Duration.from({ seconds: 59 }); +TemporalHelpers.assertDuration(almostMinute.round({ smallestUnit: "minutes" }), + 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, "minutes"); +const almostSecond = Temporal.Duration.from({ nanoseconds: 999999999 }); +TemporalHelpers.assertDuration(almostSecond.round({ smallestUnit: "seconds" }), + 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, "seconds"); +const almostMillisecond = Temporal.Duration.from({ nanoseconds: 999999 }); +TemporalHelpers.assertDuration(almostMillisecond.round({ smallestUnit: "milliseconds" }), + 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, "milliseconds"); +const almostMicrosecond = Temporal.Duration.from({ nanoseconds: 999 }); +TemporalHelpers.assertDuration(almostMicrosecond.round({ smallestUnit: "microseconds" }), + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, "microseconds"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-smallestunit-mismatch.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-smallestunit-mismatch.js new file mode 100644 index 0000000000..b5d3eca51d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-smallestunit-mismatch.js @@ -0,0 +1,23 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: RangeError thrown when smallestUnit is larger than largestUnit +features: [Temporal] +---*/ + +const d = new Temporal.Duration(5, 5, 5, 5, 5, 5, 5, 5, 5, 5); +const relativeTo = Temporal.PlainDate.from('2020-01-01'); +const units = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"]; +for (let largestIdx = 1; largestIdx < units.length; largestIdx++) { + for (let smallestIdx = 0; smallestIdx < largestIdx; smallestIdx++) { + const largestUnit = units[largestIdx]; + const smallestUnit = units[smallestIdx]; + assert.throws(RangeError, () => d.round({ largestUnit, smallestUnit, relativeTo }), + `${smallestUnit} > ${largestUnit}`); + } +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-undefined.js new file mode 100644 index 0000000000..ba1a633d8e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-undefined.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Fallback value for largestUnit option +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const duration1 = new Temporal.Duration(0, 0, 0, 0, 1, 120, 1, 123, 456, 789); +const explicit1 = duration1.round({ largestUnit: undefined, smallestUnit: "nanosecond" }); +TemporalHelpers.assertDuration(explicit1, 0, 0, 0, 0, 3, 0, 1, 123, 456, 789, "default largestUnit is largest in input"); +const implicit1 = duration1.round({ smallestUnit: "nanosecond" }); +TemporalHelpers.assertDuration(implicit1, 0, 0, 0, 0, 3, 0, 1, 123, 456, 789, "default largestUnit is largest in input"); + +const duration2 = new Temporal.Duration(0, 0, 0, 0, 0, 120, 1, 123, 456, 789); +const explicit2 = duration2.round({ largestUnit: undefined, smallestUnit: "nanosecond" }); +TemporalHelpers.assertDuration(explicit2, 0, 0, 0, 0, 0, 120, 1, 123, 456, 789, "default largestUnit is largest in input"); +const implicit2 = duration2.round({ smallestUnit: "nanosecond" }); +TemporalHelpers.assertDuration(implicit2, 0, 0, 0, 0, 0, 120, 1, 123, 456, 789, "default largestUnit is largest in input"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-wrong-type.js new file mode 100644 index 0000000000..c4dafeda71 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/largestunit-wrong-type.js @@ -0,0 +1,18 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Type conversions for largestUnit option +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 456, 789); +TemporalHelpers.checkStringOptionWrongType("largestUnit", "minute", + (largestUnit) => duration.round({ largestUnit }), + (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 754, 56, 123, 456, 789, descr), +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/length.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/length.js new file mode 100644 index 0000000000..f89fe17939 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/length.js @@ -0,0 +1,28 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Temporal.Duration.prototype.round.length is 1 +info: | + Every built-in function object, including constructors, has a "length" property whose value is + an integer. Unless otherwise specified, this value is equal to the largest number of named + arguments shown in the subclause headings for the function description. Optional parameters + (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form + «...name») are not included in the default argument count. + + Unless otherwise specified, the "length" property of a built-in function object has the + attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +verifyProperty(Temporal.Duration.prototype.round, "length", { + value: 1, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/name.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/name.js new file mode 100644 index 0000000000..7cbb8609c8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/name.js @@ -0,0 +1,26 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Temporal.Duration.prototype.round.name is "round". +info: | + Every built-in function object, including constructors, that is not identified as an anonymous + function has a "name" property whose value is a String. Unless otherwise specified, this value + is the name that is given to the function in this specification. + + Unless otherwise specified, the "name" property of a built-in function object, if it exists, + has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +verifyProperty(Temporal.Duration.prototype.round, "name", { + value: "round", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/nanoseconds-to-days-loop-indefinitely-1.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/nanoseconds-to-days-loop-indefinitely-1.js new file mode 100644 index 0000000000..0c12bef3e9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/nanoseconds-to-days-loop-indefinitely-1.js @@ -0,0 +1,61 @@ +// |reftest| skip -- Temporal is not supported +// 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.round +description: > + NanosecondsToDays can loop arbitrarily long, performing observable operations each iteration. +info: | + NanosecondsToDays ( nanoseconds, relativeTo ) + + ... + 15. If sign is 1, then + a. Repeat, while days > 0 and intermediateNs > endNs, + i. Set days to days - 1. + ii. Set intermediateNs to ℝ(? AddZonedDateTime(ℤ(startNs), relativeTo.[[TimeZone]], + relativeTo.[[Calendar]], 0, 0, 0, days, 0, 0, 0, 0, 0, 0)). + ... +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calls = []; +const duration = Temporal.Duration.from({ days: 1 }); + +function createRelativeTo(count) { + const tz = new Temporal.TimeZone("UTC"); + // Record calls in calls[] + TemporalHelpers.observeMethod(calls, tz, "getPossibleInstantsFor"); + const cal = new Temporal.Calendar("iso8601"); + // Return _count_ days for the first call to dateUntil, behaving normally after + TemporalHelpers.substituteMethod(cal, "dateUntil", [ + Temporal.Duration.from({ days: count }), + ]); + return new Temporal.ZonedDateTime(0n, tz, cal); +} + +let zdt = createRelativeTo(200); +calls.splice(0); // Reset calls list after ZonedDateTime construction +duration.round({ + largestUnit: "days", + relativeTo: zdt, +}); +assert.sameValue( + calls.length, + 200 + 2, + "Expected duration.round to call getPossibleInstantsFor correct number of times" +); + +zdt = createRelativeTo(300); +calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction +duration.round({ + largestUnit: "days", + relativeTo: zdt, +}); +assert.sameValue( + calls.length, + 300 + 2, + "Expected duration.round to call getPossibleInstantsFor correct number of times" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/nanoseconds-to-days-loop-indefinitely-2.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/nanoseconds-to-days-loop-indefinitely-2.js new file mode 100644 index 0000000000..51d3b7e100 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/nanoseconds-to-days-loop-indefinitely-2.js @@ -0,0 +1,75 @@ +// |reftest| skip -- Temporal is not supported +// 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.round +description: > + NanosecondsToDays can loop infinitely. +info: | + NanosecondsToDays ( nanoseconds, relativeTo ) + + ... + 18. Repeat, while done is false, + a. Let oneDayFartherNs be ℝ(? AddZonedDateTime(ℤ(intermediateNs), relativeTo.[[TimeZone]], + relativeTo.[[Calendar]], 0, 0, 0, sign, 0, 0, 0, 0, 0, 0)). + b. Set dayLengthNs to oneDayFartherNs - intermediateNs. + c. If (nanoseconds - dayLengthNs) × sign ≥ 0, then + i. Set nanoseconds to nanoseconds - dayLengthNs. + ii. Set intermediateNs to oneDayFartherNs. + iii. Set days to days + sign. + d. Else, + i. Set done to true. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calls = []; +const duration = Temporal.Duration.from({ days: 1 }); + +function createRelativeTo(count) { + const dayLengthNs = 86400000000000n; + const dayInstant = new Temporal.Instant(dayLengthNs); + const substitutions = []; + const timeZone = new Temporal.TimeZone("UTC"); + // Return constant value for first _count_ calls + TemporalHelpers.substituteMethod( + timeZone, + "getPossibleInstantsFor", + substitutions + ); + substitutions.length = count; + let i = 0; + for (i = 0; i < substitutions.length; i++) { + // (this value) + substitutions[i] = [dayInstant]; + } + // Record calls in calls[] + TemporalHelpers.observeMethod(calls, timeZone, "getPossibleInstantsFor"); + return new Temporal.ZonedDateTime(0n, timeZone); +} + +let zdt = createRelativeTo(200); +calls.splice(0); // Reset calls list after ZonedDateTime construction +duration.round({ + smallestUnit: "days", + relativeTo: zdt, +}); +assert.sameValue( + calls.length, + 200 + 1, + "Expected duration.round to call getPossibleInstantsFor correct number of times" +); + +zdt = createRelativeTo(300); +calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction +duration.round({ + smallestUnit: "days", + relativeTo: zdt, +}); +assert.sameValue( + calls.length, + 300 + 1, + "Expected duration.round to call getPossibleInstantsFor correct number of times" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/nanoseconds-to-days-precision-exact-mathematical-values-1.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/nanoseconds-to-days-precision-exact-mathematical-values-1.js new file mode 100644 index 0000000000..b2f4235ed1 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/nanoseconds-to-days-precision-exact-mathematical-values-1.js @@ -0,0 +1,80 @@ +// |reftest| skip -- Temporal is not supported +// 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)); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/nanoseconds-to-days-precision-exact-mathematical-values-2.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/nanoseconds-to-days-precision-exact-mathematical-values-2.js new file mode 100644 index 0000000000..aebaf528bc --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/nanoseconds-to-days-precision-exact-mathematical-values-2.js @@ -0,0 +1,48 @@ +// |reftest| skip -- Temporal is not supported +// 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)); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/not-a-constructor.js new file mode 100644 index 0000000000..3a91f5837a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/not-a-constructor.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: > + Temporal.Duration.prototype.round does not implement [[Construct]], is not new-able +info: | + Built-in function objects that are not identified as constructors do not implement the + [[Construct]] internal method unless otherwise specified in the description of a particular + function. +includes: [isConstructor.js] +features: [Reflect.construct, Temporal] +---*/ + +assert.throws(TypeError, () => { + new Temporal.Duration.prototype.round(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.Duration.prototype.round), false, + "isConstructor(Temporal.Duration.prototype.round)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/number-max-value-too-large.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/number-max-value-too-large.js new file mode 100644 index 0000000000..5783a7b0e0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/number-max-value-too-large.js @@ -0,0 +1,30 @@ +// |reftest| skip -- Temporal is not supported +// 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.round +description: > + RoundDuration throws a RangeError when the result duration is invalid. +features: [Temporal] +---*/ + +function test(unit, nextSmallestUnit) { + var duration = Temporal.Duration.from({ + [unit]: Number.MAX_VALUE, + [nextSmallestUnit]: Number.MAX_VALUE, + }); + + var options = {smallestUnit: unit, largestUnit: unit}; + + assert.throws(RangeError, () => duration.round(options)); +} + +test("days", "hours"); +test("hours", "minutes"); +test("minutes", "seconds"); +test("seconds", "milliseconds"); +test("milliseconds", "microseconds"); +test("microseconds", "nanoseconds"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/options-wrong-type.js new file mode 100644 index 0000000000..561e227d7e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/options-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: TypeError thrown when options argument is missing or a non-string primitive +features: [BigInt, Symbol, Temporal] +---*/ + +const badOptions = [ + undefined, + null, + true, + Symbol(), + 1, + 2n, +]; + +const instance = new Temporal.Duration(0, 0, 0, 0, 1); +assert.throws(TypeError, () => instance.round(), "TypeError on missing options argument"); +for (const value of badOptions) { + assert.throws(TypeError, () => instance.round(value), + `TypeError on wrong options type ${typeof value}`); +}; + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/order-of-operations.js new file mode 100644 index 0000000000..96f11fbed6 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/order-of-operations.js @@ -0,0 +1,314 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Properties on objects passed to round() are accessed in the correct order +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "get options.largestUnit", + "get options.largestUnit.toString", + "call options.largestUnit.toString", + "get options.relativeTo", + "get options.roundingIncrement", + "get options.roundingIncrement.valueOf", + "call options.roundingIncrement.valueOf", + "get options.roundingMode", + "get options.roundingMode.toString", + "call options.roundingMode.toString", + "get options.smallestUnit", + "get options.smallestUnit.toString", + "call options.smallestUnit.toString", +]; +const actual = []; + +function createOptionsObserver({ smallestUnit = "nanoseconds", largestUnit = "auto", roundingMode = "halfExpand", roundingIncrement = 1, relativeTo = undefined } = {}) { + return TemporalHelpers.propertyBagObserver(actual, { + smallestUnit, + largestUnit, + roundingMode, + roundingIncrement, + relativeTo, + }, "options"); +} + +const instance = new Temporal.Duration(0, 0, 0, 0, /* hours = */ 2400); + +// basic order of operations, without relativeTo: +instance.round(createOptionsObserver({ smallestUnit: "microseconds" })); +assert.compareArray(actual, expected, "order of operations"); +actual.splice(0); // clear + +const expectedOpsForPlainRelativeTo = [ + "get options.largestUnit", + "get options.largestUnit.toString", + "call options.largestUnit.toString", + "get options.relativeTo", + "get options.relativeTo.calendar", + "has options.relativeTo.calendar.calendar", + "get options.relativeTo.calendar.fields", + "call options.relativeTo.calendar.fields", + "get options.relativeTo.day", + "get options.relativeTo.day.valueOf", + "call options.relativeTo.day.valueOf", + "get options.relativeTo.hour", + "get options.relativeTo.microsecond", + "get options.relativeTo.millisecond", + "get options.relativeTo.minute", + "get options.relativeTo.month", + "get options.relativeTo.month.valueOf", + "call options.relativeTo.month.valueOf", + "get options.relativeTo.monthCode", + "get options.relativeTo.monthCode.toString", + "call options.relativeTo.monthCode.toString", + "get options.relativeTo.nanosecond", + "get options.relativeTo.offset", + "get options.relativeTo.second", + "get options.relativeTo.timeZone", + "get options.relativeTo.year", + "get options.relativeTo.year.valueOf", + "call options.relativeTo.year.valueOf", + "get options.relativeTo.calendar.dateFromFields", + "call options.relativeTo.calendar.dateFromFields", + "get options.roundingIncrement", + "get options.roundingIncrement.valueOf", + "call options.roundingIncrement.valueOf", + "get options.roundingMode", + "get options.roundingMode.toString", + "call options.roundingMode.toString", + "get options.smallestUnit", + "get options.smallestUnit.toString", + "call options.smallestUnit.toString", +]; + +const plainRelativeTo = TemporalHelpers.propertyBagObserver(actual, { + year: 2001, + month: 5, + monthCode: "M05", + day: 2, + calendar: TemporalHelpers.calendarObserver(actual, "options.relativeTo.calendar"), +}, "options.relativeTo"); + +// basic order of observable operations, without rounding: +instance.round(createOptionsObserver({ relativeTo: plainRelativeTo })); +assert.compareArray(actual, expectedOpsForPlainRelativeTo, "order of operations for PlainDate relativeTo"); +actual.splice(0); // clear + +// code path through RoundDuration that rounds to the nearest year: +const expectedOpsForYearRounding = expectedOpsForPlainRelativeTo.concat([ + "get options.relativeTo.calendar.dateAdd", // 9.b + "call options.relativeTo.calendar.dateAdd", // 9.c + "call options.relativeTo.calendar.dateAdd", // 9.e + "call options.relativeTo.calendar.dateAdd", // 9.j + "get options.relativeTo.calendar.dateUntil", // 9.m + "call options.relativeTo.calendar.dateUntil", // 9.m + "call options.relativeTo.calendar.dateAdd", // 9.r + "call options.relativeTo.calendar.dateAdd", // 9.w MoveRelativeDate +]); +instance.round(createOptionsObserver({ smallestUnit: "years", relativeTo: plainRelativeTo })); +assert.compareArray(actual, expectedOpsForYearRounding, "order of operations with smallestUnit = years"); +actual.splice(0); // clear + +// code path through Duration.prototype.round that rounds to the nearest month: +const expectedOpsForMonthRounding = expectedOpsForPlainRelativeTo.concat([ + // UnbalanceDurationRelative + "get options.relativeTo.calendar.dateAdd", // 9.b + "get options.relativeTo.calendar.dateUntil", // 9.c + "call options.relativeTo.calendar.dateAdd", // 9.d.i + "call options.relativeTo.calendar.dateUntil", // 9.d.iv + // RoundDuration + "get options.relativeTo.calendar.dateAdd", // 10.b + "call options.relativeTo.calendar.dateAdd", // 10.c + "call options.relativeTo.calendar.dateAdd", // 10.e + "call options.relativeTo.calendar.dateAdd", // 10.k MoveRelativeDate +], Array(2).fill("call options.relativeTo.calendar.dateAdd"), [ // 2× 10.n.iii MoveRelativeDate + // BalanceDurationRelative + "get options.relativeTo.calendar.dateAdd", + "call options.relativeTo.calendar.dateAdd", +]); +const instance2 = new Temporal.Duration(1, 0, 0, 62); +instance2.round(createOptionsObserver({ largestUnit: "months", smallestUnit: "months", relativeTo: plainRelativeTo })); +assert.compareArray(actual, expectedOpsForMonthRounding, "order of operations with largestUnit = smallestUnit = months"); +actual.splice(0); // clear + +// code path through Duration.prototype.round that rounds to the nearest week: +const expectedOpsForWeekRounding = expectedOpsForPlainRelativeTo.concat([ + // UnbalanceDurationRelative + "get options.relativeTo.calendar.dateAdd", // 10.b + "call options.relativeTo.calendar.dateAdd", // 10.c.i MoveRelativeDate + "call options.relativeTo.calendar.dateAdd", // 10.d.i MoveRelativeDate + // RoundDuration + "get options.relativeTo.calendar.dateAdd", // 11.c + "call options.relativeTo.calendar.dateAdd", // 11.d MoveRelativeDate +], Array(58).fill("call options.relativeTo.calendar.dateAdd"), [ // 58× 11.g.iii MoveRelativeDate (52 + 4 + 2) + // BalanceDurationRelative + "get options.relativeTo.calendar.dateAdd", // 12.b + "call options.relativeTo.calendar.dateAdd", // 12.c +]); +const instance3 = new Temporal.Duration(1, 1, 0, 15); +instance3.round(createOptionsObserver({ largestUnit: "weeks", smallestUnit: "weeks", relativeTo: plainRelativeTo })); +assert.compareArray(actual, expectedOpsForWeekRounding, "order of operations with largestUnit = smallestUnit = weeks"); +actual.splice(0); // clear + +// code path through UnbalanceDurationRelative that rounds to the nearest day: +const expectedOpsForDayRounding = expectedOpsForPlainRelativeTo.concat([ + "get options.relativeTo.calendar.dateAdd", // 11.a.ii + "call options.relativeTo.calendar.dateAdd", // 11.a.iii.1 MoveRelativeDate + "call options.relativeTo.calendar.dateAdd", // 11.a.iv.1 MoveRelativeDate + "call options.relativeTo.calendar.dateAdd", // 11.a.v.1 MoveRelativeDate +]); +const instance4 = new Temporal.Duration(1, 1, 1) +instance4.round(createOptionsObserver({ largestUnit: "days", smallestUnit: "days", relativeTo: plainRelativeTo })); +assert.compareArray(actual, expectedOpsForDayRounding, "order of operations with largestUnit = smallestUnit = days"); +actual.splice(0); // clear + +// code path through BalanceDurationRelative balancing from days up to years: +const expectedOpsForDayToYearBalancing = expectedOpsForPlainRelativeTo.concat([ + "get options.relativeTo.calendar.dateAdd", // 10.a + "call options.relativeTo.calendar.dateAdd", // 10.b MoveRelativeDate + "call options.relativeTo.calendar.dateAdd", // 10.e.iv MoveRelativeDate + "call options.relativeTo.calendar.dateAdd", // 10.f MoveRelativeDate + "call options.relativeTo.calendar.dateAdd", // 10.i.iv MoveRelativeDate + "call options.relativeTo.calendar.dateAdd", // 10.j + "get options.relativeTo.calendar.dateUntil", // 10.k + "call options.relativeTo.calendar.dateUntil", // 10.n +]); +const instance5 = new Temporal.Duration(0, 0, 0, 0, /* hours = */ 396 * 24); +instance5.round(createOptionsObserver({ largestUnit: "years", smallestUnit: "days", relativeTo: plainRelativeTo })); +assert.compareArray(actual, expectedOpsForDayToYearBalancing, "order of operations with largestUnit = years, smallestUnit = days"); +actual.splice(0); // clear + +// code path through Duration.prototype.round balancing from months up to years: +const expectedOpsForMonthToYearBalancing = expectedOpsForPlainRelativeTo.concat([ + // RoundDuration + "get options.relativeTo.calendar.dateAdd", // 10.b + "call options.relativeTo.calendar.dateAdd", // 10.c + "call options.relativeTo.calendar.dateAdd", // 10.e + "call options.relativeTo.calendar.dateAdd", // 10.k MoveRelativeDate + // BalanceDurationRelative + "get options.relativeTo.calendar.dateAdd", // 10.a + "call options.relativeTo.calendar.dateAdd", // 10.b MoveRelativeDate + "call options.relativeTo.calendar.dateAdd", // 10.f MoveRelativeDate + "call options.relativeTo.calendar.dateAdd", // 10.j + "get options.relativeTo.calendar.dateUntil", // 10.k + "call options.relativeTo.calendar.dateUntil", // 10.n + "call options.relativeTo.calendar.dateAdd", // 10.p.iv + "call options.relativeTo.calendar.dateUntil", // 10.p.vii +]); +const instance6 = new Temporal.Duration(0, 12); +instance6.round(createOptionsObserver({ largestUnit: "years", smallestUnit: "months", relativeTo: plainRelativeTo })); +assert.compareArray(actual, expectedOpsForMonthToYearBalancing, "order of operations with largestUnit = years, smallestUnit = months"); +actual.splice(0); // clear + +const expectedOpsForDayToMonthBalancing = expectedOpsForPlainRelativeTo.concat([ + // BalanceDurationRelative + "get options.relativeTo.calendar.dateAdd", // 11.a + "call options.relativeTo.calendar.dateAdd", // 11.b MoveRelativeDate + "call options.relativeTo.calendar.dateAdd", // 11.e.iv MoveRelativeDate +]); +const instance7 = new Temporal.Duration(0, 0, 0, 0, /* hours = */ 32 * 24); +instance7.round(createOptionsObserver({ largestUnit: "months", smallestUnit: "days", relativeTo: plainRelativeTo })); +assert.compareArray(actual, expectedOpsForDayToMonthBalancing, "order of operations with largestUnit = months, smallestUnit = days"); +actual.splice(0); // clear + +const expectedOpsForDayToWeekBalancing = expectedOpsForPlainRelativeTo.concat([ + // BalanceDurationRelative + "get options.relativeTo.calendar.dateAdd", // 12.b + "call options.relativeTo.calendar.dateAdd", // 12.c MoveRelativeDate + "call options.relativeTo.calendar.dateAdd", // 12.f.iv MoveRelativeDate +]); +const instance8 = new Temporal.Duration(0, 0, 0, 0, /* hours = */ 8 * 24); +instance8.round(createOptionsObserver({ largestUnit: "weeks", smallestUnit: "days", relativeTo: plainRelativeTo })); +assert.compareArray(actual, expectedOpsForDayToWeekBalancing, "order of operations with largestUnit = weeks, smallestUnit = days"); +actual.splice(0); // clear + +const expectedOpsForZonedRelativeTo = [ + "get options.largestUnit", + "get options.largestUnit.toString", + "call options.largestUnit.toString", + "get options.relativeTo", + "get options.relativeTo.calendar", + "has options.relativeTo.calendar.calendar", + "get options.relativeTo.calendar.fields", + "call options.relativeTo.calendar.fields", + "get options.relativeTo.day", + "get options.relativeTo.day.valueOf", + "call options.relativeTo.day.valueOf", + "get options.relativeTo.hour", + "get options.relativeTo.hour.valueOf", + "call options.relativeTo.hour.valueOf", + "get options.relativeTo.microsecond", + "get options.relativeTo.microsecond.valueOf", + "call options.relativeTo.microsecond.valueOf", + "get options.relativeTo.millisecond", + "get options.relativeTo.millisecond.valueOf", + "call options.relativeTo.millisecond.valueOf", + "get options.relativeTo.minute", + "get options.relativeTo.minute.valueOf", + "call options.relativeTo.minute.valueOf", + "get options.relativeTo.month", + "get options.relativeTo.month.valueOf", + "call options.relativeTo.month.valueOf", + "get options.relativeTo.monthCode", + "get options.relativeTo.monthCode.toString", + "call options.relativeTo.monthCode.toString", + "get options.relativeTo.nanosecond", + "get options.relativeTo.nanosecond.valueOf", + "call options.relativeTo.nanosecond.valueOf", + "get options.relativeTo.offset", + "get options.relativeTo.offset.toString", + "call options.relativeTo.offset.toString", + "get options.relativeTo.second", + "get options.relativeTo.second.valueOf", + "call options.relativeTo.second.valueOf", + "get options.relativeTo.timeZone", + "get options.relativeTo.year", + "get options.relativeTo.year.valueOf", + "call options.relativeTo.year.valueOf", + "get options.relativeTo.calendar.dateFromFields", + "call options.relativeTo.calendar.dateFromFields", + "has options.relativeTo.timeZone.timeZone", + // InterpretISODateTimeOffset + "get options.relativeTo.timeZone.getPossibleInstantsFor", + "call options.relativeTo.timeZone.getPossibleInstantsFor", + "get options.relativeTo.timeZone.getOffsetNanosecondsFor", + "call options.relativeTo.timeZone.getOffsetNanosecondsFor", + "get options.roundingIncrement", + "get options.roundingIncrement.valueOf", + "call options.roundingIncrement.valueOf", + "get options.roundingMode", + "get options.roundingMode.toString", + "call options.roundingMode.toString", + "get options.smallestUnit", + "get options.smallestUnit.toString", + "call options.smallestUnit.toString", + // RoundDuration → ToTemporalDate + "get options.relativeTo.timeZone.getOffsetNanosecondsFor", + "call options.relativeTo.timeZone.getOffsetNanosecondsFor", +]; + +const zonedRelativeTo = TemporalHelpers.propertyBagObserver(actual, { + year: 2001, + month: 5, + monthCode: "M05", + day: 2, + hour: 6, + minute: 54, + second: 32, + millisecond: 987, + microsecond: 654, + nanosecond: 321, + offset: "+00:00", + calendar: TemporalHelpers.calendarObserver(actual, "options.relativeTo.calendar"), + timeZone: TemporalHelpers.timeZoneObserver(actual, "options.relativeTo.timeZone"), +}, "options.relativeTo"); + +// basic order of operations with ZonedDateTime relativeTo: +instance.round(createOptionsObserver({ relativeTo: zonedRelativeTo })); +assert.compareArray(actual, expectedOpsForZonedRelativeTo, "order of operations for ZonedDateTime relativeTo"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/precision-exact-in-balance-duration-relative-months.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/precision-exact-in-balance-duration-relative-months.js new file mode 100644 index 0000000000..b0048c76ef --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/precision-exact-in-balance-duration-relative-months.js @@ -0,0 +1,55 @@ +// |reftest| skip -- Temporal is not supported +// 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.round +description: > + BalanceDurationRelative computes on exact mathematical values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +{ + let date = new Temporal.PlainDate(1970, 1, 1); + + let duration = Temporal.Duration.from({ + months: Number.MAX_SAFE_INTEGER, + days: 31 + 28 + 31, + }); + + let result = duration.round({ + largestUnit: "months", + relativeTo: date, + }); + + TemporalHelpers.assertDuration( + result, + 0, 9007199254740994, 0, 0, + 0, 0, 0, + 0, 0, 0, + ); +} + +{ + let date = new Temporal.PlainDate(1970, 1, 1); + + let duration = Temporal.Duration.from({ + months: Number.MAX_SAFE_INTEGER, + days: 31 + 28 + 31 + 1, + }); + + let result = duration.round({ + largestUnit: "months", + relativeTo: date, + }); + + TemporalHelpers.assertDuration( + result, + 0, 9007199254740994, 0, 1, + 0, 0, 0, + 0, 0, 0, + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/precision-exact-in-balance-duration-relative-weeks.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/precision-exact-in-balance-duration-relative-weeks.js new file mode 100644 index 0000000000..530b19ddfa --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/precision-exact-in-balance-duration-relative-weeks.js @@ -0,0 +1,55 @@ +// |reftest| skip -- Temporal is not supported +// 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.round +description: > + BalanceDurationRelative computes on exact mathematical values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +{ + let date = new Temporal.PlainDate(1970, 1, 1); + + let duration = Temporal.Duration.from({ + weeks: Number.MAX_SAFE_INTEGER, + days: 7 * 3, + }); + + let result = duration.round({ + largestUnit: "weeks", + relativeTo: date, + }); + + TemporalHelpers.assertDuration( + result, + 0, 0, 9007199254740994, 0, + 0, 0, 0, + 0, 0, 0, + ); +} + +{ + let date = new Temporal.PlainDate(1970, 1, 1); + + let duration = Temporal.Duration.from({ + weeks: Number.MAX_SAFE_INTEGER, + days: 7 * 3 + 1, + }); + + let result = duration.round({ + largestUnit: "weeks", + relativeTo: date, + }); + + TemporalHelpers.assertDuration( + result, + 0, 0, 9007199254740994, 1, + 0, 0, 0, + 0, 0, 0, + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/precision-exact-in-balance-duration-relative-years-days.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/precision-exact-in-balance-duration-relative-years-days.js new file mode 100644 index 0000000000..7a5978d281 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/precision-exact-in-balance-duration-relative-years-days.js @@ -0,0 +1,55 @@ +// |reftest| skip -- Temporal is not supported +// 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.round +description: > + BalanceDurationRelative computes on exact mathematical values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +{ + let date = new Temporal.PlainDate(1970, 1, 1); + + let duration = Temporal.Duration.from({ + years: Number.MAX_SAFE_INTEGER, + days: 366 + 365 + 365, + }); + + let result = duration.round({ + largestUnit: "years", + relativeTo: date, + }); + + TemporalHelpers.assertDuration( + result, + 9007199254740994, 0, 0, 0, + 0, 0, 0, + 0, 0, 0, + ); +} + +{ + let date = new Temporal.PlainDate(1970, 1, 1); + + let duration = Temporal.Duration.from({ + years: Number.MAX_SAFE_INTEGER, + days: 366 + 365 + 365 + 1, + }); + + let result = duration.round({ + largestUnit: "years", + relativeTo: date, + }); + + TemporalHelpers.assertDuration( + result, + 9007199254740994, 0, 0, 1, + 0, 0, 0, + 0, 0, 0, + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/precision-exact-in-balance-duration-relative-years-months.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/precision-exact-in-balance-duration-relative-years-months.js new file mode 100644 index 0000000000..e65a7c2fd6 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/precision-exact-in-balance-duration-relative-years-months.js @@ -0,0 +1,55 @@ +// |reftest| skip -- Temporal is not supported +// 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.round +description: > + BalanceDurationRelative computes on exact mathematical values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +{ + let date = new Temporal.PlainDate(1970, 1, 1); + + let duration = Temporal.Duration.from({ + years: Number.MAX_SAFE_INTEGER, + months: 12 * 3, + }); + + let result = duration.round({ + largestUnit: "years", + relativeTo: date, + }); + + TemporalHelpers.assertDuration( + result, + 9007199254740994, 0, 0, 0, + 0, 0, 0, + 0, 0, 0, + ); +} + +{ + let date = new Temporal.PlainDate(1970, 1, 1); + + let duration = Temporal.Duration.from({ + years: Number.MAX_SAFE_INTEGER, + months: 12 * 3 + 1, + }); + + let result = duration.round({ + largestUnit: "years", + relativeTo: date, + }); + + TemporalHelpers.assertDuration( + result, + 9007199254740994, 1, 0, 0, + 0, 0, 0, + 0, 0, 0, + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/precision-exact-in-balance-duration-relative-years-with-calendar.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/precision-exact-in-balance-duration-relative-years-with-calendar.js new file mode 100644 index 0000000000..203525d8a6 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/precision-exact-in-balance-duration-relative-years-with-calendar.js @@ -0,0 +1,56 @@ +// |reftest| skip -- Temporal is not supported +// 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.round +description: > + BalanceDurationRelative computes on exact mathematical values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +let calendar = new class extends Temporal.Calendar { + #dateUntil = 0; + dateUntil(one, two, options) { + this.#dateUntil++; + + // Subtract one six times from 9007199254740996. + if (this.#dateUntil <= 6) { + return Temporal.Duration.from({months: 1}); + } + + // After subtracting six in total, months is 9007199254740996 - 6 = 9007199254740990. + // |MAX_SAFE_INTEGER| = 9007199254740991 is larger than 9007199254740990, so we exit + // from the loop. + if (this.#dateUntil === 7) { + return Temporal.Duration.from({months: Number.MAX_SAFE_INTEGER}); + } + + // Any additional calls to dateUntil are incorrect. + throw new Test262Error("dateUntil called more times than expected"); + } +}("iso8601"); + +let date = new Temporal.PlainDate(1970, 1, 1, calendar); + +let duration = Temporal.Duration.from({ + years: 0, + months: Number.MAX_SAFE_INTEGER + 4, // 9007199254740996 +}); + +let result = duration.round({ + largestUnit: "years", + relativeTo: date, +}); + +// Years is equal to the number of times we returned one month from dateUntil. +// Months is equal to 9007199254740996 - 6. +TemporalHelpers.assertDuration( + result, + 6, 9007199254740990, 0, 0, + 0, 0, 0, + 0, 0, 0, +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/precision-exact-in-round-duration.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/precision-exact-in-round-duration.js new file mode 100644 index 0000000000..f057c65b51 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/precision-exact-in-round-duration.js @@ -0,0 +1,53 @@ +// |reftest| skip -- Temporal is not supported +// 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.round +description: > + RoundDuration computes on exact mathematical values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +{ + let duration = Temporal.Duration.from({ + hours: 100_000, + nanoseconds: 5, + }); + + let rounded = duration.round({smallestUnit: "hours", roundingMode: "ceil"}); + + // If RoundDuration() was implemented with float64, precision loss would lead + // to computing an incorrect result. + // + // "PT100000H" with float64, but "PT100001H" with exact mathematical values. + TemporalHelpers.assertDuration( + rounded, + 0, 0, 0, 0, + 100001, 0, 0, + 0, 0, 0, + ); +} + +{ + let duration = Temporal.Duration.from({ + days: 1000, + nanoseconds: 5, + }); + + let rounded = duration.round({smallestUnit: "days", roundingMode: "ceil"}); + + // If RoundDuration() was implemented with float64, precision loss would lead + // to computing an incorrect result. + // + // "P1000D" with float64, but "P1001D" with exact mathematical values. + TemporalHelpers.assertDuration( + rounded, + 0, 0, 0, 1001, + 0, 0, 0, + 0, 0, 0, + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/prop-desc.js new file mode 100644 index 0000000000..876fc83d43 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: The "round" property of Temporal.Duration.prototype +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.Duration.prototype.round, + "function", + "`typeof Duration.prototype.round` is `function`" +); + +verifyProperty(Temporal.Duration.prototype, "round", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/read-time-fields-before-datefromfields.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/read-time-fields-before-datefromfields.js new file mode 100644 index 0000000000..6d3e3cf162 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/read-time-fields-before-datefromfields.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: The time fields are read from the object before being passed to dateFromFields(). +info: | + sec-temporal.duration.prototype.round step 19: + 19. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_). + sec-temporal-torelativetemporalobject step 4.g: + g. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_). + sec-temporal-interprettemporaldatetimefields steps 1–2: + 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_). + 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarMakeInvalidGettersTime(); +const duration = new Temporal.Duration(1, 1, 1, 1, 1, 1, 1); +duration.round({ smallestUnit: 'months', relativeTo: { year: 2000, month: 1, day: 1, calendar } }); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-infinity-throws-rangeerror.js new file mode 100644 index 0000000000..6f4b5bf9a9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-infinity-throws-rangeerror.js @@ -0,0 +1,26 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Throws if any value in the property bag is Infinity or -Infinity +esid: sec-temporal.duration.prototype.round +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); +const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321 }; + +[Infinity, -Infinity].forEach((inf) => { + ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => { + assert.throws(RangeError, () => instance.round({ smallestUnit: "seconds", relativeTo: { ...base, [prop]: inf } }), `${prop} property cannot be ${inf} in relativeTo`); + + const calls = []; + const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop); + assert.throws(RangeError, () => instance.round({ smallestUnit: "seconds", relativeTo: { ...base, [prop]: obj } })); + assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value"); + }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-leap-second.js new file mode 100644 index 0000000000..dc78f1117f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-leap-second.js @@ -0,0 +1,46 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Leap second is constrained in both an ISO string and a property bag +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1, 0, 0, 0, 24); + +let relativeTo = "2016-12-31T23:59:60"; +const result1 = instance.round({ largestUnit: "years", relativeTo }); +TemporalHelpers.assertDuration( + result1, + 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, + "leap second is a valid ISO string for PlainDate relativeTo" +); + +relativeTo = "2016-12-31T23:59:60+00:00[UTC]"; +const result2 = instance.round({ largestUnit: "years", relativeTo }); +TemporalHelpers.assertDuration( + result2, + 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, + "leap second is a valid ISO string for ZonedDateTime relativeTo" +); + +relativeTo = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 }; +const result3 = instance.round({ largestUnit: "years", relativeTo }); +TemporalHelpers.assertDuration( + result3, + 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, + "second: 60 is valid in a property bag for PlainDate relativeTo" +); + +relativeTo = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60, timeZone: "UTC" }; +const result4 = instance.round({ largestUnit: "years", relativeTo }); +TemporalHelpers.assertDuration( + result4, + 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, + "second: 60 is valid in a property bag for ZonedDateTime relativeTo" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-number.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-number.js new file mode 100644 index 0000000000..a4f913d222 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-number.js @@ -0,0 +1,33 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: A number as relativeTo option is converted to a string, then to Temporal.PlainDate +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1, 0, 0, 0, 24); + +const relativeTo = 20191101; + +const result = instance.round({ largestUnit: "years", relativeTo }); +TemporalHelpers.assertDuration(result, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, "20191101 is a valid ISO string for relativeTo"); + +const numbers = [ + 1, + -20191101, + 1234567890, +]; + +for (const relativeTo of numbers) { + assert.throws( + RangeError, + () => instance.round({ largestUnit: "years", relativeTo }), + `Number ${relativeTo} does not convert to a valid ISO string for relativeTo` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-calendar-datefromfields-called-with-null-prototype-fields.js new file mode 100644 index 0000000000..4a8eb93230 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-calendar-datefromfields-called-with-null-prototype-fields.js @@ -0,0 +1,19 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: > + Calendar.dateFromFields method is called with a null-prototype fields object +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution(); +const instance = new Temporal.Duration(1, 0, 0, 0, 24); +const relativeTo = { year: 2000, month: 5, day: 2, calendar }; +instance.round({ largestUnit: "years", relativeTo }); +assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-calendar-number.js new file mode 100644 index 0000000000..8a343976e7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-calendar-number.js @@ -0,0 +1,45 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: A number as calendar in relativeTo property bag is converted to a string, then to a calendar +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1, 0, 0, 0, 24); + +const calendar = 19970327; + +let relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar }; +const result1 = instance.round({ largestUnit: "years", relativeTo }); +TemporalHelpers.assertDuration(result1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, "19970327 is a valid ISO string for relativeTo.calendar"); + +relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar: { calendar } }; +const result2 = instance.round({ largestUnit: "years", relativeTo }); +TemporalHelpers.assertDuration(result2, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, "19970327 is a valid ISO string for relativeTo.calendar (nested property)"); + +const numbers = [ + 1, + -19970327, + 1234567890, +]; + +for (const calendar of numbers) { + let relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar }; + assert.throws( + RangeError, + () => instance.round({ largestUnit: "years", relativeTo }), + `Number ${calendar} does not convert to a valid ISO string for relativeTo.calendar` + ); + relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar: { calendar } }; + assert.throws( + RangeError, + () => instance.round({ largestUnit: "years", relativeTo }), + `Number ${calendar} does not convert to a valid ISO string for relativeTo.calendar (nested property)` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-calendar-wrong-type.js new file mode 100644 index 0000000000..20024a8532 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-calendar-wrong-type.js @@ -0,0 +1,53 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: > + Appropriate error thrown when relativeTo.calendar cannot be converted to a + calendar object or string +features: [BigInt, Symbol, Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.Duration(1, 0, 0, 0, 24); + +const rangeErrorTests = [ + [null, "null"], + [true, "boolean"], + ["", "empty string"], + [1, "number that doesn't convert to a valid ISO string"], + [1n, "bigint"], + [new Temporal.TimeZone("UTC"), "time zone instance"], +]; + +for (const [calendar, description] of rangeErrorTests) { + let relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar }; + assert.throws(RangeError, () => instance.round({ largestUnit: "years", relativeTo }), `${description} does not convert to a valid ISO string`); + + relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar: { calendar } }; + assert.throws(RangeError, () => instance.round({ largestUnit: "years", relativeTo }), `${description} does not convert to a valid ISO string (nested property)`); +} + +const typeErrorTests = [ + [Symbol(), "symbol"], + [{}, "plain object"], + [Temporal.PlainDate, "Temporal.PlainDate, object"], + [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"], + [Temporal.ZonedDateTime, "Temporal.ZonedDateTime, object"], + [Temporal.ZonedDateTime.prototype, "Temporal.ZonedDateTime.prototype, object"], +]; + +for (const [calendar, description] of typeErrorTests) { + let relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar }; + assert.throws(TypeError, () => instance.round({ largestUnit: "years", relativeTo }), `${description} is not a valid property bag and does not convert to a string`); + + relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar: { calendar } }; + assert.throws(TypeError, () => instance.round({ largestUnit: "years", relativeTo }), `${description} is not a valid property bag and does not convert to a string (nested property)`); +} + +const relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar: { calendar: undefined } }; +assert.throws(RangeError, () => instance.round({ largestUnit: "years", relativeTo }), `nested undefined calendar property is always a RangeError`); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-invalid-offset-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-invalid-offset-string.js new file mode 100644 index 0000000000..62dcb814a5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-invalid-offset-string.js @@ -0,0 +1,25 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: relativeTo property bag with offset property is rejected if offset is in the wrong format +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.Duration(1, 0, 0, 0, 24); + +const badOffsets = [ + "00:00", // missing sign + "+0", // too short + "-000:00", // too long + 0, // converts to a string that is invalid +]; +badOffsets.forEach((offset) => { + const relativeTo = { year: 2021, month: 10, day: 28, offset, timeZone }; + assert.throws(RangeError, () => instance.round({ largestUnit: "years", relativeTo }), `"${offset} is not a valid offset string`); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-no-time-units.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-no-time-units.js new file mode 100644 index 0000000000..ef01abbada --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-no-time-units.js @@ -0,0 +1,18 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Missing time units in relativeTo property bag default to 0 +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1, 0, 0, 0, 24); + +let relativeTo = { year: 2000, month: 1, day: 1 }; +const result = instance.round({ largestUnit: "years", relativeTo }); +TemporalHelpers.assertDuration(result, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, "missing time units default to 0"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..7e182b4491 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,18 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); + assert.throws(RangeError, () => duration.round({ smallestUnit: "seconds", relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..f57aaf852e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,22 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => duration.round({ smallestUnit: "seconds", relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..45825ea137 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,18 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); + assert.throws(RangeError, () => duration.round({ smallestUnit: "seconds", relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..4a85e22460 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); + assert.throws(TypeError, () => duration.round({ smallestUnit: "seconds", relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-instance-does-not-get-timeZone-property.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-instance-does-not-get-timeZone-property.js new file mode 100644 index 0000000000..e5f10efaa7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-instance-does-not-get-timeZone-property.js @@ -0,0 +1,25 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: > + A Temporal.TimeZone instance passed to round() does not have its + 'timeZone' property observably checked +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1); + +const timeZone = new Temporal.TimeZone("UTC"); +Object.defineProperty(timeZone, "timeZone", { + get() { + throw new Test262Error("timeZone.timeZone should not be accessed"); + }, +}); + +instance.round({ largestUnit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone } }); +instance.round({ largestUnit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-string-datetime.js new file mode 100644 index 0000000000..4cfff7dbdc --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-string-datetime.js @@ -0,0 +1,39 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Conversion of ISO date-time strings to Temporal.TimeZone instances +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1); + +let timeZone = "2021-08-19T17:30"; +assert.throws(RangeError, () => instance.round({ largestUnit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), "bare date-time string is not a time zone"); +assert.throws(RangeError, () => instance.round({ largestUnit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }), "bare date-time string is not a time zone"); + +// The following are all valid strings so should not throw: + +[ + "2021-08-19T17:30Z", + "2021-08-19T1730Z", + "2021-08-19T17:30-07:00", + "2021-08-19T1730-07:00", + "2021-08-19T17:30-0700", + "2021-08-19T1730-0700", + "2021-08-19T17:30[UTC]", + "2021-08-19T1730[UTC]", + "2021-08-19T17:30Z[UTC]", + "2021-08-19T1730Z[UTC]", + "2021-08-19T17:30-07:00[UTC]", + "2021-08-19T1730-07:00[UTC]", + "2021-08-19T17:30-0700[UTC]", + "2021-08-19T1730-0700[UTC]", +].forEach((timeZone) => { + instance.round({ largestUnit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone } }); + instance.round({ largestUnit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-string-leap-second.js new file mode 100644 index 0000000000..ab8265731f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-string-leap-second.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Leap second is a valid ISO string for TimeZone +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1); +let timeZone = "2016-12-31T23:59:60+00:00[UTC]"; + +// A string with a leap second is a valid ISO string, so the following two +// operations should not throw + +instance.round({ largestUnit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone } }); +instance.round({ largestUnit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }); + +timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]"; +assert.throws(RangeError, () => instance.round({ largestUnit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), "leap second in time zone name not valid"); +assert.throws(RangeError, () => instance.round({ largestUnit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }), "leap second in time zone name not valid (nested property)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-string-year-zero.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-string-year-zero.js new file mode 100644 index 0000000000..0ae795e75f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-string-year-zero.js @@ -0,0 +1,29 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Negative zero, as an extended year, is rejected +features: [Temporal, arrow-function] +---*/ + +const invalidStrings = [ + "-000000-10-31T17:45Z", + "-000000-10-31T17:45+00:00[UTC]", +]; +const instance = new Temporal.Duration(1); +invalidStrings.forEach((timeZone) => { + assert.throws( + RangeError, + () => instance.round({ largestUnit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), + "reject minus zero as extended year" + ); + assert.throws( + RangeError, + () => instance.round({ largestUnit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }), + "reject minus zero as extended year (nested property)" + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-string.js new file mode 100644 index 0000000000..2167a57923 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-string.js @@ -0,0 +1,19 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Time zone IDs are valid input for a time zone +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1); + +// The following are all valid strings so should not throw: + +["UTC", "+01:00"].forEach((timeZone) => { + instance.round({ largestUnit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone } }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-wrong-type.js new file mode 100644 index 0000000000..2b8d16dc34 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-propertybag-timezone-wrong-type.js @@ -0,0 +1,42 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: > + Appropriate error thrown when argument cannot be converted to a valid string + or object for TimeZone +features: [BigInt, Symbol, Temporal] +---*/ + +const instance = new Temporal.Duration(1); + +const rangeErrorTests = [ + [null, "null"], + [true, "boolean"], + ["", "empty string"], + [1, "number that doesn't convert to a valid ISO string"], + [19761118, "number that would convert to a valid ISO string in other contexts"], + [1n, "bigint"], + [new Temporal.Calendar("iso8601"), "calendar instance"], +]; + +for (const [timeZone, description] of rangeErrorTests) { + assert.throws(RangeError, () => instance.round({ largestUnit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), `${description} does not convert to a valid ISO string`); + assert.throws(RangeError, () => instance.round({ largestUnit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }), `${description} does not convert to a valid ISO string (nested property)`); +} + +const typeErrorTests = [ + [Symbol(), "symbol"], +]; + +for (const [timeZone, description] of typeErrorTests) { + assert.throws(TypeError, () => instance.round({ largestUnit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), `${description} is not a valid object and does not convert to a string`); + assert.throws(TypeError, () => instance.round({ largestUnit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }), `${description} is not a valid object and does not convert to a string (nested property)`); +} + +const timeZone = undefined; +assert.throws(RangeError, () => instance.round({ largestUnit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }), `undefined is always a RangeError as nested property`); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-string-datetime.js new file mode 100644 index 0000000000..ac3bf16ce1 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-string-datetime.js @@ -0,0 +1,41 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: > + Conversion of ISO date-time strings as relativeTo option to + Temporal.ZonedDateTime or Temporal.PlainDateTime instances +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1, 0, 0, 0, 24); + +let relativeTo = "2019-11-01T00:00"; +const result1 = instance.round({ largestUnit: "years", relativeTo }); +TemporalHelpers.assertDuration(result1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, "bare date-time string is a plain relativeTo"); + +relativeTo = "2019-11-01T00:00-07:00"; +const result2 = instance.round({ largestUnit: "years", relativeTo }); +TemporalHelpers.assertDuration(result2, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, "date-time + offset is a plain relativeTo"); + +relativeTo = "2019-11-01T00:00[-07:00]"; +const result3 = instance.round({ largestUnit: "years", relativeTo }); +TemporalHelpers.assertDuration(result3, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, "date-time + IANA annotation is a zoned relativeTo"); + +relativeTo = "2019-11-01T00:00Z[-07:00]"; +const result4 = instance.round({ largestUnit: "years", relativeTo }); +TemporalHelpers.assertDuration(result4, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, "date-time + Z + IANA annotation is a zoned relativeTo"); + +relativeTo = "2019-11-01T00:00+00:00[UTC]"; +const result5 = instance.round({ largestUnit: "years", relativeTo }); +TemporalHelpers.assertDuration(result5, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, "date-time + offset + IANA annotation is a zoned relativeTo"); + +relativeTo = "2019-11-01T00:00Z"; +assert.throws(RangeError, () => instance.round({ largestUnit: "years", relativeTo }), "date-time + Z throws without an IANA annotation"); +relativeTo = "2019-11-01T00:00+04:15[UTC]"; +assert.throws(RangeError, () => instance.round({ largestUnit: "years", relativeTo }), "date-time + offset + IANA annotation throws if wall time and exact time mismatch"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-string-invalid.js new file mode 100644 index 0000000000..32df5017a5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-string-invalid.js @@ -0,0 +1,16 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: RangeError thrown if relativeTo is a string with the wrong format +features: [Temporal] +---*/ + +['bad string', '15:30:45.123456', 'iso8601', 'UTC', 'P1YT1H'].forEach((relativeTo) => { + const duration = new Temporal.Duration(0, 0, 0, 31); + assert.throws(RangeError, () => duration.round({ largestUnit: "months", relativeTo })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-string-plaindatetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-string-plaindatetime.js new file mode 100644 index 0000000000..8459a1ec13 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-string-plaindatetime.js @@ -0,0 +1,18 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: The relativeTo option accepts a PlainDateTime-like ISO 8601 string +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +['2000-01-01', '2000-01-01T00:00', '2000-01-01T00:00[u-ca=iso8601]'].forEach((relativeTo) => { + const duration = new Temporal.Duration(0, 0, 0, 31); + const result = duration.round({ largestUnit: "months", relativeTo }); + TemporalHelpers.assertDuration(result, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-string-zoneddatetime-wrong-offset.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-string-zoneddatetime-wrong-offset.js new file mode 100644 index 0000000000..41d37c8d54 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-string-zoneddatetime-wrong-offset.js @@ -0,0 +1,19 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Throws if a ZonedDateTime-like relativeTo string has the wrong UTC offset +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1, 0, 0, 0, 24); +const relativeTo = "2000-01-01T00:00+05:30[UTC]"; +assert.throws( + RangeError, + () => instance.round({ largestUnit: "years", relativeTo }), + "round should throw RangeError on a string with UTC offset mismatch" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-string-zoneddatetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-string-zoneddatetime.js new file mode 100644 index 0000000000..7765f9eeca --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-string-zoneddatetime.js @@ -0,0 +1,23 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: The relativeTo option accepts a ZonedDateTime-like ISO 8601 string +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + '2000-01-01[UTC]', + '2000-01-01T00:00[UTC]', + '2000-01-01T00:00+00:00[UTC]', + '2000-01-01T00:00+00:00[UTC][u-ca=iso8601]', +].forEach((relativeTo) => { + const duration = new Temporal.Duration(0, 0, 0, 31); + const result = duration.round({ largestUnit: "months", relativeTo }); + TemporalHelpers.assertDuration(result, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-sub-minute-offset.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-sub-minute-offset.js new file mode 100644 index 0000000000..0bc627a21a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-sub-minute-offset.js @@ -0,0 +1,25 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: relativeTo string accepts an inexact UTC offset rounded to hours and minutes +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1, 0, 0, 0, 24); + +let relativeTo = "2000-01-01T00:00+00:45[+00:44:30.123456789]"; +const result = instance.round({ largestUnit: "years", relativeTo }); +TemporalHelpers.assertDuration(result, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, "rounded HH:MM is accepted in string"); + +relativeTo = "2000-01-01T00:00+00:44:30[+00:44:30.123456789]"; +assert.throws(RangeError, () => instance.round({ largestUnit: "years", relativeTo }), "no other rounding is accepted for offset"); + +const timeZone = new Temporal.TimeZone("+00:44:30.123456789"); +relativeTo = { year: 2000, month: 1, day: 1, offset: "+00:45", timeZone }; +assert.throws(RangeError, () => instance.round({ largestUnit: "years", relativeTo }), "rounded HH:MM not accepted as offset in property bag"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-undefined-throw-on-calendar-units.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-undefined-throw-on-calendar-units.js new file mode 100644 index 0000000000..807c8e6965 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-undefined-throw-on-calendar-units.js @@ -0,0 +1,31 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: > + The relativeTo option is required when the Duration contains years, months, + or weeks, and largestUnit is days; or largestUnit is weeks or months +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +const oneYear = new Temporal.Duration(1); +const oneMonth = new Temporal.Duration(0, 1); +const oneWeek = new Temporal.Duration(0, 0, 1); +const oneDay = new Temporal.Duration(0, 0, 0, 1); + +const options = { largestUnit: "days" }; +TemporalHelpers.assertDuration(oneDay.round(options), 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, "days do not require relativeTo"); +assert.throws(RangeError, () => oneWeek.round(options), "balancing weeks to days requires relativeTo"); +assert.throws(RangeError, () => oneMonth.round(options), "balancing months to days requires relativeTo"); +assert.throws(RangeError, () => oneYear.round(options), "balancing years to days requires relativeTo"); + +["months", "weeks"].forEach((largestUnit) => { + [oneDay, oneWeek, oneMonth, oneYear].forEach((duration) => { + assert.throws(RangeError, () => duration.round({ largestUnit }), `balancing ${duration} to ${largestUnit} requires relativeTo`); + }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-wrong-type.js new file mode 100644 index 0000000000..fdf975ab64 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-wrong-type.js @@ -0,0 +1,42 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: > + Appropriate error thrown when relativeTo cannot be converted to a valid + relativeTo string or property bag +features: [BigInt, Symbol, Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.Duration(1, 0, 0, 0, 24); + +const rangeErrorTests = [ + [undefined, "undefined"], + [null, "null"], + [true, "boolean"], + ["", "empty string"], + [1, "number that doesn't convert to a valid ISO string"], + [1n, "bigint"], +]; + +for (const [relativeTo, description] of rangeErrorTests) { + assert.throws(RangeError, () => instance.round({ largestUnit: "years", relativeTo }), `${description} does not convert to a valid ISO string`); +} + +const typeErrorTests = [ + [Symbol(), "symbol"], + [{}, "plain object"], + [Temporal.PlainDate, "Temporal.PlainDate, object"], + [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"], + [Temporal.ZonedDateTime, "Temporal.ZonedDateTime, object"], + [Temporal.ZonedDateTime.prototype, "Temporal.ZonedDateTime.prototype, object"], +]; + +for (const [relativeTo, description] of typeErrorTests) { + assert.throws(TypeError, () => instance.round({ largestUnit: "years", relativeTo }), `${description} is not a valid property bag and does not convert to a string`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-convert.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-convert.js new file mode 100644 index 0000000000..9239651c7c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-convert.js @@ -0,0 +1,22 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: An exception from TimeZone#getOffsetNanosecondsFor() is propagated. +features: [Temporal] +---*/ + +class TZ extends Temporal.TimeZone { + constructor() { super("UTC") } + getOffsetNanosecondsFor() { throw new Test262Error() } +} + +const tz = new TZ(); +const arg = new Temporal.ZonedDateTime(0n, tz); +const instance = new Temporal.Duration(0, 0, 0, 365); + +assert.throws(Test262Error, () => instance.round({ relativeTo: arg, largestUnit: "years" })); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-nanoseconds-to-days-range-errors.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-nanoseconds-to-days-range-errors.js new file mode 100644 index 0000000000..3258c172f8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-nanoseconds-to-days-range-errors.js @@ -0,0 +1,104 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-temporal.duration.prototype.round +description: > + Called abstract operation NanosecondsToDays can throw three different RangeErrors when paired with a ZonedDateTime. +info: | + 6.5.7 NanosecondsToDays ( nanoseconds, relativeTo ) + 19. If days < 0 and sign = 1, throw a RangeError exception. + 20. If days > 0 and sign = -1, throw a RangeError exception. + ... + 22. If nanoseconds > 0 and sign = -1, throw a RangeError exception. +features: [Temporal, BigInt] +includes: [temporalHelpers.js] +---*/ + +const oneNsDuration = Temporal.Duration.from({ nanoseconds: 1 }); +const negOneNsDuration = Temporal.Duration.from({ nanoseconds: -1 }); +const dayNs = 86_400_000_000_000; +const epochInstant = new Temporal.Instant(0n); + +function timeZoneSubstituteValues( + getPossibleInstantsFor, + getOffsetNanosecondsFor +) { + const tz = new Temporal.TimeZone("UTC"); + TemporalHelpers.substituteMethod( + tz, + "getPossibleInstantsFor", + getPossibleInstantsFor + ); + TemporalHelpers.substituteMethod( + tz, + "getOffsetNanosecondsFor", + getOffsetNanosecondsFor + ); + return tz; +} + +// NanosecondsToDays.19: days < 0 and sign = 1 +let zdt = new Temporal.ZonedDateTime( + 0n, // Sets _startNs_ to 0 + timeZoneSubstituteValues( + [[epochInstant]], // Returned for NanosecondsToDays step 14, setting _intermediateNs_ + [ + 0, // Returned for RoundDuration step 6.c.i, setting _intermediate_ - making _startNs_ 0 in NanosecondsToDays + dayNs - 1, // Returned for NanosecondsToDays step 7, setting _startDateTime_ + -dayNs + 1, // Returned for NanosecondsToDays step 11, setting _endDateTime_ + ] + ) +); +assert.throws(RangeError, () => + // Using 1ns duration sets _nanoseconds_ to 1 and _sign_ to 1 + oneNsDuration.round({ + relativeTo: zdt, + smallestUnit: "days", + }) +); + +// NanosecondsToDays.20: days > 0 and sign = -1 +zdt = new Temporal.ZonedDateTime( + 0n, // Sets _startNs_ to 0 + timeZoneSubstituteValues( + [[epochInstant]], // Returned for NanosecondsToDays step 14, setting _intermediateNs_ + [ + 0, // Returned for RoundDuration step 6.c.i, setting _intermediate_ - making _startNs_ 0 in NanosecondsToDays + -dayNs + 1, // Returned for NanosecondsToDays step 7, setting _startDateTime_ + dayNs - 1, // Returned for NanosecondsToDays step 11, setting _endDateTime_ + ] + ) +); +assert.throws(RangeError, () => + // Using -1ns duration sets _nanoseconds_ to -1 and _sign_ to -1 + negOneNsDuration.round({ + relativeTo: zdt, + smallestUnit: "days", + }) +); + +// NanosecondsToDays.22: nanoseconds > 0 and sign = -1 +zdt = new Temporal.ZonedDateTime( + 0n, // Sets _startNs_ to 0 + timeZoneSubstituteValues( + [ + [new Temporal.Instant(-2n)], // Returned for NanosecondsToDays step 14, setting _intermediateNs_ + [new Temporal.Instant(-4n)], // Returned for NanosecondsToDays step 18.a, setting _oneDayFartherNs_ + ], + [ + 0, // Returned for RoundDuration step 6.c.i, setting _intermediate_ - making _startNs_ 0 in NanosecondsToDays + dayNs - 1, // Returned for NanosecondsToDays step 7, setting _startDateTime_ + -dayNs + 1, // Returned for NanosecondsToDays step 11, setting _endDateTime_ + ] + ) +); +assert.throws(RangeError, () => + // Using -1ns duration sets _nanoseocnds_ to -1 and _sign_ to -1 + negOneNsDuration.round({ + relativeTo: zdt, + smallestUnit: "days", + }) +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-negative-epochnanoseconds.js new file mode 100644 index 0000000000..8584938ea5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-negative-epochnanoseconds.js @@ -0,0 +1,27 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch +info: | + sec-temporal-getisopartsfromepoch step 1: + 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 10<sup>6</sup>. + sec-temporal-builtintimezonegetplaindatetimefor step 2: + 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]). +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +const relativeTo = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC"); +const duration = new Temporal.Duration(0, 0, 0, 1); + +// This code path shows up anywhere we convert an exact time, before the Unix +// epoch, with nonzero microseconds or nanoseconds, into a wall time; in this +// case via relativeTo. + +const result = duration.round({ relativeTo, largestUnit: "days" }); +TemporalHelpers.assertDuration(result, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-slots.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-slots.js new file mode 100644 index 0000000000..3bcd18f0f6 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-slots.js @@ -0,0 +1,40 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Getters are not called when converting a ZonedDateTime to a PlainDate. +includes: [compareArray.js] +features: [Temporal] +---*/ + +const actual = []; +const prototypeDescrs = Object.getOwnPropertyDescriptors(Temporal.ZonedDateTime.prototype); +const getters = ["year", "month", "monthCode", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond", "calendar"]; + +for (const property of getters) { + Object.defineProperty(Temporal.ZonedDateTime.prototype, property, { + get() { + actual.push(`get ${property}`); + const value = prototypeDescrs[property].get.call(this); + return { + toString() { + actual.push(`toString ${property}`); + return value.toString(); + }, + valueOf() { + actual.push(`valueOf ${property}`); + return value; + }, + }; + }, + }); +} + +const arg = new Temporal.ZonedDateTime(0n, "UTC"); +const instance = new Temporal.Duration(0, 0, 0, 365); +instance.round({ relativeTo: arg, largestUnit: "years" }); +assert.compareArray(actual, []); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..c963bc42fb --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,19 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => duration.round({ smallestUnit: "seconds", relativeTo: datetime })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..3b1b1188ca --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,23 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => duration.round({ smallestUnit: "seconds", relativeTo: datetime }), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..d647f74246 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,19 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => duration.round({ smallestUnit: "seconds", relativeTo: datetime })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..8136e44077 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,28 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => duration.round({ smallestUnit: "seconds", relativeTo: datetime })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/round-negative-result.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/round-negative-result.js new file mode 100644 index 0000000000..84616cd123 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/round-negative-result.js @@ -0,0 +1,26 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: A negative duration result is balanced correctly by the modulo operation in NanosecondsToDays +info: | + sec-temporal-nanosecondstodays step 6: + 6. If Type(_relativeTo_) is not Object or _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot, then + a. Return the new Record { ..., [[Nanoseconds]]\: abs(_nanoseconds_) modulo _dayLengthNs_ × _sign_, ... }. + sec-temporal-roundduration step 6: + 6. If _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then + ... + d. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _intermediate_). + sec-temporal.duration.prototype.round step 21: + 21. Let _roundResult_ be ? RoundDuration(_unbalanceResult_.[[Years]], _unbalanceResult_.[[Months]], _unbalanceResult_.[[Weeks]], _unbalanceResult_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_[[Seconds]], _duration_[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _relativeTo_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(0, 0, 0, 0, -60); +const result = duration.round({ smallestUnit: "days" }); +TemporalHelpers.assertDuration(result, 0, 0, 0, -3, 0, 0, 0, 0, 0, 0); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingincrement-nan.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingincrement-nan.js new file mode 100644 index 0000000000..7d19fa3768 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingincrement-nan.js @@ -0,0 +1,21 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: RangeError thrown when roundingIncrement option is NaN +info: | + sec-getoption step 8.b: + b. If _value_ is *NaN*, throw a *RangeError* exception. + sec-temporal-totemporalroundingincrement step 5: + 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1). + sec-temporal.duration.prototype.round step 18: + 18. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*). +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(0, 0, 0, 4, 12, 34, 56, 987, 654, 321); +assert.throws(RangeError, () => duration.round({ smallestUnit: 'second', roundingIncrement: NaN })); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingincrement-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingincrement-non-integer.js new file mode 100644 index 0000000000..f65e3451bb --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingincrement-non-integer.js @@ -0,0 +1,26 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Rounding for roundingIncrement option +info: | + sec-temporal-totemporalroundingincrement: + 3. Let _integerIncrement_ be truncate(ℝ(_increment_)). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1); +const options = { + smallestUnit: "years", + roundingMode: "expand", + relativeTo: new Temporal.PlainDate(2000, 1, 1), +}; +const result = instance.round({ ...options, roundingIncrement: 2.5 }); +TemporalHelpers.assertDuration(result, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, "roundingIncrement 2.5 truncates to 2"); +const result2 = instance.round({ ...options, roundingIncrement: 1e9 + 0.5 }); +TemporalHelpers.assertDuration(result2, 1e9, 0, 0, 0, 0, 0, 0, 0, 0, 0, "roundingIncrement 1e9 + 0.5 truncates to 1e9"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingincrement-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingincrement-out-of-range.js new file mode 100644 index 0000000000..cd51f23753 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingincrement-out-of-range.js @@ -0,0 +1,22 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: RangeError thrown when roundingIncrement option out of range +info: | + sec-temporal-totemporalroundingincrement: + 4. If _integerIncrement_ < 1 or _integerIncrement_ > 10<sup>9</sup>, throw a *RangeError* exception. +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1); +const options = { smallestUnit: "years", relativeTo: new Temporal.PlainDate(2000, 1, 1) }; +assert.throws(RangeError, () => instance.round({ ...options, roundingIncrement: -Infinity })); +assert.throws(RangeError, () => instance.round({ ...options, roundingIncrement: -1 })); +assert.throws(RangeError, () => instance.round({ ...options, roundingIncrement: 0 })); +assert.throws(RangeError, () => instance.round({ ...options, roundingIncrement: 1e9 + 1 })); +assert.throws(RangeError, () => instance.round({ ...options, roundingIncrement: Infinity })); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingincrement-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingincrement-undefined.js new file mode 100644 index 0000000000..b2e9cfeadb --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingincrement-undefined.js @@ -0,0 +1,27 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Fallback value for roundingIncrement option +info: | + sec-getoption step 3: + 3. If _value_ is *undefined*, return _fallback_. + sec-temporal-totemporalroundingincrement step 5: + 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1). + sec-temporal.duration.prototype.round step 18: + 18. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(0, 0, 0, 4, 12, 34, 56, 987, 654, 321); + +const explicit = duration.round({ smallestUnit: 'second', roundingIncrement: undefined }); +TemporalHelpers.assertDuration(explicit, 0, 0, 0, 4, 12, 34, 57, 0, 0, 0, "default roundingIncrement is 1"); + +const implicit = duration.round({ smallestUnit: 'second' }); +TemporalHelpers.assertDuration(implicit, 0, 0, 0, 4, 12, 34, 57, 0, 0, 0, "default roundingIncrement is 1"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingincrement-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingincrement-wrong-type.js new file mode 100644 index 0000000000..6da009e3c6 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingincrement-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.plaintime.prototype.round +description: Type conversions for roundingIncrement option +info: | + sec-getoption step 8.a: + a. Set _value_ to ? ToNumber(value). + sec-temporal-totemporalroundingincrement step 5: + 5. Let _increment_ be ? GetOption(_normalizedOptions_, *"roundingIncrement"*, « Number », *undefined*, 1). + sec-temporal.plaintime.prototype.round step 11: + 11. Let _roundingIncrement_ be ? ToTemporalRoundingIncrement(_options_, _maximum_, *false*). +includes: [temporalHelpers.js, compareArray.js] +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(0, 0, 0, 4, 12, 34, 56, 987, 654, 321); + +TemporalHelpers.checkRoundingIncrementOptionWrongType( + (roundingIncrement) => duration.round({ smallestUnit: 'second', roundingIncrement }), + (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 4, 12, 34, 57, 0, 0, 0, descr), + (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 4, 12, 34, 56, 0, 0, 0, descr), +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-ceil.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-ceil.js new file mode 100644 index 0000000000..74f62cd254 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-ceil.js @@ -0,0 +1,45 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Tests calculations with roundingMode "ceil". +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(5, 6, 7, 8, 40, 30, 20, 123, 987, 500); +const relativeTo = new Temporal.PlainDate(2020, 1, 1); + +const expected = [ + ["years", [6], [-5]], + ["months", [5, 8], [-5, -7]], + ["weeks", [5, 6, 9], [-5, -6, -8]], + ["days", [5, 6, 7, 10], [-5, -6, -7, -9]], + ["hours", [5, 6, 7, 9, 17], [-5, -6, -7, -9, -16]], + ["minutes", [5, 6, 7, 9, 16, 31], [-5, -6, -7, -9, -16, -30]], + ["seconds", [5, 6, 7, 9, 16, 30, 21], [-5, -6, -7, -9, -16, -30, -20]], + ["milliseconds", [5, 6, 7, 9, 16, 30, 20, 124], [-5, -6, -7, -9, -16, -30, -20, -123]], + ["microseconds", [5, 6, 7, 9, 16, 30, 20, 123, 988], [-5, -6, -7, -9, -16, -30, -20, -123, -987]], + ["nanoseconds", [5, 6, 7, 9, 16, 30, 20, 123, 987, 500], [-5, -6, -7, -9, -16, -30, -20, -123, -987, -500]], +]; + +const roundingMode = "ceil"; + +expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => { + const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive; + const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative; + TemporalHelpers.assertDuration( + instance.round({ smallestUnit, relativeTo, roundingMode }), + py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns, + `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)` + ); + TemporalHelpers.assertDuration( + instance.negated().round({ smallestUnit, relativeTo, roundingMode }), + ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns, + `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-expand.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-expand.js new file mode 100644 index 0000000000..7b5e9d3e37 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-expand.js @@ -0,0 +1,45 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Tests calculations with roundingMode "expand". +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(5, 6, 7, 8, 40, 30, 20, 123, 987, 500); +const relativeTo = new Temporal.PlainDate(2020, 1, 1); + +const expected = [ + ["years", [6], [-6]], + ["months", [5, 8], [-5, -8]], + ["weeks", [5, 6, 9], [-5, -6, -9]], + ["days", [5, 6, 7, 10], [-5, -6, -7, -10]], + ["hours", [5, 6, 7, 9, 17], [-5, -6, -7, -9, -17]], + ["minutes", [5, 6, 7, 9, 16, 31], [-5, -6, -7, -9, -16, -31]], + ["seconds", [5, 6, 7, 9, 16, 30, 21], [-5, -6, -7, -9, -16, -30, -21]], + ["milliseconds", [5, 6, 7, 9, 16, 30, 20, 124], [-5, -6, -7, -9, -16, -30, -20, -124]], + ["microseconds", [5, 6, 7, 9, 16, 30, 20, 123, 988], [-5, -6, -7, -9, -16, -30, -20, -123, -988]], + ["nanoseconds", [5, 6, 7, 9, 16, 30, 20, 123, 987, 500], [-5, -6, -7, -9, -16, -30, -20, -123, -987, -500]], +]; + +const roundingMode = "expand"; + +expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => { + const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive; + const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative; + TemporalHelpers.assertDuration( + instance.round({ smallestUnit, relativeTo, roundingMode }), + py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns, + `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)` + ); + TemporalHelpers.assertDuration( + instance.negated().round({ smallestUnit, relativeTo, roundingMode }), + ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns, + `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-floor.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-floor.js new file mode 100644 index 0000000000..240e0ccdde --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-floor.js @@ -0,0 +1,45 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Tests calculations with roundingMode "floor". +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(5, 6, 7, 8, 40, 30, 20, 123, 987, 500); +const relativeTo = new Temporal.PlainDate(2020, 1, 1); + +const expected = [ + ["years", [5], [-6]], + ["months", [5, 7], [-5, -8]], + ["weeks", [5, 6, 8], [-5, -6, -9]], + ["days", [5, 6, 7, 9], [-5, -6, -7, -10]], + ["hours", [5, 6, 7, 9, 16], [-5, -6, -7, -9, -17]], + ["minutes", [5, 6, 7, 9, 16, 30], [-5, -6, -7, -9, -16, -31]], + ["seconds", [5, 6, 7, 9, 16, 30, 20], [-5, -6, -7, -9, -16, -30, -21]], + ["milliseconds", [5, 6, 7, 9, 16, 30, 20, 123], [-5, -6, -7, -9, -16, -30, -20, -124]], + ["microseconds", [5, 6, 7, 9, 16, 30, 20, 123, 987], [-5, -6, -7, -9, -16, -30, -20, -123, -988]], + ["nanoseconds", [5, 6, 7, 9, 16, 30, 20, 123, 987, 500], [-5, -6, -7, -9, -16, -30, -20, -123, -987, -500]], +]; + +const roundingMode = "floor"; + +expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => { + const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive; + const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative; + TemporalHelpers.assertDuration( + instance.round({ smallestUnit, relativeTo, roundingMode }), + py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns, + `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)` + ); + TemporalHelpers.assertDuration( + instance.negated().round({ smallestUnit, relativeTo, roundingMode }), + ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns, + `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-halfCeil.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-halfCeil.js new file mode 100644 index 0000000000..13ea4c1a01 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-halfCeil.js @@ -0,0 +1,45 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Tests calculations with roundingMode "halfCeil". +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(5, 6, 7, 8, 40, 30, 20, 123, 987, 500); +const relativeTo = new Temporal.PlainDate(2020, 1, 1); + +const expected = [ + ["years", [6], [-6]], + ["months", [5, 8], [-5, -8]], + ["weeks", [5, 6, 8], [-5, -6, -8]], + ["days", [5, 6, 7, 10], [-5, -6, -7, -10]], + ["hours", [5, 6, 7, 9, 17], [-5, -6, -7, -9, -17]], + ["minutes", [5, 6, 7, 9, 16, 30], [-5, -6, -7, -9, -16, -30]], + ["seconds", [5, 6, 7, 9, 16, 30, 20], [-5, -6, -7, -9, -16, -30, -20]], + ["milliseconds", [5, 6, 7, 9, 16, 30, 20, 124], [-5, -6, -7, -9, -16, -30, -20, -124]], + ["microseconds", [5, 6, 7, 9, 16, 30, 20, 123, 988], [-5, -6, -7, -9, -16, -30, -20, -123, -987]], + ["nanoseconds", [5, 6, 7, 9, 16, 30, 20, 123, 987, 500], [-5, -6, -7, -9, -16, -30, -20, -123, -987, -500]], +]; + +const roundingMode = "halfCeil"; + +expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => { + const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive; + const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative; + TemporalHelpers.assertDuration( + instance.round({ smallestUnit, relativeTo, roundingMode }), + py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns, + `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)` + ); + TemporalHelpers.assertDuration( + instance.negated().round({ smallestUnit, relativeTo, roundingMode }), + ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns, + `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-halfEven.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-halfEven.js new file mode 100644 index 0000000000..8cdd34d0dc --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-halfEven.js @@ -0,0 +1,45 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Tests calculations with roundingMode "halfEven". +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(5, 6, 7, 8, 40, 30, 20, 123, 987, 500); +const relativeTo = new Temporal.PlainDate(2020, 1, 1); + +const expected = [ + ["years", [6], [-6]], + ["months", [5, 8], [-5, -8]], + ["weeks", [5, 6, 8], [-5, -6, -8]], + ["days", [5, 6, 7, 10], [-5, -6, -7, -10]], + ["hours", [5, 6, 7, 9, 17], [-5, -6, -7, -9, -17]], + ["minutes", [5, 6, 7, 9, 16, 30], [-5, -6, -7, -9, -16, -30]], + ["seconds", [5, 6, 7, 9, 16, 30, 20], [-5, -6, -7, -9, -16, -30, -20]], + ["milliseconds", [5, 6, 7, 9, 16, 30, 20, 124], [-5, -6, -7, -9, -16, -30, -20, -124]], + ["microseconds", [5, 6, 7, 9, 16, 30, 20, 123, 988], [-5, -6, -7, -9, -16, -30, -20, -123, -988]], + ["nanoseconds", [5, 6, 7, 9, 16, 30, 20, 123, 987, 500], [-5, -6, -7, -9, -16, -30, -20, -123, -987, -500]], +]; + +const roundingMode = "halfEven"; + +expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => { + const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive; + const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative; + TemporalHelpers.assertDuration( + instance.round({ smallestUnit, relativeTo, roundingMode }), + py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns, + `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)` + ); + TemporalHelpers.assertDuration( + instance.negated().round({ smallestUnit, relativeTo, roundingMode }), + ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns, + `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-halfExpand.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-halfExpand.js new file mode 100644 index 0000000000..905ea41280 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-halfExpand.js @@ -0,0 +1,45 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Tests calculations with roundingMode "halfExpand". +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(5, 6, 7, 8, 40, 30, 20, 123, 987, 500); +const relativeTo = new Temporal.PlainDate(2020, 1, 1); + +const expected = [ + ["years", [6], [-6]], + ["months", [5, 8], [-5, -8]], + ["weeks", [5, 6, 8], [-5, -6, -8]], + ["days", [5, 6, 7, 10], [-5, -6, -7, -10]], + ["hours", [5, 6, 7, 9, 17], [-5, -6, -7, -9, -17]], + ["minutes", [5, 6, 7, 9, 16, 30], [-5, -6, -7, -9, -16, -30]], + ["seconds", [5, 6, 7, 9, 16, 30, 20], [-5, -6, -7, -9, -16, -30, -20]], + ["milliseconds", [5, 6, 7, 9, 16, 30, 20, 124], [-5, -6, -7, -9, -16, -30, -20, -124]], + ["microseconds", [5, 6, 7, 9, 16, 30, 20, 123, 988], [-5, -6, -7, -9, -16, -30, -20, -123, -988]], + ["nanoseconds", [5, 6, 7, 9, 16, 30, 20, 123, 987, 500], [-5, -6, -7, -9, -16, -30, -20, -123, -987, -500]], +]; + +const roundingMode = "halfExpand"; + +expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => { + const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive; + const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative; + TemporalHelpers.assertDuration( + instance.round({ smallestUnit, relativeTo, roundingMode }), + py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns, + `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)` + ); + TemporalHelpers.assertDuration( + instance.negated().round({ smallestUnit, relativeTo, roundingMode }), + ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns, + `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-halfFloor.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-halfFloor.js new file mode 100644 index 0000000000..77490422d7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-halfFloor.js @@ -0,0 +1,45 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Tests calculations with roundingMode "halfFloor". +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(5, 6, 7, 8, 40, 30, 20, 123, 987, 500); +const relativeTo = new Temporal.PlainDate(2020, 1, 1); + +const expected = [ + ["years", [6], [-6]], + ["months", [5, 8], [-5, -8]], + ["weeks", [5, 6, 8], [-5, -6, -8]], + ["days", [5, 6, 7, 10], [-5, -6, -7, -10]], + ["hours", [5, 6, 7, 9, 17], [-5, -6, -7, -9, -17]], + ["minutes", [5, 6, 7, 9, 16, 30], [-5, -6, -7, -9, -16, -30]], + ["seconds", [5, 6, 7, 9, 16, 30, 20], [-5, -6, -7, -9, -16, -30, -20]], + ["milliseconds", [5, 6, 7, 9, 16, 30, 20, 124], [-5, -6, -7, -9, -16, -30, -20, -124]], + ["microseconds", [5, 6, 7, 9, 16, 30, 20, 123, 987], [-5, -6, -7, -9, -16, -30, -20, -123, -988]], + ["nanoseconds", [5, 6, 7, 9, 16, 30, 20, 123, 987, 500], [-5, -6, -7, -9, -16, -30, -20, -123, -987, -500]], +]; + +const roundingMode = "halfFloor"; + +expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => { + const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive; + const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative; + TemporalHelpers.assertDuration( + instance.round({ smallestUnit, relativeTo, roundingMode }), + py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns, + `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)` + ); + TemporalHelpers.assertDuration( + instance.negated().round({ smallestUnit, relativeTo, roundingMode }), + ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns, + `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-halfTrunc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-halfTrunc.js new file mode 100644 index 0000000000..3898c9e856 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-halfTrunc.js @@ -0,0 +1,45 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Tests calculations with roundingMode "halfTrunc". +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(5, 6, 7, 8, 40, 30, 20, 123, 987, 500); +const relativeTo = new Temporal.PlainDate(2020, 1, 1); + +const expected = [ + ["years", [6], [-6]], + ["months", [5, 8], [-5, -8]], + ["weeks", [5, 6, 8], [-5, -6, -8]], + ["days", [5, 6, 7, 10], [-5, -6, -7, -10]], + ["hours", [5, 6, 7, 9, 17], [-5, -6, -7, -9, -17]], + ["minutes", [5, 6, 7, 9, 16, 30], [-5, -6, -7, -9, -16, -30]], + ["seconds", [5, 6, 7, 9, 16, 30, 20], [-5, -6, -7, -9, -16, -30, -20]], + ["milliseconds", [5, 6, 7, 9, 16, 30, 20, 124], [-5, -6, -7, -9, -16, -30, -20, -124]], + ["microseconds", [5, 6, 7, 9, 16, 30, 20, 123, 987], [-5, -6, -7, -9, -16, -30, -20, -123, -987]], + ["nanoseconds", [5, 6, 7, 9, 16, 30, 20, 123, 987, 500], [-5, -6, -7, -9, -16, -30, -20, -123, -987, -500]], +]; + +const roundingMode = "halfTrunc"; + +expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => { + const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive; + const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative; + TemporalHelpers.assertDuration( + instance.round({ smallestUnit, relativeTo, roundingMode }), + py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns, + `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)` + ); + TemporalHelpers.assertDuration( + instance.negated().round({ smallestUnit, relativeTo, roundingMode }), + ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns, + `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-invalid-string.js new file mode 100644 index 0000000000..c192b3b669 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-invalid-string.js @@ -0,0 +1,16 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: RangeError thrown when roundingMode option not one of the allowed string values +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 987, 500); +for (const roundingMode of ["other string", "cile", "CEIL", "ce\u0131l", "auto", "halfexpand", "floor\0"]) { + assert.throws(RangeError, () => duration.round({ smallestUnit: "microsecond", roundingMode })); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-trunc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-trunc.js new file mode 100644 index 0000000000..186fd53971 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-trunc.js @@ -0,0 +1,45 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Tests calculations with roundingMode "trunc". +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(5, 6, 7, 8, 40, 30, 20, 123, 987, 500); +const relativeTo = new Temporal.PlainDate(2020, 1, 1); + +const expected = [ + ["years", [5], [-5]], + ["months", [5, 7], [-5, -7]], + ["weeks", [5, 6, 8], [-5, -6, -8]], + ["days", [5, 6, 7, 9], [-5, -6, -7, -9]], + ["hours", [5, 6, 7, 9, 16], [-5, -6, -7, -9, -16]], + ["minutes", [5, 6, 7, 9, 16, 30], [-5, -6, -7, -9, -16, -30]], + ["seconds", [5, 6, 7, 9, 16, 30, 20], [-5, -6, -7, -9, -16, -30, -20]], + ["milliseconds", [5, 6, 7, 9, 16, 30, 20, 123], [-5, -6, -7, -9, -16, -30, -20, -123]], + ["microseconds", [5, 6, 7, 9, 16, 30, 20, 123, 987], [-5, -6, -7, -9, -16, -30, -20, -123, -987]], + ["nanoseconds", [5, 6, 7, 9, 16, 30, 20, 123, 987, 500], [-5, -6, -7, -9, -16, -30, -20, -123, -987, -500]], +]; + +const roundingMode = "trunc"; + +expected.forEach(([smallestUnit, expectedPositive, expectedNegative]) => { + const [py, pm = 0, pw = 0, pd = 0, ph = 0, pmin = 0, ps = 0, pms = 0, pµs = 0, pns = 0] = expectedPositive; + const [ny, nm = 0, nw = 0, nd = 0, nh = 0, nmin = 0, ns = 0, nms = 0, nµs = 0, nns = 0] = expectedNegative; + TemporalHelpers.assertDuration( + instance.round({ smallestUnit, relativeTo, roundingMode }), + py, pm, pw, pd, ph, pmin, ps, pms, pµs, pns, + `rounds to ${smallestUnit} (roundingMode = ${roundingMode}, positive case)` + ); + TemporalHelpers.assertDuration( + instance.negated().round({ smallestUnit, relativeTo, roundingMode }), + ny, nm, nw, nd, nh, nmin, ns, nms, nµs, nns, + `rounds to ${smallestUnit} (rounding mode = ${roundingMode}, negative case)` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-undefined.js new file mode 100644 index 0000000000..de1a178305 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-undefined.js @@ -0,0 +1,29 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Fallback value for roundingMode option +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 987, 500); + +const explicit1 = duration.round({ smallestUnit: "microsecond", roundingMode: undefined }); +TemporalHelpers.assertDuration(explicit1, 0, 0, 0, 0, 12, 34, 56, 123, 988, 0, "default roundingMode is halfExpand"); +const implicit1 = duration.round({ smallestUnit: "microsecond" }); +TemporalHelpers.assertDuration(implicit1, 0, 0, 0, 0, 12, 34, 56, 123, 988, 0, "default roundingMode is halfExpand"); + +const explicit2 = duration.round({ smallestUnit: "millisecond", roundingMode: undefined }); +TemporalHelpers.assertDuration(explicit2, 0, 0, 0, 0, 12, 34, 56, 124, 0, 0, "default roundingMode is halfExpand"); +const implicit2 = duration.round({ smallestUnit: "millisecond" }); +TemporalHelpers.assertDuration(implicit2, 0, 0, 0, 0, 12, 34, 56, 124, 0, 0, "default roundingMode is halfExpand"); + +const explicit3 = duration.round({ smallestUnit: "second", roundingMode: undefined }); +TemporalHelpers.assertDuration(explicit3, 0, 0, 0, 0, 12, 34, 56, 0, 0, 0, "default roundingMode is halfExpand"); +const implicit3 = duration.round({ smallestUnit: "second" }); +TemporalHelpers.assertDuration(implicit3, 0, 0, 0, 0, 12, 34, 56, 0, 0, 0, "default roundingMode is halfExpand"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-wrong-type.js new file mode 100644 index 0000000000..621050693c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundingmode-wrong-type.js @@ -0,0 +1,18 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Type conversions for roundingMode option +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 987, 500); +TemporalHelpers.checkStringOptionWrongType("roundingMode", "halfExpand", + (roundingMode) => duration.round({ smallestUnit: "microsecond", roundingMode }), + (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 12, 34, 56, 123, 988, 0, descr), +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundto-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundto-invalid-string.js new file mode 100644 index 0000000000..cb08721254 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/roundto-invalid-string.js @@ -0,0 +1,30 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: RangeError thrown when smallestUnit option not one of the allowed string values +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 987, 500); +const badValues = [ + "era", + "eraYear", + "millisecond\0", + "mill\u0131second", + "SECOND", + "eras", + "eraYears", + "milliseconds\0", + "mill\u0131seconds", + "SECONDS", + "other string", +]; +for (const smallestUnit of badValues) { + assert.throws(RangeError, () => duration.round(smallestUnit), + `"${smallestUnit}" is not a valid value for smallest unit`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/shell.js new file mode 100644 index 0000000000..eda1477282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/shell.js @@ -0,0 +1,24 @@ +// GENERATED, DO NOT EDIT +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-invalid-string.js new file mode 100644 index 0000000000..6400b3546d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-invalid-string.js @@ -0,0 +1,30 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: RangeError thrown when smallestUnit option not one of the allowed string values +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 987, 500); +const badValues = [ + "era", + "eraYear", + "millisecond\0", + "mill\u0131second", + "SECOND", + "eras", + "eraYears", + "milliseconds\0", + "mill\u0131seconds", + "SECONDS", + "other string", +]; +for (const smallestUnit of badValues) { + assert.throws(RangeError, () => duration.round({ smallestUnit }), + `"${smallestUnit}" is not a valid value for smallest unit`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-plurals-accepted-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-plurals-accepted-string.js new file mode 100644 index 0000000000..e79d41dce3 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-plurals-accepted-string.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Plural units are accepted as well for the shorthand for the smallestUnit option +includes: [temporalHelpers.js] +features: [Temporal, arrow-function] +---*/ + +const duration = new Temporal.Duration(0, 0, 0, 4, 5, 6, 7, 987, 654, 321); +const validUnits = [ + "day", + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => duration.round(smallestUnit), validUnits); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-plurals-accepted.js new file mode 100644 index 0000000000..75b51076ba --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-plurals-accepted.js @@ -0,0 +1,28 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Plural units are accepted as well for the smallestUnit option +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); +const relativeTo = new Temporal.PlainDate(2000, 1, 1); +const validUnits = [ + "year", + "month", + "week", + "day", + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => duration.round({ smallestUnit, relativeTo }), validUnits); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-string-shorthand-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-string-shorthand-string.js new file mode 100644 index 0000000000..549e7f3548 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-string-shorthand-string.js @@ -0,0 +1,28 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: String as first argument is equivalent to options bag with smallestUnit option +includes: [temporalHelpers.js] +features: [Temporal, arrow-function] +---*/ + +const instance = new Temporal.Duration(0, 0, 0, 4, 5, 6, 7, 987, 654, 321); +const validUnits = [ + "day", + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +validUnits.forEach((smallestUnit) => { + const full = instance.round({ smallestUnit }); + const shorthand = instance.round(smallestUnit); + TemporalHelpers.assertDurationsEqual(shorthand, full, `"${smallestUnit}" as first argument to round is equivalent to options bag`); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-undefined.js new file mode 100644 index 0000000000..fb6bf56c38 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-undefined.js @@ -0,0 +1,18 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Fallback value for smallestUnit option +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(0, 0, 0, 0, 1, 2, 3, 123, 456, 789); +const explicit1 = duration.round({ largestUnit: "day", smallestUnit: undefined }); +TemporalHelpers.assertDuration(explicit1, 0, 0, 0, 0, 1, 2, 3, 123, 456, 789, "default smallestUnit is nanosecond"); +const implicit1 = duration.round({ largestUnit: "day" }); +TemporalHelpers.assertDuration(implicit1, 0, 0, 0, 0, 1, 2, 3, 123, 456, 789, "default smallestUnit is nanosecond"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-wrong-type.js new file mode 100644 index 0000000000..8bd13396de --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit-wrong-type.js @@ -0,0 +1,18 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Type conversions for smallestUnit option +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 987, 500); +TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond", + (smallestUnit) => duration.round({ smallestUnit }), + (result, descr) => TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 12, 34, 56, 123, 988, 0, descr), +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit.js new file mode 100644 index 0000000000..413237571e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/smallestunit.js @@ -0,0 +1,37 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: smallestUnit should be taken into account +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const d = Temporal.Duration.from({ + days: 1, + hours: 2, + minutes: 3, + seconds: 4, + milliseconds: 5, + microseconds: 6, + nanoseconds: 7 +}); +const tests = { + 'day': [0, 0, 0, 1, 0, 0, 0, 0, 0, 0], + 'hour': [0, 0, 0, 1, 2, 0, 0, 0, 0, 0], + 'minute': [0, 0, 0, 1, 2, 3, 0, 0, 0, 0], + 'second': [0, 0, 0, 1, 2, 3, 4, 0, 0, 0], + 'millisecond': [0, 0, 0, 1, 2, 3, 4, 5, 0, 0], + 'microsecond': [0, 0, 0, 1, 2, 3, 4, 5, 6, 0], + 'nanosecond': [0, 0, 0, 1, 2, 3, 4, 5, 6, 7], +}; +for (const [smallestUnit, expected] of Object.entries(tests)) { + TemporalHelpers.assertDuration(d.round(smallestUnit), ...expected, + `"${smallestUnit}" should work as argument`); + TemporalHelpers.assertDuration(d.round({ smallestUnit }), ...expected, + `"${smallestUnit}" should work in option bag`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/subclassing-ignored.js new file mode 100644 index 0000000000..447feb06f2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/subclassing-ignored.js @@ -0,0 +1,20 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Objects of a subclass are never created as return values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.checkSubclassingIgnored( + Temporal.Duration, + [0, 0, 0, 4, 5, 6, 7, 987, 654, 321], + "round", + [{ smallestUnit: 'seconds' }], + (result) => TemporalHelpers.assertDuration(result, 0, 0, 0, 4, 5, 6, 8, 0, 0, 0), +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/throws-in-balance-duration-when-sign-mismatched-with-zoned-date-time.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/throws-in-balance-duration-when-sign-mismatched-with-zoned-date-time.js new file mode 100644 index 0000000000..e627793e50 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/throws-in-balance-duration-when-sign-mismatched-with-zoned-date-time.js @@ -0,0 +1,51 @@ +// |reftest| skip -- Temporal is not supported +// 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.round +description: > + BalanceDuration throws when the duration signs don't match. +info: | + BalanceDuration ( days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds, + largestUnit [ , relativeTo ] ) + + ... + 4. If largestUnit is one of "year", "month", "week", or "day", then + a. Let result be ? NanosecondsToDays(nanoseconds, relativeTo). + b. Set days to result.[[Days]]. + c. Set nanoseconds to result.[[Nanoseconds]]. + ... + 15. Return ? CreateTimeDurationRecord(days, hours × sign, minutes × sign, seconds × sign, + milliseconds × sign, microseconds × sign, nanoseconds × sign). +features: [Temporal] +---*/ + +let duration = Temporal.Duration.from({ + hours: -(24 * 1), + nanoseconds: -1, +}); + +let tz = new class extends Temporal.TimeZone { + #getPossibleInstantsFor = 0; + + getPossibleInstantsFor(dt) { + this.#getPossibleInstantsFor++; + + if (this.#getPossibleInstantsFor === 1) { + return [new Temporal.Instant(-86400_000_000_000n - 2n)] + } + return super.getPossibleInstantsFor(dt); + } +}("UTC"); + +let zdt = new Temporal.ZonedDateTime(0n, tz, "iso8601"); + +let options = { + relativeTo: zdt, + largestUnit: "days", +}; + +assert.throws(RangeError, () => duration.round(options)); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/throws-in-unbalance-duration-relative-when-sign-mismatched.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/throws-in-unbalance-duration-relative-when-sign-mismatched.js new file mode 100644 index 0000000000..8f7d5ddc5c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/throws-in-unbalance-duration-relative-when-sign-mismatched.js @@ -0,0 +1,36 @@ +// |reftest| skip -- Temporal is not supported +// 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.round +description: > + UnbalanceDurationRelative throws a RangeError when duration signs don't match. +features: [Temporal] +---*/ + +var duration = Temporal.Duration.from({ + years: 1, + months: 0, + weeks: 1, +}); + +var cal = new class extends Temporal.Calendar { + dateUntil(one, two, options) { + var result = super.dateUntil(one, two, options); + return result.negated(); + } +}("iso8601"); + +var relativeTo = new Temporal.PlainDateTime(1970, 1, 1, 0, 0, 0, 0, 0, 0, cal); +assert.sameValue(relativeTo.calendar, cal); + +var options = { + smallestUnit: "days", + largestUnit: "month", + relativeTo, +}; + +assert.throws(RangeError, () => duration.round(options)); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/timezone-getpossibleinstantsfor-iterable.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/timezone-getpossibleinstantsfor-iterable.js new file mode 100644 index 0000000000..228c934c00 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/timezone-getpossibleinstantsfor-iterable.js @@ -0,0 +1,48 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call +info: | + sec-temporal.duration.prototype.round steps 19, 21, and 24: + 19. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_). + 21. Let _roundResult_ be ? RoundDuration(_unbalanceResult_.[[Years]], [...], _unbalanceResult_.[[Days]], _duration_.[[Hours]], [...], _duration_.[[Nanoseconds]], _roundingIncrement_, _smallestUnit_, _roundingMode_, _relativeTo_). + 24. If _relativeTo_ has an [[InitializedTemporalZonedDateTime]] internal slot, then + a. Set _relativeTo_ to ? MoveRelativeZonedDateTime(_relativeTo_, _balanceResult_.[[Years]], _balanceResult_.[[Months]], _balanceResult_.[[Weeks]], 0). + sec-temporal-torelativetemporalobject step 6.d: + d. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_result_.[[Year]], [...], _result_.[[Nanosecond]], _offsetNs_, _timeZone_, *"compatible"*, *"reject"*). + sec-temporal-interpretisodatetimeoffset step 7: + 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_). + sec-temporal-roundduration step 5.c–d: + c. If _zonedRelativeTo_ is not *undefined*, then + i. Let _intermediate_ be ? MoveRelativeZonedDateTime(_zonedRelativeTo_, _years_, _months_, _weeks_, _days_). + d. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _intermediate_). + sec-temporal-moverelativezoneddatetime step 1: + 1. Let _intermediateNs_ be ? AddZonedDateTime(_zonedDateTime_.[[Nanoseconds]], _zonedDateTime_.[[TimeZone]], _zonedDateTime_.[[Calendar]], _years_, _months_, _weeks_, _days_, 0, 0, 0, 0, 0, 0). + sec-temporal-nanosecondstodays step 13: + 13. Let _intermediateNs_ be ? AddZonedDateTime(_startNs_, _relativeTo_.[[TimeZone]], _relativeTo_.[[Calendar]], 0, 0, 0, _days_, 0, 0, 0, 0, 0, 0). + sec-temporal-addzoneddatetime step 8: + 8. Let _intermediateInstant_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _intermediateDateTime_, *"compatible"*). + sec-temporal-builtintimezonegetinstantfor step 1: + 1. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_). + sec-temporal-getpossibleinstantsfor step 2: + 2. Let _list_ be ? IterableToList(_possibleInstants_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "2000-01-01T00:00:00", // called once on the input relativeTo object + "2001-02-09T00:00:00", // called once on relativeTo plus years, months, weeks, days from the receiver + "2001-02-10T00:00:00", // called once on the previous value plus the calendar days difference between that and the time part of the duration + "2001-02-01T00:00:00", // called once on relativeTo plus the years, months, and weeks part of the balance result +]; + +TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => { + const duration = new Temporal.Duration(1, 1, 1, 1, 1, 1, 1); + duration.round({ smallestUnit: 'months', relativeTo: { year: 2000, month: 1, day: 1, timeZone } }); +}, expected); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/total-duration-nanoseconds-too-large-with-zoned-datetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/total-duration-nanoseconds-too-large-with-zoned-datetime.js new file mode 100644 index 0000000000..a7e20541cd --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/total-duration-nanoseconds-too-large-with-zoned-datetime.js @@ -0,0 +1,26 @@ +// |reftest| skip -- Temporal is not supported +// 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.round +description: > + NanosecondsToDays throws a RangeError when the number of nanoseconds is too large. +features: [Temporal] +---*/ + +var duration = Temporal.Duration.from({ + nanoseconds: Number.MAX_VALUE, +}); + +var zonedDateTime = new Temporal.ZonedDateTime(0n, "UTC"); + +var options = { + smallestUnit: "day", + largestUnit: "day", + relativeTo: zonedDateTime, +}; + +assert.throws(RangeError, () => duration.round(options)); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/year-zero.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/year-zero.js new file mode 100644 index 0000000000..7464d539ca --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/round/year-zero.js @@ -0,0 +1,20 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.round +description: Negative zero, as an extended year, is rejected +features: [Temporal, arrow-function] +---*/ + +const instance = new Temporal.Duration(1, 0, 0, 0, 24); + +let relativeTo = "-000000-11-04T00:00"; +assert.throws( + RangeError, + () => { instance.round({ largestUnit: "years", relativeTo }); }, + "reject minus zero as extended year" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/seconds/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/seconds/branding.js new file mode 100644 index 0000000000..65f0d147db --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/seconds/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.duration.prototype.seconds +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const seconds = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "seconds").get; + +assert.sameValue(typeof seconds, "function"); + +assert.throws(TypeError, () => seconds.call(undefined), "undefined"); +assert.throws(TypeError, () => seconds.call(null), "null"); +assert.throws(TypeError, () => seconds.call(true), "true"); +assert.throws(TypeError, () => seconds.call(""), "empty string"); +assert.throws(TypeError, () => seconds.call(Symbol()), "symbol"); +assert.throws(TypeError, () => seconds.call(1), "1"); +assert.throws(TypeError, () => seconds.call({}), "plain object"); +assert.throws(TypeError, () => seconds.call(Temporal.Duration), "Temporal.Duration"); +assert.throws(TypeError, () => seconds.call(Temporal.Duration.prototype), "Temporal.Duration.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/seconds/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/seconds/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/seconds/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/seconds/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/seconds/prop-desc.js new file mode 100644 index 0000000000..d8f4d9a498 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/seconds/prop-desc.js @@ -0,0 +1,17 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.duration.prototype.seconds +description: The "seconds" property of Temporal.Duration.prototype +features: [Temporal] +---*/ + +const descriptor = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "seconds"); +assert.sameValue(typeof descriptor.get, "function"); +assert.sameValue(descriptor.set, undefined); +assert.sameValue(descriptor.enumerable, false); +assert.sameValue(descriptor.configurable, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/seconds/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/seconds/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/seconds/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/sign/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/sign/branding.js new file mode 100644 index 0000000000..c9e7990629 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/sign/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.duration.prototype.sign +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const sign = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "sign").get; + +assert.sameValue(typeof sign, "function"); + +assert.throws(TypeError, () => sign.call(undefined), "undefined"); +assert.throws(TypeError, () => sign.call(null), "null"); +assert.throws(TypeError, () => sign.call(true), "true"); +assert.throws(TypeError, () => sign.call(""), "empty string"); +assert.throws(TypeError, () => sign.call(Symbol()), "symbol"); +assert.throws(TypeError, () => sign.call(1), "1"); +assert.throws(TypeError, () => sign.call({}), "plain object"); +assert.throws(TypeError, () => sign.call(Temporal.Duration), "Temporal.Duration"); +assert.throws(TypeError, () => sign.call(Temporal.Duration.prototype), "Temporal.Duration.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/sign/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/sign/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/sign/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/sign/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/sign/prop-desc.js new file mode 100644 index 0000000000..e118147d3e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/sign/prop-desc.js @@ -0,0 +1,17 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.duration.prototype.sign +description: The "sign" property of Temporal.Duration.prototype +features: [Temporal] +---*/ + +const descriptor = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "sign"); +assert.sameValue(typeof descriptor.get, "function"); +assert.sameValue(descriptor.set, undefined); +assert.sameValue(descriptor.enumerable, false); +assert.sameValue(descriptor.configurable, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/sign/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/sign/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/sign/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-invalid-property.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-invalid-property.js new file mode 100644 index 0000000000..56686ae5a0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-invalid-property.js @@ -0,0 +1,31 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: temporalDurationLike object must contain at least one correctly spelled property +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321); + +assert.throws( + TypeError, + () => instance.subtract({}), + "Throws TypeError if no property is present" +); + +assert.throws( + TypeError, + () => instance.subtract({ nonsense: true }), + "Throws TypeError if no recognized property is present" +); + +assert.throws( + TypeError, + () => instance.subtract({ sign: 1 }), + "Sign property is not recognized" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-mixed-sign.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-mixed-sign.js new file mode 100644 index 0000000000..918def5e87 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-mixed-sign.js @@ -0,0 +1,19 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: Positive and negative values in the temporalDurationLike argument are not acceptable +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321); + +assert.throws( + RangeError, + () => instance.subtract({ hours: 1, minutes: -30 }), + `mixed positive and negative values always throw` +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-not-object.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-not-object.js new file mode 100644 index 0000000000..19777965fb --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-not-object.js @@ -0,0 +1,22 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: Passing a primitive other than string to subtract() throws +features: [Symbol, Temporal] +---*/ + +const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321); +assert.throws(RangeError, () => instance.subtract(undefined), "undefined"); +assert.throws(RangeError, () => instance.subtract(null), "null"); +assert.throws(RangeError, () => instance.subtract(true), "boolean"); +assert.throws(RangeError, () => instance.subtract(""), "empty string"); +assert.throws(TypeError, () => instance.subtract(Symbol()), "Symbol"); +assert.throws(RangeError, () => instance.subtract(7), "number"); +assert.throws(RangeError, () => instance.subtract(7n), "bigint"); +assert.throws(TypeError, () => instance.subtract([]), "array"); +assert.throws(TypeError, () => instance.subtract(() => {}), "function"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-singular-properties.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-singular-properties.js new file mode 100644 index 0000000000..1ef63dd757 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-singular-properties.js @@ -0,0 +1,30 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: Singular properties in the property bag are always ignored +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321); + +[ + { year: 1 }, + { month: 2 }, + { week: 3 }, + { day: 4 }, + { hour: 5 }, + { minute: 6 }, + { second: 7 }, + { millisecond: 8 }, + { microsecond: 9 }, + { nanosecond: 10 }, +].forEach((badObject) => { + assert.throws(TypeError, () => instance.subtract(badObject), + "Throw TypeError if temporalDurationLike is not valid"); +}); + + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-string-fractional-units-rounding-mode.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-string-fractional-units-rounding-mode.js new file mode 100644 index 0000000000..c296d5089c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-string-fractional-units-rounding-mode.js @@ -0,0 +1,19 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: Strings with fractional duration units are rounded with the correct rounding mode +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const blank = new Temporal.Duration(); + +TemporalHelpers.assertDuration(blank.subtract("PT1.03125H"), 0, 0, 0, 0, -1, -1, -52, -500, 0, 0, + "positive fractional units rounded with correct rounding mode"); +TemporalHelpers.assertDuration(blank.subtract("-PT1.03125H"), 0, 0, 0, 0, 1, 1, 52, 500, 0, 0, + "negative fractional units rounded with correct rounding mode"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-string-negative-fractional-units.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-string-negative-fractional-units.js new file mode 100644 index 0000000000..713c5ba2a5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-string-negative-fractional-units.js @@ -0,0 +1,20 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: Strings with fractional duration units are treated with the correct sign +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(); + +const resultHours = instance.subtract("-PT24.567890123H"); +TemporalHelpers.assertDuration(resultHours, 0, 0, 0, 0, 24, 34, 4, 404, 442, 800, "negative fractional hours"); + +const resultMinutes = instance.subtract("-PT1440.567890123M"); +TemporalHelpers.assertDuration(resultMinutes, 0, 0, 0, 0, 0, 1440, 34, 73, 407, 380, "negative fractional minutes"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-string.js new file mode 100644 index 0000000000..4d0037afbb --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/argument-string.js @@ -0,0 +1,17 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: String arguments are supported. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const duration = Temporal.Duration.from({ days: 3, hours: 1, minutes: 10 }); +const result = duration.subtract('P1DT5M'); +TemporalHelpers.assertDuration(result, 0, 0, 0, 2, 1, 5, 0, 0, 0, 0, "String argument should be supported"); +assert.throws(RangeError, () => duration.subtract("2DT5M"), "Invalid string argument should throw"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/balance-negative-result.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/balance-negative-result.js new file mode 100644 index 0000000000..b937c08485 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/balance-negative-result.js @@ -0,0 +1,39 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: A negative duration result is balanced correctly by the modulo operation in NanosecondsToDays +info: | + sec-temporal-nanosecondstodays step 6: + 6. If Type(_relativeTo_) is not Object or _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot, then + a. Return the new Record { ..., [[Nanoseconds]]: abs(_nanoseconds_) modulo _dayLengthNs_ × _sign_, ... }. + sec-temporal-balanceduration step 4: + 4. If _largestUnit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then + a. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _relativeTo_). + sec-temporal-addduration steps 5–6: + 5. If _relativeTo_ is *undefined*, then + ... + b. Let _result_ be ! BalanceDuration(_d1_ + _d2_, _h1_ + _h2_, _min1_ + _min2_, _s1_ + _s2_, _ms1_ + _ms2_, _mus1_ + _mus2_, _ns1_ + _ns2_, _largestUnit_). + ... + 6. Else if _relativeTo_ has an [[InitializedTemporalPlainDateTime]] internal slot, then + ... + n. Let _result_ be ! BalanceDuration(_dateDifference_.[[Days]], _h1_ + _h2_, _min1_ + _min2_, _s1_ + _s2_, _ms1_ + _ms2_, _mus1_ + _mus2_, _ns1_ + _ns2_, _largestUnit_). + sec-temporal.duration.prototype.subtract step 6: + 6. Let _result_ be ? AddDuration(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], −_other_.[[Years]], −_other_.[[Months]], −_other_.[[Weeks]], −_other_.[[Days]], −_other_.[[Hours]], −_other_.[[Minutes]], −_other_.[[Seconds]], −_other_.[[Milliseconds]], −_other_.[[Microseconds]], −_other_.[[Nanoseconds]], _relativeTo_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const duration1 = new Temporal.Duration(0, 0, 0, 0, -60); +const duration2 = new Temporal.Duration(0, 0, 0, -1); + +const resultNotRelative = duration1.subtract(duration2); +TemporalHelpers.assertDuration(resultNotRelative, 0, 0, 0, -1, -12, 0, 0, 0, 0, 0); + +const relativeTo = new Temporal.PlainDateTime(2000, 1, 1); +const resultRelative = duration1.subtract(duration2, { relativeTo }); +TemporalHelpers.assertDuration(resultRelative, 0, 0, 0, -1, -12, 0, 0, 0, 0, 0); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/balance-negative-time-units.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/balance-negative-time-units.js new file mode 100644 index 0000000000..7cd7ad8412 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/balance-negative-time-units.js @@ -0,0 +1,63 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: Negative time fields in relativeTo are balanced upwards +info: | + sec-temporal-balancetime steps 3–14: + 3. Set _microsecond_ to _microsecond_ + floor(_nanosecond_ / 1000). + 4. Set _nanosecond_ to _nanosecond_ modulo 1000. + 5. Set _millisecond_ to _millisecond_ + floor(_microsecond_ / 1000). + 6. Set _microsecond_ to _microsecond_ modulo 1000. + 7. Set _second_ to _second_ + floor(_millisecond_ / 1000). + 8. Set _millisecond_ to _millisecond_ modulo 1000. + 9. Set _minute_ to _minute_ + floor(_second_ / 60). + 10. Set _second_ to _second_ modulo 60. + 11. Set _hour_ to _hour_ + floor(_minute_ / 60). + 12. Set _minute_ to _minute_ modulo 60. + 13. Let _days_ be floor(_hour_ / 24). + 14. Set _hour_ to _hour_ modulo 24. + sec-temporal-differencetime step 8: + 8. Let _bt_ be ? BalanceTime(_hours_, _minutes_, _seconds_, _milliseconds_, _microseconds_, _nanoseconds_). + sec-temporal-differenceisodatetime step 2: + 2. Let _timeDifference_ be ? DifferenceTime(_h1_, _min1_, _s1_, _ms1_, _mus1_, _ns1_, _h2_, _min2_, _s2_, _ms2_, _mus2_, _ns2_). + sec-temporal-differencezoneddatetime step 7: + 7. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _calendar_, _largestUnit_, _options_). + sec-temporal-addduration step 7.g.i: + i. Let _result_ be ? DifferenceZonedDateTime(_relativeTo_.[[Nanoseconds]], _endNs_, _timeZone_, _calendar_, _largestUnit_). + sec-temporal.duration.prototype.subtract step 6: + 6. Let _result_ be ? AddDuration(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], −_other_.[[Years]], −_other_.[[Months]], −_other_.[[Weeks]], −_other_.[[Days]], −_other_.[[Hours]], −_other_.[[Minutes]], −_other_.[[Seconds]], −_other_.[[Milliseconds]], −_other_.[[Microseconds]], −_other_.[[Nanoseconds]], _relativeTo_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(0, 0, 0, 0, 1, 1, 1, 1, 1, 1); + +const timeZone = new Temporal.TimeZone("UTC"); +const relativeTo = new Temporal.ZonedDateTime(830998861_000_000_000n, timeZone); +// This code path is encountered if largestUnit is years, months, weeks, or days +// and relativeTo is a ZonedDateTime +const options = { largestUnit: "days", relativeTo }; + +const result1 = duration.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 2), options); +TemporalHelpers.assertDuration(result1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 999, "nanoseconds balance"); + +const result2 = duration.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 2), options); +TemporalHelpers.assertDuration(result2, 0, 0, 0, 0, 1, 1, 1, 0, 999, 1, "microseconds balance"); + +const result3 = duration.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 2), options); +TemporalHelpers.assertDuration(result3, 0, 0, 0, 0, 1, 1, 0, 999, 1, 1, "milliseconds balance"); + +const result4 = duration.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 0, 2), options); +TemporalHelpers.assertDuration(result4, 0, 0, 0, 0, 1, 0, 59, 1, 1, 1, "seconds balance"); + +const result5 = duration.subtract(new Temporal.Duration(0, 0, 0, 0, 0, 2), options); +TemporalHelpers.assertDuration(result5, 0, 0, 0, 0, 0, 59, 1, 1, 1, 1, "minutes balance"); + +// This one is different because hours are later balanced again in BalanceDuration +const result6 = duration.subtract(new Temporal.Duration(0, 0, 0, 0, 2), options); +TemporalHelpers.assertDuration(result6, 0, 0, 0, 0, 0, -58, -58, -998, -998, -999, "hours balance"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/basic.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/basic.js new file mode 100644 index 0000000000..4cdd1a3f6e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/basic.js @@ -0,0 +1,69 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: Basic behavior +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const duration = Temporal.Duration.from({ days: 3, hours: 1, minutes: 10 }); +TemporalHelpers.assertDuration(duration.subtract({ days: 1, minutes: 5 }), + 0, 0, 0, 2, 1, 5, 0, 0, 0, 0); +TemporalHelpers.assertDuration(duration.subtract(duration), + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); +TemporalHelpers.assertDuration(duration.subtract({ days: 3 }), + 0, 0, 0, 0, 1, 10, 0, 0, 0, 0); +TemporalHelpers.assertDuration(duration.subtract({ minutes: 10 }), + 0, 0, 0, 3, 1, 0, 0, 0, 0, 0); +TemporalHelpers.assertDuration(duration.subtract({ minutes: 15 }), + 0, 0, 0, 3, 0, 55, 0, 0, 0, 0); +TemporalHelpers.assertDuration(duration.subtract({ seconds: 30 }), + 0, 0, 0, 3, 1, 9, 30, 0, 0, 0); +TemporalHelpers.assertDuration(Temporal.Duration.from('P2DT1H5M').subtract({ days: -1, minutes: -5 }), + 0, 0, 0, 3, 1, 10, 0, 0, 0, 0); +TemporalHelpers.assertDuration(new Temporal.Duration().subtract({ days: -3, hours: -1, minutes: -10 }), + 0, 0, 0, 3, 1, 10, 0, 0, 0, 0); +TemporalHelpers.assertDuration(Temporal.Duration.from('PT1H10M').subtract({ days: -3 }), + 0, 0, 0, 3, 1, 10, 0, 0, 0, 0); +TemporalHelpers.assertDuration(Temporal.Duration.from('P3DT1H').subtract({ minutes: -10 }), + 0, 0, 0, 3, 1, 10, 0, 0, 0, 0); +TemporalHelpers.assertDuration(Temporal.Duration.from('P3DT55M').subtract({ minutes: -15 }), + 0, 0, 0, 3, 1, 10, 0, 0, 0, 0); +TemporalHelpers.assertDuration(Temporal.Duration.from('P3DT1H9M30S').subtract({ seconds: -30 }), + 0, 0, 0, 3, 1, 10, 0, 0, 0, 0); +const d = Temporal.Duration.from({ + minutes: 100, + seconds: 100, + milliseconds: 2000, + microseconds: 2000, + nanoseconds: 2000 +}); +const less = Temporal.Duration.from({ + minutes: 10, + seconds: 10, + milliseconds: 500, + microseconds: 500, + nanoseconds: 500 +}); +TemporalHelpers.assertDuration(d.subtract(less), + 0, 0, 0, 0, 0, 91, 31, 501, 501, 500); +const tenDays = Temporal.Duration.from('P10D'); +const tenMinutes = Temporal.Duration.from('PT10M'); +TemporalHelpers.assertDuration(tenDays.subtract({ days: 15 }), + 0, 0, 0, -5, 0, 0, 0, 0, 0, 0); +TemporalHelpers.assertDuration(tenMinutes.subtract({ minutes: 15 }), + 0, 0, 0, 0, 0, -5, 0, 0, 0, 0); +const d1 = Temporal.Duration.from({ hours: 1, seconds: 60 }); +TemporalHelpers.assertDuration(d1.subtract({ minutes: 122 }), + 0, 0, 0, 0, -1, -1, 0, 0, 0, 0); +const d2 = Temporal.Duration.from({ hours: 1, seconds: 3721 }); +TemporalHelpers.assertDuration(d2.subtract({ minutes: 61, nanoseconds: 3722000000001 }), + 0, 0, 0, 0, 0, -1, -1, 0, 0, -1); +TemporalHelpers.assertDuration(duration.subtract({ month: 1, days: 1 }), + 0, 0, 0, 2, 1, 10, 0, 0, 0, 0, + "incorrectly-spelled properties are ignored"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/branding.js new file mode 100644 index 0000000000..0f6eeb7e7f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/branding.js @@ -0,0 +1,27 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const subtract = Temporal.Duration.prototype.subtract; + +assert.sameValue(typeof subtract, "function"); + +const args = [new Temporal.Duration(0, 0, 0, 0, 5)]; + +assert.throws(TypeError, () => subtract.apply(undefined, args), "undefined"); +assert.throws(TypeError, () => subtract.apply(null, args), "null"); +assert.throws(TypeError, () => subtract.apply(true, args), "true"); +assert.throws(TypeError, () => subtract.apply("", args), "empty string"); +assert.throws(TypeError, () => subtract.apply(Symbol(), args), "symbol"); +assert.throws(TypeError, () => subtract.apply(1, args), "1"); +assert.throws(TypeError, () => subtract.apply({}, args), "plain object"); +assert.throws(TypeError, () => subtract.apply(Temporal.Duration, args), "Temporal.Duration"); +assert.throws(TypeError, () => subtract.apply(Temporal.Duration.prototype, args), "Temporal.Duration.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/builtin.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/builtin.js new file mode 100644 index 0000000000..d70030af3e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/builtin.js @@ -0,0 +1,36 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: > + Tests that Temporal.Duration.prototype.subtract + meets the requirements for built-in objects defined by the + introduction of chapter 17 of the ECMAScript Language Specification. +info: | + Built-in functions that are not constructors do not have a "prototype" property unless + otherwise specified in the description of a particular function. + + Unless specified otherwise, a built-in object that is callable as a function is a built-in + function object with the characteristics described in 10.3. Unless specified otherwise, the + [[Extensible]] internal slot of a built-in object initially has the value true. + + Unless otherwise specified every built-in function and every built-in constructor has the + Function prototype object [...] as the value of its [[Prototype]] internal slot. +features: [Temporal] +---*/ + +assert.sameValue(Object.isExtensible(Temporal.Duration.prototype.subtract), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.Duration.prototype.subtract), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.Duration.prototype.subtract), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.Duration.prototype.subtract.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-dateadd-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-dateadd-called-with-options-undefined.js new file mode 100644 index 0000000000..4d4f254729 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-dateadd-called-with-options-undefined.js @@ -0,0 +1,20 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: > + BuiltinTimeZoneGetInstantFor calls Calendar.dateAdd with undefined as the + options value +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarDateAddUndefinedOptions(); +const timeZone = TemporalHelpers.oneShiftTimeZone(new Temporal.Instant(0n), 3600e9); +const instance = new Temporal.Duration(1, 1, 1, 1); +instance.subtract(new Temporal.Duration(-1, -1, -1, -1), { relativeTo: new Temporal.ZonedDateTime(0n, timeZone, calendar) }); +assert.sameValue(calendar.dateAddCallCount, 5); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-dateadd-called-with-plaindate-instance.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-dateadd-called-with-plaindate-instance.js new file mode 100644 index 0000000000..0bb1cc47c5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-dateadd-called-with-plaindate-instance.js @@ -0,0 +1,21 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: > + relativeTo parameters that are not ZonedDateTime or undefined, are always + converted to PlainDate for observable calendar calls +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarDateAddPlainDateInstance(); +const instance = new Temporal.Duration(1, 1, 1, 1); +const relativeTo = new Temporal.PlainDate(2000, 1, 1, calendar); +calendar.specificPlainDate = relativeTo; +instance.subtract(new Temporal.Duration(-1, -1, -1, -1), { relativeTo }); +assert(calendar.dateAddCallCount > 0, "assertions in calendar.dateAdd() should have been tested"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-dateadd.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-dateadd.js new file mode 100644 index 0000000000..b3282b4101 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-dateadd.js @@ -0,0 +1,43 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: Duration.prototype.subtract should call dateAdd with the appropriate values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +let calls = 0; +const expected = [ + { + plainDate: [1920, 5, "M05", 3], + duration: [2, 0, 0, 4, 0, 0, 0, 0, 0, 0], + }, + { + plainDate: [1922, 5, "M05", 7], + duration: [0, -10, 0, 0, 0, 0, 0, 0, 0, 0], + }, +]; +class CustomCalendar extends Temporal.Calendar { + constructor() { + super("iso8601"); + } + dateAdd(plainDate, duration, options) { + TemporalHelpers.assertPlainDate(plainDate, ...expected[calls].plainDate, + `plainDate argument ${calls}`); + TemporalHelpers.assertDuration(duration, ...expected[calls].duration, + `duration argument ${calls}`); + assert.sameValue(options, undefined, "options argument"); + ++calls; + return super.dateAdd(plainDate, duration, options); + } +} +const relativeTo = new Temporal.PlainDate(1920, 5, 3, new CustomCalendar()); +const duration = new Temporal.Duration(2, 0, 0, 4, 2); +const result = duration.subtract({ months: 10, hours: 14 }, { relativeTo }); +TemporalHelpers.assertDuration(result, 1, 2, 0, 3, 12, 0, 0, 0, 0, 0, "result"); +assert.sameValue(calls, 2, "should have called dateAdd"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-dateuntil-called-with-singular-largestunit.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-dateuntil-called-with-singular-largestunit.js new file mode 100644 index 0000000000..861b717c0c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-dateuntil-called-with-singular-largestunit.js @@ -0,0 +1,81 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form +info: | + sec-temporal.duration.prototype.subtract step 6: + 6. Let _result_ be ? AddDuration(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _duration_.[[Hours]], _duration_.[[Minutes]], _duration_.[[Seconds]], _duration_.[[Milliseconds]], _duration_.[[Microseconds]], _duration_.[[Nanoseconds]], −_other_.[[Years]], −_other_.[[Months]], −_other_.[[Weeks]], −_other_.[[Days]], −_other_.[[Hours]], −_other_.[[Minutes]], −_other_.[[Seconds]], −_other_.[[Milliseconds]], −_other_.[[Microseconds]], −_other_.[[Nanoseconds]], _relativeTo_). + sec-temporal-addduration steps 6-7: + 6. If _relativeTo_ has an [[InitializedTemporalPlainDateTime]] internal slot, then + ... + j. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_). + k. Let _differenceOptions_ be ! OrdinaryObjectCreate(*null*). + l. Perform ! CreateDataPropertyOrThrow(_differenceOptions_, *"largestUnit"*, _dateLargestUnit_). + m. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _datePart_, _end_, _differenceOptions_). + ... + 7. Else, + a. Assert: _relativeTo_ has an [[IntializedTemporalZonedDateTime]] internal slot. + ... + f. If _largestUnit_ is not one of *"year"*, *"month"*, *"week"*, or *"day"*, then + ... + g. Else, + i. Let _result_ be ? DifferenceZonedDateTime(_relativeTo_.[[Nanoseconds]], _endNs_, _timeZone_, _calendar_, _largestUnit_). + sec-temporal-differencezoneddatetime steps 7 and 11: + 7. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _calendar_, _largestUnit_, _options_). + 11. Let _result_ be ? NanosecondsToDays(_timeRemainderNs_, _intermediate_). + sec-temporal-nanosecondstodays step 11: + 11. 1. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _relativeTo_.[[Calendar]], *"day"*). + sec-temporal-differenceisodatetime steps 9–11: + 9. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_). + 10. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _dateLargestUnit_). + 11. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.checkCalendarDateUntilLargestUnitSingular( + (calendar, largestUnit, index) => { + const one = new Temporal.Duration(...[...Array(index).fill(0), ...Array(10 - index).fill(1)]); + const two = new Temporal.Duration(...[...Array(index).fill(0), ...Array(10 - index).fill(2)]); + const relativeTo = new Temporal.PlainDateTime(2000, 5, 2, 12, 34, 56, 987, 654, 321, calendar); + two.subtract(one, { relativeTo }); + }, + { + years: ["year"], + months: ["month"], + weeks: ["week"], + days: ["day"], + hours: ["day"], + minutes: ["day"], + seconds: ["day"], + milliseconds: ["day"], + microseconds: ["day"], + nanoseconds: ["day"] + } +); + +TemporalHelpers.checkCalendarDateUntilLargestUnitSingular( + (calendar, largestUnit, index) => { + const one = new Temporal.Duration(...[...Array(index).fill(0), ...Array(10 - index).fill(1)]); + const two = new Temporal.Duration(...[...Array(index).fill(0), ...Array(10 - index).fill(2)]); + const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, "UTC", calendar); + two.subtract(one, { relativeTo }); + }, + { + years: ["year", "day"], + months: ["month", "day"], + weeks: ["week", "day"], + days: ["day", "day"], + hours: [], + minutes: [], + seconds: [], + milliseconds: [], + microseconds: [], + nanoseconds: [] + } +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-fields-iterable.js new file mode 100644 index 0000000000..eec67c5cba --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-fields-iterable.js @@ -0,0 +1,41 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: Verify the result of calendar.fields() is treated correctly. +info: | + sec-temporal.duration.prototype.subtract step 5: + 5. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_). + sec-temporal-torelativetemporalobject step 4.c: + c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »). + sec-temporal-calendarfields step 4: + 4. Let _result_ be ? IterableToList(_fieldsArray_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "day", + "hour", + "microsecond", + "millisecond", + "minute", + "month", + "monthCode", + "nanosecond", + "second", + "year", +]; + +const calendar = TemporalHelpers.calendarFieldsIterable(); +const duration1 = new Temporal.Duration(1); +const duration2 = new Temporal.Duration(0, 12); +duration1.subtract(duration2, { relativeTo: { year: 2000, month: 1, day: 1, calendar } }); + +assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once"); +assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args"); +assert(calendar.iteratorExhausted[0], "iterated through the whole iterable"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-temporal-object.js new file mode 100644 index 0000000000..7e84901087 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/calendar-temporal-object.js @@ -0,0 +1,30 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots +info: | + sec-temporal.duration.prototype.subtract step 5: + 5. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_). + sec-temporal-torelativetemporalobject step 4.b: + b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_). + sec-temporal-gettemporalcalendarwithisodefault step 2: + 2. Return ? ToTemporalCalendarWithISODefault(_calendar_). + sec-temporal-totemporalcalendarwithisodefault step 2: + 3. Return ? ToTemporalCalendar(_temporalCalendarLike_). + sec-temporal-totemporalcalendar step 1.a: + a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then + i. Return _temporalCalendarLike_.[[Calendar]]. +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => { + const duration1 = new Temporal.Duration(1); + const duration2 = new Temporal.Duration(0, 12); + duration1.subtract(duration2, { relativeTo: { year: 2000, month: 1, day: 1, calendar: temporalObject } }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/days-is-number-max-value-with-zoneddatetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/days-is-number-max-value-with-zoneddatetime.js new file mode 100644 index 0000000000..92d9261b5b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/days-is-number-max-value-with-zoneddatetime.js @@ -0,0 +1,22 @@ +// |reftest| skip -- Temporal is not supported +// 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.subtract +description: > + AddZonedDateTime throws a RangeError when the intermediate instant is too large. +features: [Temporal] +---*/ + +const plainDate = new Temporal.PlainDate(1970, 1, 1); +const zonedDateTime = new Temporal.ZonedDateTime(0n, "UTC", "iso8601"); + +var duration1 = Temporal.Duration.from({days: 1, nanoseconds: Number.MAX_VALUE}); +var duration2 = Temporal.Duration.from({days: -1, nanoseconds: -Number.MAX_VALUE}); + +var options = {relativeTo: zonedDateTime}; + +assert.throws(RangeError, () => duration1.subtract(duration2, options)); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/days-is-number-max-value.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/days-is-number-max-value.js new file mode 100644 index 0000000000..00975ef74e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/days-is-number-max-value.js @@ -0,0 +1,17 @@ +// |reftest| skip -- Temporal is not supported +// 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.subtract +description: > + BalanceDuration throws a RangeError when the result is too large. +features: [Temporal] +---*/ + +var duration1 = Temporal.Duration.from({days: Number.MAX_VALUE}); +var duration2 = Temporal.Duration.from({days: -Number.MAX_VALUE}); + +assert.throws(RangeError, () => duration1.subtract(duration2)); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/infinity-throws-rangeerror.js new file mode 100644 index 0000000000..729561ceb9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/infinity-throws-rangeerror.js @@ -0,0 +1,34 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2020 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Temporal.Duration.prototype.subtract throws a RangeError if any value in a property bag is Infinity +esid: sec-temporal.duration.prototype.subtract +features: [Temporal] +---*/ + +const fields = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds', 'nanoseconds']; + +const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); +const relativeTo = new Temporal.PlainDateTime(2000, 1, 1); + +fields.forEach((field) => { + assert.throws(RangeError, () => instance.subtract({ [field]: Infinity }, { relativeTo })); +}); + +let calls = 0; +const obj = { + valueOf() { + calls++; + return Infinity; + } +}; + +fields.forEach((field) => { + calls = 0; + assert.throws(RangeError, () => instance.subtract({ [field]: obj }, { relativeTo })); + assert.sameValue(calls, 1, "it fails after fetching the primitive value"); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/length.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/length.js new file mode 100644 index 0000000000..4e0e2ae95f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/length.js @@ -0,0 +1,28 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2020 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: Temporal.Duration.prototype.subtract.length is 1 +info: | + Every built-in function object, including constructors, has a "length" property whose value is + an integer. Unless otherwise specified, this value is equal to the largest number of named + arguments shown in the subclause headings for the function description. Optional parameters + (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form + «...name») are not included in the default argument count. + + Unless otherwise specified, the "length" property of a built-in function object has the + attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +verifyProperty(Temporal.Duration.prototype.subtract, "length", { + value: 1, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/name.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/name.js new file mode 100644 index 0000000000..31f99615e4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/name.js @@ -0,0 +1,26 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: Temporal.Duration.prototype.subtract.name is "subtract". +info: | + Every built-in function object, including constructors, that is not identified as an anonymous + function has a "name" property whose value is a String. Unless otherwise specified, this value + is the name that is given to the function in this specification. + + Unless otherwise specified, the "name" property of a built-in function object, if it exists, + has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +verifyProperty(Temporal.Duration.prototype.subtract, "name", { + value: "subtract", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/nanoseconds-is-number-max-safe-integer.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/nanoseconds-is-number-max-safe-integer.js new file mode 100644 index 0000000000..a52c537796 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/nanoseconds-is-number-max-safe-integer.js @@ -0,0 +1,60 @@ +// |reftest| skip -- Temporal is not supported +// 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.subtract +description: > + AddDuration computes on exact mathematical number values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const plainDate = new Temporal.PlainDate(1970, 1, 1); +const zonedDateTime = new Temporal.ZonedDateTime(0n, "UTC", "iso8601"); + +// Largest temporal unit is "day". +const duration1 = Temporal.Duration.from({nanoseconds: Number.MAX_SAFE_INTEGER}); +const duration2 = Temporal.Duration.from({nanoseconds: -2, days: -1}); +const nanos = BigInt(Number.MAX_SAFE_INTEGER) + 2n; + +TemporalHelpers.assertDuration( + duration1.subtract(duration2), + 0, 0, 0, + 1 + Number((nanos / (24n * 60n * 60n * 1_000_000_000n))), + Number((nanos / (60n * 60n * 1_000_000_000n)) % 24n), + Number((nanos / (60n * 1_000_000_000n)) % 60n), + Number((nanos / 1_000_000_000n) % 60n), + Number((nanos / 1_000_000n) % 1000n), + Number((nanos / 1000n) % 1000n), + Number(nanos % 1000n), + "duration1.subtract(duration2)" +); + +TemporalHelpers.assertDuration( + duration1.subtract(duration2, {relativeTo: plainDate}), + 0, 0, 0, + 1 + Number((nanos / (24n * 60n * 60n * 1_000_000_000n))), + Number((nanos / (60n * 60n * 1_000_000_000n)) % 24n), + Number((nanos / (60n * 1_000_000_000n)) % 60n), + Number((nanos / 1_000_000_000n) % 60n), + Number((nanos / 1_000_000n) % 1000n), + Number((nanos / 1000n) % 1000n), + Number(nanos % 1000n), + "duration1.subtract(duration2, {relativeTo: plainDate})" +); + +TemporalHelpers.assertDuration( + duration1.subtract(duration2, {relativeTo: zonedDateTime}), + 0, 0, 0, + 1 + Number((nanos / (24n * 60n * 60n * 1_000_000_000n))), + Number((nanos / (60n * 60n * 1_000_000_000n)) % 24n), + Number((nanos / (60n * 1_000_000_000n)) % 60n), + Number((nanos / 1_000_000_000n) % 60n), + Number((nanos / 1_000_000n) % 1000n), + Number((nanos / 1000n) % 1000n), + Number(nanos % 1000n), + "duration1.subtract(duration2, {relativeTo: zonedDateTime})" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/nanoseconds-is-number-max-value-1.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/nanoseconds-is-number-max-value-1.js new file mode 100644 index 0000000000..d297c6f3c4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/nanoseconds-is-number-max-value-1.js @@ -0,0 +1,31 @@ +// |reftest| skip -- Temporal is not supported +// 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.subtract +description: > + BalanceDuration throws a RangeError when the result is too large. +features: [Temporal] +---*/ + +const plainDate = new Temporal.PlainDate(1970, 1, 1); +const zonedDateTime = new Temporal.ZonedDateTime(0n, "UTC", "iso8601"); + +// Largest temporal unit is "nanosecond". +const duration1 = Temporal.Duration.from({nanoseconds: Number.MAX_VALUE}); +const duration2 = Temporal.Duration.from({nanoseconds: -Number.MAX_VALUE}); + +assert.throws(RangeError, () => { + duration1.subtract(duration2); +}); + +assert.throws(RangeError, () => { + duration1.subtract(duration2, {relativeTo: plainDate}); +}); + +assert.throws(RangeError, () => { + duration1.subtract(duration2, {relativeTo: zonedDateTime}); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/nanoseconds-is-number-max-value-2.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/nanoseconds-is-number-max-value-2.js new file mode 100644 index 0000000000..f8f6fb796d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/nanoseconds-is-number-max-value-2.js @@ -0,0 +1,52 @@ +// |reftest| skip -- Temporal is not supported +// 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.subtract +description: > + BalanceDuration computes on exact mathematical number values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const plainDate = new Temporal.PlainDate(1970, 1, 1); +const zonedDateTime = new Temporal.ZonedDateTime(0n, "UTC", "iso8601"); + +// Largest temporal unit is "day". +const duration1 = Temporal.Duration.from({nanoseconds: Number.MAX_VALUE}); +const duration2 = Temporal.Duration.from({nanoseconds: -Number.MAX_VALUE, days: -1}); +const nanos = BigInt(Number.MAX_VALUE) * 2n; + +TemporalHelpers.assertDuration( + duration1.subtract(duration2), + 0, 0, 0, + 1 + Number((nanos / (24n * 60n * 60n * 1_000_000_000n))), + Number((nanos / (60n * 60n * 1_000_000_000n)) % 24n), + Number((nanos / (60n * 1_000_000_000n)) % 60n), + Number((nanos / 1_000_000_000n) % 60n), + Number((nanos / 1_000_000n) % 1000n), + Number((nanos / 1000n) % 1000n), + Number(nanos % 1000n), + "duration1.subtract(duration2)" +); + +TemporalHelpers.assertDuration( + duration1.subtract(duration2, {relativeTo: plainDate}), + 0, 0, 0, + 1 + Number((nanos / (24n * 60n * 60n * 1_000_000_000n))), + Number((nanos / (60n * 60n * 1_000_000_000n)) % 24n), + Number((nanos / (60n * 1_000_000_000n)) % 60n), + Number((nanos / 1_000_000_000n) % 60n), + Number((nanos / 1_000_000n) % 1000n), + Number((nanos / 1000n) % 1000n), + Number(nanos % 1000n), + "duration1.subtract(duration2, {relativeTo: plainDate})" +); + +// Throws a RangeError because the intermediate instant is too large. +assert.throws(RangeError, () => { + duration1.subtract(duration2, {relativeTo: zonedDateTime}); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/nanoseconds-to-days-loop-indefinitely-1.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/nanoseconds-to-days-loop-indefinitely-1.js new file mode 100644 index 0000000000..7e2afd0786 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/nanoseconds-to-days-loop-indefinitely-1.js @@ -0,0 +1,62 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: > + NanosecondsToDays can loop arbitrarily long, performing observable operations each iteration. +info: | + NanosecondsToDays ( nanoseconds, relativeTo ) + + ... + 15. If sign is 1, then + a. Repeat, while days > 0 and intermediateNs > endNs, + i. Set days to days - 1. + ii. Set intermediateNs to ℝ(? AddZonedDateTime(ℤ(startNs), relativeTo.[[TimeZone]], + relativeTo.[[Calendar]], 0, 0, 0, days, 0, 0, 0, 0, 0, 0)). + ... +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calls = []; +const duration = Temporal.Duration.from({ days: 1 }); +const other = Temporal.Duration.from({ hours: 1 }); + +function createRelativeTo(count) { + const tz = new Temporal.TimeZone("UTC"); + // Record calls in calls[] + TemporalHelpers.observeMethod(calls, tz, "getPossibleInstantsFor"); + const cal = new Temporal.Calendar("iso8601"); + // Return _count_ days for the second call to dateUntil, behaving normally after + TemporalHelpers.substituteMethod(cal, "dateUntil", [ + TemporalHelpers.SUBSTITUTE_SKIP, + Temporal.Duration.from({ days: count }), + ]); + return new Temporal.ZonedDateTime(0n, tz, cal); +} + +let zdt = createRelativeTo(200); +calls.splice(0); // Reset calls list after ZonedDateTime construction +duration.subtract(other, { + relativeTo: zdt, +}); +assert.sameValue( + calls.length, + 200 + 2, + "Expected duration.subtract to call getPossibleInstantsFor correct number of times" +); + +zdt = createRelativeTo(300); +calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction +duration.subtract(other, { + relativeTo: zdt, +}); +assert.sameValue( + calls.length, + 300 + 2, + "Expected duration.subtract to call getPossibleInstantsFor correct number of times" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/nanoseconds-to-days-loop-indefinitely-2.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/nanoseconds-to-days-loop-indefinitely-2.js new file mode 100644 index 0000000000..757b7060cd --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/nanoseconds-to-days-loop-indefinitely-2.js @@ -0,0 +1,74 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: > + NanosecondsToDays can loop infinitely. +info: | + NanosecondsToDays ( nanoseconds, relativeTo ) + + ... + 18. Repeat, while done is false, + a. Let oneDayFartherNs be ℝ(? AddZonedDateTime(ℤ(intermediateNs), relativeTo.[[TimeZone]], + relativeTo.[[Calendar]], 0, 0, 0, sign, 0, 0, 0, 0, 0, 0)). + b. Set dayLengthNs to oneDayFartherNs - intermediateNs. + c. If (nanoseconds - dayLengthNs) × sign ≥ 0, then + i. Set nanoseconds to nanoseconds - dayLengthNs. + ii. Set intermediateNs to oneDayFartherNs. + iii. Set days to days + sign. + d. Else, + i. Set done to true. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calls = []; +const duration = Temporal.Duration.from({ days: 1 }); + +function createRelativeTo(count) { + const dayLengthNs = 86400000000000n; + const dayInstant = new Temporal.Instant(dayLengthNs); + const substitutions = []; + const timeZone = new Temporal.TimeZone("UTC"); + // Return constant value for first _count_ calls + TemporalHelpers.substituteMethod( + timeZone, + "getPossibleInstantsFor", + substitutions + ); + substitutions.length = count; + let i = 0; + for (i = 0; i < substitutions.length; i++) { + // (this value) + substitutions[i] = [dayInstant]; + } + // Record calls in calls[] + TemporalHelpers.observeMethod(calls, timeZone, "getPossibleInstantsFor"); + return new Temporal.ZonedDateTime(0n, timeZone); +} + +let zdt = createRelativeTo(200); +calls.splice(0); // Reset calls list after ZonedDateTime construction +duration.subtract(duration, { + relativeTo: zdt, +}); +assert.sameValue( + calls.length, + 200 + 1, + "Expected duration.subtract to call getPossibleInstantsFor correct number of times" +); + +zdt = createRelativeTo(300); +calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction +duration.subtract(duration, { + relativeTo: zdt, +}); +assert.sameValue( + calls.length, + 300 + 1, + "Expected duration.subtract to call getPossibleInstantsFor correct number of times" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/negative-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/negative-infinity-throws-rangeerror.js new file mode 100644 index 0000000000..d44ae2ddc7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/negative-infinity-throws-rangeerror.js @@ -0,0 +1,34 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2020 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Temporal.Duration.prototype.subtract throws a RangeError if any value in a property bag is -Infinity +esid: sec-temporal.duration.prototype.subtract +features: [Temporal] +---*/ + +const fields = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds', 'nanoseconds']; + +const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); +const relativeTo = new Temporal.PlainDateTime(2000, 1, 1); + +fields.forEach((field) => { + assert.throws(RangeError, () => instance.subtract({ [field]: -Infinity }, { relativeTo })); +}); + +let calls = 0; +const obj = { + valueOf() { + calls++; + return -Infinity; + } +}; + +fields.forEach((field) => { + calls = 0; + assert.throws(RangeError, () => instance.subtract({ [field]: obj }, { relativeTo })); + assert.sameValue(calls, 1, "it fails after fetching the primitive value"); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/non-integer-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/non-integer-throws-rangeerror.js new file mode 100644 index 0000000000..fa82b8e9a8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/non-integer-throws-rangeerror.js @@ -0,0 +1,29 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: A non-integer value for any recognized property in the property bag, throws a RangeError +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321); +const fields = [ + "years", + "months", + "weeks", + "days", + "hours", + "minutes", + "seconds", + "milliseconds", + "microseconds", + "nanoseconds", +]; +fields.forEach((field) => { + assert.throws(RangeError, () => instance.subtract({ [field]: 1.5 })); + assert.throws(RangeError, () => instance.subtract({ [field]: -1.5 })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/not-a-constructor.js new file mode 100644 index 0000000000..e06eb61832 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/not-a-constructor.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: > + Temporal.Duration.prototype.subtract does not implement [[Construct]], is not new-able +info: | + Built-in function objects that are not identified as constructors do not implement the + [[Construct]] internal method unless otherwise specified in the description of a particular + function. +includes: [isConstructor.js] +features: [Reflect.construct, Temporal] +---*/ + +assert.throws(TypeError, () => { + new Temporal.Duration.prototype.subtract(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.Duration.prototype.subtract), false, + "isConstructor(Temporal.Duration.prototype.subtract)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/options-object.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/options-object.js new file mode 100644 index 0000000000..6887366df4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/options-object.js @@ -0,0 +1,26 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: Empty or a function object may be used as options +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(0, 0, 0, 0, 1); + +const result1 = instance.subtract({ hours: 1 }, {}); +TemporalHelpers.assertDuration( + result1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "options may be an empty plain object" +); + +const result2 = instance.subtract({ hours: 1 }, () => {}); +TemporalHelpers.assertDuration( + result2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "options may be a function object" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/options-undefined.js new file mode 100644 index 0000000000..6fe398c162 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/options-undefined.js @@ -0,0 +1,35 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: Verify that undefined options are handled correctly. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const duration1 = new Temporal.Duration(1); +const duration2 = new Temporal.Duration(0, 24); +const duration3 = new Temporal.Duration(0, 0, 0, 1); +const duration4 = new Temporal.Duration(0, 0, 0, 0, 48); + +assert.throws(RangeError, () => duration1.subtract(duration2), "no options with years"); +TemporalHelpers.assertDuration(duration3.subtract(duration4), + 0, 0, 0, /* days = */ -1, 0, 0, 0, 0, 0, 0, + "no options with days"); + +const optionValues = [ + [undefined, "undefined"], + [{}, "plain object"], + [() => {}, "lambda"], +]; +for (const [options, description] of optionValues) { + assert.throws(RangeError, () => duration1.subtract(duration2, options), + `options ${description} with years`); + TemporalHelpers.assertDuration(duration3.subtract(duration4, options), + 0, 0, 0, /* days = */ -1, 0, 0, 0, 0, 0, 0, + `options ${description} with days`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/options-wrong-type.js new file mode 100644 index 0000000000..32458fa7cb --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/options-wrong-type.js @@ -0,0 +1,26 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: TypeError thrown when options argument is a primitive +features: [BigInt, Symbol, Temporal] +---*/ + +const badOptions = [ + null, + true, + "some string", + Symbol(), + 1, + 2n, +]; + +const instance = new Temporal.Duration(0, 0, 0, 0, 1); +for (const value of badOptions) { + assert.throws(TypeError, () => instance.subtract({ hours: 1 }, value), + `TypeError on wrong options type ${typeof value}`); +}; + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/order-of-operations.js new file mode 100644 index 0000000000..b9784675ea --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/order-of-operations.js @@ -0,0 +1,260 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2020 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: Properties on an object passed to subtract() are accessed in the correct order +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + // ToTemporalDurationRecord + "get fields.days", + "get fields.days.valueOf", + "call fields.days.valueOf", + "get fields.hours", + "get fields.hours.valueOf", + "call fields.hours.valueOf", + "get fields.microseconds", + "get fields.microseconds.valueOf", + "call fields.microseconds.valueOf", + "get fields.milliseconds", + "get fields.milliseconds.valueOf", + "call fields.milliseconds.valueOf", + "get fields.minutes", + "get fields.minutes.valueOf", + "call fields.minutes.valueOf", + "get fields.months", + "get fields.months.valueOf", + "call fields.months.valueOf", + "get fields.nanoseconds", + "get fields.nanoseconds.valueOf", + "call fields.nanoseconds.valueOf", + "get fields.seconds", + "get fields.seconds.valueOf", + "call fields.seconds.valueOf", + "get fields.weeks", + "get fields.weeks.valueOf", + "call fields.weeks.valueOf", + "get fields.years", + "get fields.years.valueOf", + "call fields.years.valueOf", + // ToRelativeTemporalObject + "get options.relativeTo", +]; +const actual = []; + +const simpleFields = TemporalHelpers.propertyBagObserver(actual, { + years: 0, + months: 0, + weeks: 0, + days: 1, + hours: 1, + minutes: 1, + seconds: 1, + milliseconds: 1, + microseconds: 1, + nanoseconds: 1, +}, "fields"); + +function createOptionsObserver(relativeTo = undefined) { + return TemporalHelpers.propertyBagObserver(actual, { relativeTo }, "options"); +} + +// basic order of observable operations, without any calendar units: +const simpleInstance = new Temporal.Duration(0, 0, 0, 1, 1, 1, 1, 1, 1, 1); +simpleInstance.subtract(simpleFields, createOptionsObserver()); +assert.compareArray(actual, expected, "order of operations"); +actual.splice(0); // clear + +const expectedOpsForPlainRelativeTo = expected.concat([ + // ToRelativeTemporalObject + "get options.relativeTo.calendar", + "has options.relativeTo.calendar.calendar", + "get options.relativeTo.calendar.fields", + "call options.relativeTo.calendar.fields", + // PrepareTemporalFields + "get options.relativeTo.day", + "get options.relativeTo.day.valueOf", + "call options.relativeTo.day.valueOf", + "get options.relativeTo.hour", + "get options.relativeTo.microsecond", + "get options.relativeTo.millisecond", + "get options.relativeTo.minute", + "get options.relativeTo.month", + "get options.relativeTo.month.valueOf", + "call options.relativeTo.month.valueOf", + "get options.relativeTo.monthCode", + "get options.relativeTo.monthCode.toString", + "call options.relativeTo.monthCode.toString", + "get options.relativeTo.nanosecond", + "get options.relativeTo.offset", + "get options.relativeTo.second", + "get options.relativeTo.timeZone", + "get options.relativeTo.year", + "get options.relativeTo.year.valueOf", + "call options.relativeTo.year.valueOf", + // InterpretTemporalDateTimeFields + "get options.relativeTo.calendar.dateFromFields", + "call options.relativeTo.calendar.dateFromFields", + // AddDuration + "get options.relativeTo.calendar.dateAdd", + "call options.relativeTo.calendar.dateAdd", + "call options.relativeTo.calendar.dateAdd", + "get options.relativeTo.calendar.dateUntil", + "call options.relativeTo.calendar.dateUntil", +]); + +const instance = new Temporal.Duration(1, 2, 1, 4, 5, 6, 7, 987, 654, 321); + +const fields = TemporalHelpers.propertyBagObserver(actual, { + years: 1, + months: 1, + weeks: 1, + days: 1, + hours: 1, + minutes: 1, + seconds: 1, + milliseconds: 1, + microseconds: 1, + nanoseconds: 1, +}, "fields"); + +const plainRelativeTo = TemporalHelpers.propertyBagObserver(actual, { + year: 2000, + month: 1, + monthCode: "M01", + day: 1, + calendar: TemporalHelpers.calendarObserver(actual, "options.relativeTo.calendar"), +}, "options.relativeTo"); + +instance.subtract(fields, createOptionsObserver(plainRelativeTo)); +assert.compareArray(actual, expectedOpsForPlainRelativeTo, "order of operations with PlainDate relativeTo"); +actual.splice(0); // clear + +const expectedOpsForZonedRelativeTo = expected.concat([ + // ToRelativeTemporalObject + "get options.relativeTo.calendar", + "has options.relativeTo.calendar.calendar", + "get options.relativeTo.calendar.fields", + "call options.relativeTo.calendar.fields", + // PrepareTemporalFields + "get options.relativeTo.day", + "get options.relativeTo.day.valueOf", + "call options.relativeTo.day.valueOf", + "get options.relativeTo.hour", + "get options.relativeTo.hour.valueOf", + "call options.relativeTo.hour.valueOf", + "get options.relativeTo.microsecond", + "get options.relativeTo.microsecond.valueOf", + "call options.relativeTo.microsecond.valueOf", + "get options.relativeTo.millisecond", + "get options.relativeTo.millisecond.valueOf", + "call options.relativeTo.millisecond.valueOf", + "get options.relativeTo.minute", + "get options.relativeTo.minute.valueOf", + "call options.relativeTo.minute.valueOf", + "get options.relativeTo.month", + "get options.relativeTo.month.valueOf", + "call options.relativeTo.month.valueOf", + "get options.relativeTo.monthCode", + "get options.relativeTo.monthCode.toString", + "call options.relativeTo.monthCode.toString", + "get options.relativeTo.nanosecond", + "get options.relativeTo.nanosecond.valueOf", + "call options.relativeTo.nanosecond.valueOf", + "get options.relativeTo.offset", + "get options.relativeTo.offset.toString", + "call options.relativeTo.offset.toString", + "get options.relativeTo.second", + "get options.relativeTo.second.valueOf", + "call options.relativeTo.second.valueOf", + "get options.relativeTo.timeZone", + "get options.relativeTo.year", + "get options.relativeTo.year.valueOf", + "call options.relativeTo.year.valueOf", + // InterpretTemporalDateTimeFields + "get options.relativeTo.calendar.dateFromFields", + "call options.relativeTo.calendar.dateFromFields", + // ToRelativeTemporalObject again + "has options.relativeTo.timeZone.timeZone", + // InterpretISODateTimeOffset + "get options.relativeTo.timeZone.getPossibleInstantsFor", + "call options.relativeTo.timeZone.getPossibleInstantsFor", + "get options.relativeTo.timeZone.getOffsetNanosecondsFor", + "call options.relativeTo.timeZone.getOffsetNanosecondsFor", + // AddDuration → AddZonedDateTime 1 + "get options.relativeTo.timeZone.getOffsetNanosecondsFor", + "call options.relativeTo.timeZone.getOffsetNanosecondsFor", + "get options.relativeTo.calendar.dateAdd", + "call options.relativeTo.calendar.dateAdd", + "get options.relativeTo.timeZone.getPossibleInstantsFor", + "call options.relativeTo.timeZone.getPossibleInstantsFor", + // AddDuration → AddZonedDateTime 2 + "get options.relativeTo.timeZone.getOffsetNanosecondsFor", + "call options.relativeTo.timeZone.getOffsetNanosecondsFor", + "get options.relativeTo.calendar.dateAdd", + "call options.relativeTo.calendar.dateAdd", + "get options.relativeTo.timeZone.getPossibleInstantsFor", + "call options.relativeTo.timeZone.getPossibleInstantsFor", + // AddDuration → DifferenceZonedDateTime + "get options.relativeTo.timeZone.getOffsetNanosecondsFor", + "call options.relativeTo.timeZone.getOffsetNanosecondsFor", + "get options.relativeTo.timeZone.getOffsetNanosecondsFor", + "call options.relativeTo.timeZone.getOffsetNanosecondsFor", + // AddDuration → DifferenceZonedDateTime → DifferenceISODateTime + "get options.relativeTo.calendar.dateUntil", + "call options.relativeTo.calendar.dateUntil", + // AddDuration → DifferenceZonedDateTime → AddZonedDateTime + "get options.relativeTo.timeZone.getOffsetNanosecondsFor", + "call options.relativeTo.timeZone.getOffsetNanosecondsFor", + "get options.relativeTo.calendar.dateAdd", + "call options.relativeTo.calendar.dateAdd", + "get options.relativeTo.timeZone.getPossibleInstantsFor", + "call options.relativeTo.timeZone.getPossibleInstantsFor", + // AddDuration → DifferenceZonedDateTime → NanosecondsToDays + "get options.relativeTo.timeZone.getOffsetNanosecondsFor", + "call options.relativeTo.timeZone.getOffsetNanosecondsFor", + "get options.relativeTo.timeZone.getOffsetNanosecondsFor", + "call options.relativeTo.timeZone.getOffsetNanosecondsFor", + // AddDuration → DifferenceZonedDateTime → NanosecondsToDays → DifferenceISODateTime + "get options.relativeTo.calendar.dateUntil", + "call options.relativeTo.calendar.dateUntil", + // AddDuration → DifferenceZonedDateTime → NanosecondsToDays → AddZonedDateTime 1 + "get options.relativeTo.timeZone.getOffsetNanosecondsFor", + "call options.relativeTo.timeZone.getOffsetNanosecondsFor", + "get options.relativeTo.calendar.dateAdd", + "call options.relativeTo.calendar.dateAdd", + "get options.relativeTo.timeZone.getPossibleInstantsFor", + "call options.relativeTo.timeZone.getPossibleInstantsFor", + // AddDuration → DifferenceZonedDateTime → NanosecondsToDays → AddZonedDateTime 2 + "get options.relativeTo.timeZone.getOffsetNanosecondsFor", + "call options.relativeTo.timeZone.getOffsetNanosecondsFor", + "get options.relativeTo.calendar.dateAdd", + "call options.relativeTo.calendar.dateAdd", + "get options.relativeTo.timeZone.getPossibleInstantsFor", + "call options.relativeTo.timeZone.getPossibleInstantsFor", +]); + +const zonedRelativeTo = TemporalHelpers.propertyBagObserver(actual, { + year: 2000, + month: 1, + monthCode: "M01", + day: 1, + hour: 0, + minute: 0, + second: 0, + millisecond: 0, + microsecond: 0, + nanosecond: 0, + offset: "+00:00", + calendar: TemporalHelpers.calendarObserver(actual, "options.relativeTo.calendar"), + timeZone: TemporalHelpers.timeZoneObserver(actual, "options.relativeTo.timeZone"), +}, "options.relativeTo"); + +instance.subtract(fields, createOptionsObserver(zonedRelativeTo)); +assert.compareArray(actual, expectedOpsForZonedRelativeTo, "order of operations with ZonedDateTime relativeTo"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/precision-exact-mathematical-values.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/precision-exact-mathematical-values.js new file mode 100644 index 0000000000..61f0ec294a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/precision-exact-mathematical-values.js @@ -0,0 +1,26 @@ +// |reftest| skip -- Temporal is not supported +// 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.subtract +description: > + AddDuration computes on exact mathematical number values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +// Largest temporal unit is "microsecond". +let duration1 = Temporal.Duration.from({microseconds: Number.MAX_SAFE_INTEGER + 1, nanoseconds: 0}); +let duration2 = Temporal.Duration.from({microseconds: -1, nanoseconds: -1000}); + +TemporalHelpers.assertDuration( + duration1.subtract(duration2), + 0, 0, 0, 0, + 0, 0, 0, 0, + 9007199254740994, + 0, + "duration1.subtract(duration2)" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/prop-desc.js new file mode 100644 index 0000000000..5499b8f692 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: The "subtract" property of Temporal.Duration.prototype +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.Duration.prototype.subtract, + "function", + "`typeof Duration.prototype.subtract` is `function`" +); + +verifyProperty(Temporal.Duration.prototype, "subtract", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/read-time-fields-before-datefromfields.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/read-time-fields-before-datefromfields.js new file mode 100644 index 0000000000..98f2d1c0d9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/read-time-fields-before-datefromfields.js @@ -0,0 +1,25 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: The time fields are read from the object before being passed to dateFromFields(). +info: | + sec-temporal.duration.prototype.subtract step 5: + 5. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_). + sec-temporal-torelativetemporalobject step 4.g: + g. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_). + sec-temporal-interprettemporaldatetimefields steps 1–2: + 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_). + 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarMakeInvalidGettersTime(); +const duration1 = new Temporal.Duration(1); +const duration2 = new Temporal.Duration(0, 12); +duration1.subtract(duration2, { relativeTo: { year: 2000, month: 1, day: 1, calendar } }); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-infinity-throws-rangeerror.js new file mode 100644 index 0000000000..928023c455 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-infinity-throws-rangeerror.js @@ -0,0 +1,26 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Throws if any value in the property bag is Infinity or -Infinity +esid: sec-temporal.duration.prototype.subtract +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); +const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321 }; + +[Infinity, -Infinity].forEach((inf) => { + ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => { + assert.throws(RangeError, () => instance.subtract(instance, { relativeTo: { ...base, [prop]: inf } }), `${prop} property cannot be ${inf} in relativeTo`); + + const calls = []; + const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop); + assert.throws(RangeError, () => instance.subtract(instance, { relativeTo: { ...base, [prop]: obj } })); + assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value"); + }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-leap-second.js new file mode 100644 index 0000000000..f9094d7c38 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-leap-second.js @@ -0,0 +1,46 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: Leap second is constrained in both an ISO string and a property bag +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1, 0, 0, 1); + +let relativeTo = "2016-12-31T23:59:60"; +const result1 = instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }); +TemporalHelpers.assertDuration( + result1, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "leap second is a valid ISO string for PlainDate relativeTo" +); + +relativeTo = "2016-12-31T23:59:60+00:00[UTC]"; +const result2 = instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }); +TemporalHelpers.assertDuration( + result2, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "leap second is a valid ISO string for ZonedDateTime relativeTo" +); + +relativeTo = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 }; +const result3 = instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }); +TemporalHelpers.assertDuration( + result3, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "second: 60 is valid in a property bag for PlainDate relativeTo" +); + +relativeTo = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60, timeZone: "UTC" }; +const result4 = instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }); +TemporalHelpers.assertDuration( + result4, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + "second: 60 is valid in a property bag for ZonedDateTime relativeTo" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-month.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-month.js new file mode 100644 index 0000000000..0f20595706 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-month.js @@ -0,0 +1,21 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: relativeTo with months. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const oneMonth = new Temporal.Duration(0, 1); +const days30 = new Temporal.Duration(0, 0, 0, 30); +TemporalHelpers.assertDuration(oneMonth.subtract(days30, { relativeTo: Temporal.PlainDate.from('2018-02-01') }), + 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, "February"); +TemporalHelpers.assertDuration(oneMonth.subtract(days30, { relativeTo: Temporal.PlainDate.from('2018-03-01') }), + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, "March"); +TemporalHelpers.assertDuration(oneMonth.subtract(days30, { relativeTo: Temporal.PlainDate.from('2018-04-01') }), + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "April"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-number.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-number.js new file mode 100644 index 0000000000..d9efa271e6 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-number.js @@ -0,0 +1,33 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: A number as relativeTo option is converted to a string, then to Temporal.PlainDate +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1, 0, 0, 1); + +const relativeTo = 20191101; + +const result = instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }); +TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "20191101 is a valid ISO string for relativeTo"); + +const numbers = [ + 1, + -20191101, + 1234567890, +]; + +for (const relativeTo of numbers) { + assert.throws( + RangeError, + () => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }), + `Number ${relativeTo} does not convert to a valid ISO string for relativeTo` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-order.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-order.js new file mode 100644 index 0000000000..af57b1a137 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-order.js @@ -0,0 +1,19 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: relativeTo with years. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const d1 = new Temporal.Duration(0, 2, 1, 4); +const d2 = new Temporal.Duration(0, 1, 1, 1); +const relativeTo = new Temporal.PlainDate(2000, 1, 1); +TemporalHelpers.assertDuration(d1.subtract(d2, { relativeTo }), + 0, 1, 0, 3, 0, 0, 0, 0, 0, 0, + "first this is resolved against relativeTo, then the argument against relativeTo + this"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-calendar-datefromfields-called-with-null-prototype-fields.js new file mode 100644 index 0000000000..5a5ac76245 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-calendar-datefromfields-called-with-null-prototype-fields.js @@ -0,0 +1,19 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: > + Calendar.dateFromFields method is called with a null-prototype fields object +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution(); +const instance = new Temporal.Duration(1, 0, 0, 1); +const relativeTo = { year: 2000, month: 5, day: 2, calendar }; +instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }); +assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-calendar-number.js new file mode 100644 index 0000000000..c29d216c7c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-calendar-number.js @@ -0,0 +1,45 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: A number as calendar in relativeTo property bag is converted to a string, then to a calendar +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1, 0, 0, 1); + +const calendar = 19970327; + +let relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar }; +const result1 = instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }); +TemporalHelpers.assertDuration(result1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "19970327 is a valid ISO string for relativeTo.calendar"); + +relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar: { calendar } }; +const result2 = instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }); +TemporalHelpers.assertDuration(result2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "19970327 is a valid ISO string for relativeTo.calendar (nested property)"); + +const numbers = [ + 1, + -19970327, + 1234567890, +]; + +for (const calendar of numbers) { + let relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar }; + assert.throws( + RangeError, + () => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }), + `Number ${calendar} does not convert to a valid ISO string for relativeTo.calendar` + ); + relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar: { calendar } }; + assert.throws( + RangeError, + () => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }), + `Number ${calendar} does not convert to a valid ISO string for relativeTo.calendar (nested property)` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-calendar-wrong-type.js new file mode 100644 index 0000000000..78d9ed8985 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-calendar-wrong-type.js @@ -0,0 +1,53 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: > + Appropriate error thrown when relativeTo.calendar cannot be converted to a + calendar object or string +features: [BigInt, Symbol, Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.Duration(1, 0, 0, 1); + +const rangeErrorTests = [ + [null, "null"], + [true, "boolean"], + ["", "empty string"], + [1, "number that doesn't convert to a valid ISO string"], + [1n, "bigint"], + [new Temporal.TimeZone("UTC"), "time zone instance"], +]; + +for (const [calendar, description] of rangeErrorTests) { + let relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar }; + assert.throws(RangeError, () => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }), `${description} does not convert to a valid ISO string`); + + relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar: { calendar } }; + assert.throws(RangeError, () => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }), `${description} does not convert to a valid ISO string (nested property)`); +} + +const typeErrorTests = [ + [Symbol(), "symbol"], + [{}, "plain object"], + [Temporal.PlainDate, "Temporal.PlainDate, object"], + [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"], + [Temporal.ZonedDateTime, "Temporal.ZonedDateTime, object"], + [Temporal.ZonedDateTime.prototype, "Temporal.ZonedDateTime.prototype, object"], +]; + +for (const [calendar, description] of typeErrorTests) { + let relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar }; + assert.throws(TypeError, () => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }), `${description} is not a valid property bag and does not convert to a string`); + + relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar: { calendar } }; + assert.throws(TypeError, () => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }), `${description} is not a valid property bag and does not convert to a string (nested property)`); +} + +const relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar: { calendar: undefined } }; +assert.throws(RangeError, () => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }), `nested undefined calendar property is always a RangeError`); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-invalid-offset-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-invalid-offset-string.js new file mode 100644 index 0000000000..a8c23a6500 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-invalid-offset-string.js @@ -0,0 +1,25 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: relativeTo property bag with offset property is rejected if offset is in the wrong format +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.Duration(1, 0, 0, 1); + +const badOffsets = [ + "00:00", // missing sign + "+0", // too short + "-000:00", // too long + 0, // converts to a string that is invalid +]; +badOffsets.forEach((offset) => { + const relativeTo = { year: 2021, month: 10, day: 28, offset, timeZone }; + assert.throws(RangeError, () => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }), `"${offset} is not a valid offset string`); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-no-time-units.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-no-time-units.js new file mode 100644 index 0000000000..efe2fcce56 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-no-time-units.js @@ -0,0 +1,18 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: Missing time units in relativeTo property bag default to 0 +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1, 0, 0, 1); + +let relativeTo = { year: 2000, month: 1, day: 1 }; +const result = instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }); +TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "missing time units default to 0"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..37901ef4c3 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,19 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); + const other = new Temporal.Duration(0, 3); + assert.throws(RangeError, () => duration.subtract(other, { relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..dbef8089c3 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,23 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); + const other = new Temporal.Duration(0, 3); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => duration.subtract(other, { relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..f569b88119 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,19 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); + const other = new Temporal.Duration(0, 3); + assert.throws(RangeError, () => duration.subtract(other, { relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..47abd8b7b7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,28 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); + const other = new Temporal.Duration(0, 3); + assert.throws(TypeError, () => duration.subtract(other, { relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-instance-does-not-get-timeZone-property.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-instance-does-not-get-timeZone-property.js new file mode 100644 index 0000000000..ad2882e403 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-instance-does-not-get-timeZone-property.js @@ -0,0 +1,25 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: > + A Temporal.TimeZone instance passed to subtract() does not have its + 'timeZone' property observably checked +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1); + +const timeZone = new Temporal.TimeZone("UTC"); +Object.defineProperty(timeZone, "timeZone", { + get() { + throw new Test262Error("timeZone.timeZone should not be accessed"); + }, +}); + +instance.subtract(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }); +instance.subtract(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-string-datetime.js new file mode 100644 index 0000000000..4e0a5cb00b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-string-datetime.js @@ -0,0 +1,39 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: Conversion of ISO date-time strings to Temporal.TimeZone instances +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1); + +let timeZone = "2021-08-19T17:30"; +assert.throws(RangeError, () => instance.subtract(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), "bare date-time string is not a time zone"); +assert.throws(RangeError, () => instance.subtract(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }), "bare date-time string is not a time zone"); + +// The following are all valid strings so should not throw: + +[ + "2021-08-19T17:30Z", + "2021-08-19T1730Z", + "2021-08-19T17:30-07:00", + "2021-08-19T1730-07:00", + "2021-08-19T17:30-0700", + "2021-08-19T1730-0700", + "2021-08-19T17:30[UTC]", + "2021-08-19T1730[UTC]", + "2021-08-19T17:30Z[UTC]", + "2021-08-19T1730Z[UTC]", + "2021-08-19T17:30-07:00[UTC]", + "2021-08-19T1730-07:00[UTC]", + "2021-08-19T17:30-0700[UTC]", + "2021-08-19T1730-0700[UTC]", +].forEach((timeZone) => { + instance.subtract(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }); + instance.subtract(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-string-leap-second.js new file mode 100644 index 0000000000..cb3e0b86aa --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-string-leap-second.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: Leap second is a valid ISO string for TimeZone +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1); +let timeZone = "2016-12-31T23:59:60+00:00[UTC]"; + +// A string with a leap second is a valid ISO string, so the following two +// operations should not throw + +instance.subtract(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }); +instance.subtract(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }); + +timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]"; +assert.throws(RangeError, () => instance.subtract(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), "leap second in time zone name not valid"); +assert.throws(RangeError, () => instance.subtract(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }), "leap second in time zone name not valid (nested property)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-string-year-zero.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-string-year-zero.js new file mode 100644 index 0000000000..5fffeeac31 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-string-year-zero.js @@ -0,0 +1,29 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: Negative zero, as an extended year, is rejected +features: [Temporal, arrow-function] +---*/ + +const invalidStrings = [ + "-000000-10-31T17:45Z", + "-000000-10-31T17:45+00:00[UTC]", +]; +const instance = new Temporal.Duration(1); +invalidStrings.forEach((timeZone) => { + assert.throws( + RangeError, + () => instance.subtract(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), + "reject minus zero as extended year" + ); + assert.throws( + RangeError, + () => instance.subtract(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }), + "reject minus zero as extended year (nested property)" + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-string.js new file mode 100644 index 0000000000..9ccdcaed71 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-string.js @@ -0,0 +1,19 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: Time zone IDs are valid input for a time zone +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1); + +// The following are all valid strings so should not throw: + +["UTC", "+01:00"].forEach((timeZone) => { + instance.subtract(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-wrong-type.js new file mode 100644 index 0000000000..2c2847c4cf --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-propertybag-timezone-wrong-type.js @@ -0,0 +1,42 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: > + Appropriate error thrown when argument cannot be converted to a valid string + or object for TimeZone +features: [BigInt, Symbol, Temporal] +---*/ + +const instance = new Temporal.Duration(1); + +const rangeErrorTests = [ + [null, "null"], + [true, "boolean"], + ["", "empty string"], + [1, "number that doesn't convert to a valid ISO string"], + [19761118, "number that would convert to a valid ISO string in other contexts"], + [1n, "bigint"], + [new Temporal.Calendar("iso8601"), "calendar instance"], +]; + +for (const [timeZone, description] of rangeErrorTests) { + assert.throws(RangeError, () => instance.subtract(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), `${description} does not convert to a valid ISO string`); + assert.throws(RangeError, () => instance.subtract(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }), `${description} does not convert to a valid ISO string (nested property)`); +} + +const typeErrorTests = [ + [Symbol(), "symbol"], +]; + +for (const [timeZone, description] of typeErrorTests) { + assert.throws(TypeError, () => instance.subtract(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), `${description} is not a valid object and does not convert to a string`); + assert.throws(TypeError, () => instance.subtract(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }), `${description} is not a valid object and does not convert to a string (nested property)`); +} + +const timeZone = undefined; +assert.throws(RangeError, () => instance.subtract(new Temporal.Duration(1), { relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }), `undefined is always a RangeError as nested property`); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-required.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-required.js new file mode 100644 index 0000000000..96aee23b34 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-required.js @@ -0,0 +1,36 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: relativeTo is required if the largest unit is at least weeks. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const d = Temporal.Duration.from({ hours: 1 }); +const dy = Temporal.Duration.from({ years: 1, hours: 1 }); +const dm = Temporal.Duration.from({ months: 1, hours: 1 }); +const dw = Temporal.Duration.from({ weeks: 1, hours: 1 }); +assert.throws(RangeError, () => d.subtract(dy)); +assert.throws(RangeError, () => d.subtract(dm)); +assert.throws(RangeError, () => d.subtract(dw)); +assert.throws(RangeError, () => dy.subtract(d)); +assert.throws(RangeError, () => dm.subtract(d)); +assert.throws(RangeError, () => dw.subtract(d)); +const relativeTo = Temporal.PlainDate.from("2000-01-01"); +TemporalHelpers.assertDuration(d.subtract(dy, { relativeTo }), + -1, 0, 0, 0, 0, 0, 0, 0, 0, 0); +TemporalHelpers.assertDuration(d.subtract(dm, { relativeTo }), + 0, -1, 0, 0, 0, 0, 0, 0, 0, 0); +TemporalHelpers.assertDuration(d.subtract(dw, { relativeTo }), + 0, 0, -1, 0, 0, 0, 0, 0, 0, 0); +TemporalHelpers.assertDuration(dy.subtract(d, { relativeTo }), + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0); +TemporalHelpers.assertDuration(dm.subtract(d, { relativeTo }), + 0, 1, 0, 0, 0, 0, 0, 0, 0, 0); +TemporalHelpers.assertDuration(dw.subtract(d, { relativeTo }), + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-datetime.js new file mode 100644 index 0000000000..1c4c76b5aa --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-datetime.js @@ -0,0 +1,41 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: > + Conversion of ISO date-time strings as relativeTo option to + Temporal.ZonedDateTime or Temporal.PlainDateTime instances +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1, 0, 0, 1); + +let relativeTo = "2019-11-01T00:00"; +const result1 = instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }); +TemporalHelpers.assertDuration(result1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "bare date-time string is a plain relativeTo"); + +relativeTo = "2019-11-01T00:00-07:00"; +const result2 = instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }); +TemporalHelpers.assertDuration(result2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "date-time + offset is a plain relativeTo"); + +relativeTo = "2019-11-01T00:00[-07:00]"; +const result3 = instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }); +TemporalHelpers.assertDuration(result3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "date-time + IANA annotation is a zoned relativeTo"); + +relativeTo = "2019-11-01T00:00Z[-07:00]"; +const result4 = instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }); +TemporalHelpers.assertDuration(result4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "date-time + Z + IANA annotation is a zoned relativeTo"); + +relativeTo = "2019-11-01T00:00+00:00[UTC]"; +const result5 = instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }); +TemporalHelpers.assertDuration(result5, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "date-time + offset + IANA annotation is a zoned relativeTo"); + +relativeTo = "2019-11-01T00:00Z"; +assert.throws(RangeError, () => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }), "date-time + Z throws without an IANA annotation"); +relativeTo = "2019-11-01T00:00+04:15[UTC]"; +assert.throws(RangeError, () => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }), "date-time + offset + IANA annotation throws if wall time and exact time mismatch"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-invalid.js new file mode 100644 index 0000000000..8c9e5c0fb4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-invalid.js @@ -0,0 +1,16 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: RangeError thrown if relativeTo is a string with the wrong format +features: [Temporal] +---*/ + +['bad string', '15:30:45.123456', 'iso8601', 'UTC', 'P1YT1H'].forEach((relativeTo) => { + const duration = new Temporal.Duration(1, 0, 0, 41); + assert.throws(RangeError, () => duration.subtract(new Temporal.Duration(0, 0, 0, 10), { relativeTo })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-plaindatetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-plaindatetime.js new file mode 100644 index 0000000000..d08bd3d685 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-plaindatetime.js @@ -0,0 +1,18 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: The relativeTo option accepts a PlainDateTime-like ISO 8601 string +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +['2000-01-01', '2000-01-01T00:00', '2000-01-01T00:00[u-ca=iso8601]'].forEach((relativeTo) => { + const duration = new Temporal.Duration(1, 0, 0, 41); + const result = duration.subtract(new Temporal.Duration(0, 0, 0, 10), { relativeTo }); + TemporalHelpers.assertDuration(result, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-zoneddatetime-wrong-offset.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-zoneddatetime-wrong-offset.js new file mode 100644 index 0000000000..ab0c9b7e9b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-zoneddatetime-wrong-offset.js @@ -0,0 +1,19 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: Throws if a ZonedDateTime-like relativeTo string has the wrong UTC offset +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1, 0, 0, 1); +const relativeTo = "2000-01-01T00:00+05:30[UTC]"; +assert.throws( + RangeError, + () => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }), + "subtract should throw RangeError on a string with UTC offset mismatch" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-zoneddatetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-zoneddatetime.js new file mode 100644 index 0000000000..388e60c203 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-string-zoneddatetime.js @@ -0,0 +1,23 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: The relativeTo option accepts a ZonedDateTime-like ISO 8601 string +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + '2000-01-01[UTC]', + '2000-01-01T00:00[UTC]', + '2000-01-01T00:00+00:00[UTC]', + '2000-01-01T00:00+00:00[UTC][u-ca=iso8601]', +].forEach((relativeTo) => { + const duration = new Temporal.Duration(1, 0, 0, 41); + const result = duration.subtract(new Temporal.Duration(0, 0, 0, 10), { relativeTo }); + TemporalHelpers.assertDuration(result, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-sub-minute-offset.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-sub-minute-offset.js new file mode 100644 index 0000000000..4a938159e1 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-sub-minute-offset.js @@ -0,0 +1,25 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: relativeTo string accepts an inexact UTC offset rounded to hours and minutes +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1, 0, 0, 1); + +let relativeTo = "2000-01-01T00:00+00:45[+00:44:30.123456789]"; +const result = instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }); +TemporalHelpers.assertDuration(result, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, "rounded HH:MM is accepted in string"); + +relativeTo = "2000-01-01T00:00+00:44:30[+00:44:30.123456789]"; +assert.throws(RangeError, () => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }), "no other rounding is accepted for offset"); + +const timeZone = new Temporal.TimeZone("+00:44:30.123456789"); +relativeTo = { year: 2000, month: 1, day: 1, offset: "+00:45", timeZone }; +assert.throws(RangeError, () => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }), "rounded HH:MM not accepted as offset in property bag"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-wrong-type.js new file mode 100644 index 0000000000..a5fc023809 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-wrong-type.js @@ -0,0 +1,42 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: > + Appropriate error thrown when relativeTo cannot be converted to a valid + relativeTo string or property bag +features: [BigInt, Symbol, Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.Duration(1, 0, 0, 1); + +const rangeErrorTests = [ + [undefined, "undefined"], + [null, "null"], + [true, "boolean"], + ["", "empty string"], + [1, "number that doesn't convert to a valid ISO string"], + [1n, "bigint"], +]; + +for (const [relativeTo, description] of rangeErrorTests) { + assert.throws(RangeError, () => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }), `${description} does not convert to a valid ISO string`); +} + +const typeErrorTests = [ + [Symbol(), "symbol"], + [{}, "plain object"], + [Temporal.PlainDate, "Temporal.PlainDate, object"], + [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"], + [Temporal.ZonedDateTime, "Temporal.ZonedDateTime, object"], + [Temporal.ZonedDateTime.prototype, "Temporal.ZonedDateTime.prototype, object"], +]; + +for (const [relativeTo, description] of typeErrorTests) { + assert.throws(TypeError, () => instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }), `${description} is not a valid property bag and does not convert to a string`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-year.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-year.js new file mode 100644 index 0000000000..dc170da196 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-year.js @@ -0,0 +1,19 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: relativeTo with years. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const oneYear = new Temporal.Duration(1); +const days365 = new Temporal.Duration(0, 0, 0, 365); +TemporalHelpers.assertDuration(oneYear.subtract(days365, { relativeTo: Temporal.PlainDate.from("2017-01-01") }), + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "non-leap year"); +TemporalHelpers.assertDuration(oneYear.subtract(days365, { relativeTo: Temporal.PlainDate.from("2016-01-01") }), + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, "leap year"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-nanoseconds-to-days-range-errors.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-nanoseconds-to-days-range-errors.js new file mode 100644 index 0000000000..4310042d73 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-nanoseconds-to-days-range-errors.js @@ -0,0 +1,122 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-temporal.duration.prototype.subtract +description: > + Called abstract operation NanosecondsToDays can throw three different RangeErrors when paired with a ZonedDateTime. +info: | + 6.5.7 NanosecondsToDays ( nanoseconds, relativeTo ) + 19. If days < 0 and sign = 1, throw a RangeError exception. + 20. If days > 0 and sign = -1, throw a RangeError exception. + ... + 22. If nanoseconds > 0 and sign = -1, throw a RangeError exception. +features: [Temporal, BigInt] +includes: [temporalHelpers.js] +---*/ + +const dayNs = 86_400_000_000_000; +const dayDuration = Temporal.Duration.from({ days: 1 }); +const epochInstant = new Temporal.Instant(0n); + +function timeZoneSubstituteValues( + getPossibleInstantsFor, + getOffsetNanosecondsFor +) { + const tz = new Temporal.TimeZone("UTC"); + TemporalHelpers.substituteMethod( + tz, + "getPossibleInstantsFor", + getPossibleInstantsFor + ); + TemporalHelpers.substituteMethod( + tz, + "getOffsetNanosecondsFor", + getOffsetNanosecondsFor + ); + return tz; +} + +// NanosecondsToDays.19: days < 0 and sign = 1 +let zdt = new Temporal.ZonedDateTime( + -1n, // Set DifferenceZonedDateTime _ns1_ + timeZoneSubstituteValues( + [ + TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for first call, AddDuration step 9 + [epochInstant], // Returned for AddDuration step 10, setting _endNs_ -> DifferenceZonedDateTime _ns2_ + [epochInstant], // Returned for NanosecondsToDays step 14, setting _intermediateNs_ + ], + [ + // Behave normally in 4 calls made prior to NanosecondsToDays + TemporalHelpers.SUBSTITUTE_SKIP, + TemporalHelpers.SUBSTITUTE_SKIP, + TemporalHelpers.SUBSTITUTE_SKIP, + TemporalHelpers.SUBSTITUTE_SKIP, + dayNs - 1, // Returned for NanosecondsToDays step 7, setting _startDateTime_ + -dayNs + 1, // Returned for NanosecondsToDays step 11, setting _endDateTime_ + ] + ) +); +assert.throws(RangeError, () => + // Subtracting day from day sets largestUnit to 'day', avoids having any week/month/year components in difference + dayDuration.subtract(dayDuration, { + relativeTo: zdt, + }) +); + +// NanosecondsToDays.20: days > 0 and sign = -1 +zdt = new Temporal.ZonedDateTime( + 1n, // Set DifferenceZonedDateTime _ns1_ + timeZoneSubstituteValues( + [ + TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for first call, AddDuration step 9 + [epochInstant], // Returned for AddDuration step 10, setting _endNs_ -> DifferenceZonedDateTime _ns2_ + [epochInstant], // Returned for NanosecondsToDays step 14, setting _intermediateNs_ + ], + [ + // Behave normally in 4 calls made prior to NanosecondsToDays + TemporalHelpers.SUBSTITUTE_SKIP, + TemporalHelpers.SUBSTITUTE_SKIP, + TemporalHelpers.SUBSTITUTE_SKIP, + TemporalHelpers.SUBSTITUTE_SKIP, + -dayNs + 1, // Returned for NanosecondsToDays step 7, setting _startDateTime_ + dayNs - 1, // Returned for NanosecondsToDays step 11, setting _endDateTime_ + ] + ) +); +assert.throws(RangeError, () => + // Subtracting day from day sets largestUnit to 'day', avoids having any week/month/year components in difference + dayDuration.subtract(dayDuration, { + relativeTo: zdt, + }) +); + +// NanosecondsToDays.22: nanoseconds > 0 and sign = -1 +zdt = new Temporal.ZonedDateTime( + 0n, // Set DifferenceZonedDateTime _ns1_ + timeZoneSubstituteValues( + [ + TemporalHelpers.SUBSTITUTE_SKIP, // Behave normally for first call, AddDuration step 9 + [new Temporal.Instant(-1n)], // Returned for AddDuration step 10, setting _endNs_ -> DifferenceZonedDateTime _ns2_ + [new Temporal.Instant(-2n)], // Returned for NanosecondsToDays step 14, setting _intermediateNs_ + [new Temporal.Instant(-4n)], // Returned for NanosecondsToDays step 18.a, setting _oneDayFartherNs_ + ], + [ + // Behave normally in 4 calls made prior to NanosecondsToDays + TemporalHelpers.SUBSTITUTE_SKIP, + TemporalHelpers.SUBSTITUTE_SKIP, + TemporalHelpers.SUBSTITUTE_SKIP, + TemporalHelpers.SUBSTITUTE_SKIP, + dayNs - 1, // Returned for NanosecondsToDays step 7, setting _startDateTime_ + -dayNs + 1, // Returned for NanosecondsToDays step 11, setting _endDateTime_ + ] + ) +); +assert.throws(RangeError, () => + // Subtracting day from day sets largestUnit to 'day', avoids having any week/month/year components in difference + dayDuration.subtract(dayDuration, { + relativeTo: zdt, + }) +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-negative-epochnanoseconds.js new file mode 100644 index 0000000000..d3e948e0f1 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-negative-epochnanoseconds.js @@ -0,0 +1,27 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch +info: | + sec-temporal-getisopartsfromepoch step 1: + 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 10<sup>6</sup>. + sec-temporal-builtintimezonegetplaindatetimefor step 2: + 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]). +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +const relativeTo = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC"); +const duration = new Temporal.Duration(0, 0, 0, 1); + +// This code path shows up anywhere we convert an exact time, before the Unix +// epoch, with nonzero microseconds or nanoseconds, into a wall time; in this +// case via relativeTo. + +const result = duration.subtract(duration, { relativeTo }); +TemporalHelpers.assertDuration(result, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..377ed25b5c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,20 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); + const other = new Temporal.Duration(0, 3); + const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => duration.subtract(other, { relativeTo })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..82b13321bf --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); + const other = new Temporal.Duration(0, 3); + const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => duration.subtract(other, { relativeTo }), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..f96dc816c6 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,20 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); + const other = new Temporal.Duration(0, 3); + const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => duration.subtract(other, { relativeTo })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..2b24e2f263 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,29 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); + const other = new Temporal.Duration(0, 3); + const relativeTo = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => duration.subtract(other, { relativeTo })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/shell.js new file mode 100644 index 0000000000..eda1477282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/shell.js @@ -0,0 +1,24 @@ +// GENERATED, DO NOT EDIT +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/subclassing-ignored.js new file mode 100644 index 0000000000..46ff031103 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/subclassing-ignored.js @@ -0,0 +1,20 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: Objects of a subclass are never created as return values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.checkSubclassingIgnored( + Temporal.Duration, + [0, 0, 0, 4, 5, 6, 7, 987, 654, 321], + "subtract", + [{ nanoseconds: 1 }], + (result) => TemporalHelpers.assertDuration(result, 0, 0, 0, 4, 5, 6, 7, 987, 654, 320), +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/timezone-getpossibleinstantsfor-iterable.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/timezone-getpossibleinstantsfor-iterable.js new file mode 100644 index 0000000000..d242cdec94 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/timezone-getpossibleinstantsfor-iterable.js @@ -0,0 +1,46 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call +info: | + sec-temporal.duration.prototype.subtract steps 5–6: + 5. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_). + 6. Let _result_ be ? AddDuration(_duration_.[[Years]], [...], _duration_.[[Nanoseconds]], −_other_.[[Years]], [...], −_other_.[[Nanoseconds]], _relativeTo_). + sec-temporal-torelativetemporalobject step 6.d: + d. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_result_.[[Year]], [...], _result_.[[Nanosecond]], _offsetNs_, _timeZone_, *"compatible"*, *"reject"*). + sec-temporal-interpretisodatetimeoffset step 7: + 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_). + sec-temporal-addduration steps 7.d–e and 7.g.i: + d. Let _intermediateNs_ be ? AddZonedDateTime(_relativeTo_.[[Nanoseconds]], _timeZone_, _calendar_, _y1_, [...], _ns1_). + e. Let _endNs_ be ? AddZonedDateTime(_intermediateNs_, _timeZone_, _calendar_, _y2_, [...], _ns2_). + [...] + i. Let _result_ be ? DifferenceZonedDateTime(_relativeTo_.[[Nanoseconds]], _endNs_, _timeZone_, _calendar_, _largestUnit_). + sec-temporal-differencezoneddatetime step 8: + 8. Let _intermediateNs_ be ? AddZonedDateTime(_ns1_, _timeZone_, _calendar_, _dateDifference_.[[Years]], _dateDifference_.[[Months]], _dateDifference_.[[Weeks]], 0, 0, 0, 0, 0, 0, 0). + sec-temporal-addzoneddatetime step 8: + 8. Let _intermediateInstant_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _intermediateDateTime_, *"compatible"*). + sec-temporal-builtintimezonegetinstantfor step 1: + 1. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_). + sec-temporal-getpossibleinstantsfor step 2: + 2. Let _list_ be ? IterableToList(_possibleInstants_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "2000-01-01T09:00:00", // called once on the input relativeTo object + "2001-01-01T09:00:00", // called once on relativeTo plus the receiver + "1999-12-01T09:00:00", // called once on relativeTo plus the receiver minus the argument + "1999-12-01T09:00:00", // called once on relativeTo plus the years, months, and weeks from the difference of relativeTo minus endNs +]; + +TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => { + const duration1 = new Temporal.Duration(1); + const duration2 = new Temporal.Duration(0, 13); + duration1.subtract(duration2, { relativeTo: { year: 2000, month: 1, day: 1, hour: 9, timeZone } }); +}, expected); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/year-zero.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/year-zero.js new file mode 100644 index 0000000000..b7c9d22d71 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/subtract/year-zero.js @@ -0,0 +1,20 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.subtract +description: Negative zero, as an extended year, is rejected +features: [Temporal, arrow-function] +---*/ + +const instance = new Temporal.Duration(1, 0, 0, 1); + +let relativeTo = "-000000-11-04T00:00"; +assert.throws( + RangeError, + () => { instance.subtract(new Temporal.Duration(0, 0, 0, 0, 24), { relativeTo }); }, + "reject minus zero as extended year" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/basic.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/basic.js new file mode 100644 index 0000000000..2c7055f8c9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/basic.js @@ -0,0 +1,243 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tojson +description: Temporal.Duration.prototype.toJSON will return correct iso8601 string for the given duration. +info: | + 1. Let duration be the this value. + 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]). + 3. Return ! TemporalDurationToString(duration.[[Years]], duration.[[Months]], duration.[[Weeks]], duration.[[Days]], duration.[[Hours]], duration.[[Minutes]], duration.[[Seconds]], duration.[[Milliseconds]], duration.[[Microseconds]], duration.[[Nanoseconds]], "auto"). +features: [Temporal] +---*/ + +let d = new Temporal.Duration(); +assert.sameValue(d.toJSON(), "PT0S", "zero duration"); + +d = new Temporal.Duration(1); +assert.sameValue(d.toJSON(), "P1Y", "positive small years"); +d = new Temporal.Duration(-1); +assert.sameValue(d.toJSON(), "-P1Y", "negative small years"); +d = new Temporal.Duration(1234567890); +assert.sameValue(d.toJSON(), "P1234567890Y", "positive large years"); +d = new Temporal.Duration(-1234567890); +assert.sameValue(d.toJSON(), "-P1234567890Y", "negative large years"); + +d = new Temporal.Duration(1, 2); +assert.sameValue(d.toJSON(), "P1Y2M", "positive years and months"); +d = new Temporal.Duration(-1, -2); +assert.sameValue(d.toJSON(), "-P1Y2M", "negative years and months"); +d = new Temporal.Duration(0, 2); +assert.sameValue(d.toJSON(), "P2M", "positive small months"); +d = new Temporal.Duration(0,-2); +assert.sameValue(d.toJSON(), "-P2M", "negative small months"); +d = new Temporal.Duration(0, 1234567890); +assert.sameValue(d.toJSON(), "P1234567890M", "positive large months"); +d = new Temporal.Duration(0,-1234567890); +assert.sameValue(d.toJSON(), "-P1234567890M", "negative large months"); + +d = new Temporal.Duration(1, 2, 3); +assert.sameValue(d.toJSON(), "P1Y2M3W", "positive years, months, weeks"); +d = new Temporal.Duration(-1, -2, -3); +assert.sameValue(d.toJSON(), "-P1Y2M3W", "negative years, months, weeks"); +d = new Temporal.Duration(0, 0, 3); +assert.sameValue(d.toJSON(), "P3W", "positive small weeks"); +d = new Temporal.Duration(0, 0, -3); +assert.sameValue(d.toJSON(), "-P3W", "negative small weeks"); +d = new Temporal.Duration(1, 0, 3); +assert.sameValue(d.toJSON(), "P1Y3W", "positive years and weeks"); +d = new Temporal.Duration(-1, 0, -3); +assert.sameValue(d.toJSON(), "-P1Y3W", "negative years and weeks"); +d = new Temporal.Duration(0, 2, 3); +assert.sameValue(d.toJSON(), "P2M3W", "positive months and weeks"); +d = new Temporal.Duration(0, -2, -3); +assert.sameValue(d.toJSON(), "-P2M3W", "negative months and weeks"); +d = new Temporal.Duration(0, 0, 1234567890); +assert.sameValue(d.toJSON(), "P1234567890W", "positive large weeks"); +d = new Temporal.Duration(0, 0, -1234567890); +assert.sameValue(d.toJSON(), "-P1234567890W", "negative large weeks"); + +d = new Temporal.Duration(1, 2, 3, 4); +assert.sameValue(d.toJSON(), "P1Y2M3W4D", "positive years, months, weeks, days"); +d = new Temporal.Duration(-1, -2, -3, -4); +assert.sameValue(d.toJSON(), "-P1Y2M3W4D", "negative years, months, weeks, days"); +d = new Temporal.Duration(0, 0, 0, 1234567890); +assert.sameValue(d.toJSON(), "P1234567890D", "positive large days"); +d = new Temporal.Duration(0, 0, 0, -1234567890); +assert.sameValue(d.toJSON(), "-P1234567890D", "negative large days"); +d = new Temporal.Duration(0, 0, 0, 4); +assert.sameValue(d.toJSON(), "P4D", "positive small days"); +d = new Temporal.Duration(0, 0, 0, -4); +assert.sameValue(d.toJSON(), "-P4D", "negative small days"); +d = new Temporal.Duration(1, 0, 0, 4); +assert.sameValue(d.toJSON(), "P1Y4D", "positive years and days"); +d = new Temporal.Duration(-1, 0, 0, -4); +assert.sameValue(d.toJSON(), "-P1Y4D", "negative years and days"); +d = new Temporal.Duration(0, 2, 0, 4); +assert.sameValue(d.toJSON(), "P2M4D", "positive months and days"); +d = new Temporal.Duration(0, -2, 0, -4); +assert.sameValue(d.toJSON(), "-P2M4D", "negative months and days"); +d = new Temporal.Duration(0, 0, 3, 4); +assert.sameValue(d.toJSON(), "P3W4D", "positive weeks and days"); +d = new Temporal.Duration(0, 0, -3, -4); +assert.sameValue(d.toJSON(), "-P3W4D", "negative weeks and days"); + +d = new Temporal.Duration(0, 0, 0, 0, 5); +assert.sameValue(d.toJSON(), "PT5H", "positive hours"); +d = new Temporal.Duration(0, 0, 0, 0, -5); +assert.sameValue(d.toJSON(), "-PT5H", "negative hours"); +d = new Temporal.Duration(1, 0, 0, 0, 5); +assert.sameValue(d.toJSON(), "P1YT5H", "positive years and hours"); +d = new Temporal.Duration(-1, 0, 0, 0, -5); +assert.sameValue(d.toJSON(), "-P1YT5H", "negative years and hours"); +d = new Temporal.Duration(0, 2, 0, 0, 5); +assert.sameValue(d.toJSON(), "P2MT5H", "positive months and hours"); +d = new Temporal.Duration(0, -2, 0, 0, -5); +assert.sameValue(d.toJSON(), "-P2MT5H", "negative months and hours"); + +d = new Temporal.Duration(0, 0, 0, 0, 0, 6); +assert.sameValue(d.toJSON(), "PT6M", "positive minutes"); +d = new Temporal.Duration(0, 0, 0, 0, 0, -6); +assert.sameValue(d.toJSON(), "-PT6M", "negative minutes"); +d = new Temporal.Duration(0, 0, 0, 0, 5, 6); +assert.sameValue(d.toJSON(), "PT5H6M", "positive hours and minutes"); +d = new Temporal.Duration(0, 0, 0, 0, -5, -6); +assert.sameValue(d.toJSON(), "-PT5H6M", "negative hours and minutes"); +d = new Temporal.Duration(0, 0, 3, 0, 0, 6); +assert.sameValue(d.toJSON(), "P3WT6M", "positive weeks and minutes"); +d = new Temporal.Duration(0, 0, -3, 0, 0, -6); +assert.sameValue(d.toJSON(), "-P3WT6M", "negative weeks and minutes"); +d = new Temporal.Duration(0, 0, 0, 4, 0, 6); +assert.sameValue(d.toJSON(), "P4DT6M", "positive days and minutes"); +d = new Temporal.Duration(0, 0, 0, -4, 0, -6); +assert.sameValue(d.toJSON(), "-P4DT6M", "negative days and minutes"); + +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 7); +assert.sameValue(d.toJSON(), "PT7S", "positive seconds"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, -7); +assert.sameValue(d.toJSON(), "-PT7S", "negative seconds"); +d = new Temporal.Duration(0, 0, 0, 0, 5, 0, 7); +assert.sameValue(d.toJSON(), "PT5H7S", "positive hours and seconds"); +d = new Temporal.Duration(0, 0, 0, 0, -5, 0, -7); +assert.sameValue(d.toJSON(), "-PT5H7S", "negative hours and seconds"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 6, 7); +assert.sameValue(d.toJSON(), "PT6M7S", "positive minutes and seconds"); +d = new Temporal.Duration(0, 0, 0, 0, 0, -6, -7); +assert.sameValue(d.toJSON(), "-PT6M7S", "negative minutes and seconds"); +d = new Temporal.Duration(0, 0, 0, 0, 5, 6, 7); +assert.sameValue(d.toJSON(), "PT5H6M7S", "positive hours, minutes, seconds"); +d = new Temporal.Duration(0, 0, 0, 0, -5, -6, -7); +assert.sameValue(d.toJSON(), "-PT5H6M7S", "negative hours, minutes, seconds"); +d = new Temporal.Duration(1, 0, 0, 0, 5, 6, 7); +assert.sameValue(d.toJSON(), "P1YT5H6M7S", "positive years, hours, minutes, seconds"); +d = new Temporal.Duration(-1, 0, 0, 0, -5, -6, -7); +assert.sameValue(d.toJSON(), "-P1YT5H6M7S", "negative years, hours, minutes, seconds"); + +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 8); +assert.sameValue(d.toJSON(), "PT0.008S", "positive milliseconds"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, -8); +assert.sameValue(d.toJSON(), "-PT0.008S", "negative milliseconds"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 80); +assert.sameValue(d.toJSON(), "PT0.08S", "positive milliseconds multiple of 10"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, -80); +assert.sameValue(d.toJSON(), "-PT0.08S", "negative milliseconds multiple of 10"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 87); +assert.sameValue(d.toJSON(), "PT0.087S", "positive two-digit milliseconds"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, -87); +assert.sameValue(d.toJSON(), "-PT0.087S", "negative two-digit milliseconds"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 876); +assert.sameValue(d.toJSON(), "PT0.876S", "positive three-digit milliseconds"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, -876); +assert.sameValue(d.toJSON(), "-PT0.876S", "negative three-digit milliseconds"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 876543); +assert.sameValue(d.toJSON(), "PT876.543S", "positive large milliseconds"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, -876543); +assert.sameValue(d.toJSON(), "-PT876.543S", "negative large milliseconds"); + +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 9); +assert.sameValue(d.toJSON(), "PT0.000009S", "positive microseconds"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, -9); +assert.sameValue(d.toJSON(), "-PT0.000009S", "negative microseconds"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 90); +assert.sameValue(d.toJSON(), "PT0.00009S", "positive microseconds multiple of 10"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, -90); +assert.sameValue(d.toJSON(), "-PT0.00009S", "negative microseconds multiple of 10"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 98); +assert.sameValue(d.toJSON(), "PT0.000098S", "positive two-digit microseconds"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, -98); +assert.sameValue(d.toJSON(), "-PT0.000098S", "negative two-digit microseconds"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 900); +assert.sameValue(d.toJSON(), "PT0.0009S", "positive microseconds multiple of 100"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, -900); +assert.sameValue(d.toJSON(), "-PT0.0009S", "negative microseconds multiple of 100"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 987); +assert.sameValue(d.toJSON(), "PT0.000987S", "positive three-digit microseconds"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, -987); +assert.sameValue(d.toJSON(), "-PT0.000987S", "negative three-digit microseconds"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 987654); +assert.sameValue(d.toJSON(), "PT0.987654S", "positive large microseconds"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, -987654); +assert.sameValue(d.toJSON(), "-PT0.987654S", "negative large microseconds"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 987654321); +assert.sameValue(d.toJSON(), "PT987.654321S", "positive larger microseconds"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, -987654321); +assert.sameValue(d.toJSON(), "-PT987.654321S", "negative larger microseconds"); + +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 1); +assert.sameValue(d.toJSON(), "PT0.000000001S", "positive nanoseconds"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, -1); +assert.sameValue(d.toJSON(), "-PT0.000000001S", "negative nanoseconds"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 10); +assert.sameValue(d.toJSON(), "PT0.00000001S", "positive nanoseconds multiple of 10"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, -10); +assert.sameValue(d.toJSON(), "-PT0.00000001S", "negative nanoseconds multiple of 10"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 12); +assert.sameValue(d.toJSON(), "PT0.000000012S", "positive two-digit nanoseconds"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, -12); +assert.sameValue(d.toJSON(), "-PT0.000000012S", "negative two-digit nanoseconds"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 100); +assert.sameValue(d.toJSON(), "PT0.0000001S", "positive nanoseconds multiple of 100"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, -100); +assert.sameValue(d.toJSON(), "-PT0.0000001S", "negative nanoseconds multiple of 100"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 123); +assert.sameValue(d.toJSON(), "PT0.000000123S", "positive three-digit nanoseconds"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, -123); +assert.sameValue(d.toJSON(), "-PT0.000000123S", "negative three-digit nanoseconds"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 123456); +assert.sameValue(d.toJSON(), "PT0.000123456S", "positive large nanoseconds"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, -123456); +assert.sameValue(d.toJSON(), "-PT0.000123456S", "negative large nanoseconds"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 123456789); +assert.sameValue(d.toJSON(), "PT0.123456789S", "positive larger nanoseconds"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, -123456789); +assert.sameValue(d.toJSON(), "-PT0.123456789S", "negative larger nanoseconds"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, 1234567891); +assert.sameValue(d.toJSON(), "PT1.234567891S", "positive even larger nanoseconds"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, 0, -1234567891); +assert.sameValue(d.toJSON(), "-PT1.234567891S", "negative even larger nanoseconds"); + +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 4, 3, 2, 1); +assert.sameValue(d.toJSON(), "PT4.003002001S", "positive seconds and subseconds"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, -4, -3, -2, -1); +assert.sameValue(d.toJSON(), "-PT4.003002001S", "negative seconds and subseconds"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 4, 3, 2, 90001); +assert.sameValue(d.toJSON(), "PT4.003092001S", "positive seconds and large subseconds"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, -4, -3, -2, -90001); +assert.sameValue(d.toJSON(), "-PT4.003092001S", "negative seconds and large subseconds"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, 4, 3, 2, 90080001); +assert.sameValue(d.toJSON(), "PT4.093082001S", "positive seconds and larger subseconds"); +d = new Temporal.Duration(0, 0, 0, 0, 0, 0, -4, -3, -2, -90080001); +assert.sameValue(d.toJSON(), "-PT4.093082001S", "negative seconds and larger subseconds"); + +d = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 8, 9, 1); +assert.sameValue(d.toJSON(), "P1Y2M3W4DT5H6M7.008009001S", "all fields positive"); +d = new Temporal.Duration(-1, -2, -3, -4, -5, -6, -7, -8, -9, -1); +assert.sameValue(d.toJSON(), "-P1Y2M3W4DT5H6M7.008009001S", "all fields negative"); + +d = new Temporal.Duration(1234, 2345, 3456, 4567, 5678, 6789, 7890, 890, 901, 123); +assert.sameValue(d.toJSON(), "P1234Y2345M3456W4567DT5678H6789M7890.890901123S", "all fields large and positive"); +d = new Temporal.Duration(-1234, -2345, -3456, -4567, -5678, -6789, -7890, -890, -901, -123); +assert.sameValue(d.toJSON(), "-P1234Y2345M3456W4567DT5678H6789M7890.890901123S", "all fields large and negative"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/branding.js new file mode 100644 index 0000000000..14a64b3dd3 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tojson +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const toJSON = Temporal.Duration.prototype.toJSON; + +assert.sameValue(typeof toJSON, "function"); + +assert.throws(TypeError, () => toJSON.call(undefined), "undefined"); +assert.throws(TypeError, () => toJSON.call(null), "null"); +assert.throws(TypeError, () => toJSON.call(true), "true"); +assert.throws(TypeError, () => toJSON.call(""), "empty string"); +assert.throws(TypeError, () => toJSON.call(Symbol()), "symbol"); +assert.throws(TypeError, () => toJSON.call(1), "1"); +assert.throws(TypeError, () => toJSON.call({}), "plain object"); +assert.throws(TypeError, () => toJSON.call(Temporal.Duration), "Temporal.Duration"); +assert.throws(TypeError, () => toJSON.call(Temporal.Duration.prototype), "Temporal.Duration.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/builtin.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/builtin.js new file mode 100644 index 0000000000..f7e6f1d7be --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/builtin.js @@ -0,0 +1,36 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tojson +description: > + Tests that Temporal.Duration.prototype.toJSON + meets the requirements for built-in objects defined by the + introduction of chapter 17 of the ECMAScript Language Specification. +info: | + Built-in functions that are not constructors do not have a "prototype" property unless + otherwise specified in the description of a particular function. + + Unless specified otherwise, a built-in object that is callable as a function is a built-in + function object with the characteristics described in 10.3. Unless specified otherwise, the + [[Extensible]] internal slot of a built-in object initially has the value true. + + Unless otherwise specified every built-in function and every built-in constructor has the + Function prototype object [...] as the value of its [[Prototype]] internal slot. +features: [Temporal] +---*/ + +assert.sameValue(Object.isExtensible(Temporal.Duration.prototype.toJSON), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.Duration.prototype.toJSON), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.Duration.prototype.toJSON), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.Duration.prototype.toJSON.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/length.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/length.js new file mode 100644 index 0000000000..677218fef5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/length.js @@ -0,0 +1,28 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2020 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tojson +description: Temporal.Duration.prototype.toJSON.length is 0 +info: | + Every built-in function object, including constructors, has a "length" property whose value is + an integer. Unless otherwise specified, this value is equal to the largest number of named + arguments shown in the subclause headings for the function description. Optional parameters + (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form + «...name») are not included in the default argument count. + + Unless otherwise specified, the "length" property of a built-in function object has the + attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +verifyProperty(Temporal.Duration.prototype.toJSON, "length", { + value: 0, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/name.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/name.js new file mode 100644 index 0000000000..1ba0653776 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/name.js @@ -0,0 +1,26 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tojson +description: Temporal.Duration.prototype.toJSON.name is "toJSON". +info: | + Every built-in function object, including constructors, that is not identified as an anonymous + function has a "name" property whose value is a String. Unless otherwise specified, this value + is the name that is given to the function in this specification. + + Unless otherwise specified, the "name" property of a built-in function object, if it exists, + has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +verifyProperty(Temporal.Duration.prototype.toJSON, "name", { + value: "toJSON", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/negative-components.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/negative-components.js new file mode 100644 index 0000000000..db523b0a66 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/negative-components.js @@ -0,0 +1,14 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tojson +description: Temporal.Duration.toJSON handles negative components +features: [Temporal] +---*/ +const d = new Temporal.Duration(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1); +const expected = "-P1Y1M1W1DT1H1M1.001001001S"; +assert.sameValue(d.toJSON(), expected, "toJSON with negative components"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/not-a-constructor.js new file mode 100644 index 0000000000..7c937be773 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/not-a-constructor.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tojson +description: > + Temporal.Duration.prototype.toJSON does not implement [[Construct]], is not new-able +info: | + Built-in function objects that are not identified as constructors do not implement the + [[Construct]] internal method unless otherwise specified in the description of a particular + function. +includes: [isConstructor.js] +features: [Reflect.construct, Temporal] +---*/ + +assert.throws(TypeError, () => { + new Temporal.Duration.prototype.toJSON(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.Duration.prototype.toJSON), false, + "isConstructor(Temporal.Duration.prototype.toJSON)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/options.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/options.js new file mode 100644 index 0000000000..bf8775a05d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/options.js @@ -0,0 +1,21 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tojson +description: Temporal.Duration.prototype.toJSON does not support options, unlike toString. +features: [Temporal] +---*/ + +let called = 0; +const options = new Proxy({}, { + get() { + ++called; + } +}); +const d = new Temporal.Duration(1, 2); +assert.sameValue(d.toJSON(options), "P1Y2M"); +assert.sameValue(called, 0); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/prop-desc.js new file mode 100644 index 0000000000..66e165392d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tojson +description: The "toJSON" property of Temporal.Duration.prototype +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.Duration.prototype.toJSON, + "function", + "`typeof Duration.prototype.toJSON` is `function`" +); + +verifyProperty(Temporal.Duration.prototype, "toJSON", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/shell.js new file mode 100644 index 0000000000..eda1477282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toJSON/shell.js @@ -0,0 +1,24 @@ +// GENERATED, DO NOT EDIT +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/branding.js new file mode 100644 index 0000000000..90e94458ee --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tolocalestring +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const toLocaleString = Temporal.Duration.prototype.toLocaleString; + +assert.sameValue(typeof toLocaleString, "function"); + +assert.throws(TypeError, () => toLocaleString.call(undefined), "undefined"); +assert.throws(TypeError, () => toLocaleString.call(null), "null"); +assert.throws(TypeError, () => toLocaleString.call(true), "true"); +assert.throws(TypeError, () => toLocaleString.call(""), "empty string"); +assert.throws(TypeError, () => toLocaleString.call(Symbol()), "symbol"); +assert.throws(TypeError, () => toLocaleString.call(1), "1"); +assert.throws(TypeError, () => toLocaleString.call({}), "plain object"); +assert.throws(TypeError, () => toLocaleString.call(Temporal.Duration), "Temporal.Duration"); +assert.throws(TypeError, () => toLocaleString.call(Temporal.Duration.prototype), "Temporal.Duration.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/builtin.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/builtin.js new file mode 100644 index 0000000000..9a0424909d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/builtin.js @@ -0,0 +1,36 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tolocalestring +description: > + Tests that Temporal.Duration.prototype.toLocaleString + meets the requirements for built-in objects defined by the + introduction of chapter 17 of the ECMAScript Language Specification. +info: | + Built-in functions that are not constructors do not have a "prototype" property unless + otherwise specified in the description of a particular function. + + Unless specified otherwise, a built-in object that is callable as a function is a built-in + function object with the characteristics described in 10.3. Unless specified otherwise, the + [[Extensible]] internal slot of a built-in object initially has the value true. + + Unless otherwise specified every built-in function and every built-in constructor has the + Function prototype object [...] as the value of its [[Prototype]] internal slot. +features: [Temporal] +---*/ + +assert.sameValue(Object.isExtensible(Temporal.Duration.prototype.toLocaleString), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.Duration.prototype.toLocaleString), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.Duration.prototype.toLocaleString), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.Duration.prototype.toLocaleString.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/length.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/length.js new file mode 100644 index 0000000000..9317f8dff9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/length.js @@ -0,0 +1,28 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2020 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tolocalestring +description: Temporal.Duration.prototype.toLocaleString.length is 0 +info: | + Every built-in function object, including constructors, has a "length" property whose value is + an integer. Unless otherwise specified, this value is equal to the largest number of named + arguments shown in the subclause headings for the function description. Optional parameters + (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form + «...name») are not included in the default argument count. + + Unless otherwise specified, the "length" property of a built-in function object has the + attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +verifyProperty(Temporal.Duration.prototype.toLocaleString, "length", { + value: 0, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/name.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/name.js new file mode 100644 index 0000000000..46f2f43c35 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/name.js @@ -0,0 +1,26 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tolocalestring +description: Temporal.Duration.prototype.toLocaleString.name is "toLocaleString". +info: | + Every built-in function object, including constructors, that is not identified as an anonymous + function has a "name" property whose value is a String. Unless otherwise specified, this value + is the name that is given to the function in this specification. + + Unless otherwise specified, the "name" property of a built-in function object, if it exists, + has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +verifyProperty(Temporal.Duration.prototype.toLocaleString, "name", { + value: "toLocaleString", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/not-a-constructor.js new file mode 100644 index 0000000000..27abbe9184 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/not-a-constructor.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tolocalestring +description: > + Temporal.Duration.prototype.toLocaleString does not implement [[Construct]], is not new-able +info: | + Built-in function objects that are not identified as constructors do not implement the + [[Construct]] internal method unless otherwise specified in the description of a particular + function. +includes: [isConstructor.js] +features: [Reflect.construct, Temporal] +---*/ + +assert.throws(TypeError, () => { + new Temporal.Duration.prototype.toLocaleString(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.Duration.prototype.toLocaleString), false, + "isConstructor(Temporal.Duration.prototype.toLocaleString)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/prop-desc.js new file mode 100644 index 0000000000..ac70c97ee5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tolocalestring +description: The "toLocaleString" property of Temporal.Duration.prototype +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.Duration.prototype.toLocaleString, + "function", + "`typeof Duration.prototype.toLocaleString` is `function`" +); + +verifyProperty(Temporal.Duration.prototype, "toLocaleString", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/shell.js new file mode 100644 index 0000000000..eda1477282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toLocaleString/shell.js @@ -0,0 +1,24 @@ +// GENERATED, DO NOT EDIT +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/balance.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/balance.js new file mode 100644 index 0000000000..ec3eaa1870 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/balance.js @@ -0,0 +1,27 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tostring +description: Verify that values are balanced correctly. +features: [Temporal] +---*/ + +assert.sameValue( + Temporal.Duration.from({ milliseconds: 3500 }).toString(), + "PT3.5S"); +assert.sameValue( + Temporal.Duration.from({ microseconds: 3500 }).toString(), + "PT0.0035S"); +assert.sameValue( + Temporal.Duration.from({ nanoseconds: 3500 }).toString(), + "PT0.0000035S"); +assert.sameValue( + new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 1111, 1111, 1111).toString(), + "PT1.112112111S"); +assert.sameValue( + Temporal.Duration.from({ seconds: 120, milliseconds: 3500 }).toString(), + "PT123.5S"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/blank-duration-precision.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/blank-duration-precision.js new file mode 100644 index 0000000000..950bbffdd5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/blank-duration-precision.js @@ -0,0 +1,25 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tostring +description: > + Precision is handled correctly for blank durations, whether specified by + fractionalSecondDigits or smallestUnit +features: [Temporal] +---*/ + +const blank = new Temporal.Duration(); + +assert.sameValue(blank.toString({ fractionalSecondDigits: "auto" }), "PT0S"); +assert.sameValue(blank.toString({ fractionalSecondDigits: 0 }), "PT0S"); +assert.sameValue(blank.toString({ fractionalSecondDigits: 2 }), "PT0.00S"); +assert.sameValue(blank.toString({ fractionalSecondDigits: 9 }), "PT0.000000000S"); + +assert.sameValue(blank.toString({ smallestUnit: "seconds" }), "PT0S"); +assert.sameValue(blank.toString({ smallestUnit: "milliseconds" }), "PT0.000S"); +assert.sameValue(blank.toString({ smallestUnit: "microseconds" }), "PT0.000000S"); +assert.sameValue(blank.toString({ smallestUnit: "nanoseconds" }), "PT0.000000000S"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/branding.js new file mode 100644 index 0000000000..f89356bb6a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tostring +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const toString = Temporal.Duration.prototype.toString; + +assert.sameValue(typeof toString, "function"); + +assert.throws(TypeError, () => toString.call(undefined), "undefined"); +assert.throws(TypeError, () => toString.call(null), "null"); +assert.throws(TypeError, () => toString.call(true), "true"); +assert.throws(TypeError, () => toString.call(""), "empty string"); +assert.throws(TypeError, () => toString.call(Symbol()), "symbol"); +assert.throws(TypeError, () => toString.call(1), "1"); +assert.throws(TypeError, () => toString.call({}), "plain object"); +assert.throws(TypeError, () => toString.call(Temporal.Duration), "Temporal.Duration"); +assert.throws(TypeError, () => toString.call(Temporal.Duration.prototype), "Temporal.Duration.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/builtin.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/builtin.js new file mode 100644 index 0000000000..d2c4f65190 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/builtin.js @@ -0,0 +1,36 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tostring +description: > + Tests that Temporal.Duration.prototype.toString + meets the requirements for built-in objects defined by the + introduction of chapter 17 of the ECMAScript Language Specification. +info: | + Built-in functions that are not constructors do not have a "prototype" property unless + otherwise specified in the description of a particular function. + + Unless specified otherwise, a built-in object that is callable as a function is a built-in + function object with the characteristics described in 10.3. Unless specified otherwise, the + [[Extensible]] internal slot of a built-in object initially has the value true. + + Unless otherwise specified every built-in function and every built-in constructor has the + Function prototype object [...] as the value of its [[Prototype]] internal slot. +features: [Temporal] +---*/ + +assert.sameValue(Object.isExtensible(Temporal.Duration.prototype.toString), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.Duration.prototype.toString), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.Duration.prototype.toString), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.Duration.prototype.toString.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-auto.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-auto.js new file mode 100644 index 0000000000..f30df60fc2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-auto.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tostring +description: auto value for fractionalSecondDigits option +features: [Temporal] +---*/ + +const wholeSeconds = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7); +const subSeconds = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 650); + +const tests = [ + [wholeSeconds, "P1Y2M3W4DT5H6M7S"], + [subSeconds, "P1Y2M3W4DT5H6M7.98765S"], +]; + +for (const [duration, expected] of tests) { + assert.sameValue(duration.toString(), expected, "default is to emit seconds and drop trailing zeroes"); + assert.sameValue(duration.toString({ fractionalSecondDigits: "auto" }), expected, "auto is the default"); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-exact-number-of-digits.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-exact-number-of-digits.js new file mode 100644 index 0000000000..2b5545b62a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-exact-number-of-digits.js @@ -0,0 +1,40 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tostring +description: | + The fractionalSecondDigits and smallestUnit options determine the exact number + of digits shown after the decimal separator, no more and no less +info: | + sec-temporaldurationtostring step 21: + 21. If any of _seconds_, _milliseconds_, _microseconds_, and _nanoseconds_ are not 0; or _years_, _months_, _weeks_, _days_, _hours_, and _minutes_ are all 0; or _precision_ is not *"auto"*; then +features: [Temporal] +---*/ + +const threeYears = new Temporal.Duration(3); +assert.sameValue(threeYears.toString({ fractionalSecondDigits: 0 }), "P3YT0S"); +assert.sameValue(threeYears.toString({ smallestUnit: 'seconds' }), "P3YT0S"); +assert.sameValue(threeYears.toString({ smallestUnit: 'milliseconds' }), "P3YT0.000S"); +assert.sameValue(threeYears.toString({ fractionalSecondDigits: 5 }), "P3YT0.00000S"); + +const halfHour = new Temporal.Duration(0, 0, 0, 0, 0, 30); +assert.sameValue(halfHour.toString({ fractionalSecondDigits: 0 }), "PT30M0S"); +assert.sameValue(halfHour.toString({ smallestUnit: 'seconds' }), "PT30M0S"); +assert.sameValue(halfHour.toString({ smallestUnit: 'milliseconds' }), "PT30M0.000S"); +assert.sameValue(halfHour.toString({ fractionalSecondDigits: 5 }), "PT30M0.00000S"); + +const hundredMs = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 100); +assert.sameValue(hundredMs.toString({ fractionalSecondDigits: 0 }), "PT0S"); +assert.sameValue(hundredMs.toString({ smallestUnit: 'seconds' }), "PT0S"); +assert.sameValue(hundredMs.toString({ smallestUnit: 'milliseconds' }), "PT0.100S"); +assert.sameValue(hundredMs.toString({ fractionalSecondDigits: 5 }), "PT0.10000S"); + +const zero = new Temporal.Duration(); +assert.sameValue(zero.toString({ fractionalSecondDigits: 0 }), "PT0S"); +assert.sameValue(zero.toString({ smallestUnit: 'seconds' }), "PT0S"); +assert.sameValue(zero.toString({ smallestUnit: 'milliseconds' }), "PT0.000S"); +assert.sameValue(zero.toString({ fractionalSecondDigits: 5 }), "PT0.00000S"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-invalid-string.js new file mode 100644 index 0000000000..c2d1b2789f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-invalid-string.js @@ -0,0 +1,25 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tostring +description: RangeError thrown when fractionalSecondDigits option not one of the allowed string values +info: | + sec-getstringornumberoption step 4: + 4. If _stringValues_ is not *undefined* and _stringValues_ does not contain an element equal to _value_, throw a *RangeError* exception. + sec-temporal-tosecondsstringprecision step 9: + 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*). + sec-temporal.duration.prototype.tostring step 4: + 4. Let _precision_ be ? ToSecondsStringPrecision(_options_). +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 650, 0); + +for (const fractionalSecondDigits of ["other string", "AUTO", "not-auto", "autos", "auto\0"]) { + assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits }), + `"${fractionalSecondDigits}" is not a valid value for fractionalSecondDigits`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-nan.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-nan.js new file mode 100644 index 0000000000..4f7d771b32 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-nan.js @@ -0,0 +1,23 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tostring +description: RangeError thrown when fractionalSecondDigits option is NaN +info: | + sec-getoption step 8.b: + b. If _value_ is *NaN*, throw a *RangeError* exception. + sec-getstringornumberoption step 2: + 2. Let _value_ be ? GetOption(_options_, _property_, « Number, String », *undefined*, _fallback_). + sec-temporal-tosecondsstringprecision step 9: + 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*). + sec-temporal.duration.prototype.tostring step 4: + 4. Let _precision_ be ? ToSecondsStringPrecision(_options_). +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 650, 0); +assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits: NaN })); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-non-integer.js new file mode 100644 index 0000000000..585ef98b2c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-non-integer.js @@ -0,0 +1,32 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tostring +description: Rounding for fractionalSecondDigits option +info: | + sec-getstringornumberoption step 3.b: + b. Return floor(ℝ(_value_)). + sec-temporal-tosecondsstringprecision step 9: + 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*). + sec-temporal.duration.prototype.tostring step 4: + 4. Let _precision_ be ? ToSecondsStringPrecision(_options_). +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 650, 0); + +let string = duration.toString({ fractionalSecondDigits: 2.5 }); +assert.sameValue(string, "P1Y2M3W4DT5H6M7.98S", "fractionalSecondDigits 2.5 floors to 2"); + +string = duration.toString({ fractionalSecondDigits: 9.7 }); +assert.sameValue(string, "P1Y2M3W4DT5H6M7.987650000S", "fractionalSecondDigits 9.7 floors to 9 and is not out of range"); + +assert.throws( + RangeError, + () => duration.toString({ fractionalSecondDigits: -0.6 }), + "fractionalSecondDigits -0.6 floors to -1 and is out of range" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-number.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-number.js new file mode 100644 index 0000000000..96bdbcd6ca --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-number.js @@ -0,0 +1,31 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tostring +description: Number for fractionalSecondDigits option +features: [Temporal] +---*/ + +const wholeSeconds = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7); +const subSeconds = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 650); + +assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 0 }), "P1Y2M3W4DT5H6M7S", + "truncates 4 decimal places to 0"); +assert.sameValue(wholeSeconds.toString({ fractionalSecondDigits: 2 }), "P1Y2M3W4DT5H6M7.00S", + "pads whole seconds to 2 decimal places"); +assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 2 }), "P1Y2M3W4DT5H6M7.98S", + "truncates 4 decimal places to 2"); +assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 3 }), "P1Y2M3W4DT5H6M7.987S", + "truncates 4 decimal places to 3"); +assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 6 }), "P1Y2M3W4DT5H6M7.987650S", + "pads 4 decimal places to 6"); +assert.sameValue(wholeSeconds.toString({ fractionalSecondDigits: 7 }), "P1Y2M3W4DT5H6M7.0000000S", + "pads whole seconds to 7 decimal places"); +assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 7 }), "P1Y2M3W4DT5H6M7.9876500S", + "pads 4 decimal places to 7"); +assert.sameValue(subSeconds.toString({ fractionalSecondDigits: 9 }), "P1Y2M3W4DT5H6M7.987650000S", + "pads 4 decimal places to 9"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-out-of-range.js new file mode 100644 index 0000000000..307beed884 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-out-of-range.js @@ -0,0 +1,29 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tostring +description: RangeError thrown when fractionalSecondDigits option out of range +info: | + sec-getstringornumberoption step 3.a: + a. If _value_ < _minimum_ or _value_ > _maximum_, throw a *RangeError* exception. + sec-temporal-tosecondsstringprecision step 9: + 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*). + sec-temporal.duration.prototype.tostring step 4: + 4. Let _precision_ be ? ToSecondsStringPrecision(_options_). +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 650, 0); + +assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits: -Infinity }), + "−∞ is out of range for fractionalSecondDigits"); +assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits: -1 }), + "−1 is out of range for fractionalSecondDigits"); +assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits: 10 }), + "10 is out of range for fractionalSecondDigits"); +assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits: Infinity }), + "∞ is out of range for fractionalSecondDigits"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-undefined.js new file mode 100644 index 0000000000..0059bbb420 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-undefined.js @@ -0,0 +1,39 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tostring +description: Fallback value for fractionalSecondDigits option +info: | + sec-getoption step 3: + 3. If _value_ is *undefined*, return _fallback_. + sec-getstringornumberoption step 2: + 2. Let _value_ be ? GetOption(_options_, _property_, « Number, String », *undefined*, _fallback_). + sec-temporal-tosecondsstringprecision step 9: + 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*). + sec-temporal.duration.prototype.tostring step 4: + 4. Let _precision_ be ? ToSecondsStringPrecision(_options_). +features: [Temporal] +---*/ + +const wholeSeconds = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7); +const subSeconds = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 650); + +const tests = [ + [wholeSeconds, "P1Y2M3W4DT5H6M7S"], + [subSeconds, "P1Y2M3W4DT5H6M7.98765S"], +]; + +for (const [duration, expected] of tests) { + const explicit = duration.toString({ fractionalSecondDigits: undefined }); + assert.sameValue(explicit, expected, "default fractionalSecondDigits is auto (property present but undefined)"); + + const implicit = duration.toString({}); + assert.sameValue(implicit, expected, "default fractionalSecondDigits is auto (property not present)"); + + const lambda = duration.toString(() => {}); + assert.sameValue(lambda, expected, "default fractionalSecondDigits is auto (property not present, function object)"); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-wrong-type.js new file mode 100644 index 0000000000..a4bd73020d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/fractionalseconddigits-wrong-type.js @@ -0,0 +1,50 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tostring +description: Type conversions for fractionalSecondDigits option +info: | + sec-getoption steps 8–9: + 8. Else if _type_ is Number, then + a. Set _value_ to ? ToNumber(value). + b. ... + 9. Else, + a. Set _value_ to ? ToString(value). + sec-getstringornumberoption step 2: + 2. Let _value_ be ? GetOption(_options_, _property_, « Number, String », *undefined*, _fallback_). + sec-temporal-tosecondsstringprecision step 9: + 9. Let _digits_ be ? GetStringOrNumberOption(_normalizedOptions_, *"fractionalSecondDigits"*, « *"auto"* », 0, 9, *"auto"*). + sec-temporal.duration.prototype.tostring step 4: + 4. Let _precision_ be ? ToSecondsStringPrecision(_options_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 650, 0); + +assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits: null }), + "null is not a number and converts to the string 'null' which is not valid for fractionalSecondDigits"); +assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits: true }), + "true is not a number and converts to the string 'true' which is not valid for fractionalSecondDigits"); +assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits: false }), + "false is not a number and converts to the string 'false' which is not valid for fractionalSecondDigits"); +assert.throws(TypeError, () => duration.toString({ fractionalSecondDigits: Symbol() }), + "symbols are not numbers and cannot convert to strings"); +assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits: 2n }), + "bigints are not numbers and convert to strings which are not valid for fractionalSecondDigits"); +assert.throws(RangeError, () => duration.toString({ fractionalSecondDigits: {} }), + "plain objects are not numbers and convert to strings which are not valid for fractionalSecondDigits"); + +const expected = [ + "get fractionalSecondDigits.toString", + "call fractionalSecondDigits.toString", +]; +const actual = []; +const observer = TemporalHelpers.toPrimitiveObserver(actual, "auto", "fractionalSecondDigits"); +const result = duration.toString({ fractionalSecondDigits: observer }); +assert.sameValue(result, "P1Y2M3W4DT5H6M7.98765S", "object with toString uses toString return value"); +assert.compareArray(actual, expected, "object with toString calls toString and not valueOf"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/length.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/length.js new file mode 100644 index 0000000000..9eada90fcd --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/length.js @@ -0,0 +1,28 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2020 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tostring +description: Temporal.Duration.prototype.toString.length is 0 +info: | + Every built-in function object, including constructors, has a "length" property whose value is + an integer. Unless otherwise specified, this value is equal to the largest number of named + arguments shown in the subclause headings for the function description. Optional parameters + (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form + «...name») are not included in the default argument count. + + Unless otherwise specified, the "length" property of a built-in function object has the + attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +verifyProperty(Temporal.Duration.prototype.toString, "length", { + value: 0, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/name.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/name.js new file mode 100644 index 0000000000..0f7b7d100a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/name.js @@ -0,0 +1,26 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tostring +description: Temporal.Duration.prototype.toString.name is "toString". +info: | + Every built-in function object, including constructors, that is not identified as an anonymous + function has a "name" property whose value is a String. Unless otherwise specified, this value + is the name that is given to the function in this specification. + + Unless otherwise specified, the "name" property of a built-in function object, if it exists, + has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +verifyProperty(Temporal.Duration.prototype.toString, "name", { + value: "toString", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/negative-components.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/negative-components.js new file mode 100644 index 0000000000..fe0cabd988 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/negative-components.js @@ -0,0 +1,41 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tostring +description: Temporal.Duration.toString handles negative components +features: [Temporal] +---*/ +assert.sameValue( + new Temporal.Duration(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1).toString(), + "-P1Y1M1W1DT1H1M1.001001001S"); +assert.sameValue( + Temporal.Duration.from({ milliseconds: -250 }).toString(), + "-PT0.25S"); +assert.sameValue( + Temporal.Duration.from({ milliseconds: -3500 }).toString(), + "-PT3.5S"); +assert.sameValue( + Temporal.Duration.from({ microseconds: -250 }).toString(), + "-PT0.00025S"); +assert.sameValue( + Temporal.Duration.from({ microseconds: -3500 }).toString(), + "-PT0.0035S"); +assert.sameValue( + Temporal.Duration.from({ nanoseconds: -250 }).toString(), + "-PT0.00000025S"); +assert.sameValue( + Temporal.Duration.from({ nanoseconds: -3500 }).toString(), + "-PT0.0000035S"); +assert.sameValue( + new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, -1111, -1111, -1111).toString(), + "-PT1.112112111S"); +assert.sameValue( + Temporal.Duration.from({ seconds: -120, milliseconds: -3500 }).toString(), + "-PT123.5S"); +assert.sameValue( + Temporal.Duration.from({ weeks: -1, days: -1 }).toString(), + "-P1W1D"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/not-a-constructor.js new file mode 100644 index 0000000000..c170225343 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/not-a-constructor.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tostring +description: > + Temporal.Duration.prototype.toString does not implement [[Construct]], is not new-able +info: | + Built-in function objects that are not identified as constructors do not implement the + [[Construct]] internal method unless otherwise specified in the description of a particular + function. +includes: [isConstructor.js] +features: [Reflect.construct, Temporal] +---*/ + +assert.throws(TypeError, () => { + new Temporal.Duration.prototype.toString(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.Duration.prototype.toString), false, + "isConstructor(Temporal.Duration.prototype.toString)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/options-object.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/options-object.js new file mode 100644 index 0000000000..1580ee1e02 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/options-object.js @@ -0,0 +1,25 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tostring +description: Empty or a function object may be used as options +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(0, 0, 0, 0, 1); + +const result1 = instance.toString({}); +assert.sameValue( + result1, "PT1H", + "options may be an empty plain object" +); + +const result2 = instance.toString(() => {}); +assert.sameValue( + result2, "PT1H", + "options may be a function object" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/options-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/options-undefined.js new file mode 100644 index 0000000000..e59cb12a47 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/options-undefined.js @@ -0,0 +1,19 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tostring +description: Verify that undefined options are handled correctly. +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 650, 0); + +const explicit = duration.toString(undefined); +assert.sameValue(explicit, "P1Y2M3W4DT5H6M7.98765S", "default precision is auto, and rounding is trunc"); + +const implicit = duration.toString(); +assert.sameValue(implicit, "P1Y2M3W4DT5H6M7.98765S", "default precision is auto, and rounding is trunc"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/options-wrong-type.js new file mode 100644 index 0000000000..f1ccff62f4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/options-wrong-type.js @@ -0,0 +1,26 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tostring +description: TypeError thrown when options argument is a primitive +features: [BigInt, Symbol, Temporal] +---*/ + +const badOptions = [ + null, + true, + "some string", + Symbol(), + 1, + 2n, +]; + +const instance = new Temporal.Duration(0, 0, 0, 0, 1); +for (const value of badOptions) { + assert.throws(TypeError, () => instance.toString(value), + `TypeError on wrong options type ${typeof value}`); +}; + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/order-of-operations.js new file mode 100644 index 0000000000..e4adef5706 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/order-of-operations.js @@ -0,0 +1,49 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tostring +description: Properties on objects passed to toString() are accessed in the correct order +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "get options.fractionalSecondDigits", + "get options.fractionalSecondDigits.toString", + "call options.fractionalSecondDigits.toString", + "get options.roundingMode", + "get options.roundingMode.toString", + "call options.roundingMode.toString", + "get options.smallestUnit", +]; +const actual = []; + +const instance = new Temporal.Duration(1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + +const expectedForSmallestUnit = expected.concat([ + "get options.smallestUnit.toString", + "call options.smallestUnit.toString", +]); + +instance.toString( + TemporalHelpers.propertyBagObserver(actual, { + fractionalSecondDigits: "auto", + roundingMode: "halfExpand", + smallestUnit: "millisecond", + }, "options"), +); +assert.compareArray(actual, expectedForSmallestUnit, "order of operations"); +actual.splice(0); // clear + +instance.toString( + TemporalHelpers.propertyBagObserver(actual, { + fractionalSecondDigits: "auto", + roundingMode: "halfExpand", + smallestUnit: undefined, + }, "options"), +); +assert.compareArray(actual, expected, "order of operations with smallestUnit undefined"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/precision-exact-mathematical-values.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/precision-exact-mathematical-values.js new file mode 100644 index 0000000000..10eef2855d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/precision-exact-mathematical-values.js @@ -0,0 +1,39 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-temporaldurationtostring +description: > + Duration components are precise mathematical integers. +info: | + TemporalDurationToString ( years, months, weeks, days, hours, minutes, seconds, milliseconds, + microseconds, nanoseconds, precision ) + ... + 2. Set microseconds to microseconds + RoundTowardsZero(nanoseconds / 1000). + 3. Set nanoseconds to remainder(nanoseconds, 1000). + 4. Set milliseconds to milliseconds + RoundTowardsZero(microseconds / 1000). + 5. Set microseconds to remainder(microseconds, 1000). + 6. Set seconds to seconds + RoundTowardsZero(milliseconds / 1000). + 7. Set milliseconds to remainder(milliseconds, 1000). + ... +features: [Temporal] +---*/ + +{ + let d = Temporal.Duration.from({microseconds: 10000000000000004000, nanoseconds: 1000}); + + // "PT10000000000000.004096S" with float64. + // "PT10000000000000.004097S" with exact precision. + assert.sameValue(d.toString(), "PT10000000000000.004097S"); +} + +{ + let d = Temporal.Duration.from({seconds: 10000000000000004000, microseconds: 1000}); + + // "PT10000000000000004000.001S" with float64. + // "PT10000000000000004096.001S" with exact precision. + assert.sameValue(d.toString(), "PT10000000000000004096.001S"); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/precision-formatted-as-decimal-number.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/precision-formatted-as-decimal-number.js new file mode 100644 index 0000000000..8d99fd620d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/precision-formatted-as-decimal-number.js @@ -0,0 +1,47 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal-temporaldurationtostring +description: > + Duration components are formatted as precise decimal numbers. +info: | + TemporalDurationToString ( years, months, weeks, days, hours, minutes, seconds, milliseconds, + microseconds, nanoseconds, precision ) + ... + 9. If years is not 0, then + a. Set datePart to the string concatenation of abs(years) formatted as a decimal number and + the code unit 0x0059 (LATIN CAPITAL LETTER Y). + 10. If months is not 0, then + a. Set datePart to the string concatenation of datePart, abs(months) formatted as a decimal + number, and the code unit 0x004D (LATIN CAPITAL LETTER M). + If weeks is not 0, then + a. Set datePart to the string concatenation of datePart, abs(weeks) formatted as a decimal + number, and the code unit 0x0057 (LATIN CAPITAL LETTER W). + ... +features: [Temporal] +---*/ + +{ + let d = Temporal.Duration.from({weeks: 10000000000000004000}); + + // Number(10000000000000004000).toString() is "10000000000000004000". + assert.sameValue(d.toString(), "P10000000000000004096W"); +} + +{ + let d = Temporal.Duration.from({months: 9e59}); + + // Number(9e+59).toString() is "9e+59". + assert.sameValue(d.toString(), "P899999999999999918767229449717619953810131273674690656206848M"); +} + +{ + let d = Temporal.Duration.from({years: Number.MAX_VALUE}); + + // Number.MAX_VALUE.toString() is "1.7976931348623157e+308". + assert.sameValue(d.toString(), "P179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368Y"); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/precision.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/precision.js new file mode 100644 index 0000000000..3a7c0b5767 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/precision.js @@ -0,0 +1,22 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tostring +description: toString() produces a fractional part of the correct length +features: [Temporal] +---*/ + +const { Duration } = Temporal; + +const durationString = 'PT0.084000159S'; +const duration = Duration.from(durationString); +const precisionString = duration.toString({ + smallestUnit: 'milliseconds' +}); + +assert.sameValue(durationString, duration.toString()); +assert.sameValue(precisionString, "PT0.084S"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/prop-desc.js new file mode 100644 index 0000000000..4064467675 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tostring +description: The "toString" property of Temporal.Duration.prototype +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.Duration.prototype.toString, + "function", + "`typeof Duration.prototype.toString` is `function`" +); + +verifyProperty(Temporal.Duration.prototype, "toString", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-ceil.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-ceil.js new file mode 100644 index 0000000000..ccc6d376dc --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-ceil.js @@ -0,0 +1,37 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tostring +description: ceil value for roundingMode option +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 123, 987, 500); + +const result1 = duration.toString({ smallestUnit: "microsecond", roundingMode: "ceil" }); +assert.sameValue(result1, "P1Y2M3W4DT5H6M7.123988S", + "roundingMode is ceil (with 6 digits from smallestUnit)"); + +const result2 = duration.toString({ fractionalSecondDigits: 6, roundingMode: "ceil" }); +assert.sameValue(result2, "P1Y2M3W4DT5H6M7.123988S", + "roundingMode is ceil (with 6 digits from fractionalSecondDigits)"); + +const result3 = duration.toString({ smallestUnit: "millisecond", roundingMode: "ceil" }); +assert.sameValue(result3, "P1Y2M3W4DT5H6M7.124S", + "roundingMode is ceil (with 3 digits from smallestUnit)"); + +const result4 = duration.toString({ fractionalSecondDigits: 3, roundingMode: "ceil" }); +assert.sameValue(result4, "P1Y2M3W4DT5H6M7.124S", + "roundingMode is ceil (with 3 digits from fractionalSecondDigits)"); + +const result5 = duration.toString({ smallestUnit: "second", roundingMode: "ceil" }); +assert.sameValue(result5, "P1Y2M3W4DT5H6M8S", + "roundingMode is ceil (with 0 digits from smallestUnit)"); + +const result6 = duration.toString({ fractionalSecondDigits: 0, roundingMode: "ceil" }); +assert.sameValue(result6, "P1Y2M3W4DT5H6M8S", + "roundingMode is ceil (with 0 digits from fractionalSecondDigits)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-floor.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-floor.js new file mode 100644 index 0000000000..2f2173cb88 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-floor.js @@ -0,0 +1,37 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tostring +description: floor value for roundingMode option +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 123, 987, 500); + +const result1 = duration.toString({ smallestUnit: "microsecond", roundingMode: "floor" }); +assert.sameValue(result1, "P1Y2M3W4DT5H6M7.123987S", + "roundingMode is floor (with 6 digits from smallestUnit)"); + +const result2 = duration.toString({ fractionalSecondDigits: 6, roundingMode: "floor" }); +assert.sameValue(result2, "P1Y2M3W4DT5H6M7.123987S", + "roundingMode is floor (with 6 digits from fractionalSecondDigits)"); + +const result3 = duration.toString({ smallestUnit: "millisecond", roundingMode: "floor" }); +assert.sameValue(result3, "P1Y2M3W4DT5H6M7.123S", + "roundingMode is floor (with 3 digits from smallestUnit)"); + +const result4 = duration.toString({ fractionalSecondDigits: 3, roundingMode: "floor" }); +assert.sameValue(result4, "P1Y2M3W4DT5H6M7.123S", + "roundingMode is floor (with 3 digits from fractionalSecondDigits)"); + +const result5 = duration.toString({ smallestUnit: "second", roundingMode: "floor" }); +assert.sameValue(result5, "P1Y2M3W4DT5H6M7S", + "roundingMode is floor (with 0 digits from smallestUnit)"); + +const result6 = duration.toString({ fractionalSecondDigits: 0, roundingMode: "floor" }); +assert.sameValue(result6, "P1Y2M3W4DT5H6M7S", + "roundingMode is floor (with 0 digits from fractionalSecondDigits)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-halfExpand.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-halfExpand.js new file mode 100644 index 0000000000..81f89c060c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-halfExpand.js @@ -0,0 +1,37 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tostring +description: halfExpand value for roundingMode option +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 123, 987, 500); + +const result1 = duration.toString({ smallestUnit: "microsecond", roundingMode: "halfExpand" }); +assert.sameValue(result1, "P1Y2M3W4DT5H6M7.123988S", + "roundingMode is halfExpand (with 6 digits from smallestUnit)"); + +const result2 = duration.toString({ fractionalSecondDigits: 6, roundingMode: "halfExpand" }); +assert.sameValue(result2, "P1Y2M3W4DT5H6M7.123988S", + "roundingMode is halfExpand (with 6 digits from fractionalSecondDigits)"); + +const result3 = duration.toString({ smallestUnit: "millisecond", roundingMode: "halfExpand" }); +assert.sameValue(result3, "P1Y2M3W4DT5H6M7.124S", + "roundingMode is halfExpand (with 3 digits from smallestUnit)"); + +const result4 = duration.toString({ fractionalSecondDigits: 3, roundingMode: "halfExpand" }); +assert.sameValue(result4, "P1Y2M3W4DT5H6M7.124S", + "roundingMode is halfExpand (with 3 digits from fractionalSecondDigits)"); + +const result5 = duration.toString({ smallestUnit: "second", roundingMode: "halfExpand" }); +assert.sameValue(result5, "P1Y2M3W4DT5H6M7S", + "roundingMode is halfExpand (with 0 digits from smallestUnit)"); + +const result6 = duration.toString({ fractionalSecondDigits: 0, roundingMode: "halfExpand" }); +assert.sameValue(result6, "P1Y2M3W4DT5H6M7S", + "roundingMode is halfExpand (with 0 digits from fractionalSecondDigits)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-invalid-string.js new file mode 100644 index 0000000000..d044d8bec9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-invalid-string.js @@ -0,0 +1,16 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tostring +description: RangeError thrown when roundingMode option not one of the allowed string values +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 987, 500); +for (const roundingMode of ["other string", "cile", "CEIL", "ce\u0131l", "auto", "halfexpand", "floor\0"]) { + assert.throws(RangeError, () => duration.toString({ smallestUnit: "microsecond", roundingMode })); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-trunc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-trunc.js new file mode 100644 index 0000000000..f93512994d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-trunc.js @@ -0,0 +1,37 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tostring +description: trunc value for roundingMode option +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 123, 987, 500); + +const result1 = duration.toString({ smallestUnit: "microsecond", roundingMode: "trunc" }); +assert.sameValue(result1, "P1Y2M3W4DT5H6M7.123987S", + "roundingMode is trunc (with 6 digits from smallestUnit)"); + +const result2 = duration.toString({ fractionalSecondDigits: 6, roundingMode: "trunc" }); +assert.sameValue(result2, "P1Y2M3W4DT5H6M7.123987S", + "roundingMode is trunc (with 6 digits from fractionalSecondDigits)"); + +const result3 = duration.toString({ smallestUnit: "millisecond", roundingMode: "trunc" }); +assert.sameValue(result3, "P1Y2M3W4DT5H6M7.123S", + "roundingMode is trunc (with 3 digits from smallestUnit)"); + +const result4 = duration.toString({ fractionalSecondDigits: 3, roundingMode: "trunc" }); +assert.sameValue(result4, "P1Y2M3W4DT5H6M7.123S", + "roundingMode is trunc (with 3 digits from fractionalSecondDigits)"); + +const result5 = duration.toString({ smallestUnit: "second", roundingMode: "trunc" }); +assert.sameValue(result5, "P1Y2M3W4DT5H6M7S", + "roundingMode is trunc (with 0 digits from smallestUnit)"); + +const result6 = duration.toString({ fractionalSecondDigits: 0, roundingMode: "trunc" }); +assert.sameValue(result6, "P1Y2M3W4DT5H6M7S", + "roundingMode is trunc (with 0 digits from fractionalSecondDigits)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-undefined.js new file mode 100644 index 0000000000..8244b34fa2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-undefined.js @@ -0,0 +1,28 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tostring +description: Fallback value for roundingMode option +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 987, 500); + +const explicit1 = duration.toString({ smallestUnit: "microsecond", roundingMode: undefined }); +assert.sameValue(explicit1, "PT12H34M56.123987S", "default roundingMode is trunc"); +const implicit1 = duration.toString({ smallestUnit: "microsecond" }); +assert.sameValue(implicit1, "PT12H34M56.123987S", "default roundingMode is trunc"); + +const explicit2 = duration.toString({ smallestUnit: "millisecond", roundingMode: undefined }); +assert.sameValue(explicit2, "PT12H34M56.123S", "default roundingMode is trunc"); +const implicit2 = duration.toString({ smallestUnit: "millisecond" }); +assert.sameValue(implicit2, "PT12H34M56.123S", "default roundingMode is trunc"); + +const explicit3 = duration.toString({ smallestUnit: "second", roundingMode: undefined }); +assert.sameValue(explicit3, "PT12H34M56S", "default roundingMode is trunc"); +const implicit3 = duration.toString({ smallestUnit: "second" }); +assert.sameValue(implicit3, "PT12H34M56S", "default roundingMode is trunc"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-wrong-type.js new file mode 100644 index 0000000000..8bd76f27b7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/roundingmode-wrong-type.js @@ -0,0 +1,18 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tostring +description: Type conversions for roundingMode option +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 987, 500); +TemporalHelpers.checkStringOptionWrongType("roundingMode", "trunc", + (roundingMode) => duration.toString({ smallestUnit: "microsecond", roundingMode }), + (result, descr) => assert.sameValue(result, "PT12H34M56.123987S", descr), +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/shell.js new file mode 100644 index 0000000000..eda1477282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/shell.js @@ -0,0 +1,24 @@ +// GENERATED, DO NOT EDIT +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-fractionalseconddigits.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-fractionalseconddigits.js new file mode 100644 index 0000000000..50ba85cac8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-fractionalseconddigits.js @@ -0,0 +1,32 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tostring +description: fractionalSecondDigits option is not used with smallestUnit present +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 789, 999, 999); +const tests = [ + ["second", "P1Y2M3W4DT5H6M7S"], + ["millisecond", "P1Y2M3W4DT5H6M7.789S"], + ["microsecond", "P1Y2M3W4DT5H6M7.789999S"], + ["nanosecond", "P1Y2M3W4DT5H6M7.789999999S"], +]; + +for (const [smallestUnit, expected] of tests) { + const string = duration.toString({ + smallestUnit, + fractionalSecondDigits: 5, + }); + assert.sameValue(string, expected, `smallestUnit: "${smallestUnit}" overrides fractionalSecondDigits`); +} + +assert.throws(RangeError, () => duration.toString({ + smallestUnit: "hour", + fractionalSecondDigits: 5, +}), "hour is an invalid smallestUnit but still overrides fractionalSecondDigits"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-invalid-string.js new file mode 100644 index 0000000000..4814101a35 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-invalid-string.js @@ -0,0 +1,42 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tostring +description: RangeError thrown when smallestUnit option not one of the allowed string values +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 987, 500); +const badValues = [ + "era", + "eraYear", + "year", + "month", + "week", + "day", + "hour", + "minute", + "millisecond\0", + "mill\u0131second", + "SECOND", + "eras", + "eraYears", + "years", + "months", + "weeks", + "days", + "hours", + "minutes", + "milliseconds\0", + "mill\u0131seconds", + "SECONDS", + "other string", +]; +for (const smallestUnit of badValues) { + assert.throws(RangeError, () => duration.toString({ smallestUnit }), + `"${smallestUnit}" is not a valid value for smallest unit`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-plurals-accepted.js new file mode 100644 index 0000000000..b3830900e9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-plurals-accepted.js @@ -0,0 +1,21 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tostring +description: Plural units are accepted as well for the smallestUnit option +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); +const validUnits = [ + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +TemporalHelpers.checkPluralUnitsAccepted((smallestUnit) => duration.toString({ smallestUnit }), validUnits); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-undefined.js new file mode 100644 index 0000000000..c84b941e49 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-undefined.js @@ -0,0 +1,23 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tostring +description: Fallback value for smallestUnit option +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 987, 500); + +const explicit1 = duration.toString({ smallestUnit: undefined, fractionalSecondDigits: 6 }); +assert.sameValue(explicit1, "PT12H34M56.123987S", "default smallestUnit defers to fractionalSecondDigits"); +const implicit1 = duration.toString({ fractionalSecondDigits: 6 }); +assert.sameValue(implicit1, "PT12H34M56.123987S", "default smallestUnit defers to fractionalSecondDigits"); + +const explicit2 = duration.toString({ smallestUnit: undefined, fractionalSecondDigits: 3 }); +assert.sameValue(explicit2, "PT12H34M56.123S", "default smallestUnit defers to fractionalSecondDigits"); +const implicit2 = duration.toString({ fractionalSecondDigits: 3 }); +assert.sameValue(implicit2, "PT12H34M56.123S", "default smallestUnit defers to fractionalSecondDigits"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-valid-units.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-valid-units.js new file mode 100644 index 0000000000..8be618d6b7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-valid-units.js @@ -0,0 +1,57 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tostring +description: Valid units for the smallestUnit option +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); + +function test(instance, expectations, description) { + for (const [smallestUnit, expectedResult] of expectations) { + assert.sameValue(instance.toString({ smallestUnit }), expectedResult, + `${description} with smallestUnit "${smallestUnit}"`); + } +} + +test( + duration, + [ + ["seconds", "P1Y2M3W4DT5H6M7S"], + ["milliseconds", "P1Y2M3W4DT5H6M7.987S"], + ["microseconds", "P1Y2M3W4DT5H6M7.987654S"], + ["nanoseconds", "P1Y2M3W4DT5H6M7.987654321S"], + ], + "subseconds toString" +); + +test( + new Temporal.Duration(1, 2, 3, 4, 5, 6, 7), + [ + ["seconds", "P1Y2M3W4DT5H6M7S"], + ["milliseconds", "P1Y2M3W4DT5H6M7.000S"], + ["microseconds", "P1Y2M3W4DT5H6M7.000000S"], + ["nanoseconds", "P1Y2M3W4DT5H6M7.000000000S"], + ], + "whole seconds toString" +); + +const notValid = [ + "era", + "year", + "month", + "week", + "day", + "hour", + "minute", +]; + +notValid.forEach((smallestUnit) => { + assert.throws(RangeError, () => duration.toString({ smallestUnit }), + `"${smallestUnit}" is not a valid unit for the smallestUnit option`); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-wrong-type.js new file mode 100644 index 0000000000..45bef000cb --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/smallestunit-wrong-type.js @@ -0,0 +1,18 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.tostring +description: Type conversions for smallestUnit option +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(0, 0, 0, 0, 12, 34, 56, 123, 987, 500); +TemporalHelpers.checkStringOptionWrongType("smallestUnit", "microsecond", + (smallestUnit) => duration.toString({ smallestUnit }), + (result, descr) => assert.sameValue(result, "PT12H34M56.123987S", descr), +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/throws-when-rounded-duration-is-invalid.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/throws-when-rounded-duration-is-invalid.js new file mode 100644 index 0000000000..fb803a9214 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toString/throws-when-rounded-duration-is-invalid.js @@ -0,0 +1,48 @@ +// |reftest| skip -- Temporal is not supported +// 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.tostring +description: > + RoundDuration throws when the rounded duration can't be represented using + float64-representable integers. +info: | + Temporal.Duration.prototype.toString ( [ options ] ) + + ... + 7. Let result be (? RoundDuration(...)).[[DurationRecord]]. + ... + + RoundDuration ( years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, + nanoseconds, increment, unit, roundingMode [ , relativeTo ] ) + + ... + 15. Else if unit is "second", then + a. Set seconds to RoundNumberToIncrement(fractionalSeconds, increment, roundingMode). + b. Set remainder to fractionalSeconds - seconds. + c. Set milliseconds, microseconds, and nanoseconds to 0. + ... + 19. Let duration be ? CreateDurationRecord(years, months, weeks, days, hours, minutes, seconds, + milliseconds, microseconds, nanoseconds). + ... + + CreateDurationRecord ( years, months, weeks, days, hours, minutes, seconds, milliseconds, + microseconds, nanoseconds ) + + 1. If ! IsValidDuration(years, months, weeks, days, hours, minutes, seconds, milliseconds, + microseconds, nanoseconds) is false, throw a RangeError exception. + ... +features: [Temporal] +---*/ + +var duration = Temporal.Duration.from({ + seconds: Number.MAX_VALUE, + milliseconds: Number.MAX_VALUE, +}); + +var options = {smallestUnit: "seconds"}; + +assert.throws(RangeError, () => duration.toString(options)); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toStringTag/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toStringTag/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toStringTag/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toStringTag/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toStringTag/prop-desc.js new file mode 100644 index 0000000000..539a808228 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toStringTag/prop-desc.js @@ -0,0 +1,19 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype-@@tostringtag +description: The @@toStringTag property of Temporal.Duration +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +verifyProperty(Temporal.Duration.prototype, Symbol.toStringTag, { + value: "Temporal.Duration", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toStringTag/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toStringTag/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/toStringTag/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/balance-negative-result.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/balance-negative-result.js new file mode 100644 index 0000000000..c60dee0127 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/balance-negative-result.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: A negative duration result is balanced correctly by the modulo operation in NanosecondsToDays +info: | + sec-temporal-nanosecondstodays step 6: + 6. If Type(_relativeTo_) is not Object or _relativeTo_ does not have an [[InitializedTemporalZonedDateTime]] internal slot, then + a. Return the new Record { ..., [[Nanoseconds]]: abs(_nanoseconds_) modulo _dayLengthNs_ × _sign_, ... }. + sec-temporal-balanceduration step 4: + 4. If _largestUnit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then + a. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _relativeTo_). + sec-temporal.duration.prototype.round step 9: + 9. Let _balanceResult_ be ? BalanceDuration(_unbalanceResult_.[[Days]], _unbalanceResult_.[[Hours]], _unbalanceResult_.[[Minutes]], _unbalanceResult_.[[Seconds]], _unbalanceResult_.[[Milliseconds]], _unbalanceResult_.[[Microseconds]], _unbalanceResult_.[[Nanoseconds]], _unit_, _intermediate_). +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(0, 0, 0, 0, -60); +const result = duration.total({ unit: "days" }); +assert.sameValue(result, -2.5); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/branding.js new file mode 100644 index 0000000000..475737c115 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/branding.js @@ -0,0 +1,27 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const total = Temporal.Duration.prototype.total; + +assert.sameValue(typeof total, "function"); + +const args = ["hour"]; + +assert.throws(TypeError, () => total.apply(undefined, args), "undefined"); +assert.throws(TypeError, () => total.apply(null, args), "null"); +assert.throws(TypeError, () => total.apply(true, args), "true"); +assert.throws(TypeError, () => total.apply("", args), "empty string"); +assert.throws(TypeError, () => total.apply(Symbol(), args), "symbol"); +assert.throws(TypeError, () => total.apply(1, args), "1"); +assert.throws(TypeError, () => total.apply({}, args), "plain object"); +assert.throws(TypeError, () => total.apply(Temporal.Duration, args), "Temporal.Duration"); +assert.throws(TypeError, () => total.apply(Temporal.Duration.prototype, args), "Temporal.Duration.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/builtin.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/builtin.js new file mode 100644 index 0000000000..4b3ada6f30 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/builtin.js @@ -0,0 +1,36 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: > + Tests that Temporal.Duration.prototype.total + meets the requirements for built-in objects defined by the + introduction of chapter 17 of the ECMAScript Language Specification. +info: | + Built-in functions that are not constructors do not have a "prototype" property unless + otherwise specified in the description of a particular function. + + Unless specified otherwise, a built-in object that is callable as a function is a built-in + function object with the characteristics described in 10.3. Unless specified otherwise, the + [[Extensible]] internal slot of a built-in object initially has the value true. + + Unless otherwise specified every built-in function and every built-in constructor has the + Function prototype object [...] as the value of its [[Prototype]] internal slot. +features: [Temporal] +---*/ + +assert.sameValue(Object.isExtensible(Temporal.Duration.prototype.total), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.Duration.prototype.total), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.Duration.prototype.total), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.Duration.prototype.total.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-dateadd-called-with-options-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-dateadd-called-with-options-undefined.js new file mode 100644 index 0000000000..75afa3acea --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-dateadd-called-with-options-undefined.js @@ -0,0 +1,52 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: > + BuiltinTimeZoneGetInstantFor calls Calendar.dateAdd with undefined as the + options value +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarDateAddUndefinedOptions(); +const timeZone = TemporalHelpers.oneShiftTimeZone(new Temporal.Instant(0n), 3600e9); +const relativeTo = new Temporal.ZonedDateTime(0n, timeZone, calendar); + +// Total of a calendar unit where larger calendar units have to be converted +// down, to cover the path that goes through UnbalanceDurationRelative +// The calls come from these paths: +// Duration.total() -> +// UnbalanceDurationRelative -> MoveRelativeDate -> calendar.dateAdd() (3x) +// BalanceDuration -> +// AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd() +// NanosecondsToDays -> AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd() (2x) +// RoundDuration -> +// MoveRelativeZonedDateTime -> AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd() +// NanosecondsToDays -> AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd() + +const instance1 = new Temporal.Duration(1, 1, 1, 1, 1); +instance1.total({ unit: "days", relativeTo }); +assert.sameValue(calendar.dateAddCallCount, 8, "converting larger calendar units down"); + +// Total of a calendar unit where smaller calendar units have to be converted +// up, to cover the path that goes through MoveRelativeZonedDateTime +// The calls come from these paths: +// Duration.total() -> +// MoveRelativeZonedDateTime -> AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd() +// BalanceDuration -> +// AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd() +// NanosecondsToDays -> AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd() (2x) +// RoundDuration -> +// MoveRelativeZonedDateTime -> AddZonedDateTime -> BuiltinTimeZoneGetInstantFor -> calendar.dateAdd() +// MoveRelativeDate -> calendar.dateAdd() + +calendar.dateAddCallCount = 0; + +const instance2 = new Temporal.Duration(0, 0, 1, 1); +instance2.total({ unit: "weeks", relativeTo }); +assert.sameValue(calendar.dateAddCallCount, 6, "converting smaller calendar units up"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-dateadd-called-with-plaindate-instance.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-dateadd-called-with-plaindate-instance.js new file mode 100644 index 0000000000..2232f3eaf4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-dateadd-called-with-plaindate-instance.js @@ -0,0 +1,21 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: > + relativeTo parameters that are not ZonedDateTime or undefined, are always + converted to PlainDate for observable calendar calls +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarDateAddPlainDateInstance(); +const instance = new Temporal.Duration(1, 1, 1, 1); +const relativeTo = new Temporal.PlainDate(2000, 1, 1, calendar); +calendar.specificPlainDate = relativeTo; +instance.total({ unit: "days", relativeTo }); +assert(calendar.dateAddCallCount > 0, "assertions in calendar.dateAdd() should have been tested"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-dateuntil-called-with-singular-largestunit.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-dateuntil-called-with-singular-largestunit.js new file mode 100644 index 0000000000..9fa92eb4f4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-dateuntil-called-with-singular-largestunit.js @@ -0,0 +1,96 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: The options object passed to calendar.dateUntil has a largestUnit property with its value in the singular form +info: | + sec-temporal.duration.prototype.total steps 7–11: + 7. Let _unbalanceResult_ be ? UnbalanceDurationRelative(_duration_.[[Years]], _duration_.[[Months]], _duration_.[[Weeks]], _duration_.[[Days]], _unit_, _relativeTo_). + ... + 10. Let _balanceResult_ be ? BalanceDuration(_unbalanceResult_.[[Days]], _unbalanceResult_.[[Hours]], _unbalanceResult_.[[Minutes]], _unbalanceResult_.[[Seconds]], _unbalanceResult_.[[Milliseconds]], _unbalanceResult_.[[Microseconds]], _unbalanceResult_.[[Nanoseconds]], _unit_, _intermediate_). + 11. Let _roundResult_ be ? RoundDuration(_unbalanceResult_.[[Years]], _unbalanceResult_.[[Months]], _unbalanceResult_.[[Weeks]], _balanceResult_.[[Days]], _balanceResult_.[[Hours]], _balanceResult_.[[Minutes]], _balanceResult_.[[Seconds]], _balanceResult_.[[Milliseconds]], _balanceResult_.[[Microseconds]], _balanceResult_.[[Nanoseconds]], 1, _unit_, *"trunc"*, _relativeTo_). + sec-temporal-unbalancedurationrelative steps 1 and 9.d.iii–v: + 1. If _largestUnit_ is *"year"*, or _years_, _months_, _weeks_, and _days_ are all 0, then + a. Return ... + ... + 9. If _largestUnit_ is *"month"*, then + ... + d. Repeat, while abs(_years_) > 0, + ... + iii. Let _untilOptions_ be ! OrdinaryObjectCreate(*null*). + iv. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"month"*). + v. Let _untilResult_ be ? CalendarDateUntil(_calendar_, _relativeTo_, _newRelativeTo_, _untilOptions_, _dateUntil_). + sec-temporal-balanceduration step 3.a: + 3. If _largestUnit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then + a. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _relativeTo_). + sec-temporal-roundduration steps 5.d and 8.n–p: + 5. If _unit_ is one of *"year"*, *"month"*, *"week"*, or *"day"*, then + ... + d. Let _result_ be ? NanosecondsToDays(_nanoseconds_, _intermediate_). + ... + 8. If _unit_ is *"year"*, then + ... + n. Let _untilOptions_ be ! OrdinaryObjectCreate(*null*). + o. Perform ! CreateDataPropertyOrThrow(_untilOptions_, *"largestUnit"*, *"year"*). + p. Let _timePassed_ be ? CalendarDateUntil(_calendar_, _relativeTo_, _daysLater_, _untilOptions_) + sec-temporal-nanosecondstodays step 11: + 11. 1. Let _dateDifference_ be ? DifferenceISODateTime(_startDateTime_.[[ISOYear]], _startDateTime_.[[ISOMonth]], _startDateTime_.[[ISODay]], _startDateTime_.[[ISOHour]], _startDateTime_.[[ISOMinute]], _startDateTime_.[[ISOSecond]], _startDateTime_.[[ISOMillisecond]], _startDateTime_.[[ISOMicrosecond]], _startDateTime_.[[ISONanosecond]], _endDateTime_.[[ISOYear]], _endDateTime_.[[ISOMonth]], _endDateTime_.[[ISODay]], _endDateTime_.[[ISOHour]], _endDateTime_.[[ISOMinute]], _endDateTime_.[[ISOSecond]], _endDateTime_.[[ISOMillisecond]], _endDateTime_.[[ISOMicrosecond]], _endDateTime_.[[ISONanosecond]], _relativeTo_.[[Calendar]], *"day"*). + sec-temporal-differenceisodatetime steps 9–11: + 9. Let _dateLargestUnit_ be ! LargerOfTwoTemporalUnits(*"day"*, _largestUnit_). + 10. Let _untilOptions_ be ? MergeLargestUnitOption(_options_, _dateLargestUnit_). + 11. Let _dateDifference_ be ? CalendarDateUntil(_calendar_, _date1_, _date2_, _untilOptions_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +// Check the paths that go through NanosecondsToDays: one call to dateUntil() in +// BalanceDuration and one in RoundDuration with largestUnit: "day" when the +// unit is "year", "month", "week", or "day", and one extra call with +// largestUnit: "year" in RoundDuration when the unit is "year". + +const duration = new Temporal.Duration(0, 1, 1, 1, 1, 1, 1, 1, 1, 1); + +TemporalHelpers.checkCalendarDateUntilLargestUnitSingular( + (calendar, unit) => { + const relativeTo = new Temporal.ZonedDateTime(0n, "UTC", calendar); + duration.total({ unit, relativeTo }); + }, + { + years: ["day", "day", "year"], + months: ["day", "day"], + weeks: ["day", "day"], + days: ["day", "day"], + hours: [], + minutes: [], + seconds: [], + milliseconds: [], + microseconds: [], + nanoseconds: [] + } +); + +// Check the path that converts years to months in UnbalanceDurationRelative. + +TemporalHelpers.checkCalendarDateUntilLargestUnitSingular( + (calendar, unit) => { + const duration = new Temporal.Duration(5); + const relativeTo = new Temporal.PlainDateTime(2000, 5, 2, 0, 0, 0, 0, 0, 0, calendar); + duration.total({ unit, relativeTo }); + }, + { + years: ["year"], + months: ["month", "month", "month", "month", "month"], + weeks: [], + days: [], + hours: [], + minutes: [], + seconds: [], + milliseconds: [], + microseconds: [], + nanoseconds: [] + } +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-fields-iterable.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-fields-iterable.js new file mode 100644 index 0000000000..a9a8456316 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-fields-iterable.js @@ -0,0 +1,40 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: Verify the result of calendar.fields() is treated correctly. +info: | + sec-temporal.duration.prototype.total step 4: + 4. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_). + sec-temporal-torelativetemporalobject step 4.c: + c. Let _fieldNames_ be ? CalendarFields(_calendar_, « *"day"*, *"month"*, *"monthCode"*, *"year"* »). + sec-temporal-calendarfields step 4: + 4. Let _result_ be ? IterableToList(_fieldsArray_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "day", + "hour", + "microsecond", + "millisecond", + "minute", + "month", + "monthCode", + "nanosecond", + "second", + "year", +]; + +const calendar = TemporalHelpers.calendarFieldsIterable(); +const duration = new Temporal.Duration(1, 1, 1, 1, 1, 1, 1); +duration.total({ unit: 'seconds', relativeTo: { year: 2000, month: 1, day: 1, calendar } }); + +assert.sameValue(calendar.fieldsCallCount, 1, "fields() method called once"); +assert.compareArray(calendar.fieldsCalledWith[0], expected, "fields() method called with correct args"); +assert(calendar.iteratorExhausted[0], "iterated through the whole iterable"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-possibly-required.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-possibly-required.js new file mode 100644 index 0000000000..f2748a1c68 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-possibly-required.js @@ -0,0 +1,49 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: Calendar required when days = 0 but years/months/weeks non-zero +features: [Temporal] +---*/ + +const yearInstance = new Temporal.Duration(1999); +const monthInstance = new Temporal.Duration(0, 49); +const weekInstance = new Temporal.Duration(0, 0, 1); +const dayInstance = new Temporal.Duration(0, 0, 0, 42); + +let relativeTo = new Temporal.PlainDate(2021, 12, 15); + +assert.throws( + RangeError, + () => { yearInstance.total({ unit: "days" }); }, + "total a Duration with non-zero years fails without largest/smallest unit" +); +const yearResult = yearInstance.total({ unit: "days", relativeTo }); +assert.sameValue(yearResult, 730120, "year duration contains proper days"); + +assert.throws( + RangeError, + () => { monthInstance.total({ unit: "days" }); }, + "total a Duration with non-zero month fails without largest/smallest unit" +); + +const monthResult = monthInstance.total({ unit: "days", relativeTo }); +assert.sameValue(monthResult, 1492, "month duration contains proper days"); + +assert.throws( + RangeError, + () => { weekInstance.total({ unit: "days" }); }, + "total a Duration with non-zero weeks fails without largest/smallest unit" +); + +const weekResult = weekInstance.total({ unit: "days", relativeTo }); +assert.sameValue(weekResult, 7, "week duration contains proper days"); + +const dayResultWithoutRelative = dayInstance.total({ unit: "days" }); +const dayResultWithRelative = dayInstance.total({ unit: "days", relativeTo }); +assert.sameValue(dayResultWithoutRelative, 42, "day duration without relative-to part contains proper days"); +assert.sameValue(dayResultWithRelative, 42, "day duration with relative-to part contains proper days"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-temporal-object.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-temporal-object.js new file mode 100644 index 0000000000..c834e67ff2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/calendar-temporal-object.js @@ -0,0 +1,29 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: Fast path for converting other Temporal objects to Temporal.Calendar by reading internal slots +info: | + sec-temporal.duration.prototype.total step 4: + 4. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_). + sec-temporal-torelativetemporalobject step 4.b: + b. Let _calendar_ be ? GetTemporalCalendarWithISODefault(_item_). + sec-temporal-gettemporalcalendarwithisodefault step 2: + 2. Return ? ToTemporalCalendarWithISODefault(_calendar_). + sec-temporal-totemporalcalendarwithisodefault step 2: + 3. Return ? ToTemporalCalendar(_temporalCalendarLike_). + sec-temporal-totemporalcalendar step 1.a: + a. If _temporalCalendarLike_ has an [[InitializedTemporalDate]], [[InitializedTemporalDateTime]], [[InitializedTemporalMonthDay]], [[InitializedTemporalYearMonth]], or [[InitializedTemporalZonedDateTime]] internal slot, then + i. Return _temporalCalendarLike_.[[Calendar]]. +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.checkToTemporalCalendarFastPath((temporalObject) => { + const duration = new Temporal.Duration(1, 1, 1, 1, 1, 1, 1); + duration.total({ unit: 'seconds', relativeTo: { year: 2000, month: 1, day: 1, calendar: temporalObject } }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/dateuntil-field.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/dateuntil-field.js new file mode 100644 index 0000000000..19239ae622 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/dateuntil-field.js @@ -0,0 +1,31 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: > + When consulting calendar.dateUntil() to calculate the number of months in a + year, the months property is not accessed on the result Duration +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const actual = []; +const expected = [ + "call dateUntil", + "call dateUntil", +]; + +const duration = new Temporal.Duration(0, 12); +TemporalHelpers.observeProperty(actual, duration, "months", 1); + +const calendar = TemporalHelpers.calendarDateUntilObservable(actual, duration); +const relativeTo = new Temporal.PlainDateTime(2018, 10, 12, 0, 0, 0, 0, 0, 0, calendar); + +const years = new Temporal.Duration(2); +const result = years.total({ unit: "months", relativeTo }); +assert.sameValue(result, 24, "result"); +assert.compareArray(actual, expected, "operations"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/length.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/length.js new file mode 100644 index 0000000000..227088e877 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/length.js @@ -0,0 +1,28 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: Temporal.Duration.prototype.total.length is 1 +info: | + Every built-in function object, including constructors, has a "length" property whose value is + an integer. Unless otherwise specified, this value is equal to the largest number of named + arguments shown in the subclause headings for the function description. Optional parameters + (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form + «...name») are not included in the default argument count. + + Unless otherwise specified, the "length" property of a built-in function object has the + attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +verifyProperty(Temporal.Duration.prototype.total, "length", { + value: 1, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/name.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/name.js new file mode 100644 index 0000000000..c1ba133fb1 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/name.js @@ -0,0 +1,26 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: Temporal.Duration.prototype.total.name is "total". +info: | + Every built-in function object, including constructors, that is not identified as an anonymous + function has a "name" property whose value is a String. Unless otherwise specified, this value + is the name that is given to the function in this specification. + + Unless otherwise specified, the "name" property of a built-in function object, if it exists, + has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +verifyProperty(Temporal.Duration.prototype.total, "name", { + value: "total", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/nanoseconds-to-days-loop-indefinitely-1.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/nanoseconds-to-days-loop-indefinitely-1.js new file mode 100644 index 0000000000..375a688ddf --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/nanoseconds-to-days-loop-indefinitely-1.js @@ -0,0 +1,62 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: > + NanosecondsToDays can loop arbitrarily long, performing observable operations each iteration. +info: | + NanosecondsToDays ( nanoseconds, relativeTo ) + + ... + 15. If sign is 1, then + a. Repeat, while days > 0 and intermediateNs > endNs, + i. Set days to days - 1. + ii. Set intermediateNs to ℝ(? AddZonedDateTime(ℤ(startNs), relativeTo.[[TimeZone]], + relativeTo.[[Calendar]], 0, 0, 0, days, 0, 0, 0, 0, 0, 0)). + ... +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calls = []; +const duration = Temporal.Duration.from({ days: 1 }); + +function createRelativeTo(count) { + const tz = new Temporal.TimeZone("UTC"); + // Record calls in calls[] + TemporalHelpers.observeMethod(calls, tz, "getPossibleInstantsFor"); + const cal = new Temporal.Calendar("iso8601"); + // Return _count_ days for the first call to dateUntil, behaving normally after + TemporalHelpers.substituteMethod(cal, "dateUntil", [ + Temporal.Duration.from({ days: count }), + ]); + return new Temporal.ZonedDateTime(0n, tz, cal); +} + +let zdt = createRelativeTo(200); +calls.splice(0); // Reset calls list after ZonedDateTime construction +duration.total({ + unit: "day", + relativeTo: zdt, +}); +assert.sameValue( + calls.length, + 200 + 3, + "Expected duration.total to call getPossibleInstantsFor correct number of times" +); + +zdt = createRelativeTo(300); +calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction +duration.total({ + unit: "day", + relativeTo: zdt, +}); +assert.sameValue( + calls.length, + 300 + 3, + "Expected duration.total to call getPossibleInstantsFor correct number of times" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/nanoseconds-to-days-loop-indefinitely-2.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/nanoseconds-to-days-loop-indefinitely-2.js new file mode 100644 index 0000000000..dfc771ac3d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/nanoseconds-to-days-loop-indefinitely-2.js @@ -0,0 +1,76 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: > + NanosecondsToDays can loop infinitely. +info: | + NanosecondsToDays ( nanoseconds, relativeTo ) + + ... + 18. Repeat, while done is false, + a. Let oneDayFartherNs be ℝ(? AddZonedDateTime(ℤ(intermediateNs), relativeTo.[[TimeZone]], + relativeTo.[[Calendar]], 0, 0, 0, sign, 0, 0, 0, 0, 0, 0)). + b. Set dayLengthNs to oneDayFartherNs - intermediateNs. + c. If (nanoseconds - dayLengthNs) × sign ≥ 0, then + i. Set nanoseconds to nanoseconds - dayLengthNs. + ii. Set intermediateNs to oneDayFartherNs. + iii. Set days to days + sign. + d. Else, + i. Set done to true. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calls = []; +const duration = Temporal.Duration.from({ days: 1 }); + +function createRelativeTo(count) { + const dayLengthNs = 86400000000000n; + const dayInstant = new Temporal.Instant(dayLengthNs); + const substitutions = []; + const timeZone = new Temporal.TimeZone("UTC"); + // Return constant value for first _count_ calls + TemporalHelpers.substituteMethod( + timeZone, + "getPossibleInstantsFor", + substitutions + ); + substitutions.length = count; + let i = 0; + for (i = 0; i < substitutions.length; i++) { + // (this value) + substitutions[i] = [dayInstant]; + } + // Record calls in calls[] + TemporalHelpers.observeMethod(calls, timeZone, "getPossibleInstantsFor"); + return new Temporal.ZonedDateTime(0n, timeZone); +} + +let zdt = createRelativeTo(200); +calls.splice(0); // Reset calls list after ZonedDateTime construction +duration.total({ + unit: "day", + relativeTo: zdt, +}); +assert.sameValue( + calls.length, + 200 + 2, + "Expected duration.total to call getPossibleInstantsFor correct number of times" +); + +zdt = createRelativeTo(300); +calls.splice(0); // Reset calls list after previous loop + ZonedDateTime construction +duration.total({ + unit: "day", + relativeTo: zdt, +}); +assert.sameValue( + calls.length, + 300 + 2, + "Expected duration.total to call getPossibleInstantsFor correct number of times" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/not-a-constructor.js new file mode 100644 index 0000000000..abf1ec84d3 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/not-a-constructor.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: > + Temporal.Duration.prototype.total does not implement [[Construct]], is not new-able +info: | + Built-in function objects that are not identified as constructors do not implement the + [[Construct]] internal method unless otherwise specified in the description of a particular + function. +includes: [isConstructor.js] +features: [Reflect.construct, Temporal] +---*/ + +assert.throws(TypeError, () => { + new Temporal.Duration.prototype.total(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.Duration.prototype.total), false, + "isConstructor(Temporal.Duration.prototype.total)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/options-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/options-wrong-type.js new file mode 100644 index 0000000000..c11bc7d901 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/options-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: TypeError thrown when options argument is missing or a non-string primitive +features: [BigInt, Symbol, Temporal] +---*/ + +const badOptions = [ + undefined, + null, + true, + Symbol(), + 1, + 2n, +]; + +const instance = new Temporal.Duration(0, 0, 0, 0, 1); +assert.throws(TypeError, () => instance.total(), "TypeError on missing options argument"); +for (const value of badOptions) { + assert.throws(TypeError, () => instance.total(value), + `TypeError on wrong options type ${typeof value}`); +}; + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/order-of-operations.js new file mode 100644 index 0000000000..0f217afcff --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/order-of-operations.js @@ -0,0 +1,222 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: Properties on objects passed to total() are accessed in the correct order +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "get options.relativeTo", + "get options.unit", + "get options.unit.toString", + "call options.unit.toString", +]; +const actual = []; + +function createOptionsObserver({ unit = "nanoseconds", roundingMode = "halfExpand", roundingIncrement = 1, relativeTo = undefined } = {}) { + return TemporalHelpers.propertyBagObserver(actual, { + unit, + roundingMode, + roundingIncrement, + relativeTo, + }, "options"); +} + +const instance = new Temporal.Duration(0, 0, 0, 0, 2400); + +// basic order of observable operations, with no relativeTo +instance.total(createOptionsObserver({ unit: "nanoseconds" })); +assert.compareArray(actual, expected, "order of operations"); +actual.splice(0); // clear + +const expectedOpsForPlainRelativeTo = [ + // ToRelativeTemporalObject + "get options.relativeTo", + "get options.relativeTo.calendar", + "has options.relativeTo.calendar.calendar", + "get options.relativeTo.calendar.fields", + "call options.relativeTo.calendar.fields", + "get options.relativeTo.day", + "get options.relativeTo.day.valueOf", + "call options.relativeTo.day.valueOf", + "get options.relativeTo.hour", + "get options.relativeTo.microsecond", + "get options.relativeTo.millisecond", + "get options.relativeTo.minute", + "get options.relativeTo.month", + "get options.relativeTo.month.valueOf", + "call options.relativeTo.month.valueOf", + "get options.relativeTo.monthCode", + "get options.relativeTo.monthCode.toString", + "call options.relativeTo.monthCode.toString", + "get options.relativeTo.nanosecond", + "get options.relativeTo.offset", + "get options.relativeTo.second", + "get options.relativeTo.timeZone", + "get options.relativeTo.year", + "get options.relativeTo.year.valueOf", + "call options.relativeTo.year.valueOf", + "get options.relativeTo.calendar.dateFromFields", + "call options.relativeTo.calendar.dateFromFields", + // GetTemporalUnit + "get options.unit", + "get options.unit.toString", + "call options.unit.toString", +]; + +const plainRelativeTo = TemporalHelpers.propertyBagObserver(actual, { + year: 2001, + month: 5, + monthCode: "M05", + day: 2, + calendar: TemporalHelpers.calendarObserver(actual, "options.relativeTo.calendar"), +}, "options.relativeTo"); + +// basic order of observable operations, without rounding: +instance.total(createOptionsObserver({ unit: "nanoseconds", relativeTo: plainRelativeTo })); +assert.compareArray(actual, expectedOpsForPlainRelativeTo, "order of operations for PlainDate relativeTo"); +actual.splice(0); // clear + +// code path through RoundDuration that rounds to the nearest year: +const expectedOpsForYearRounding = expectedOpsForPlainRelativeTo.concat([ + "get options.relativeTo.calendar.dateAdd", // 9.b + "call options.relativeTo.calendar.dateAdd", // 9.c + "call options.relativeTo.calendar.dateAdd", // 9.e + "call options.relativeTo.calendar.dateAdd", // 9.j + "get options.relativeTo.calendar.dateUntil", // 9.m + "call options.relativeTo.calendar.dateUntil", // 9.m + "call options.relativeTo.calendar.dateAdd", // 9.r + "call options.relativeTo.calendar.dateAdd", // 9.w MoveRelativeDate +]); +instance.total(createOptionsObserver({ unit: "years", relativeTo: plainRelativeTo })); +assert.compareArray(actual, expectedOpsForYearRounding, "order of operations with unit = years"); +actual.splice(0); // clear + +// code path through Duration.prototype.total that rounds to the nearest month: +const expectedOpsForMonthRounding = expectedOpsForPlainRelativeTo.concat([ + // UnbalanceDurationRelative + "get options.relativeTo.calendar.dateAdd", // 9.b + "get options.relativeTo.calendar.dateUntil", // 9.c + "call options.relativeTo.calendar.dateAdd", // 9.d.i + "call options.relativeTo.calendar.dateUntil", // 9.d.iv + // RoundDuration + "get options.relativeTo.calendar.dateAdd", // 10.b + "call options.relativeTo.calendar.dateAdd", // 10.c + "call options.relativeTo.calendar.dateAdd", // 10.e + "call options.relativeTo.calendar.dateAdd", // 10.k MoveRelativeDate +], Array(2).fill("call options.relativeTo.calendar.dateAdd")); // 2× 10.n.iii MoveRelativeDate +const instance2 = new Temporal.Duration(1, 0, 0, 62); +instance2.total(createOptionsObserver({ unit: "months", relativeTo: plainRelativeTo })); +assert.compareArray(actual, expectedOpsForMonthRounding, "order of operations with unit = months"); +actual.splice(0); // clear + +// code path through Duration.prototype.total that rounds to the nearest week: +const expectedOpsForWeekRounding = expectedOpsForPlainRelativeTo.concat([ + // UnbalanceDurationRelative + "get options.relativeTo.calendar.dateAdd", // 10.b + "call options.relativeTo.calendar.dateAdd", // 10.c.i MoveRelativeDate + "call options.relativeTo.calendar.dateAdd", // 10.d.i MoveRelativeDate + // RoundDuration + "get options.relativeTo.calendar.dateAdd", // 11.c + "call options.relativeTo.calendar.dateAdd", // 11.d MoveRelativeDate +], Array(58).fill("call options.relativeTo.calendar.dateAdd")); // 58× 11.g.iii MoveRelativeDate (52 + 4 + 2) +const instance3 = new Temporal.Duration(1, 1, 0, 15); +instance3.total(createOptionsObserver({ unit: "weeks", relativeTo: plainRelativeTo })); +assert.compareArray(actual, expectedOpsForWeekRounding, "order of operations with unit = weeks"); +actual.splice(0); // clear + +// code path through UnbalanceDurationRelative that rounds to the nearest day: +const expectedOpsForDayRounding = expectedOpsForPlainRelativeTo.concat([ + "get options.relativeTo.calendar.dateAdd", // 11.a.ii + "call options.relativeTo.calendar.dateAdd", // 11.a.iii.1 MoveRelativeDate + "call options.relativeTo.calendar.dateAdd", // 11.a.iv.1 MoveRelativeDate + "call options.relativeTo.calendar.dateAdd", // 11.a.v.1 MoveRelativeDate +]); +const instance4 = new Temporal.Duration(1, 1, 1) +instance4.total(createOptionsObserver({ unit: "days", relativeTo: plainRelativeTo })); +assert.compareArray(actual, expectedOpsForDayRounding, "order of operations with unit = days"); +actual.splice(0); // clear + +const expectedOpsForZonedRelativeTo = [ + // ToRelativeTemporalObject + "get options.relativeTo", + "get options.relativeTo.calendar", + "has options.relativeTo.calendar.calendar", + "get options.relativeTo.calendar.fields", + "call options.relativeTo.calendar.fields", + "get options.relativeTo.day", + "get options.relativeTo.day.valueOf", + "call options.relativeTo.day.valueOf", + "get options.relativeTo.hour", + "get options.relativeTo.hour.valueOf", + "call options.relativeTo.hour.valueOf", + "get options.relativeTo.microsecond", + "get options.relativeTo.microsecond.valueOf", + "call options.relativeTo.microsecond.valueOf", + "get options.relativeTo.millisecond", + "get options.relativeTo.millisecond.valueOf", + "call options.relativeTo.millisecond.valueOf", + "get options.relativeTo.minute", + "get options.relativeTo.minute.valueOf", + "call options.relativeTo.minute.valueOf", + "get options.relativeTo.month", + "get options.relativeTo.month.valueOf", + "call options.relativeTo.month.valueOf", + "get options.relativeTo.monthCode", + "get options.relativeTo.monthCode.toString", + "call options.relativeTo.monthCode.toString", + "get options.relativeTo.nanosecond", + "get options.relativeTo.nanosecond.valueOf", + "call options.relativeTo.nanosecond.valueOf", + "get options.relativeTo.offset", + "get options.relativeTo.offset.toString", + "call options.relativeTo.offset.toString", + "get options.relativeTo.second", + "get options.relativeTo.second.valueOf", + "call options.relativeTo.second.valueOf", + "get options.relativeTo.timeZone", + "get options.relativeTo.year", + "get options.relativeTo.year.valueOf", + "call options.relativeTo.year.valueOf", + "get options.relativeTo.calendar.dateFromFields", + "call options.relativeTo.calendar.dateFromFields", + "has options.relativeTo.timeZone.timeZone", + // InterpretISODateTimeOffset + "get options.relativeTo.timeZone.getPossibleInstantsFor", + "call options.relativeTo.timeZone.getPossibleInstantsFor", + "get options.relativeTo.timeZone.getOffsetNanosecondsFor", + "call options.relativeTo.timeZone.getOffsetNanosecondsFor", + // GetTemporalUnit + "get options.unit", + "get options.unit.toString", + "call options.unit.toString", + // RoundDuration → ToTemporalDate + "get options.relativeTo.timeZone.getOffsetNanosecondsFor", + "call options.relativeTo.timeZone.getOffsetNanosecondsFor", +]; + +const zonedRelativeTo = TemporalHelpers.propertyBagObserver(actual, { + year: 2001, + month: 5, + monthCode: "M05", + day: 2, + hour: 6, + minute: 54, + second: 32, + millisecond: 987, + microsecond: 654, + nanosecond: 321, + offset: "+00:00", + calendar: TemporalHelpers.calendarObserver(actual, "options.relativeTo.calendar"), + timeZone: TemporalHelpers.timeZoneObserver(actual, "options.relativeTo.timeZone"), +}, "options.relativeTo"); + +// basic order of observable operations, without rounding: +instance.total(createOptionsObserver({ unit: "nanoseconds", relativeTo: zonedRelativeTo })); +assert.compareArray(actual, expectedOpsForZonedRelativeTo, "order of operations for ZonedDateTime relativeTo"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-1.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-1.js new file mode 100644 index 0000000000..495b6c4ec7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-1.js @@ -0,0 +1,99 @@ +// |reftest| skip -- Temporal is not supported +// 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 float representation of the result is 4000.0000000000005"); + +// The next Number in direction -Infinity is less precise. +assert.sameValue(nextDown(expected), 4000, "the next Number in direction -Infinity is less precise"); + +// The next Number in direction +Infinity is less precise. +assert.sameValue(nextUp(expected), 4000.000000000001, "the next Number in direction +Infinity is less precise"); + +assert.sameValue(total, expected, "return value of total()"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-2.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-2.js new file mode 100644 index 0000000000..8d6f149a8b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/precision-exact-mathematical-values-2.js @@ -0,0 +1,101 @@ +// |reftest| skip -- Temporal is not supported +// 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); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/prop-desc.js new file mode 100644 index 0000000000..1a7b0a5740 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: The "total" property of Temporal.Duration.prototype +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.Duration.prototype.total, + "function", + "`typeof Duration.prototype.total` is `function`" +); + +verifyProperty(Temporal.Duration.prototype, "total", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/read-time-fields-before-datefromfields.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/read-time-fields-before-datefromfields.js new file mode 100644 index 0000000000..6b1f823943 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/read-time-fields-before-datefromfields.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: The time fields are read from the object before being passed to dateFromFields(). +info: | + sec-temporal.duration.prototype.total step 4: + 4. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_). + sec-temporal-torelativetemporalobject step 4.g: + g. Let _result_ be ? InterpretTemporalDateTimeFields(_calendar_, _fields_, _options_). + sec-temporal-interprettemporaldatetimefields steps 1–2: + 1. Let _timeResult_ be ? ToTemporalTimeRecord(_fields_). + 2. Let _temporalDate_ be ? DateFromFields(_calendar_, _fields_, _options_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarMakeInvalidGettersTime(); +const duration = new Temporal.Duration(1, 1, 1, 1, 1, 1, 1); +duration.total({ unit: 'seconds', relativeTo: { year: 2000, month: 1, day: 1, calendar } }); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-infinity-throws-rangeerror.js new file mode 100644 index 0000000000..fd593ce59b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-infinity-throws-rangeerror.js @@ -0,0 +1,26 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Throws if any value in the property bag is Infinity or -Infinity +esid: sec-temporal.duration.prototype.total +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); +const base = { year: 2000, month: 5, day: 2, hour: 15, minute: 30, second: 45, millisecond: 987, microsecond: 654, nanosecond: 321 }; + +[Infinity, -Infinity].forEach((inf) => { + ["year", "month", "day", "hour", "minute", "second", "millisecond", "microsecond", "nanosecond"].forEach((prop) => { + assert.throws(RangeError, () => instance.total({ unit: "seconds", relativeTo: { ...base, [prop]: inf } }), `${prop} property cannot be ${inf} in relativeTo`); + + const calls = []; + const obj = TemporalHelpers.toPrimitiveObserver(calls, inf, prop); + assert.throws(RangeError, () => instance.total({ unit: "seconds", relativeTo: { ...base, [prop]: obj } })); + assert.compareArray(calls, [`get ${prop}.valueOf`, `call ${prop}.valueOf`], "it fails after fetching the primitive value"); + }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-leap-second.js new file mode 100644 index 0000000000..6af825f2d4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-leap-second.js @@ -0,0 +1,45 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: Leap second is constrained in both an ISO string and a property bag +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1, 0, 0, 0, 24); + +let relativeTo = "2016-12-31T23:59:60"; +const result1 = instance.total({ unit: "days", relativeTo }); +assert.sameValue( + result1, + 366, + "leap second is a valid ISO string for PlainDate relativeTo" +); + +relativeTo = "2016-12-31T23:59:60+00:00[UTC]"; +const result2 = instance.total({ unit: "days", relativeTo }); +assert.sameValue( + result2, + 366, + "leap second is a valid ISO string for ZonedDateTime relativeTo" +); + +relativeTo = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60 }; +const result3 = instance.total({ unit: "days", relativeTo }); +assert.sameValue( + result3, + 366, + "second: 60 is valid in a property bag for PlainDate relativeTo" +); + +relativeTo = { year: 2016, month: 12, day: 31, hour: 23, minute: 59, second: 60, timeZone: "UTC" }; +const result4 = instance.total({ unit: "days", relativeTo }); +assert.sameValue( + result4, + 366, + "second: 60 is valid in a property bag for ZonedDateTime relativeTo" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-number.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-number.js new file mode 100644 index 0000000000..826a1317c2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-number.js @@ -0,0 +1,32 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: A number as relativeTo option is converted to a string, then to Temporal.PlainDate +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1, 0, 0, 0, 24); + +const relativeTo = 20191101; + +const result = instance.total({ unit: "days", relativeTo }); +assert.sameValue(result, 367, "20191101 is a valid ISO string for relativeTo"); + +const numbers = [ + 1, + -20191101, + 1234567890, +]; + +for (const relativeTo of numbers) { + assert.throws( + RangeError, + () => instance.total({ unit: "days", relativeTo }), + `Number ${relativeTo} does not convert to a valid ISO string for relativeTo` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-calendar-datefromfields-called-with-null-prototype-fields.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-calendar-datefromfields-called-with-null-prototype-fields.js new file mode 100644 index 0000000000..5d889a1925 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-calendar-datefromfields-called-with-null-prototype-fields.js @@ -0,0 +1,19 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: > + Calendar.dateFromFields method is called with a null-prototype fields object +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const calendar = TemporalHelpers.calendarCheckFieldsPrototypePollution(); +const instance = new Temporal.Duration(1, 0, 0, 0, 24); +const relativeTo = { year: 2000, month: 5, day: 2, calendar }; +instance.total({ unit: "days", relativeTo }); +assert.sameValue(calendar.dateFromFieldsCallCount, 1, "dateFromFields should be called on the property bag's calendar"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-calendar-number.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-calendar-number.js new file mode 100644 index 0000000000..92fc470e39 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-calendar-number.js @@ -0,0 +1,44 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: A number as calendar in relativeTo property bag is converted to a string, then to a calendar +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1, 0, 0, 0, 24); + +const calendar = 19970327; + +let relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar }; +const result1 = instance.total({ unit: "days", relativeTo }); +assert.sameValue(result1, 367, "19970327 is a valid ISO string for relativeTo.calendar"); + +relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar: { calendar } }; +const result2 = instance.total({ unit: "days", relativeTo }); +assert.sameValue(result2, 367, "19970327 is a valid ISO string for relativeTo.calendar (nested property)"); + +const numbers = [ + 1, + -19970327, + 1234567890, +]; + +for (const calendar of numbers) { + let relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar }; + assert.throws( + RangeError, + () => instance.total({ unit: "days", relativeTo }), + `Number ${calendar} does not convert to a valid ISO string for relativeTo.calendar` + ); + relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar: { calendar } }; + assert.throws( + RangeError, + () => instance.total({ unit: "days", relativeTo }), + `Number ${calendar} does not convert to a valid ISO string for relativeTo.calendar (nested property)` + ); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-calendar-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-calendar-wrong-type.js new file mode 100644 index 0000000000..a59f187f37 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-calendar-wrong-type.js @@ -0,0 +1,53 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: > + Appropriate error thrown when relativeTo.calendar cannot be converted to a + calendar object or string +features: [BigInt, Symbol, Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.Duration(1, 0, 0, 0, 24); + +const rangeErrorTests = [ + [null, "null"], + [true, "boolean"], + ["", "empty string"], + [1, "number that doesn't convert to a valid ISO string"], + [1n, "bigint"], + [new Temporal.TimeZone("UTC"), "time zone instance"], +]; + +for (const [calendar, description] of rangeErrorTests) { + let relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar }; + assert.throws(RangeError, () => instance.total({ unit: "days", relativeTo }), `${description} does not convert to a valid ISO string`); + + relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar: { calendar } }; + assert.throws(RangeError, () => instance.total({ unit: "days", relativeTo }), `${description} does not convert to a valid ISO string (nested property)`); +} + +const typeErrorTests = [ + [Symbol(), "symbol"], + [{}, "plain object"], + [Temporal.PlainDate, "Temporal.PlainDate, object"], + [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"], + [Temporal.ZonedDateTime, "Temporal.ZonedDateTime, object"], + [Temporal.ZonedDateTime.prototype, "Temporal.ZonedDateTime.prototype, object"], +]; + +for (const [calendar, description] of typeErrorTests) { + let relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar }; + assert.throws(TypeError, () => instance.total({ unit: "days", relativeTo }), `${description} is not a valid property bag and does not convert to a string`); + + relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar: { calendar } }; + assert.throws(TypeError, () => instance.total({ unit: "days", relativeTo }), `${description} is not a valid property bag and does not convert to a string (nested property)`); +} + +const relativeTo = { year: 2019, monthCode: "M11", day: 1, calendar: { calendar: undefined } }; +assert.throws(RangeError, () => instance.total({ unit: "days", relativeTo }), `nested undefined calendar property is always a RangeError`); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-invalid-offset-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-invalid-offset-string.js new file mode 100644 index 0000000000..31ce379e7c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-invalid-offset-string.js @@ -0,0 +1,25 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: relativeTo property bag with offset property is rejected if offset is in the wrong format +features: [Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.Duration(1, 0, 0, 0, 24); + +const badOffsets = [ + "00:00", // missing sign + "+0", // too short + "-000:00", // too long + 0, // converts to a string that is invalid +]; +badOffsets.forEach((offset) => { + const relativeTo = { year: 2021, month: 10, day: 28, offset, timeZone }; + assert.throws(RangeError, () => instance.total({ unit: "days", relativeTo }), `"${offset} is not a valid offset string`); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-no-time-units.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-no-time-units.js new file mode 100644 index 0000000000..8550d132fa --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-no-time-units.js @@ -0,0 +1,17 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: Missing time units in relativeTo property bag default to 0 +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1, 0, 0, 0, 24); + +let relativeTo = { year: 2000, month: 1, day: 1 }; +const result = instance.total({ unit: "days", relativeTo }); +assert.sameValue(result, 367, "missing time units default to 0"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..d2e4f8ca9d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,18 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); + assert.throws(RangeError, () => duration.total({ unit: "seconds", relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..89d6a6d6e6 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,22 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => duration.total({ unit: "seconds", relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } }), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..9961cf01d4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,18 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); + assert.throws(RangeError, () => duration.total({ unit: "seconds", relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..a6f9e394ed --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,27 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); + assert.throws(TypeError, () => duration.total({ unit: "seconds", relativeTo: { year: 2000, month: 5, day: 2, hour: 12, timeZone } })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-instance-does-not-get-timeZone-property.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-instance-does-not-get-timeZone-property.js new file mode 100644 index 0000000000..69810bb337 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-instance-does-not-get-timeZone-property.js @@ -0,0 +1,25 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: > + A Temporal.TimeZone instance passed to total() does not have its + 'timeZone' property observably checked +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1); + +const timeZone = new Temporal.TimeZone("UTC"); +Object.defineProperty(timeZone, "timeZone", { + get() { + throw new Test262Error("timeZone.timeZone should not be accessed"); + }, +}); + +instance.total({ unit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone } }); +instance.total({ unit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-string-datetime.js new file mode 100644 index 0000000000..8496a2e9e4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-string-datetime.js @@ -0,0 +1,39 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: Conversion of ISO date-time strings to Temporal.TimeZone instances +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1); + +let timeZone = "2021-08-19T17:30"; +assert.throws(RangeError, () => instance.total({ unit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), "bare date-time string is not a time zone"); +assert.throws(RangeError, () => instance.total({ unit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }), "bare date-time string is not a time zone"); + +// The following are all valid strings so should not throw: + +[ + "2021-08-19T17:30Z", + "2021-08-19T1730Z", + "2021-08-19T17:30-07:00", + "2021-08-19T1730-07:00", + "2021-08-19T17:30-0700", + "2021-08-19T1730-0700", + "2021-08-19T17:30[UTC]", + "2021-08-19T1730[UTC]", + "2021-08-19T17:30Z[UTC]", + "2021-08-19T1730Z[UTC]", + "2021-08-19T17:30-07:00[UTC]", + "2021-08-19T1730-07:00[UTC]", + "2021-08-19T17:30-0700[UTC]", + "2021-08-19T1730-0700[UTC]", +].forEach((timeZone) => { + instance.total({ unit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone } }); + instance.total({ unit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-string-leap-second.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-string-leap-second.js new file mode 100644 index 0000000000..864f18a214 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-string-leap-second.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: Leap second is a valid ISO string for TimeZone +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1); +let timeZone = "2016-12-31T23:59:60+00:00[UTC]"; + +// A string with a leap second is a valid ISO string, so the following two +// operations should not throw + +instance.total({ unit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone } }); +instance.total({ unit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }); + +timeZone = "2021-08-19T17:30:45.123456789+23:59[+23:59:60]"; +assert.throws(RangeError, () => instance.total({ unit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), "leap second in time zone name not valid"); +assert.throws(RangeError, () => instance.total({ unit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }), "leap second in time zone name not valid (nested property)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-string-year-zero.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-string-year-zero.js new file mode 100644 index 0000000000..5b2d9bfbd5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-string-year-zero.js @@ -0,0 +1,29 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: Negative zero, as an extended year, is rejected +features: [Temporal, arrow-function] +---*/ + +const invalidStrings = [ + "-000000-10-31T17:45Z", + "-000000-10-31T17:45+00:00[UTC]", +]; +const instance = new Temporal.Duration(1); +invalidStrings.forEach((timeZone) => { + assert.throws( + RangeError, + () => instance.total({ unit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), + "reject minus zero as extended year" + ); + assert.throws( + RangeError, + () => instance.total({ unit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }), + "reject minus zero as extended year (nested property)" + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-string.js new file mode 100644 index 0000000000..eed30e87d6 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-string.js @@ -0,0 +1,19 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: Time zone IDs are valid input for a time zone +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1); + +// The following are all valid strings so should not throw: + +["UTC", "+01:00"].forEach((timeZone) => { + instance.total({ unit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone } }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-wrong-type.js new file mode 100644 index 0000000000..36f4fe822c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-propertybag-timezone-wrong-type.js @@ -0,0 +1,42 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: > + Appropriate error thrown when argument cannot be converted to a valid string + or object for TimeZone +features: [BigInt, Symbol, Temporal] +---*/ + +const instance = new Temporal.Duration(1); + +const rangeErrorTests = [ + [null, "null"], + [true, "boolean"], + ["", "empty string"], + [1, "number that doesn't convert to a valid ISO string"], + [19761118, "number that would convert to a valid ISO string in other contexts"], + [1n, "bigint"], + [new Temporal.Calendar("iso8601"), "calendar instance"], +]; + +for (const [timeZone, description] of rangeErrorTests) { + assert.throws(RangeError, () => instance.total({ unit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), `${description} does not convert to a valid ISO string`); + assert.throws(RangeError, () => instance.total({ unit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }), `${description} does not convert to a valid ISO string (nested property)`); +} + +const typeErrorTests = [ + [Symbol(), "symbol"], +]; + +for (const [timeZone, description] of typeErrorTests) { + assert.throws(TypeError, () => instance.total({ unit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone } }), `${description} is not a valid object and does not convert to a string`); + assert.throws(TypeError, () => instance.total({ unit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }), `${description} is not a valid object and does not convert to a string (nested property)`); +} + +const timeZone = undefined; +assert.throws(RangeError, () => instance.total({ unit: "months", relativeTo: { year: 2000, month: 5, day: 2, timeZone: { timeZone } } }), `undefined is always a RangeError as nested property`); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-datetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-datetime.js new file mode 100644 index 0000000000..394da447fc --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-datetime.js @@ -0,0 +1,40 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: > + Conversion of ISO date-time strings as relativeTo option to + Temporal.ZonedDateTime or Temporal.PlainDateTime instances +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1, 0, 0, 0, 24); + +let relativeTo = "2019-11-01T00:00"; +const result1 = instance.total({ unit: "days", relativeTo }); +assert.sameValue(result1, 367, "bare date-time string is a plain relativeTo"); + +relativeTo = "2019-11-01T00:00-07:00"; +const result2 = instance.total({ unit: "days", relativeTo }); +assert.sameValue(result2, 367, "date-time + offset is a plain relativeTo"); + +relativeTo = "2019-11-01T00:00[-07:00]"; +const result3 = instance.total({ unit: "days", relativeTo }); +assert.sameValue(result3, 367, "date-time + IANA annotation is a zoned relativeTo"); + +relativeTo = "2019-11-01T00:00Z[-07:00]"; +const result4 = instance.total({ unit: "days", relativeTo }); +assert.sameValue(result4, 367, "date-time + Z + IANA annotation is a zoned relativeTo"); + +relativeTo = "2019-11-01T00:00+00:00[UTC]"; +const result5 = instance.total({ unit: "days", relativeTo }); +assert.sameValue(result5, 367, "date-time + offset + IANA annotation is a zoned relativeTo"); + +relativeTo = "2019-11-01T00:00Z"; +assert.throws(RangeError, () => instance.total({ unit: "days", relativeTo }), "date-time + Z throws without an IANA annotation"); +relativeTo = "2019-11-01T00:00+04:15[UTC]"; +assert.throws(RangeError, () => instance.total({ unit: "days", relativeTo }), "date-time + offset + IANA annotation throws if wall time and exact time mismatch"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-invalid.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-invalid.js new file mode 100644 index 0000000000..60d0f25974 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-invalid.js @@ -0,0 +1,16 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: RangeError thrown if relativeTo is a string with the wrong format +features: [Temporal] +---*/ + +['bad string', '15:30:45.123456', 'iso8601', 'UTC', 'P1YT1H'].forEach((relativeTo) => { + const duration = new Temporal.Duration(0, 0, 0, 31); + assert.throws(RangeError, () => duration.total({ unit: "months", relativeTo })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-plaindatetime-invalid.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-plaindatetime-invalid.js new file mode 100644 index 0000000000..37befcb2b4 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-plaindatetime-invalid.js @@ -0,0 +1,26 @@ +// |reftest| skip -- Temporal is not supported +// 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: > + Throws a RangeError if "relativeTo" is a date/time value outside the valid limits. +info: | + Temporal.Duration.prototype.total ( totalOf ) + ... + 6. Let relativeTo be ? ToRelativeTemporalObject(totalOf). + ... + + ToRelativeTemporalObject ( options ) + ... + 9. Return ? CreateTemporalDate(result.[[Year]], result.[[Month]], result.[[Day]], calendar). +features: [Temporal] +---*/ + +var duration = Temporal.Duration.from({nanoseconds: 0}); +var options = {unit: "nanoseconds", relativeTo: "+999999-01-01"}; + +assert.throws(RangeError, () => duration.total(options)); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-plaindatetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-plaindatetime.js new file mode 100644 index 0000000000..82b52a2fda --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-plaindatetime.js @@ -0,0 +1,17 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: The relativeTo option accepts a PlainDateTime-like ISO 8601 string +features: [Temporal] +---*/ + +['2000-01-01', '2000-01-01T00:00', '2000-01-01T00:00[u-ca=iso8601]'].forEach((relativeTo) => { + const duration = new Temporal.Duration(0, 0, 0, 31); + const result = duration.total({ unit: "months", relativeTo }); + assert.sameValue(result, 1); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-zoneddatetime-wrong-offset.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-zoneddatetime-wrong-offset.js new file mode 100644 index 0000000000..d987551d2a --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-zoneddatetime-wrong-offset.js @@ -0,0 +1,19 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: Throws if a ZonedDateTime-like relativeTo string has the wrong UTC offset +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1, 0, 0, 0, 24); +const relativeTo = "2000-01-01T00:00+05:30[UTC]"; +assert.throws( + RangeError, + () => instance.total({ unit: "days", relativeTo }), + "total should throw RangeError on a string with UTC offset mismatch" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-zoneddatetime.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-zoneddatetime.js new file mode 100644 index 0000000000..8b52616d10 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-string-zoneddatetime.js @@ -0,0 +1,22 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: The relativeTo option accepts a ZonedDateTime-like ISO 8601 string +features: [Temporal] +---*/ + +[ + '2000-01-01[UTC]', + '2000-01-01T00:00[UTC]', + '2000-01-01T00:00+00:00[UTC]', + '2000-01-01T00:00+00:00[UTC][u-ca=iso8601]', +].forEach((relativeTo) => { + const duration = new Temporal.Duration(0, 0, 0, 31); + const result = duration.total({ unit: "months", relativeTo }); + assert.sameValue(result, 1); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-sub-minute-offset.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-sub-minute-offset.js new file mode 100644 index 0000000000..97bf73bdac --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-sub-minute-offset.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: relativeTo string accepts an inexact UTC offset rounded to hours and minutes +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1, 0, 0, 0, 24); + +let relativeTo = "2000-01-01T00:00+00:45[+00:44:30.123456789]"; +const result = instance.total({ unit: "days", relativeTo }); +assert.sameValue(result, 367, "rounded HH:MM is accepted in string"); + +relativeTo = "2000-01-01T00:00+00:44:30[+00:44:30.123456789]"; +assert.throws(RangeError, () => instance.total({ unit: "days", relativeTo }), "no other rounding is accepted for offset"); + +const timeZone = new Temporal.TimeZone("+00:44:30.123456789"); +relativeTo = { year: 2000, month: 1, day: 1, offset: "+00:45", timeZone }; +assert.throws(RangeError, () => instance.total({ unit: "days", relativeTo }), "rounded HH:MM not accepted as offset in property bag"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-undefined-throw-on-calendar-units.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-undefined-throw-on-calendar-units.js new file mode 100644 index 0000000000..a845710605 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-undefined-throw-on-calendar-units.js @@ -0,0 +1,35 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: > + The relativeTo option is required when the Duration contains years, months, + or weeks, and unit is days; or unit is weeks or months +features: [Temporal, arrow-function] +---*/ + +const oneYear = new Temporal.Duration(1); +const oneMonth = new Temporal.Duration(0, 1); +const oneWeek = new Temporal.Duration(0, 0, 1); +const oneDay = new Temporal.Duration(0, 0, 0, 1); + +const options = { unit: "days" }; +assert.sameValue(oneDay.total(options), 1, "days do not require relativeTo"); +assert.sameValue(oneDay.total("days"), 1, "days do not require relativeTo (string shorthand)"); +assert.throws(RangeError, () => oneWeek.total(options), "total days of weeks requires relativeTo"); +assert.throws(RangeError, () => oneWeek.total("days"), "total days of weeks requires relativeTo (string shorthand)"); +assert.throws(RangeError, () => oneMonth.total(options), "total days of months requires relativeTo"); +assert.throws(RangeError, () => oneMonth.total("days"), "total days of months requires relativeTo (string shorthand)"); +assert.throws(RangeError, () => oneYear.total(options), "total days of years requires relativeTo"); +assert.throws(RangeError, () => oneYear.total("days"), "total days of years requires relativeTo (string shorthand)"); + +["months", "weeks"].forEach((unit) => { + [oneDay, oneWeek, oneMonth, oneYear].forEach((duration) => { + assert.throws(RangeError, () => duration.total({ unit }), `${duration} total ${unit} requires relativeTo`); + assert.throws(RangeError, () => duration.total(unit), `${duration} total ${unit} requires relativeTo (string shorthand)`); + }); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-wrong-type.js new file mode 100644 index 0000000000..108b7f1f46 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-wrong-type.js @@ -0,0 +1,42 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: > + Appropriate error thrown when relativeTo cannot be converted to a valid + relativeTo string or property bag +features: [BigInt, Symbol, Temporal] +---*/ + +const timeZone = new Temporal.TimeZone("UTC"); +const instance = new Temporal.Duration(1, 0, 0, 0, 24); + +const rangeErrorTests = [ + [undefined, "undefined"], + [null, "null"], + [true, "boolean"], + ["", "empty string"], + [1, "number that doesn't convert to a valid ISO string"], + [1n, "bigint"], +]; + +for (const [relativeTo, description] of rangeErrorTests) { + assert.throws(RangeError, () => instance.total({ unit: "days", relativeTo }), `${description} does not convert to a valid ISO string`); +} + +const typeErrorTests = [ + [Symbol(), "symbol"], + [{}, "plain object"], + [Temporal.PlainDate, "Temporal.PlainDate, object"], + [Temporal.PlainDate.prototype, "Temporal.PlainDate.prototype, object"], + [Temporal.ZonedDateTime, "Temporal.ZonedDateTime, object"], + [Temporal.ZonedDateTime.prototype, "Temporal.ZonedDateTime.prototype, object"], +]; + +for (const [relativeTo, description] of typeErrorTests) { + assert.throws(TypeError, () => instance.total({ unit: "days", relativeTo }), `${description} is not a valid property bag and does not convert to a string`); +} + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-nanoseconds-to-days-range-errors.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-nanoseconds-to-days-range-errors.js new file mode 100644 index 0000000000..580115a956 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-nanoseconds-to-days-range-errors.js @@ -0,0 +1,101 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-temporal.duration.prototype.total +description: > + Called abstract operation NanosecondsToDays can throw three different RangeErrors when paired with a ZonedDateTime. +info: | + 6.5.7 NanosecondsToDays ( nanoseconds, relativeTo ) + 19. If days < 0 and sign = 1, throw a RangeError exception. + 20. If days > 0 and sign = -1, throw a RangeError exception. + ... + 22. If nanoseconds > 0 and sign = -1, throw a RangeError exception. +features: [Temporal, BigInt] +includes: [temporalHelpers.js] +---*/ + +const oneNsDuration = Temporal.Duration.from({ nanoseconds: 1 }); +const negOneNsDuration = Temporal.Duration.from({ nanoseconds: -1 }); +const dayNs = 86_400_000_000_000; +const epochInstant = new Temporal.Instant(0n); + +function timeZoneSubstituteValues( + getPossibleInstantsFor, + getOffsetNanosecondsFor +) { + const tz = new Temporal.TimeZone("UTC"); + TemporalHelpers.substituteMethod( + tz, + "getPossibleInstantsFor", + getPossibleInstantsFor + ); + TemporalHelpers.substituteMethod( + tz, + "getOffsetNanosecondsFor", + getOffsetNanosecondsFor + ); + return tz; +} + +// NanosecondsToDays.19: days < 0 and sign = 1 +let zdt = new Temporal.ZonedDateTime( + 0n, // Sets _startNs_ to 0 + timeZoneSubstituteValues( + [[epochInstant]], // Returned for NanosecondsToDays step 14, setting _intermediateNs_ + [ + dayNs - 1, // Returned for NanosecondsToDays step 7, setting _startDateTime_ + -dayNs + 1, // Returned for NanosecondsToDays step 11, setting _endDateTime_ + ] + ) +); +assert.throws(RangeError, () => + // Using 1ns duration _nanoseconds_ to 1 and _sign_ to 1 + oneNsDuration.total({ + relativeTo: zdt, + unit: "day", + }) +); + +// NanosecondsToDays.20: days > 0 and sign = -1 +zdt = new Temporal.ZonedDateTime( + 0n, // Sets _startNs_ to 0 + timeZoneSubstituteValues( + [[epochInstant]], // Returned for NanosecondsToDays step 14, setting _intermediateNs_ + [ + -dayNs + 1, // Returned for NanosecondsToDays step 7, setting _startDateTime_ + dayNs - 1, // Returned for NanosecondsToDays step 11, setting _endDateTime_ + ] + ) +); +assert.throws(RangeError, () => + // Using -1ns duration sets _nanoseconds_ to -1 and _sign_ to -1 + negOneNsDuration.total({ + relativeTo: zdt, + unit: "day", + }) +); + +// NanosecondsToDays.22: nanoseconds > 0 and sign = -1 +zdt = new Temporal.ZonedDateTime( + 0n, // Sets _startNs_ to 0 + timeZoneSubstituteValues( + [ + [new Temporal.Instant(-2n)], // Returned for NanosecondsToDays step 14, setting _intermediateNs_ + [new Temporal.Instant(-4n)], // Returned for NanosecondsToDays step 18.a, setting _oneDayFartherNs_ + ], + [ + dayNs - 1, // Returned for NanosecondsToDays step 7, setting _startDateTime_ + -dayNs + 1, // Returned for NanosecondsToDays step 11, setting _endDateTime_ + ] + ) +); +assert.throws(RangeError, () => + // Using -1ns duration sets _nanoseconds_ to -1 and _sign_ to -1 + negOneNsDuration.total({ + relativeTo: zdt, + unit: "day", + }) +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-negative-epochnanoseconds.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-negative-epochnanoseconds.js new file mode 100644 index 0000000000..97a0bdb830 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-negative-epochnanoseconds.js @@ -0,0 +1,26 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: A pre-epoch value is handled correctly by the modulo operation in GetISOPartsFromEpoch +info: | + sec-temporal-getisopartsfromepoch step 1: + 1. Let _remainderNs_ be the mathematical value whose sign is the sign of _epochNanoseconds_ and whose magnitude is abs(_epochNanoseconds_) modulo 10<sup>6</sup>. + sec-temporal-builtintimezonegetplaindatetimefor step 2: + 2. Let _result_ be ! GetISOPartsFromEpoch(_instant_.[[Nanoseconds]]). +features: [Temporal] +---*/ + +const relativeTo = new Temporal.ZonedDateTime(-13849764_999_999_999n, "UTC"); +const duration = new Temporal.Duration(0, 0, 0, 1); + +// This code path shows up anywhere we convert an exact time, before the Unix +// epoch, with nonzero microseconds or nanoseconds, into a wall time; in this +// case via relativeTo. + +const result = duration.total({ relativeTo, unit: "days" }); +assert.sameValue(result, 1); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js new file mode 100644 index 0000000000..3af889efe6 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-non-integer.js @@ -0,0 +1,19 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: RangeError thrown if time zone reports an offset that is not an integer number of nanoseconds +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[3600_000_000_000.5, NaN, -Infinity, Infinity].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => duration.total({ unit: "seconds", relativeTo: datetime })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js new file mode 100644 index 0000000000..9b8a52a146 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-not-callable.js @@ -0,0 +1,23 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: TypeError thrown if timeZone.getOffsetNanosecondsFor is not callable +features: [BigInt, Symbol, Temporal, arrow-function] +---*/ + +[undefined, null, true, Math.PI, 'string', Symbol('sym'), 42n, {}].forEach((notCallable) => { + const timeZone = new Temporal.TimeZone("UTC"); + const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + timeZone.getOffsetNanosecondsFor = notCallable; + assert.throws( + TypeError, + () => duration.total({ unit: "seconds", relativeTo: datetime }), + `Uncallable ${notCallable === null ? 'null' : typeof notCallable} getOffsetNanosecondsFor should throw TypeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js new file mode 100644 index 0000000000..c6d1cf60fa --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-out-of-range.js @@ -0,0 +1,19 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: RangeError thrown if time zone reports an offset that is out of range +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[-86400_000_000_000, 86400_000_000_000].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(RangeError, () => duration.total({ unit: "seconds", relativeTo: datetime })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js new file mode 100644 index 0000000000..b23356aea8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-timezone-getoffsetnanosecondsfor-wrong-type.js @@ -0,0 +1,28 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: TypeError thrown if time zone reports an offset that is not a Number +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +[ + undefined, + null, + true, + "+01:00", + Symbol(), + 3600_000_000_000n, + {}, + { valueOf() { return 3600_000_000_000; } }, +].forEach((wrongOffset) => { + const timeZone = TemporalHelpers.specificOffsetTimeZone(wrongOffset); + const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); + const datetime = new Temporal.ZonedDateTime(1_000_000_000_987_654_321n, timeZone); + assert.throws(TypeError, () => duration.total({ unit: "seconds", relativeTo: datetime })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-with-fractional-days-different-sign.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-with-fractional-days-different-sign.js new file mode 100644 index 0000000000..a4ef0eaad8 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-with-fractional-days-different-sign.js @@ -0,0 +1,38 @@ +// |reftest| skip -- Temporal is not supported +// 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: > + Relative to a ZonedDateTime with a fractional number of days and different sign. +features: [Temporal] +---*/ + +let duration = Temporal.Duration.from({ + weeks: 1, + days: 0, + hours: 1, +}); + +let cal = new class extends Temporal.Calendar { + #dateAdd = 0; + + dateAdd(date, duration, options) { + if (++this.#dateAdd === 1) { + duration = "-P1W"; + } + return super.dateAdd(date, duration, options); + } +}("iso8601"); + +let zdt = new Temporal.ZonedDateTime(0n, "UTC", cal); + +let result = duration.total({ + relativeTo: zdt, + unit: "days", +}); + +assert.sameValue(result, -7 + 1 / 24); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-with-fractional-days.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-with-fractional-days.js new file mode 100644 index 0000000000..caaf6d636d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/relativeto-zoneddatetime-with-fractional-days.js @@ -0,0 +1,27 @@ +// |reftest| skip -- Temporal is not supported +// 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: > + Relative to a ZonedDateTime with a fractional number of days. +features: [Temporal] +---*/ + +let duration = Temporal.Duration.from({ + weeks: 1, + days: 0, + hours: 1, +}); + +let zdt = new Temporal.ZonedDateTime(0n, "UTC", "iso8601"); + +let result = duration.total({ + relativeTo: zdt, + unit: "days", +}); + +assert.sameValue(result, 7 + 1 / 24); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/shell.js new file mode 100644 index 0000000000..eda1477282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/shell.js @@ -0,0 +1,24 @@ +// GENERATED, DO NOT EDIT +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/timezone-getpossibleinstantsfor-iterable.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/timezone-getpossibleinstantsfor-iterable.js new file mode 100644 index 0000000000..5c6171e133 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/timezone-getpossibleinstantsfor-iterable.js @@ -0,0 +1,36 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: An iterable returned from timeZone.getPossibleInstantsFor is consumed after each call +info: | + sec-temporal.duration.prototype.total steps 4 and 10: + 4. Let _relativeTo_ be ? ToRelativeTemporalObject(_options_). + 10. Let _balanceResult_ be ? BalanceDuration(_unbalanceResult_.[[Days]], [...], _unbalanceResult_.[[Nanoseconds]], _unit_, _intermediate_). + sec-temporal-torelativetemporalobject step 6.d: + d. Let _epochNanoseconds_ be ? InterpretISODateTimeOffset(_result_.[[Year]], [...], _result_.[[Nanosecond]], _offsetNs_, _timeZone_, *"compatible"*, *"reject"*). + sec-temporal-interpretisodatetimeoffset step 7: + 7. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_). + sec-temporal-addzoneddatetime step 8: + 8. Let _intermediateInstant_ be ? BuiltinTimeZoneGetInstantFor(_timeZone_, _intermediateDateTime_, *"compatible"*). + sec-temporal-builtintimezonegetinstantfor step 1: + 1. Let _possibleInstants_ be ? GetPossibleInstantsFor(_timeZone_, _dateTime_). + sec-temporal-getpossibleinstantsfor step 2: + 2. Let _list_ be ? IterableToList(_possibleInstants_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const expected = [ + "2000-01-01T00:00:00", // called once on the input relativeTo if ZonedDateTime + "2001-02-09T00:00:00", // called once on the intermediate ZonedDateTime with the calendar parts of the Duration added +]; + +TemporalHelpers.checkTimeZonePossibleInstantsIterable((timeZone) => { + const duration = new Temporal.Duration(1, 1, 1, 1, 1, 1, 1); + duration.total({ unit: 'seconds', relativeTo: { year: 2000, month: 1, day: 1, timeZone } }); +}, expected); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/total-value-infinity.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/total-value-infinity.js new file mode 100644 index 0000000000..8442f84e1e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/total-value-infinity.js @@ -0,0 +1,15 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: Returns ±∞ when total is not representable as a Number value. +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(0, 0, 0, 0, 0, 0, 0, 0, Number.MAX_VALUE, 0); + +assert.sameValue(instance.total('nanoseconds'), Infinity); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-disallowed-units-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-disallowed-units-string.js new file mode 100644 index 0000000000..a809afa1a9 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-disallowed-units-string.js @@ -0,0 +1,29 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: Specifically disallowed units for the unit option +features: [Temporal, arrow-function] +---*/ + +const instance = new Temporal.Duration(0, 0, 0, 4, 5, 6, 7, 987, 654, 321); +const invalidUnits = [ + "era", + "eras", +]; +invalidUnits.forEach((unit) => { + assert.throws( + RangeError, + () => instance.total({ unit }), + `{ unit: "${unit}" } should not be allowed as an argument to total` + ); + assert.throws( + RangeError, + () => instance.total(unit), + `"${unit}" should not be allowed as an argument to total` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-invalid-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-invalid-string.js new file mode 100644 index 0000000000..e8f44ec99c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-invalid-string.js @@ -0,0 +1,21 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.protoype.total +description: RangeError thrown when unit option not one of the allowed string values +info: | + sec-getoption step 10: + 10. If _values_ is not *undefined* and _values_ does not contain an element equal to _value_, throw a *RangeError* exception. + sec-temporal-totemporaldurationtotal step 1: + 1. Let _unit_ be ? GetOption(_normalizedOptions_, *"unit"*, « String », « *"year"*, *"years"*, *"month"*, *"months"*, *"week"*, *"weeks"*, *"day"*, *"days"*, *"hour"*, *"hours"*, *"minute"*, *"minutes"*, *"second"*, *"seconds"*, *"millisecond"*, *"milliseconds"*, *"microsecond"*, *"microseconds"*, *"nanosecond"*, *"nanoseconds"* », *undefined*). + sec-temporal.duration.protoype.total step 5: + 5. Let _unit_ be ? ToTemporalDurationTotalUnit(_options_). +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(0, 0, 0, 1); +assert.throws(RangeError, () => duration.total({ unit: "other string" })); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-plurals-accepted-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-plurals-accepted-string.js new file mode 100644 index 0000000000..10ab9c8cce --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-plurals-accepted-string.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: Plural units are accepted as well for the shorthand for the unit option +includes: [temporalHelpers.js] +features: [Temporal, arrow-function] +---*/ + +const duration = new Temporal.Duration(0, 0, 0, 4, 5, 6, 7, 987, 654, 321); +const validUnits = [ + "day", + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +TemporalHelpers.checkPluralUnitsAccepted((unit) => duration.total(unit), validUnits); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-plurals-accepted.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-plurals-accepted.js new file mode 100644 index 0000000000..907013a3cb --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-plurals-accepted.js @@ -0,0 +1,28 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: Plural units are accepted as well for the unit option +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); +const relativeTo = new Temporal.PlainDate(2000, 1, 1); +const validUnits = [ + "year", + "month", + "week", + "day", + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +TemporalHelpers.checkPluralUnitsAccepted((unit) => duration.total({ unit, relativeTo }), validUnits); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-string-shorthand-string.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-string-shorthand-string.js new file mode 100644 index 0000000000..1656ed163e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-string-shorthand-string.js @@ -0,0 +1,27 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: String as first argument is equivalent to options bag with unit option +features: [Temporal, arrow-function] +---*/ + +const instance = new Temporal.Duration(0, 0, 0, 4, 5, 6, 7, 987, 654, 321); +const validUnits = [ + "day", + "hour", + "minute", + "second", + "millisecond", + "microsecond", + "nanosecond", +]; +validUnits.forEach((unit) => { + const full = instance.total({ unit }); + const shorthand = instance.total(unit); + assert.sameValue(shorthand, full, `"${unit}" as first argument to total is equivalent to options bag`); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-wrong-type.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-wrong-type.js new file mode 100644 index 0000000000..17cb648121 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/unit-wrong-type.js @@ -0,0 +1,25 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: Type conversions for unit option +info: | + sec-getoption step 9.a: + a. Set _value_ to ? ToString(_value_). + sec-temporal-totemporaldurationtotal step 1: + 1. Let _unit_ be ? GetOption(_normalizedOptions_, *"unit"*, « String », « *"year"*, *"years"*, *"month"*, *"months"*, *"week"*, *"weeks"*, *"day"*, *"days"*, *"hour"*, *"hours"*, *"minute"*, *"minutes"*, *"second"*, *"seconds"*, *"millisecond"*, *"milliseconds"*, *"microsecond"*, *"microseconds"*, *"nanosecond"*, *"nanoseconds"* », *undefined*). + sec-temporal.duration.protoype.total step 5: + 5. Let _unit_ be ? ToTemporalDurationTotalUnit(_options_). +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const duration = new Temporal.Duration(0, 0, 0, 1); +TemporalHelpers.checkStringOptionWrongType("unit", "hour", + (unit) => duration.total({ unit }), + (result, descr) => assert.sameValue(result, 24, descr), +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/year-zero.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/year-zero.js new file mode 100644 index 0000000000..998103d1df --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/total/year-zero.js @@ -0,0 +1,20 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.total +description: Negative zero, as an extended year, is rejected +features: [Temporal, arrow-function] +---*/ + +const instance = new Temporal.Duration(1, 0, 0, 0, 24); + +let relativeTo = "-000000-11-04T00:00"; +assert.throws( + RangeError, + () => { instance.total({ unit: "days", relativeTo }); }, + "reject minus zero as extended year" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/basic.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/basic.js new file mode 100644 index 0000000000..a1b5de5a36 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/basic.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.valueof +description: Basic tests for valueOf(). +features: [Temporal] +---*/ + +const d1 = Temporal.Duration.from("P3DT1H"); +const d2 = Temporal.Duration.from("P3DT1H"); + +assert.throws(TypeError, () => d1.valueOf(), "valueOf"); +assert.throws(TypeError, () => d1 < d1, "<"); +assert.throws(TypeError, () => d1 <= d1, "<="); +assert.throws(TypeError, () => d1 > d1, ">"); +assert.throws(TypeError, () => d1 >= d1, ">="); +assert.sameValue(d1 === d1, true, "==="); +assert.sameValue(d1 === d2, false, "==="); +assert.sameValue(d1 !== d1, false, "!=="); +assert.sameValue(d1 !== d2, true, "!=="); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/branding.js new file mode 100644 index 0000000000..d85c0b1517 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.valueof +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const valueOf = Temporal.Duration.prototype.valueOf; + +assert.sameValue(typeof valueOf, "function"); + +assert.throws(TypeError, () => valueOf.call(undefined), "undefined"); +assert.throws(TypeError, () => valueOf.call(null), "null"); +assert.throws(TypeError, () => valueOf.call(true), "true"); +assert.throws(TypeError, () => valueOf.call(""), "empty string"); +assert.throws(TypeError, () => valueOf.call(Symbol()), "symbol"); +assert.throws(TypeError, () => valueOf.call(1), "1"); +assert.throws(TypeError, () => valueOf.call({}), "plain object"); +assert.throws(TypeError, () => valueOf.call(Temporal.Duration), "Temporal.Duration"); +assert.throws(TypeError, () => valueOf.call(Temporal.Duration.prototype), "Temporal.Duration.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/builtin.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/builtin.js new file mode 100644 index 0000000000..3f3b70b680 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/builtin.js @@ -0,0 +1,36 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.valueof +description: > + Tests that Temporal.Duration.prototype.valueOf + meets the requirements for built-in objects defined by the + introduction of chapter 17 of the ECMAScript Language Specification. +info: | + Built-in functions that are not constructors do not have a "prototype" property unless + otherwise specified in the description of a particular function. + + Unless specified otherwise, a built-in object that is callable as a function is a built-in + function object with the characteristics described in 10.3. Unless specified otherwise, the + [[Extensible]] internal slot of a built-in object initially has the value true. + + Unless otherwise specified every built-in function and every built-in constructor has the + Function prototype object [...] as the value of its [[Prototype]] internal slot. +features: [Temporal] +---*/ + +assert.sameValue(Object.isExtensible(Temporal.Duration.prototype.valueOf), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.Duration.prototype.valueOf), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.Duration.prototype.valueOf), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.Duration.prototype.valueOf.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/length.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/length.js new file mode 100644 index 0000000000..303ce90d09 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/length.js @@ -0,0 +1,28 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2020 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.valueof +description: Temporal.Duration.prototype.valueOf.length is 0 +info: | + Every built-in function object, including constructors, has a "length" property whose value is + an integer. Unless otherwise specified, this value is equal to the largest number of named + arguments shown in the subclause headings for the function description. Optional parameters + (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form + «...name») are not included in the default argument count. + + Unless otherwise specified, the "length" property of a built-in function object has the + attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +verifyProperty(Temporal.Duration.prototype.valueOf, "length", { + value: 0, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/name.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/name.js new file mode 100644 index 0000000000..4987ef53fb --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/name.js @@ -0,0 +1,26 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.valueof +description: Temporal.Duration.prototype.valueOf.name is "valueOf". +info: | + Every built-in function object, including constructors, that is not identified as an anonymous + function has a "name" property whose value is a String. Unless otherwise specified, this value + is the name that is given to the function in this specification. + + Unless otherwise specified, the "name" property of a built-in function object, if it exists, + has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +verifyProperty(Temporal.Duration.prototype.valueOf, "name", { + value: "valueOf", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/not-a-constructor.js new file mode 100644 index 0000000000..8d97930484 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/not-a-constructor.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.valueof +description: > + Temporal.Duration.prototype.valueOf does not implement [[Construct]], is not new-able +info: | + Built-in function objects that are not identified as constructors do not implement the + [[Construct]] internal method unless otherwise specified in the description of a particular + function. +includes: [isConstructor.js] +features: [Reflect.construct, Temporal] +---*/ + +assert.throws(TypeError, () => { + new Temporal.Duration.prototype.valueOf(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.Duration.prototype.valueOf), false, + "isConstructor(Temporal.Duration.prototype.valueOf)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/prop-desc.js new file mode 100644 index 0000000000..6f0515265d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.valueof +description: The "valueOf" property of Temporal.Duration.prototype +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.Duration.prototype.valueOf, + "function", + "`typeof Duration.prototype.valueOf` is `function`" +); + +verifyProperty(Temporal.Duration.prototype, "valueOf", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/shell.js new file mode 100644 index 0000000000..eda1477282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/valueOf/shell.js @@ -0,0 +1,24 @@ +// GENERATED, DO NOT EDIT +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/weeks/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/weeks/branding.js new file mode 100644 index 0000000000..2340ebe513 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/weeks/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.duration.prototype.weeks +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const weeks = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "weeks").get; + +assert.sameValue(typeof weeks, "function"); + +assert.throws(TypeError, () => weeks.call(undefined), "undefined"); +assert.throws(TypeError, () => weeks.call(null), "null"); +assert.throws(TypeError, () => weeks.call(true), "true"); +assert.throws(TypeError, () => weeks.call(""), "empty string"); +assert.throws(TypeError, () => weeks.call(Symbol()), "symbol"); +assert.throws(TypeError, () => weeks.call(1), "1"); +assert.throws(TypeError, () => weeks.call({}), "plain object"); +assert.throws(TypeError, () => weeks.call(Temporal.Duration), "Temporal.Duration"); +assert.throws(TypeError, () => weeks.call(Temporal.Duration.prototype), "Temporal.Duration.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/weeks/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/weeks/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/weeks/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/weeks/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/weeks/prop-desc.js new file mode 100644 index 0000000000..41d8328102 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/weeks/prop-desc.js @@ -0,0 +1,17 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.duration.prototype.weeks +description: The "weeks" property of Temporal.Duration.prototype +features: [Temporal] +---*/ + +const descriptor = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "weeks"); +assert.sameValue(typeof descriptor.get, "function"); +assert.sameValue(descriptor.set, undefined); +assert.sameValue(descriptor.enumerable, false); +assert.sameValue(descriptor.configurable, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/weeks/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/weeks/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/weeks/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/all-negative.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/all-negative.js new file mode 100644 index 0000000000..d9c82daef3 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/all-negative.js @@ -0,0 +1,96 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.with +description: > + Returns a correctly merged object when the argument replaces the fields with + all negative values. +info: | + 1. Let duration be the this value. + 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]). + 3. Let temporalDurationLike be ? ToPartialDuration(temporalDurationLike). + 4. If temporalDurationLike.[[Years]] is not undefined, then + a. Let years be temporalDurationLike.[[Years]]. + 5. Else, + a. Let years be duration.[[Years]]. + 6. If temporalDurationLike.[[Months]] is not undefined, then + a. Let months be temporalDurationLike.[[Months]]. + 7. Else, + a. Let months be duration.[[Months]]. + 8. If temporalDurationLike.[[Weeks]] is not undefined, then + a. Let weeks be temporalDurationLike.[[Weeks]]. + 9. Else, + a. Let weeks be duration.[[Weeks]]. + 10. If temporalDurationLike.[[Days]] is not undefined, then + a. Let days be temporalDurationLike.[[Days]]. + 11. Else, + a. Let days be duration.[[Days]]. + 12. If temporalDurationLike.[[Hours]] is not undefined, then + a. Let hours be temporalDurationLike.[[Hours]]. + 13. Else, + a. Let hours be duration.[[Hours]]. + 14. If temporalDurationLike.[[Minutes]] is not undefined, then + a. Let minutes be temporalDurationLike.[[Minutes]]. + 15. Else, + a. Let minutes be duration.[[Minutes]]. + 16. If temporalDurationLike.[[Seconds]] is not undefined, then + a. Let seconds be temporalDurationLike.[[Seconds]]. + 17. Else, + a. Let seconds be duration.[[Seconds]]. + 18. If temporalDurationLike.[[Milliseconds]] is not undefined, then + a. Let milliseconds be temporalDurationLike.[[Milliseconds]]. + 19. Else, + a. Let milliseconds be duration.[[Milliseconds]]. + 20. If temporalDurationLike.[[Microseconds]] is not undefined, then + a. Let microseconds be temporalDurationLike.[[Microseconds]]. + 21. Else, + a. Let microseconds be duration.[[Microseconds]]. + 22. If temporalDurationLike.[[Nanoseconds]] is not undefined, then + a. Let nanoseconds be temporalDurationLike.[[Nanoseconds]]. + 23. Else, + a. Let nanoseconds be duration.[[Nanoseconds]]. + 24. Return ? CreateTemporalDuration(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds). +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +const argAllNegative = { + years: -9, + months: -8, + weeks: -7, + days: -6, + hours: -5, + minutes: -4, + seconds: -3, + milliseconds: -2, + microseconds: -1, + nanoseconds: -10, +}; + +const d1 = new Temporal.Duration(); +TemporalHelpers.assertDuration( + d1.with(argAllNegative), -9, -8, -7, -6, -5, -4, -3, -2, -1, -10, + "replace all zeroes with all negative" +); + +const d2 = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); +TemporalHelpers.assertDuration( + d2.with(argAllNegative), -9, -8, -7, -6, -5, -4, -3, -2, -1, -10, + "replace all positive with all negative" +); + +const d3 = new Temporal.Duration(1e5, 2e5, 3e5, 4e5, 5e5, 6e5, 7e5, 8e5, 9e5, 10e5); +TemporalHelpers.assertDuration( + d3.with(argAllNegative), -9, -8, -7, -6, -5, -4, -3, -2, -1, -10, + "replace all positive large numbers with all negative" +); + +const d4 = new Temporal.Duration(-1, -2, -3, -4, -5, -6, -7, -8, -9, -10); +TemporalHelpers.assertDuration( + d4.with(argAllNegative), -9, -8, -7, -6, -5, -4, -3, -2, -1, -10, + "replace all negative with all negative" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/all-positive.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/all-positive.js new file mode 100644 index 0000000000..cb5944e082 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/all-positive.js @@ -0,0 +1,95 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.with +description: > + Returns a correctly merged object when the argument replaces the fields with + all positive values. +info: | + 1. Let duration be the this value. + 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]). + 3. Let temporalDurationLike be ? ToPartialDuration(temporalDurationLike). + 4. If temporalDurationLike.[[Years]] is not undefined, then + a. Let years be temporalDurationLike.[[Years]]. + 5. Else, + a. Let years be duration.[[Years]]. + 6. If temporalDurationLike.[[Months]] is not undefined, then + a. Let months be temporalDurationLike.[[Months]]. + 7. Else, + a. Let months be duration.[[Months]]. + 8. If temporalDurationLike.[[Weeks]] is not undefined, then + a. Let weeks be temporalDurationLike.[[Weeks]]. + 9. Else, + a. Let weeks be duration.[[Weeks]]. + 10. If temporalDurationLike.[[Days]] is not undefined, then + a. Let days be temporalDurationLike.[[Days]]. + 11. Else, + a. Let days be duration.[[Days]]. + 12. If temporalDurationLike.[[Hours]] is not undefined, then + a. Let hours be temporalDurationLike.[[Hours]]. + 13. Else, + a. Let hours be duration.[[Hours]]. + 14. If temporalDurationLike.[[Minutes]] is not undefined, then + a. Let minutes be temporalDurationLike.[[Minutes]]. + 15. Else, + a. Let minutes be duration.[[Minutes]]. + 16. If temporalDurationLike.[[Seconds]] is not undefined, then + a. Let seconds be temporalDurationLike.[[Seconds]]. + 17. Else, + a. Let seconds be duration.[[Seconds]]. + 18. If temporalDurationLike.[[Milliseconds]] is not undefined, then + a. Let milliseconds be temporalDurationLike.[[Milliseconds]]. + 19. Else, + a. Let milliseconds be duration.[[Milliseconds]]. + 20. If temporalDurationLike.[[Microseconds]] is not undefined, then + a. Let microseconds be temporalDurationLike.[[Microseconds]]. + 21. Else, + a. Let microseconds be duration.[[Microseconds]]. + 22. If temporalDurationLike.[[Nanoseconds]] is not undefined, then + a. Let nanoseconds be temporalDurationLike.[[Nanoseconds]]. + 23. Else, + a. Let nanoseconds be duration.[[Nanoseconds]]. + 24. Return ? CreateTemporalDuration(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds). +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +const argAllPositive = { + years: 9, + months: 8, + weeks: 7, + days: 6, + hours: 5, + minutes: 4, + seconds: 3, + milliseconds: 2, + microseconds: 1, + nanoseconds: 10, +}; + +const d1 = new Temporal.Duration(); +TemporalHelpers.assertDuration( + d1.with(argAllPositive), 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, + "replace all zeroes with all positive" +); + +const d2 = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); +TemporalHelpers.assertDuration( + d2.with(argAllPositive), 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, + "replace all positive with all positive"); + +const d3 = new Temporal.Duration(1e5, 2e5, 3e5, 4e5, 5e5, 6e5, 7e5, 8e5, 9e5, 10e5); +TemporalHelpers.assertDuration( + d3.with(argAllPositive), 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, + "replace all positive large numbers with all positive" +); + +const d4 = new Temporal.Duration(-1, -2, -3, -4, -5, -6, -7, -8, -9, -10); +TemporalHelpers.assertDuration( + d4.with(argAllPositive), 9, 8, 7, 6, 5, 4, 3, 2, 1, 10, + "replace all negative with all positive" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/argument-invalid-property.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/argument-invalid-property.js new file mode 100644 index 0000000000..87050f47b5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/argument-invalid-property.js @@ -0,0 +1,31 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.with +description: temporalDurationLike object must contain at least one correctly spelled property +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321); + +assert.throws( + TypeError, + () => instance.with({}), + "Throws TypeError if no property is present" +); + +assert.throws( + TypeError, + () => instance.with({ nonsense: true }), + "Throws TypeError if no recognized property is present" +); + +assert.throws( + TypeError, + () => instance.with({ sign: 1 }), + "Sign property is not recognized" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/argument-mixed-sign.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/argument-mixed-sign.js new file mode 100644 index 0000000000..9bcaf3e9a2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/argument-mixed-sign.js @@ -0,0 +1,19 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.with +description: Positive and negative values in the temporalDurationLike argument are not acceptable +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321); + +assert.throws( + RangeError, + () => instance.with({ hours: 1, minutes: -30 }), + `mixed positive and negative values always throw` +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/argument-not-object.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/argument-not-object.js new file mode 100644 index 0000000000..08d7069e52 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/argument-not-object.js @@ -0,0 +1,23 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.with +description: Passing a primitive other than string to with() throws +features: [Symbol, Temporal] +---*/ + +const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321); +assert.throws(TypeError, () => instance.with(undefined), "undefined"); +assert.throws(TypeError, () => instance.with(null), "null"); +assert.throws(TypeError, () => instance.with(true), "boolean"); +assert.throws(TypeError, () => instance.with(""), "empty string"); +assert.throws(TypeError, () => instance.with(Symbol()), "Symbol"); +assert.throws(TypeError, () => instance.with(7), "number"); +assert.throws(TypeError, () => instance.with(7n), "bigint"); +assert.throws(TypeError, () => instance.with([]), "array"); +assert.throws(TypeError, () => instance.with(() => {}), "function"); +assert.throws(TypeError, () => instance.with("string"), "string"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/argument-singular-properties.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/argument-singular-properties.js new file mode 100644 index 0000000000..e3f2b54d3f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/argument-singular-properties.js @@ -0,0 +1,31 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 the V8 project authors. All rights reserved. +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.with +description: Singular properties in the property bag are always ignored +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321); + +[ + { year: 1 }, + { month: 2 }, + { week: 3 }, + { day: 4 }, + { hour: 5 }, + { minute: 6 }, + { second: 7 }, + { millisecond: 8 }, + { microsecond: 9 }, + { nanosecond: 10 }, +].forEach((badObject) => { + assert.throws(TypeError, () => instance.with(badObject), + "Throw TypeError if temporalDurationLike is not valid"); +}); + + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/branding.js new file mode 100644 index 0000000000..ec52c84c40 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/branding.js @@ -0,0 +1,27 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.with +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const with_ = Temporal.Duration.prototype.with; + +assert.sameValue(typeof with_, "function"); + +const args = [{ years: 3 }]; + +assert.throws(TypeError, () => with_.apply(undefined, args), "undefined"); +assert.throws(TypeError, () => with_.apply(null, args), "null"); +assert.throws(TypeError, () => with_.apply(true, args), "true"); +assert.throws(TypeError, () => with_.apply("", args), "empty string"); +assert.throws(TypeError, () => with_.apply(Symbol(), args), "symbol"); +assert.throws(TypeError, () => with_.apply(1, args), "1"); +assert.throws(TypeError, () => with_.apply({}, args), "plain object"); +assert.throws(TypeError, () => with_.apply(Temporal.Duration, args), "Temporal.Duration"); +assert.throws(TypeError, () => with_.apply(Temporal.Duration.prototype, args), "Temporal.Duration.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/builtin.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/builtin.js new file mode 100644 index 0000000000..cc68b2546f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/builtin.js @@ -0,0 +1,36 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.with +description: > + Tests that Temporal.Duration.prototype.with + meets the requirements for built-in objects defined by the + introduction of chapter 17 of the ECMAScript Language Specification. +info: | + Built-in functions that are not constructors do not have a "prototype" property unless + otherwise specified in the description of a particular function. + + Unless specified otherwise, a built-in object that is callable as a function is a built-in + function object with the characteristics described in 10.3. Unless specified otherwise, the + [[Extensible]] internal slot of a built-in object initially has the value true. + + Unless otherwise specified every built-in function and every built-in constructor has the + Function prototype object [...] as the value of its [[Prototype]] internal slot. +features: [Temporal] +---*/ + +assert.sameValue(Object.isExtensible(Temporal.Duration.prototype.with), + true, "Built-in objects must be extensible."); + +assert.sameValue(Object.prototype.toString.call(Temporal.Duration.prototype.with), + "[object Function]", "Object.prototype.toString"); + +assert.sameValue(Object.getPrototypeOf(Temporal.Duration.prototype.with), + Function.prototype, "prototype"); + +assert.sameValue(Temporal.Duration.prototype.with.hasOwnProperty("prototype"), + false, "prototype property"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/copy-properties-not-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/copy-properties-not-undefined.js new file mode 100644 index 0000000000..1c9a442106 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/copy-properties-not-undefined.js @@ -0,0 +1,26 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.with +description: PreparePartialTemporalFields copies only defined properties of source object +info: | + 4. For each value _property_ of _fieldNames_, do + a. Let _value_ be ? Get(_fields_, _property_). + b. If _value_ is not *undefined*, then + ... + iii. Perform ! CreateDataPropertyOrThrow(_result_, _property_, _value_). +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const d = new Temporal.Duration(9, 8, 7, 6, 5, 4, 3, 2, 1, 0); + +TemporalHelpers.assertDuration( + d.with({ minutes: 11, hours: 6, months: undefined }), + 9, 8, 7, 6, 6, 11, 3, 2, 1, 0, + "only the properties that are present and defined in the plain object are copied" +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/infinity-throws-rangeerror.js new file mode 100644 index 0000000000..8ea74b43c5 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/infinity-throws-rangeerror.js @@ -0,0 +1,33 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2020 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Temporal.Duration.prototype.with handles a property bag if any value is Infinity +esid: sec-temporal.duration.prototype.with +features: [Temporal] +---*/ + +const fields = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds', 'nanoseconds']; + +const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); + +fields.forEach((field) => { + assert.throws(RangeError, () => instance.with({ [field]: Infinity })); +}); + +let calls = 0; +const obj = { + valueOf() { + calls++; + return Infinity; + } +}; + +fields.forEach((field) => { + calls = 0; + assert.throws(RangeError, () => instance.with({ [field]: obj })); + assert.sameValue(calls, 1, "it fails after fetching the primitive value"); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/length.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/length.js new file mode 100644 index 0000000000..0b4020884d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/length.js @@ -0,0 +1,28 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2020 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.with +description: Temporal.Duration.prototype.with.length is 1 +info: | + Every built-in function object, including constructors, has a "length" property whose value is + an integer. Unless otherwise specified, this value is equal to the largest number of named + arguments shown in the subclause headings for the function description. Optional parameters + (which are indicated with brackets: [ ]) or rest parameters (which are shown using the form + «...name») are not included in the default argument count. + + Unless otherwise specified, the "length" property of a built-in function object has the + attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +verifyProperty(Temporal.Duration.prototype.with, "length", { + value: 1, + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/name.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/name.js new file mode 100644 index 0000000000..1b49cf1e02 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/name.js @@ -0,0 +1,26 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.with +description: Temporal.Duration.prototype.with.name is "with". +info: | + Every built-in function object, including constructors, that is not identified as an anonymous + function has a "name" property whose value is a String. Unless otherwise specified, this value + is the name that is given to the function in this specification. + + Unless otherwise specified, the "name" property of a built-in function object, if it exists, + has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }. +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +verifyProperty(Temporal.Duration.prototype.with, "name", { + value: "with", + writable: false, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/negative-infinity-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/negative-infinity-throws-rangeerror.js new file mode 100644 index 0000000000..c84e2f379e --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/negative-infinity-throws-rangeerror.js @@ -0,0 +1,33 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2020 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: Temporal.Duration.prototype.with throws a RangeError if any value in a property bag is -Infinity +esid: sec-temporal.duration.prototype.with +features: [Temporal] +---*/ + +const fields = ['years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds', 'nanoseconds']; + +const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); + +fields.forEach((field) => { + assert.throws(RangeError, () => instance.with({ [field]: -Infinity })); +}); + +let calls = 0; +const obj = { + valueOf() { + calls++; + return -Infinity; + } +}; + +fields.forEach((field) => { + calls = 0; + assert.throws(RangeError, () => instance.with({ [field]: obj })); + assert.sameValue(calls, 1, "it fails after fetching the primitive value"); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/non-integer-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/non-integer-throws-rangeerror.js new file mode 100644 index 0000000000..3ab67e29b7 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/non-integer-throws-rangeerror.js @@ -0,0 +1,29 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.with +description: A non-integer value for any recognized property in the property bag, throws a RangeError +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(0, 0, 0, 1, 2, 3, 4, 987, 654, 321); +const fields = [ + "years", + "months", + "weeks", + "days", + "hours", + "minutes", + "seconds", + "milliseconds", + "microseconds", + "nanoseconds", +]; +fields.forEach((field) => { + assert.throws(RangeError, () => instance.with({ [field]: 1.5 })); + assert.throws(RangeError, () => instance.with({ [field]: -1.5 })); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/not-a-constructor.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/not-a-constructor.js new file mode 100644 index 0000000000..f75031928d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/not-a-constructor.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.with +description: > + Temporal.Duration.prototype.with does not implement [[Construct]], is not new-able +info: | + Built-in function objects that are not identified as constructors do not implement the + [[Construct]] internal method unless otherwise specified in the description of a particular + function. +includes: [isConstructor.js] +features: [Reflect.construct, Temporal] +---*/ + +assert.throws(TypeError, () => { + new Temporal.Duration.prototype.with(); +}, "Calling as constructor"); + +assert.sameValue(isConstructor(Temporal.Duration.prototype.with), false, + "isConstructor(Temporal.Duration.prototype.with)"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/order-of-operations.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/order-of-operations.js new file mode 100644 index 0000000000..1f9b8c08a6 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/order-of-operations.js @@ -0,0 +1,62 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2020 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.with +description: Properties on an object passed to with() are accessed in the correct order +includes: [compareArray.js, temporalHelpers.js] +features: [Temporal] +---*/ + +const instance = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 987, 654, 321); +const expected = [ + "get fields.days", + "get fields.days.valueOf", + "call fields.days.valueOf", + "get fields.hours", + "get fields.hours.valueOf", + "call fields.hours.valueOf", + "get fields.microseconds", + "get fields.microseconds.valueOf", + "call fields.microseconds.valueOf", + "get fields.milliseconds", + "get fields.milliseconds.valueOf", + "call fields.milliseconds.valueOf", + "get fields.minutes", + "get fields.minutes.valueOf", + "call fields.minutes.valueOf", + "get fields.months", + "get fields.months.valueOf", + "call fields.months.valueOf", + "get fields.nanoseconds", + "get fields.nanoseconds.valueOf", + "call fields.nanoseconds.valueOf", + "get fields.seconds", + "get fields.seconds.valueOf", + "call fields.seconds.valueOf", + "get fields.weeks", + "get fields.weeks.valueOf", + "call fields.weeks.valueOf", + "get fields.years", + "get fields.years.valueOf", + "call fields.years.valueOf", +]; +const actual = []; +const fields = TemporalHelpers.propertyBagObserver(actual, { + years: 1, + months: 1, + weeks: 1, + days: 1, + hours: 1, + minutes: 1, + seconds: 1, + milliseconds: 1, + microseconds: 1, + nanoseconds: 1, +}, "fields"); +const result = instance.with(fields); +TemporalHelpers.assertDuration(result, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); +assert.compareArray(actual, expected, "order of operations"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/partial-positive.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/partial-positive.js new file mode 100644 index 0000000000..1fcf68a06d --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/partial-positive.js @@ -0,0 +1,89 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.with +description: > + Returns a correctly merged object when the argument replaces only some of the + fields with positive values. +info: | + 1. Let duration be the this value. + 2. Perform ? RequireInternalSlot(duration, [[InitializedTemporalDuration]]). + 3. Let temporalDurationLike be ? ToPartialDuration(temporalDurationLike). + 4. If temporalDurationLike.[[Years]] is not undefined, then + a. Let years be temporalDurationLike.[[Years]]. + 5. Else, + a. Let years be duration.[[Years]]. + 6. If temporalDurationLike.[[Months]] is not undefined, then + a. Let months be temporalDurationLike.[[Months]]. + 7. Else, + a. Let months be duration.[[Months]]. + 8. If temporalDurationLike.[[Weeks]] is not undefined, then + a. Let weeks be temporalDurationLike.[[Weeks]]. + 9. Else, + a. Let weeks be duration.[[Weeks]]. + 10. If temporalDurationLike.[[Days]] is not undefined, then + a. Let days be temporalDurationLike.[[Days]]. + 11. Else, + a. Let days be duration.[[Days]]. + 12. If temporalDurationLike.[[Hours]] is not undefined, then + a. Let hours be temporalDurationLike.[[Hours]]. + 13. Else, + a. Let hours be duration.[[Hours]]. + 14. If temporalDurationLike.[[Minutes]] is not undefined, then + a. Let minutes be temporalDurationLike.[[Minutes]]. + 15. Else, + a. Let minutes be duration.[[Minutes]]. + 16. If temporalDurationLike.[[Seconds]] is not undefined, then + a. Let seconds be temporalDurationLike.[[Seconds]]. + 17. Else, + a. Let seconds be duration.[[Seconds]]. + 18. If temporalDurationLike.[[Milliseconds]] is not undefined, then + a. Let milliseconds be temporalDurationLike.[[Milliseconds]]. + 19. Else, + a. Let milliseconds be duration.[[Milliseconds]]. + 20. If temporalDurationLike.[[Microseconds]] is not undefined, then + a. Let microseconds be temporalDurationLike.[[Microseconds]]. + 21. Else, + a. Let microseconds be duration.[[Microseconds]]. + 22. If temporalDurationLike.[[Nanoseconds]] is not undefined, then + a. Let nanoseconds be temporalDurationLike.[[Nanoseconds]]. + 23. Else, + a. Let nanoseconds be duration.[[Nanoseconds]]. + 24. Return ? CreateTemporalDuration(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds). +features: [Temporal] +includes: [temporalHelpers.js] +---*/ + +const durationlike1 = { years: 9, hours: 5 }; +const durationlike2 = { months: 8, minutes: 4 }; +const durationlike3 = { weeks: 7, seconds: 3 }; +const durationlike4 = { days: 6, milliseconds: 2 }; +const durationlike5 = { microseconds: 987, nanoseconds: 123 }; + +const d1 = new Temporal.Duration(); +TemporalHelpers.assertDuration( + d1.with(durationlike1), 9, 0, 0, 0, 5, 0, 0, 0, 0, 0, "replace all zeroes with years and hours"); +TemporalHelpers.assertDuration( + d1.with(durationlike2), 0, 8, 0, 0, 0, 4, 0, 0, 0, 0, "replace all zeroes wtih months and minutes"); +TemporalHelpers.assertDuration( + d1.with(durationlike3), 0, 0, 7, 0, 0, 0, 3, 0, 0, 0, "replace all zeroes with weeks and seconds"); +TemporalHelpers.assertDuration( + d1.with(durationlike4), 0, 0, 0, 6, 0, 0, 0, 2, 0, 0, "replace all zeroes with days and milliseconds"); +TemporalHelpers.assertDuration( + d1.with(durationlike5), 0, 0, 0, 0, 0, 0, 0, 0, 987, 123, "replace all zeroes with microseconds and nanoseconds"); + +const d2 = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); +TemporalHelpers.assertDuration( + d2.with(durationlike1), 9, 2, 3, 4, 5, 6, 7, 8, 9, 10, "replace all positive with years and hours"); +TemporalHelpers.assertDuration( + d2.with(durationlike2), 1, 8, 3, 4, 5, 4, 7, 8, 9, 10, "replace all positive with months and minutes"); +TemporalHelpers.assertDuration( + d2.with(durationlike3), 1, 2, 7, 4, 5, 6, 3, 8, 9, 10, "replace all positive with weeks and seconds"); +TemporalHelpers.assertDuration( + d2.with(durationlike4), 1, 2, 3, 6, 5, 6, 7, 2, 9, 10, "replace all positive with days and milliseconds"); +TemporalHelpers.assertDuration( + d2.with(durationlike5), 1, 2, 3, 4, 5, 6, 7, 8, 987, 123, "replace all positive with microseconds and nanoseconds"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/prop-desc.js new file mode 100644 index 0000000000..089d685b76 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/prop-desc.js @@ -0,0 +1,24 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.with +description: The "with" property of Temporal.Duration.prototype +includes: [propertyHelper.js] +features: [Temporal] +---*/ + +assert.sameValue( + typeof Temporal.Duration.prototype.with, + "function", + "`typeof Duration.prototype.with` is `function`" +); + +verifyProperty(Temporal.Duration.prototype, "with", { + writable: true, + enumerable: false, + configurable: true, +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/shell.js new file mode 100644 index 0000000000..eda1477282 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/shell.js @@ -0,0 +1,24 @@ +// GENERATED, DO NOT EDIT +// file: isConstructor.js +// Copyright (C) 2017 André Bargull. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +description: | + Test if a given function is a constructor function. +defines: [isConstructor] +features: [Reflect.construct] +---*/ + +function isConstructor(f) { + if (typeof f !== "function") { + throw new Test262Error("isConstructor invoked with a non-function value"); + } + + try { + Reflect.construct(function(){}, [], f); + } catch (e) { + return false; + } + return true; +} diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/sign-conflict-throws-rangeerror.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/sign-conflict-throws-rangeerror.js new file mode 100644 index 0000000000..d62a20b49b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/sign-conflict-throws-rangeerror.js @@ -0,0 +1,31 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.with +description: Throw RangeError if the resulting duration has mixed signs +info: | + 24. Return ? CreateTemporalDuration(years, months, weeks, days, hours, minutes, seconds, milliseconds, microseconds, nanoseconds). +features: [Temporal] +---*/ + +const d1 = new Temporal.Duration(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); +const d2 = new Temporal.Duration(-1, -2, -3, -4, -5, -6, -7, -8, -9, -10); +const fields = ["years", "months", "weeks", "days", "hours", "minutes", "seconds", "milliseconds", "microseconds", "nanoseconds"]; + +fields.forEach((field) => { + assert.throws( + RangeError, + () => d1.with({ [field]: -1 }), + `sign in argument { ${field}: -1 } conflicting with sign of duration should throw RangeError` + ); + + assert.throws( + RangeError, + () => d2.with({ [field]: 1 }), + `sign in argument { ${field}: 1 } conflicting with sign of duration should throw RangeError` + ); +}); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/sign-replace.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/sign-replace.js new file mode 100644 index 0000000000..9f66c16b13 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/sign-replace.js @@ -0,0 +1,18 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.with +description: Replacing the sign is supported. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const d = Temporal.Duration.from({ years: 5, days: 1 }); +assert.sameValue(d.sign, 1, "original sign"); +const d2 = d.with({ years: -1, days: 0, minutes: -1 }); +assert.sameValue(d2.sign, -1, "new sign"); +TemporalHelpers.assertDuration(d2, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/subclassing-ignored.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/subclassing-ignored.js new file mode 100644 index 0000000000..e4cb685bae --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/with/subclassing-ignored.js @@ -0,0 +1,20 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration.prototype.with +description: Objects of a subclass are never created as return values. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +TemporalHelpers.checkSubclassingIgnored( + Temporal.Duration, + [0, 0, 0, 4, 5, 6, 7, 987, 654, 321], + "with", + [{ nanoseconds: 1 }], + (result) => TemporalHelpers.assertDuration(result, 0, 0, 0, 4, 5, 6, 7, 987, 654, 1), +); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/years/branding.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/years/branding.js new file mode 100644 index 0000000000..b4daa3153c --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/years/branding.js @@ -0,0 +1,25 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.duration.prototype.years +description: Throw a TypeError if the receiver is invalid +features: [Symbol, Temporal] +---*/ + +const years = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "years").get; + +assert.sameValue(typeof years, "function"); + +assert.throws(TypeError, () => years.call(undefined), "undefined"); +assert.throws(TypeError, () => years.call(null), "null"); +assert.throws(TypeError, () => years.call(true), "true"); +assert.throws(TypeError, () => years.call(""), "empty string"); +assert.throws(TypeError, () => years.call(Symbol()), "symbol"); +assert.throws(TypeError, () => years.call(1), "1"); +assert.throws(TypeError, () => years.call({}), "plain object"); +assert.throws(TypeError, () => years.call(Temporal.Duration), "Temporal.Duration"); +assert.throws(TypeError, () => years.call(Temporal.Duration.prototype), "Temporal.Duration.prototype"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/years/browser.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/years/browser.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/years/browser.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/years/prop-desc.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/years/prop-desc.js new file mode 100644 index 0000000000..6f9a65911f --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/years/prop-desc.js @@ -0,0 +1,17 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-get-temporal.duration.prototype.years +description: The "years" property of Temporal.Duration.prototype +features: [Temporal] +---*/ + +const descriptor = Object.getOwnPropertyDescriptor(Temporal.Duration.prototype, "years"); +assert.sameValue(typeof descriptor.get, "function"); +assert.sameValue(descriptor.set, undefined); +assert.sameValue(descriptor.enumerable, false); +assert.sameValue(descriptor.configurable, true); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/prototype/years/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/years/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/prototype/years/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/seconds-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/seconds-undefined.js new file mode 100644 index 0000000000..1cb0d3b60b --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/seconds-undefined.js @@ -0,0 +1,20 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration +description: Undefined arguments should be treated as zero. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const args = [1, 1, 1, 1, 1, 1]; + +const explicit = new Temporal.Duration(...args, undefined); +TemporalHelpers.assertDuration(explicit, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, "explicit"); + +const implicit = new Temporal.Duration(...args); +TemporalHelpers.assertDuration(implicit, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, "implicit"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/shell.js b/js/src/tests/test262/built-ins/Temporal/Duration/shell.js new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/shell.js diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/subclass.js b/js/src/tests/test262/built-ins/Temporal/Duration/subclass.js new file mode 100644 index 0000000000..ca291c9999 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/subclass.js @@ -0,0 +1,21 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration +description: Test for Temporal.Duration subclassing. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +class CustomDuration extends Temporal.Duration { +} + +const instance = new CustomDuration(1, 1, 0, 1); +TemporalHelpers.assertDuration(instance, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0); +assert.sameValue(Object.getPrototypeOf(instance), CustomDuration.prototype, "Instance of CustomDuration"); +assert(instance instanceof CustomDuration, "Instance of CustomDuration"); +assert(instance instanceof Temporal.Duration, "Instance of Temporal.Duration"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/weeks-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/weeks-undefined.js new file mode 100644 index 0000000000..f7536b3fea --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/weeks-undefined.js @@ -0,0 +1,20 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration +description: Undefined arguments should be treated as zero. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const args = [1, 1]; + +const explicit = new Temporal.Duration(...args, undefined); +TemporalHelpers.assertDuration(explicit, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "explicit"); + +const implicit = new Temporal.Duration(...args); +TemporalHelpers.assertDuration(implicit, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, "implicit"); + +reportCompare(0, 0); diff --git a/js/src/tests/test262/built-ins/Temporal/Duration/years-undefined.js b/js/src/tests/test262/built-ins/Temporal/Duration/years-undefined.js new file mode 100644 index 0000000000..7907131bd0 --- /dev/null +++ b/js/src/tests/test262/built-ins/Temporal/Duration/years-undefined.js @@ -0,0 +1,20 @@ +// |reftest| skip -- Temporal is not supported +// Copyright (C) 2021 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-temporal.duration +description: Undefined arguments should be treated as zero. +includes: [temporalHelpers.js] +features: [Temporal] +---*/ + +const args = []; + +const explicit = new Temporal.Duration(...args, undefined); +TemporalHelpers.assertDuration(explicit, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "explicit"); + +const implicit = new Temporal.Duration(...args); +TemporalHelpers.assertDuration(implicit, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "implicit"); + +reportCompare(0, 0); |