Skip to content
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

Tests for BitInt.asIntN #1191

Merged
merged 4 commits into from
Sep 6, 2017
Merged

Tests for BitInt.asIntN #1191

merged 4 commits into from
Sep 6, 2017

Conversation

thejoshwolfe
Copy link
Contributor

I intend to make more BigInt tests. This is what I have so far. I wanted to submit this PR to get feedback on organization/style/coverage/etc before duplicating and modifying these for BigInt.asUintN.

Copy link
Member

@leobalter leobalter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some comments but most of them are only for minor fixes.

BigInt.asIntN ( bits, bigint )

2. Let bigint ? ToBigInt(bigint).
features: [Symbol]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should add the arrow-function feature as well, or at least use regular functions instead.

BigInt.asIntN ( bits, bigint )

1. Let bits be ? ToIndex(bits).
features: [Symbol]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should add the arrow-function feature as well, or at least use regular functions instead.

assert.throws(SyntaxError, () => BigInt.asIntN(0, "foo"));
assert.sameValue(BigInt.asIntN(3, "10"), 2n);
assert.sameValue(BigInt.asIntN(4, "12345678901234567890003"), 3n);
assert.throws(TypeError, () => BigInt.asIntN(0, Symbol("1")));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the mixed sameValue and throws calls makes it so hard to read. I know this is esthetic, but it would help a lot if they are sorted.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought of that too. I organized it here in a depth-first traversal order of the spec for all this. It was easier for me to make sure I was getting some decent coverage with this organization, but I can easily sort it to make it clearer to read.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might be nitpicking on this, so just do as you prefer. It's ok in both ways.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might be nitpicking on this

I don't think you are, but we can always clean it up later.


function MyError() {}

assert.sameValue(BigInt.asIntN(3, Object(10n)), 2n);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is basically the same as testing with 10n, there's no coercion here, AFAICT.

There should be other tests - not for this PR - to test calling Object with a bigint.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Object(10n) creates a BigInt object, wrapping the primitive.

Copy link
Member

@leobalter leobalter Aug 22, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in this case, we should use new BigInt, explicit.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new BigInt throws, whereas Object(10n) creates a wrapper.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh I see. Yes, Object(10n) seems like the only way to create an object with an internal [[BigIntData]].

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@leo yes, similar to:

Object("string") instanceof String

Which will have:

[[PrimitiveValue]]: "string"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, I was nitpicking, requesting something like an explicit new String call instead of using Object.

Anyway, that cannot happen with the BigInt ctor.

assert.throws(TypeError, () => BigInt.asIntN(Symbol(0), 0n));
assert.throws(TypeError, () => BigInt.asIntN(1n, 0n));
assert.sameValue(BigInt.asIntN(Object(3), 10n), 2n);
assert.sameValue(BigInt.asIntN({valueOf:()=>3}, 10n), 2n);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think checking Object(3) is necessary as we already have this custom object here. Removing the previous line (with the Object call) should be fine, otherwise, it could be explicit using new Number.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it hurt to have tests like this? In some places in JS, Number wrappers are treated differently from other objects.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really understand the preference for new Number of Object.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new Number is explicit. If we want to be precise at the point we compare an ordinary object with a valueOf method vs another object with a primitive value, this all goes back to the fact we need tests for Symbol.toPrimitive calls or .toString calls after valueOf.

Copy link
Contributor Author

@thejoshwolfe thejoshwolfe Aug 23, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can add tests with Symbol.toPrimitive methods. I originally shied away from writing thorough tests for ToPrimitive for such a distant method as BigInt.asIntN, but if that's what we want, I can do that.

I figured at some point implementations are going to be using the same ToPrimitive implementation that they got test coverage for in another context. It seems like more work to violate the details of ToPrimitive for only BigInt.asIntN than to just use a common ToPrimitive implementation. So it seems not very important to test every corner case of every reachable abstract operation from every user-facing API, but I can see the appeal of that approach as well.

Should I throw in some Symbol.toPrimitive tests? If so, I think I agree that they and some of these other ToPrimitive tests should be sectioned out into a separate test file.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, it's good to have extensive coercion tests. You're right.

I don't think we need to check in tests for all combinations of possible coercion tests x operations which coerce to BigInt. However, this is the first place where we're testing coercions exhaustively, so it's not a bad place to put them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So are you saying that BigInt.asIntN is going to be the call that exhaustively tests the type coercion for ToBigInt? And then BigInt.asUintN would not need as thorough tests? Is that what we're suggesting?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

other methods will still use similar tests, but after this part is done, anything next should be easier.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just noticed that ? GetMethod(input, @@toPrimitive) can throw a TypeError if the [Symbol.toPrimitive] is not callable. I should probably add a test for that too.

I'm getting nervous about writing such thorough tests under the name "BigInt" for an aspect of the spec that has nothing to do with the BigInt proposal. This kind of thing makes #1197 appealing.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in a user level, this might not seem relevant, but the purpose of this project is to confirm the observable steps in the spec, in the order they are defined. This is crucial to mitigate inconsistency from browsers and even detect issues like those in the web reality matters.

In the moment we say it's relevant for the spec to say a TypeError should be thrown if @@toPrimitive is not callable, a test for that is important, and that's a part of the BigInt api being covered here.

Although, it's not necessary to write a complete coverage in a single Pull Request, it can be fixed through follow up Pull Requests and I'm happy to volunteer writing those.


assert.sameValue(BigInt.asIntN(3, Object(10n)), 2n);
assert.sameValue(BigInt.asIntN(3, {valueOf:()=>10n}), 2n);
assert.throws(MyError, () => BigInt.asIntN(0, {valueOf(){throw new MyError();}}));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also not observing calls to @@toPrimitive or calling the toString method. I believe these are worth to be in a new separate file, splitting into one for objects and other for direct primitives coercion, at least.

Copy link
Member

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 you're getting at. Do you mean that the test doesn't sufficiently assert that it's being reached? This is converting to a numeric type, so I believe it would call valueOf.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We observe it calling valueOf only, we don't have any observations for Symbol.toPrimitive or .toString calls when valueOf is not available

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought you were opposed to too many redundant combinations of tests, as you've been removing them from the repository. This test is trying to get at some of what's involved in type conversions, but doesn't do all possible tests. I don't think the distinctions you're raising are extremely likely to cause bugs.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think the distinctions you're raising are extremely likely to cause bugs.

That's a common phrase before tests. :)

I thought you were opposed to too many redundant combinations of tests, as you've been removing them from the repository. This test is trying to get at some of what's involved in type conversions, but doesn't do all possible tests.

I don't think these would be redundant, but they would check other easy to observe units (getting and calling other methods). With that, we could also check the order of them being called, as valueOf throwing a custom error after a valid @@toPrimitive return, e.g.

assert.throws(TypeError, () => BigInt.asIntN(1n, 0n));
assert.sameValue(BigInt.asIntN(Object(3), 10n), 2n);
assert.sameValue(BigInt.asIntN({valueOf:()=>3}, 10n), 2n);
assert.throws(MyError, () => BigInt.asIntN({valueOf(){throw new MyError();}}, 0n));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as mentioned above:

This is also not observing calls to @@toPrimitive or calling the toString method. I believe these are worth to be in a new separate file, splitting into one for objects and other for direct primitives coercion, at least.


verifyNotEnumerable(BigInt.asIntN, 'length');
verifyNotWritable(BigInt.asIntN, 'length');
verifyConfigurable(BigInt.asIntN, 'length');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we're trying to get rid of these separate verify helpers. verifyProperty should be fine:

verifyProperty(BigInt.asIntN, 'length', {
  value: 2,
  enumerable: false,
  writable: false,
  configurable: true
});

The same applies for other test files in this PR

// Copyright (C) 2017 Josh Wolfe. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: pending
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we already have an esid: sec-integer.asintn

https://tc39.github.io/proposal-bigint/#sec-integer.asintn

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the current policy on esid--do you add it for things that are at Stage 3/4, or only once it's integrated into the main spec?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

esid should be pending when an id is not available. In this case, we can have a prediction in the proposal. esid: pending should be the last resource, as it's becoming common to see tests left this way without any fix by the original contributors. The sooner we have it, better. Otherwise, this will cause an extra maintenance cost.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the section won't be renamed from integer to bigint?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess yes, so sec-bigint.asintn. If it's still cloudy, let's keep esid: pending. I just need to include it into my future tasks as soon as it is defined.

BigInt.asIntN ( bits, bigint )

3. Let mod be a BigInt representing bigint modulo 2**bits.
4. If mod ≥ 2**bits - 1, return mod - 2**bits; otherwise, return mod.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all the test files should add a features: [BigInt, BigInt.asIntN].

These features should be added to the features list as well: https://github.com/tc39/test262/blob/master/features.txt

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if BigInt.asIntN is necessary to add here, maybe BigInt only.

cc @rwaldron

Copy link
Member

@littledan littledan Aug 22, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1. I'd say BigInt is enough of a feature tag; I don't think we need feature tags for each little method of the feature.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, we should follow only with BigInt for now. This can be easily fixed in the future if anyone provides another feedback.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The features.txt mentions Symbol as a "standard language feature" that's "largely maintained for historical reasons". How important is it to declare my dependency on that feature?

And for the arrow-function issue below, is there any value in me avoiding arrow functions to avoid depending on arrow functions? Is there any concern for allowing a partially-conformant implementation to test their support for BigInt without also needing support for arrow functions? If not, I can easily just throw arrow-function into the features declaration.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are trying to flag every feature that date from ES6+. We missed a bunch for some ES6, but we still have a reasonable amount of Symbol uses.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm +1 to BigInt only. BigInt.asIntN is part of the whole BigInt

Copy link
Member

@leobalter leobalter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good. Any further improvements can be done in a follow up PR. e.g. the esid.

@leobalter leobalter merged commit 443b15e into tc39:master Sep 6, 2017
@thejoshwolfe thejoshwolfe deleted the bigint branch September 7, 2017 14:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants