Revert "Skia Gold Support for Local & PreSubmit Testing in package:flutter (#40710)" (#43227)
This reverts commit 8df0d6556dd5ce5931fe79c8b177d7930d188c1f.
diff --git a/bin/internal/goldens.version b/bin/internal/goldens.version
new file mode 100644
index 0000000..278ccf4
--- /dev/null
+++ b/bin/internal/goldens.version
@@ -0,0 +1 @@
+65bd07204149c4f7612bbf179cf088a2d69ca549
diff --git a/packages/flutter/test/cupertino/activity_indicator_test.dart b/packages/flutter/test/cupertino/activity_indicator_test.dart
index ae0cec2..cd529c9 100644
--- a/packages/flutter/test/cupertino/activity_indicator_test.dart
+++ b/packages/flutter/test/cupertino/activity_indicator_test.dart
@@ -45,7 +45,7 @@
await expectLater(
find.byKey(key),
- matchesGoldenFile('activityIndicator.paused.light.png'),
+ matchesGoldenFile('activityIndicator.paused.light.png', version: 0),
);
await tester.pumpWidget(
@@ -65,7 +65,7 @@
await expectLater(
find.byKey(key),
- matchesGoldenFile('activityIndicator.paused.dark.png'),
+ matchesGoldenFile('activityIndicator.paused.dark.png', version: 0),
);
});
diff --git a/packages/flutter/test/cupertino/date_picker_test.dart b/packages/flutter/test/cupertino/date_picker_test.dart
index b99a2aa..6403e13 100644
--- a/packages/flutter/test/cupertino/date_picker_test.dart
+++ b/packages/flutter/test/cupertino/date_picker_test.dart
@@ -922,7 +922,10 @@
await expectLater(
find.byType(CupertinoDatePicker),
- matchesGoldenFile('date_picker_test.datetime.initial.png'),
+ matchesGoldenFile(
+ 'date_picker_test.datetime.initial.png',
+ version: 2,
+ ),
);
// Slightly drag the hour component to make the current hour off-center.
@@ -931,7 +934,10 @@
await expectLater(
find.byType(CupertinoDatePicker),
- matchesGoldenFile('date_picker_test.datetime.drag.png'),
+ matchesGoldenFile(
+ 'date_picker_test.datetime.drag.png',
+ version: 2,
+ ),
);
});
});
@@ -965,7 +971,10 @@
await expectLater(
find.byType(CupertinoTimerPicker),
- matchesGoldenFile('timer_picker_test.datetime.initial.png'),
+ matchesGoldenFile(
+ 'timer_picker_test.datetime.initial.png',
+ version: 1,
+ ),
);
// Slightly drag the minute component to make the current minute off-center.
@@ -974,7 +983,10 @@
await expectLater(
find.byType(CupertinoTimerPicker),
- matchesGoldenFile('timer_picker_test.datetime.drag.png'),
+ matchesGoldenFile(
+ 'timer_picker_test.datetime.drag.png',
+ version: 1,
+ ),
);
});
diff --git a/packages/flutter/test/cupertino/nav_bar_test.dart b/packages/flutter/test/cupertino/nav_bar_test.dart
index c2b8148..66a87ec 100644
--- a/packages/flutter/test/cupertino/nav_bar_test.dart
+++ b/packages/flutter/test/cupertino/nav_bar_test.dart
@@ -820,7 +820,10 @@
await expectLater(
find.byType(RepaintBoundary).last,
- matchesGoldenFile('nav_bar_test.standard_title.png'),
+ matchesGoldenFile(
+ 'nav_bar_test.standard_title.png',
+ version: 2,
+ ),
);
},
);
@@ -851,7 +854,10 @@
await expectLater(
find.byType(RepaintBoundary).last,
- matchesGoldenFile('nav_bar_test.large_title.png'),
+ matchesGoldenFile(
+ 'nav_bar_test.large_title.png',
+ version: 2,
+ ),
);
},
);
diff --git a/packages/flutter/test/cupertino/segmented_control_test.dart b/packages/flutter/test/cupertino/segmented_control_test.dart
index cef95e3..1ad5878 100644
--- a/packages/flutter/test/cupertino/segmented_control_test.dart
+++ b/packages/flutter/test/cupertino/segmented_control_test.dart
@@ -1414,7 +1414,10 @@
await expectLater(
find.byType(RepaintBoundary),
- matchesGoldenFile('segmented_control_test.0.png'),
+ matchesGoldenFile(
+ 'segmented_control_test.0.png',
+ version: 0,
+ ),
);
});
@@ -1452,7 +1455,10 @@
await expectLater(
find.byType(RepaintBoundary),
- matchesGoldenFile('segmented_control_test.1.png'),
+ matchesGoldenFile(
+ 'segmented_control_test.1.png',
+ version: 0,
+ ),
);
});
}
diff --git a/packages/flutter/test/cupertino/switch_test.dart b/packages/flutter/test/cupertino/switch_test.dart
index 2075dcc..10d5d1f 100644
--- a/packages/flutter/test/cupertino/switch_test.dart
+++ b/packages/flutter/test/cupertino/switch_test.dart
@@ -542,7 +542,10 @@
await expectLater(
find.byKey(switchKey),
- matchesGoldenFile('switch.tap.off.png'),
+ matchesGoldenFile(
+ 'switch.tap.off.png',
+ version: 1,
+ ),
);
await tester.tap(find.byKey(switchKey));
@@ -553,13 +556,19 @@
await tester.pump(const Duration(milliseconds: 60));
await expectLater(
find.byKey(switchKey),
- matchesGoldenFile('switch.tap.turningOn.png'),
+ matchesGoldenFile(
+ 'switch.tap.turningOn.png',
+ version: 1,
+ ),
);
await tester.pumpAndSettle();
await expectLater(
find.byKey(switchKey),
- matchesGoldenFile('switch.tap.on.png'),
+ matchesGoldenFile(
+ 'switch.tap.on.png',
+ version: 1,
+ ),
);
});
@@ -595,7 +604,10 @@
await expectLater(
find.byKey(switchKey),
- matchesGoldenFile('switch.tap.off.dark.png'),
+ matchesGoldenFile(
+ 'switch.tap.off.dark.png',
+ version: 0,
+ ),
);
await tester.tap(find.byKey(switchKey));
@@ -604,7 +616,10 @@
await tester.pumpAndSettle();
await expectLater(
find.byKey(switchKey),
- matchesGoldenFile('switch.tap.on.dark.png'),
+ matchesGoldenFile(
+ 'switch.tap.on.dark.png',
+ version: 0,
+ ),
);
});
}
diff --git a/packages/flutter/test/cupertino/text_field_test.dart b/packages/flutter/test/cupertino/text_field_test.dart
index c932c69..17f40cf 100644
--- a/packages/flutter/test/cupertino/text_field_test.dart
+++ b/packages/flutter/test/cupertino/text_field_test.dart
@@ -503,7 +503,10 @@
await expectLater(
find.byKey(const ValueKey<int>(1)),
- matchesGoldenFile('text_field_cursor_test.cupertino.0.png'),
+ matchesGoldenFile(
+ 'text_field_cursor_test.cupertino.0.png',
+ version: 3,
+ ),
);
});
@@ -533,7 +536,10 @@
debugDefaultTargetPlatformOverride = null;
await expectLater(
find.byKey(const ValueKey<int>(1)),
- matchesGoldenFile('text_field_cursor_test.cupertino.1.png'),
+ matchesGoldenFile(
+ 'text_field_cursor_test.cupertino.1.png',
+ version: 3,
+ ),
);
});
@@ -3036,7 +3042,10 @@
await expectLater(
find.byKey(const ValueKey<int>(1)),
- matchesGoldenFile('text_field_test.disabled.png'),
+ matchesGoldenFile(
+ 'text_field_test.disabled.png',
+ version: 1,
+ ),
);
});
diff --git a/packages/flutter/test/material/bottom_app_bar_test.dart b/packages/flutter/test/material/bottom_app_bar_test.dart
index 6c9d69b..9725ab0 100644
--- a/packages/flutter/test/material/bottom_app_bar_test.dart
+++ b/packages/flutter/test/material/bottom_app_bar_test.dart
@@ -71,13 +71,19 @@
await pump(FloatingActionButtonLocation.endDocked);
await expectLater(
find.byKey(key),
- matchesGoldenFile('bottom_app_bar.custom_shape.1.png'),
+ matchesGoldenFile(
+ 'bottom_app_bar.custom_shape.1.png',
+ version: null,
+ ),
);
await pump(FloatingActionButtonLocation.centerDocked);
await tester.pumpAndSettle();
await expectLater(
find.byKey(key),
- matchesGoldenFile('bottom_app_bar.custom_shape.2.png'),
+ matchesGoldenFile(
+ 'bottom_app_bar.custom_shape.2.png',
+ version: null,
+ ),
);
}, skip: isBrowser);
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 60609e6..3b4cd47 100644
--- a/packages/flutter/test/material/bottom_app_bar_theme_test.dart
+++ b/packages/flutter/test/material/bottom_app_bar_theme_test.dart
@@ -80,7 +80,10 @@
await expectLater(
find.byKey(_painterKey),
- matchesGoldenFile('bottom_app_bar_theme.custom_shape.png'),
+ matchesGoldenFile(
+ 'bottom_app_bar_theme.custom_shape.png',
+ version: null,
+ ),
);
}, skip: isBrowser);
diff --git a/packages/flutter/test/material/bottom_navigation_bar_test.dart b/packages/flutter/test/material/bottom_navigation_bar_test.dart
index 65a9443..bf32b1f 100644
--- a/packages/flutter/test/material/bottom_navigation_bar_test.dart
+++ b/packages/flutter/test/material/bottom_navigation_bar_test.dart
@@ -1426,7 +1426,10 @@
await tester.pump(const Duration(milliseconds: 30));
await expectLater(
find.byType(BottomNavigationBar),
- matchesGoldenFile('bottom_navigation_bar.shifting_transition.$pump.png'),
+ matchesGoldenFile(
+ 'bottom_navigation_bar.shifting_transition.$pump.png',
+ version: 2,
+ ),
);
}
}, skip: isBrowser);
diff --git a/packages/flutter/test/material/card_theme_test.dart b/packages/flutter/test/material/card_theme_test.dart
index 49a4e97..abdf8cc 100644
--- a/packages/flutter/test/material/card_theme_test.dart
+++ b/packages/flutter/test/material/card_theme_test.dart
@@ -137,7 +137,10 @@
await expectLater(
find.byKey(painterKey),
- matchesGoldenFile('card_theme.custom_shape.png'),
+ matchesGoldenFile(
+ 'card_theme.custom_shape.png',
+ version: null,
+ ),
);
}, skip: isBrowser);
}
diff --git a/packages/flutter/test/material/dialog_theme_test.dart b/packages/flutter/test/material/dialog_theme_test.dart
index 834cefe..133ecc1 100644
--- a/packages/flutter/test/material/dialog_theme_test.dart
+++ b/packages/flutter/test/material/dialog_theme_test.dart
@@ -130,7 +130,10 @@
await expectLater(
find.byKey(_painterKey),
- matchesGoldenFile('dialog_theme.dialog_with_custom_border.png'),
+ matchesGoldenFile(
+ 'dialog_theme.dialog_with_custom_border.png',
+ version: null,
+ ),
);
}, skip: isBrowser);
diff --git a/packages/flutter/test/material/dropdown_test.dart b/packages/flutter/test/material/dropdown_test.dart
index fdbab11..99a403c 100644
--- a/packages/flutter/test/material/dropdown_test.dart
+++ b/packages/flutter/test/material/dropdown_test.dart
@@ -227,7 +227,10 @@
assert(tester.renderObject(buttonFinder).attached);
await expectLater(
find.ancestor(of: buttonFinder, matching: find.byType(RepaintBoundary)).first,
- matchesGoldenFile('dropdown_test.default.png'),
+ matchesGoldenFile(
+ 'dropdown_test.default.png',
+ version: 0,
+ ),
);
}, skip: isBrowser);
@@ -239,7 +242,10 @@
assert(tester.renderObject(buttonFinder).attached);
await expectLater(
find.ancestor(of: buttonFinder, matching: find.byType(RepaintBoundary)).first,
- matchesGoldenFile('dropdown_test.expanded.png'),
+ matchesGoldenFile(
+ 'dropdown_test.expanded.png',
+ version: 0,
+ ),
);
}, skip: isBrowser);
diff --git a/packages/flutter/test/material/floating_action_button_test.dart b/packages/flutter/test/material/floating_action_button_test.dart
index 992385a..65dd4bf 100644
--- a/packages/flutter/test/material/floating_action_button_test.dart
+++ b/packages/flutter/test/material/floating_action_button_test.dart
@@ -740,7 +740,10 @@
await tester.pump(const Duration(milliseconds: 1000));
await expectLater(
find.byKey(key),
- matchesGoldenFile('floating_action_button_test.clip.png'),
+ matchesGoldenFile(
+ 'floating_action_button_test.clip.png',
+ version: 2,
+ ),
);
});
diff --git a/packages/flutter/test/material/input_decorator_test.dart b/packages/flutter/test/material/input_decorator_test.dart
index 9d5e78b..bd13982 100644
--- a/packages/flutter/test/material/input_decorator_test.dart
+++ b/packages/flutter/test/material/input_decorator_test.dart
@@ -2960,13 +2960,19 @@
await tester.pumpWidget(buildFrame(TextDirection.ltr));
await expectLater(
find.byType(InputDecorator),
- matchesGoldenFile('input_decorator.outline_icon_label.ltr.png'),
+ matchesGoldenFile(
+ 'input_decorator.outline_icon_label.ltr.png',
+ version: null,
+ ),
);
await tester.pumpWidget(buildFrame(TextDirection.rtl));
await expectLater(
find.byType(InputDecorator),
- matchesGoldenFile('input_decorator.outline_icon_label.rtl.png'),
+ matchesGoldenFile(
+ 'input_decorator.outline_icon_label.rtl.png',
+ version: null,
+ ),
);
},
);
diff --git a/packages/flutter/test/material/material_test.dart b/packages/flutter/test/material/material_test.dart
index e51d167..dfcfde2 100644
--- a/packages/flutter/test/material/material_test.dart
+++ b/packages/flutter/test/material/material_test.dart
@@ -715,7 +715,10 @@
await expectLater(
find.byKey(painterKey),
- matchesGoldenFile('material.border_paint_above.png'),
+ matchesGoldenFile(
+ 'material.border_paint_above.png',
+ version: null,
+ ),
);
}, skip: isBrowser);
@@ -755,7 +758,10 @@
await expectLater(
find.byKey(painterKey),
- matchesGoldenFile('material.border_paint_below.png'),
+ matchesGoldenFile(
+ 'material.border_paint_below.png',
+ version: null,
+ ),
);
}, skip: isBrowser);
});
diff --git a/packages/flutter/test/material/radio_test.dart b/packages/flutter/test/material/radio_test.dart
index 3a1741f..bf817a5 100644
--- a/packages/flutter/test/material/radio_test.dart
+++ b/packages/flutter/test/material/radio_test.dart
@@ -276,7 +276,10 @@
await tester.pumpAndSettle();
await expectLater(
find.byKey(painterKey),
- matchesGoldenFile('radio.ink_ripple.png'),
+ matchesGoldenFile(
+ 'radio.ink_ripple.png',
+ version: null,
+ ),
);
}, skip: isBrowser);
}
diff --git a/packages/flutter/test/material/tab_bar_theme_test.dart b/packages/flutter/test/material/tab_bar_theme_test.dart
index b0b05d9..bb97c5d 100644
--- a/packages/flutter/test/material/tab_bar_theme_test.dart
+++ b/packages/flutter/test/material/tab_bar_theme_test.dart
@@ -267,7 +267,10 @@
await expectLater(
find.byKey(_painterKey),
- matchesGoldenFile('tab_bar_theme.tab_indicator_size_tab.png'),
+ matchesGoldenFile(
+ 'tab_bar_theme.tab_indicator_size_tab.png',
+ version: null,
+ ),
);
}, skip: isBrowser);
@@ -278,7 +281,10 @@
await expectLater(
find.byKey(_painterKey),
- matchesGoldenFile('tab_bar_theme.tab_indicator_size_label.png'),
+ matchesGoldenFile(
+ 'tab_bar_theme.tab_indicator_size_label.png',
+ version: null,
+ ),
);
}, skip: isBrowser);
@@ -294,7 +300,10 @@
await expectLater(
find.byKey(_painterKey),
- matchesGoldenFile('tab_bar_theme.custom_tab_indicator.png'),
+ matchesGoldenFile(
+ 'tab_bar_theme.custom_tab_indicator.png',
+ version: null,
+ ),
);
}, skip: isBrowser);
@@ -310,7 +319,10 @@
await expectLater(
find.byKey(_painterKey),
- matchesGoldenFile('tab_bar_theme.beveled_rect_indicator.png'),
+ matchesGoldenFile(
+ 'tab_bar_theme.beveled_rect_indicator.png',
+ version: null,
+ ),
);
}, skip: isBrowser);
}
diff --git a/packages/flutter/test/material/text_field_test.dart b/packages/flutter/test/material/text_field_test.dart
index 363b75b..7ef5724 100644
--- a/packages/flutter/test/material/text_field_test.dart
+++ b/packages/flutter/test/material/text_field_test.dart
@@ -412,7 +412,10 @@
await expectLater(
find.byKey(const ValueKey<int>(1)),
- matchesGoldenFile('text_field_cursor_test.material.0.png'),
+ matchesGoldenFile(
+ 'text_field_cursor_test.material.0.png',
+ version: 0,
+ ),
);
});
@@ -441,7 +444,10 @@
debugDefaultTargetPlatformOverride = null;
await expectLater(
find.byKey(const ValueKey<int>(1)),
- matchesGoldenFile('text_field_cursor_test.material.1.png'),
+ matchesGoldenFile(
+ 'text_field_cursor_test.material.1.png',
+ version: 0,
+ ),
);
});
@@ -492,7 +498,10 @@
await expectLater(
// The toolbar exists in the Overlay above the MaterialApp.
find.byType(Overlay),
- matchesGoldenFile('text_field_opacity_test.0.png'),
+ matchesGoldenFile(
+ 'text_field_opacity_test.0.png',
+ version: 3,
+ ),
);
}, skip: isBrowser);
diff --git a/packages/flutter/test/painting/continous_rectangle_border_test.dart b/packages/flutter/test/painting/continous_rectangle_border_test.dart
index 755df17..f644fb0 100644
--- a/packages/flutter/test/painting/continous_rectangle_border_test.dart
+++ b/packages/flutter/test/painting/continous_rectangle_border_test.dart
@@ -71,7 +71,10 @@
await expectLater(
find.byType(RepaintBoundary),
- matchesGoldenFile('continuous_rectangle_border.golden_test_even_radii.png'),
+ matchesGoldenFile(
+ 'continuous_rectangle_border.golden_test_even_radii.png',
+ version: null,
+ ),
);
}, skip: isBrowser);
@@ -92,7 +95,10 @@
await expectLater(
find.byType(RepaintBoundary),
- matchesGoldenFile('continuous_rectangle_border.golden_test_varying_radii.png'),
+ matchesGoldenFile(
+ 'continuous_rectangle_border.golden_test_varying_radii.png',
+ version: null,
+ ),
);
}, skip: isBrowser);
@@ -110,7 +116,10 @@
await expectLater(
find.byType(RepaintBoundary),
- matchesGoldenFile('continuous_rectangle_border.golden_test_large_radii.png'),
+ matchesGoldenFile(
+ 'continuous_rectangle_border.golden_test_large_radii.png',
+ version: null,
+ ),
);
}, skip: isBrowser);
diff --git a/packages/flutter/test/painting/gradient_test.dart b/packages/flutter/test/painting/gradient_test.dart
index 7fc653e..802919b 100644
--- a/packages/flutter/test/painting/gradient_test.dart
+++ b/packages/flutter/test/painting/gradient_test.dart
@@ -806,10 +806,7 @@
),
),
));
- await expectLater(
- find.byKey(painterKey),
- matchesGoldenFile(goldenName),
- );
+ await expectLater(find.byKey(painterKey), matchesGoldenFile(goldenName));
}
testWidgets('Gradients - 45 degrees', (WidgetTester tester) async {
diff --git a/packages/flutter/test/rendering/localized_fonts_test.dart b/packages/flutter/test/rendering/localized_fonts_test.dart
index f8a9438..9f07f79 100644
--- a/packages/flutter/test/rendering/localized_fonts_test.dart
+++ b/packages/flutter/test/rendering/localized_fonts_test.dart
@@ -49,7 +49,10 @@
await expectLater(
find.byType(RichText),
- matchesGoldenFile('localized_fonts.rich_text.styled_text_span.png'),
+ matchesGoldenFile(
+ 'localized_fonts.rich_text.styled_text_span.png',
+ version: null,
+ ),
);
},
skip: isBrowser, // TODO(yjbanov): implement goldens on the Web: https://github.com/flutter/flutter/issues/40297
@@ -101,7 +104,10 @@
await expectLater(
find.byType(Row),
- matchesGoldenFile('localized_fonts.text_ambient_locale.chars.png'),
+ matchesGoldenFile(
+ 'localized_fonts.text_ambient_locale.chars.png',
+ version: null,
+ ),
);
},
skip: isBrowser, // TODO(yjbanov): implement goldens on the Web: https://github.com/flutter/flutter/issues/40297
@@ -145,7 +151,10 @@
await expectLater(
find.byType(Row),
- matchesGoldenFile('localized_fonts.text_explicit_locale.chars.png'),
+ matchesGoldenFile(
+ 'localized_fonts.text_explicit_locale.chars.png',
+ version: null,
+ ),
);
},
skip: isBrowser, // TODO(yjbanov): implement goldens on the Web: https://github.com/flutter/flutter/issues/40297
diff --git a/packages/flutter/test/widgets/backdrop_filter_test.dart b/packages/flutter/test/widgets/backdrop_filter_test.dart
index 6275b88..81f2597 100644
--- a/packages/flutter/test/widgets/backdrop_filter_test.dart
+++ b/packages/flutter/test/widgets/backdrop_filter_test.dart
@@ -43,7 +43,10 @@
);
await expectLater(
find.byType(RepaintBoundary).first,
- matchesGoldenFile('backdrop_filter_test.cull_rect.png'),
+ matchesGoldenFile(
+ 'backdrop_filter_test.cull_rect.png',
+ version: 1,
+ ),
);
}, skip: isBrowser);
}
diff --git a/packages/flutter/test/widgets/clip_test.dart b/packages/flutter/test/widgets/clip_test.dart
index 24e013a..941f209 100644
--- a/packages/flutter/test/widgets/clip_test.dart
+++ b/packages/flutter/test/widgets/clip_test.dart
@@ -375,7 +375,10 @@
);
await expectLater(
find.byType(RepaintBoundary).first,
- matchesGoldenFile('clip.ClipRect.png'),
+ matchesGoldenFile(
+ 'clip.ClipRect.png',
+ version: 1,
+ ),
);
}, skip: isBrowser);
@@ -415,7 +418,10 @@
);
await expectLater(
find.byType(RepaintBoundary).first,
- matchesGoldenFile('clip.ClipRectOverlay.png'),
+ matchesGoldenFile(
+ 'clip.ClipRectOverlay.png',
+ version: 1,
+ ),
);
}, skip: isBrowser);
@@ -464,7 +470,10 @@
);
await expectLater(
find.byType(RepaintBoundary).first,
- matchesGoldenFile('clip.ClipRRect.png'),
+ matchesGoldenFile(
+ 'clip.ClipRRect.png',
+ version: 1,
+ ),
);
}, skip: isBrowser);
@@ -507,7 +516,10 @@
);
await expectLater(
find.byType(RepaintBoundary).first,
- matchesGoldenFile('clip.ClipOval.png'),
+ matchesGoldenFile(
+ 'clip.ClipOval.png',
+ version: 1,
+ ),
);
}, skip: isBrowser);
@@ -555,7 +567,10 @@
);
await expectLater(
find.byType(RepaintBoundary).first,
- matchesGoldenFile('clip.ClipPath.png'),
+ matchesGoldenFile(
+ 'clip.ClipPath.png',
+ version: 1,
+ ),
);
}, skip: isBrowser);
@@ -600,7 +615,10 @@
await tester.pumpWidget(genPhysicalModel(Clip.antiAlias));
await expectLater(
find.byType(RepaintBoundary).first,
- matchesGoldenFile('clip.PhysicalModel.antiAlias.png'),
+ matchesGoldenFile(
+ 'clip.PhysicalModel.antiAlias.png',
+ version: 1,
+ ),
);
}, skip: isBrowser);
@@ -608,7 +626,10 @@
await tester.pumpWidget(genPhysicalModel(Clip.hardEdge));
await expectLater(
find.byType(RepaintBoundary).first,
- matchesGoldenFile('clip.PhysicalModel.hardEdge.png'),
+ matchesGoldenFile(
+ 'clip.PhysicalModel.hardEdge.png',
+ version: 1,
+ ),
);
}, skip: isBrowser);
@@ -618,7 +639,10 @@
await tester.pumpWidget(genPhysicalModel(Clip.antiAliasWithSaveLayer));
await expectLater(
find.byType(RepaintBoundary).first,
- matchesGoldenFile('clip.PhysicalModel.antiAliasWithSaveLayer.png'),
+ matchesGoldenFile(
+ 'clip.PhysicalModel.antiAliasWithSaveLayer.png',
+ version: null,
+ ),
);
}, skip: isBrowser);
@@ -660,7 +684,10 @@
);
await expectLater(
find.byType(RepaintBoundary).first,
- matchesGoldenFile('clip.PhysicalModel.default.png'),
+ matchesGoldenFile(
+ 'clip.PhysicalModel.default.png',
+ version: 1,
+ ),
);
}, skip: isBrowser);
@@ -709,7 +736,10 @@
await tester.pumpWidget(genPhysicalShape(Clip.antiAlias));
await expectLater(
find.byType(RepaintBoundary).first,
- matchesGoldenFile('clip.PhysicalShape.antiAlias.png'),
+ matchesGoldenFile(
+ 'clip.PhysicalShape.antiAlias.png',
+ version: 1,
+ ),
);
}, skip: isBrowser);
@@ -717,7 +747,10 @@
await tester.pumpWidget(genPhysicalShape(Clip.hardEdge));
await expectLater(
find.byType(RepaintBoundary).first,
- matchesGoldenFile('clip.PhysicalShape.hardEdge.png'),
+ matchesGoldenFile(
+ 'clip.PhysicalShape.hardEdge.png',
+ version: 1,
+ ),
);
}, skip: isBrowser);
@@ -725,7 +758,10 @@
await tester.pumpWidget(genPhysicalShape(Clip.antiAliasWithSaveLayer));
await expectLater(
find.byType(RepaintBoundary).first,
- matchesGoldenFile('clip.PhysicalShape.antiAliasWithSaveLayer.png'),
+ matchesGoldenFile(
+ 'clip.PhysicalShape.antiAliasWithSaveLayer.png',
+ version: null,
+ ),
);
}, skip: isBrowser);
@@ -771,7 +807,10 @@
);
await expectLater(
find.byType(RepaintBoundary).first,
- matchesGoldenFile('clip.PhysicalShape.default.png'),
+ matchesGoldenFile(
+ 'clip.PhysicalShape.default.png',
+ version: 1,
+ ),
);
}, skip: isBrowser);
diff --git a/packages/flutter/test/widgets/color_filter_test.dart b/packages/flutter/test/widgets/color_filter_test.dart
index 0874bb6..7822bbe 100644
--- a/packages/flutter/test/widgets/color_filter_test.dart
+++ b/packages/flutter/test/widgets/color_filter_test.dart
@@ -18,7 +18,10 @@
);
await expectLater(
find.byType(ColorFiltered),
- matchesGoldenFile('color_filter_red.png'),
+ matchesGoldenFile(
+ 'color_filter_red.png',
+ version: 1,
+ ),
);
});
@@ -55,7 +58,10 @@
);
await expectLater(
find.byType(ColorFiltered),
- matchesGoldenFile('color_filter_sepia.png'),
+ matchesGoldenFile(
+ 'color_filter_sepia.png',
+ version: 1,
+ ),
);
});
diff --git a/packages/flutter/test/widgets/editable_text_cursor_test.dart b/packages/flutter/test/widgets/editable_text_cursor_test.dart
index 313a81c..e9fd27e 100644
--- a/packages/flutter/test/widgets/editable_text_cursor_test.dart
+++ b/packages/flutter/test/widgets/editable_text_cursor_test.dart
@@ -90,7 +90,10 @@
await expectLater(
find.byKey(const ValueKey<int>(1)),
- matchesGoldenFile('editable_text_test.0.png'),
+ matchesGoldenFile(
+ 'editable_text_test.0.png',
+ version: 3,
+ ),
);
});
@@ -141,7 +144,10 @@
await expectLater(
find.byKey(const ValueKey<int>(1)),
- matchesGoldenFile('editable_text_test.1.png'),
+ matchesGoldenFile(
+ 'editable_text_test.1.png',
+ version: 3,
+ ),
);
});
@@ -791,7 +797,10 @@
await expectLater(
find.byKey(const ValueKey<int>(1)),
- matchesGoldenFile('editable_text_test.2.png'),
+ matchesGoldenFile(
+ 'editable_text_test.2.png',
+ version: 0,
+ ),
);
debugDefaultTargetPlatformOverride = null;
});
diff --git a/packages/flutter/test/widgets/invert_colors_test.dart b/packages/flutter/test/widgets/invert_colors_test.dart
index d07fc1c..8489f6e 100644
--- a/packages/flutter/test/widgets/invert_colors_test.dart
+++ b/packages/flutter/test/widgets/invert_colors_test.dart
@@ -20,7 +20,10 @@
await expectLater(
find.byType(RepaintBoundary),
- matchesGoldenFile('invert_colors_test.0.png'),
+ matchesGoldenFile(
+ 'invert_colors_test.0.png',
+ version: null,
+ ),
);
}, skip: isBrowser);
@@ -38,7 +41,10 @@
await expectLater(
find.byType(RepaintBoundary),
- matchesGoldenFile('invert_colors_test.1.png'),
+ matchesGoldenFile(
+ 'invert_colors_test.1.png',
+ version: null,
+ ),
);
}, skip: isBrowser);
}
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 4585376..380cb4b 100644
--- a/packages/flutter/test/widgets/list_wheel_scroll_view_test.dart
+++ b/packages/flutter/test/widgets/list_wheel_scroll_view_test.dart
@@ -535,7 +535,10 @@
await expectLater(
find.byKey(const Key('list_wheel_scroll_view')),
- matchesGoldenFile('list_wheel_scroll_view.center_child.magnified.png'),
+ matchesGoldenFile(
+ 'list_wheel_scroll_view.center_child.magnified.png',
+ version: null,
+ ),
);
}, skip: isBrowser);
@@ -589,7 +592,10 @@
await expectLater(
find.byKey(const Key('list_wheel_scroll_view')),
- matchesGoldenFile('list_wheel_scroll_view.curved_wheel.left.png'),
+ matchesGoldenFile(
+ 'list_wheel_scroll_view.curved_wheel.left.png',
+ version: null,
+ ),
);
}, skip: isBrowser);
diff --git a/packages/flutter/test/widgets/opacity_test.dart b/packages/flutter/test/widgets/opacity_test.dart
index f081468..c6aed6f 100644
--- a/packages/flutter/test/widgets/opacity_test.dart
+++ b/packages/flutter/test/widgets/opacity_test.dart
@@ -178,7 +178,10 @@
);
await expectLater(
find.byType(RepaintBoundary).first,
- matchesGoldenFile('opacity_test.offset.png'),
+ matchesGoldenFile(
+ 'opacity_test.offset.png',
+ version: 1,
+ ),
);
}, skip: isBrowser);
diff --git a/packages/flutter/test/widgets/physical_model_test.dart b/packages/flutter/test/widgets/physical_model_test.dart
index d9517bc..c1ae8db 100644
--- a/packages/flutter/test/widgets/physical_model_test.dart
+++ b/packages/flutter/test/widgets/physical_model_test.dart
@@ -110,7 +110,10 @@
expect(exception.diagnostics.first.toString(), startsWith('A RenderFlex overflowed by '));
await expectLater(
find.byKey(key),
- matchesGoldenFile('physical_model_overflow.png'),
+ matchesGoldenFile(
+ 'physical_model_overflow.png',
+ version: null,
+ ),
);
}, skip: isBrowser);
diff --git a/packages/flutter/test/widgets/shadow_test.dart b/packages/flutter/test/widgets/shadow_test.dart
index bdd8e5e..f573974 100644
--- a/packages/flutter/test/widgets/shadow_test.dart
+++ b/packages/flutter/test/widgets/shadow_test.dart
@@ -23,14 +23,20 @@
);
await expectLater(
find.byType(Container),
- matchesGoldenFile('shadow.BoxDecoration.disabled.png'),
+ matchesGoldenFile(
+ 'shadow.BoxDecoration.disabled.png',
+ version: null,
+ ),
);
debugDisableShadows = false;
tester.binding.reassembleApplication();
await tester.pump();
await expectLater(
find.byType(Container),
- matchesGoldenFile('shadow.BoxDecoration.enabled.png'),
+ matchesGoldenFile(
+ 'shadow.BoxDecoration.enabled.png',
+ version: null,
+ ),
);
debugDisableShadows = true;
}, skip: isBrowser);
@@ -56,7 +62,10 @@
await tester.pumpWidget(build(elevation));
await expectLater(
find.byType(Container),
- matchesGoldenFile('shadow.ShapeDecoration.$elevation.png'),
+ matchesGoldenFile(
+ 'shadow.ShapeDecoration.$elevation.png',
+ version: null,
+ ),
);
}
debugDisableShadows = true;
@@ -83,14 +92,20 @@
);
await expectLater(
find.byType(Container),
- matchesGoldenFile('shadow.PhysicalModel.disabled.png'),
+ matchesGoldenFile(
+ 'shadow.PhysicalModel.disabled.png',
+ version: null,
+ ),
);
debugDisableShadows = false;
tester.binding.reassembleApplication();
await tester.pump();
await expectLater(
find.byType(Container),
- matchesGoldenFile('shadow.PhysicalModel.enabled.png'),
+ matchesGoldenFile(
+ 'shadow.PhysicalModel.enabled.png',
+ version: null,
+ ),
);
debugDisableShadows = true;
}, skip: isBrowser);
@@ -120,7 +135,10 @@
await tester.pumpWidget(build(elevation.toDouble()));
await expectLater(
find.byType(Container),
- matchesGoldenFile('shadow.PhysicalShape.$elevation.png'),
+ matchesGoldenFile(
+ 'shadow.PhysicalShape.$elevation.png',
+ version: 1,
+ ),
);
}
debugDisableShadows = true;
diff --git a/packages/flutter/test/widgets/text_golden_test.dart b/packages/flutter/test/widgets/text_golden_test.dart
index 24a1891..f4a464b 100644
--- a/packages/flutter/test/widgets/text_golden_test.dart
+++ b/packages/flutter/test/widgets/text_golden_test.dart
@@ -30,7 +30,10 @@
await expectLater(
find.byType(Container),
- matchesGoldenFile('text_golden.Centered.png'),
+ matchesGoldenFile(
+ 'text_golden.Centered.png',
+ version: null,
+ ),
);
await tester.pumpWidget(
@@ -54,7 +57,10 @@
await expectLater(
find.byType(Container),
- matchesGoldenFile('text_golden.Centered.wrap.png'),
+ matchesGoldenFile(
+ 'text_golden.Centered.wrap.png',
+ version: null,
+ ),
);
});
@@ -85,7 +91,10 @@
await expectLater(
find.byType(RepaintBoundary),
- matchesGoldenFile('text_golden.Foreground.gradient.png'),
+ matchesGoldenFile(
+ 'text_golden.Foreground.gradient.png',
+ version: null,
+ ),
);
await tester.pumpWidget(
@@ -107,7 +116,10 @@
await expectLater(
find.byType(RepaintBoundary),
- matchesGoldenFile('text_golden.Foreground.stroke.png'),
+ matchesGoldenFile(
+ 'text_golden.Foreground.stroke.png',
+ version: null,
+ ),
);
await tester.pumpWidget(
@@ -130,7 +142,10 @@
await expectLater(
find.byType(RepaintBoundary),
- matchesGoldenFile('text_golden.Foreground.stroke_and_gradient.png'),
+ matchesGoldenFile(
+ 'text_golden.Foreground.stroke_and_gradient.png',
+ version: null,
+ ),
);
});
@@ -180,7 +195,10 @@
await expectLater(
find.byType(RepaintBoundary),
- matchesGoldenFile('text_golden.Background.png'),
+ matchesGoldenFile(
+ 'text_golden.Background.png',
+ version: null,
+ ),
);
});
@@ -216,7 +234,10 @@
await expectLater(
find.byType(RepaintBoundary).first,
- matchesGoldenFile('text_golden.Fade.png'),
+ matchesGoldenFile(
+ 'text_golden.Fade.png',
+ version: 1,
+ ),
);
});
@@ -241,7 +262,10 @@
);
await expectLater(
find.byType(Container),
- matchesGoldenFile('text_golden.StrutDefault.png'),
+ matchesGoldenFile(
+ 'text_golden.StrutDefault.png',
+ version: null,
+ ),
);
});
@@ -268,7 +292,10 @@
);
await expectLater(
find.byType(Container),
- matchesGoldenFile('text_golden.Strut.1.png'),
+ matchesGoldenFile(
+ 'text_golden.Strut.1.png',
+ version: 1,
+ ),
);
});
@@ -296,7 +323,10 @@
);
await expectLater(
find.byType(Container),
- matchesGoldenFile('text_golden.Strut.2.png'),
+ matchesGoldenFile(
+ 'text_golden.Strut.2.png',
+ version: 1,
+ ),
);
});
@@ -347,7 +377,10 @@
);
await expectLater(
find.byType(Container),
- matchesGoldenFile('text_golden.Strut.3.png'),
+ matchesGoldenFile(
+ 'text_golden.Strut.3.png',
+ version: 1,
+ ),
);
});
@@ -382,7 +415,10 @@
);
await expectLater(
find.byType(Container),
- matchesGoldenFile('text_golden.Strut.4.png'),
+ matchesGoldenFile(
+ 'text_golden.Strut.4.png',
+ version: 1,
+ ),
);
});
@@ -433,7 +469,10 @@
);
await expectLater(
find.byType(Container),
- matchesGoldenFile('text_golden.StrutForce.1.png'),
+ matchesGoldenFile(
+ 'text_golden.StrutForce.1.png',
+ version: 1,
+ ),
);
});
@@ -471,7 +510,10 @@
);
await expectLater(
find.byType(Container),
- matchesGoldenFile('text_golden.Decoration.1.png'),
+ matchesGoldenFile(
+ 'text_golden.Decoration.1.png',
+ version: 0,
+ ),
);
});
@@ -510,7 +552,10 @@
);
await expectLater(
find.byType(Container),
- matchesGoldenFile('text_golden.DecorationThickness.1.png'),
+ matchesGoldenFile(
+ 'text_golden.DecorationThickness.1.png',
+ version: 1,
+ ),
);
});
@@ -604,7 +649,10 @@
);
await expectLater(
find.byType(Container),
- matchesGoldenFile('text_golden.TextInlineWidget.1.png'),
+ matchesGoldenFile(
+ 'text_golden.TextInlineWidget.1.png',
+ version: 1,
+ ),
);
});
@@ -649,7 +697,10 @@
);
await expectLater(
find.byType(Container),
- matchesGoldenFile('text_golden.TextInlineWidget.2.png'),
+ matchesGoldenFile(
+ 'text_golden.TextInlineWidget.2.png',
+ version: 2,
+ ),
);
});
@@ -778,7 +829,10 @@
);
await expectLater(
find.byType(Container),
- matchesGoldenFile('text_golden.TextInlineWidgetNest.1.png'),
+ matchesGoldenFile(
+ 'text_golden.TextInlineWidgetNest.1.png',
+ version: 3,
+ ),
);
});
@@ -885,7 +939,10 @@
);
await expectLater(
find.byType(Container),
- matchesGoldenFile('text_golden.TextInlineWidgetBaseline.1.png'),
+ matchesGoldenFile(
+ 'text_golden.TextInlineWidgetBaseline.1.png',
+ version: 1,
+ ),
);
});
@@ -992,7 +1049,10 @@
);
await expectLater(
find.byType(Container),
- matchesGoldenFile('text_golden.TextInlineWidgetAboveBaseline.1.png'),
+ matchesGoldenFile(
+ 'text_golden.TextInlineWidgetAboveBaseline.1.png',
+ version: 1,
+ ),
);
});
@@ -1099,7 +1159,10 @@
);
await expectLater(
find.byType(Container),
- matchesGoldenFile('text_golden.TextInlineWidgetBelowBaseline.1.png'),
+ matchesGoldenFile(
+ 'text_golden.TextInlineWidgetBelowBaseline.1.png',
+ version: 1,
+ ),
);
});
@@ -1206,7 +1269,10 @@
);
await expectLater(
find.byType(Container),
- matchesGoldenFile('text_golden.TextInlineWidgetTop.1.png'),
+ matchesGoldenFile(
+ 'text_golden.TextInlineWidgetTop.1.png',
+ version: 1,
+ ),
);
});
@@ -1313,7 +1379,10 @@
);
await expectLater(
find.byType(Container),
- matchesGoldenFile('text_golden.TextInlineWidgetMiddle.1.png'),
+ matchesGoldenFile(
+ 'text_golden.TextInlineWidgetMiddle.1.png',
+ version: 1,
+ ),
);
});
}
diff --git a/packages/flutter/test/widgets/widget_inspector_test.dart b/packages/flutter/test/widgets/widget_inspector_test.dart
index e10f11a..a965473 100644
--- a/packages/flutter/test/widgets/widget_inspector_test.dart
+++ b/packages/flutter/test/widgets/widget_inspector_test.dart
@@ -2039,7 +2039,10 @@
expect(expectedChildLayerCount, equals(2));
await expectLater(
layer.toImage(renderObject.semanticBounds.inflate(50.0)),
- matchesGoldenFile('inspector.repaint_boundary_margin.png'),
+ matchesGoldenFile(
+ 'inspector.repaint_boundary_margin.png',
+ version: null,
+ ),
);
// Regression test for how rendering with a pixel scale other than 1.0
@@ -2049,7 +2052,10 @@
renderObject.semanticBounds.inflate(50.0),
pixelRatio: 0.5,
),
- matchesGoldenFile('inspector.repaint_boundary_margin_small.png'),
+ matchesGoldenFile(
+ 'inspector.repaint_boundary_margin_small.png',
+ version: null,
+ ),
);
await expectLater(
@@ -2057,7 +2063,10 @@
renderObject.semanticBounds.inflate(50.0),
pixelRatio: 2.0,
),
- matchesGoldenFile('inspector.repaint_boundary_margin_large.png'),
+ matchesGoldenFile(
+ 'inspector.repaint_boundary_margin_large.png',
+ version: null,
+ ),
);
final Layer layerParent = layer.parent;
@@ -2072,7 +2081,10 @@
width: 300.0,
height: 300.0,
),
- matchesGoldenFile('inspector.repaint_boundary.png'),
+ matchesGoldenFile(
+ 'inspector.repaint_boundary.png',
+ version: null,
+ ),
);
// Verify that taking a screenshot didn't change the layers associated with
@@ -2089,7 +2101,10 @@
height: 500.0,
margin: 50.0,
),
- matchesGoldenFile('inspector.repaint_boundary_margin.png'),
+ matchesGoldenFile(
+ 'inspector.repaint_boundary_margin.png',
+ version: null,
+ ),
);
// Verify that taking a screenshot didn't change the layers associated with
@@ -2109,7 +2124,10 @@
height: 300.0,
debugPaint: true,
),
- matchesGoldenFile('inspector.repaint_boundary_debugPaint.png'),
+ matchesGoldenFile(
+ 'inspector.repaint_boundary_debugPaint.png',
+ version: null,
+ ),
);
// Verify that taking a screenshot with debug paint on did not change
// the number of children the layer has.
@@ -2119,7 +2137,10 @@
// hasn't changed the regular render of the widget.
await expectLater(
find.byType(RepaintBoundaryWithDebugPaint),
- matchesGoldenFile('inspector.repaint_boundary.png'),
+ matchesGoldenFile(
+ 'inspector.repaint_boundary.png',
+ version: null,
+ ),
);
expect(renderObject.debugLayer, equals(layer));
@@ -2132,7 +2153,10 @@
width: 100.0,
height: 100.0,
),
- matchesGoldenFile('inspector.container.png'),
+ matchesGoldenFile(
+ 'inspector.container.png',
+ version: null,
+ ),
);
await expectLater(
@@ -2142,7 +2166,10 @@
height: 100.0,
debugPaint: true,
),
- matchesGoldenFile('inspector.container_debugPaint.png'),
+ matchesGoldenFile(
+ 'inspector.container_debugPaint.png',
+ version: null,
+ ),
);
{
@@ -2162,7 +2189,10 @@
height: 100.0,
debugPaint: true,
),
- matchesGoldenFile('inspector.container_debugPaint.png'),
+ matchesGoldenFile(
+ 'inspector.container_debugPaint.png',
+ version: null,
+ ),
);
expect(container.debugNeedsLayout, isFalse);
}
@@ -2174,7 +2204,10 @@
width: 50.0,
height: 100.0,
),
- matchesGoldenFile('inspector.container_small.png'),
+ matchesGoldenFile(
+ 'inspector.container_small.png',
+ version: null,
+ ),
);
await expectLater(
@@ -2184,7 +2217,10 @@
height: 400.0,
maxPixelRatio: 3.0,
),
- matchesGoldenFile('inspector.container_large.png'),
+ matchesGoldenFile(
+ 'inspector.container_large.png',
+ version: null,
+ ),
);
// This screenshot will show the clip rect debug paint but no other
@@ -2196,7 +2232,10 @@
height: 100.0,
debugPaint: true,
),
- matchesGoldenFile('inspector.clipRect_debugPaint.png'),
+ matchesGoldenFile(
+ 'inspector.clipRect_debugPaint.png',
+ version: null,
+ ),
);
final Element clipRect = find.byType(ClipRRect).evaluate().single;
@@ -2212,7 +2251,10 @@
// This golden image is platform dependent due to the clip icon.
await expectLater(
clipRectScreenshot,
- matchesGoldenFile('inspector.clipRect_debugPaint_margin.png'),
+ matchesGoldenFile(
+ 'inspector.clipRect_debugPaint_margin.png',
+ version: null,
+ ),
);
// Verify we get the same image if we go through the service extension
@@ -2251,7 +2293,10 @@
height: 300.0,
debugPaint: true,
),
- matchesGoldenFile('inspector.padding_debugPaint.png'),
+ matchesGoldenFile(
+ 'inspector.padding_debugPaint.png',
+ version: null,
+ ),
);
// The bounds for this box crop its rendered content.
@@ -2262,7 +2307,10 @@
height: 300.0,
debugPaint: true,
),
- matchesGoldenFile('inspector.sizedBox_debugPaint.png'),
+ matchesGoldenFile(
+ 'inspector.sizedBox_debugPaint.png',
+ version: 1,
+ ),
);
// Verify that setting a margin includes the previously cropped content.
@@ -2274,7 +2322,10 @@
margin: 50.0,
debugPaint: true,
),
- matchesGoldenFile('inspector.sizedBox_debugPaint_margin.png'),
+ matchesGoldenFile(
+ 'inspector.sizedBox_debugPaint_margin.png',
+ version: null,
+ ),
);
}, skip: isBrowser);
@@ -2410,7 +2461,10 @@
await expectLater(
find.byKey(mainStackKey),
- matchesGoldenFile('inspector.composited_transform.only_offsets.png'),
+ matchesGoldenFile(
+ 'inspector.composited_transform.only_offsets.png',
+ version: null,
+ ),
);
await expectLater(
@@ -2419,12 +2473,18 @@
width: 5000.0,
height: 500.0,
),
- matchesGoldenFile('inspector.composited_transform.only_offsets_follower.png'),
+ matchesGoldenFile(
+ 'inspector.composited_transform.only_offsets_follower.png',
+ version: null,
+ ),
);
await expectLater(
WidgetInspectorService.instance.screenshot(find.byType(Stack).evaluate().first, width: 300.0, height: 300.0),
- matchesGoldenFile('inspector.composited_transform.only_offsets_small.png'),
+ matchesGoldenFile(
+ 'inspector.composited_transform.only_offsets_small.png',
+ version: 1,
+ ),
);
await expectLater(
@@ -2433,7 +2493,10 @@
width: 500.0,
height: 500.0,
),
- matchesGoldenFile('inspector.composited_transform.only_offsets_target.png'),
+ matchesGoldenFile(
+ 'inspector.composited_transform.only_offsets_target.png',
+ version: null,
+ ),
);
}, skip: isBrowser);
@@ -2505,7 +2568,10 @@
// screenshots of specific subtrees are reasonable.
await expectLater(
find.byKey(mainStackKey),
- matchesGoldenFile('inspector.composited_transform.with_rotations.png'),
+ matchesGoldenFile(
+ 'inspector.composited_transform.with_rotations.png',
+ version: null,
+ ),
);
await expectLater(
@@ -2514,7 +2580,10 @@
width: 500.0,
height: 500.0,
),
- matchesGoldenFile('inspector.composited_transform.with_rotations_small.png'),
+ matchesGoldenFile(
+ 'inspector.composited_transform.with_rotations_small.png',
+ version: null,
+ ),
);
await expectLater(
@@ -2523,7 +2592,10 @@
width: 500.0,
height: 500.0,
),
- matchesGoldenFile('inspector.composited_transform.with_rotations_target.png'),
+ matchesGoldenFile(
+ 'inspector.composited_transform.with_rotations_target.png',
+ version: null,
+ ),
);
await expectLater(
@@ -2532,7 +2604,10 @@
width: 500.0,
height: 500.0,
),
- matchesGoldenFile('inspector.composited_transform.with_rotations_follower.png'),
+ matchesGoldenFile(
+ 'inspector.composited_transform.with_rotations_follower.png',
+ version: null,
+ ),
);
// 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 7a88a00..0804836 100644
--- a/packages/flutter_goldens/lib/flutter_goldens.dart
+++ b/packages/flutter_goldens/lib/flutter_goldens.dart
@@ -11,93 +11,49 @@
import 'package:meta/meta.dart';
import 'package:platform/platform.dart';
+import 'package:flutter_goldens_client/client.dart';
import 'package:flutter_goldens_client/skia_client.dart';
+
+export 'package:flutter_goldens_client/client.dart';
export 'package:flutter_goldens_client/skia_client.dart';
-// If you are here trying to figure out how to use golden files in the Flutter
-// repo itself, consider reading this wiki page:
-// https://github.com/flutter/flutter/wiki/Writing-a-golden-file-test-for-package%3Aflutter
-
-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. _Which_ FlutterGoldenFileComparator is
/// instantiated is based on the current testing environment.
Future<void> main(FutureOr<void> testMain()) async {
const Platform platform = LocalPlatform();
- if (FlutterSkiaGoldFileComparator.isAvailableForEnvironment(platform)) {
- goldenFileComparator = await FlutterSkiaGoldFileComparator.fromDefaultComparator(platform);
- } else if (FlutterPreSubmitFileComparator.isAvailableForEnvironment(platform)) {
- goldenFileComparator = await FlutterPreSubmitFileComparator.fromDefaultComparator(platform);
+ if (FlutterSkiaGoldFileComparator.isAvailableOnPlatform(platform)) {
+ goldenFileComparator = await FlutterSkiaGoldFileComparator.fromDefaultComparator();
+ } else if (FlutterGoldensRepositoryFileComparator.isAvailableOnPlatform(platform)) {
+ goldenFileComparator = await FlutterGoldensRepositoryFileComparator.fromDefaultComparator();
} else {
- goldenFileComparator = await FlutterLocalFileComparator.fromDefaultComparator(platform);
+ goldenFileComparator = FlutterSkippingGoldenFileComparator.fromDefaultComparator();
}
await testMain();
}
/// Abstract base class golden file comparator specific to the `flutter/flutter`
/// repository.
-///
-/// Golden file testing for the `flutter/flutter` repository is handled by three
-/// different [FlutterGoldenFileComparator]s, depending on the current testing
-/// environment.
-///
-/// * The [FlutterSkiaGoldFileComparator] is utilized during post-submit
-/// testing, after a pull request has landed on the master branch. This
-/// comparator uses the [SkiaGoldClient] and the `goldctl` tool to upload
-/// tests to the [Flutter Gold dashboard](https://flutter-gold.skia.org).
-/// Flutter Gold manages the master golden files for the `flutter/flutter`
-/// repository.
-///
-/// * The [FlutterPreSubmitFileComparator] is utilized in pre-submit testing,
-/// before a pull request can land on the master branch. This comparator
-/// uses the [SkiaGoldClient] to request the baseline images kept by the
-/// [Flutter Gold dashboard](https://flutter-gold.skia.org). It then
-/// compares the current test image to the baseline images using the
-/// standard [GoldenFileComparator.compareLists] to detect any pixel
-/// difference. The [SkiaGoldClient] is also used here to check the active
-/// ignores from the dashboard, in order to allow intended changes to pass
-/// tests.
-///
-/// * The [FlutterLocalFileComparator] is used for any other tests run outside
-/// of the above conditions. Similar to the
-/// [FlutterPreSubmitFileComparator], this comparator will use the
-/// [SkiaGoldClient] to request baseline images from
-/// [Flutter Gold](https://flutter-gold.skia.org) and compares for the
-/// current test image. If a difference is detected, this comparator will
-/// generate failure output illustrating the found difference. If a baseline
-/// is not found for a given test image, it will consider it a new test and
-/// output the new image for verification.
abstract class FlutterGoldenFileComparator extends GoldenFileComparator {
/// Creates a [FlutterGoldenFileComparator] that will resolve golden file
- /// URIs relative to the specified [basedir], and retrieve golden baselines
- /// using the [skiaClient]. The [basedir] is used for writing and accessing
- /// information and files for interacting with the [skiaClient]. When testing
- /// locally, the [basedir] will also contain any diffs from failed tests, or
- /// goldens generated from newly introduced tests.
+ /// URIs relative to the specified [basedir].
///
- /// The [fs] and [platform] parameters are useful in tests, where the default
- /// file system and platform can be replaced by mock instances.
+ /// The [fs] and [platform] parameters useful in tests, where the default file
+ /// system and platform can be replaced by mock instances.
@visibleForTesting
FlutterGoldenFileComparator(
- this.basedir,
- this.skiaClient, {
+ this.basedir, {
this.fs = const LocalFileSystem(),
this.platform = const LocalPlatform(),
}) : assert(basedir != null),
- assert(skiaClient != null),
assert(fs != null),
assert(platform != null);
/// The directory to which golden file URIs will be resolved in [compare] and
- /// [update], cannot be null.
+ /// [update].
final Uri basedir;
- /// A client for uploading image tests and making baseline requests to the
- /// Flutter Gold Dashboard, cannot be null.
- final SkiaGoldClient skiaClient;
-
/// The file system used to perform file access.
@visibleForTesting
final FileSystem fs;
@@ -113,44 +69,97 @@
await goldenFile.writeAsBytes(imageBytes, flush: true);
}
- @override
- Uri getTestUri(Uri key, int version) => key;
-
/// Calculate the appropriate basedir for the current test context.
@protected
@visibleForTesting
- static Directory getBaseDirectory(LocalFileComparator defaultComparator, Platform platform) {
- const FileSystem fs = LocalFileSystem();
- final Directory flutterRoot = fs.directory(platform.environment[_kFlutterRootKey]);
- final Directory comparisonRoot = flutterRoot.childDirectory(
- fs.path.join(
- 'bin',
- 'cache',
- 'pkg',
- 'skia_goldens',
- )
- );
+ static Directory getBaseDirectory(GoldensClient goldens, LocalFileComparator defaultComparator) {
+ final FileSystem fs = goldens.fs;
final Directory testDirectory = fs.directory(defaultComparator.basedir);
- final String testDirectoryRelativePath = fs.path.relative(
- testDirectory.path,
- from: flutterRoot.path,
- );
- return comparisonRoot.childDirectory(testDirectoryRelativePath);
+ final String testDirectoryRelativePath = fs.path.relative(testDirectory.path, from: goldens.flutterRoot.path);
+ return goldens.comparisonRoot.childDirectory(testDirectoryRelativePath);
}
/// Returns the golden [File] identified by the given [Uri].
@protected
File getGoldenFile(Uri uri) {
+ assert(basedir.scheme == 'file');
final File goldenFile = fs.directory(basedir).childFile(fs.file(uri).path);
+ assert(goldenFile.uri.scheme == 'file');
return goldenFile;
}
+}
- /// Prepends the golden Uri with the library name that encloses the current
- /// test.
- Uri _addPrefix(Uri golden) {
- final String prefix = basedir.pathSegments[basedir.pathSegments.length - 2];
- return Uri.parse(prefix + '.' + golden.toString());
+/// A [FlutterGoldenFileComparator] for testing golden images against the
+/// `flutter/goldens` repository.
+///
+/// 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 retrieves the golden
+/// files from a sibling repository, `flutter/goldens`.
+///
+/// This comparator will locally clone the `flutter/goldens` repository into
+/// the `$FLUTTER_ROOT/bin/cache/pkg/goldens` folder using the
+/// [GoldensRepositoryClient], then perform the comparison against the files
+/// therein.
+///
+/// See also:
+///
+/// * [GoldenFileComparator], the abstract class that
+/// [FlutterGoldenFileComparator] implements.
+/// * [FlutterSkiaGoldFileComparator], another [FlutterGoldenFileComparator]
+/// that tests golden images through Skia Gold.
+class FlutterGoldensRepositoryFileComparator extends FlutterGoldenFileComparator {
+ /// Creates a [FlutterGoldensRepositoryFileComparator] that will test golden
+ /// file images against the `flutter/goldens` repository.
+ ///
+ /// The [fs] and [platform] parameters useful in tests, where the default file
+ /// system and platform can be replaced by mock instances.
+ FlutterGoldensRepositoryFileComparator(
+ Uri basedir, {
+ FileSystem fs = const LocalFileSystem(),
+ Platform platform = const LocalPlatform(),
+ }) : super(
+ basedir,
+ fs: fs,
+ platform: platform,
+ );
+
+ /// Creates a new [FlutterGoldensRespositoryFileComparator] that mirrors the
+ /// relative path resolution of the default [goldenFileComparator].
+ ///
+ /// By the time the future completes, the clone of the `flutter/goldens`
+ /// repository is guaranteed to be ready to use.
+ ///
+ /// The [goldens] and [defaultComparator] parameters are visible for testing
+ /// purposes only.
+ static Future<FlutterGoldensRepositoryFileComparator> fromDefaultComparator({
+ GoldensRepositoryClient goldens,
+ LocalFileComparator defaultComparator,
+ }) async {
+ defaultComparator ??= goldenFileComparator;
+
+ // Prepare the goldens repo.
+ goldens ??= GoldensRepositoryClient();
+ await goldens.prepare();
+
+ final Directory baseDirectory = FlutterGoldenFileComparator.getBaseDirectory(goldens, defaultComparator);
+ return FlutterGoldensRepositoryFileComparator(baseDirectory.uri);
}
+
+ @override
+ Future<bool> compare(Uint8List imageBytes, Uri golden) async {
+ final File goldenFile = getGoldenFile(golden);
+ if (!goldenFile.existsSync()) {
+ throw TestFailure('Could not be compared against non-existent file: "$golden"');
+ }
+ final List<int> goldenBytes = await goldenFile.readAsBytes();
+ final ComparisonResult result = GoldenFileComparator.compareLists(imageBytes, goldenBytes);
+ return result.passed;
+ }
+
+ /// Decides based on the current platform whether goldens tests should be
+ /// performed against the flutter/goldens repository.
+ static bool isAvailableOnPlatform(Platform platform) => platform.isLinux;
}
/// A [FlutterGoldenFileComparator] for testing golden images with Skia Gold.
@@ -164,53 +173,44 @@
///
/// * [GoldenFileComparator], the abstract class that
/// [FlutterGoldenFileComparator] implements.
-/// * [FlutterPreSubmitFileComparator], another
-/// [FlutterGoldenFileComparator] that tests golden images before changes are
-/// merged into the master branch.
-/// * [FlutterLocalFileComparator], another
-/// [FlutterGoldenFileComparator] that tests golden images locally on your
-/// current machine.
+/// * [FlutterGoldensRepositoryFileComparator], another
+/// [FlutterGoldenFileComparator] that tests golden images using the
+/// flutter/goldens repository.
class FlutterSkiaGoldFileComparator extends FlutterGoldenFileComparator {
/// Creates a [FlutterSkiaGoldFileComparator] that will test golden file
/// images against Skia Gold.
///
- /// The [fs] and [platform] parameters are useful in tests, where the default
- /// file system and platform can be replaced by mock instances.
+ /// The [fs] and [platform] parameters useful in tests, where the default file
+ /// system and platform can be replaced by mock instances.
FlutterSkiaGoldFileComparator(
final Uri basedir,
- final SkiaGoldClient skiaClient, {
- final FileSystem fs = const LocalFileSystem(),
- final Platform platform = const LocalPlatform(),
+ this.skiaClient, {
+ FileSystem fs = const LocalFileSystem(),
+ Platform platform = const LocalPlatform(),
}) : super(
basedir,
- skiaClient,
fs: fs,
platform: platform,
);
+ final SkiaGoldClient skiaClient;
+
/// Creates a new [FlutterSkiaGoldFileComparator] that mirrors the relative
/// path resolution of the default [goldenFileComparator].
///
/// The [goldens] and [defaultComparator] parameters are visible for testing
/// purposes only.
- static Future<FlutterSkiaGoldFileComparator> fromDefaultComparator(
- final Platform platform, {
+ static Future<FlutterSkiaGoldFileComparator> fromDefaultComparator({
SkiaGoldClient goldens,
LocalFileComparator defaultComparator,
}) async {
-
defaultComparator ??= goldenFileComparator;
- final Directory baseDirectory = FlutterGoldenFileComparator.getBaseDirectory(
- defaultComparator,
- platform,
- );
+ goldens ??= SkiaGoldClient();
- if(!baseDirectory.existsSync()) {
+ final Directory baseDirectory = FlutterGoldenFileComparator.getBaseDirectory(goldens, defaultComparator);
+ if (!baseDirectory.existsSync())
baseDirectory.createSync(recursive: true);
- }
-
- goldens ??= SkiaGoldClient(baseDirectory);
- await goldens.auth();
+ await goldens.auth(baseDirectory);
await goldens.imgtestInit();
return FlutterSkiaGoldFileComparator(baseDirectory.uri, goldens);
}
@@ -219,14 +219,20 @@
Future<bool> compare(Uint8List imageBytes, Uri golden) async {
golden = _addPrefix(golden);
await update(golden, imageBytes);
- final File goldenFile = getGoldenFile(golden);
- return skiaClient.imgtestAdd(golden.path, goldenFile);
+ final File goldenFile = getGoldenFile(golden);
+ if (!goldenFile.existsSync()) {
+ throw TestFailure('Could not be compared against non-existent file: "$golden"');
+ }
+ return await skiaClient.imgtestAdd(golden.path, goldenFile);
}
+ @override
+ Uri getTestUri(Uri key, int version) => key;
+
/// Decides based on the current environment whether goldens tests should be
/// performed against Skia Gold.
- static bool isAvailableForEnvironment(Platform platform) {
+ static bool isAvailableOnPlatform(Platform platform) {
final String cirrusCI = platform.environment['CIRRUS_CI'] ?? '';
final String cirrusPR = platform.environment['CIRRUS_PR'] ?? '';
final String cirrusBranch = platform.environment['CIRRUS_BRANCH'] ?? '';
@@ -236,210 +242,49 @@
&& cirrusBranch == 'master'
&& goldServiceAccount.isNotEmpty;
}
+
+ /// Prepends the golden Uri with the library name that encloses the current
+ /// test.
+ Uri _addPrefix(Uri golden) {
+ final String prefix = basedir.pathSegments[basedir.pathSegments.length - 2];
+ return Uri.parse(prefix + '.' + golden.toString());
+ }
}
-/// A [FlutterGoldenFileComparator] for testing golden images before changes are
-/// merged into the master branch.
-///
-/// This comparator utilizes the [SkiaGoldClient] to request baseline images for
-/// the given device under test for comparison. This comparator is only
-/// initialized during pre-submit testing on Cirrus CI.
+/// A [FlutterGoldenFileComparator] for skipping golden image tests when Skia
+/// Gold is unavailable or the current platform that is executing tests is not
+/// Linux.
///
/// See also:
///
-/// * [GoldenFileComparator], the abstract class that
-/// [FlutterGoldenFileComparator] implements.
-/// * [FlutterSkiaGoldFileComparator], another
-/// [FlutterGoldenFileComparator] that uploads tests to the Skia Gold
-/// dashboard.
-/// * [FlutterLocalFileComparator], another
-/// [FlutterGoldenFileComparator] that tests golden images locally on your
-/// current machine.
-class FlutterPreSubmitFileComparator extends FlutterGoldenFileComparator {
- /// Creates a [FlutterPreSubmitFileComparator] that will test golden file
- /// images against baselines requested from Flutter Gold.
- ///
- /// The [fs] and [platform] parameters are useful in tests, where the default
- /// file system and platform can be replaced by mock instances.
- FlutterPreSubmitFileComparator(
- final Uri basedir,
- final SkiaGoldClient skiaClient, {
- final FileSystem fs = const LocalFileSystem(),
- final Platform platform = const LocalPlatform(),
- }) : super(
- basedir,
- skiaClient,
- fs: fs,
- platform: platform,
- );
+/// * [FlutterGoldensRepositoryFileComparator], another
+/// [FlutterGoldenFileComparator] that tests golden images using the
+/// flutter/goldens repository.
+/// * [FlutterSkiaGoldFileComparator], another [FlutterGoldenFileComparator]
+/// that tests golden images through Skia Gold.
+class FlutterSkippingGoldenFileComparator extends FlutterGoldenFileComparator {
+ /// Creates a [FlutterSkippingGoldenFileComparator] that will skip tests that
+ /// are not in the right environment for golden file testing.
+ FlutterSkippingGoldenFileComparator(Uri basedir) : super(basedir);
- /// Creates a new [FlutterPreSubmitFileComparator] that mirrors the
- /// relative path resolution of the default [goldenFileComparator].
- ///
- /// The [goldens] and [defaultComparator] parameters are visible for testing
- /// purposes only.
- static Future<FlutterGoldenFileComparator> fromDefaultComparator(
- final Platform platform, {
- SkiaGoldClient goldens,
+ /// Creates a new [FlutterSkippingGoldenFileComparator] that mirrors the relative
+ /// path resolution of the default [goldenFileComparator].
+ static FlutterSkippingGoldenFileComparator fromDefaultComparator({
LocalFileComparator defaultComparator,
- }) async {
-
+ }) {
defaultComparator ??= goldenFileComparator;
- final Directory baseDirectory = FlutterGoldenFileComparator.getBaseDirectory(
- defaultComparator,
- platform,
- );
-
- if(!baseDirectory.existsSync()) {
- baseDirectory.createSync(recursive: true);
- }
-
- goldens ??= SkiaGoldClient(baseDirectory);
- await goldens.getExpectations();
-
- return FlutterPreSubmitFileComparator(baseDirectory.uri, goldens);
+ return FlutterSkippingGoldenFileComparator(defaultComparator.basedir);
}
@override
Future<bool> compare(Uint8List imageBytes, Uri golden) async {
- golden = _addPrefix(golden);
- final String testName = skiaClient.cleanTestName(golden.path);
- final List<String> testExpectations = skiaClient.expectations[testName];
-
- if (testExpectations == null) {
- // There is no baseline for this test
- return true;
- }
-
- ComparisonResult result;
- for (String expectation in testExpectations) {
- final List<int> goldenBytes = await skiaClient.getImageBytes(expectation);
-
- result = GoldenFileComparator.compareLists(
- imageBytes,
- goldenBytes,
- );
-
- if (result.passed) {
- return true;
- }
- }
-
- return skiaClient.testIsIgnoredForPullRequest(
- platform.environment['CIRRUS_PR'] ?? '',
- golden.path,
+ print('Skipping "$golden" test : Skia Gold is not available in this testing '
+ 'environment and flutter/goldens repository comparison is only available '
+ 'on Linux machines.'
);
- }
-
- /// Decides based on the current environment whether goldens tests should be
- /// performed as pre-submit tests with Skia Gold.
- static bool isAvailableForEnvironment(Platform platform) {
- final String cirrusCI = platform.environment['CIRRUS_CI'] ?? '';
- final String cirrusPR = platform.environment['CIRRUS_PR'] ?? '';
- return cirrusCI.isNotEmpty && cirrusPR.isNotEmpty;
- }
-}
-
-/// A [FlutterGoldenFileComparator] for testing golden images locally on your
-/// current machine.
-///
-/// This comparator utilizes the [SkiaGoldClient] to request baseline images for
-/// the given device under test for comparison. This comparator is only
-/// initialized when running tests locally, and is intended to serve as a smoke
-/// test during development. As such, it will not be able to detect unintended
-/// changes on other machines until it they are tested using the
-/// [FlutterPreSubmitFileComparator].
-///
-/// See also:
-///
-/// * [GoldenFileComparator], the abstract class that
-/// [FlutterGoldenFileComparator] implements.
-/// * [FlutterSkiaGoldFileComparator], another
-/// [FlutterGoldenFileComparator] that uploads tests to the Skia Gold
-/// dashboard.
-/// * [FlutterPreSubmitFileComparator], another
-/// [FlutterGoldenFileComparator] that tests golden images before changes are
-/// merged into the master branch.
-class FlutterLocalFileComparator extends FlutterGoldenFileComparator with LocalComparisonOutput {
- /// Creates a [FlutterLocalFileComparator] that will test golden file
- /// images against baselines requested from Flutter Gold.
- ///
- /// The [fs] and [platform] parameters are useful in tests, where the default
- /// file system and platform can be replaced by mock instances.
- FlutterLocalFileComparator(
- final Uri basedir,
- final SkiaGoldClient skiaClient, {
- final FileSystem fs = const LocalFileSystem(),
- final Platform platform = const LocalPlatform(),
- }) : super(
- basedir,
- skiaClient,
- fs: fs,
- platform: platform,
- );
-
- /// Creates a new [FlutterLocalFileComparator] that mirrors the
- /// relative path resolution of the default [goldenFileComparator].
- ///
- /// The [goldens] and [defaultComparator] parameters are visible for testing
- /// purposes only.
- static Future<FlutterGoldenFileComparator> fromDefaultComparator(
- final Platform platform, {
- SkiaGoldClient goldens,
- LocalFileComparator defaultComparator,
- }) async {
-
- defaultComparator ??= goldenFileComparator;
- final Directory baseDirectory = FlutterGoldenFileComparator.getBaseDirectory(
- defaultComparator,
- platform,
- );
-
- if(!baseDirectory.existsSync()) {
- baseDirectory.createSync(recursive: true);
- }
-
- goldens ??= SkiaGoldClient(baseDirectory);
- await goldens.getExpectations();
-
- return FlutterLocalFileComparator(baseDirectory.uri, goldens);
+ return true;
}
@override
- Future<bool> compare(Uint8List imageBytes, Uri golden) async {
- golden = _addPrefix(golden);
- final String testName = skiaClient.cleanTestName(golden.path);
- final List<String> testExpectations = skiaClient.expectations[testName];
- if (testExpectations == null) {
- // There is no baseline for this test
- print('No expectations provided by Skia Gold for test: $golden. '
- 'This may be a new test. If this is an unexpected result, check'
- ' https://flutter-gold.skia.org.\n'
- 'Validate image output found at $basedir'
- );
- update(golden, imageBytes);
- return true;
- }
-
- ComparisonResult result;
- final Map<String, ComparisonResult> validFailures = <String, ComparisonResult>{};
- for (String expectation in testExpectations) {
- final List<int> goldenBytes = await skiaClient.getImageBytes(expectation);
-
- result = GoldenFileComparator.compareLists(
- imageBytes,
- goldenBytes,
- );
-
- if (result.passed) {
- return true;
- } else if (await skiaClient.isValidDigestForExpectation(expectation, golden.path)) {
- validFailures[expectation] = result;
- }
- }
- validFailures.forEach((String expectation, ComparisonResult result) {
- generateFailureOutput(result, golden, basedir, key: expectation);
- });
- return false;
- }
+ Future<void> update(Uri golden, Uint8List imageBytes) => null;
}
diff --git a/packages/flutter_goldens/test/flutter_goldens_test.dart b/packages/flutter_goldens/test/flutter_goldens_test.dart
index f8dd7bd..4f15200 100644
--- a/packages/flutter_goldens/test/flutter_goldens_test.dart
+++ b/packages/flutter_goldens/test/flutter_goldens_test.dart
@@ -2,10 +2,7 @@
// 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:convert';
-import 'dart:core';
-import 'dart:io';
+import 'dart:io' as io;
import 'dart:typed_data';
import 'package:file/file.dart';
@@ -16,544 +13,196 @@
import 'package:platform/platform.dart';
import 'package:process/process.dart';
-import 'json_templates.dart';
-
const String _kFlutterRoot = '/flutter';
-
-// 1x1 transparent pixel
-const List<int> _kTestPngBytes =
-<int>[137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0,
- 1, 0, 0, 0, 1, 8, 6, 0, 0, 0, 31, 21, 196, 137, 0, 0, 0, 11, 73, 68, 65, 84,
- 120, 1, 99, 97, 0, 2, 0, 0, 25, 0, 5, 144, 240, 54, 245, 0, 0, 0, 0, 73, 69,
- 78, 68, 174, 66, 96, 130];
-
-// 1x1 colored pixel
-const List<int> _kFailPngBytes =
-<int>[137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0,
- 1, 0, 0, 0, 1, 8, 6, 0, 0, 0, 31, 21, 196, 137, 0, 0, 0, 13, 73, 68, 65, 84,
- 120, 1, 99, 249, 207, 240, 255, 63, 0, 7, 18, 3, 2, 164, 147, 160, 197, 0,
- 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130];
+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;
- MockHttpClient mockHttpClient;
setUp(() {
fs = MemoryFileSystem();
- platform = FakePlatform(
- environment: <String, String>{'FLUTTER_ROOT': _kFlutterRoot},
- operatingSystem: 'macos'
- );
+ platform = FakePlatform(environment: <String, String>{'FLUTTER_ROOT': _kFlutterRoot});
process = MockProcessManager();
- mockHttpClient = MockHttpClient();
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 skiaClient;
+ group('GoldensClient', () {
+ GoldensRepositoryClient goldens;
setUp(() {
- final Directory workDirectory = fs.directory('/workDirectory')
- ..createSync(recursive: true);
- skiaClient = SkiaGoldClient(
- workDirectory,
+ goldens = GoldensRepositoryClient(
fs: fs,
process: process,
platform: platform,
- httpClient: mockHttpClient,
+ );
+ });
+
+ group('prepare', () {
+ test('performs minimal work if versions match', () async {
+ when(process.run(any, workingDirectory: anyNamed('workingDirectory')))
+ .thenAnswer((_) => Future<io.ProcessResult>.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, <String>['git', 'rev-parse', 'HEAD']);
+ expect(verifyProcessRun.captured.last, _kRepositoryRoot);
+ });
+ });
+ });
+
+ group('SkiaGoldClient', () {
+ SkiaGoldClient goldens;
+
+ setUp(() {
+ goldens = SkiaGoldClient(
+ fs: fs,
+ process: process,
+ platform: platform,
);
});
group('auth', () {
test('performs minimal work if already authorized', () async {
- fs.file('/workDirectory/temp/auth_opt.json')
- ..createSync(recursive: true);
- when(process.run(any))
- .thenAnswer((_) => Future<ProcessResult>
- .value(ProcessResult(123, 0, '', '')));
- await skiaClient.auth();
+ final Directory workDirectory = fs.directory('/workDirectory')..createSync(recursive: true);
+ fs.file('/workDirectory/temp/auth_opt.json')..createSync(recursive: true);
+ when(process.run(any)).thenAnswer((_) => Future<io.ProcessResult>.value(io.ProcessResult(123, 0, '', '')));
+ await goldens.auth(workDirectory);
- verifyNever(process.run(
- captureAny,
- workingDirectory: captureAnyNamed('workingDirectory'),
- ));
- });
- });
-
- group('Request Handling', () {
- String testName;
- String pullRequestNumber;
- String expectation;
- Uri url;
- MockHttpClientRequest mockHttpRequest;
-
- setUp(() {
- testName = 'flutter.golden_test.1.png';
- pullRequestNumber = '1234';
- expectation = '55109a4bed52acc780530f7a9aeff6c0';
- mockHttpRequest = MockHttpClientRequest();
- });
-
- test('validates SkiaDigest', () {
- final Map<String, dynamic> skiaJson = json.decode(digestResponseTemplate());
- final SkiaGoldDigest digest = SkiaGoldDigest.fromJson(skiaJson['digest']);
- expect(
- digest.isValid(
- platform,
- 'flutter.golden_test.1',
- expectation,
- ),
- isTrue,
- );
- });
-
- test('invalidates bad SkiaDigest - platform', () {
- final Map<String, dynamic> skiaJson = json.decode(
- digestResponseTemplate(platform: 'linux')
- );
- final SkiaGoldDigest digest = SkiaGoldDigest.fromJson(skiaJson['digest']);
- expect(
- digest.isValid(
- platform,
- 'flutter.golden_test.1',
- expectation,
- ),
- isFalse,
- );
- });
-
- test('invalidates bad SkiaDigest - test name', () {
- final Map<String, dynamic> skiaJson = json.decode(
- digestResponseTemplate(testName: 'flutter.golden_test.2')
- );
- final SkiaGoldDigest digest = SkiaGoldDigest.fromJson(skiaJson['digest']);
- expect(
- digest.isValid(
- platform,
- 'flutter.golden_test.1',
- expectation,
- ),
- isFalse,
- );
- });
-
- test('invalidates bad SkiaDigest - expectation', () {
- final Map<String, dynamic> skiaJson = json.decode(
- digestResponseTemplate(expectation: '1deg543sf645erg44awqcc78')
- );
- final SkiaGoldDigest digest = SkiaGoldDigest.fromJson(skiaJson['digest']);
- expect(
- digest.isValid(
- platform,
- 'flutter.golden_test.1',
- expectation,
- ),
- isFalse,
- );
- });
-
- test('invalidates bad SkiaDigest - status', () {
- final Map<String, dynamic> skiaJson = json.decode(
- digestResponseTemplate(status: 'negative')
- );
- final SkiaGoldDigest digest = SkiaGoldDigest.fromJson(skiaJson['digest']);
- expect(
- digest.isValid(
- platform,
- 'flutter.golden_test.1',
- expectation,
- ),
- isFalse,
- );
- });
-
- test('sets up expectations', () async {
- url = Uri.parse('https://flutter-gold.skia.org/json/expectations/commit/HEAD');
- final MockHttpClientResponse mockHttpResponse = MockHttpClientResponse(
- utf8.encode(rawExpectationsTemplate())
- );
- when(mockHttpClient.getUrl(url))
- .thenAnswer((_) => Future<MockHttpClientRequest>.value(mockHttpRequest));
- when(mockHttpRequest.close())
- .thenAnswer((_) => Future<MockHttpClientResponse>.value(mockHttpResponse));
-
- await skiaClient.getExpectations();
- expect(skiaClient.expectations, isNotNull);
- expect(
- skiaClient.expectations['flutter.golden_test.1'],
- contains(expectation),
- );
- });
-
- test('detects invalid digests SkiaDigest', () {
- const String testName = 'flutter.golden_test.2';
- final Map<String, dynamic> skiaJson = json.decode(digestResponseTemplate());
- final SkiaGoldDigest digest = SkiaGoldDigest.fromJson(skiaJson['digest']);
- expect(digest.isValid(platform, testName, expectation), isFalse);
- });
-
- test('image bytes are processed properly', () async {
- final Uri imageUrl = Uri.parse(
- 'https://flutter-gold.skia.org/img/images/$expectation.png'
- );
- final MockHttpClientRequest mockImageRequest = MockHttpClientRequest();
- final MockHttpImageResponse mockImageResponse = MockHttpImageResponse(
- imageResponseTemplate()
- );
- when(mockHttpClient.getUrl(imageUrl))
- .thenAnswer((_) => Future<MockHttpClientRequest>.value(mockImageRequest));
- when(mockImageRequest.close())
- .thenAnswer((_) => Future<MockHttpImageResponse>.value(mockImageResponse));
-
- final List<int> masterBytes = await skiaClient.getImageBytes(expectation);
-
- expect(masterBytes, equals(_kTestPngBytes));
- });
-
- group('ignores', () {
- Uri url;
- MockHttpClientRequest mockHttpRequest;
- MockHttpClientResponse mockHttpResponse;
-
- setUp(() {
- url = Uri.parse('https://flutter-gold.skia.org/json/ignores');
- mockHttpRequest = MockHttpClientRequest();
- mockHttpResponse = MockHttpClientResponse(utf8.encode(
- ignoreResponseTemplate(pullRequestNumber: pullRequestNumber)
- ));
- when(mockHttpClient.getUrl(url))
- .thenAnswer((_) => Future<MockHttpClientRequest>.value(mockHttpRequest));
- when(mockHttpRequest.close())
- .thenAnswer((_) => Future<MockHttpClientResponse>.value(mockHttpResponse));
- });
-
- test('returns true for ignored test and ignored pull request number', () async {
- expect(
- await skiaClient.testIsIgnoredForPullRequest(
- pullRequestNumber,
- testName,
- ),
- isTrue,
- );
- });
-
- test('returns false for not ignored test and ignored pull request number', () async {
- expect(
- await skiaClient.testIsIgnoredForPullRequest(
- '5678',
- testName,
- ),
- isFalse,
- );
- });
-
- test('returns false for ignored test and not ignored pull request number', () async {
- expect(
- await skiaClient.testIsIgnoredForPullRequest(
- pullRequestNumber,
- 'failure.png',
- ),
- isFalse,
- );
- });
- });
-
- group('digest parsing', () {
- Uri url;
- MockHttpClientRequest mockHttpRequest;
- MockHttpClientResponse mockHttpResponse;
-
- setUp(() {
- url = Uri.parse(
- 'https://flutter-gold.skia.org/json/details?'
- 'test=flutter.golden_test.1&digest=$expectation'
- );
- mockHttpRequest = MockHttpClientRequest();
- when(mockHttpClient.getUrl(url))
- .thenAnswer((_) => Future<MockHttpClientRequest>.value(mockHttpRequest));
- });
-
- test('succeeds when valid', () async {
- mockHttpResponse = MockHttpClientResponse(utf8.encode(digestResponseTemplate()));
- when(mockHttpRequest.close())
- .thenAnswer((_) => Future<MockHttpClientResponse>.value(mockHttpResponse));
- expect(
- await skiaClient.isValidDigestForExpectation(
- expectation,
- testName,
- ),
- isTrue,
- );
- });
-
- test('fails when invalid', () async {
- mockHttpResponse = MockHttpClientResponse(utf8.encode(
- digestResponseTemplate(platform: 'linux')
- ));
- when(mockHttpRequest.close())
- .thenAnswer((_) => Future<MockHttpClientResponse>.value(mockHttpResponse));
- expect(
- await skiaClient.isValidDigestForExpectation(
- expectation,
- testName,
- ),
- isFalse,
- );
- });
+ // Verify that we spawned no process calls
+ final VerificationResult verifyProcessRun =
+ verifyNever(process.run(captureAny, workingDirectory: captureAnyNamed('workingDirectory')));
+ expect(verifyProcessRun.callCount, 0);
});
});
});
group('FlutterGoldenFileComparator', () {
+ test('calculates the basedir correctly', () async {
+ final MockSkiaGoldClient goldens = MockSkiaGoldClient();
+ 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.comparisonRoot).thenReturn(goldensRoot);
+ when(defaultComparator.basedir).thenReturn(flutterRoot.childDirectory('baz').uri);
+ final Directory basedir = FlutterGoldenFileComparator.getBaseDirectory(goldens, defaultComparator);
+ expect(basedir.uri, fs.directory('/foo/bar/baz').uri);
+ });
+ });
+
+ group('FlutterGoldensRepositoryFileComparator', () {
+ MemoryFileSystem fs;
+ FlutterGoldensRepositoryFileComparator comparator;
+
+ setUp(() {
+ fs = MemoryFileSystem();
+ platform = FakePlatform(
+ operatingSystem: 'linux',
+ environment: <String, String>{'FLUTTER_ROOT': _kFlutterRoot},
+ );
+ final Directory flutterRoot = fs.directory('/path/to/flutter')..createSync(recursive: true);
+ final Directory goldensRoot = flutterRoot.childDirectory('bin/cache/goldens')..createSync(recursive: true);
+ final Directory testDirectory = goldensRoot.childDirectory('test/foo/bar')..createSync(recursive: true);
+ comparator = FlutterGoldensRepositoryFileComparator(
+ testDirectory.uri,
+ fs: fs,
+ platform: platform,
+ );
+ });
+
+ group('compare', () {
+ test('throws if golden file is not found', () async {
+ try {
+ await comparator.compare(Uint8List.fromList(<int>[1, 2, 3]), Uri.parse('test.png'));
+ fail('TestFailure expected but not thrown');
+ } on TestFailure catch (error) {
+ expect(error.message, contains('Could not be compared against non-existent file'));
+ }
+ });
+
+ 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(<int>[4, 5, 6], flush: true);
+ final bool result = await comparator.compare(Uint8List.fromList(<int>[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(<int>[1, 2, 3], flush: true);
+ final bool result = await comparator.compare(Uint8List.fromList(<int>[1, 2, 3]), Uri.parse('test.png'));
+ expect(result, isTrue);
+ });
+ });
+
+ group('update', () {
+ test('creates golden file if it does not already exist', () async {
+ final File goldenFile = fs.file('/path/to/flutter/bin/cache/goldens/test/foo/bar/test.png');
+ expect(goldenFile.existsSync(), isFalse);
+ await comparator.update(Uri.parse('test.png'), Uint8List.fromList(<int>[1, 2, 3]));
+ expect(goldenFile.existsSync(), isTrue);
+ expect(goldenFile.readAsBytesSync(), <int>[1, 2, 3]);
+ });
+
+ test('overwrites golden bytes if golden file already exist', () async {
+ final File goldenFile = fs.file('/path/to/flutter/bin/cache/goldens/test/foo/bar/test.png')
+ ..createSync(recursive: true);
+ goldenFile.writeAsBytesSync(<int>[4, 5, 6], flush: true);
+ await comparator.update(Uri.parse('test.png'), Uint8List.fromList(<int>[1, 2, 3]));
+ expect(goldenFile.readAsBytesSync(), <int>[1, 2, 3]);
+ });
+ });
+
+ group('getTestUri', () {
+ test('incorporates version number', () {
+ final Uri key = comparator.getTestUri(Uri.parse('foo.png'), 1);
+ expect(key, Uri.parse('foo.1.png'));
+ });
+ test('ignores null version number', () {
+ final Uri key = comparator.getTestUri(Uri.parse('foo.png'), null);
+ expect(key, Uri.parse('foo.png'));
+ });
+ });
+ });
+
+ group('FlutterSkiaGoldFileComparator', () {
FlutterSkiaGoldFileComparator comparator;
setUp(() {
- final Directory basedir = fs.directory('flutter/test/library/')
- ..createSync(recursive: true);
+ final Directory flutterRoot = fs.directory('/path/to/flutter')..createSync(recursive: true);
+ final Directory goldensRoot = flutterRoot.childDirectory('bin/cache/goldens')..createSync(recursive: true);
+ final Directory testDirectory = goldensRoot.childDirectory('test/foo/bar')..createSync(recursive: true);
comparator = FlutterSkiaGoldFileComparator(
- basedir.uri,
+ testDirectory.uri,
MockSkiaGoldClient(),
fs: fs,
platform: platform,
);
});
- test('calculates the basedir correctly from defaultComparator', () async {
- final MockLocalFileComparator defaultComparator = MockLocalFileComparator();
- final Directory flutterRoot = fs.directory(platform.environment['FLUTTER_ROOT'])
- ..createSync(recursive: true);
- when(defaultComparator.basedir).thenReturn(flutterRoot.childDirectory('baz').uri);
-
- final Directory basedir = FlutterGoldenFileComparator.getBaseDirectory(
- defaultComparator,
- platform,
- );
- expect(
- basedir.uri,
- fs.directory('/flutter/bin/cache/pkg/skia_goldens/baz').uri,
- );
- });
-
- test('ignores version number', () {
- final Uri key = comparator.getTestUri(Uri.parse('foo.png'), 1);
- expect(key, Uri.parse('foo.png'));
- });
-
- group('Post-Submit', () {
- final MockSkiaGoldClient mockSkiaClient = MockSkiaGoldClient();
-
- setUp(() {
- final Directory basedir = fs.directory('flutter/test/library/')
- ..createSync(recursive: true);
- comparator = FlutterSkiaGoldFileComparator(
- basedir.uri,
- mockSkiaClient,
- fs: fs,
- platform: platform,
- );
- });
-
- test('correctly determines testing environment', () {
- platform = FakePlatform(
- environment: <String, String>{
- 'FLUTTER_ROOT': _kFlutterRoot,
- 'CIRRUS_CI' : 'true',
- 'CIRRUS_PR' : '',
- 'CIRRUS_BRANCH' : 'master',
- 'GOLD_SERVICE_ACCOUNT' : 'service account...',
- },
- operatingSystem: 'macos'
- );
- expect(
- FlutterSkiaGoldFileComparator.isAvailableForEnvironment(platform),
- isTrue,
- );
- });
- });
-
- group('Pre-Submit', () {
- FlutterPreSubmitFileComparator comparator;
- final MockSkiaGoldClient mockSkiaClient = MockSkiaGoldClient();
-
- setUp(() {
- final Directory basedir = fs.directory('flutter/test/library/')
- ..createSync(recursive: true);
- comparator = FlutterPreSubmitFileComparator(
- basedir.uri,
- mockSkiaClient,
- fs: fs,
- platform: FakePlatform(
- environment: <String, String>{
- 'FLUTTER_ROOT': _kFlutterRoot,
- 'CIRRUS_CI' : 'true',
- 'CIRRUS_PR' : '1234',
- },
- operatingSystem: 'macos'
- ),
- );
-
- when(mockSkiaClient.getImageBytes('55109a4bed52acc780530f7a9aeff6c0'))
- .thenAnswer((_) => Future<List<int>>.value(_kTestPngBytes));
- when(mockSkiaClient.expectations)
- .thenReturn(expectationsTemplate());
- when(mockSkiaClient.cleanTestName('library.flutter.golden_test.1.png'))
- .thenReturn('flutter.golden_test.1');
- when(mockSkiaClient.isValidDigestForExpectation(
- '55109a4bed52acc780530f7a9aeff6c0',
- 'library.flutter.golden_test.1.png',
- ))
- .thenAnswer((_) => Future<bool>.value(false));
- });
-
- test('correctly determines testing environment', () {
- platform = FakePlatform(
- environment: <String, String>{
- 'FLUTTER_ROOT': _kFlutterRoot,
- 'CIRRUS_CI' : 'true',
- 'CIRRUS_PR' : '1234',
- },
- operatingSystem: 'macos'
- );
- expect(
- FlutterPreSubmitFileComparator.isAvailableForEnvironment(platform),
- isTrue,
- );
- });
-
- test('comparison passes test that is ignored for this PR', () async {
- when(mockSkiaClient.getImageBytes('55109a4bed52acc780530f7a9aeff6c0'))
- .thenAnswer((_) => Future<List<int>>.value(_kTestPngBytes));
- when(mockSkiaClient.testIsIgnoredForPullRequest(
- '1234',
- 'library.flutter.golden_test.1.png',
- ))
- .thenAnswer((_) => Future<bool>.value(true));
- expect(
- await comparator.compare(
- Uint8List.fromList(_kFailPngBytes),
- Uri.parse('flutter.golden_test.1.png'),
- ),
- isTrue,
- );
- });
-
- test('fails test that is not ignored for this PR', () async {
- when(mockSkiaClient.getImageBytes('55109a4bed52acc780530f7a9aeff6c0'))
- .thenAnswer((_) => Future<List<int>>.value(_kTestPngBytes));
- when(mockSkiaClient.testIsIgnoredForPullRequest(
- '1234',
- 'library.flutter.golden_test.1.png',
- ))
- .thenAnswer((_) => Future<bool>.value(false));
- expect(
- await comparator.compare(
- Uint8List.fromList(_kFailPngBytes),
- Uri.parse('flutter.golden_test.1.png'),
- ),
- isFalse,
- );
- });
-
- test('passes non-existent baseline for new test', () async {
- expect(
- await comparator.compare(
- Uint8List.fromList(_kFailPngBytes),
- Uri.parse('flutter.new_golden_test.1.png'),
- ),
- isTrue,
- );
- });
- });
-
- group('Local', () {
- FlutterLocalFileComparator comparator;
- final MockSkiaGoldClient mockSkiaClient = MockSkiaGoldClient();
-
- setUp(() async {
- final Directory basedir = fs.directory('flutter/test/library/')
- ..createSync(recursive: true);
- comparator = FlutterLocalFileComparator(
- basedir.uri,
- mockSkiaClient,
- fs: fs,
- platform: FakePlatform(
- environment: <String, String>{'FLUTTER_ROOT': _kFlutterRoot},
- operatingSystem: 'macos'
- ),
- );
-
- when(mockSkiaClient.getImageBytes('55109a4bed52acc780530f7a9aeff6c0'))
- .thenAnswer((_) => Future<List<int>>.value(_kTestPngBytes));
- when(mockSkiaClient.expectations)
- .thenReturn(expectationsTemplate());
- when(mockSkiaClient.cleanTestName('library.flutter.golden_test.1.png'))
- .thenReturn('flutter.golden_test.1');
- when(mockSkiaClient.isValidDigestForExpectation(
- '55109a4bed52acc780530f7a9aeff6c0',
- 'library.flutter.golden_test.1.png',
- ))
- .thenAnswer((_) => Future<bool>.value(false));
- });
-
- test('passes when bytes match', () async {
- expect(
- await comparator.compare(
- Uint8List.fromList(_kTestPngBytes),
- Uri.parse('flutter.golden_test.1.png'),
- ),
- isTrue,
- );
- });
-
- test('passes non-existent baseline for new test', () async {
- expect(
- await comparator.compare(
- Uint8List.fromList(_kFailPngBytes),
- Uri.parse('flutter.new_golden_test.1'),
- ),
- isTrue,
- );
+ group('getTestUri', () {
+ test('ignores version number', () {
+ final Uri key = comparator.getTestUri(Uri.parse('foo.png'), 1);
+ expect(key, Uri.parse('foo.png'));
});
});
});
}
class MockProcessManager extends Mock implements ProcessManager {}
-
+class MockGoldensRepositoryClient extends Mock implements GoldensRepositoryClient {}
class MockSkiaGoldClient extends Mock implements SkiaGoldClient {}
-
class MockLocalFileComparator extends Mock implements LocalFileComparator {}
-
-class MockHttpClient extends Mock implements HttpClient {}
-
-class MockHttpClientRequest extends Mock implements HttpClientRequest {}
-
-class MockHttpClientResponse extends Mock implements HttpClientResponse {
- MockHttpClientResponse(this.response);
-
- final Uint8List response;
-
- @override
- StreamSubscription<Uint8List> listen(
- void onData(Uint8List event), {
- Function onError,
- void onDone(),
- bool cancelOnError,
- }) {
- return Stream<Uint8List>.fromFuture(Future<Uint8List>.value(response))
- .listen(onData, onError: onError, onDone: onDone, cancelOnError: cancelOnError);
- }
-}
-
-class MockHttpImageResponse extends Mock implements HttpClientResponse {
- MockHttpImageResponse(this.response);
-
- final List<List<int>> response;
-
- @override
- Future<void> forEach(void action(List<int> element)) async {
- response.forEach(action);
- }
-}
diff --git a/packages/flutter_goldens/test/json_templates.dart b/packages/flutter_goldens/test/json_templates.dart
deleted file mode 100644
index f33a398..0000000
--- a/packages/flutter_goldens/test/json_templates.dart
+++ /dev/null
@@ -1,202 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/// Json response template for Skia Gold expectations request:
-/// https://flutter-gold.skia.org/json/expectations/commit/HEAD
-String rawExpectationsTemplate() {
- return '''
- {
- "md5": "a7489b00e03a1846e43500b7c14dd7b0",
- "master": {
- "flutter.golden_test.1": {
- "55109a4bed52acc780530f7a9aeff6c0": 1
- },
- "flutter.golden_test.3": {
- "87cb35131e6ad4b57d4d09d59ae743c3": 1,
- "dc94eb2c39c0c8ae11a4efd090b72f94": 1,
- "f2583c9003978a06b7888878bdc089e2": 1
- },
- "flutter.golden_test.2": {
- "eb03a5e3114c9ecad5e4f1178f285a49": 1,
- "f14631979de24fca6e14ad247d5f2bd6": 1
- }
- }
- }
- ''';
-}
-
-/// Decoded json response template for Skia Gold expectations request:
-/// https://flutter-gold.skia.org/json/expectations/commit/HEAD
-Map<String, List<String>> expectationsTemplate() {
- return <String, List<String>>{
- 'flutter.golden_test.1': <String>[
- '55109a4bed52acc780530f7a9aeff6c0'
- ],
- 'flutter.golden_test.3': <String>[
- '87cb35131e6ad4b57d4d09d59ae743c3',
- 'dc94eb2c39c0c8ae11a4efd090b72f94',
- 'f2583c9003978a06b7888878bdc089e2',
- ],
- 'flutter.golden_test.2': <String>[
- 'eb03a5e3114c9ecad5e4f1178f285a49',
- 'f14631979de24fca6e14ad247d5f2bd6',
- ],
- };
-}
-
-/// Json response template for Skia Gold digest request:
-/// https://flutter-gold.skia.org/json/details?test=[testName]&digest=[expectation]
-String digestResponseTemplate({
- String testName = 'flutter.golden_test.1',
- String expectation = '55109a4bed52acc780530f7a9aeff6c0',
- String platform = 'macos',
- String status = 'positive',
-}) {
- return '''
- {
- "digest": {
- "test": "$testName",
- "digest": "$expectation",
- "status": "$status",
- "paramset": {
- "Platform": [
- "$platform"
- ],
- "ext": [
- "png"
- ],
- "name": [
- "$testName"
- ],
- "source_type": [
- "flutter"
- ]
- },
- "traces": {
- "tileSize": 200,
- "traces": [
- {
- "data": [
- {
- "x": 0,
- "y": 0,
- "s": 0
- },
- {
- "x": 1,
- "y": 0,
- "s": 0
- },
- {
- "x": 199,
- "y": 0,
- "s": 0
- }
- ],
- "label": ",Platform=$platform,name=$testName,source_type=flutter,",
- "params": {
- "Platform": "$platform",
- "ext": "png",
- "name": "$testName",
- "source_type": "flutter"
- }
- }
- ],
- "digests": [
- {
- "digest": "$expectation",
- "status": "$status"
- }
- ]
- },
- "closestRef": "pos",
- "refDiffs": {
- "neg": null,
- "pos": {
- "numDiffPixels": 999,
- "pixelDiffPercent": 0.4995,
- "maxRGBADiffs": [
- 86,
- 86,
- 86,
- 0
- ],
- "dimDiffer": false,
- "diffs": {
- "combined": 0.381955,
- "percent": 0.4995,
- "pixel": 999
- },
- "digest": "aa748136c70cefdda646df5be0ae189d",
- "status": "positive",
- "paramset": {
- "Platform": [
- "macos"
- ],
- "ext": [
- "png"
- ],
- "name": [
- "$testName"
- ],
- "source_type": [
- "flutter"
- ]
- },
- "n": 197
- }
- }
- },
- "commits": [
- {
- "commit_time": 1568069344,
- "hash": "399bb04e2de41665320d3c888f40af6d8bc734a2",
- "author": "Contributor A (contributorA@getMail.com)"
- },
- {
- "commit_time": 1568078053,
- "hash": "0f365d3add253a65e5e5af1024f56c6169bf9739",
- "author": "Contributor B (contributorB@getMail.com)"
- },
- {
- "commit_time": 1569353925,
- "hash": "81e693a7fe3b808cc9ae2bb3a2cbe404e67ec773",
- "author": "Contributor C (contributorC@getMail.com)"
- }
- ]
-}
- ''';
-}
-
-/// Json response template for Skia Gold ignore request:
-/// https://flutter-gold.skia.org/json/ignores
-String ignoreResponseTemplate({
- String pullRequestNumber = '0000',
- String testName = 'flutter.golden_test.1',
-}) {
- return '''
- [
- {
- "id": "7579425228619212078",
- "name": "contributor@getMail.com",
- "updatedBy": "contributor@getMail.com",
- "expires": "2019-09-06T21:28:18.815336Z",
- "query": "ext=png&name=$testName",
- "note": "https://github.com/flutter/flutter/pull/$pullRequestNumber"
- }
- ]
- ''';
-}
-
-/// Json response template for Skia Gold image request:
-/// https://flutter-gold.skia.org/img/images/[imageHash].png
-List<List<int>> imageResponseTemplate() {
- return <List<int>>[
- <int>[137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73,
- 72, 68, 82, 0, 0, 0, 1, 0, 0, 0, 1, 8, 6, 0, 0, 0, 31, 21, 196, 137, 0],
- <int>[0, 0, 11, 73, 68, 65, 84, 120, 1, 99, 97, 0, 2, 0,
- 0, 25, 0, 5, 144, 240, 54, 245, 0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96,
- 130],
- ];
-}
diff --git a/packages/flutter_goldens_client/lib/client.dart b/packages/flutter_goldens_client/lib/client.dart
new file mode 100644
index 0000000..2a0c792
--- /dev/null
+++ b/packages/flutter_goldens_client/lib/client.dart
@@ -0,0 +1,212 @@
+// Copyright 2018 The Chromium 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' as io;
+
+import 'package:file/file.dart';
+import 'package:file/local.dart';
+import 'package:platform/platform.dart';
+import 'package:process/process.dart';
+
+// If you are here trying to figure out how to use golden files in the Flutter
+// repo itself, consider reading this wiki page:
+// https://github.com/flutter/flutter/wiki/Writing-a-golden-file-test-for-package%3Aflutter
+
+const String _kFlutterRootKey = 'FLUTTER_ROOT';
+
+/// A base class that provides shared information to the
+/// [FlutterGoldenFileComparator] as well as the [SkiaGoldClient] and
+/// [GoldensRepositoryClient].
+abstract class GoldensClient {
+ /// Creates a handle to the local environment of golden file images.
+ GoldensClient({
+ this.fs = const LocalFileSystem(),
+ this.platform = const LocalPlatform(),
+ this.process = const LocalProcessManager(),
+ });
+
+ /// 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.
+ 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.
+ final Platform platform;
+
+ /// 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
+ /// subprocesses.
+ final ProcessManager process;
+
+ /// The local [Directory] where the Flutter repository is hosted.
+ ///
+ /// Uses the [fs] file system.
+ Directory get flutterRoot => fs.directory(platform.environment[_kFlutterRootKey]);
+
+ /// The local [Directory] where the goldens files are located.
+ ///
+ /// Uses the [fs] file system.
+ Directory get comparisonRoot => flutterRoot.childDirectory(fs.path.join('bin', 'cache', 'pkg', 'goldens'));
+
+}
+
+/// 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 GoldensRepositoryClient extends GoldensClient {
+ GoldensRepositoryClient({
+ FileSystem fs = const LocalFileSystem(),
+ ProcessManager process = const LocalProcessManager(),
+ Platform platform = const LocalPlatform(),
+ }) : super(
+ fs: fs,
+ process: process,
+ platform: platform,
+ );
+
+ RandomAccessFile _lock;
+
+ /// Prepares the local clone of the `flutter/goldens` repository for golden
+ /// file testing.
+ ///
+ /// 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`.
+ ///
+ /// 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<void> 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<String> _getCurrentCommit() async {
+ if (!comparisonRoot.existsSync()) {
+ return null;
+ } else {
+ final io.ProcessResult revParse = await process.run(
+ <String>['git', 'rev-parse', 'HEAD'],
+ workingDirectory: comparisonRoot.path,
+ );
+ return revParse.exitCode == 0 ? revParse.stdout.trim() : null;
+ }
+ }
+
+ Future<String> _getGoldensCommit() async {
+ final File versionFile = flutterRoot.childFile(fs.path.join('bin', 'internal', 'goldens.version'));
+ return (await versionFile.readAsString()).trim();
+ }
+
+ Future<void> _initRepository() async {
+ await comparisonRoot.create(recursive: true);
+ await _runCommands(
+ <String>[
+ '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: comparisonRoot,
+ );
+ }
+
+ Future<void> _checkCanSync() async {
+ final io.ProcessResult result = await process.run(
+ <String>['git', 'status', '--porcelain'],
+ workingDirectory: comparisonRoot.path,
+ );
+ if (result.stdout.trim().isNotEmpty) {
+ final StringBuffer buf = StringBuffer()
+ ..writeln('flutter_goldens git checkout at ${comparisonRoot.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 ${comparisonRoot.path}')
+ ..writeln('git reset --hard HEAD')
+ ..writeln('git clean -x -d -f -f');
+ throw NonZeroExitCode(1, buf.toString());
+ }
+ }
+
+ Future<void> _syncTo(String commit) async {
+ await _runCommands(
+ <String>[
+ 'git pull upstream master',
+ 'git fetch upstream $commit',
+ 'git reset --hard FETCH_HEAD',
+ ],
+ workingDirectory: comparisonRoot,
+ );
+ }
+
+ Future<void> _runCommands(
+ List<String> commands, {
+ Directory workingDirectory,
+ }) async {
+ for (String command in commands) {
+ final List<String> parts = command.split(' ');
+ final io.ProcessResult result = await process.run(
+ parts,
+ workingDirectory: workingDirectory?.path,
+ );
+ if (result.exitCode != 0) {
+ throw NonZeroExitCode(result.exitCode, result.stderr);
+ }
+ }
+ }
+
+ Future<void> _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<void> _releaseLock() async {
+ await _lock.close();
+ _lock = null;
+ }
+}
+
+/// Exception that signals a process' exit with a non-zero exit code.
+class NonZeroExitCode implements Exception {
+ /// Create an exception that represents a non-zero exit code.
+ ///
+ /// The first argument must be non-zero.
+ const NonZeroExitCode(this.exitCode, this.stderr) : assert(exitCode != 0);
+
+ /// The code that the process will signal to the operating system.
+ ///
+ /// By definition, this is not zero.
+ final int exitCode;
+
+ /// The message to show on standard error.
+ final String stderr;
+
+ @override
+ String toString() {
+ return 'Exit code $exitCode: $stderr';
+ }
+}
diff --git a/packages/flutter_goldens_client/lib/skia_client.dart b/packages/flutter_goldens_client/lib/skia_client.dart
index 4a78f25..27de48c 100644
--- a/packages/flutter_goldens_client/lib/skia_client.dart
+++ b/packages/flutter_goldens_client/lib/skia_client.dart
@@ -12,50 +12,30 @@
import 'package:platform/platform.dart';
import 'package:process/process.dart';
+import 'package:flutter_goldens_client/client.dart';
+
// If you are here trying to figure out how to use golden files in the Flutter
// repo itself, consider reading this wiki page:
// https://github.com/flutter/flutter/wiki/Writing-a-golden-file-test-for-package%3Aflutter
-const String _kFlutterRootKey = 'FLUTTER_ROOT';
+// TODO(Piinks): This file will replace ./client.dart when transition to Skia
+// Gold testing is complete
+
const String _kGoldctlKey = 'GOLDCTL';
const String _kServiceAccountKey = 'GOLD_SERVICE_ACCOUNT';
-/// A client for uploading image tests and making baseline requests to the
-/// Flutter Gold Dashboard.
-class SkiaGoldClient {
- SkiaGoldClient(
- this.workDirectory, {
- this.fs = const LocalFileSystem(),
- this.process = const LocalProcessManager(),
- this.platform = const LocalPlatform(),
- io.HttpClient httpClient,
- }) : assert(workDirectory != null),
- assert(fs != null),
- assert(process != null),
- assert(platform != null),
- httpClient = httpClient ?? io.HttpClient();
-
- /// 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.
- 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.
- final Platform platform;
-
- /// A controller for launching 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
- /// sub-processes.
- final ProcessManager process;
-
- /// A client for making Http requests to the Flutter Gold dashboard.
- final io.HttpClient httpClient;
+/// An extension of the [GoldensClient] class that interfaces with Skia Gold
+/// for golden file testing.
+class SkiaGoldClient extends GoldensClient {
+ SkiaGoldClient({
+ FileSystem fs = const LocalFileSystem(),
+ ProcessManager process = const LocalProcessManager(),
+ Platform platform = const LocalPlatform(),
+ }) : super(
+ fs: fs,
+ process: process,
+ platform: platform,
+ );
/// The local [Directory] within the [comparisonRoot] for the current test
/// context. In this directory, the client will create image and json files
@@ -63,21 +43,7 @@
///
/// This is informed by the [FlutterGoldenFileComparator] [basedir]. It cannot
/// be null.
- final Directory workDirectory;
-
- /// A map of known golden file tests and their associated positive image
- /// hashes.
- ///
- /// This is set and used by the [FlutterLocalFileComparator] and
- /// [FlutterPreSubmitFileComparator] to test against golden masters maintained
- /// in the Flutter Gold dashboard.
- Map<String, List<String>> get expectations => _expectations;
- Map<String, List<String>> _expectations;
-
- /// The local [Directory] where the Flutter repository is hosted.
- ///
- /// Uses the [fs] file system.
- Directory get _flutterRoot => fs.directory(platform.environment[_kFlutterRootKey]);
+ Directory _workDirectory;
/// The path to the local [Directory] where the goldctl tool is hosted.
///
@@ -90,6 +56,9 @@
/// Uses the [platform] environment in this implementation.
String get _serviceAccount => platform.environment[_kServiceAccountKey];
+ @override
+ Directory get comparisonRoot => flutterRoot.childDirectory(fs.path.join('bin', 'cache', 'pkg', 'skia_goldens'));
+
/// Prepares the local work space for golden file testing and calls the
/// goldctl `auth` command.
///
@@ -100,31 +69,39 @@
/// The [workDirectory] parameter specifies the current directory that golden
/// tests are executing in, relative to the library of the given test. It is
/// informed by the basedir of the [FlutterSkiaGoldFileComparator].
- Future<void> auth() async {
+ Future<void> auth(Directory workDirectory) async {
+ assert(workDirectory != null);
+ _workDirectory = workDirectory;
if (_clientIsAuthorized())
return;
if (_serviceAccount.isEmpty) {
- final StringBuffer buf = StringBuffer()
- ..writeln('Gold service account is unavailable.');
+ final StringBuffer buf = StringBuffer()..writeln('Gold service account is unavailable.');
throw NonZeroExitCode(1, buf.toString());
}
- final File authorization = workDirectory.childFile('serviceAccount.json');
+ final File authorization = _workDirectory.childFile('serviceAccount.json');
await authorization.writeAsString(_serviceAccount);
final List<String> authArguments = <String>[
'auth',
'--service-account', authorization.path,
- '--work-dir', workDirectory
- .childDirectory('temp')
- .path,
+ '--work-dir', _workDirectory.childDirectory('temp').path,
];
+ // final io.ProcessResult authResults =
await io.Process.run(
_goldctl,
authArguments,
);
+ // TODO(Piinks): Re-enable after Gold flakes are resolved, https://github.com/flutter/flutter/pull/36103
+ // if (authResults.exitCode != 0) {
+ // final StringBuffer buf = StringBuffer()
+ // ..writeln('Flutter + Skia Gold auth failed.')
+ // ..writeln('stdout: ${authResults.stdout}')
+ // ..writeln('stderr: ${authResults.stderr}');
+ // throw NonZeroExitCode(authResults.exitCode, buf.toString());
+ // }
}
/// Executes the `imgtest init` command in the goldctl tool.
@@ -132,8 +109,8 @@
/// The `imgtest` command collects and uploads test results to the Skia Gold
/// backend, the `init` argument initializes the current test.
Future<void> imgtestInit() async {
- final File keys = workDirectory.childFile('keys.json');
- final File failures = workDirectory.childFile('failures.json');
+ final File keys = _workDirectory.childFile('keys.json');
+ final File failures = _workDirectory.childFile('failures.json');
await keys.writeAsString(_getKeysJSON());
await failures.create();
@@ -142,9 +119,7 @@
final List<String> imgtestInitArguments = <String>[
'imgtest', 'init',
'--instance', 'flutter',
- '--work-dir', workDirectory
- .childDirectory('temp')
- .path,
+ '--work-dir', _workDirectory.childDirectory('temp').path,
'--commit', commitHash,
'--keys-file', keys.path,
'--failure-file', failures.path,
@@ -152,16 +127,26 @@
];
if (imgtestInitArguments.contains(null)) {
- final StringBuffer buf = StringBuffer()
- ..writeln('Null argument for Skia Gold imgtest init:');
+ 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 =
await io.Process.run(
_goldctl,
imgtestInitArguments,
);
+
+ // TODO(Piinks): Re-enable after Gold flakes are resolved, https://github.com/flutter/flutter/pull/36103
+ // if (imgtestInitResult.exitCode != 0) {
+ // final StringBuffer buf = StringBuffer()
+ // ..writeln('Flutter + Skia Gold imgtest init failed.')
+ // ..writeln('stdout: ${imgtestInitResult.stdout}')
+ // ..writeln('stderr: ${imgtestInitResult.stderr}');
+ // throw NonZeroExitCode(imgtestInitResult.exitCode, buf.toString());
+ // }
}
/// Executes the `imgtest add` command in the goldctl tool.
@@ -179,10 +164,8 @@
final List<String> imgtestArguments = <String>[
'imgtest', 'add',
- '--work-dir', workDirectory
- .childDirectory('temp')
- .path,
- '--test-name', cleanTestName(testName),
+ '--work-dir', _workDirectory.childDirectory('temp').path,
+ '--test-name', testName.split(path.extension(testName.toString()))[0],
'--png-file', goldenFile.path,
];
@@ -190,145 +173,25 @@
_goldctl,
imgtestArguments,
);
+
+ // TODO(Piinks): Comment on PR if triage is needed, https://github.com/flutter/flutter/issues/34673
+ // So as not to turn the tree red in this initial implementation, this will
+ // return true for now.
+ // The ProcessResult that returns from line 157 contains the pass/fail
+ // result of the test & links to the dashboard and diffs.
return true;
}
- /// Requests and sets the [_expectations] known to Flutter Gold at head.
- Future<void> getExpectations() async {
- _expectations = <String, List<String>>{};
- await io.HttpOverrides.runWithHttpOverrides<Future<void>>(() async {
- final Uri requestForExpectations = Uri.parse(
- 'https://flutter-gold.skia.org/json/expectations/commit/HEAD'
- );
- String rawResponse;
- try {
- final io.HttpClientRequest request = await httpClient.getUrl(requestForExpectations);
- final io.HttpClientResponse response = await request.close();
- rawResponse = await utf8.decodeStream(response);
- final Map<String, dynamic> skiaJson = json.decode(rawResponse)['master'];
-
- skiaJson.forEach((String key, dynamic value) {
- final Map<String, dynamic> hashesMap = value;
- _expectations[key] = hashesMap.keys.toList();
- });
- } on FormatException catch(_) {
- print('Formatting error detected requesting expectations from Flutter Gold.\n'
- 'rawResponse: $rawResponse');
- rethrow;
- }
- },
- SkiaGoldHttpOverrides(),
- );
- }
-
- /// Returns a list of bytes representing the golden image retrieved from the
- /// Flutter Gold dashboard.
- ///
- /// The provided image hash represents an expectation from Flutter Gold.
- Future<List<int>>getImageBytes(String imageHash) async {
- final List<int> imageBytes = <int>[];
- await io.HttpOverrides.runWithHttpOverrides<Future<void>>(() async {
- final Uri requestForImage = Uri.parse(
- 'https://flutter-gold.skia.org/img/images/$imageHash.png',
- );
-
- try {
- final io.HttpClientRequest request = await httpClient.getUrl(requestForImage);
- final io.HttpClientResponse response = await request.close();
- await response.forEach((List<int> bytes) => imageBytes.addAll(bytes));
-
- } catch(e) {
- rethrow;
- }
- },
- SkiaGoldHttpOverrides(),
- );
- return imageBytes;
- }
-
- /// Returns a boolean value for whether or not the given test and current pull
- /// request are ignored on Flutter Gold.
- ///
- /// This is only relevant when used by the [FlutterPreSubmitFileComparator].
- /// In order to land a change to an exiting golden file, an ignore must be set
- /// up in Flutter Gold. This will serve as a flag to permit the change to
- /// land, and protect against any unwanted changes.
- Future<bool> testIsIgnoredForPullRequest(String pullRequest, String testName) async {
- bool ignoreIsActive = false;
- testName = cleanTestName(testName);
- String rawResponse;
- await io.HttpOverrides.runWithHttpOverrides<Future<void>>(() async {
- final Uri requestForIgnores = Uri.parse(
- 'https://flutter-gold.skia.org/json/ignores'
- );
-
- try {
- final io.HttpClientRequest request = await httpClient.getUrl(requestForIgnores);
- final io.HttpClientResponse response = await request.close();
- rawResponse = await utf8.decodeStream(response);
- final List<dynamic> ignores = json.decode(rawResponse);
- for(Map<String, dynamic> ignore in ignores) {
- final List<String> ignoredQueries = ignore['query'].split('&');
- final String ignoredPullRequest = ignore['note'].split('/').last;
- if (ignoredQueries.contains('name=$testName') &&
- ignoredPullRequest == pullRequest) {
- ignoreIsActive = true;
- break;
- }
- }
- } on FormatException catch(_) {
- print('Formatting error detected requesting ignores from Flutter Gold.\n'
- 'rawResponse: $rawResponse');
- rethrow;
- }
- },
- SkiaGoldHttpOverrides(),
- );
- return ignoreIsActive;
- }
-
- /// The [_expectations] retrieved from Flutter Gold do not include the
- /// parameters of the given test. This function queries the Flutter Gold
- /// details api to determine if the given expectation for a test matches the
- /// configuration of the executing machine.
- Future<bool> isValidDigestForExpectation(String expectation, String testName) async {
- bool isValid = false;
- testName = cleanTestName(testName);
- String rawResponse;
- await io.HttpOverrides.runWithHttpOverrides<Future<void>>(() async {
- final Uri requestForDigest = Uri.parse(
- 'https://flutter-gold.skia.org/json/details?test=$testName&digest=$expectation'
- );
-
- try {
- final io.HttpClientRequest request = await httpClient.getUrl(requestForDigest);
- final io.HttpClientResponse response = await request.close();
- rawResponse = await utf8.decodeStream(response);
- final Map<String, dynamic> skiaJson = json.decode(rawResponse);
- final SkiaGoldDigest digest = SkiaGoldDigest.fromJson(skiaJson['digest']);
- isValid = digest.isValid(platform, testName, expectation);
-
- } on FormatException catch(_) {
- print('Formatting error detected requesting digest from Flutter Gold.\n'
- 'rawResponse: $rawResponse');
- rethrow;
- }
- },
- SkiaGoldHttpOverrides(),
- );
- return isValid;
- }
-
/// Returns the current commit hash of the Flutter repository.
Future<String> _getCurrentCommit() async {
- if (!_flutterRoot.existsSync()) {
+ if (!flutterRoot.existsSync()) {
final StringBuffer buf = StringBuffer()
- ..writeln('Flutter root could not be found: $_flutterRoot');
+ ..writeln('Flutter root could not be found: $flutterRoot');
throw NonZeroExitCode(1, buf.toString());
} else {
final io.ProcessResult revParse = await process.run(
<String>['git', 'rev-parse', 'HEAD'],
- workingDirectory: _flutterRoot.path,
+ workingDirectory: flutterRoot.path,
);
return revParse.exitCode == 0 ? revParse.stdout.trim() : null;
}
@@ -347,85 +210,13 @@
);
}
- /// Removes the file extension from the [fileName] to represent the test name
- /// properly.
- String cleanTestName(String fileName) {
- return fileName.split(path.extension(fileName.toString()))[0];
- }
-
/// Returns a boolean value to prevent the client from re-authorizing itself
/// for multiple tests.
bool _clientIsAuthorized() {
- final File authFile = workDirectory?.childFile(fs.path.join(
+ final File authFile = _workDirectory?.childFile(super.fs.path.join(
'temp',
'auth_opt.json',
));
return authFile.existsSync();
}
}
-
-/// Used to make HttpRequests during testing.
-class SkiaGoldHttpOverrides extends io.HttpOverrides {}
-
-/// A digest returned from a request to the Flutter Gold dashboard.
-class SkiaGoldDigest {
- const SkiaGoldDigest({
- this.imageHash,
- this.paramSet,
- this.testName,
- this.status,
- });
-
- /// Create a digest from requested json.
- factory SkiaGoldDigest.fromJson(Map<String, dynamic> json) {
- if (json == null)
- return null;
-
- return SkiaGoldDigest(
- imageHash: json['digest'],
- paramSet: Map<String, dynamic>.from(json['paramset'] ??
- <String, String>{'Platform': 'none'}),
- testName: json['test'],
- status: json['status'],
- );
- }
-
- /// Unique identifier for the image associated with the digest.
- final String imageHash;
-
- /// Parameter set for the given test, e.g. Platform : Windows.
- final Map<String, dynamic> paramSet;
-
- /// Test name associated with the digest, e.g. positive or untriaged.
- final String testName;
-
- /// Status of the given digest, e.g. positive or untriaged.
- final String status;
-
- /// Validates a given digest against the current testing conditions.
- bool isValid(Platform platform, String name, String expectation) {
- return imageHash == expectation
- && paramSet['Platform'].contains(platform.operatingSystem)
- && testName == name
- && status == 'positive';
- }
- }
-
-/// Exception that signals a process' exit with a non-zero exit code.
-class NonZeroExitCode implements Exception {
- /// Create an exception that represents a non-zero exit code.
- ///
- /// The first argument must be non-zero.
- const NonZeroExitCode(this.exitCode, this.stderr) : assert(exitCode != 0);
-
- /// The code that the process will signal to the operating system.
- ///
- /// By definition, this is not zero.
- final int exitCode;
-
- /// The message to show on standard error.
- final String stderr;
-
- @override
- String toString() => 'Exit code $exitCode: $stderr';
-}
diff --git a/packages/flutter_test/lib/src/_goldens_io.dart b/packages/flutter_test/lib/src/_goldens_io.dart
index c8e11cb..902aa93 100644
--- a/packages/flutter_test/lib/src/_goldens_io.dart
+++ b/packages/flutter_test/lib/src/_goldens_io.dart
@@ -15,8 +15,8 @@
/// The default [GoldenFileComparator] implementation for `flutter test`.
///
-/// The term __golden file__ refers to a master image that is considered the
-/// true rendering of a given widget, state, application, or other visual
+/// The term __golden file__ refers to a master image that is considered the true
+/// rendering of a given widget, state, application, or other visual
/// representation you have chosen to capture. This comparator loads golden
/// files from the local file system, treating the golden key as a relative
/// path from the test file's directory.
@@ -53,7 +53,7 @@
/// implements.
/// * [matchesGoldenFile], the function from [flutter_test] that invokes the
/// comparator.
-class LocalFileComparator extends GoldenFileComparator with LocalComparisonOutput {
+class LocalFileComparator extends GoldenFileComparator {
/// Creates a new [LocalFileComparator] for the specified [testFile].
///
/// Golden file keys will be interpreted as file paths relative to the
@@ -90,18 +90,24 @@
Future<bool> 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"'
- );
+ throw test_package.TestFailure('Could not be compared against non-existent file: "$golden"');
}
final List<int> goldenBytes = await goldenFile.readAsBytes();
- final ComparisonResult result = GoldenFileComparator.compareLists(
- imageBytes,
- goldenBytes,
- );
+ final ComparisonResult result = GoldenFileComparator.compareLists(imageBytes, goldenBytes);
if (!result.passed) {
- generateFailureOutput(result, golden, basedir);
+ String additionalFeedback = '';
+ if (result.diffs != null) {
+ additionalFeedback = '\nFailure feedback can be found at ${path.join(basedir.path, 'failures')}';
+ final Map<String, Object> diffs = result.diffs;
+ diffs.forEach((String name, Object untypedImage) {
+ final Image image = untypedImage;
+ final File output = _getFailureFile(name, golden);
+ output.parent.createSync(recursive: true);
+ output.writeAsBytesSync(encodePng(image));
+ });
+ }
+ throw test_package.TestFailure('Golden "$golden": ${result.error}$additionalFeedback');
}
return result.passed;
}
@@ -116,50 +122,16 @@
File _getGoldenFile(Uri golden) {
return File(_path.join(_path.fromUri(basedir), _path.fromUri(golden.path)));
}
-}
-/// A class for use in golden file comparators that run locally and provide
-/// output.
-class LocalComparisonOutput {
- /// Writes out diffs from the [ComparisonResult] of a golden file test.
- ///
- /// Will throw an error if a null result is provided.
- void generateFailureOutput(
- ComparisonResult result,
- Uri golden,
- Uri basedir, {
- String key = '',
- }) {
- String additionalFeedback = '';
- if (result.diffs != null) {
- additionalFeedback = '\nFailure feedback can be found at '
- '${path.join(basedir.path, 'failures')}';
- final Map<String, Image> diffs = result.diffs;
- diffs.forEach((String name, Image image) {
- final File output = getFailureFile(
- key.isEmpty ? name : name + '_' + key,
- golden,
- basedir,
- );
- output.parent.createSync(recursive: true);
- output.writeAsBytesSync(encodePng(image));
- });
- }
- throw test_package.TestFailure(
- 'Golden "$golden": ${result.error}$additionalFeedback'
- );
- }
-
- /// Returns the appropriate file for a given diff from a [ComparisonResult].
- File getFailureFile(String failure, Uri golden, Uri basedir) {
+ File _getFailureFile(String failure, Uri golden) {
final String fileName = golden.pathSegments[0];
final String testName = fileName.split(path.extension(fileName))[0]
+ '_'
+ failure
+ '.png';
- return File(path.join(
- path.fromUri(basedir),
- path.fromUri(Uri.parse('failures/$testName')),
+ return File(_path.join(
+ _path.fromUri(basedir),
+ _path.fromUri(Uri.parse('failures/$testName')),
));
}
}
@@ -231,9 +203,7 @@
if (pixelDiffCount > 0) {
return ComparisonResult(
passed: false,
- error: 'Pixel test failed, '
- '${((pixelDiffCount/totalPixels) * 100).toStringAsFixed(2)}% '
- 'diff detected.',
+ error: 'Pixel test failed, ${((pixelDiffCount/totalPixels) * 100).toStringAsFixed(2)}% diff detected.',
diffs: diffs,
);
}
diff --git a/packages/flutter_test/lib/src/matchers.dart b/packages/flutter_test/lib/src/matchers.dart
index 11d9f9f..fd4bfce 100644
--- a/packages/flutter_test/lib/src/matchers.dart
+++ b/packages/flutter_test/lib/src/matchers.dart
@@ -303,7 +303,9 @@
/// The [key] may be either a [Uri] or a [String] representation of a URI.
///
/// The [version] is a number that can be used to differentiate historical
-/// golden files. This parameter is optional.
+/// golden files. This parameter is optional. Version numbers are used in golden
+/// file tests for package:flutter. You can learn more about these tests
+/// [here](https://github.com/flutter/flutter/wiki/Writing-a-golden-file-test-for-package:flutter).
///
/// This is an asynchronous matcher, meaning that callers should use
/// [expectLater] when using this matcher and await the future returned by
@@ -334,11 +336,8 @@
///
/// await expectLater(
/// imageFuture,
-/// matchesGoldenFile(
-/// 'save.png',
-/// version: 2,
-/// ),
-/// );
+/// matchesGoldenFile('save.png'),
+/// );
///
/// await expectLater(
/// find.byType(MyWidget),