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

Implement screenshot test for flutter web. #45530

Merged
merged 10 commits into from Dec 6, 2019

Conversation

chingjun
Copy link
Contributor

Description

  • Added a class WebGoldenComparator (corresponds to GoldenFileComparator for non-web), with default implementation being DefaultWebGoldenComparator.
  • DefaultWebGoldenComparator will issue a web request to the test server for taking and comparing the screenshot.
  • Added new files _matchers_io.dart and _matcher_web.dart, that contain the implementation of MatchesGoldenFile. On web, this matcher will first render the requested Element on screen, and defer the screenshot taking to the test server.
  • In flutter_test_platform.dart, add a flutter_golden handler for the test server, which does two things: screenshot taking and image comparison.
  • Screenshot is taken using WIP, and is clipped to only contain the Element requested.
  • For image comparison, it will start a flutter_tester process that loads flutter_test_config.dart file and uses the default goldenFileComparator to compare the golden images.
  • An additional FLUTTER_TEST_BROWSER environment variable is set when starting the flutter_tester for image comparison. SkiaClient is also updated to include this as one of the parameters.
  • Currently only one file is enabled for Flutter Web screenshot test, just to test the overall workflow. A followup PR will be sent to enable all screenshot tests for Flutter Web.

Related Issues

#40297

@chingjun chingjun added a: tests "flutter test", flutter_test, or one of our tests platform-web Web applications specifically labels Nov 25, 2019
@fluttergithubbot fluttergithubbot added framework flutter/packages/flutter repository. See also f: labels. team Infra upgrades, team productivity, code health, technical debt. See also team: labels. tool Affects the "flutter" command-line tool. See also t: labels. labels Nov 25, 2019

@override
Future<bool> compare(Element element, Size size, Uri golden) {
throw UnsupportedError('DefaultWebGoldenComparator is noonly supported on the web.');
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
throw UnsupportedError('DefaultWebGoldenComparator is noonly supported on the web.');
throw UnsupportedError('DefaultWebGoldenComparator is not supported on the web.');

Copy link
Contributor

Choose a reason for hiding this comment

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

I think this should read "only supported on the web".

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated. It's actually only supported instead of `not supported though.


@override
Future<void> update(Uri golden, Element element, Size size) {
throw UnsupportedError('DefaultWebGoldenComparator is noonly supported on the web.');
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
throw UnsupportedError('DefaultWebGoldenComparator is noonly supported on the web.');
throw UnsupportedError('DefaultWebGoldenComparator is not supported on the web.');

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

final num height = body['height'];
final Runtime browser = Runtime.chrome;
final BrowserManager browserManager = await _browserManagerFor(browser);
final ChromeTab chromeTab = await browserManager._browser.chromeConnection.getTab((ChromeTab tab) {
Copy link
Member

Choose a reason for hiding this comment

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

This code should handle the errors thrown by WIP, like WIPError (there might be some more). probably printError and move on if possible

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done, I've handled it by catching WIPError and returning the error to the caller.

/// be executed in a `flutter_tester` environment. This helper class generates a
/// Dart file configured with flutter_test_config.dart to perform the comparison
/// of golden files.
class _TestGoldenComparator {
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 a really interesting approach. i think this PR should have at least one end to end test that goes through this path, if possible.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added tests for TestGoldenComparator and TestGoldenComparatorProcess, PTAL, thanks.

@@ -371,6 +372,8 @@ class SkiaGoldClient {
return json.encode(
<String, dynamic>{
'Platform' : platform.operatingSystem,
if (platform.environment[_kTestBrowserKey] != null)
Copy link
Contributor

Choose a reason for hiding this comment

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

The documentation above will need to be updated.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

Copy link
Contributor

@yjbanov yjbanov left a comment

Choose a reason for hiding this comment

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

LGTM if LGT @Piinks and @jonahwilliams

Copy link
Contributor

@Piinks Piinks left a comment

Choose a reason for hiding this comment

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

Also, this appears to be breaking the current pre-submit workflow, see test failure in the framework-misc shards.

@chingjun
Copy link
Contributor Author

chingjun commented Dec 2, 2019

Talked to @Piinks and we are going to wait on #44474, reverted part of my changes in flutter_golden.

@goderbauer goderbauer removed the framework flutter/packages/flutter repository. See also f: labels. label Dec 4, 2019
@Piinks
Copy link
Contributor

Piinks commented Dec 4, 2019

#44474 has landed. If you update your branch, you should see the results of pre-submit checks on the Flutter Gold dashboard under Changelists, where you can approve them ahead of time.

@chingjun
Copy link
Contributor Author

chingjun commented Dec 5, 2019

Thanks Kate! Just rebased and updated my branch, waiting for the tests to run :)

@Piinks
Copy link
Contributor

Piinks commented Dec 5, 2019

Not all of the tests have finished yet, but it is looking promising!

Screen Shot 2019-12-04 at 4 33 22 PM

@chingjun chingjun force-pushed the flutter-web-screenshot-test branch 3 times, most recently from 8592f28 to d0c9f64 Compare December 5, 2019 22:06
Copy link
Contributor

@Piinks Piinks left a comment

Choose a reason for hiding this comment

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

Curious, does this enable golden file testing for web for everyone?

packages/flutter/test/widgets/shadow_test.dart Outdated Show resolved Hide resolved
packages/flutter/test/widgets/shadow_test.dart Outdated Show resolved Hide resolved
packages/flutter/test/widgets/shadow_test.dart Outdated Show resolved Hide resolved
@chingjun
Copy link
Contributor Author

chingjun commented Dec 5, 2019

Curious, does this enable golden file testing for web for everyone?

Yes. This PR only enabled that for a single test, and we plan to enable all the other tests in a later PR.

@chingjun
Copy link
Contributor Author

chingjun commented Dec 6, 2019

The tests now passed with the new images all approved on skia-gold.

@Piinks @jonahwilliams PTAL, thanks.

});
final WipConnection connection = await chromeTab.connect();
final WipResponse response = await connection.sendCommand('Page.captureScreenshot', <String, Object>{
// Clip the screenshot to include only the element.
Copy link
Member

Choose a reason for hiding this comment

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

Does this actually clip the screenshot in anyway? What if it is offset from the origin?

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 are calling window.render() before taking the screenshot (in _matchers_web.dart), so the element is supposed to be at the origin.

https://github.com/flutter/flutter/pull/45530/files#diff-a337fbcec47632d587865bec77ec07ddR88

Copy link
Member

Choose a reason for hiding this comment

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

Can you document that here and where the render is done for posterity?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added comment here, and in _matches_web.dart as well.

}
});
bytes = base64.decode(response.result['data'] as String);
} on WipError catch (ex) {
Copy link
Member

Choose a reason for hiding this comment

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

catch FormatException from decoding

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done


final String bootstrap = TestGoldenComparatorProcess.generateBootstrap(testUri);
final Process process = await _startProcess(bootstrap);
unawaited(_previousComparator?.close());
Copy link
Member

Choose a reason for hiding this comment

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

Does this work in the presence of test concurrency? Or does that not work for golden files?

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 noticed that we have --concurrency=1 for web tests on cirrus so I didn't take that into account when writing this.

Do we actually run tests concurrently for web? If so I can try to fix this.

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 talked to @yjbanov , the test runner actually uses the same browser to run multiple tests in multiple iframes, so screenshot taking doesn't work in this case.

printTrace('<<< $line');
return line.isNotEmpty && line[0] == '{';
})
.map<dynamic>((String line) => jsonDecode(line))
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
.map<dynamic>((String line) => jsonDecode(line))
.map<dynamic>(jsonDecode)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

// Configure package:test to use the Flutter engine for child processes.
final String shellPath = artifacts.getArtifactPath(Artifact.flutterTester);
if (!processManager.canRun(shellPath)) {
throwToolExit('Cannot find Flutter shell at $shellPath');
Copy link
Member

Choose a reason for hiding this comment

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

Maybe something like "Cannot execute tester at $shellPath"

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done. I actually copied this text from below, in the same file :)

});
});

test('succeed when goldem comparison succeed', () => testbed.run(() async {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
test('succeed when goldem comparison succeed', () => testbed.run(() async {
test('succeed when golden comparison succeed', () => testbed.run(() async {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

expect(result, null);
}));

test('fail with error message when goldem comparison failed', () => testbed.run(() async {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
test('fail with error message when goldem comparison failed', () => testbed.run(() async {
test('fail with error message when golden comparison failed', () => testbed.run(() async {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

verify(mockProcessManager.start(any, environment: anyNamed('environment'))).called(1);
}));

test('does not reuse the process for differnt test file', () => testbed.run(() async {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
test('does not reuse the process for differnt test file', () => testbed.run(() async {
test('does not reuse the process for different test file', () => testbed.run(() async {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

Copy link
Member

@jonahwilliams jonahwilliams left a comment

Choose a reason for hiding this comment

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

Just some small nits, overall LGTM

Copy link
Contributor

@Piinks Piinks left a comment

Choose a reason for hiding this comment

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

LGTM! 🎉 📸

@chingjun
Copy link
Contributor Author

chingjun commented Dec 6, 2019

Thank you everyone who has reviewed and given feedback to this PR!

@chingjun chingjun merged commit c2eb068 into flutter:master Dec 6, 2019
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 2, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
a: tests "flutter test", flutter_test, or one of our tests platform-web Web applications specifically team Infra upgrades, team productivity, code health, technical debt. See also team: labels. tool Affects the "flutter" command-line tool. See also t: labels.
Projects
No open projects
Development

Successfully merging this pull request may close these issues.

None yet

8 participants