Commit 84aa29ce authored by Kate Lovett's avatar Kate Lovett Committed by Flutter GitHub Bot

Gold Pre-submit flow for contributors without permissions (#47551)

parent a245cd78
...@@ -248,7 +248,8 @@ class AnimationController extends Animation<double> ...@@ -248,7 +248,8 @@ class AnimationController extends Animation<double>
_internalSetValue(value ?? lowerBound); _internalSetValue(value ?? lowerBound);
} }
/// Creates an animation controller with no upper or lower bound for its value. /// Creates an animation controller with no upper or lower bound for its
/// value.
/// ///
/// * [value] is the initial value of the animation. /// * [value] is the initial value of the animation.
/// ///
......
...@@ -268,10 +268,6 @@ class FlutterSkiaGoldFileComparator extends FlutterGoldenFileComparator { ...@@ -268,10 +268,6 @@ class FlutterSkiaGoldFileComparator extends FlutterGoldenFileComparator {
/// * [FlutterLocalFileComparator], another /// * [FlutterLocalFileComparator], another
/// [FlutterGoldenFileComparator] that tests golden images locally on your /// [FlutterGoldenFileComparator] that tests golden images locally on your
/// current machine. /// current machine.
// TODO(Piinks): Better handling for first-time contributors that cannot decrypt
// the service account is needed. Gold has a new feature `goldctl imgtest check`
// that could work. There is also the previous implementation that did not use
// goldctl for this edge case. https://github.com/flutter/flutter/issues/46687
class FlutterPreSubmitFileComparator extends FlutterGoldenFileComparator { class FlutterPreSubmitFileComparator extends FlutterGoldenFileComparator {
/// Creates a [FlutterPreSubmitFileComparator] that will test golden file /// Creates a [FlutterPreSubmitFileComparator] that will test golden file
/// images against baselines requested from Flutter Gold. /// images against baselines requested from Flutter Gold.
...@@ -299,41 +295,132 @@ class FlutterPreSubmitFileComparator extends FlutterGoldenFileComparator { ...@@ -299,41 +295,132 @@ class FlutterPreSubmitFileComparator extends FlutterGoldenFileComparator {
final Platform platform, { final Platform platform, {
SkiaGoldClient goldens, SkiaGoldClient goldens,
LocalFileComparator defaultComparator, LocalFileComparator defaultComparator,
final Directory testBasedir,
}) async { }) async {
defaultComparator ??= goldenFileComparator as LocalFileComparator; defaultComparator ??= goldenFileComparator as LocalFileComparator;
final Directory baseDirectory = FlutterGoldenFileComparator.getBaseDirectory( final Directory baseDirectory = testBasedir ?? FlutterGoldenFileComparator.getBaseDirectory(
defaultComparator, defaultComparator,
platform, platform,
suffix: '${math.Random().nextInt(10000)}', suffix: '${math.Random().nextInt(10000)}',
); );
baseDirectory.createSync(recursive: true);
if (!baseDirectory.existsSync())
baseDirectory.createSync(recursive: true);
goldens ??= SkiaGoldClient(baseDirectory); goldens ??= SkiaGoldClient(baseDirectory);
await goldens.auth();
await goldens.tryjobInit(); final bool hasWritePermission = !platform.environment['GOLD_SERVICE_ACCOUNT'].startsWith('ENCRYPTED');
return FlutterPreSubmitFileComparator(baseDirectory.uri, goldens); if (hasWritePermission) {
await goldens.auth();
await goldens.tryjobInit();
return _AuthorizedFlutterPreSubmitComparator(
baseDirectory.uri,
goldens,
platform: platform,
);
}
goldens.emptyAuth();
return _UnauthorizedFlutterPreSubmitComparator(
baseDirectory.uri,
goldens,
platform: platform,
);
} }
@override @override
Future<bool> compare(Uint8List imageBytes, Uri golden) async { Future<bool> compare(Uint8List imageBytes, Uri golden) async {
golden = _addPrefix(golden); assert(
await update(golden, imageBytes); false,
final File goldenFile = getGoldenFile(golden); 'The FlutterPreSubmitFileComparator has been used to execute a golden '
'file test; this should never happen. Presubmit golden file testing '
return skiaClient.tryjobAdd(golden.path, goldenFile); 'should be executed by either the _AuthorizedFlutterPreSubmitComparator '
'or the _UnauthorizedFlutterPreSubmitComparator based on contributor '
'permissions.'
);
return false;
} }
/// Decides based on the current environment whether goldens tests should be /// Decides based on the current environment whether goldens tests should be
/// performed as pre-submit tests with Skia Gold. /// performed as pre-submit tests with Skia Gold.
static bool isAvailableForEnvironment(Platform platform) { static bool isAvailableForEnvironment(Platform platform) {
final String cirrusPR = platform.environment['CIRRUS_PR'] ?? ''; final String cirrusPR = platform.environment['CIRRUS_PR'] ?? '';
final bool hasWritePermission = platform.environment['CIRRUS_USER_PERMISSION'] == 'admin'
|| platform.environment['CIRRUS_USER_PERMISSION'] == 'write';
return platform.environment.containsKey('CIRRUS_CI') return platform.environment.containsKey('CIRRUS_CI')
&& cirrusPR.isNotEmpty && cirrusPR.isNotEmpty
&& platform.environment.containsKey('GOLD_SERVICE_ACCOUNT') && platform.environment.containsKey('GOLD_SERVICE_ACCOUNT');
&& hasWritePermission; }
}
class _AuthorizedFlutterPreSubmitComparator extends FlutterPreSubmitFileComparator {
_AuthorizedFlutterPreSubmitComparator(
final Uri basedir,
final SkiaGoldClient skiaClient, {
final FileSystem fs = const LocalFileSystem(),
final Platform platform = const LocalPlatform(),
}) : super(
basedir,
skiaClient,
fs: fs,
platform: platform,
);
@override
Future<bool> compare(Uint8List imageBytes, Uri golden) async {
golden = _addPrefix(golden);
await update(golden, imageBytes);
final File goldenFile = getGoldenFile(golden);
return skiaClient.tryjobAdd(golden.path, goldenFile);
}
}
class _UnauthorizedFlutterPreSubmitComparator extends FlutterPreSubmitFileComparator {
_UnauthorizedFlutterPreSubmitComparator(
final Uri basedir,
final SkiaGoldClient skiaClient, {
final FileSystem fs = const LocalFileSystem(),
final Platform platform = const LocalPlatform(),
}) : super(
basedir,
skiaClient,
fs: fs,
platform: platform,
);
@override
Future<bool> compare(Uint8List imageBytes, Uri golden) async {
golden = _addPrefix(golden);
await update(golden, imageBytes);
final File goldenFile = getGoldenFile(golden);
// Check for match to existing baseline.
if (await skiaClient.imgtestCheck(golden.path, goldenFile))
return true;
// We do not have a matching image, so we need to check a few things
// manually. We wait until this point to do this work so request traffic
// low.
skiaClient.getExpectations();
final String testName = skiaClient.cleanTestName(golden.path);
final List<String> testExpectations = skiaClient.expectations[testName];
if (testExpectations == null) {
// This is a new test.
print('No expectations provided by Skia Gold for test: $golden. '
'This may be a new test. If this is an unexpected result, check '
'https://flutter-gold.skia.org.\n'
);
return true;
}
// Contributors without the proper permissions to execute a tryjob can make
// a golden file change through Gold's ignore feature instead.
final bool ignoreResult = await skiaClient.testIsIgnoredForPullRequest(
platform.environment['CIRRUS_PR'] ?? '',
golden.path,
);
// If true, this is an intended change.
return ignoreResult;
} }
} }
...@@ -399,12 +486,8 @@ class FlutterSkippingGoldenFileComparator extends FlutterGoldenFileComparator { ...@@ -399,12 +486,8 @@ class FlutterSkippingGoldenFileComparator extends FlutterGoldenFileComparator {
/// Decides based on the current environment whether this comparator should be /// Decides based on the current environment whether this comparator should be
/// used. /// used.
static bool isAvailableForEnvironment(Platform platform) { static bool isAvailableForEnvironment(Platform platform) {
return (platform.environment.containsKey('SWARMING_TASK_ID') return platform.environment.containsKey('SWARMING_TASK_ID')
|| platform.environment.containsKey('CIRRUS_CI')) || platform.environment.containsKey('CIRRUS_CI');
// A service account means that this is a Gold shard. At this point, it
// means we don't have permission to use the account, so we will pass
// through to the [FlutterLocalFileComparator].
&& !platform.environment.containsKey('GOLD_SERVICE_ACCOUNT');
} }
} }
......
...@@ -169,6 +169,37 @@ String digestResponseTemplate({ ...@@ -169,6 +169,37 @@ String digestResponseTemplate({
'''; ''';
} }
/// Json response template for Skia Gold ignore request:
/// https://flutter-gold.skia.org/json/ignores
String ignoreResponseTemplate({
String pullRequestNumber = '0000',
String testName = 'flutter.golden_test.1',
String otherTestName = 'flutter.golden_test.1',
String expires = '2019-09-06T21:28:18.815336Z',
String otherExpires = '2019-09-06T21:28:18.815336Z',
}) {
return '''
[
{
"id": "7579425228619212078",
"name": "contributor@getMail.com",
"updatedBy": "contributor@getMail.com",
"expires": "$expires",
"query": "ext=png&name=$testName",
"note": "https://github.com/flutter/flutter/pull/$pullRequestNumber"
},
{
"id": "7579425228619212078",
"name": "contributor@getMail.com",
"updatedBy": "contributor@getMail.com",
"expires": "$otherExpires",
"query": "ext=png&name=$otherTestName",
"note": "https://github.com/flutter/flutter/pull/99999"
}
]
''';
}
/// Json response template for Skia Gold image request: /// Json response template for Skia Gold image request:
/// https://flutter-gold.skia.org/img/images/[imageHash].png /// https://flutter-gold.skia.org/img/images/[imageHash].png
List<List<int>> imageResponseTemplate() { List<List<int>> imageResponseTemplate() {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment