-
Notifications
You must be signed in to change notification settings - Fork 475
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Promise.prototype.finally: add tests #1156
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These tests look good--it's nice to see edge cases tested.
One interaction that I'm wondering about is the interaction with the HostPromiseRejectionTracker hook. Seems like the Promise there will be the result of finally
, rather than the Promise used as input to it.
It'd be nice to have a test for this behavior, as I imagine some implementers might intuit to go the other way. That could be through host hooks, or if that's too tough, in a web-platform-test.
That's a good point, the promise caught should be the result of the let p = Promise.reject(new Error());
let p2 = p.finally(() => {}); The unhandled rejection should be p2 and not p. |
@benjamingr To be clear, I believe the semantics you're saying are the right ones are what the specification implies. I was just wondering about tests for this property. Thanks for spelling it out better. |
If there are existing tests for this hook, then I should be able to utilize them to test it here - if not, I'd need some guidance. |
@ljharb Node tracks them with If you'd like help I can send you links to the PRs adding the hooks which should shed light on how one might test them. |
@benjamingr i'm aware of how a specific implementation tests them; but how would test262 - which is supposed to be host-agnostic - test those? It's run on more engines than browsers and node, and the spec permits unhandled rejection tracking, but does not mandate it. |
I don't see any mention of |
Test262 would have to add a host hook to test this. I don't think it has a hook like this currently. If test262 maintainers don't like the idea of adding such a hook, another method of testing would be to add something to web-platform-tests. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have the proper time to do a complete review but I left some comments. Some are about whitespace or conventions.
Something I miss a lot reviewing the files is the info tag with at least some minimal spec part as a reference for the tests.
author: Jordan Harband | ||
description: Promise.prototype.finally invokes `then` method | ||
esid: sec-promise.prototype.finally | ||
features: [Promise, Promise.prototype.finally] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Promise.prototype.finally
is sufficient.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would nobody want to run tests against an engine without Promise
?
features.txt
Outdated
@@ -64,12 +68,19 @@ default-arg | |||
default-parameters | |||
destructuring-binding | |||
Float64Array | |||
Function.prototype.call |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe we should not add these features in this PR.
We can't assure this flag is enough here to capture every test using the feature. The same goes for any other new features added that are not Promise.prototype.finally
.
|
||
verifyNotEnumerable(Promise.prototype.finally, 'length'); | ||
verifyNotWritable(Promise.prototype.finally, 'length'); | ||
verifyConfigurable(Promise.prototype.finally, 'length'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
propertyHelper as a new function that we should use instead:
verifyProperty(Promise.prototype.finally, "length", {
value: 1,
enumerable: false,
configurable: true,
writable: false,
});
The order of the properties are not relevant in this function.
description: Promise.prototype.finally `name` property | ||
esid: sec-promise.prototype.finally | ||
info: > | ||
ES6 Section 17: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are over ES6 :)
ES should be fine
@@ -7,6 +7,10 @@ | |||
# | |||
# https://github.com/tc39/process-document | |||
|
|||
# Promise.prototype.finally | |||
# https://github.com/tc39/proposal-promise-finally | |||
Promise.prototype.finally |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perfect, thanks!
author: Jordan Harband | ||
description: > | ||
Promise.prototype.finally called with a `this` value whose `then` property is | ||
an accessor property that returns an abrupt completion |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for the clarification here.
}); | ||
|
||
assert.throws(Test262Error, function() { | ||
Promise.prototype.finally.call(poisonedThen); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this would like calling poisonedThen.finally()
, right? We can add a new assert.throws
in this file.
|
||
assert.throws(Test262Error, function() { | ||
Promise.prototype.finally.call(thrower); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this would like calling thrower.finally()
, right? We can add a new assert.throws
in this file.
|
||
p.then = undefined; | ||
assert.throws(TypeError, function() { | ||
Promise.prototype.finally.call(p); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we could poison finally to make sure TypeError happens at the expected time:
Correct me if I'm not using the API properly:
p.finally(function() { throw new Test262Error('this should not run'); });
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah, interesting. sure, i can do that (no need to poison the finally method, but i can certainly pass a poisoned onFinally)
|
||
assert.throws(TypeError, function() { | ||
Promise.prototype.finally.call(undefined); | ||
}, 'undefined'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
here you can poison a promise's then
:
const p = new Promise(function() {})
p.then = function() { throw new Test262Error('poison'); };
assert.throws(TypeError, function() {
p.finally.call(undefined);
}, 'undefined');
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure what value this adds; it we use .call
then the function has no more link to p
, so p.then
couldn't be reached even if i wanted it to.
I would need more time to review this, maybe @rwaldron is interested. |
Thanks, updated! I haven't added "info" since I'm not sure where to link to prior to actually merging with the spec. |
Quick question: do these tests pass when running V8 with --harmony-promise-finally flag? FYI, there's a lot of V8 tests as well if you want to import them: https://cs.chromium.org/chromium/src/v8/test/mjsunit/harmony/promise-prototype-finally.js |
@gsathya running test262 tests isn't easy; i can definitely give that a shot :-) in general tho, wouldn't it make sense to never write v8-only tests, when something could go in test262? :-p |
Sounds good. Thanks! Node 8 should have this feature behind the flag, no need to build d8.
Until quite recently it wasn't possible to write test262 tests as part of a v8 patch -- we had to do out of band test262 code review dance and then roll that change into v8. @littledan has done some great work in making this possible now, so going forward we'll be writing more test262 tests. |
Tests pass with both my |
However, when I run Needless to say we'll have to make sure both implementations as well as the spec and the test262 tests are all in line :-) |
I'll dig into this. |
@ljharb the flag is |
I wouldn't bother using Node.js to test anything until the Node.js patch to eshost has landed. Also, I am experiencing the same problem with (inside ConsoleAgent.js, which is in eshost) createChildProcess(args) {
args = args || [];
args = args.concat(this.args);
return cp.spawn(this.hostPath, args);
} Which results in the args coming out as (for example):
The flag needs to be before the file:
So I will make a PR to eshost that re-arranges the args for Moving on to my local run results: d8
|
@rwaldron ah! thanks. with |
Using my patch, I was able to confirm these tests on Node.js 8.2.1 Nice work @ljharb! We can do the eshost dependent tests in a follow up PR. |
figured it out; it's a bug in eshost via test262-harness. When patched, (edit: lol, github didn't update so i missed you figuring this out half an hour ago) |
Ah, that's probably very likely. There's no reason why NodeAgent couldn't/shouldn't try to do that as well. |
I was wrong. Either dash or underscore work just fine, the issue was always just the order of the args. Patch to eshost is coming. |
|
||
no.catch(function (e) { | ||
assert.sameValue(e, noReason); | ||
throw e; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would add cases with thow e
, within fulfillment and rejection handlers i.e.:
Promise.resolve(1)
.then(function () { throw new Error('');})
.finally(/* Check if finally is reached */);
Promise.reject(1)
.then(function () {}, function () { throw new Error('');})
.finally(/* Check if finally is reached */)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that we need test that we raise TypeError when we use finally
with new
i.e.
const p = new Promise((resolve)=>{});
assert.throws(TypeError, function() {
const f = new p.finally();
}, 'no constructor');
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm - I can add that test, but p.finally()
returns a Promise, so I have no idea why it'd ever work with new
, and .catch()
lacks the same test. I'm pretty sure we don't test every returned object value to ensure it doesn't work with new
- would checking that typeof
wasn't "function" suffice?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ohh, I see. You're right.
So it will be enough to check if finally
returns Promise
. Not sure that I can find such test, or I missed something.
If there is no such test we can use Symbol.toStringTag
:
const p = Promise.resolve(1);
assert.sameValue(p.finally()[Symbol.toStringTag], 'Promise');
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd think chaining a .then
or .catch
on the end would be sufficient, and I think some of the tests already do that.
The patch to eshost is in final stages now, so I don't consider this "blocked" anymore (not that it ever was, I just hoped to get that fixed first... you know how it is). |
Success! |
assert.sameValue(arguments.length, 0, 'onFinally receives zero args'); | ||
return {}; | ||
}).then(function (x) { | ||
assert.sameValue(x, obj, 'onFinally can not override the resolution value'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the onFinally and the onFulfilled callbacks are skipped, the tests would still pass right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(I see other tests also that don't verify if the onFulfilled/onRejected/onFinally has actually run.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, that's true. i can make a followup PR that adds counters to ensure they're called; that's a good idea
We are missing tests that call the SpeciesConstructor. The following test fails in V8: // Flags: --allow-natives-syntax --harmony-promise-finally
var thenCalled = 0;
class FooPromise extends Promise {
then(onFullfilled, onRejected) {
thenCalled++;
return super.then(onFullfilled, onRejected);
}
}
var x = new FooPromise(r => r()).finally(() => {});
%RunMicrotasks();
// (1) Promise.prototype.finally
// 1. 7. Return ? Invoke(promise, "then", thenFinally, catchFinally).
// (2) ThenFinally function
// 1.1. 8. Return ? Invoke(promise, "then", valueThunk>
// (3) Promise Resolve Functions
// 25.4.1.3.2. 8. Let then be Get(resolution, "then").
print(thenCalled == 3); |
Thanks, I'll write a followup PR soon that strengthens the tests! |
+1 for follow up PRs.
…On Fri, Aug 11, 2017 at 7:40 PM Jordan Harband ***@***.***> wrote:
Thanks, I'll write a followup PR this weekend that strengthens the tests!
—
You are receiving this because you modified the open/close state.
Reply to this email directly, view it on GitHub
<#1156 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AASYkV6LQzTLgjQccsBNcuGZt1tITMOdks5sXOZngaJpZM4Ojf3A>
.
|
#1398 is merged; but more followup is still ideal. |
(The first two commits can and should be cherry-picked in separately; i'll rebase this PR ASAP if that happens)
Promise.prototype.finally
reached stage 3 today at TC39; here's my first crack at the tests.See tc39/proposal-promise-finally#18, https://tc39.github.io/proposal-promise-finally/, and https://github.com/es-shims/Promise.prototype.finally/blob/master/test/tests.js for more info.