From d6e5f938032c43279459b7c45e91d9cb6e259ab5 Mon Sep 17 00:00:00 2001 From: Tim Sneath Date: Tue, 11 Jan 2022 15:24:44 -0800 Subject: [PATCH 1/3] Update linear_gradient.0.dart Tweaking example so that it's more attractive to a design-orientated audience --- .../painting/gradient/linear_gradient.0.dart | 46 +++++++++++-------- examples/api/pubspec.yaml | 2 + examples/api/test/flutter_test_config.dart | 14 ++++++ examples/api/test/goldens_io.dart | 5 ++ examples/api/test/goldens_web.dart | 8 ++++ .../test/painting/linear_gradient.0_test.dart | 39 ++++++++++++++++ .../flutter/lib/src/painting/gradient.dart | 5 +- 7 files changed, 98 insertions(+), 21 deletions(-) create mode 100644 examples/api/test/flutter_test_config.dart create mode 100644 examples/api/test/goldens_io.dart create mode 100644 examples/api/test/goldens_web.dart create mode 100644 examples/api/test/painting/linear_gradient.0_test.dart diff --git a/examples/api/lib/painting/gradient/linear_gradient.0.dart b/examples/api/lib/painting/gradient/linear_gradient.0.dart index 5662e6df3e359..6d5f9027579a7 100644 --- a/examples/api/lib/painting/gradient/linear_gradient.0.dart +++ b/examples/api/lib/painting/gradient/linear_gradient.0.dart @@ -11,33 +11,41 @@ void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); - static const String _title = 'Flutter Code Sample'; - @override Widget build(BuildContext context) { - return const MaterialApp( - title: _title, - home: MyStatelessWidget(), - ); + return const MaterialApp(home: MoodyGradient()); } } -class MyStatelessWidget extends StatelessWidget { - const MyStatelessWidget({Key? key}) : super(key: key); +class MoodyGradient extends StatelessWidget { + const MoodyGradient({Key? key}) : super(key: key); @override Widget build(BuildContext context) { - return Container( - decoration: const BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topLeft, - end: - Alignment(0.8, 0.0), // 10% of the width, so there are ten blinds. - colors: [ - Color(0xffee0000), - Color(0xffeeee00) - ], // red to yellow - tileMode: TileMode.repeated, // repeats the gradient over the canvas + return Material( + child: Container( + decoration: const BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topLeft, + end: Alignment(0.8, 1), + colors: [ + Color(0xff1f005c), + Color(0xff5b0060), + Color(0xff870160), + Color(0xffac255e), + Color(0xffca485c), + Color(0xffe16b5c), + Color(0xfff39060), + Color(0xffffb56b), + ], // Gradient from https://learnui.design/tools/gradient-generator.html + tileMode: TileMode.mirror, + ), + ), + child: const Center( + child: Text( + 'From Night to Day', + style: TextStyle(fontSize: 24, color: Colors.white), + ), ), ), ); diff --git a/examples/api/pubspec.yaml b/examples/api/pubspec.yaml index 5c9a2ecdd6bfe..cdb927ac65c6e 100644 --- a/examples/api/pubspec.yaml +++ b/examples/api/pubspec.yaml @@ -25,6 +25,8 @@ dev_dependencies: sdk: flutter flutter_driver: sdk: flutter + flutter_goldens: + sdk: flutter flutter_test: sdk: flutter test: 1.20.1 diff --git a/examples/api/test/flutter_test_config.dart b/examples/api/test/flutter_test_config.dart new file mode 100644 index 0000000000000..ee0bf4d5a90f3 --- /dev/null +++ b/examples/api/test/flutter_test_config.dart @@ -0,0 +1,14 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter_test/flutter_test.dart'; + +import 'goldens_io.dart' if (dart.library.html) 'goldens_web.dart' as flutter_goldens; + +Future testExecutable(FutureOr Function() testMain) { + // Enable golden file testing using Skia Gold. + return flutter_goldens.testExecutable(testMain); +} diff --git a/examples/api/test/goldens_io.dart b/examples/api/test/goldens_io.dart new file mode 100644 index 0000000000000..d552235d6903e --- /dev/null +++ b/examples/api/test/goldens_io.dart @@ -0,0 +1,5 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +export 'package:flutter_goldens/flutter_goldens.dart' show testExecutable; diff --git a/examples/api/test/goldens_web.dart b/examples/api/test/goldens_web.dart new file mode 100644 index 0000000000000..57c0fbad759ff --- /dev/null +++ b/examples/api/test/goldens_web.dart @@ -0,0 +1,8 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +// package:flutter_goldens is not used as part of the test process for web. +Future testExecutable(FutureOr Function() testMain) async => testMain(); diff --git a/examples/api/test/painting/linear_gradient.0_test.dart b/examples/api/test/painting/linear_gradient.0_test.dart new file mode 100644 index 0000000000000..0ba1105f64751 --- /dev/null +++ b/examples/api/test/painting/linear_gradient.0_test.dart @@ -0,0 +1,39 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_api_samples/painting/gradient/linear_gradient.0.dart' + as example; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets('finds a gradient', (WidgetTester tester) async { + await tester.pumpWidget( + const MaterialApp( + home: example.MoodyGradient(), + ), + ); + + expect(find.byType(example.MoodyGradient), findsOneWidget); + }); + + testWidgets('gradient matches golden', (WidgetTester tester) async { + await tester.pumpWidget( + const MaterialApp( + home: SizedBox( + width: 800, + height: 600, + child: RepaintBoundary( + child: example.MoodyGradient(), + ), + ), + ), + ); + await expectLater( + find.byType(example.MoodyGradient), + matchesGoldenFile('linear_gradient.0_test.png'), + ); + }); +} diff --git a/packages/flutter/lib/src/painting/gradient.dart b/packages/flutter/lib/src/painting/gradient.dart index 471a6b99e2481..045e0dba77bba 100644 --- a/packages/flutter/lib/src/painting/gradient.dart +++ b/packages/flutter/lib/src/painting/gradient.dart @@ -350,8 +350,9 @@ abstract class Gradient { /// To use a [LinearGradient] to paint on a canvas directly, see [createShader]. /// /// {@tool dartpad} -/// This sample draws a picture that looks like vertical window shades by having -/// a [Container] display a [BoxDecoration] with a [LinearGradient]. +/// This sample draws a picture with a gradient sweeping through different +/// colors, by having a [Container] display a [BoxDecoration] with a +/// [LinearGradient]. /// /// ** See code in examples/api/lib/painting/gradient/linear_gradient.0.dart ** /// {@end-tool} From c77bcc0383a1aecdee1a6eccefaf47dbcc45a3dd Mon Sep 17 00:00:00 2001 From: Michael Goderbauer Date: Mon, 28 Feb 2022 14:57:36 -0800 Subject: [PATCH 2/3] add api prefix --- examples/api/test/flutter_test_config.dart | 2 +- .../flutter_goldens/lib/flutter_goldens.dart | 40 ++++++++++++++----- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/examples/api/test/flutter_test_config.dart b/examples/api/test/flutter_test_config.dart index ee0bf4d5a90f3..2028d46c83f60 100644 --- a/examples/api/test/flutter_test_config.dart +++ b/examples/api/test/flutter_test_config.dart @@ -10,5 +10,5 @@ import 'goldens_io.dart' if (dart.library.html) 'goldens_web.dart' as flutter_go Future testExecutable(FutureOr Function() testMain) { // Enable golden file testing using Skia Gold. - return flutter_goldens.testExecutable(testMain); + return flutter_goldens.testExecutable(testMain, namePrefix: 'api'); } diff --git a/packages/flutter_goldens/lib/flutter_goldens.dart b/packages/flutter_goldens/lib/flutter_goldens.dart index e0b5bdfcc2255..caa790421a340 100644 --- a/packages/flutter_goldens/lib/flutter_goldens.dart +++ b/packages/flutter_goldens/lib/flutter_goldens.dart @@ -25,15 +25,18 @@ const String _kFlutterRootKey = 'FLUTTER_ROOT'; /// [goldenFileComparator] to an instance of [FlutterGoldenFileComparator] that /// works for the current test. _Which_ FlutterGoldenFileComparator is /// instantiated is based on the current testing environment. -Future testExecutable(FutureOr Function() testMain) async { +/// +/// When set, the `namePrefix` is prepended to the names of all gold images. +Future testExecutable(FutureOr Function() testMain, {String? namePrefix}) async { const Platform platform = LocalPlatform(); if (FlutterPostSubmitFileComparator.isAvailableForEnvironment(platform)) { - goldenFileComparator = await FlutterPostSubmitFileComparator.fromDefaultComparator(platform); + goldenFileComparator = await FlutterPostSubmitFileComparator.fromDefaultComparator(platform, namePrefix: namePrefix); } else if (FlutterPreSubmitFileComparator.isAvailableForEnvironment(platform)) { - goldenFileComparator = await FlutterPreSubmitFileComparator.fromDefaultComparator(platform); + goldenFileComparator = await FlutterPreSubmitFileComparator.fromDefaultComparator(platform, namePrefix: namePrefix); } else if (FlutterSkippingFileComparator.isAvailableForEnvironment(platform)) { goldenFileComparator = FlutterSkippingFileComparator.fromDefaultComparator( - 'Golden file testing is not executed on Cirrus, or LUCI environments outside of flutter/flutter.' + 'Golden file testing is not executed on Cirrus, or LUCI environments outside of flutter/flutter.', + namePrefix: namePrefix ); } else { goldenFileComparator = await FlutterLocalFileComparator.fromDefaultComparator(platform); @@ -91,6 +94,7 @@ abstract class FlutterGoldenFileComparator extends GoldenFileComparator { this.skiaClient, { this.fs = const LocalFileSystem(), this.platform = const LocalPlatform(), + this.namePrefix, }); /// The directory to which golden file URIs will be resolved in [compare] and @@ -109,6 +113,9 @@ abstract class FlutterGoldenFileComparator extends GoldenFileComparator { @visibleForTesting final Platform platform; + /// The prefix that is added to all golden names. + final String? namePrefix; + @override Future update(Uri golden, Uint8List imageBytes) async { final File goldenFile = getGoldenFile(golden); @@ -173,8 +180,12 @@ abstract class FlutterGoldenFileComparator extends GoldenFileComparator { 'Golden files in the Flutter framework must end with the file extension ' '.png.' ); - final String prefix = basedir.pathSegments[basedir.pathSegments.length - 2]; - return Uri.parse('$prefix.$golden'); + return Uri.parse([ + if (namePrefix != null) + namePrefix!, + basedir.pathSegments[basedir.pathSegments.length - 2], + golden.toString(), + ].join('.')); } } @@ -205,11 +216,13 @@ class FlutterPostSubmitFileComparator extends FlutterGoldenFileComparator { final SkiaGoldClient skiaClient, { final FileSystem fs = const LocalFileSystem(), final Platform platform = const LocalPlatform(), + String? namePrefix, }) : super( basedir, skiaClient, fs: fs, platform: platform, + namePrefix: namePrefix, ); /// Creates a new [FlutterPostSubmitFileComparator] that mirrors the relative @@ -221,6 +234,7 @@ class FlutterPostSubmitFileComparator extends FlutterGoldenFileComparator { final Platform platform, { SkiaGoldClient? goldens, LocalFileComparator? defaultComparator, + String? namePrefix, }) async { defaultComparator ??= goldenFileComparator as LocalFileComparator; @@ -233,7 +247,7 @@ class FlutterPostSubmitFileComparator extends FlutterGoldenFileComparator { goldens ??= SkiaGoldClient(baseDirectory); await goldens.auth(); - return FlutterPostSubmitFileComparator(baseDirectory.uri, goldens); + return FlutterPostSubmitFileComparator(baseDirectory.uri, goldens, namePrefix: namePrefix); } @override @@ -283,11 +297,13 @@ class FlutterPreSubmitFileComparator extends FlutterGoldenFileComparator { final SkiaGoldClient skiaClient, { final FileSystem fs = const LocalFileSystem(), final Platform platform = const LocalPlatform(), + final String? namePrefix, }) : super( basedir, skiaClient, fs: fs, platform: platform, + namePrefix: namePrefix, ); /// Creates a new [FlutterPreSubmitFileComparator] that mirrors the @@ -300,6 +316,7 @@ class FlutterPreSubmitFileComparator extends FlutterGoldenFileComparator { SkiaGoldClient? goldens, LocalFileComparator? defaultComparator, Directory? testBasedir, + String? namePrefix, }) async { defaultComparator ??= goldenFileComparator as LocalFileComparator; @@ -318,6 +335,7 @@ class FlutterPreSubmitFileComparator extends FlutterGoldenFileComparator { return FlutterPreSubmitFileComparator( baseDirectory.uri, goldens, platform: platform, + namePrefix: namePrefix, ); } @@ -367,8 +385,9 @@ class FlutterSkippingFileComparator extends FlutterGoldenFileComparator { FlutterSkippingFileComparator( final Uri basedir, final SkiaGoldClient skiaClient, - this.reason, - ) : super(basedir, skiaClient); + this.reason,{ + String? namePrefix, + }) : super(basedir, skiaClient, namePrefix: namePrefix); /// Describes the reason for using the [FlutterSkippingFileComparator]. /// @@ -380,12 +399,13 @@ class FlutterSkippingFileComparator extends FlutterGoldenFileComparator { static FlutterSkippingFileComparator fromDefaultComparator( String reason, { LocalFileComparator? defaultComparator, + String? namePrefix, }) { defaultComparator ??= goldenFileComparator as LocalFileComparator; const FileSystem fs = LocalFileSystem(); final Uri basedir = defaultComparator.basedir; final SkiaGoldClient skiaClient = SkiaGoldClient(fs.directory(basedir)); - return FlutterSkippingFileComparator(basedir, skiaClient, reason); + return FlutterSkippingFileComparator(basedir, skiaClient, reason, namePrefix: namePrefix); } @override From a6d274c18bb02276494bc69a7f38428d8cc653d8 Mon Sep 17 00:00:00 2001 From: Michael Goderbauer Date: Mon, 28 Feb 2022 16:28:34 -0800 Subject: [PATCH 3/3] add test --- .../flutter_goldens/lib/flutter_goldens.dart | 2 +- .../test/flutter_goldens_test.dart | 28 ++++++++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/packages/flutter_goldens/lib/flutter_goldens.dart b/packages/flutter_goldens/lib/flutter_goldens.dart index caa790421a340..04c9a6279ebc5 100644 --- a/packages/flutter_goldens/lib/flutter_goldens.dart +++ b/packages/flutter_goldens/lib/flutter_goldens.dart @@ -385,7 +385,7 @@ class FlutterSkippingFileComparator extends FlutterGoldenFileComparator { FlutterSkippingFileComparator( final Uri basedir, final SkiaGoldClient skiaClient, - this.reason,{ + this.reason, { String? namePrefix, }) : super(basedir, skiaClient, namePrefix: namePrefix); diff --git a/packages/flutter_goldens/test/flutter_goldens_test.dart b/packages/flutter_goldens/test/flutter_goldens_test.dart index 494f582cfe01f..feb3ef75fd5a2 100644 --- a/packages/flutter_goldens/test/flutter_goldens_test.dart +++ b/packages/flutter_goldens/test/flutter_goldens_test.dart @@ -466,6 +466,27 @@ void main() { expect(key, Uri.parse('foo.png')); }); + test('adds namePrefix', () async { + const String libraryName = 'sidedishes'; + const String namePrefix = 'tomatosalad'; + const String fileName = 'lettuce.png'; + final FakeSkiaGoldClient fakeSkiaClient = FakeSkiaGoldClient(); + final Directory basedir = fs.directory('flutter/test/$libraryName/') + ..createSync(recursive: true); + final FlutterGoldenFileComparator comparator = FlutterPostSubmitFileComparator( + basedir.uri, + fakeSkiaClient, + fs: fs, + platform: platform, + namePrefix: namePrefix, + ); + await comparator.compare( + Uint8List.fromList(_kTestPngBytes), + Uri.parse(fileName), + ); + expect(fakeSkiaClient.testNames.single, '$namePrefix.$libraryName.$fileName'); + }); + group('Post-Submit', () { late FakeSkiaGoldClient fakeSkiaClient; @@ -926,11 +947,16 @@ class FakeSkiaGoldClient extends Fake implements SkiaGoldClient { @override Future auth() async {} + final List testNames = []; + int initCalls = 0; @override Future imgtestInit() async => initCalls += 1; @override - Future imgtestAdd(String testName, File goldenFile) async => true; + Future imgtestAdd(String testName, File goldenFile) async { + testNames.add(testName); + return true; + } int tryInitCalls = 0; @override