diff --git a/packages/flutter/test/cupertino/date_picker_test.dart b/packages/flutter/test/cupertino/date_picker_test.dart index 3b1f081e813..83a9f637517 100644 --- a/packages/flutter/test/cupertino/date_picker_test.dart +++ b/packages/flutter/test/cupertino/date_picker_test.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:io' show Platform; import 'dart:ui'; import 'package:flutter/cupertino.dart'; @@ -822,6 +823,43 @@ void main() { ); }); + testWidgets('DatePicker golden tests', (WidgetTester tester) async { + await tester.pumpWidget( + CupertinoApp( + home: Center( + child: SizedBox( + width: 400, + height: 400, + child: RepaintBoundary( + child: CupertinoDatePicker( + mode: CupertinoDatePickerMode.dateAndTime, + initialDateTime: DateTime(2019, 1, 1, 4), + onDateTimeChanged: (_) {}, + ), + ) + ), + ) + ) + ); + + await expectLater( + find.byType(CupertinoDatePicker), + matchesGoldenFile('date_picker_test.datetime.initial.1.png'), + skip: !Platform.isLinux + ); + + // Slightly drag the hour component to make the current hour off-center. + await tester.drag(find.text('4'), Offset(0, _kRowOffset.dy / 2)); + await tester.pump(); + + await expectLater( + find.byType(CupertinoDatePicker), + matchesGoldenFile('date_picker_test.datetime.drag.1.png'), + skip: !Platform.isLinux + ); + }); + }); + testWidgets('scrollController can be removed or added', (WidgetTester tester) async { final SemanticsHandle handle = tester.ensureSemantics(); int lastSelectedItem; @@ -895,35 +933,6 @@ void main() { expect(lastSelectedItem, 1); handle.dispose(); }); - - testWidgets('DatePicker golden tests', (WidgetTester tester) async { - await tester.pumpWidget( - CupertinoApp( - home: SizedBox( - width: 200, - child: CupertinoDatePicker( - mode: CupertinoDatePickerMode.dateAndTime, - initialDateTime: DateTime(2019, 1, 1, 4), - onDateTimeChanged: (_) {}, - ) - ) - ) - ); - - await expectLater( - find.byType(CupertinoDatePicker), - matchesSkiaGoldFile('date_picker_test.datetime.initial.png'), - ); - - // Slightly drag the hour component to make the current hour off-center. - await tester.drag(find.text('4'), Offset(0, _kRowOffset.dy / 2)); - await tester.pump(); - - await expectLater( - find.byType(CupertinoDatePicker), - matchesSkiaGoldFile('date_picker_test.datetime.drag.png'), - ); - }); } Widget _buildPicker({ FixedExtentScrollController controller, ValueChanged onSelectedItemChanged }) { diff --git a/packages/flutter/test/cupertino/nav_bar_test.dart b/packages/flutter/test/cupertino/nav_bar_test.dart index c1e683b2225..09419a0393f 100644 --- a/packages/flutter/test/cupertino/nav_bar_test.dart +++ b/packages/flutter/test/cupertino/nav_bar_test.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:io'; + import 'package:flutter/cupertino.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; @@ -799,9 +801,12 @@ void main() { await expectLater( find.byType(RepaintBoundary).last, - matchesSkiaGoldFile('nav_bar_test.standard_title.png'), + matchesGoldenFile('nav_bar_test.standard_title.1.png'), ); }, + // TODO(xster): remove once https://github.com/flutter/flutter/issues/17483 + // is fixed. + skip: !Platform.isLinux, ); testWidgets( @@ -830,10 +835,13 @@ void main() { await expectLater( find.byType(RepaintBoundary).last, - matchesSkiaGoldFile('nav_bar_test.large_title.png'), + matchesGoldenFile('nav_bar_test.large_title.1.png'), ); }, - ); + // TODO(xster): remove once https://github.com/flutter/flutter/issues/17483 + // is fixed. + skip: !Platform.isLinux, + ); testWidgets('NavBar draws a light system bar for a dark background', (WidgetTester tester) async { diff --git a/packages/flutter/test/cupertino/segmented_control_test.dart b/packages/flutter/test/cupertino/segmented_control_test.dart index 31568c93a6c..1214541640d 100644 --- a/packages/flutter/test/cupertino/segmented_control_test.dart +++ b/packages/flutter/test/cupertino/segmented_control_test.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:io'; + import 'package:flutter/widgets.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -1325,9 +1327,9 @@ void main() { await expectLater( find.byType(RepaintBoundary), - matchesSkiaGoldFile('segmented_control_test.0.png'), + matchesGoldenFile('segmented_control_test.0.0.png'), ); - }); + }, skip: !Platform.isLinux); testWidgets('Golden Test Pressed State', (WidgetTester tester) async { final Map children = {}; @@ -1363,7 +1365,7 @@ void main() { await expectLater( find.byType(RepaintBoundary), - matchesSkiaGoldFile('segmented_control_test.1.png'), + matchesGoldenFile('segmented_control_test.1.0.png'), ); - }); + }, skip: !Platform.isLinux); } diff --git a/packages/flutter/test/material/bottom_app_bar_test.dart b/packages/flutter/test/material/bottom_app_bar_test.dart index 8003d533d66..92929cfe40a 100644 --- a/packages/flutter/test/material/bottom_app_bar_test.dart +++ b/packages/flutter/test/material/bottom_app_bar_test.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:io'; + import 'package:flutter_test/flutter_test.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; @@ -71,15 +73,15 @@ void main() { await pump(FloatingActionButtonLocation.endDocked); await expectLater( find.byKey(key), - matchesSkiaGoldFile('bottom_app_bar.custom_shape.1.png'), + matchesGoldenFile('bottom_app_bar.custom_shape.1.png'), ); await pump(FloatingActionButtonLocation.centerDocked); await tester.pumpAndSettle(); await expectLater( find.byKey(key), - matchesSkiaGoldFile('bottom_app_bar.custom_shape.2.png'), + matchesGoldenFile('bottom_app_bar.custom_shape.2.png'), ); - }); + }, skip: !Platform.isLinux); testWidgets('color defaults to Theme.bottomAppBarColor', (WidgetTester tester) async { await tester.pumpWidget( diff --git a/packages/flutter/test/material/bottom_app_bar_theme_test.dart b/packages/flutter/test/material/bottom_app_bar_theme_test.dart index 80486c02261..0dd0fb77cc7 100644 --- a/packages/flutter/test/material/bottom_app_bar_theme_test.dart +++ b/packages/flutter/test/material/bottom_app_bar_theme_test.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:io' show Platform; + import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -80,7 +82,8 @@ void main() { await expectLater( find.byKey(_painterKey), - matchesSkiaGoldFile('bottom_app_bar_theme.custom_shape.png'), + matchesGoldenFile('bottom_app_bar_theme.custom_shape.png'), + skip: !Platform.isLinux, ); }); diff --git a/packages/flutter/test/material/bottom_navigation_bar_test.dart b/packages/flutter/test/material/bottom_navigation_bar_test.dart index c84abe17a12..a17635c3179 100644 --- a/packages/flutter/test/material/bottom_navigation_bar_test.dart +++ b/packages/flutter/test/material/bottom_navigation_bar_test.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:io'; import 'dart:ui'; import 'package:flutter/material.dart'; diff --git a/packages/flutter/test/material/card_theme_test.dart b/packages/flutter/test/material/card_theme_test.dart index 4ced6250d8c..d45de99831b 100644 --- a/packages/flutter/test/material/card_theme_test.dart +++ b/packages/flutter/test/material/card_theme_test.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:io' show Platform; + import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -137,7 +139,8 @@ void main() { await expectLater( find.byKey(painterKey), - matchesSkiaGoldFile('card_theme.custom_shape.png'), + matchesGoldenFile('card_theme.custom_shape.png'), + skip: !Platform.isLinux, ); }); } diff --git a/packages/flutter/test/material/dialog_theme_test.dart b/packages/flutter/test/material/dialog_theme_test.dart index 87c655241dc..c682fdcd3e2 100644 --- a/packages/flutter/test/material/dialog_theme_test.dart +++ b/packages/flutter/test/material/dialog_theme_test.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:io' show Platform; + import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -130,7 +132,8 @@ void main() { await expectLater( find.byKey(_painterKey), - matchesSkiaGoldFile('dialog_theme.dialog_with_custom_border.png'), + matchesGoldenFile('dialog_theme.dialog_with_custom_border.png'), + skip: !Platform.isLinux, ); }); diff --git a/packages/flutter/test/material/dropdown_test.dart b/packages/flutter/test/material/dropdown_test.dart index 4144fd57e96..a5730c66c6e 100644 --- a/packages/flutter/test/material/dropdown_test.dart +++ b/packages/flutter/test/material/dropdown_test.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:io'; import 'dart:math' as math; import 'dart:ui' show window; @@ -140,7 +141,8 @@ void main() { assert(tester.renderObject(buttonFinder).attached); await expectLater( find.ancestor(of: buttonFinder, matching: find.byType(RepaintBoundary)).first, - matchesSkiaGoldFile('dropdown_test.default.png'), + matchesGoldenFile('dropdown_test.default.0.png'), + skip: !Platform.isLinux, ); }); @@ -152,7 +154,8 @@ void main() { assert(tester.renderObject(buttonFinder).attached); await expectLater( find.ancestor(of: buttonFinder, matching: find.byType(RepaintBoundary)).first, - matchesSkiaGoldFile('dropdown_test.expanded.png'), + matchesGoldenFile('dropdown_test.expanded.0.png'), + skip: !Platform.isLinux, ); }); diff --git a/packages/flutter/test/material/floating_action_button_test.dart b/packages/flutter/test/material/floating_action_button_test.dart index 4949f46f7f8..b72eff81724 100644 --- a/packages/flutter/test/material/floating_action_button_test.dart +++ b/packages/flutter/test/material/floating_action_button_test.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:io'; import 'dart:ui'; import 'package:flutter/material.dart'; @@ -680,7 +681,8 @@ void main() { await tester.pump(const Duration(milliseconds: 1000)); await expectLater( find.byKey(key), - matchesSkiaGoldFile('floating_action_button_test.clip.png'), + matchesGoldenFile('floating_action_button_test.clip.2.png'), // .clip.1.png is obsolete and can be removed + skip: !Platform.isLinux, ); }); diff --git a/packages/flutter/test/material/input_decorator_test.dart b/packages/flutter/test/material/input_decorator_test.dart index da1acfa1221..6697d224832 100644 --- a/packages/flutter/test/material/input_decorator_test.dart +++ b/packages/flutter/test/material/input_decorator_test.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'dart:async'; +import 'dart:io' show Platform; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; @@ -2019,15 +2020,19 @@ void main() { await tester.pumpWidget(buildFrame(TextDirection.ltr)); await expectLater( find.byType(InputDecorator), - matchesSkiaGoldFile('input_decorator.outline_icon_label.ltr.png'), + matchesGoldenFile('input_decorator.outline_icon_label.ltr.png'), + skip: !Platform.isLinux, ); await tester.pumpWidget(buildFrame(TextDirection.rtl)); await expectLater( find.byType(InputDecorator), - matchesSkiaGoldFile('input_decorator.outline_icon_label.rtl.png'), + matchesGoldenFile('input_decorator.outline_icon_label.rtl.png'), + skip: !Platform.isLinux, ); - }); + }, + skip: !Platform.isLinux, + ); testWidgets('InputDecorationTheme.toString()', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/19305 diff --git a/packages/flutter/test/material/material_test.dart b/packages/flutter/test/material/material_test.dart index ff22c6cb084..2299e9b3ef7 100644 --- a/packages/flutter/test/material/material_test.dart +++ b/packages/flutter/test/material/material_test.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:io' show Platform; + import 'package:flutter/material.dart'; import 'package:flutter/painting.dart'; import 'package:flutter/rendering.dart'; @@ -616,7 +618,8 @@ void main() { await expectLater( find.byKey(painterKey), - matchesSkiaGoldFile('material.border_paint_above.png'), + matchesGoldenFile('material.border_paint_above.png'), + skip: !Platform.isLinux, ); }); @@ -656,7 +659,8 @@ void main() { await expectLater( find.byKey(painterKey), - matchesSkiaGoldFile('material.border_paint_below.png'), + matchesGoldenFile('material.border_paint_below.png'), + skip: !Platform.isLinux, ); }); }); diff --git a/packages/flutter/test/material/radio_test.dart b/packages/flutter/test/material/radio_test.dart index 4a4d55c3b72..3be3d203f98 100644 --- a/packages/flutter/test/material/radio_test.dart +++ b/packages/flutter/test/material/radio_test.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:io' show Platform; import 'dart:ui'; import 'package:flutter/rendering.dart'; @@ -276,7 +277,8 @@ void main() { await tester.pumpAndSettle(); await expectLater( find.byKey(painterKey), - matchesSkiaGoldFile('radio.ink_ripple.png'), + matchesGoldenFile('radio.ink_ripple.png'), + skip: !Platform.isLinux, ); }); } diff --git a/packages/flutter/test/material/tab_bar_theme_test.dart b/packages/flutter/test/material/tab_bar_theme_test.dart index a6f6864ad73..23fef94a1b3 100644 --- a/packages/flutter/test/material/tab_bar_theme_test.dart +++ b/packages/flutter/test/material/tab_bar_theme_test.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:io' show Platform; + import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -267,7 +269,8 @@ void main() { await expectLater( find.byKey(_painterKey), - matchesSkiaGoldFile('tab_bar_theme.tab_indicator_size_tab.png'), + matchesGoldenFile('tab_bar_theme.tab_indicator_size_tab.png'), + skip: !Platform.isLinux, ); }); @@ -278,7 +281,8 @@ void main() { await expectLater( find.byKey(_painterKey), - matchesSkiaGoldFile('tab_bar_theme.tab_indicator_size_label.png'), + matchesGoldenFile('tab_bar_theme.tab_indicator_size_label.png'), + skip: !Platform.isLinux, ); }); @@ -294,7 +298,8 @@ void main() { await expectLater( find.byKey(_painterKey), - matchesSkiaGoldFile('tab_bar_theme.custom_tab_indicator.png'), + matchesGoldenFile('tab_bar_theme.custom_tab_indicator.png'), + skip: !Platform.isLinux, ); }); @@ -310,7 +315,8 @@ void main() { await expectLater( find.byKey(_painterKey), - matchesSkiaGoldFile('tab_bar_theme.beveled_rect_indicator.png'), + matchesGoldenFile('tab_bar_theme.beveled_rect_indicator.png'), + skip: !Platform.isLinux, ); }); } diff --git a/packages/flutter/test/painting/continous_rectangle_border_test.dart b/packages/flutter/test/painting/continous_rectangle_border_test.dart index 6cd071cd9fa..37e5ea36dd8 100644 --- a/packages/flutter/test/painting/continous_rectangle_border_test.dart +++ b/packages/flutter/test/painting/continous_rectangle_border_test.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:io' show Platform; import 'package:flutter/material.dart'; import 'package:flutter/painting.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -71,7 +72,8 @@ void main() { await expectLater( find.byType(RepaintBoundary), - matchesSkiaGoldFile('continuous_rectangle_border.golden_test_even_radii.png'), + matchesGoldenFile('continuous_rectangle_border.golden_test_even_radii.png'), + skip: !Platform.isLinux, ); }); @@ -92,7 +94,8 @@ void main() { await expectLater( find.byType(RepaintBoundary), - matchesSkiaGoldFile('continuous_rectangle_border.golden_test_varying_radii.png'), + matchesGoldenFile('continuous_rectangle_border.golden_test_varying_radii.png'), + skip: !Platform.isLinux, ); }); @@ -110,7 +113,8 @@ void main() { await expectLater( find.byType(RepaintBoundary), - matchesSkiaGoldFile('continuous_rectangle_border.golden_test_large_radii.png'), + matchesGoldenFile('continuous_rectangle_border.golden_test_large_radii.png'), + skip: !Platform.isLinux, ); }); diff --git a/packages/flutter/test/rendering/localized_fonts_test.dart b/packages/flutter/test/rendering/localized_fonts_test.dart index f2cbc1e0748..623c04d2c91 100644 --- a/packages/flutter/test/rendering/localized_fonts_test.dart +++ b/packages/flutter/test/rendering/localized_fonts_test.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:io' show Platform; + import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -49,9 +51,11 @@ void main() { await expectLater( find.byType(RichText), - matchesSkiaGoldFile('localized_fonts.rich_text.styled_text_span.png'), + matchesGoldenFile('localized_fonts.rich_text.styled_text_span.png'), ); - }); + }, + skip: !Platform.isLinux, + ); testWidgets( 'Text with locale-specific glyphs, ambient locale', @@ -99,9 +103,11 @@ void main() { await expectLater( find.byType(Row), - matchesSkiaGoldFile('localized_fonts.text_ambient_locale.chars.png'), + matchesGoldenFile('localized_fonts.text_ambient_locale.chars.png'), ); - }); + }, + skip: !Platform.isLinux, + ); testWidgets( 'Text with locale-specific glyphs, explicit locale', @@ -141,8 +147,10 @@ void main() { await expectLater( find.byType(Row), - matchesSkiaGoldFile('localized_fonts.text_explicit_locale.chars.png'), + matchesGoldenFile('localized_fonts.text_explicit_locale.chars.png'), ); - }); + }, + skip: !Platform.isLinux, + ); } diff --git a/packages/flutter/test/widgets/backdrop_filter_test.dart b/packages/flutter/test/widgets/backdrop_filter_test.dart index 9ef0b4cd377..df499b82536 100644 --- a/packages/flutter/test/widgets/backdrop_filter_test.dart +++ b/packages/flutter/test/widgets/backdrop_filter_test.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:io'; import 'dart:ui'; import 'package:flutter/widgets.dart'; @@ -42,7 +43,8 @@ void main() { ); await expectLater( find.byType(RepaintBoundary).first, - matchesSkiaGoldFile('backdrop_filter_test.cull_rect.png'), + matchesGoldenFile('backdrop_filter_test.cull_rect.1.png'), + skip: !Platform.isLinux, ); }); } diff --git a/packages/flutter/test/widgets/clip_test.dart b/packages/flutter/test/widgets/clip_test.dart index 69dbf368929..668ba730d5d 100644 --- a/packages/flutter/test/widgets/clip_test.dart +++ b/packages/flutter/test/widgets/clip_test.dart @@ -356,7 +356,7 @@ void main() { ); await expectLater( find.byType(RepaintBoundary).first, - matchesSkiaGoldFile('clip.ClipRect.png'), + matchesGoldenFile('clip.ClipRect.1.png'), ); }); @@ -396,7 +396,7 @@ void main() { ); await expectLater( find.byType(RepaintBoundary).first, - matchesSkiaGoldFile('clip.ClipRectOverlay.png'), + matchesGoldenFile('clip.ClipRectOverlay.1.png'), ); }); @@ -445,7 +445,7 @@ void main() { ); await expectLater( find.byType(RepaintBoundary).first, - matchesSkiaGoldFile('clip.ClipRRect.png'), + matchesGoldenFile('clip.ClipRRect.1.png'), ); }); @@ -488,7 +488,7 @@ void main() { ); await expectLater( find.byType(RepaintBoundary).first, - matchesSkiaGoldFile('clip.ClipOval.png'), + matchesGoldenFile('clip.ClipOval.1.png'), ); }); @@ -536,7 +536,7 @@ void main() { ); await expectLater( find.byType(RepaintBoundary).first, - matchesSkiaGoldFile('clip.ClipPath.png'), + matchesGoldenFile('clip.ClipPath.1.png'), ); }); @@ -581,7 +581,7 @@ void main() { await tester.pumpWidget(genPhysicalModel(Clip.antiAlias)); await expectLater( find.byType(RepaintBoundary).first, - matchesSkiaGoldFile('clip.PhysicalModel.antiAlias.png'), + matchesGoldenFile('clip.PhysicalModel.antiAlias.1.png'), ); }); @@ -589,7 +589,7 @@ void main() { await tester.pumpWidget(genPhysicalModel(Clip.hardEdge)); await expectLater( find.byType(RepaintBoundary).first, - matchesSkiaGoldFile('clip.PhysicalModel.hardEdge.png'), + matchesGoldenFile('clip.PhysicalModel.hardEdge.1.png'), ); }); @@ -599,7 +599,7 @@ void main() { await tester.pumpWidget(genPhysicalModel(Clip.antiAliasWithSaveLayer)); await expectLater( find.byType(RepaintBoundary).first, - matchesSkiaGoldFile('clip.PhysicalModel.antiAliasWithSaveLayer.png'), + matchesGoldenFile('clip.PhysicalModel.antiAliasWithSaveLayer.png'), ); }); @@ -641,7 +641,7 @@ void main() { ); await expectLater( find.byType(RepaintBoundary).first, - matchesSkiaGoldFile('clip.PhysicalModel.default.png'), + matchesGoldenFile('clip.PhysicalModel.default.1.png'), ); }); @@ -690,7 +690,7 @@ void main() { await tester.pumpWidget(genPhysicalShape(Clip.antiAlias)); await expectLater( find.byType(RepaintBoundary).first, - matchesSkiaGoldFile('clip.PhysicalShape.antiAlias.png'), + matchesGoldenFile('clip.PhysicalShape.antiAlias.1.png'), ); }); @@ -698,7 +698,7 @@ void main() { await tester.pumpWidget(genPhysicalShape(Clip.hardEdge)); await expectLater( find.byType(RepaintBoundary).first, - matchesSkiaGoldFile('clip.PhysicalShape.hardEdge.png'), + matchesGoldenFile('clip.PhysicalShape.hardEdge.1.png'), ); }); @@ -706,7 +706,7 @@ void main() { await tester.pumpWidget(genPhysicalShape(Clip.antiAliasWithSaveLayer)); await expectLater( find.byType(RepaintBoundary).first, - matchesSkiaGoldFile('clip.PhysicalShape.antiAliasWithSaveLayer.png'), + matchesGoldenFile('clip.PhysicalShape.antiAliasWithSaveLayer.png'), ); }); @@ -752,7 +752,7 @@ void main() { ); await expectLater( find.byType(RepaintBoundary).first, - matchesSkiaGoldFile('clip.PhysicalShape.default.png'), + matchesGoldenFile('clip.PhysicalShape.default.1.png'), ); }); diff --git a/packages/flutter/test/widgets/editable_text_cursor_test.dart b/packages/flutter/test/widgets/editable_text_cursor_test.dart index b755619f79d..2fe028b0f6c 100644 --- a/packages/flutter/test/widgets/editable_text_cursor_test.dart +++ b/packages/flutter/test/widgets/editable_text_cursor_test.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:io'; + import 'package:flutter/foundation.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -90,9 +92,9 @@ void main() { await expectLater( find.byKey(const ValueKey(1)), - matchesSkiaGoldFile('editable_text_test.0.png'), + matchesGoldenFile('editable_text_test.0.3.png'), ); - }); + }, skip: !Platform.isLinux); testWidgets('cursor layout has correct radius', (WidgetTester tester) async { final GlobalKey editableTextKey = GlobalKey(); @@ -141,9 +143,9 @@ void main() { await expectLater( find.byKey(const ValueKey(1)), - matchesSkiaGoldFile('editable_text_test.1.png'), + matchesGoldenFile('editable_text_test.1.3.png'), ); - }); + }, skip: !Platform.isLinux); testWidgets('Cursor animates on iOS', (WidgetTester tester) async { final Widget widget = MaterialApp( diff --git a/packages/flutter/test/widgets/invert_colors_test.dart b/packages/flutter/test/widgets/invert_colors_test.dart index 7ad2de07cdf..7a10975b68a 100644 --- a/packages/flutter/test/widgets/invert_colors_test.dart +++ b/packages/flutter/test/widgets/invert_colors_test.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:io'; + import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -20,7 +22,8 @@ void main() { await expectLater( find.byType(RepaintBoundary), - matchesSkiaGoldFile('invert_colors_test.0.png'), + matchesGoldenFile('invert_colors_test.0.png'), + skip: !Platform.isLinux, ); }); @@ -38,7 +41,8 @@ void main() { await expectLater( find.byType(RepaintBoundary), - matchesSkiaGoldFile('invert_colors_test.1.png'), + matchesGoldenFile('invert_colors_test.1.png'), + skip: !Platform.isLinux, ); }); } diff --git a/packages/flutter/test/widgets/list_wheel_scroll_view_test.dart b/packages/flutter/test/widgets/list_wheel_scroll_view_test.dart index 219b77b1b5a..8349f29a82c 100644 --- a/packages/flutter/test/widgets/list_wheel_scroll_view_test.dart +++ b/packages/flutter/test/widgets/list_wheel_scroll_view_test.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:io'; + import 'package:flutter_test/flutter_test.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/rendering.dart'; @@ -535,7 +537,8 @@ void main() { await expectLater( find.byKey(const Key('list_wheel_scroll_view')), - matchesSkiaGoldFile('list_wheel_scroll_view.center_child.magnified.png'), + matchesGoldenFile('list_wheel_scroll_view.center_child.magnified.png'), + skip: !Platform.isLinux, ); }); @@ -589,7 +592,8 @@ void main() { await expectLater( find.byKey(const Key('list_wheel_scroll_view')), - matchesSkiaGoldFile('list_wheel_scroll_view.curved_wheel.left.png'), + matchesGoldenFile('list_wheel_scroll_view.curved_wheel.left.png'), + skip: !Platform.isLinux, ); }); diff --git a/packages/flutter/test/widgets/opacity_test.dart b/packages/flutter/test/widgets/opacity_test.dart index b778614818c..9e22c2696af 100644 --- a/packages/flutter/test/widgets/opacity_test.dart +++ b/packages/flutter/test/widgets/opacity_test.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:io'; + import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:flutter/material.dart'; @@ -177,7 +179,8 @@ void main() { ); await expectLater( find.byType(RepaintBoundary).first, - matchesSkiaGoldFile('opacity_test.offset.png'), + matchesGoldenFile('opacity_test.offset.1.png'), + skip: !Platform.isLinux, ); }); diff --git a/packages/flutter/test/widgets/physical_model_test.dart b/packages/flutter/test/widgets/physical_model_test.dart index 01d938c75c3..ea5522e80a5 100644 --- a/packages/flutter/test/widgets/physical_model_test.dart +++ b/packages/flutter/test/widgets/physical_model_test.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:io' show Platform; import 'dart:math' as math show pi; import 'package:flutter/material.dart'; @@ -110,7 +111,8 @@ void main() { expect(exception.diagnostics.first.toString(), startsWith('A RenderFlex overflowed by ')); await expectLater( find.byKey(key), - matchesSkiaGoldFile('physical_model_overflow.png'), + matchesGoldenFile('physical_model_overflow.png'), + skip: !Platform.isLinux, ); }); diff --git a/packages/flutter/test/widgets/shadow_test.dart b/packages/flutter/test/widgets/shadow_test.dart index 4f78351784d..dd90450181b 100644 --- a/packages/flutter/test/widgets/shadow_test.dart +++ b/packages/flutter/test/widgets/shadow_test.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:io' show Platform; + import 'package:flutter_test/flutter_test.dart'; import 'package:flutter/material.dart'; @@ -23,15 +25,18 @@ void main() { ); await expectLater( find.byType(Container), - matchesSkiaGoldFile('shadow.BoxDecoration.disabled.png'), + matchesGoldenFile('shadow.BoxDecoration.disabled.png'), ); debugDisableShadows = false; tester.binding.reassembleApplication(); await tester.pump(); - await expectLater( - find.byType(Container), - matchesSkiaGoldFile('shadow.BoxDecoration.enabled.png'), - ); + if (Platform.isLinux) { + // TODO(ianh): use the skip argument instead once that doesn't hang, https://github.com/dart-lang/test/issues/830 + await expectLater( + find.byType(Container), + matchesGoldenFile('shadow.BoxDecoration.enabled.png'), + ); // shadows render differently on different platforms + } debugDisableShadows = true; }); @@ -56,11 +61,11 @@ void main() { await tester.pumpWidget(build(elevation)); await expectLater( find.byType(Container), - matchesSkiaGoldFile('shadow.ShapeDecoration.$elevation.png'), + matchesGoldenFile('shadow.ShapeDecoration.$elevation.png'), ); } debugDisableShadows = true; - }); + }, skip: !Platform.isLinux); // shadows render differently on different platforms testWidgets('Shadows with PhysicalLayer', (WidgetTester tester) async { await tester.pumpWidget( @@ -83,15 +88,18 @@ void main() { ); await expectLater( find.byType(Container), - matchesSkiaGoldFile('shadow.PhysicalModel.disabled.0.png'), + matchesGoldenFile('shadow.PhysicalModel.disabled.png'), ); debugDisableShadows = false; tester.binding.reassembleApplication(); await tester.pump(); - await expectLater( - find.byType(Container), - matchesSkiaGoldFile('shadow.PhysicalModel.enabled.png'), - ); + if (Platform.isLinux) { + // TODO(ianh): use the skip argument instead once that doesn't hang, https://github.com/dart-lang/test/issues/830 + await expectLater( + find.byType(Container), + matchesGoldenFile('shadow.PhysicalModel.enabled.png'), + ); // shadows render differently on different platforms + } debugDisableShadows = true; }); @@ -120,9 +128,9 @@ void main() { await tester.pumpWidget(build(elevation.toDouble())); await expectLater( find.byType(Container), - matchesSkiaGoldFile('shadow.PhysicalModel.disabled.1.$elevation.png'), + matchesGoldenFile('shadow.PhysicalShape.$elevation.1.png'), ); } debugDisableShadows = true; - }); + }, skip: !Platform.isLinux); // shadows render differently on different platforms } diff --git a/packages/flutter/test/widgets/text_golden_test.dart b/packages/flutter/test/widgets/text_golden_test.dart index cefe38fe8dc..cb94b2ab179 100644 --- a/packages/flutter/test/widgets/text_golden_test.dart +++ b/packages/flutter/test/widgets/text_golden_test.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:io' show Platform; + import 'package:flutter_test/flutter_test.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; @@ -29,7 +31,7 @@ void main() { await expectLater( find.byType(Container), - matchesSkiaGoldFile('text_golden.Centered.png'), + matchesGoldenFile('text_golden.Centered.png'), ); await tester.pumpWidget( @@ -53,9 +55,9 @@ void main() { await expectLater( find.byType(Container), - matchesSkiaGoldFile('text_golden.Centered.wrap.png'), + matchesGoldenFile('text_golden.Centered.wrap.png'), ); - }); + }, skip: !Platform.isLinux); testWidgets('Text Foreground', (WidgetTester tester) async { @@ -84,7 +86,7 @@ void main() { await expectLater( find.byType(RepaintBoundary), - matchesSkiaGoldFile('text_golden.Foreground.gradient.png'), + matchesGoldenFile('text_golden.Foreground.gradient.png'), ); await tester.pumpWidget( @@ -106,7 +108,7 @@ void main() { await expectLater( find.byType(RepaintBoundary), - matchesSkiaGoldFile('text_golden.Foreground.stroke.png'), + matchesGoldenFile('text_golden.Foreground.stroke.png'), ); await tester.pumpWidget( @@ -129,9 +131,9 @@ void main() { await expectLater( find.byType(RepaintBoundary), - matchesSkiaGoldFile('text_golden.Foreground.stroke_and_gradient.png'), + matchesGoldenFile('text_golden.Foreground.stroke_and_gradient.png'), ); - }); + }, skip: !Platform.isLinux); // TODO(garyq): This test requires an update when the background // drawing from the beginning of the line bug is fixed. The current @@ -179,9 +181,9 @@ void main() { await expectLater( find.byType(RepaintBoundary), - matchesSkiaGoldFile('text_golden.Background.png'), + matchesGoldenFile('text_golden.Background.png'), ); - }); + }, skip: !Platform.isLinux); testWidgets('Text Fade', (WidgetTester tester) async { await tester.pumpWidget( @@ -215,9 +217,9 @@ void main() { await expectLater( find.byType(RepaintBoundary).first, - matchesSkiaGoldFile('text_golden.Fade.1.png'), + matchesGoldenFile('text_golden.Fade.1.png'), ); - }); + }, skip: !Platform.isLinux); testWidgets('Default Strut text', (WidgetTester tester) async { await tester.pumpWidget( @@ -240,9 +242,10 @@ void main() { ); await expectLater( find.byType(Container), - matchesSkiaGoldFile('text_golden.StrutDefault.png'), + matchesGoldenFile('text_golden.StrutDefault.png'), ); - }); + }, skip: true); // Should only be on linux (skip: !Platform.isLinux). + // Disabled for now until font inconsistency is resolved. testWidgets('Strut text 1', (WidgetTester tester) async { await tester.pumpWidget( @@ -267,9 +270,10 @@ void main() { ); await expectLater( find.byType(Container), - matchesSkiaGoldFile('text_golden.Strut.1.png'), + matchesGoldenFile('text_golden.Strut.1.1.png'), ); - }); + }, skip: true); // Should only be on linux (skip: !Platform.isLinux). + // Disabled for now until font inconsistency is resolved. testWidgets('Strut text 2', (WidgetTester tester) async { await tester.pumpWidget( @@ -295,9 +299,10 @@ void main() { ); await expectLater( find.byType(Container), - matchesSkiaGoldFile('text_golden.Strut.2.png'), + matchesGoldenFile('text_golden.Strut.2.1.png'), ); - }); + }, skip: true); // Should only be on linux (skip: !Platform.isLinux). + // Disabled for now until font inconsistency is resolved. testWidgets('Strut text rich', (WidgetTester tester) async { await tester.pumpWidget( @@ -346,9 +351,10 @@ void main() { ); await expectLater( find.byType(Container), - matchesSkiaGoldFile('text_golden.Strut.3.png'), + matchesGoldenFile('text_golden.Strut.3.1.png'), ); - }); + }, skip: true); // Should only be on linux (skip: !Platform.isLinux). + // Disabled for now until font inconsistency is resolved. testWidgets('Strut text font fallback', (WidgetTester tester) async { // Font Fallback @@ -381,9 +387,10 @@ void main() { ); await expectLater( find.byType(Container), - matchesSkiaGoldFile('text_golden.Strut.4.png'), + matchesGoldenFile('text_golden.Strut.4.1.png'), ); - }); + }, skip: true); // Should only be on linux (skip: !Platform.isLinux). + // Disabled for now until font inconsistency is resolved. testWidgets('Strut text rich forceStrutHeight', (WidgetTester tester) async { await tester.pumpWidget( @@ -432,9 +439,10 @@ void main() { ); await expectLater( find.byType(Container), - matchesSkiaGoldFile('text_golden.StrutForce.1.png'), + matchesGoldenFile('text_golden.StrutForce.1.1.png'), ); - }); + }, skip: true); // Should only be on linux (skip: !Platform.isLinux). + // Disabled for now until font inconsistency is resolved. testWidgets('Decoration thickness', (WidgetTester tester) async { final TextDecoration allDecorations = TextDecoration.combine( @@ -470,9 +478,9 @@ void main() { ); await expectLater( find.byType(Container), - matchesSkiaGoldFile('text_golden.Decoration.1.png'), + matchesGoldenFile('text_golden.Decoration.1.0.png'), ); - }); + }, skip: !Platform.isLinux); // Coretext uses different thicknesses for decoration testWidgets('Decoration thickness', (WidgetTester tester) async { final TextDecoration allDecorations = TextDecoration.combine( @@ -509,7 +517,7 @@ void main() { ); await expectLater( find.byType(Container), - matchesSkiaGoldFile('text_golden.DecorationThickness.1.png'), + matchesGoldenFile('text_golden.DecorationThickness.1.0.png'), ); - }); + }, skip: !Platform.isLinux); // Coretext uses different thicknesses for decoration } diff --git a/packages/flutter/test/widgets/widget_inspector_test.dart b/packages/flutter/test/widgets/widget_inspector_test.dart index 400bd8a0812..4c09d4b6573 100644 --- a/packages/flutter/test/widgets/widget_inspector_test.dart +++ b/packages/flutter/test/widgets/widget_inspector_test.dart @@ -1896,7 +1896,8 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { expect(expectedChildLayerCount, equals(2)); await expectLater( layer.toImage(renderObject.semanticBounds.inflate(50.0)), - matchesSkiaGoldFile('inspector.repaint_boundary_margin.png'), + matchesGoldenFile('inspector.repaint_boundary_margin.png'), + skip: !Platform.isLinux, ); // Regression test for how rendering with a pixel scale other than 1.0 @@ -1906,7 +1907,8 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { renderObject.semanticBounds.inflate(50.0), pixelRatio: 0.5, ), - matchesSkiaGoldFile('inspector.repaint_boundary_margin_small.png'), + matchesGoldenFile('inspector.repaint_boundary_margin_small.png'), + skip: !Platform.isLinux, ); await expectLater( @@ -1914,7 +1916,8 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { renderObject.semanticBounds.inflate(50.0), pixelRatio: 2.0, ), - matchesSkiaGoldFile('inspector.repaint_boundary_margin_large.png'), + matchesGoldenFile('inspector.repaint_boundary_margin_large.png'), + skip: !Platform.isLinux, ); final Layer layerParent = layer.parent; @@ -1929,7 +1932,8 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { width: 300.0, height: 300.0, ), - matchesSkiaGoldFile('inspector.repaint_boundary.png'), + matchesGoldenFile('inspector.repaint_boundary.png'), + skip: !Platform.isLinux, ); // Verify that taking a screenshot didn't change the layers associated with @@ -1946,7 +1950,8 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { height: 500.0, margin: 50.0, ), - matchesSkiaGoldFile('inspector.repaint_boundary_margin.png'), + matchesGoldenFile('inspector.repaint_boundary_margin.png'), + skip: !Platform.isLinux, ); // Verify that taking a screenshot didn't change the layers associated with @@ -1966,7 +1971,8 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { height: 300.0, debugPaint: true, ), - matchesSkiaGoldFile('inspector.repaint_boundary_debugPaint.png'), + matchesGoldenFile('inspector.repaint_boundary_debugPaint.png'), + skip: !Platform.isLinux, ); // Verify that taking a screenshot with debug paint on did not change // the number of children the layer has. @@ -1976,7 +1982,8 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { // hasn't changed the regular render of the widget. await expectLater( find.byType(RepaintBoundaryWithDebugPaint), - matchesSkiaGoldFile('inspector.repaint_boundary.png'), + matchesGoldenFile('inspector.repaint_boundary.png'), + skip: !Platform.isLinux, ); expect(renderObject.debugLayer, equals(layer)); @@ -1989,7 +1996,8 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { width: 100.0, height: 100.0, ), - matchesSkiaGoldFile('inspector.container.png'), + matchesGoldenFile('inspector.container.png'), + skip: !Platform.isLinux, ); await expectLater( @@ -1999,7 +2007,8 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { height: 100.0, debugPaint: true, ), - matchesSkiaGoldFile('inspector.container_debugPaint.png'), + matchesGoldenFile('inspector.container_debugPaint.png'), + skip: !Platform.isLinux, ); { @@ -2019,7 +2028,8 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { height: 100.0, debugPaint: true, ), - matchesSkiaGoldFile('inspector.container_debugPaint.png'), + matchesGoldenFile('inspector.container_debugPaint.png'), + skip: !Platform.isLinux, ); expect(container.debugNeedsLayout, isFalse); } @@ -2031,7 +2041,8 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { width: 50.0, height: 100.0, ), - matchesSkiaGoldFile('inspector.container_small.png'), + matchesGoldenFile('inspector.container_small.png'), + skip: !Platform.isLinux, ); await expectLater( @@ -2041,7 +2052,8 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { height: 400.0, maxPixelRatio: 3.0, ), - matchesSkiaGoldFile('inspector.container_large.png'), + matchesGoldenFile('inspector.container_large.png'), + skip: !Platform.isLinux, ); // This screenshot will show the clip rect debug paint but no other @@ -2053,7 +2065,8 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { height: 100.0, debugPaint: true, ), - matchesSkiaGoldFile('inspector.clipRect_debugPaint.png'), + matchesGoldenFile('inspector.clipRect_debugPaint.png'), + skip: !Platform.isLinux, ); final Element clipRect = find.byType(ClipRRect).evaluate().single; @@ -2067,10 +2080,10 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { ); // Add a margin so that the clip icon shows up in the screenshot. // This golden image is platform dependent due to the clip icon. - final String platform = Platform.operatingSystem; await expectLater( clipRectScreenshot, - matchesSkiaGoldFile('inspector.clipRect_debugPaint_margin.$platform.png'), + matchesGoldenFile('inspector.clipRect_debugPaint_margin.png'), + skip: !Platform.isLinux, ); // Verify we get the same image if we go through the service extension @@ -2109,7 +2122,8 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { height: 300.0, debugPaint: true, ), - matchesSkiaGoldFile('inspector.padding_debugPaint.png'), + matchesGoldenFile('inspector.padding_debugPaint.png'), + skip: !Platform.isLinux, ); // The bounds for this box crop its rendered content. @@ -2120,7 +2134,8 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { height: 300.0, debugPaint: true, ), - matchesSkiaGoldFile('inspector.sizedBox_debugPaint.png'), + matchesGoldenFile('inspector.sizedBox_debugPaint.1.png'), + skip: !Platform.isLinux, ); // Verify that setting a margin includes the previously cropped content. @@ -2132,7 +2147,8 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { margin: 50.0, debugPaint: true, ), - matchesSkiaGoldFile('inspector.sizedBox_debugPaint_margin.png'), + matchesGoldenFile('inspector.sizedBox_debugPaint_margin.png'), + skip: !Platform.isLinux, ); }); @@ -2203,7 +2219,8 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { await expectLater( find.byKey(mainStackKey), - matchesSkiaGoldFile('inspector.composited_transform.only_offsets.png'), + matchesGoldenFile('inspector.composited_transform.only_offsets.png'), + skip: !Platform.isLinux, ); await expectLater( @@ -2212,12 +2229,14 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { width: 5000.0, height: 500.0, ), - matchesSkiaGoldFile('inspector.composited_transform.only_offsets_follower.png'), + matchesGoldenFile('inspector.composited_transform.only_offsets_follower.png'), + skip: !Platform.isLinux, ); await expectLater( WidgetInspectorService.instance.screenshot(find.byType(Stack).evaluate().first, width: 300.0, height: 300.0), - matchesSkiaGoldFile('inspector.composited_transform.only_offsets_small.png'), + matchesGoldenFile('inspector.composited_transform.only_offsets_small.1.png'), + skip: !Platform.isLinux, ); await expectLater( @@ -2226,7 +2245,8 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { width: 500.0, height: 500.0, ), - matchesSkiaGoldFile('inspector.composited_transform.only_offsets_target.png'), + matchesGoldenFile('inspector.composited_transform.only_offsets_target.png'), + skip: !Platform.isLinux, ); }); @@ -2298,7 +2318,8 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { // screenshots of specific subtrees are reasonable. await expectLater( find.byKey(mainStackKey), - matchesSkiaGoldFile('inspector.composited_transform.with_rotations.png'), + matchesGoldenFile('inspector.composited_transform.with_rotations.png'), + skip: !Platform.isLinux, ); await expectLater( @@ -2307,7 +2328,8 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { width: 500.0, height: 500.0, ), - matchesSkiaGoldFile('inspector.composited_transform.with_rotations_small.png'), + matchesGoldenFile('inspector.composited_transform.with_rotations_small.png'), + skip: !Platform.isLinux, ); await expectLater( @@ -2316,7 +2338,8 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { width: 500.0, height: 500.0, ), - matchesSkiaGoldFile('inspector.composited_transform.with_rotations_target.png'), + matchesGoldenFile('inspector.composited_transform.with_rotations_target.png'), + skip: !Platform.isLinux, ); await expectLater( @@ -2325,7 +2348,8 @@ class TestWidgetInspectorService extends Object with WidgetInspectorService { width: 500.0, height: 500.0, ), - matchesSkiaGoldFile('inspector.composited_transform.with_rotations_follower.png'), + matchesGoldenFile('inspector.composited_transform.with_rotations_follower.png'), + skip: !Platform.isLinux, ); // Make sure taking screenshots hasn't modified the positions of the diff --git a/packages/flutter_goldens/lib/flutter_goldens.dart b/packages/flutter_goldens/lib/flutter_goldens.dart index decc5d65935..4f28df3576e 100644 --- a/packages/flutter_goldens/lib/flutter_goldens.dart +++ b/packages/flutter_goldens/lib/flutter_goldens.dart @@ -3,20 +3,16 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:io'; import 'dart:typed_data'; import 'package:file/file.dart'; import 'package:file/local.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:meta/meta.dart'; -import 'package:test_api/test_api.dart' as test_package show TestFailure; import 'package:flutter_goldens_client/client.dart'; export 'package:flutter_goldens_client/client.dart'; -const String _kFlutterRootKey = 'FLUTTER_ROOT'; - /// Main method that can be used in a `flutter_test_config.dart` file to set /// [goldenFileComparator] to an instance of [FlutterGoldenFileComparator] that /// works for the current test. @@ -29,12 +25,12 @@ Future main(FutureOr testMain()) async { /// /// Within the https://github.com/flutter/flutter repository, it's important /// not to check-in binaries in order to keep the size of the repository to a -/// minimum. To satisfy this requirement, this comparator uses the -/// [SkiaGoldClient] to upload widgets for framework-related golden tests and -/// process results. +/// minimum. To satisfy this requirement, this comparator retrieves the golden +/// files from a sibling repository, `flutter/goldens`. /// -/// This comparator will instantiate the [SkiaGoldClient] and process the -/// results of the test. +/// This comparator will locally clone the `flutter/goldens` repository into +/// the `$FLUTTER_ROOT/bin/cache/pkg/goldens` folder, then perform the comparison against +/// the files therein. class FlutterGoldenFileComparator implements GoldenFileComparator { /// Creates a [FlutterGoldenFileComparator] that will resolve golden file /// URIs relative to the specified [basedir]. @@ -53,51 +49,48 @@ class FlutterGoldenFileComparator implements GoldenFileComparator { @visibleForTesting final FileSystem fs; - /// Instance of the [SkiaGoldClient] for executing tests. - final SkiaGoldClient _skiaClient = SkiaGoldClient(); - /// Creates a new [FlutterGoldenFileComparator] that mirrors the relative /// path resolution of the default [goldenFileComparator]. /// - /// The [defaultComparator] parameter is visible for testing + /// By the time the future completes, the clone of the `flutter/goldens` + /// repository is guaranteed to be ready use. + /// + /// The [goldens] and [defaultComparator] parameters are visible for testing /// purposes only. static Future fromDefaultComparator({ + GoldensClient goldens, LocalFileComparator defaultComparator, }) async { defaultComparator ??= goldenFileComparator; + // Prepare the goldens repo. + goldens ??= GoldensClient(); + await goldens.prepare(); + // Calculate the appropriate basedir for the current test context. - const FileSystem fs = LocalFileSystem(); + final FileSystem fs = goldens.fs; final Directory testDirectory = fs.directory(defaultComparator.basedir); - final Directory flutterRoot = fs.directory(Platform.environment[_kFlutterRootKey]); - final Directory goldenRoot = flutterRoot.childDirectory(fs.path.join( - 'bin', - 'cache', - 'pkg', - 'goldens', - )); - final String testDirectoryRelativePath = fs.path.relative( - testDirectory.path, - from: flutterRoot.path, - ); - return FlutterGoldenFileComparator(goldenRoot.childDirectory(testDirectoryRelativePath).uri); + final String testDirectoryRelativePath = fs.path.relative(testDirectory.path, from: goldens.flutterRoot.path); + return FlutterGoldenFileComparator(goldens.repositoryRoot.childDirectory(testDirectoryRelativePath).uri); } @override Future compare(Uint8List imageBytes, Uri golden) async { final File goldenFile = _getGoldenFile(golden); - if(!goldenFile.existsSync()) { - throw test_package.TestFailure('Could not be compared against non-existent file: "$golden"'); + if (!goldenFile.existsSync()) { + throw TestFailure('Could not be compared against non-existent file: "$golden"'); } - final bool authorized = await _skiaClient.auth(fs.directory(basedir)); - if (!authorized) { - // TODO(Piinks): Clean up for final implementation on CI, https://github.com/flutter/flutter/pull/31630 - return true; - //throw test_package.TestFailure('Could not authorize golctl.'); + final List goldenBytes = await goldenFile.readAsBytes(); + // TODO(tvolkert): Improve the intelligence of this comparison. + if (goldenBytes.length != imageBytes.length) { + return false; } - await _skiaClient.imgtestInit(); - - return await _skiaClient.imgtestAdd(golden.path, goldenFile); + for (int i = 0; i < goldenBytes.length; i++) { + if (goldenBytes[i] != imageBytes[i]) { + return false; + } + } + return true; } @override diff --git a/packages/flutter_goldens/test/flutter_goldens_test.dart b/packages/flutter_goldens/test/flutter_goldens_test.dart index a1a46968947..19645edc8f2 100644 --- a/packages/flutter_goldens/test/flutter_goldens_test.dart +++ b/packages/flutter_goldens/test/flutter_goldens_test.dart @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:io' as io; import 'dart:typed_data'; import 'package:file/file.dart'; @@ -13,57 +14,49 @@ import 'package:platform/platform.dart'; import 'package:process/process.dart'; const String _kFlutterRoot = '/flutter'; -//const String _kGoldenRoot = '$_kFlutterRoot/bin/cache/pkg/goldens'; -//const String _kVersionFile = '$_kFlutterRoot/bin/internal/goldens.version'; -//const String _kGoldensVersion = '123456abcdef'; -// TODO(Piinks): Finish testing, https://github.com/flutter/flutter/pull/31630 +const String _kRepositoryRoot = '$_kFlutterRoot/bin/cache/pkg/goldens'; +const String _kVersionFile = '$_kFlutterRoot/bin/internal/goldens.version'; +const String _kGoldensVersion = '123456abcdef'; + void main() { MemoryFileSystem fs; FakePlatform platform; MockProcessManager process; - //Directory flutter; - //Directory golden; - setUp(() async { + setUp(() { fs = MemoryFileSystem(); - platform = FakePlatform(environment: { - 'FLUTTER_ROOT': _kFlutterRoot, - // TODO(Piinks): Add other env vars for testing, https://github.com/flutter/flutter/pull/31630 - }); + platform = FakePlatform(environment: {'FLUTTER_ROOT': _kFlutterRoot}); process = MockProcessManager(); - //flutter = await fs.directory(_kFlutterRoot).create(recursive: true); - //golden = await fs.directory(_kGoldenRoot).create(recursive: true); - //fs.file(_kVersionFile).createSync(recursive: true); - //fs.file(_kVersionFile).writeAsStringSync(_kGoldensVersion); + fs.directory(_kFlutterRoot).createSync(recursive: true); + fs.directory(_kRepositoryRoot).createSync(recursive: true); + fs.file(_kVersionFile).createSync(recursive: true); + fs.file(_kVersionFile).writeAsStringSync(_kGoldensVersion); }); - group('SkiaGoldClient', () { - //SkiaGoldClient skiaGold; + group('GoldensClient', () { + GoldensClient goldens; setUp(() { - //skiaGold = - SkiaGoldClient( + goldens = GoldensClient( fs: fs, platform: platform, process: process, ); }); - group('auth', () { - // check for successful auth - return true - // check for unsuccessful auth - throw NonZeroExitCode - // check for unavailable auth (not on CI) - return false - // check for redundant work - }); - - group('init', () { - // check for successful init - return true - // check for unsuccessful init - throw NonZeroExitCode - // Check for redundant work - }); - - group('imgtest', () { + group('prepare', () { + test('performs minimal work if versions match', () async { + when(process.run(any, workingDirectory: anyNamed('workingDirectory'))) + .thenAnswer((_) => Future.value(io.ProcessResult(123, 0, _kGoldensVersion, ''))); + await goldens.prepare(); + // Verify that we only spawned `git rev-parse HEAD` + final VerificationResult verifyProcessRun = + verify(process.run(captureAny, workingDirectory: captureAnyNamed('workingDirectory'))); + verifyProcessRun.called(1); + expect(verifyProcessRun.captured.first, ['git', 'rev-parse', 'HEAD']); + expect(verifyProcessRun.captured.last, _kRepositoryRoot); + }); }); }); @@ -81,22 +74,21 @@ void main() { group('fromDefaultComparator', () { test('calculates the basedir correctly', () async { -// final MockSkiaGoldClient skiaGold = MockSkiaGoldClient(); -// final MockLocalFileComparator defaultComparator = MockLocalFileComparator(); -// final Directory flutterRoot = fs.directory('/foo')..createSync(recursive: true); -// final Directory skiaGoldRoot = flutterRoot.childDirectory('bar')..createSync(recursive: true); -// when(skiaGold.fs).thenReturn(fs); -// when(skiaGold.flutterRoot).thenReturn(flutterRoot); -// when(skiaGold.repositoryRoot).thenReturn(skiaGoldRoot); -// when(defaultComparator.basedir).thenReturn(flutterRoot.childDirectory('baz').uri); -// comparator = await FlutterGoldenFileComparator.fromDefaultComparator( -// goldens: goldens, defaultComparator: defaultComparator); -// expect(comparator.basedir, fs.directory('/foo/bar/baz').uri); + final MockGoldensClient goldens = MockGoldensClient(); + final MockLocalFileComparator defaultComparator = MockLocalFileComparator(); + final Directory flutterRoot = fs.directory('/foo')..createSync(recursive: true); + final Directory goldensRoot = flutterRoot.childDirectory('bar')..createSync(recursive: true); + when(goldens.fs).thenReturn(fs); + when(goldens.flutterRoot).thenReturn(flutterRoot); + when(goldens.repositoryRoot).thenReturn(goldensRoot); + when(defaultComparator.basedir).thenReturn(flutterRoot.childDirectory('baz').uri); + comparator = await FlutterGoldenFileComparator.fromDefaultComparator( + goldens: goldens, defaultComparator: defaultComparator); + expect(comparator.basedir, fs.directory('/foo/bar/baz').uri); }); }); group('compare', () { - test('throws if golden file is not found', () async { try { await comparator.compare(Uint8List.fromList([1, 2, 3]), Uri.parse('test.png')); @@ -106,32 +98,21 @@ void main() { } }); - // TODO(Piinks): This is currently disabled in flutter_goldens.dart, https://github.com/flutter/flutter/pull/31630 -// test('throws if goldctl has not been authorized', () async { -// // See that preceding test does not leave auth behind [52] -// try { -// await comparator.compare(Uint8List.fromList([1, 2, 3]), Uri.parse('test.png')); -// fail('TestFailure expected but not thrown'); -// } on TestFailure catch (error) { -// expect(error.message, contains('Could not authorize goldctl.')); -// } -// }); - // TODO(Piinks): Add methods to Mock SkiaGoldClient to inform the comparator and test for proper behavior. See matcher_test.dart for model, https://github.com/flutter/flutter/pull/31630 -// test('returns false if skia gold test fails', () async { -// final File goldenFile = fs.file('/path/to/flutter/bin/cache/goldens/test/foo/bar/test.png') -// ..createSync(recursive: true); -// goldenFile.writeAsBytesSync([4, 5, 6], flush: true); -// final bool result = await comparator.compare(Uint8List.fromList([1, 2, 3]), Uri.parse('test.png')); -// expect(result, isFalse); -// }); -// -// test('returns true if skia gold test passes', () async { -// final File goldenFile = fs.file('/path/to/flutter/bin/cache/goldens/test/foo/bar/test.png') -// ..createSync(recursive: true); -// goldenFile.writeAsBytesSync([1, 2, 3], flush: true); -// final bool result = await comparator.compare(Uint8List.fromList([1, 2, 3]), Uri.parse('test.png')); -// expect(result, isTrue); -// }); + test('returns false if golden bytes do not match', () async { + final File goldenFile = fs.file('/path/to/flutter/bin/cache/goldens/test/foo/bar/test.png') + ..createSync(recursive: true); + goldenFile.writeAsBytesSync([4, 5, 6], flush: true); + final bool result = await comparator.compare(Uint8List.fromList([1, 2, 3]), Uri.parse('test.png')); + expect(result, isFalse); + }); + + test('returns true if golden bytes match', () async { + final File goldenFile = fs.file('/path/to/flutter/bin/cache/goldens/test/foo/bar/test.png') + ..createSync(recursive: true); + goldenFile.writeAsBytesSync([1, 2, 3], flush: true); + final bool result = await comparator.compare(Uint8List.fromList([1, 2, 3]), Uri.parse('test.png')); + expect(result, isTrue); + }); }); group('update', () { @@ -155,5 +136,5 @@ void main() { } class MockProcessManager extends Mock implements ProcessManager {} -class MockSkiaGoldClient extends Mock implements SkiaGoldClient {} +class MockGoldensClient extends Mock implements GoldensClient {} class MockLocalFileComparator extends Mock implements LocalFileComparator {} diff --git a/packages/flutter_goldens_client/lib/client.dart b/packages/flutter_goldens_client/lib/client.dart index 27e3b2ab16a..6c290ed7939 100644 --- a/packages/flutter_goldens_client/lib/client.dart +++ b/packages/flutter_goldens_client/lib/client.dart @@ -3,12 +3,10 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:convert' as convert; import 'dart:io' as io; import 'package:file/file.dart'; import 'package:file/local.dart'; -import 'package:path/path.dart' as path; import 'package:platform/platform.dart'; import 'package:process/process.dart'; @@ -17,210 +15,163 @@ import 'package:process/process.dart'; // https://github.com/flutter/flutter/wiki/Writing-a-golden-file-test-for-package%3Aflutter const String _kFlutterRootKey = 'FLUTTER_ROOT'; -const String _kGoldctlKey = 'GOLDCTL'; -const String _kServiceAccountKey = 'GOLD_SERVICE_ACCOUNT'; -const String _kSkiaGoldInstance = 'SKIA_GOLD_INSTANCE'; -/// A class that represents the Skia Gold client for golden file testing. -class SkiaGoldClient { - /// Create a handle to a local workspace for the Skia Gold Client. - SkiaGoldClient({ +/// A class that represents a clone of the https://github.com/flutter/goldens +/// repository, nested within the `bin/cache` directory of the caller's Flutter +/// repository. +class GoldensClient { + /// Create a handle to a local clone of the goldens repository. + GoldensClient({ this.fs = const LocalFileSystem(), this.platform = const LocalPlatform(), this.process = const LocalProcessManager(), }); - /// The file system to use for storing local files for running imgtests. + /// The file system to use for storing the local clone of the repository. /// - /// This is useful in tests, where a local file system (the default) can be - /// replaced by a memory file system. + /// This is useful in tests, where a local file system (the default) can + /// be replaced by a memory file system. final FileSystem fs; /// A wrapper for the [dart:io.Platform] API. /// - /// This is useful in tests, where the system platform (the default) can be - /// replaced by a mock platform instance. + /// This is useful in tests, where the system platform (the default) can + /// be replaced by a mock platform instance. final Platform platform; - /// A controller for launching sub-processes. + /// A controller for launching subprocesses. /// - /// This is useful in tests, where the real process manager (the default) can - /// be replaced by a mock process manager that doesn't really create - /// sub-processes. + /// This is useful in tests, where the real process manager (the default) + /// can be replaced by a mock process manager that doesn't really create + /// subprocesses. final ProcessManager process; - Directory _workDirectory; + RandomAccessFile _lock; - // TODO(Piinks): Environment variables are temporary for local testing, https://github.com/flutter/flutter/pull/31630 /// The local [Directory] where the Flutter repository is hosted. /// /// Uses the [fs] file system. Directory get flutterRoot => fs.directory(platform.environment[_kFlutterRootKey]); - /// The [path] to the local [Directory] where the goldctl tool is hosted. + /// The local [Directory] where the goldens repository is hosted. /// - /// Uses the [platform] [environment] in this iteration. - String get _goldctl => platform.environment[_kGoldctlKey]; + /// Uses the [fs] file system. + Directory get repositoryRoot => flutterRoot.childDirectory(fs.path.join('bin', 'cache', 'pkg', 'goldens')); - /// The [path] to the local [Directory] where the service account key is - /// hosted. + /// Prepares the local clone of the `flutter/goldens` repository for golden + /// file testing. /// - /// Uses the [platform] [environment] in this iteration. - String get _serviceAccount => platform.environment[_kServiceAccountKey]; - - /// The name of the Skia Gold Flutter instance. + /// This ensures that the goldens repository has been cloned into its + /// expected location within `bin/cache` and that it is synced to the Git + /// revision specified in `bin/internal/goldens.version`. /// - /// Uses the [platform] [environment] in this iteration. - String get _skiaGoldInstance => platform.environment[_kSkiaGoldInstance]; - - /// Prepares the local work space for golden file testing and initializes the - /// goldctl authorization for executing tests. - /// - /// This ensures that the goldctl tool is authorized and ready for testing. - Future auth(Directory workDirectory) async { - _workDirectory = workDirectory; - - // TODO(Piinks): Cleanup for final CI implementation, https://github.com/flutter/flutter/pull/31630 - if (_serviceAccount == null) - return false; // Not in the proper environment for golden file testing. - - final File authFile = _workDirectory.childFile(fs.path.join( - 'temp', - 'auth_opt.json' - )); - if (!authFile.existsSync()) { - final List authArguments = [ - 'auth', - '--service-account', _serviceAccount, - '--work-dir', _workDirectory.childDirectory('temp').path, - ]; - - final io.ProcessResult authResults = io.Process.runSync( - _goldctl, - authArguments - ); - if (authResults.exitCode != 0) { - final StringBuffer buf = StringBuffer(); - buf - ..writeln('Flutter + Skia Gold auth failed.') - ..writeln('stdout: ${authResults.stdout}') - ..writeln('stderr: ${authResults.stderr}'); - throw NonZeroExitCode(authResults.exitCode, buf.toString()); - } - } - return true; - } - - Future imgtestInit() async { - final File keysFile = _workDirectory.childFile('keys.json'); - - if(!keysFile.existsSync() || await _isNewCommit()) { - final String commitHash = await _getCommitHash(); - final String keys = '${_workDirectory.path}keys.json'; - final String failures = '${_workDirectory.path}failures.json'; - - await io.File(keys).writeAsString(_getKeysJSON()); - await io.File(failures).create(); - - final List imgtestInitArguments = [ - 'imgtest', 'init', - '--instance', _skiaGoldInstance, - '--work-dir', _workDirectory.childDirectory('temp').path, - '--commit', commitHash, - '--keys-file', keys, - '--failure-file', failures, - '--passfail', - ]; - - if(imgtestInitArguments.contains(null)) { - final StringBuffer buf = StringBuffer(); - buf.writeln('Null argument for Skia Gold imgtest init:'); - imgtestInitArguments.forEach(buf.writeln); - throw NonZeroExitCode(1, buf.toString()); - } - - final io.ProcessResult imgtestInitResult = io.Process.runSync( - _goldctl, - imgtestInitArguments, - ); - - if (imgtestInitResult.exitCode != 0) { - final StringBuffer buf = StringBuffer(); - buf - ..writeln('Flutter + Skia Gold imgtest init failed.') - ..writeln('stdout: ${imgtestInitResult.stdout}') - ..writeln('stderr: ${imgtestInitResult.stderr}'); - throw NonZeroExitCode(imgtestInitResult.exitCode, buf.toString()); + /// While this is preparing the repository, it obtains a file lock such that + /// [GoldensClient] instances in other processes or isolates will not + /// duplicate the work that this is doing. + Future prepare() async { + final String goldensCommit = await _getGoldensCommit(); + String currentCommit = await _getCurrentCommit(); + if (currentCommit != goldensCommit) { + await _obtainLock(); + try { + // Check the current commit again now that we have the lock. + currentCommit = await _getCurrentCommit(); + if (currentCommit != goldensCommit) { + if (currentCommit == null) { + await _initRepository(); + } + await _checkCanSync(); + await _syncTo(goldensCommit); + } + } finally { + await _releaseLock(); } } } - Future imgtestAdd(String testName, File goldenFile) async { - final List imgtestArguments = [ - 'imgtest', 'add', - '--work-dir', _workDirectory.childDirectory('temp').path, - '--test-name', testName, - '--png-file', goldenFile.path, - ]; + Future _getGoldensCommit() async { + final File versionFile = flutterRoot.childFile(fs.path.join('bin', 'internal', 'goldens.version')); + return (await versionFile.readAsString()).trim(); + } - if(imgtestArguments.contains(null)) { - final StringBuffer buf = StringBuffer(); - buf.writeln('Null argument for Skia Gold imgtest add:'); - imgtestArguments.forEach(buf.writeln); - throw NonZeroExitCode(1, buf.toString()); + Future _getCurrentCommit() async { + if (!repositoryRoot.existsSync()) { + return null; + } else { + final io.ProcessResult revParse = await process.run( + ['git', 'rev-parse', 'HEAD'], + workingDirectory: repositoryRoot.path, + ); + return revParse.exitCode == 0 ? revParse.stdout.trim() : null; } + } - final io.ProcessResult imgtestResult = io.Process.runSync( - _goldctl, - imgtestArguments, + Future _initRepository() async { + await repositoryRoot.create(recursive: true); + await _runCommands( + [ + 'git init', + 'git remote add upstream https://github.com/flutter/goldens.git', + 'git remote set-url --push upstream git@github.com:flutter/goldens.git', + ], + workingDirectory: repositoryRoot, ); - if (imgtestResult.exitCode != 0) { + } + + Future _checkCanSync() async { + final io.ProcessResult result = await process.run( + ['git', 'status', '--porcelain'], + workingDirectory: repositoryRoot.path, + ); + if (result.stdout.trim().isNotEmpty) { final StringBuffer buf = StringBuffer(); buf - ..writeln('Flutter + Skia Gold imgtest add failed.') - ..writeln('If this is the first execution of this test, it may need to be triaged.') - ..writeln('In this case, re-run the test after triage is completed.\n') - ..writeln('stdout: ${imgtestResult.stdout}') - ..writeln('stderr: ${imgtestResult.stderr}'); - throw NonZeroExitCode(imgtestResult.exitCode, buf.toString()); + ..writeln('flutter_goldens git checkout at ${repositoryRoot.path} has local changes and cannot be synced.') + ..writeln('To reset your client to a clean state, and lose any local golden test changes:') + ..writeln('cd ${repositoryRoot.path}') + ..writeln('git reset --hard HEAD') + ..writeln('git clean -x -d -f -f'); + throw NonZeroExitCode(1, buf.toString()); } - return true; } - Future _getCommitHash() async { - // TODO(Piinks): Remove after pre-commit tests can be ingested by Skia Gold, https://github.com/flutter/flutter/pull/31630 - return 'e51947241b37790a9e4e33bfdef59aaa9b930851'; -// if (!flutterRoot.existsSync()) { -// return null; -// } else { -// final io.ProcessResult revParse = await process.run( -// ['git', 'rev-parse', 'HEAD'], -// workingDirectory: flutterRoot.path, -// ); -// return revParse.exitCode == 0 ? revParse.stdout.trim() : null; + Future _syncTo(String commit) async { + await _runCommands( + [ + 'git pull upstream master', + 'git fetch upstream $commit', + 'git reset --hard FETCH_HEAD', + ], + workingDirectory: repositoryRoot, + ); } - Future _isNewCommit() async { - // auth file is there, need to check if we are on a new commit - final File resultFile = _workDirectory.childFile(fs.path.join( - 'temp', - 'result-state.json' - )); - final String contents = await resultFile.readAsString(); - final Map resultJSON = convert.json.decode(contents); - final String lastTestedCommit = resultJSON['SharedConfig']['gitHash']; - final String currentCommit = await _getCommitHash(); - return lastTestedCommit == currentCommit ? false : true; + Future _runCommands( + List commands, { + Directory workingDirectory, + }) async { + for (String command in commands) { + final List parts = command.split(' '); + final io.ProcessResult result = await process.run( + parts, + workingDirectory: workingDirectory?.path, + ); + if (result.exitCode != 0) { + throw NonZeroExitCode(result.exitCode, result.stderr); + } + } } - String _getKeysJSON() { - // TODO(Piinks): Parse out cleaner key information, https://github.com/flutter/flutter/pull/31630 - return convert.json.encode( - { - 'Operating System' : io.Platform.operatingSystem, - 'Operating System Version' : io.Platform.operatingSystemVersion, - 'Dart Version' : io.Platform.version, - }); + Future _obtainLock() async { + final File lockFile = flutterRoot.childFile(fs.path.join('bin', 'cache', 'goldens.lockfile')); + await lockFile.create(recursive: true); + _lock = await lockFile.open(mode: io.FileMode.write); + await _lock.lock(io.FileLock.blockingExclusive); + } + + Future _releaseLock() async { + await _lock.close(); + _lock = null; } } /// Exception that signals a process' exit with a non-zero exit code. @@ -242,4 +193,4 @@ class NonZeroExitCode implements Exception { String toString() { return 'Exit code $exitCode: $stderr'; } -} \ No newline at end of file +} diff --git a/packages/flutter_test/lib/src/matchers.dart b/packages/flutter_test/lib/src/matchers.dart index d716b3985ee..d6350417407 100644 --- a/packages/flutter_test/lib/src/matchers.dart +++ b/packages/flutter_test/lib/src/matchers.dart @@ -332,43 +332,6 @@ AsyncMatcher matchesGoldenFile(dynamic key) { throw ArgumentError('Unexpected type for golden file: ${key.runtimeType}'); } -/// Asserts that a [Finder], [Future], or [ui.Image] matches the -/// golden image file identified by [key] through Skia Gold. -/// -/// For the case of a [Finder], the [Finder] must match exactly one widget and -/// the rendered image of the first [RepaintBoundary] ancestor of the widget is -/// treated as the image for the widget. -/// -/// [key] may be either a [Uri] or a [String] representation of a URI. -/// -/// This is an asynchronous matcher, meaning that callers should use -/// [expectLater] when using this matcher and await the future returned by -/// [expectLater]. -/// -/// ## Sample code -/// -/// ```dart -/// await expectLater(find.text('Save'), matchesSkiaGoldFile('save.png')); -/// await expectLater(image, matchesSkiaGoldFile('save.png')); -/// await expectLater(imageFuture, matchesSkiaGoldFile('save.png')); -/// ``` -/// -/// See also: -/// -/// * [FlutterGoldenFileComparator], which acts as the backend for this matcher. -/// * [SkiaGoldClient], which the [FlutterGoldenFileComparator] uses to execute -/// and process results of testing with Skia Gold. -/// * [flutter_test] for a discussion of test configurations, whereby callers -/// may swap out the backend for this matcher. -AsyncMatcher matchesSkiaGoldFile(dynamic key) { - if (key is Uri) { - return _MatchesSkiaGoldFile(key); - } else if (key is String) { - return _MatchesSkiaGoldFile.forStringPath(key); - } - throw ArgumentError('Unexpected type for Skia Gold file: ${key.runtimeType}'); -} - /// Asserts that a [Finder], [Future], or [ui.Image] matches a /// reference image identified by [image]. /// @@ -1763,53 +1726,6 @@ class _MatchesGoldenFile extends AsyncMatcher { description.add('one widget whose rasterized image matches golden image "$key"'); } -class _MatchesSkiaGoldFile extends AsyncMatcher { - const _MatchesSkiaGoldFile(this.key); - - _MatchesSkiaGoldFile.forStringPath(String path) : key = Uri.parse(path); - - final Uri key; - - @override - Future matchAsync(dynamic item) async { - Future imageFuture; - if (item is Future) { - imageFuture = item; - }else if (item is ui.Image) { - imageFuture = Future.value(item); - } else { - final Finder finder = item; - final Iterable elements = finder.evaluate(); - if (elements.isEmpty) { - return 'could not be rendered because no widget was found.'; - } else if (elements.length > 1) { - return 'matched too many widgets.'; - } - imageFuture = _captureImage(elements.single); - } - - final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized(); - return binding.runAsync(() async { - final ui.Image image = await imageFuture; - final ByteData bytes = await image.toByteData(format: ui.ImageByteFormat.png) - .timeout(const Duration(seconds: 10), onTimeout: () => null); - if (bytes == null) - return 'Failed to generate screenshot from engine within the 10,000ms timeout'; - await goldenFileComparator.update(key, bytes.buffer.asUint8List()); - try { - final bool success = await goldenFileComparator.compare(null, key); - return success ? null : 'Skia Gold test fail.'; - } on TestFailure catch (ex) { - return ex.message; - } - }, additionalTime: const Duration(seconds: 11)); - } - - @override - Description describe(Description description) => - description.add('one widget whose rasterized images matches Skia Gold image $key'); -} - class _MatchesSemanticsData extends Matcher { _MatchesSemanticsData({ this.label, diff --git a/packages/flutter_test/test/matchers_test.dart b/packages/flutter_test/test/matchers_test.dart index 3af4e7f9cad..9568911da5d 100644 --- a/packages/flutter_test/test/matchers_test.dart +++ b/packages/flutter_test/test/matchers_test.dart @@ -402,88 +402,6 @@ void main() { }); }); - group('matchesSkiaGoldFile', () { - _FakeComparator comparator; - - Widget boilerplate(Widget child) { - return Directionality( - textDirection: TextDirection.ltr, - child: child, - ); - } - - setUp(() { - comparator = _FakeComparator(); - goldenFileComparator = comparator; - }); - - group('matches', () { - testWidgets('if comparator succeeds', (WidgetTester tester) async { - await tester.pumpWidget(boilerplate(const Text('hello'))); - final Finder finder = find.byType(Text); - await expectLater(finder, matchesSkiaGoldFile('foo.png')); - expect(comparator.invocation, _ComparatorInvocation.compare); - expect(comparator.imageBytes, null); - expect(comparator.golden, Uri.parse('foo.png')); - }); - }); - - group('does not match', () { - testWidgets('if comparator returns false', (WidgetTester tester) async { - comparator.behavior = _ComparatorBehavior.returnFalse; - await tester.pumpWidget(boilerplate(const Text('hello'))); - final Finder finder = find.byType(Text); - try { - await expectLater(finder, matchesSkiaGoldFile('foo.png')); - fail('TestFailure expected but not thrown'); - } on TestFailure catch (error) { - expect(comparator.invocation, _ComparatorInvocation.compare); - expect(error.message, contains('does not match')); - } - }); - - testWidgets('if comparator throws', (WidgetTester tester) async { - comparator.behavior = _ComparatorBehavior.throwTestFailure; - await tester.pumpWidget(boilerplate(const Text('hello'))); - final Finder finder = find.byType(Text); - try { - await expectLater(finder, matchesSkiaGoldFile('foo.png')); - fail('TestFailure expected but not thrown'); - } on TestFailure catch (error) { - expect(comparator.invocation, _ComparatorInvocation.compare); - expect(error.message, contains('fake message')); - } - }); - - testWidgets('if finder finds no widgets', (WidgetTester tester) async { - await tester.pumpWidget(boilerplate(Container())); - final Finder finder = find.byType(Text); - try { - await expectLater(finder, matchesSkiaGoldFile('foo.png')); - fail('TestFailure expected but not thrown'); - } on TestFailure catch (error) { - expect(comparator.invocation, isNull); - expect(error.message, contains('no widget was found')); - } - }); - - testWidgets( - 'if finder finds multiple widgets', (WidgetTester tester) async { - await tester.pumpWidget(boilerplate(Column( - children: const [Text('hello'), Text('world')], - ))); - final Finder finder = find.byType(Text); - try { - await expectLater(finder, matchesSkiaGoldFile('foo.png')); - fail('TestFailure expected but not thrown'); - } on TestFailure catch (error) { - expect(comparator.invocation, isNull); - expect(error.message, contains('too many widgets')); - } - }); - }); - }); - group('matchesSemanticsData', () { testWidgets('matches SemanticsData', (WidgetTester tester) async { final SemanticsHandle handle = tester.ensureSemantics(); diff --git a/packages/flutter_tools/lib/src/commands/update_packages.dart b/packages/flutter_tools/lib/src/commands/update_packages.dart index e87734dceb9..2e33161e101 100644 --- a/packages/flutter_tools/lib/src/commands/update_packages.dart +++ b/packages/flutter_tools/lib/src/commands/update_packages.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'dart:collection'; import 'package:meta/meta.dart'; +import 'package:flutter_goldens_client/client.dart'; import '../base/common.dart'; import '../base/file_system.dart'; @@ -138,14 +139,13 @@ class UpdatePackagesCommand extends FlutterCommand { // The dev/integration_tests/android_views integration test depends on an assets // package that is in the goldens repository. We need to make sure that the goldens // repository is cloned locally before we verify or update pubspecs. - // TODO(katelovett): Resolve dependency for android_views living in goldens repository -// printStatus('Cloning goldens repository...'); -// try { -// final GoldensClient goldensClient = GoldensClient(); -// await goldensClient.prepare(); -// } on NonZeroExitCode catch (e) { -// throwToolExit(e.stderr, exitCode: e.exitCode); -// } + printStatus('Cloning goldens repository...'); + try { + final GoldensClient goldensClient = GoldensClient(); + await goldensClient.prepare(); + } on NonZeroExitCode catch (e) { + throwToolExit(e.stderr, exitCode: e.exitCode); + } if (isVerifyOnly) { bool needsUpdate = false;