Skip to content

Commit

Permalink
Add tests ensuring that observable calls are made with options === un…
Browse files Browse the repository at this point in the history
…defined

Where possible, observable calls originating from within Temporal, that
require an options argument, should pass `undefined` as that options
argument, rather than `{}` or `Object.create(null)`.

See tc39/proposal-temporal#1685.
  • Loading branch information
ptomato authored and rwaldron committed Apr 4, 2022
1 parent 16ad841 commit ac19506
Show file tree
Hide file tree
Showing 45 changed files with 1,030 additions and 0 deletions.
38 changes: 38 additions & 0 deletions harness/temporalHelpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -1034,6 +1034,44 @@ var TemporalHelpers = {
return new CalendarFieldsIterable();
},

/*
* A custom calendar that asserts its ...FromFields() methods are called with
* the options parameter having the value undefined.
*/
calendarFromFieldsUndefinedOptions() {
class CalendarFromFieldsUndefinedOptions extends Temporal.Calendar {
constructor() {
super("iso8601");
this.dateFromFieldsCallCount = 0;
this.monthDayFromFieldsCallCount = 0;
this.yearMonthFromFieldsCallCount = 0;
}

toString() {
return "from-fields-undef-options";
}

dateFromFields(fields, options) {
this.dateFromFieldsCallCount++;
assert.sameValue(options, undefined, "dateFromFields shouldn't be called with options");
return super.dateFromFields(fields, options);
}

yearMonthFromFields(fields, options) {
this.yearMonthFromFieldsCallCount++;
assert.sameValue(options, undefined, "yearMonthFromFields shouldn't be called with options");
return super.yearMonthFromFields(fields, options);
}

monthDayFromFields(fields, options) {
this.monthDayFromFieldsCallCount++;
assert.sameValue(options, undefined, "monthDayFromFields shouldn't be called with options");
return super.monthDayFromFields(fields, options);
}
}
return new CalendarFromFieldsUndefinedOptions();
},

/*
* A custom calendar that modifies the fields object passed in to
* dateFromFields, sabotaging its time properties.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// 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.calendar.prototype.dateadd
description: >
Calendar.dateFromFields method is called with undefined as the options value
when call originates internally
includes: [temporalHelpers.js]
features: [Temporal]
---*/

const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
calendar.dateAdd({ year: 2000, month: 5, day: 2, calendar }, new Temporal.Duration(1));
assert.sameValue(calendar.dateFromFieldsCallCount, 1);
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// 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.calendar.prototype.dateuntil
description: >
Calendar.dateFromFields method is called with undefined as the options value
when call originates internally
includes: [temporalHelpers.js]
features: [Temporal]
---*/

const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
calendar.dateUntil({ year: 2000, month: 5, day: 2, calendar }, { year: 2000, month: 5, day: 3, calendar });
assert.sameValue(calendar.dateFromFieldsCallCount, 2);
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// 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.calendar.prototype.day
description: >
Calendar.dateFromFields method is called with undefined as the options value
when call originates internally
includes: [temporalHelpers.js]
features: [Temporal]
---*/

const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
calendar.day({ year: 2000, month: 5, day: 3, calendar });
assert.sameValue(calendar.dateFromFieldsCallCount, 1);
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// 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.calendar.prototype.dayofweek
description: >
Calendar.dateFromFields method is called with undefined as the options value
when call originates internally
includes: [temporalHelpers.js]
features: [Temporal]
---*/

const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
calendar.dayOfWeek({ year: 2000, month: 5, day: 3, calendar });
assert.sameValue(calendar.dateFromFieldsCallCount, 1);
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// 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.calendar.prototype.dayofyear
description: >
Calendar.dateFromFields method is called with undefined as the options value
when call originates internally
includes: [temporalHelpers.js]
features: [Temporal]
---*/

const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
calendar.dayOfYear({ year: 2000, month: 5, day: 3, calendar });
assert.sameValue(calendar.dateFromFieldsCallCount, 1);
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// 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.calendar.prototype.daysinmonth
description: >
Calendar.dateFromFields method is called with undefined as the options value
when call originates internally
includes: [temporalHelpers.js]
features: [Temporal]
---*/

const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
calendar.daysInMonth({ year: 2000, month: 5, day: 3, calendar });
assert.sameValue(calendar.dateFromFieldsCallCount, 1);
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// 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.calendar.prototype.daysinweek
description: >
Calendar.dateFromFields method is called with undefined as the options value
when call originates internally
includes: [temporalHelpers.js]
features: [Temporal]
---*/

const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
calendar.daysInWeek({ year: 2000, month: 5, day: 3, calendar });
assert.sameValue(calendar.dateFromFieldsCallCount, 1);
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// 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.calendar.prototype.daysinyear
description: >
Calendar.dateFromFields method is called with undefined as the options value
when call originates internally
includes: [temporalHelpers.js]
features: [Temporal]
---*/

const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
calendar.daysInYear({ year: 2000, month: 5, day: 3, calendar });
assert.sameValue(calendar.dateFromFieldsCallCount, 1);
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// 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.calendar.prototype.inleapyear
description: >
Calendar.dateFromFields method is called with undefined as the options value
when call originates internally
includes: [temporalHelpers.js]
features: [Temporal]
---*/

const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
calendar.inLeapYear({ year: 2000, month: 5, day: 3, calendar });
assert.sameValue(calendar.dateFromFieldsCallCount, 1);
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// 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.calendar.prototype.month
description: >
Calendar.dateFromFields method is called with undefined as the options value
when call originates internally
includes: [temporalHelpers.js]
features: [Temporal]
---*/

const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
calendar.month({ year: 2000, month: 5, day: 3, calendar });
assert.sameValue(calendar.dateFromFieldsCallCount, 1);
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// 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.calendar.prototype.monthcode
description: >
Calendar.dateFromFields method is called with undefined as the options value
when call originates internally
includes: [temporalHelpers.js]
features: [Temporal]
---*/

const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
calendar.monthCode({ year: 2000, month: 5, day: 3, calendar });
assert.sameValue(calendar.dateFromFieldsCallCount, 1);
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// 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.calendar.prototype.monthsinyear
description: >
Calendar.dateFromFields method is called with undefined as the options value
when call originates internally
includes: [temporalHelpers.js]
features: [Temporal]
---*/

const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
calendar.monthsInYear({ year: 2000, month: 5, day: 3, calendar });
assert.sameValue(calendar.dateFromFieldsCallCount, 1);
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// 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.calendar.prototype.weekofyear
description: >
Calendar.dateFromFields method is called with undefined as the options value
when call originates internally
includes: [temporalHelpers.js]
features: [Temporal]
---*/

const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
calendar.weekOfYear({ year: 2000, month: 5, day: 3, calendar });
assert.sameValue(calendar.dateFromFieldsCallCount, 1);
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// 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.calendar.prototype.year
description: >
Calendar.dateFromFields method is called with undefined as the options value
when call originates internally
includes: [temporalHelpers.js]
features: [Temporal]
---*/

const calendar = TemporalHelpers.calendarFromFieldsUndefinedOptions();
calendar.year({ year: 2000, month: 5, day: 3, calendar });
assert.sameValue(calendar.dateFromFieldsCallCount, 1);
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright (C) 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright (C) 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");
Loading

0 comments on commit ac19506

Please sign in to comment.