diff --git a/dev/devicelab/bin/tasks/flutter_driver_screenshot_test_fuchsia.dart b/dev/devicelab/bin/tasks/flutter_driver_screenshot_test_fuchsia.dart index 424bd15a815..8d97bb9ad5d 100644 --- a/dev/devicelab/bin/tasks/flutter_driver_screenshot_test_fuchsia.dart +++ b/dev/devicelab/bin/tasks/flutter_driver_screenshot_test_fuchsia.dart @@ -10,5 +10,5 @@ import 'package:flutter_devicelab/tasks/integration_tests.dart'; Future main() async { deviceOperatingSystem = DeviceOperatingSystem.fuchsia; - await task(createFlutterDriverScreenshotTest(useFlutterGold: true)); + await task(createFlutterDriverScreenshotTest()); } diff --git a/dev/devicelab/lib/tasks/integration_tests.dart b/dev/devicelab/lib/tasks/integration_tests.dart index a40b5340a44..5078cea1649 100644 --- a/dev/devicelab/lib/tasks/integration_tests.dart +++ b/dev/devicelab/lib/tasks/integration_tests.dart @@ -108,17 +108,11 @@ TaskFunction createAndroidSplashScreenKitchenSinkTest() { } /// Executes a driver test that takes a screenshot and compares it against a golden image. -/// If [useFlutterGold] is true, the golden image is served by Flutter Gold -/// (https://flutter-gold.skia.org/), otherwise the golden image is read from the disk. -TaskFunction createFlutterDriverScreenshotTest({ - bool useFlutterGold = false, -}) { +/// The golden image is served by Flutter Gold (https://flutter-gold.skia.org/). +TaskFunction createFlutterDriverScreenshotTest() { return DriverTest( '${flutterDirectory.path}/dev/integration_tests/flutter_driver_screenshot_test', 'lib/main.dart', - extraOptions: useFlutterGold ? const [ - '--driver', 'test_driver/flutter_gold_main_test.dart' - ] : const [] ); } diff --git a/dev/integration_tests/flutter_driver_screenshot_test/README.md b/dev/integration_tests/flutter_driver_screenshot_test/README.md index 110d016d2a1..e9fba7b6713 100644 --- a/dev/integration_tests/flutter_driver_screenshot_test/README.md +++ b/dev/integration_tests/flutter_driver_screenshot_test/README.md @@ -6,8 +6,7 @@ Each sub page should displays some simple UIs to screenshot tested. The flutter driver test runs the app and opens each page to take a screenshot. -Use `test_driver/flutter_gold_main_test.dart` to test against golden files stored on Flutter Gold. -Otherwise, use `main_test.dart` to test against golden files stored on `test_driver/goldens//.png`. +Use `main_test.dart` to test against golden files stored on Flutter Gold. Note that new binaries can't be checked in the Flutter repo, so use [Flutter Gold](https://github.com/flutter/flutter/wiki/Writing-a-golden-file-test-for-package:flutter) instead. @@ -17,7 +16,6 @@ Note that new binaries can't be checked in the Flutter repo, so use [Flutter Gol 2. The new class should set a static `title` and `key` 3. Add an instance of the new class to the `_allPages` list in the `main.dart` 4. Create a new test case similar to `"'A page with an image screenshot"` in `test_driver/main_test.dart` to run the screenshot test. -5. Create directories for the test: `test_driver/goldens/` should be created before running the test based on the target platform the test is designed to run. An example of a `Page` subclass can be found in `lib/image_page.dart` diff --git a/dev/integration_tests/flutter_driver_screenshot_test/ios/Podfile b/dev/integration_tests/flutter_driver_screenshot_test/ios/Podfile index e9286cbac1e..6697f0a539e 100644 --- a/dev/integration_tests/flutter_driver_screenshot_test/ios/Podfile +++ b/dev/integration_tests/flutter_driver_screenshot_test/ios/Podfile @@ -15,56 +15,69 @@ def parse_KV_file(file, separator='=') if !File.exists? file_abs_path return []; end - pods_ary = [] + generated_key_values = {} skip_line_start_symbols = ["#", "/"] - File.foreach(file_abs_path) { |line| - next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } - plugin = line.split(pattern=separator) - if plugin.length == 2 - podname = plugin[0].strip() - path = plugin[1].strip() - podpath = File.expand_path("#{path}", file_abs_path) - pods_ary.push({:name => podname, :path => podpath}); - else - puts "Invalid plugin specification: #{line}" - end - } - return pods_ary + File.foreach(file_abs_path) do |line| + next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } + plugin = line.split(pattern=separator) + if plugin.length == 2 + podname = plugin[0].strip() + path = plugin[1].strip() + podpath = File.expand_path("#{path}", file_abs_path) + generated_key_values[podname] = podpath + else + puts "Invalid plugin specification: #{line}" + end + end + generated_key_values end target 'Runner' do use_frameworks! + use_modular_headers! + + # Flutter Pod + + copied_flutter_dir = File.join(__dir__, 'Flutter') + copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework') + copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec') + unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path) + # Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet. + # That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration. + # CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist. + + generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig') + unless File.exist?(generated_xcode_build_settings_path) + raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path) + cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR']; + + unless File.exist?(copied_framework_path) + FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir) + end + unless File.exist?(copied_podspec_path) + FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir) + end + end + + # Keep pod path relative so it can be checked into Podfile.lock. + pod 'Flutter', :path => 'Flutter' + + # Plugin Pods # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock # referring to absolute paths on developers' machines. system('rm -rf .symlinks') system('mkdir -p .symlinks/plugins') - - # Flutter Pods - generated_xcode_build_settings = parse_KV_file('./Flutter/Generated.xcconfig') - if generated_xcode_build_settings.empty? - puts "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first." - end - generated_xcode_build_settings.map { |p| - if p[:name] == 'FLUTTER_FRAMEWORK_DIR' - symlink = File.join('.symlinks', 'flutter') - File.symlink(File.dirname(p[:path]), symlink) - pod 'Flutter', :path => File.join(symlink, File.basename(p[:path])) - end - } - - # Plugin Pods plugin_pods = parse_KV_file('../.flutter-plugins') - plugin_pods.map { |p| - symlink = File.join('.symlinks', 'plugins', p[:name]) - File.symlink(p[:path], symlink) - pod p[:name], :path => File.join(symlink, 'ios') - } + plugin_pods.each do |name, path| + symlink = File.join('.symlinks', 'plugins', name) + File.symlink(path, symlink) + pod name, :path => File.join(symlink, 'ios') + end end -# Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system. -install! 'cocoapods', :disable_input_output_paths => true - post_install do |installer| installer.pods_project.targets.each do |target| target.build_configurations.each do |config| diff --git a/dev/integration_tests/flutter_driver_screenshot_test/test_driver/driver_screenshot_tester.dart b/dev/integration_tests/flutter_driver_screenshot_test/test_driver/driver_screenshot_tester.dart deleted file mode 100644 index e3d1d746220..00000000000 --- a/dev/integration_tests/flutter_driver_screenshot_test/test_driver/driver_screenshot_tester.dart +++ /dev/null @@ -1,89 +0,0 @@ -// 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 'dart:io' show File; - -import 'package:flutter_driver/flutter_driver.dart'; -import 'package:meta/meta.dart'; -import 'package:path/path.dart' as path; - -const String _kPathParent = 'test_driver/goldens/'; - -/// The utility class that helps test cases to tests screenshots with a [FlutterDriver]. -@immutable -class DriverScreenShotTester { - /// Constructs a [DriverScreenShotTester]. - /// - /// All the parameters are required and must not be null. - const DriverScreenShotTester({ - @required this.testName, - @required this.driver, - @required this.deviceModel, - }) : assert(testName != null), - assert(driver != null), - assert(deviceModel != null); - - /// The name of the test. - /// - /// It needs to match the folder name which the goldens resides under `test_driver/goldens`. - final String testName; - - /// The `FlutterDriver` used to take the screenshots. - final FlutterDriver driver; - - /// The device model of the device that the test is running on. - final String deviceModel; - - /// Compares `screenshot` to the corresponding golden image. Returns true if they match. - /// - /// The golden image should exists at `test_driver/goldens//.png` - /// prior to this call. - Future compareScreenshots(List screenshot) async { - if (screenshot == null) { - return false; - } - final File file = File(_getImageFilePath()); - final List matcher = await file.readAsBytes(); - - if (matcher == null) { - return false; - } - return _bytesEqual(screenshot, matcher); - } - - bool _bytesEqual(List a, List b) { - if (a.length != b.length) { - return false; - } - for (int index = 0; index < a.length; index += 1) { - if (a[index] != b[index]) { - return false; - } - } - return true; - } - - /// Returns a bytes representation of a screenshot on the current screen. - Future> getScreenshotAsBytes() async { - return await driver.screenshot(); - } - - /// Save the `screenshot` as a golden image. - /// - /// The path of the image is defined as: - /// `test_driver/goldens//.png` - /// - /// Can be used when recording the golden for the first time. - Future saveScreenshot(List screenshot) async { - final File file = File(_getImageFilePath()); - if (!file.existsSync()) { - await file.writeAsBytes(screenshot); - } - } - - String _getImageFilePath() { - return path.joinAll([_kPathParent, testName, deviceModel + '.png']); - } -} diff --git a/dev/integration_tests/flutter_driver_screenshot_test/test_driver/flutter_gold_main_test.dart b/dev/integration_tests/flutter_driver_screenshot_test/test_driver/flutter_gold_main_test.dart deleted file mode 100644 index b8dd6ab73a2..00000000000 --- a/dev/integration_tests/flutter_driver_screenshot_test/test_driver/flutter_gold_main_test.dart +++ /dev/null @@ -1,38 +0,0 @@ -// 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_driver/flutter_driver.dart'; -import 'package:test/test.dart' hide TypeMatcher, isInstanceOf; -import 'package:flutter_test/src/buffer_matcher.dart'; - -Future main() async { - FlutterDriver driver; - String deviceModel; - - setUpAll(() async { - driver = await FlutterDriver.connect(); - deviceModel = await driver.requestData('device_model'); - }); - - tearDownAll(() => driver.close()); - - test('A page with an image screenshot', () async { - final SerializableFinder imagePageListTile = - find.byValueKey('image_page'); - await driver.waitFor(imagePageListTile); - await driver.tap(imagePageListTile); - await driver.waitFor(find.byValueKey('red_square_image')); - await driver.waitUntilNoTransientCallbacks(); - - // TODO(egarciad): This is currently a no-op on LUCI. - // https://github.com/flutter/flutter/issues/49837 - await expectLater( - driver.screenshot(), - bufferMatchesGoldenFile('red_square_driver_screenshot__$deviceModel.png'), - ); - - await driver.tap(find.byTooltip('Back')); - }); -} diff --git a/dev/integration_tests/flutter_driver_screenshot_test/test_driver/goldens/red_square_image/iPhone7,2.png b/dev/integration_tests/flutter_driver_screenshot_test/test_driver/goldens/red_square_image/iPhone7,2.png deleted file mode 100644 index 671988854f6..00000000000 Binary files a/dev/integration_tests/flutter_driver_screenshot_test/test_driver/goldens/red_square_image/iPhone7,2.png and /dev/null differ diff --git a/dev/integration_tests/flutter_driver_screenshot_test/test_driver/main_test.dart b/dev/integration_tests/flutter_driver_screenshot_test/test_driver/main_test.dart index 3ec0751a802..b8dd6ab73a2 100644 --- a/dev/integration_tests/flutter_driver_screenshot_test/test_driver/main_test.dart +++ b/dev/integration_tests/flutter_driver_screenshot_test/test_driver/main_test.dart @@ -5,7 +5,7 @@ import 'dart:async'; import 'package:flutter_driver/flutter_driver.dart'; import 'package:test/test.dart' hide TypeMatcher, isInstanceOf; -import './driver_screenshot_tester.dart'; +import 'package:flutter_test/src/buffer_matcher.dart'; Future main() async { FlutterDriver driver; @@ -19,7 +19,6 @@ Future main() async { tearDownAll(() => driver.close()); test('A page with an image screenshot', () async { - final SerializableFinder imagePageListTile = find.byValueKey('image_page'); await driver.waitFor(imagePageListTile); @@ -27,10 +26,13 @@ Future main() async { await driver.waitFor(find.byValueKey('red_square_image')); await driver.waitUntilNoTransientCallbacks(); - final DriverScreenShotTester tester = DriverScreenShotTester(testName: 'red_square_image', deviceModel: deviceModel, driver: driver); - final List screenShot = await tester.getScreenshotAsBytes(); - final bool compareResult = await tester.compareScreenshots(screenShot); - expect(compareResult, true); + // TODO(egarciad): This is currently a no-op on LUCI. + // https://github.com/flutter/flutter/issues/49837 + await expectLater( + driver.screenshot(), + bufferMatchesGoldenFile('red_square_driver_screenshot__$deviceModel.png'), + ); + await driver.tap(find.byTooltip('Back')); }); } diff --git a/packages/flutter_test/lib/src/_goldens_io.dart b/packages/flutter_test/lib/src/_goldens_io.dart index 49a24e70c04..edb81230e7f 100644 --- a/packages/flutter_test/lib/src/_goldens_io.dart +++ b/packages/flutter_test/lib/src/_goldens_io.dart @@ -6,7 +6,6 @@ import 'dart:async'; import 'dart:io'; import 'dart:math' as math; import 'dart:typed_data'; -import 'dart:ui'; import 'package:image/image.dart'; import 'package:path/path.dart' as path;