Skip to content

Commit

Permalink
Bug 1944349 [wpt PR 50338] - DOM: Add more Observable iterable tests,…
Browse files Browse the repository at this point in the history
… a=testonly

Automatic update from web-platform-tests
DOM: Add more Observable iterable tests

This CL adds and supplements a few tests:

  1. First we modify the existing "subscribe with aborted signal" tests.
     Specifically, we expand their assertions to not only assert that
     `next()` isn't ever called, but make more assertions about the
     iterator protocol getter and function invocations in general.
  2. Second, we modify the test that asserts `next()` is not called
     when you subscribe with an unaborted signal, but that signal gets
     aborted while the iterator protocol methods are called during
     subscription of the Observable. We expand the assertions in the
     same way as (1), and combine the two separate tests into one that
     covers both sync and async iterators, also to match (1).
  3. Finally, this CL adds a sync iterable version of the test added in
     https://crrev.com/c/6199630. The test scenario is: you subscribe to
     a sync iterable with an unaborted signal that gets aborted while
     obtaining the iterator (just like (2)), BUT while getting the
     iterator, an error is thrown. The tests asserts that the error is
     reported to the global before we consult the aborted signal and
     stop the subscription process. This ensures that the exception is
     not swallowed, but is appropriately surfaced, even though the
     subscription is aborted.

This corresponds with the spec PR:
WICG/observable#192.

R=masonf

Bug: 363015168
Change-Id: Ida605c49a2d73cd407a9dc3c392d6b2f338855be
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6202182
Commit-Queue: Dominic Farolino <[email protected]>
Reviewed-by: Mason Freed <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1412315}

--

wpt-commits: ea15691f277a5e965d90f9c0167638559ff62f0d
wpt-pr: 50338
  • Loading branch information
domfarolino authored and moz-wptsync-bot committed Jan 31, 2025
1 parent 768cac5 commit 3a6ceb8
Showing 1 changed file with 128 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1437,90 +1437,126 @@ promise_test(async t => {
test(() => {
const results = [];
const iterable = {
impl() {
return {
next() {
results.push('next() running');
return {done: true};
}
getter() {
results.push('GETTER called');
return () => {
results.push('Obtaining iterator');
return {
next() {
results.push('next() running');
return {done: true};
}
};
};
}
};

iterable[Symbol.iterator] = iterable.impl;
Object.defineProperty(iterable, Symbol.iterator, {
get: iterable.getter
});
{
const source = Observable.from(iterable);
assert_array_equals(results, ["GETTER called"]);
source.subscribe({}, {signal: AbortSignal.abort()});
assert_array_equals(results, []);
assert_array_equals(results, ["GETTER called"]);
}
iterable[Symbol.iterator] = undefined;
iterable[Symbol.asyncIterator] = iterable.impl;
Object.defineProperty(iterable, Symbol.asyncIterator, {
get: iterable.getter
});
{
const source = Observable.from(iterable);
assert_array_equals(results, ["GETTER called", "GETTER called"]);
source.subscribe({}, {signal: AbortSignal.abort()});
assert_array_equals(results, []);
assert_array_equals(results, ["GETTER called", "GETTER called"]);
}
}, "from(): Subscribing to an iterable Observable with an aborted signal " +
"does not call next()");

test(() => {
const results = [];
const ac = new AbortController();
let results = [];

const iterable = {
[Symbol.iterator]() {
ac.abort();
return {
val: 0,
next() {
results.push('next() called');
return {done: true};
},
return() {
results.push('return() called');
}
controller: null,
calledOnce: false,
getter() {
results.push('GETTER called');
if (!this.calledOnce) {
this.calledOnce = true;
return () => {
results.push('NOT CALLED');
// We don't need to return anything here. The only time this path is
// hit is during `Observable.from()` which doesn't actually obtain an
// iterator. It just samples the iterable protocol property to ensure
// that it's valid.
};
}

// This path is only called the second time the iterator protocol getter
// is run.
this.controller.abort();
return () => {
results.push('iterator obtained');
return {
val: 0,
next() {
results.push('next() called');
return {done: true};
},
return() {
results.push('return() called');
}
};
};
}
};
};

const source = Observable.from(iterable);
source.subscribe({
next: v => results.push(v),
complete: () => results.push('complete'),
}, {signal: ac.signal});
// Test for sync iterators.
{
const ac = new AbortController();
iterable.controller = ac;
Object.defineProperty(iterable, Symbol.iterator, {
get: iterable.getter,
});

assert_array_equals(results, []);
}, "from(): When iterable conversion aborts the subscription, next() is " +
"never called");
test(() => {
const results = [];
const ac = new AbortController();
const source = Observable.from(iterable);
assert_false(ac.signal.aborted, "[Sync iterator]: signal is not yet aborted after from() conversion");
assert_array_equals(results, ["GETTER called"]);

const iterable = {
[Symbol.asyncIterator]() {
ac.abort();
return {
val: 0,
next() {
results.push('next() called');
return {done: true};
},
return() {
results.push('return() called');
}
};
}
};
source.subscribe({
next: n => results.push(n),
complete: () => results.push('complete'),
}, {signal: ac.signal});
assert_true(ac.signal.aborted, "[Sync iterator]: signal is aborted during subscription");
assert_array_equals(results, ["GETTER called", "GETTER called", "iterator obtained"]);
}

const source = Observable.from(iterable);
source.subscribe({
next: v => results.push(v),
complete: () => results.push('complete'),
}, {signal: ac.signal});
results = [];

assert_array_equals(results, []);
}, "from(): When async iterable conversion aborts the subscription, next() " +
"is never called");
// Test for async iterators.
{
// Reset `iterable` so it can be reused.
const ac = new AbortController();
iterable.controller = ac;
iterable.calledOnce = false;
iterable[Symbol.iterator] = undefined;
Object.defineProperty(iterable, Symbol.asyncIterator, {
get: iterable.getter
});

const source = Observable.from(iterable);
assert_false(ac.signal.aborted, "[Async iterator]: signal is not yet aborted after from() conversion");
assert_array_equals(results, ["GETTER called"]);

source.subscribe({
next: n => results.push(n),
complete: () => results.push('complete'),
}, {signal: ac.signal});
assert_true(ac.signal.aborted, "[Async iterator]: signal is aborted during subscription");
assert_array_equals(results, ["GETTER called", "GETTER called", "iterator obtained"]);
}
}, "from(): When iterable conversion aborts the subscription, next() is " +
"never called");

// This test asserts some very subtle behavior with regard to async iterables
// and a mid-subscription signal abort. Specifically it detects that a signal
Expand Down Expand Up @@ -1659,3 +1695,37 @@ test(() => {
assert_not_equals(reportedError, null, "Protocol error is reported to the global");
assert_true(reportedError instanceof TypeError);
}, "Invalid async iterator protocol error is surfaced before Subscriber#signal is consulted");

test(() => {
const controller = new AbortController();
const iterable = {
calledOnce: false,
get[Symbol.iterator]() {
if (this.calledOnce) {
controller.abort();
return null;
} else {
this.calledOnce = true;
return this.validImplementation;
}
},
validImplementation() {
controller.abort();
return null;
}
};

let reportedError = null;
self.addEventListener("error", e => reportedError = e.error, {once: true});

let errorThrown = null;
const observable = Observable.from(iterable);
observable.subscribe({
error: e => errorThrown = e,
}, {signal: controller.signal});

assert_equals(errorThrown, null, "Protocol error is not surfaced to the Subscriber");

assert_not_equals(reportedError, null, "Protocol error is reported to the global");
assert_true(reportedError instanceof TypeError);
}, "Invalid iterator protocol error is surfaced before Subscriber#signal is consulted");

0 comments on commit 3a6ceb8

Please sign in to comment.