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

#1363. Tests for yield updated to the current specification #1370

Merged
merged 8 commits into from
Jul 25, 2022

Conversation

sgrekhov
Copy link
Contributor

No description provided.

Copy link

@alexmarkov alexmarkov left a comment

Choose a reason for hiding this comment

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

The new tests lgtm (modulo a couple of comments).

However, I think it would be good if @eernstg or @lrhn would review and approve these tests.

Copy link
Member

@eernstg eernstg left a comment

Choose a reason for hiding this comment

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

LGTM, but please consider the following as well:

A couple of things are worth considering: In several situations there is an ss.resume() followed by code which is intended to check that certain lists contain the expected elements. However, it looks like there should be a suspension point before those checks, because otherwise the generator doesn't get a chance to do anything wrong. So maybe await null; should be added?

Another thing to consider is that delays will make the trybots/buildbots run more slowly. I suspect that it will work just as well to have a simple await null; in several situations than it does to have a delay of 100 milliseconds. Waiting for an entire second is of course even more disruptive to the bot execution. We could have several await null;, too.

Expect.listEquals([1], readyToSend);
ss.resume();
await Future.delayed(Duration(milliseconds: 100));
Expect.listEquals([1, 2, 3], sent);
Copy link
Member

Choose a reason for hiding this comment

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

This looks dangerous: I don't think it would be a bug for an implementation to somehow fail to put all those elements into sent, in spite of the fact that it is probably extremely unlikely.

Perhaps just test that whatever elements are there is a prefix of the whole list?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed to

      // `sent` may contain not all elements of the stream
      sent.forEach((element) {
        Expect.isTrue([1, 2, 3].contains(element));
      });

Expect.listEquals([1, 2], readyToSend);
Expect.isTrue(sc.isPaused);
ss.resume();
await Future.delayed(Duration(milliseconds: 100));
Copy link
Member

Choose a reason for hiding this comment

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

Same issue as before: In principle, we can't know that other microtasks have actually been scheduled just because there was a delay.

Copy link
Member

Choose a reason for hiding this comment

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

The delay is a timer-delay, so we know that all microtask-based events have been processed.
I'm not sure we promise that buffered non-sync-controller events are delivered using microtasks, but if they're not, it'd probably be zero-timer events.
So, this likely works.

ss.cancel();
if (!c.isCompleted) {
c.complete();
}
Copy link
Member

Choose a reason for hiding this comment

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

Should it not be safe to replace line 49,50,51 by c.complete()? How would it fail?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed. Fixed. Thank you

await ss.cancel();
if (!c.isCompleted) {
c.complete();
}
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 previous test: Why not have only c.complete()?

late StreamSubscription<int> ss;
ss = s.listen((int x) async {
if (!generatorAlive) {
Expect.fail("Generator function must be terminated");
Copy link
Member

Choose a reason for hiding this comment

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

Probably Expect.fail("The generator function is unexpectedly not alive") would be easier to understand.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed

Expect.fail("Generator function must be terminated");
}
if (x == 5) {
// let generator to work some time
Copy link
Member

Choose a reason for hiding this comment

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

Perhaps // Allow the generator to work for some time.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed everywhere

}
if (x == 5) {
// let generator to work some time
Expect.equals(true, generatorAlive);
Copy link
Member

Choose a reason for hiding this comment

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

Why not Expect.isTrue(generatorAlive)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

finish = false;
Stream<int> s = generator();
s.listen((int x) async {
finish = (x > 10); // let generator to work some time
Copy link
Member

Choose a reason for hiding this comment

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

// Allow the generator....

finish = false;
Stream<int> s = generator();
s.listen((int x) {
finish = (x > 10); // let generator to work some time
Copy link
Member

Choose a reason for hiding this comment

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

// ...

late StreamSubscription<int> ss;
ss = s.listen((int i) async {
// This 'await' returns control back to event loop and generator goes on
await Future.delayed(Duration(milliseconds: 100));
Copy link
Member

Choose a reason for hiding this comment

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

I tried to replace this by await null; await null; await null;, and it works fine. ;-)

Replacing it by await null; await null; does not work, and this illustrates that it is directly connected to the number of other tasks we allow the scheduler to handle (except that we're not supposed to assume that we can control the scheduler in any precise manner by doing these things).

@sgrekhov
Copy link
Contributor Author

In several situations there is an ss.resume() followed by code which is intended to check that certain lists contain the expected elements. However, it looks like there should be a suspension point before those checks, because otherwise the generator doesn't get a chance to do anything wrong. So maybe await null; should be added?

If to add a suspension point after the resume it'll chane the expected behavior. Please see dart-lang/sdk#49465 So, when there is no such point it usually means "resume, make sure that nothing was changed immediately after the resume, then check something more meaningful". If you find this check, immediately after resume(), excessive, I don't mind to remove it

Another thing to consider is that delays will make the trybots/buildbots run more slowly. I suspect that it will work just as well to have a simple await null; in several situations than it does to have a delay of 100 milliseconds. Waiting for an entire second is of course even more disruptive to the bot execution. We could have several await null;, too.
I think it's safe to use await null; after pause(); and then check that stream is really paused. Changet it to await null; in such places. Thank you.

Please rereview

@sgrekhov sgrekhov requested a review from eernstg July 25, 2022 09:30
Copy link
Member

@eernstg eernstg left a comment

Choose a reason for hiding this comment

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

LGTM!

I didn't understand that the checks after resume were intended to check the situation without any asynchronous scheduling events. When that's the purpose it is fine.

@eernstg eernstg merged commit acc78f9 into dart-lang:master Jul 25, 2022
sgrekhov added a commit to sgrekhov/co19 that referenced this pull request Aug 4, 2022
…art-lang#1370)

Authored by @sgrekhov.

Tests for yield fixed and updated to the current specification. Completer added to avoid flakiness. Missing asyncStart()/asyncEnd() added. Some delays replaced by `await null;`.
Copy link
Member

@lrhn lrhn left a comment

Choose a reason for hiding this comment

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

drive-by comments.

if (i == 2) {
Expect.listEquals([1], sent);
Expect.listEquals([1, 2], readyToSend);
ss.resume();
Copy link
Member

Choose a reason for hiding this comment

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

Not sure what ss.resume() should do. The subscription is never paused, so it should be a no-op.
(But if you are just testing that it's a no-op, then it's correct.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Description of this test

Check that resuming of the [Stream] which was not paused doesn't affect anything

So yes, it's testing that it's no-op

await Future.delayed(Duration(milliseconds: 100));
// `sent` may contain not all elements of the stream
sent.forEach((element) {
Expect.isTrue([1, 2, 3].contains(element));
Copy link
Member

Choose a reason for hiding this comment

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

Seems like a long way of saying element <= 3.
Not a very telling test - is it possible to fail it at all?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed. Thanks! Please see #1390

await ss.cancel();
}
});
await Future.delayed(Duration(seconds: 1));
Copy link
Member

Choose a reason for hiding this comment

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

A very long delay, where seconds: 0 would likely give precisely the same result because everything else is microtask based.

(The next variant, using a Completer, seems much closer to what I'd write for this test.)

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 latest version of this test doen't contain this long delay and is based on a Completer. Please see https://github.com/dart-lang/co19/blob/master/Language/Statements/Yield_and_Yield_Each/Yield/execution_async_A01_t03.dart

Expect.listEquals([1, 2], readyToSend);
Expect.isTrue(sc.isPaused);
ss.resume();
await Future.delayed(Duration(milliseconds: 100));
Copy link
Member

Choose a reason for hiding this comment

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

The delay is a timer-delay, so we know that all microtask-based events have been processed.
I'm not sure we promise that buffered non-sync-controller events are delivered using microtasks, but if they're not, it'd probably be zero-timer events.
So, this likely works.

copybara-service bot pushed a commit to dart-lang/sdk that referenced this pull request Aug 11, 2022
2022-08-04 sgrekhov22@gmail.com dart-lang/co19#993. FFI tests refactored and improved (dart-lang/co19#1387)
2022-08-04 sgrekhov22@gmail.com dart-lang/co19#993. [FFI] Missed tests added for some new types (dart-lang/co19#1386)
2022-08-03 sgrekhov22@gmail.com Fixes dart-lang/co19#1129. Don't expect any proxy routine in case of DIRECT connection (dart-lang/co19#1383)
2022-08-03 sgrekhov22@gmail.com dart-lang/co19#993. [FFI] Missed tests added for some new types (dart-lang/co19#1384)
2022-07-29 sgrekhov22@gmail.com dart-lang/co19#673. IFrame tests that don't actually test IFrame removed (dart-lang/co19#1382)
2022-07-29 sgrekhov22@gmail.com dart-lang/co19#1363. Some delays removed or reduced (dart-lang/co19#1380)
2022-07-29 sgrekhov22@gmail.com dart-lang/co19#1363 Async `for-in` test updated according to the current specification (dart-lang/co19#1379)
2022-07-27 sgrekhov22@gmail.com dart-lang/co19#1363. Yield each tests updated to the current specification (dart-lang/co19#1374)
2022-07-27 sgrekhov22@gmail.com Fixes dart-lang/co19#1375. Add test for final variable in a for-in loop (dart-lang/co19#1378)
2022-07-25 sgrekhov22@gmail.com dart-lang/co19#1363. Tests for yield updated to the current specification (dart-lang/co19#1370)

Change-Id: Ie92f4b2157284777177012320883247411e214fa
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/254140
Reviewed-by: Erik Ernst <eernst@google.com>
Commit-Queue: Alexander Thomas <athom@google.com>
@sgrekhov sgrekhov deleted the co19-1363 branch September 29, 2022 09:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants