-
Notifications
You must be signed in to change notification settings - Fork 28.7k
Allow marking a golden check as flaky #113396
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
Conversation
0add2ea
to
cbf2318
Compare
@Piinks the PR lacks documentation and tests. At this point I'm looking for an initial design review. Didn't want to spend time on minutiae without a high-level agreement first. |
} | ||
} | ||
|
||
Future<GoldenFileComparator> _createComparator({ bool isFlaky = false }) async { |
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.
Why does the comparator need to know if something is flaky? Couldn't it just be a flag on compare
? IIRC, we create a comparator for each test file, not each individual test.
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.
That was my proposal in the previous PR, but that change turned out to be breaking and it made the isFlaky
flag public, both aspects considered undesirable in review comments. However, I'm not against that approach. It will just require a small migration.
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.
Sorry I meant the compare
of the super class of Flutter's specific comparator (FlutterGoldenFileComparator
).. not the public GoldenFileComparator.compare
. Can we not add isFlaky to that comparison path?
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.
Or a new FlutterGoldenFileComparator.compareFlaky method?
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.
Oh, yeah, sub-classes can add extra parameters. I'll give it a shot.
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 turned out not so simple: #113396 (comment)
/// color. If the color is the same for all commits in the recent history, the | ||
/// golden is likely no longer flaky and [isFlaky] can be set back to false. If | ||
/// the color changes from commit to commit then it is still flaky. | ||
AsyncMatcher matchesFlutterGolden(Object key, { int? version, bool isFlaky = false }) { |
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 have a check in the analyzer that ensures every matchesGoldenFile
has a Tag
at the top of the test file. It should probably be updated to ensure uses of matchesFlutterGolden
are accounted for.
Separately, we'd probably want to migrate all uses of matchesGoldenFile
in the framework tests to use this one for consistency, and have the analyzer enforce it.
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.
Also, the flutter/flutter wiki page on golden file testing for the framework will need to be updated.
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.
Cool. I'm going to add it to my TODO list, but will wait until we converge on the overall design before starting the migration. Also, I imagine migrating all tests should probably done incrementally, since there are many usages of matchesGoldenFile
.
|
||
import 'package:file/file.dart'; | ||
import 'package:file/local.dart'; | ||
import 'package:flutter/foundation.dart'; | ||
import 'package:flutter_goldens_client/skia_client.dart'; | ||
import 'package:flutter_test/flutter_test.dart'; | ||
import 'package:platform/platform.dart'; | ||
import 'package:test_api/src/expect/async_matcher.dart' show AsyncMatcher; // ignore: implementation_imports |
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 we be able to avoid this ignore if matchesGoldenFile
was overridden instead?
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.
matchesGoldenFile
is a top-level function. I can't think of a way to override it. Any ideas?
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.
You could use the same name and hide it from the namespace? Not sure. If there is a way to not expose this and have this ignore that would be ideal.
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's exposed from package:flutter_test/flutter_test.dart
. According to git grep
there are 874 imports of flutter_test.dart
. Does the benefit of hiding it outweigh the cost of the noise across the codebase?
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 dunno! Just brainstorming. I think in general there are a lot of options here. :)
final GoldenFileComparator nonFlakyComparator = await _createComparator(); | ||
_flakyGoldenFileComparator = await _createComparator(isFlaky: true); | ||
|
||
// Assign this last because FlutterLocalFileComparator.fromDefaultComparator |
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 am not sure I follow, is this a bug?
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 admit, I don't understand the golden design well enough to tell. The problematic line is
defaultComparator ??= goldenFileComparator as LocalFileComparator; |
So if I assign goldenFileComparator
to anything other than LocalFileComparator
the fromDefaultComparator
fails the as
cast, so I'm deferring the assignment till after all invocations of fromDefaultComparator
.
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 wonder if this could be a lot simpler if the actual comparator doesn't have anything to do with flakiness
@@ -98,14 +98,14 @@ class TestGoldenComparator { | |||
return _processManager!.start(command, environment: environment); | |||
} | |||
|
|||
Future<String?> compareGoldens(Uri testUri, Uint8List bytes, Uri goldenKey, bool? updateGoldens) async { | |||
Future<String?> compareGoldens(Uri testUri, Uint8List bytes, Uri goldenKey, bool? updateGoldens, Map<String, dynamic>? customProperties) async { |
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.
What is customProperties
for?
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.
For any custom properties needed by the custom comparator supplied by flutter_test_config.dart
. In the case of Flutter's own goldens the only custom property is isFlaky
. Without it, I'd have to hard-code the notion of isFlaky
into the tool. Since we decided to limit this concept to Flutter's own goldens, I had to add a more generic way of passing custom properties. I'm open to other suggestions.
? await _flakyGoldenFileComparator.compare(bytes, goldenKey) | ||
: await goldenFileComparator.compare(bytes, goldenKey); |
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.
Yeah, like right here.. instead of having separate comparators, why not just add compareFlaky
as a method to the base abstract FlutterGoldenFileComparator class from flutter_goldens? The post-, pre-submit, and local comparators will be able to override it if they need to do anything special in each test environment.
It looks like it could eliminate a bunch of code creating and managing multiple comparators.
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 just realized that compare
is also called by the matcher, which knows nothing about flakiness. So even if I have compareFlaky
or an override of compare
with isFlaky
parameter, the matcher will not specify it 🤔
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.
Aren't you creating a custom matcher though? I may not be understanding this properly.
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 still reusing the existing matchers. I just teach them to optionally take a custom comparator and use it instead of the default one.
I could make a custom copy of the matcher too. But now I'm starting to realize that by replacing matchesGoldenFile
, the comparator, and the matcher, I'd pretty much be duplicating all existing functionality. Is that OK? Or should we look for a way to reuse and customize existing functionality? An idea:
- Go back to Allow marking a golden check as flaky #111595, but instead of hard-coding
isFlaky
, we expose an optional namedString? tag
argument inmatchesGoldenFile
. - This argument would be passed to
GoldenFileComparator
unchanged. - We document that this argument is not used by the default implementation of the golden API but may be used in conjunction with
flutter_test_config.dart
to customize the functionality of custom comparators.
In the Flutter repo tag: 'flaky'
would be implemented by the custom comparator to implement flaky goldens, e.g.:
matchesGoldenFile('date_picker_test.time.initial.png', tag: 'flaky')
Properties of this approach:
- Pro: it does not hard-code the notion of "flaky" into the public golden API.
- Pro: it reuses all of the existing golden functionality, so that customizations like this are easy to implement.
- Pro: it's flexible enough for developer to customize for different things (it's just a string; you can put whatever you want in it).
- Con:
String
-based API is not very strongly typed (can be alleviated by checking the value at runtime). - Con: it's breaking, but easily fixable (simply pass
customProperties
toflutter_test_config.dart
, or ignore it if it's not needed).
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.
But now I'm starting to realize that by replacing matchesGoldenFile, the comparator, and the matcher, I'd pretty much be duplicating all existing functionality.
I don't think you need to replace all of these things. We already have custom comparators. They do not need to be copied and rewritten. If we create a matchesFlutterGolden
method for the specific purpose of the flutter framework, then it can call on the custom comparator (of type FlutterGoldenFileComparator) that we already have to execute the test however we want it to. Because it is already encapsulated for our own specific uses. I feel like a lot of this is not necessary.
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.
See #114026 as a rough example of what I mean.
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, you're right, I forgot that custom comparators can be reused, so we're not duplicating everything. Ok, moving in that direction.
'key': key, | ||
'width': width.round(), | ||
'height': height.round(), | ||
'customProperties': <String, dynamic>{ |
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.
Then you may not need these customProperties, if it is just a method rather than a unique comparator that handles the flaky case
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, the way web goldens work is the comparator running in the browser isn't comparing anything. It just sends a command to the Flutter Tool and the tool forwards it to a comparator process. The comparator process needs to know whether to compare the golden as stable or as flaky. I'm currently using the customProperties
to attach the isFlaky
flag. I couldn't think of a different approach.
6d75dee
to
4cc45f9
Compare
3e8b71b
to
e7e1d7e
Compare
I forked this so I could play with it and get a better understanding of the web implementation. #114450 I made a few tweaks, let me know what you think. :) |
Closing in favor of #114450 |
(this is a non-breaking non-API-changing variant of #111595)
Support marking a golden check as flaky. A flaky check will still generate a golden and report it to the current
GoldenFileComparator
, but it will not fail the test.Public API changes
In order to keep the concept of "flaky golden" private to the Flutter repo, the public golden test API was extended in the following non-breaking ways:
MatchesGoldenFile
now takes an optionalcomparator
argument. This allows the test to instruct the matcher to use a flaky comparator rather than always use the default one (the default API has no notion of "flaky golden").flutter_web_test_config.dart
, an optional sibling toflutter_test_config.dart
. Because on the web the golden comparator is split into two parts, one that runs in the browser, and on that runs outside the browser, in order to keep the concept of flaky golden private to the Flutter repo, that logic goes intoflutter_web_test_config.dart
rather than being hard-coded into any of the public tool or package code.Private changes
The following changes do not affect the public API:
isFlaky
argument from the test.package:flutter_goldens
acquired a new functionmatchesFlutterGolden
that's similar tomatchesGoldenFile
but supports flaky goldens.package:flutter_goldens
provides a command handler for web tests that implements theisFlaky
option.package:flutter_goldens
now implements atestExecutable
for web tests providing flaky functionality (previously the web version was empty).TODO
flutter_web_test_config.dart
file.isFlaky
and new public API.Fixes #111325