Skip to content

Add continuous pumping for animation#60410

Closed
CareF wants to merge 11 commits intoflutter:masterfrom
CareF:widgetTester_with_time
Closed

Add continuous pumping for animation#60410
CareF wants to merge 11 commits intoflutter:masterfrom
CareF:widgetTester_with_time

Conversation

@CareF
Copy link
Contributor

@CareF CareF commented Jun 27, 2020

Description

Implements a pumpContinuous method in WidgetTester to support testing infinite animation.

Related Issues

Closes #60372

Tests

I added the following tests:

  • "pumpContinuous" test in package/flutter_test/test/widget_tester_test.dart

Checklist

Before you create this PR confirm that it meets all requirements listed below by checking the relevant checkboxes ([x]). This will ensure a smooth and quick review process.

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • I signed the CLA.
  • I read and followed the Flutter Style Guide, including Features we expect every widget to implement.
  • I read the Tree Hygiene wiki page, which explains my responsibilities.
  • I updated/added relevant documentation (doc comments with ///).
  • All existing and new tests are passing.
  • The analyzer (flutter analyze --flutter-repo) does not report any problems on my PR.
  • I am willing to follow-up on review comments in a timely manner.

Breaking Change

Did any tests fail when you ran them? Please read Handling breaking changes.

  • No, no existing tests failed, so this is not a breaking change.

@CareF CareF self-assigned this Jun 27, 2020
@fluttergithubbot
Copy link
Contributor

It looks like this pull request may not have tests. Please make sure to add tests before merging. If you need an exemption to this rule, contact Hixie on the #hackers channel in Chat.

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing.

@fluttergithubbot fluttergithubbot added a: tests "flutter test", flutter_test, or one of our tests framework flutter/packages/flutter repository. See also f: labels. labels Jun 27, 2020
@CareF CareF marked this pull request as draft June 27, 2020 03:03
@CareF CareF requested a review from liyuqian June 28, 2020 14:57
@CareF CareF marked this pull request as ready for review June 28, 2020 14:58
@CareF CareF requested a review from dkwingsmt June 28, 2020 14:59
Copy link
Contributor

Choose a reason for hiding this comment

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

With the new integration perf test use cases, this LiveTestWidgetsFlutterBindingFramePolicy.benchmark sounds a little strange. At a first glance, I'd guess that we should use it for perf CI tests. However, it seems to be microbenchmark-only which doesn't wait for the engine VSYNC signal, and wouldn't exercise the engine frame rasterization.

To avoid breaking changes, we probably won't change the name of LiveTestWidgetsFlutterBindingFramePolicy.benchmark. But we may need to document it properly.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not 100% sure if I understand this benchmark flag but I added some comments in packages/flutter_test/lib/src/binding.dart

Copy link
Contributor

Choose a reason for hiding this comment

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

We should just document which frame scheduling polcies are incompatible with this method. I think it's probably fine.

Below this: do not throw a string, instead throw a StateError or similar here. I would also avoid documenting too much in the exception message, just say that they're not compatible and document why in the actual method/enum documentation.

Copy link
Contributor Author

@CareF CareF Jun 30, 2020

Choose a reason for hiding this comment

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

@dnfield The exception part is almost a copy of pumpAndSettle. Should I also change that?

Copy link
Contributor

Choose a reason for hiding this comment

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

If I understand correctly, this clock is a simulated clock that supports time travel (e.g., fast forward or slowdown). So we would render 60 frames for a 1-second-60-fps animation here no matter how slow the actual frame is. That is, if a frame takes 1 second to render, this could spend 1 minute rendering 60 frames and the simulated clock here think it's just 1 second.

Therefore, it may not necessarily simulate and benchmark how an app is performing on a real device. But it would still be very useful in terms of performance testing ( https://mail.google.com/mail/u/0/?pli=1#inbox/FMfcgxwHMZNWmbPBjLTWJhzlnLLfBplZ).

It would be nice if we could have both modes: (1) render only 1 frame for the 1-second-60-fps animation if a frame takes 1 second; (2) render 60 frames for 60 seconds if a frame takes 1 second for the 1-second-60-fps animation.

Copy link
Contributor Author

@CareF CareF Jun 29, 2020

Choose a reason for hiding this comment

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

https://api.flutter.dev/flutter/flutter_test/TestWidgetsFlutterBinding/clock.html I think for such purposes we should override the clock getter in TestWidgetsFlutterBinding, which is what LiveTestWidgetsFlutterBinding and AutomatedTestWidgetsFlutterBinding do:

  • LiveTestWidgetsFlutterBinding is using real world clock and therefore gives (1);
  • AutomatedTestWidgetsFlutterBinding is using a FakeAsync clock that is controllable. Although I don't find an API to explicitly control it, AutomatedTestWidgetsFlutterBinding.pump forward the clock by the given interval (
    _currentFakeAsync.elapse(duration);
    ) and achieves (2).

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 very unclear as to the value of this method in AutomatedTestWidgetsFlutterBinding. Why aren't we just throwing in that case?

This method seems like it is meant to measure the number of frames pumped for an animation on a real device with a real clock. Why would we want to measure any of that with fakeasync, which could get hung up by any number of problems in a test/benchmark such as trying to do real async work?

Copy link
Contributor

Choose a reason for hiding this comment

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

To me, it seems the returned frame count isn't very interesting. I'll be more interested in the FrameTiming resulted from the frames driven by this.

From the meeting with Ming just a few minutes ago, I think LiveTestWidgetsFlutterBinding may be less interesting to us in this case as we can just use LiveTestWidgetsFlutterBindingFramePolicy.fullyLive to drive the frames and completely remove the need of pump or pumpContinuous.

The interesting use case of pumpContinuous seems to be implementing a fake clock performance test suggested by Yegor in https://docs.google.com/document/d/1sGh6tvRUUNHI-VtnphliiYNJmmC_jo7afzQtAStPteg/edit?ts=5e80f082#bookmark=id.czojbloii9ww.

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'm very unclear as to the value of this method in AutomatedTestWidgetsFlutterBinding. Why aren't we just throwing in that case?

This method seems like it is meant to measure the number of frames pumped for an animation on a real device with a real clock. Why would we want to measure any of that with fakeasync, which could get hung up by any number of problems in a test/benchmark such as trying to do real async work?

The reason I return an int is to be consistent with pumpAndSettle.

Copy link
Contributor Author

@CareF CareF Jun 30, 2020

Choose a reason for hiding this comment

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

To me, it seems the returned frame count isn't very interesting. I'll be more interested in the FrameTiming resulted from the frames driven by this.

From the meeting with Ming just a few minutes ago, I think LiveTestWidgetsFlutterBinding may be less interesting to us in this case as we can just use LiveTestWidgetsFlutterBindingFramePolicy.fullyLive to drive the frames and completely remove the need of pump or pumpContinuous.

The interesting use case of pumpContinuous seems to be implementing a fake clock performance test suggested by Yegor in https://docs.google.com/document/d/1sGh6tvRUUNHI-VtnphliiYNJmmC_jo7afzQtAStPteg/edit?ts=5e80f082#bookmark=id.czojbloii9ww.

In my case LiveTestWidgetsFlutterBindingFramePolicy.fullyLive does work with an AnimationController-triggered animation, but from the wording of the comments I'm not sure if it's designed for synchronized running of a test app, and if pump is completely not needed (or at least can always be replaced by Future.Delayed). I think I need confirm.

Copy link
Contributor

Choose a reason for hiding this comment

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

I would find it very helpful to see a test that actually needs this as opposed to just using fullyLive. @liyuqian the linked doc isn't entirely clear to me, but I'm also not as familiar with that metric or its uses. Also, FYI, the doc is google-only.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Just not to be mixed with my other PRs / proposals, this was part of my plan but now it's more like a convenience wrap for infinite animation.

@CareF CareF requested a review from liyuqian June 29, 2020 23:31
Copy link
Contributor

@liyuqian liyuqian left a comment

Choose a reason for hiding this comment

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

Mostly looks good. Let's have @dnfield and @Hixie review this too as it may change flutter_test's API.

It might be better to also have a small integration performance test example to demonstrate how this will be used, and thus justify the API change.

Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: "This is not for integration performance tests" may be better than "This is not benchmark for general performance test"?

BTW, from the usages of pumpBenchmark, I had an impression that this is for benchmarking the UI thread frame build time only. This does not seem to be suitable for benchmarking the raster thread performance? CC @dnfield @Hixie for confirmation.

Copy link
Contributor Author

@CareF CareF Jun 30, 2020

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

Flutter style says that the first line should be a single sentence.

We should add any further explanations below the first line.

This also is a bit confusing to me, especially given the subsequent documentation below this - maybe we could just amend those lines to explain what types of benchmarks a user is interested in and which types this is meant for?

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'm thinking if we should combine the dartdocs I listed in the above link and change one of them to a link. And it doesn't explicitly say it but I found benchmark flag prevents any ticker triggered animation (which can be inferred from it ignores framework from triggering new frame), which I think should be added to the doc.

Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: pumpContinuously may be more grammatically correct?

Also CC @dnfield and @Hixie for naming ideas and potential flutter_test API changes.

Copy link
Contributor

Choose a reason for hiding this comment

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

This sentence isn't quite right - can you explain what you're trying to say here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

My original suggestion was "This is not for integration performance tests". What doesn't sound quite right to me is the "This is not benchmark for" part. I felt that "This is not benchmarking for" may be more correct? If "integration performance tests" sounds strange, how about "Do not use this for integration tests (https://flutter.dev/docs/cookbook/testing/integration)."?

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 changed it to " This does not mean benchmarking performance on integration tests."

@CareF CareF requested a review from liyuqian July 6, 2020 23:19
@CareF CareF requested a review from dnfield July 6, 2020 23:19
/// This is an analog to screen refresh rate on a device. The frequency rate
/// will be rounded down to integer microseconds frame interval.
/// Default value is 59.94 Hz, which is the real refresh rate for many devices
/// when it shows as 60 Hz, for historical reason.
Copy link
Contributor

Choose a reason for hiding this comment

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

we should just use 60Hz, which is what we use everywhere else. It doesn't matter what the actual device rate is since this is artificial.

...except maybe you mean this to not be artificial? I admit I don't really understand the use case here.

Copy link
Contributor

@dkwingsmt dkwingsmt Jul 7, 2020

Choose a reason for hiding this comment

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

I used 59.94Hz in pumpFrames first in order to simulate the real devices. @CareF feel free to change pumpFrames if we agree to unify to 60. It shouldn’t break anything since the difference is so small.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

we should just use 60Hz, which is what we use everywhere else. It doesn't matter what the actual device rate is since this is artificial.

...except maybe you mean this to not be artificial? I admit I don't really understand the use case here.

I believe there is nothing more than a number. The historical reason I learnt is to avoid resonance with 60Hz city electricity for old TVs and than it is used for newer screens as a convention (but not always respected). I'll change it to 60Hz.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@dkwingsmt It seems that change pumpFrames to 60Hz introduces a Golden file change.

/// flaky result result due to time fluctuation and rounding error.
///
/// Example:
/// testWidgets('pumpContinuous', (WidgetTester tester) async {
Copy link
Contributor

Choose a reason for hiding this comment

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

examples should use the snippet mechanism so that it gets syntax highlighting and flutter create templates and so on.

Copy link
Contributor

Choose a reason for hiding this comment

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

(and most importantly so that it gets analyzed in CI)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Can I have an example how this is done in flutter_test? The only example code in doc string I can find in flutter_test is https://api.flutter.dev/flutter/flutter_test/benchmarkWidgets.html . This example is almost a copy of my unit 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.

Can I have an example how this is done in flutter_test? The only example code in doc string I can find in flutter_test is https://api.flutter.dev/flutter/flutter_test/benchmarkWidgets.html . This example is almost a copy of my unit test.

Problem solved.

@skia-gold
Copy link

Gold has detected about 1 untriaged digest(s) on patchset 10. View them at https://flutter-gold.skia.org/cl/github/60410

@fluttergithubbot
Copy link
Contributor

Golden file changes have been found for this pull request. Click here to view and triage (e.g. because this is an intentional change).

Changes to golden files are considered breaking changes, so consult Handling Breaking Changes to proceed. While there are exceptions to this rule, if this patch modifies an existing golden file, it is probably not an exception. Only new golden file tests, or downstream changes like those from skia updates are considered non-breaking.

For more guidance, visit Writing a golden file test for package:flutter.

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing.

If you are still iterating on this change and are not ready to resolve the images on the Flutter Gold dashboard, consider marking this PR as a draft pull request above. You will still be able to view image results on the dashboard, and the check will not try to resolve itself until marked ready for review.

Changes reported for pull request #60410 at sha fe0dba8

@fluttergithubbot fluttergithubbot added c: API break Backwards-incompatible API changes will affect goldens Changes to golden files labels Jul 7, 2020
@CareF
Copy link
Contributor Author

CareF commented Jul 7, 2020

This is not critical now for my general project. Close it for now to reduce distraction. New comments and reviews are still welcomed but please don't spend too much time here.

@CareF CareF closed this Jul 7, 2020
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jul 30, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

a: animation Animation APIs a: tests "flutter test", flutter_test, or one of our tests c: API break Backwards-incompatible API changes framework flutter/packages/flutter repository. See also f: labels. will affect goldens Changes to golden files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[WidgetTester] pump for infinite animation

9 participants