Part 1: Skia Gold Testing (#33688)

* Fresh PR for Gold integration.

* Nits

* WIP

* Artifacts from merge

* Changed some platform dependencies for web, added library prefix notation for Skia Gold test names.

* Updating for CI implementation

* Write out service account

* Writing to skip out

* WIP

* ++

* Fixing depot tools deps

* Windows depot_tools

* Fixing setup scripts

* ++

* depot tools

* ++

* WIP

* Tracing depot_tools clone

* WIP

* ++

* analyzer

* WIP

* chrome typo

* copy artifact

* Working on tests

* Code cleanup

* ++

* Code cleanup, updated tests

* ++ review feedback

* Review

* Analyzer

* Review feedback

* Nits from review

* PRogress

* ++

* Fixing tests

* ++

* Testing repo route

* Just needing documention around new structures.

* cleanup

* Analyzer

* Documentation updates

* Documentation updates

* Cirrus updates

* cirrus nit

* Review feedback

* Review feedback

* Fixing skip comparator

* Fix base directory for Skia Gold case

* ++

* Feedback

* ++

* Fixed uri assertion

* Made GoldensClient abstract, altered SkiaGoldClient constructor

* Analyzer
diff --git a/.cirrus.yml b/.cirrus.yml
index 204112c..5baaa6c 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -72,7 +72,11 @@
       env:
         GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb]
         SHARD: tests
+        DEPOT_TOOLS: "tmp/depot_tools"
+        GOLDCTL: "$DEPOT_TOOLS/goldctl"
+        GOLD_SERVICE_ACCOUNT: ENCRYPTED[3afeea5ac7201151c3d0dc9648862f0462b5e4f55dc600ca8b692319622f7c3eda3d577b1b16cc2ef0311b7314c1c095]
         SUBSHARD: framework_other
+      goldctl_script: ./dev/bots/download_goldctl.sh
       test_script:
         - dart --enable-asserts ./dev/bots/test.dart
       container:
@@ -317,6 +321,9 @@
         GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb]
         SHARD: tests
         SUBSHARD: framework_other
+        GOLDCTL: "C:\\Windows\\Temp\\goldctl_tool\\goldctl.exe"
+        GOLD_SERVICE_ACCOUNT: ENCRYPTED[3afeea5ac7201151c3d0dc9648862f0462b5e4f55dc600ca8b692319622f7c3eda3d577b1b16cc2ef0311b7314c1c095]
+      goldctl_script: powershell dev\bots\download_goldctl.ps1
       test_all_script:
         - dart --enable-asserts dev\bots\test.dart
     - name: tests_extras-windows
@@ -493,6 +500,10 @@
         GCLOUD_SERVICE_ACCOUNT_KEY: ENCRYPTED[f12abe60f5045d619ef4c79b83dd1e0722a0b0b13dbea95fbe334e2db7fffbcd841a5a92da8824848b539a19afe0c9fb]
         SHARD: tests
         SUBSHARD: extras
+        DEPOT_TOOLS: "tmp/depot_tools"
+        GOLDCTL: "$DEPOT_TOOLS/goldctl"
+        GOLD_SERVICE_ACCOUNT: ENCRYPTED[3afeea5ac7201151c3d0dc9648862f0462b5e4f55dc600ca8b692319622f7c3eda3d577b1b16cc2ef0311b7314c1c095]
+      goldctl_script: ./dev/bots/download_goldctl.sh
       test_all_script:
         - ulimit -S -n 2048 # https://github.com/flutter/flutter/issues/2976
         - dart --enable-asserts dev/bots/test.dart
diff --git a/dev/bots/download_goldctl.ps1 b/dev/bots/download_goldctl.ps1
new file mode 100644
index 0000000..b983539
--- /dev/null
+++ b/dev/bots/download_goldctl.ps1
@@ -0,0 +1,5 @@
+$url= "https://chrome-infra-packages.appspot.com/p/skia/tools/goldctl/windows-amd64/+/"
+$path = "c:\Windows\Temp\goldctl.zip"
+
+(New-Object System.Net.WebClient).DownloadFile($path, $output)
+Expand-Archive -LiteralPath $path -DestinationPath "C:\Windows\Temp\goldctl_tool"
diff --git a/dev/bots/download_goldctl.sh b/dev/bots/download_goldctl.sh
new file mode 100755
index 0000000..9ef81cc
--- /dev/null
+++ b/dev/bots/download_goldctl.sh
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+
+git clone --depth 1 https://chromium.googlesource.com/chromium/tools/depot_tools.git ./tmp/depot_tools
+cd tmp/depot_tools
+echo -e '# Ensure File\n$ServiceURL https://chrome-infra-packages.appspot.com\n\n# Skia Gold Client goldctl\nskia/tools/goldctl/${platform} latest' > ensure.txt
+./cipd ensure -ensure-file ensure.txt -root .
diff --git a/packages/flutter/test/cupertino/date_picker_test.dart b/packages/flutter/test/cupertino/date_picker_test.dart
index ae90212..0b8908f 100644
--- a/packages/flutter/test/cupertino/date_picker_test.dart
+++ b/packages/flutter/test/cupertino/date_picker_test.dart
@@ -847,7 +847,6 @@
           'date_picker_test.datetime.initial.png',
           version: 1,
         ),
-        skip: !isLinux
       );
 
       // Slightly drag the hour component to make the current hour off-center.
@@ -860,7 +859,6 @@
           'date_picker_test.datetime.drag.png',
           version: 1,
         ),
-        skip: !isLinux
       );
     });
   });
diff --git a/packages/flutter/test/cupertino/nav_bar_test.dart b/packages/flutter/test/cupertino/nav_bar_test.dart
index 96afba3..56afa4a 100644
--- a/packages/flutter/test/cupertino/nav_bar_test.dart
+++ b/packages/flutter/test/cupertino/nav_bar_test.dart
@@ -805,9 +805,6 @@
         ),
       );
     },
-    // TODO(xster): remove once https://github.com/flutter/flutter/issues/17483
-    // is fixed.
-    skip: !isLinux,
   );
 
   testWidgets(
@@ -842,10 +839,7 @@
         ),
       );
     },
-    // TODO(xster): remove once https://github.com/flutter/flutter/issues/17483
-    // is fixed.
-    skip: !isLinux,
-   );
+  );
 
 
   testWidgets('NavBar draws a light system bar for a dark background', (WidgetTester tester) async {
diff --git a/packages/flutter/test/cupertino/segmented_control_test.dart b/packages/flutter/test/cupertino/segmented_control_test.dart
index 4cacef6..71556c8 100644
--- a/packages/flutter/test/cupertino/segmented_control_test.dart
+++ b/packages/flutter/test/cupertino/segmented_control_test.dart
@@ -1417,7 +1417,7 @@
         version: 0,
       ),
     );
-  }, skip: !isLinux);
+  });
 
   testWidgets('Golden Test Pressed State', (WidgetTester tester) async {
     final Map<int, Widget> children = <int, Widget>{};
@@ -1458,5 +1458,5 @@
         version: 0,
       ),
     );
-  }, skip: !isLinux);
+  });
 }
diff --git a/packages/flutter/test/cupertino/text_field_test.dart b/packages/flutter/test/cupertino/text_field_test.dart
index a4ebbd1..dcdc751 100644
--- a/packages/flutter/test/cupertino/text_field_test.dart
+++ b/packages/flutter/test/cupertino/text_field_test.dart
@@ -481,7 +481,7 @@
         version: 2,
       ),
     );
-  }, skip: !isLinux);
+  });
 
   testWidgets('Cupertino cursor iOS golden', (WidgetTester tester) async {
     debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
@@ -514,7 +514,7 @@
         version: 2,
       ),
     );
-  }, skip: !isLinux);
+  });
 
   testWidgets(
     'can control text content via controller',
@@ -2901,7 +2901,6 @@
         'text_field_test.disabled.png',
         version: 0,
       ),
-      skip: !isLinux,
     );
   });
 
diff --git a/packages/flutter/test/material/bottom_app_bar_test.dart b/packages/flutter/test/material/bottom_app_bar_test.dart
index 4a07398..7f1b3dc 100644
--- a/packages/flutter/test/material/bottom_app_bar_test.dart
+++ b/packages/flutter/test/material/bottom_app_bar_test.dart
@@ -75,7 +75,6 @@
         'bottom_app_bar.custom_shape.1.png',
         version: null,
       ),
-      skip: !isLinux,
     );
     await pump(FloatingActionButtonLocation.centerDocked);
     await tester.pumpAndSettle();
@@ -85,7 +84,6 @@
         'bottom_app_bar.custom_shape.2.png',
         version: null,
       ),
-      skip: !isLinux,
     );
   }, 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 4bbb597..3b4cd47 100644
--- a/packages/flutter/test/material/bottom_app_bar_theme_test.dart
+++ b/packages/flutter/test/material/bottom_app_bar_theme_test.dart
@@ -84,7 +84,6 @@
         'bottom_app_bar_theme.custom_shape.png',
         version: null,
       ),
-      skip: !isLinux,
     );
   }, 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 e8b277c..d8bc3a7 100644
--- a/packages/flutter/test/material/bottom_navigation_bar_test.dart
+++ b/packages/flutter/test/material/bottom_navigation_bar_test.dart
@@ -1438,7 +1438,6 @@
           'bottom_navigation_bar.shifting_transition.$pump.png',
           version: 2,
         ),
-        skip: !isLinux,
       );
     }
   }, skip: isBrowser);
diff --git a/packages/flutter/test/material/card_theme_test.dart b/packages/flutter/test/material/card_theme_test.dart
index 8dedd6c..abdf8cc 100644
--- a/packages/flutter/test/material/card_theme_test.dart
+++ b/packages/flutter/test/material/card_theme_test.dart
@@ -141,7 +141,6 @@
         'card_theme.custom_shape.png',
         version: null,
       ),
-      skip: !isLinux,
     );
   },  skip: isBrowser);
 }
diff --git a/packages/flutter/test/material/dialog_theme_test.dart b/packages/flutter/test/material/dialog_theme_test.dart
index 64378ab..133ecc1 100644
--- a/packages/flutter/test/material/dialog_theme_test.dart
+++ b/packages/flutter/test/material/dialog_theme_test.dart
@@ -134,7 +134,6 @@
         'dialog_theme.dialog_with_custom_border.png',
         version: null,
       ),
-      skip: !isLinux,
     );
   }, skip: isBrowser);
 
diff --git a/packages/flutter/test/material/dropdown_test.dart b/packages/flutter/test/material/dropdown_test.dart
index b16d9bc..03c480c 100644
--- a/packages/flutter/test/material/dropdown_test.dart
+++ b/packages/flutter/test/material/dropdown_test.dart
@@ -144,7 +144,6 @@
         'dropdown_test.default.png',
         version: 0,
       ),
-      skip: !isLinux,
     );
   }, skip: isBrowser);
 
@@ -160,7 +159,6 @@
         'dropdown_test.expanded.png',
         version: 0,
       ),
-      skip: !isLinux,
     );
   }, 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 8b89ba8..5be8270 100644
--- a/packages/flutter/test/material/floating_action_button_test.dart
+++ b/packages/flutter/test/material/floating_action_button_test.dart
@@ -740,7 +740,6 @@
         'floating_action_button_test.clip.png',
         version: 2,
       ),
-      skip: !isLinux,
     );
   });
 
diff --git a/packages/flutter/test/material/input_decorator_test.dart b/packages/flutter/test/material/input_decorator_test.dart
index 4051825..1d35a7d 100644
--- a/packages/flutter/test/material/input_decorator_test.dart
+++ b/packages/flutter/test/material/input_decorator_test.dart
@@ -2844,7 +2844,6 @@
           'input_decorator.outline_icon_label.ltr.png',
           version: null,
         ),
-        skip: !isLinux,
       );
 
       await tester.pumpWidget(buildFrame(TextDirection.rtl));
@@ -2854,10 +2853,8 @@
           'input_decorator.outline_icon_label.rtl.png',
           version: null,
         ),
-        skip: !isLinux,
       );
     },
-    skip: !isLinux,
   );
 
   testWidgets('InputDecorator draws and animates hoverColor', (WidgetTester tester) async {
diff --git a/packages/flutter/test/material/material_test.dart b/packages/flutter/test/material/material_test.dart
index cb5b103..dac7865 100644
--- a/packages/flutter/test/material/material_test.dart
+++ b/packages/flutter/test/material/material_test.dart
@@ -620,7 +620,6 @@
           'material.border_paint_above.png',
           version: null,
         ),
-        skip: !isLinux,
       );
     }, skip: isBrowser);
 
@@ -664,7 +663,6 @@
           'material.border_paint_below.png',
           version: null,
         ),
-        skip: !isLinux,
       );
     }, skip: isBrowser);
   });
diff --git a/packages/flutter/test/material/radio_test.dart b/packages/flutter/test/material/radio_test.dart
index 29d8748..bf817a5 100644
--- a/packages/flutter/test/material/radio_test.dart
+++ b/packages/flutter/test/material/radio_test.dart
@@ -280,7 +280,6 @@
         'radio.ink_ripple.png',
         version: null,
       ),
-      skip: !isLinux,
     );
   }, 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 f17d4e0..bb97c5d 100644
--- a/packages/flutter/test/material/tab_bar_theme_test.dart
+++ b/packages/flutter/test/material/tab_bar_theme_test.dart
@@ -271,7 +271,6 @@
         'tab_bar_theme.tab_indicator_size_tab.png',
         version: null,
       ),
-      skip: !isLinux,
     );
   }, skip: isBrowser);
 
@@ -286,7 +285,6 @@
         'tab_bar_theme.tab_indicator_size_label.png',
         version: null,
       ),
-      skip: !isLinux,
     );
   }, skip: isBrowser);
 
@@ -306,7 +304,6 @@
         'tab_bar_theme.custom_tab_indicator.png',
         version: null,
       ),
-      skip: !isLinux,
     );
   }, skip: isBrowser);
 
@@ -326,7 +323,6 @@
         'tab_bar_theme.beveled_rect_indicator.png',
         version: null,
       ),
-      skip: !isLinux,
     );
   }, skip: isBrowser);
 }
diff --git a/packages/flutter/test/material/text_field_test.dart b/packages/flutter/test/material/text_field_test.dart
index a432de8..0f0b0d7 100644
--- a/packages/flutter/test/material/text_field_test.dart
+++ b/packages/flutter/test/material/text_field_test.dart
@@ -416,7 +416,7 @@
         version: 0,
       ),
     );
-  }, skip: !isLinux);
+  });
 
   testWidgets('Material cursor iOS golden', (WidgetTester tester) async {
     debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
@@ -448,7 +448,7 @@
         version: 0,
       ),
     );
-  }, skip: !isLinux);
+  });
 
   testWidgets('text field selection toolbar renders correctly inside opacity', (WidgetTester tester) async {
     await tester.pumpWidget(
@@ -501,7 +501,6 @@
         'text_field_opacity_test.0.png',
         version: 2,
       ),
-      skip: !isLinux,
     );
   }, 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 70e4526..f644fb0 100644
--- a/packages/flutter/test/painting/continous_rectangle_border_test.dart
+++ b/packages/flutter/test/painting/continous_rectangle_border_test.dart
@@ -75,7 +75,6 @@
         'continuous_rectangle_border.golden_test_even_radii.png',
         version: null,
       ),
-      skip: !isLinux,
     );
   }, skip: isBrowser);
 
@@ -100,7 +99,6 @@
         'continuous_rectangle_border.golden_test_varying_radii.png',
         version: null,
       ),
-      skip: !isLinux,
     );
   }, skip: isBrowser);
 
@@ -122,7 +120,6 @@
         'continuous_rectangle_border.golden_test_large_radii.png',
         version: null,
       ),
-      skip: !isLinux,
     );
   }, skip: isBrowser);
 
diff --git a/packages/flutter/test/rendering/localized_fonts_test.dart b/packages/flutter/test/rendering/localized_fonts_test.dart
index 025bb56..e269699 100644
--- a/packages/flutter/test/rendering/localized_fonts_test.dart
+++ b/packages/flutter/test/rendering/localized_fonts_test.dart
@@ -55,7 +55,6 @@
         ),
       );
     },
-    skip: !isLinux,
   );
 
   testWidgets(
@@ -110,7 +109,6 @@
         ),
       );
     },
-    skip: !isLinux,
   );
 
   testWidgets(
@@ -157,7 +155,6 @@
         ),
       );
     },
-    skip: !isLinux,
   );
 
 }
diff --git a/packages/flutter/test/widgets/backdrop_filter_test.dart b/packages/flutter/test/widgets/backdrop_filter_test.dart
index c728f50..db339be 100644
--- a/packages/flutter/test/widgets/backdrop_filter_test.dart
+++ b/packages/flutter/test/widgets/backdrop_filter_test.dart
@@ -47,7 +47,6 @@
         'backdrop_filter_test.cull_rect.png',
         version: 1,
       ),
-      skip: !isLinux,
     );
   }, skip: isBrowser);
 }
diff --git a/packages/flutter/test/widgets/editable_text_cursor_test.dart b/packages/flutter/test/widgets/editable_text_cursor_test.dart
index 08c2d0f..3c72be4 100644
--- a/packages/flutter/test/widgets/editable_text_cursor_test.dart
+++ b/packages/flutter/test/widgets/editable_text_cursor_test.dart
@@ -95,7 +95,7 @@
         version: 3,
       ),
     );
-  }, skip: !isLinux);
+  });
 
   testWidgets('cursor layout has correct radius', (WidgetTester tester) async {
     final GlobalKey<EditableTextState> editableTextKey = GlobalKey<EditableTextState>();
@@ -149,7 +149,7 @@
         version: 3,
       ),
     );
-  }, skip: !isLinux);
+  });
 
   testWidgets('Cursor animates on iOS', (WidgetTester tester) async {
     final Widget widget = MaterialApp(
@@ -759,6 +759,5 @@
       ),
     );
     debugDefaultTargetPlatformOverride = null;
-  }, skip: !isLinux);
-
+  });
 }
diff --git a/packages/flutter/test/widgets/invert_colors_test.dart b/packages/flutter/test/widgets/invert_colors_test.dart
index bedb76c..8489f6e 100644
--- a/packages/flutter/test/widgets/invert_colors_test.dart
+++ b/packages/flutter/test/widgets/invert_colors_test.dart
@@ -24,7 +24,6 @@
         'invert_colors_test.0.png',
         version: null,
       ),
-      skip: !isLinux,
     );
   }, skip: isBrowser);
 
@@ -46,7 +45,6 @@
         'invert_colors_test.1.png',
         version: null,
       ),
-      skip: !isLinux,
     );
   }, 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 eacb3e5..b52e538 100644
--- a/packages/flutter/test/widgets/list_wheel_scroll_view_test.dart
+++ b/packages/flutter/test/widgets/list_wheel_scroll_view_test.dart
@@ -539,7 +539,6 @@
           'list_wheel_scroll_view.center_child.magnified.png',
           version: null,
         ),
-        skip: !isLinux,
       );
     }, skip: isBrowser);
 
@@ -597,7 +596,6 @@
           'list_wheel_scroll_view.curved_wheel.left.png',
           version: null,
         ),
-        skip: !isLinux,
       );
     }, skip: isBrowser);
 
diff --git a/packages/flutter/test/widgets/opacity_test.dart b/packages/flutter/test/widgets/opacity_test.dart
index 0446459..1d173b3 100644
--- a/packages/flutter/test/widgets/opacity_test.dart
+++ b/packages/flutter/test/widgets/opacity_test.dart
@@ -181,7 +181,6 @@
         'opacity_test.offset.png',
         version: 1,
       ),
-      skip: !isLinux,
     );
   }, skip: isBrowser);
 
diff --git a/packages/flutter/test/widgets/physical_model_test.dart b/packages/flutter/test/widgets/physical_model_test.dart
index c425fb4..5e043f8 100644
--- a/packages/flutter/test/widgets/physical_model_test.dart
+++ b/packages/flutter/test/widgets/physical_model_test.dart
@@ -114,7 +114,6 @@
         'physical_model_overflow.png',
         version: null,
       ),
-      skip: !isLinux,
     );
   }, skip: isBrowser);
 
diff --git a/packages/flutter/test/widgets/shadow_test.dart b/packages/flutter/test/widgets/shadow_test.dart
index bf5d6e2..f573974 100644
--- a/packages/flutter/test/widgets/shadow_test.dart
+++ b/packages/flutter/test/widgets/shadow_test.dart
@@ -37,8 +37,7 @@
         'shadow.BoxDecoration.enabled.png',
         version: null,
       ),
-      skip: !isLinux
-    ); // shadows render differently on different platforms
+    );
     debugDisableShadows = true;
   }, skip: isBrowser);
 
@@ -70,7 +69,7 @@
       );
     }
     debugDisableShadows = true;
-  }, skip: !isLinux); // shadows render differently on different platforms
+  });
 
   testWidgets('Shadows with PhysicalLayer', (WidgetTester tester) async {
     await tester.pumpWidget(
@@ -107,8 +106,7 @@
         'shadow.PhysicalModel.enabled.png',
         version: null,
       ),
-      skip: !isLinux,
-    ); // shadows render differently on different platforms
+    );
     debugDisableShadows = true;
   }, skip: isBrowser);
 
@@ -144,5 +142,5 @@
       );
     }
     debugDisableShadows = true;
-  }, skip: !isLinux); // shadows render differently on different platforms
+  });
 }
diff --git a/packages/flutter/test/widgets/text_golden_test.dart b/packages/flutter/test/widgets/text_golden_test.dart
index ce798a0..4b0d29a 100644
--- a/packages/flutter/test/widgets/text_golden_test.dart
+++ b/packages/flutter/test/widgets/text_golden_test.dart
@@ -62,7 +62,7 @@
         version: null,
       ),
     );
-  }, skip: !isLinux);
+  });
 
 
   testWidgets('Text Foreground', (WidgetTester tester) async {
@@ -147,7 +147,7 @@
         version: null,
       ),
     );
-  }, skip: !isLinux);
+  });
 
   // TODO(garyq): This test requires an update when the background
   // drawing from the beginning of the line bug is fixed. The current
@@ -200,7 +200,7 @@
         version: null,
       ),
     );
-  }, skip: !isLinux);
+  });
 
   testWidgets('Text Fade', (WidgetTester tester) async {
     await tester.pumpWidget(
@@ -239,7 +239,7 @@
         version: 1,
       ),
     );
-  }, skip: !isLinux);
+  });
 
   testWidgets('Default Strut text', (WidgetTester tester) async {
     await tester.pumpWidget(
@@ -267,8 +267,7 @@
         version: null,
       ),
     );
-  }, skip: true); // Should only be on linux (skip: !isLinux).
-                  // Disabled for now until font inconsistency is resolved.
+  });
 
   testWidgets('Strut text 1', (WidgetTester tester) async {
     await tester.pumpWidget(
@@ -298,8 +297,7 @@
         version: 1,
       ),
     );
-  }, skip: true); // Should only be on linux (skip: !isLinux).
-                  // Disabled for now until font inconsistency is resolved.
+  });
 
   testWidgets('Strut text 2', (WidgetTester tester) async {
     await tester.pumpWidget(
@@ -330,8 +328,7 @@
         version: 1,
       ),
     );
-  }, skip: true); // Should only be on linux (skip: !isLinux).
-                  // Disabled for now until font inconsistency is resolved.
+  });
 
   testWidgets('Strut text rich', (WidgetTester tester) async {
     await tester.pumpWidget(
@@ -385,8 +382,7 @@
         version: 1,
       ),
     );
-  }, skip: true); // Should only be on linux (skip: !isLinux).
-                  // Disabled for now until font inconsistency is resolved.
+  });
 
   testWidgets('Strut text font fallback', (WidgetTester tester) async {
     // Font Fallback
@@ -424,8 +420,7 @@
         version: 1,
       ),
     );
-  }, skip: true); // Should only be on linux (skip: !isLinux).
-                  // Disabled for now until font inconsistency is resolved.
+  });
 
   testWidgets('Strut text rich forceStrutHeight', (WidgetTester tester) async {
     await tester.pumpWidget(
@@ -479,8 +474,7 @@
         version: 1,
       ),
     );
-  }, skip: true); // Should only be on linux (skip: !isLinux).
-                  // Disabled for now until font inconsistency is resolved.
+  });
 
   testWidgets('Decoration thickness', (WidgetTester tester) async {
     final TextDecoration allDecorations = TextDecoration.combine(
@@ -521,7 +515,7 @@
         version: 0,
       ),
     );
-  }, skip: !isLinux); // Coretext uses different thicknesses for decoration
+  });
 
   testWidgets('Decoration thickness', (WidgetTester tester) async {
     final TextDecoration allDecorations = TextDecoration.combine(
@@ -563,7 +557,7 @@
         version: 1,
       ),
     );
-  }, skip: !isLinux); // Coretext uses different thicknesses for decoration
+  });
 
   testWidgets('Text Inline widget', (WidgetTester tester) async {
     await tester.pumpWidget(
@@ -660,7 +654,7 @@
         version: 1,
       ),
     );
-  }, skip: !isLinux); // Coretext uses different thicknesses for decoration
+  });
 
   testWidgets('Text Inline widget textfield', (WidgetTester tester) async {
     await tester.pumpWidget(
@@ -708,7 +702,7 @@
         version: 2,
       ),
     );
-  }, skip: !isLinux); // Coretext uses different thicknesses for decoration
+  });
 
   // This tests if multiple Text.rich widgets are able to inline nest within each other.
   testWidgets('Text Inline widget nesting', (WidgetTester tester) async {
@@ -840,7 +834,7 @@
         version: 2,
       ),
     );
-  }, skip: !isLinux); // Coretext uses different thicknesses for decoration
+  });
 
   testWidgets('Text Inline widget baseline', (WidgetTester tester) async {
     await tester.pumpWidget(
@@ -950,7 +944,7 @@
         version: 1,
       ),
     );
-  }, skip: !isLinux); // Coretext uses different thicknesses for decoration
+  });
 
   testWidgets('Text Inline widget aboveBaseline', (WidgetTester tester) async {
     await tester.pumpWidget(
@@ -1060,7 +1054,7 @@
         version: 1,
       ),
     );
-  }, skip: !isLinux); // Coretext uses different thicknesses for decoration
+  });
 
   testWidgets('Text Inline widget belowBaseline', (WidgetTester tester) async {
     await tester.pumpWidget(
@@ -1170,7 +1164,7 @@
         version: 1,
       ),
     );
-  }, skip: !isLinux); // Coretext uses different thicknesses for decoration
+  });
 
   testWidgets('Text Inline widget top', (WidgetTester tester) async {
     await tester.pumpWidget(
@@ -1280,7 +1274,7 @@
         version: 1,
       ),
     );
-  }, skip: !isLinux); // Coretext uses different thicknesses for decoration
+  });
 
   testWidgets('Text Inline widget middle', (WidgetTester tester) async {
     await tester.pumpWidget(
@@ -1390,5 +1384,5 @@
         version: 1,
       ),
     );
-  }, skip: !isLinux); // Coretext uses different thicknesses for decoration
+  });
 }
diff --git a/packages/flutter/test/widgets/widget_inspector_test.dart b/packages/flutter/test/widgets/widget_inspector_test.dart
index c77591d..09ea5f4 100644
--- a/packages/flutter/test/widgets/widget_inspector_test.dart
+++ b/packages/flutter/test/widgets/widget_inspector_test.dart
@@ -2028,7 +2028,6 @@
           'inspector.repaint_boundary_margin.png',
           version: null,
         ),
-        skip: !isLinux,
       );
 
       // Regression test for how rendering with a pixel scale other than 1.0
@@ -2042,7 +2041,6 @@
           'inspector.repaint_boundary_margin_small.png',
           version: null,
         ),
-        skip: !isLinux,
       );
 
       await expectLater(
@@ -2054,7 +2052,6 @@
           'inspector.repaint_boundary_margin_large.png',
           version: null,
         ),
-        skip: !isLinux,
       );
 
       final Layer layerParent = layer.parent;
@@ -2073,7 +2070,6 @@
           'inspector.repaint_boundary.png',
           version: null,
         ),
-        skip: !isLinux,
       );
 
       // Verify that taking a screenshot didn't change the layers associated with
@@ -2094,7 +2090,6 @@
           'inspector.repaint_boundary_margin.png',
           version: null,
         ),
-        skip: !isLinux,
       );
 
       // Verify that taking a screenshot didn't change the layers associated with
@@ -2118,7 +2113,6 @@
           'inspector.repaint_boundary_debugPaint.png',
           version: null,
         ),
-        skip: !isLinux,
       );
       // Verify that taking a screenshot with debug paint on did not change
       // the number of children the layer has.
@@ -2132,7 +2126,6 @@
           'inspector.repaint_boundary.png',
           version: null,
         ),
-        skip: !isLinux,
       );
 
       expect(renderObject.debugLayer, equals(layer));
@@ -2149,7 +2142,6 @@
           'inspector.container.png',
           version: null,
         ),
-        skip: !isLinux,
       );
 
       await expectLater(
@@ -2163,7 +2155,6 @@
           'inspector.container_debugPaint.png',
           version: null,
         ),
-        skip: !isLinux,
       );
 
       {
@@ -2187,7 +2178,6 @@
             'inspector.container_debugPaint.png',
             version: null,
           ),
-          skip: !isLinux,
         );
         expect(container.debugNeedsLayout, isFalse);
       }
@@ -2203,7 +2193,6 @@
           'inspector.container_small.png',
           version: null,
         ),
-        skip: !isLinux,
       );
 
       await expectLater(
@@ -2217,7 +2206,6 @@
           'inspector.container_large.png',
           version: null,
         ),
-        skip: !isLinux,
       );
 
       // This screenshot will show the clip rect debug paint but no other
@@ -2233,7 +2221,6 @@
           'inspector.clipRect_debugPaint.png',
           version: null,
         ),
-        skip: !isLinux,
       );
 
       final Element clipRect = find.byType(ClipRRect).evaluate().single;
@@ -2253,7 +2240,6 @@
           'inspector.clipRect_debugPaint_margin.png',
           version: null,
         ),
-        skip: !isLinux,
       );
 
       // Verify we get the same image if we go through the service extension
@@ -2296,7 +2282,6 @@
           'inspector.padding_debugPaint.png',
           version: null,
         ),
-        skip: !isLinux,
       );
 
       // The bounds for this box crop its rendered content.
@@ -2311,7 +2296,6 @@
           'inspector.sizedBox_debugPaint.png',
           version: 1,
         ),
-        skip: !isLinux,
       );
 
       // Verify that setting a margin includes the previously cropped content.
@@ -2327,7 +2311,6 @@
           'inspector.sizedBox_debugPaint_margin.png',
           version: null,
         ),
-        skip: !isLinux,
       );
     }, skip: isBrowser);
 
@@ -2402,7 +2385,6 @@
           'inspector.composited_transform.only_offsets.png',
           version: null,
         ),
-        skip: !isLinux,
       );
 
       await expectLater(
@@ -2415,7 +2397,6 @@
           'inspector.composited_transform.only_offsets_follower.png',
           version: null,
         ),
-        skip: !isLinux,
       );
 
       await expectLater(
@@ -2424,7 +2405,6 @@
           'inspector.composited_transform.only_offsets_small.png',
           version: 1,
         ),
-        skip: !isLinux,
       );
 
       await expectLater(
@@ -2437,7 +2417,6 @@
           'inspector.composited_transform.only_offsets_target.png',
           version: null,
         ),
-        skip: !isLinux,
       );
     }, skip: isBrowser);
 
@@ -2513,7 +2492,6 @@
           'inspector.composited_transform.with_rotations.png',
           version: null,
         ),
-        skip: !isLinux,
       );
 
       await expectLater(
@@ -2526,7 +2504,6 @@
           'inspector.composited_transform.with_rotations_small.png',
           version: null,
         ),
-        skip: !isLinux,
       );
 
       await expectLater(
@@ -2539,7 +2516,6 @@
           'inspector.composited_transform.with_rotations_target.png',
           version: null,
         ),
-        skip: !isLinux,
       );
 
       await expectLater(
@@ -2552,7 +2528,6 @@
           'inspector.composited_transform.with_rotations_follower.png',
           version: null,
         ),
-        skip: !isLinux,
       );
 
       // Make sure taking screenshots hasn't modified the positions of the
diff --git a/packages/flutter_goldens/lib/flutter_goldens.dart b/packages/flutter_goldens/lib/flutter_goldens.dart
index 4f28df3..3784c0d 100644
--- a/packages/flutter_goldens/lib/flutter_goldens.dart
+++ b/packages/flutter_goldens/lib/flutter_goldens.dart
@@ -9,19 +9,88 @@
 import 'package:file/local.dart';
 import 'package:flutter_test/flutter_test.dart';
 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';
 
 /// 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.
+/// works for the current test. _Which_ FlutterGoldenFileComparator is
+/// instantiated is based on the current testing environment.
 Future<void> main(FutureOr<void> testMain()) async {
-  goldenFileComparator = await FlutterGoldenFileComparator.fromDefaultComparator();
+  const Platform platform = LocalPlatform();
+  if (FlutterSkiaGoldFileComparator.isAvailableOnPlatform(platform)) {
+    goldenFileComparator = await FlutterSkiaGoldFileComparator.fromDefaultComparator();
+  } else if (FlutterGoldensRepositoryFileComparator.isAvailableOnPlatform(platform)) {
+    goldenFileComparator = await FlutterGoldensRepositoryFileComparator.fromDefaultComparator();
+  } else {
+    goldenFileComparator = FlutterSkippingGoldenFileComparator.fromDefaultComparator();
+  }
   await testMain();
 }
 
-/// A golden file comparator specific to the `flutter/flutter` repository.
+/// Abstract base class golden file comparator specific to the `flutter/flutter`
+/// repository.
+abstract class FlutterGoldenFileComparator extends GoldenFileComparator {
+  /// Creates a [FlutterGoldenFileComparator] that will resolve golden file
+  /// URIs relative to the specified [basedir].
+  ///
+  /// 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.fs = const LocalFileSystem(),
+    this.platform = const LocalPlatform(),
+  }) : assert(basedir != null),
+       assert(fs != null),
+       assert(platform != null);
+
+  /// The directory to which golden file URIs will be resolved in [compare] and
+  /// [update].
+  final Uri basedir;
+
+  /// The file system used to perform file access.
+  @visibleForTesting
+  final FileSystem fs;
+
+  /// A wrapper for the [dart:io.Platform] API.
+  @visibleForTesting
+  final Platform platform;
+
+  @override
+  Future<void> update(Uri golden, Uint8List imageBytes) async {
+    final File goldenFile = getGoldenFile(golden);
+    await goldenFile.parent.create(recursive: true);
+    await goldenFile.writeAsBytes(imageBytes, flush: true);
+  }
+
+  /// Calculate the appropriate basedir for the current test context.
+  @protected
+  @visibleForTesting
+  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: 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;
+  }
+}
+
+/// 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
@@ -29,59 +98,61 @@
 /// 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, then perform the comparison against
-/// the files therein.
-class FlutterGoldenFileComparator implements GoldenFileComparator {
-  /// Creates a [FlutterGoldenFileComparator] that will resolve golden file
-  /// URIs relative to the specified [basedir].
+/// 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] parameter exists for testing purposes only.
-  @visibleForTesting
-  FlutterGoldenFileComparator(
-    this.basedir, {
-    this.fs = const LocalFileSystem(),
-  });
+  /// 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,
+  );
 
-  /// The directory to which golden file URIs will be resolved in [compare] and [update].
-  final Uri basedir;
-
-  /// The file system used to perform file access.
-  @visibleForTesting
-  final FileSystem fs;
-
-  /// Creates a new [FlutterGoldenFileComparator] that mirrors the relative
-  /// path resolution of the default [goldenFileComparator].
+  /// 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 use.
+  /// repository is guaranteed to be ready to use.
   ///
   /// The [goldens] and [defaultComparator] parameters are visible for testing
   /// purposes only.
-  static Future<FlutterGoldenFileComparator> fromDefaultComparator({
-    GoldensClient goldens,
+  static Future<FlutterGoldensRepositoryFileComparator> fromDefaultComparator({
+    GoldensRepositoryClient goldens,
     LocalFileComparator defaultComparator,
   }) async {
     defaultComparator ??= goldenFileComparator;
 
     // Prepare the goldens repo.
-    goldens ??= GoldensClient();
+    goldens ??= GoldensRepositoryClient();
     await goldens.prepare();
 
-    // Calculate the appropriate basedir for the current test context.
-    final FileSystem fs = goldens.fs;
-    final Directory testDirectory = fs.directory(defaultComparator.basedir);
-    final String testDirectoryRelativePath = fs.path.relative(testDirectory.path, from: goldens.flutterRoot.path);
-    return FlutterGoldenFileComparator(goldens.repositoryRoot.childDirectory(testDirectoryRelativePath).uri);
+    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);
+    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();
-    // TODO(tvolkert): Improve the intelligence of this comparison.
     if (goldenBytes.length != imageBytes.length) {
       return false;
     }
@@ -93,14 +164,130 @@
     return true;
   }
 
-  @override
-  Future<void> update(Uri golden, Uint8List imageBytes) async {
-    final File goldenFile = _getGoldenFile(golden);
-    await goldenFile.parent.create(recursive: true);
-    await goldenFile.writeAsBytes(imageBytes, flush: true);
+  /// 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.
+///
+/// For testing across all platforms, the [SkiaGoldClient] is used to upload
+/// images for framework-related golden tests and process results. Currently
+/// these tests are designed to be run post-submit on Cirrus CI, informed by the
+/// environment.
+///
+/// See also:
+///
+///  * [GoldenFileComparator], the abstract class that
+///    [FlutterGoldenFileComparator] implements.
+///  * [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 useful in tests, where the default file
+  /// system and platform can be replaced by mock instances.
+  FlutterSkiaGoldFileComparator(
+    final Uri basedir,
+    this.skiaClient, {
+    FileSystem fs = const LocalFileSystem(),
+    Platform platform = const LocalPlatform(),
+  }) : super(
+    basedir,
+    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({
+    SkiaGoldClient goldens,
+    LocalFileComparator defaultComparator,
+  }) async {
+    defaultComparator ??= goldenFileComparator;
+    goldens ??= SkiaGoldClient();
+
+    final Directory baseDirectory = FlutterGoldenFileComparator.getBaseDirectory(goldens, defaultComparator);
+    if (!baseDirectory.existsSync())
+      baseDirectory.createSync(recursive: true);
+    await goldens.auth(baseDirectory);
+    await goldens.imgtestInit();
+    return FlutterSkiaGoldFileComparator(baseDirectory.uri, goldens);
   }
 
-  File _getGoldenFile(Uri uri) {
-    return fs.directory(basedir).childFile(fs.file(uri).path);
+  @override
+  Future<bool> compare(Uint8List imageBytes, Uri golden) async {
+    golden = _addPrefix(golden);
+    await update(golden, imageBytes);
+
+    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 isAvailableOnPlatform(Platform platform) {
+    final String cirrusCI = platform.environment['CIRRUS_CI'] ?? '';
+    final String cirrusPR = platform.environment['CIRRUS_PR'] ?? '';
+    final String cirrusBranch = platform.environment['CIRRUS_BRANCH'] ?? '';
+    return cirrusCI.isNotEmpty && cirrusPR.isEmpty && cirrusBranch == 'master';
+  }
+
+  /// 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 skipping golden image tests when Skia
+/// Gold is unavailable or the current platform that is executing tests is not
+/// Linux.
+///
+/// See also:
+///
+///  * [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 [FlutterSkippingGoldenFileComparator] that mirrors the relative
+  /// path resolution of the default [goldenFileComparator].
+  static FlutterSkippingGoldenFileComparator fromDefaultComparator({
+    LocalFileComparator defaultComparator,
+  }) {
+    defaultComparator ??= goldenFileComparator;
+    return FlutterSkippingGoldenFileComparator(defaultComparator.basedir);
+  }
+
+  @override
+  Future<bool> compare(Uint8List imageBytes, Uri golden) async {
+    print('Skipping "$golden" test : Skia Gold is not available in this testing '
+      'environment and flutter/goldens repository comparison is only available '
+      'on Linux machines.'
+    );
+    return true;
+  }
+
+  @override
+  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 19645ed..4f15200 100644
--- a/packages/flutter_goldens/test/flutter_goldens_test.dart
+++ b/packages/flutter_goldens/test/flutter_goldens_test.dart
@@ -34,13 +34,13 @@
   });
 
   group('GoldensClient', () {
-    GoldensClient goldens;
+    GoldensRepositoryClient goldens;
 
     setUp(() {
-      goldens = GoldensClient(
+      goldens = GoldensRepositoryClient(
         fs: fs,
-        platform: platform,
         process: process,
+        platform: platform,
       );
     });
 
@@ -60,32 +60,65 @@
     });
   });
 
+  group('SkiaGoldClient', () {
+    SkiaGoldClient goldens;
+
+    setUp(() {
+      goldens = SkiaGoldClient(
+        fs: fs,
+        process: process,
+        platform: platform,
+      );
+    });
+
+    group('auth', () {
+      test('performs minimal work if already authorized', () async {
+        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);
+
+        // 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;
-    FlutterGoldenFileComparator comparator;
+    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 = FlutterGoldenFileComparator(testDirectory.uri, fs: fs);
-    });
-
-    group('fromDefaultComparator', () {
-      test('calculates the basedir correctly', () async {
-        final MockGoldensClient goldens = MockGoldensClient();
-        final MockLocalFileComparator defaultComparator = MockLocalFileComparator();
-        final Directory flutterRoot = fs.directory('/foo')..createSync(recursive: true);
-        final Directory goldensRoot = flutterRoot.childDirectory('bar')..createSync(recursive: true);
-        when(goldens.fs).thenReturn(fs);
-        when(goldens.flutterRoot).thenReturn(flutterRoot);
-        when(goldens.repositoryRoot).thenReturn(goldensRoot);
-        when(defaultComparator.basedir).thenReturn(flutterRoot.childDirectory('baz').uri);
-        comparator = await FlutterGoldenFileComparator.fromDefaultComparator(
-            goldens: goldens, defaultComparator: defaultComparator);
-        expect(comparator.basedir, fs.directory('/foo/bar/baz').uri);
-      });
+      comparator = FlutterGoldensRepositoryFileComparator(
+        testDirectory.uri,
+        fs: fs,
+        platform: platform,
+      );
     });
 
     group('compare', () {
@@ -132,9 +165,44 @@
         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 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(
+        testDirectory.uri,
+        MockSkiaGoldClient(),
+        fs: fs,
+        platform: platform,
+      );
+    });
+
+    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 MockGoldensClient extends Mock implements GoldensClient {}
+class MockGoldensRepositoryClient extends Mock implements GoldensRepositoryClient {}
+class MockSkiaGoldClient extends Mock implements SkiaGoldClient {}
 class MockLocalFileComparator extends Mock implements LocalFileComparator {}
diff --git a/packages/flutter_goldens_client/lib/client.dart b/packages/flutter_goldens_client/lib/client.dart
index 3f6952f..d5257a5 100644
--- a/packages/flutter_goldens_client/lib/client.dart
+++ b/packages/flutter_goldens_client/lib/client.dart
@@ -16,11 +16,11 @@
 
 const String _kFlutterRootKey = 'FLUTTER_ROOT';
 
-/// A class that represents a clone of the https://github.com/flutter/goldens
-/// repository, nested within the `bin/cache` directory of the caller's Flutter
-/// repository.
-class GoldensClient {
-  /// Create a handle to a local clone of the goldens repository.
+/// An 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(),
@@ -46,17 +46,32 @@
   /// subprocesses.
   final ProcessManager process;
 
-  RandomAccessFile _lock;
-
   /// 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 repository is hosted.
+  /// The local [Directory] where the goldens files are located.
   ///
   /// Uses the [fs] file system.
-  Directory get repositoryRoot => flutterRoot.childDirectory(fs.path.join('bin', 'cache', 'pkg', 'goldens'));
+  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.
@@ -89,46 +104,46 @@
     }
   }
 
-  Future<String> _getGoldensCommit() async {
-    final File versionFile = flutterRoot.childFile(fs.path.join('bin', 'internal', 'goldens.version'));
-    return (await versionFile.readAsString()).trim();
-  }
-
   Future<String> _getCurrentCommit() async {
-    if (!repositoryRoot.existsSync()) {
+    if (!comparisonRoot.existsSync()) {
       return null;
     } else {
       final io.ProcessResult revParse = await process.run(
         <String>['git', 'rev-parse', 'HEAD'],
-        workingDirectory: repositoryRoot.path,
+        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 repositoryRoot.create(recursive: true);
+    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: repositoryRoot,
+      workingDirectory: comparisonRoot,
     );
   }
 
   Future<void> _checkCanSync() async {
     final io.ProcessResult result = await process.run(
       <String>['git', 'status', '--porcelain'],
-      workingDirectory: repositoryRoot.path,
+      workingDirectory: comparisonRoot.path,
     );
     if (result.stdout.trim().isNotEmpty) {
       final StringBuffer buf = StringBuffer();
       buf
-        ..writeln('flutter_goldens git checkout at ${repositoryRoot.path} has local changes and cannot be synced.')
+        ..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 ${repositoryRoot.path}')
+        ..writeln('cd ${comparisonRoot.path}')
         ..writeln('git reset --hard HEAD')
         ..writeln('git clean -x -d -f -f');
       throw NonZeroExitCode(1, buf.toString());
@@ -142,7 +157,7 @@
         'git fetch upstream $commit',
         'git reset --hard FETCH_HEAD',
       ],
-      workingDirectory: repositoryRoot,
+      workingDirectory: comparisonRoot,
     );
   }
 
@@ -174,6 +189,7 @@
     _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.
diff --git a/packages/flutter_goldens_client/lib/skia_client.dart b/packages/flutter_goldens_client/lib/skia_client.dart
new file mode 100644
index 0000000..fe3d7be
--- /dev/null
+++ b/packages/flutter_goldens_client/lib/skia_client.dart
@@ -0,0 +1,211 @@
+// 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.
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io' as io;
+
+import 'package:file/file.dart';
+import 'package:file/local.dart';
+import 'package:path/path.dart' as path;
+import 'package:platform/platform.dart';
+import 'package:process/process.dart';
+
+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
+
+// 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';
+
+/// 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
+  /// for the goldctl tool to use.
+  ///
+  /// This is informed by the [FlutterGoldenFileComparator] [basedir]. It cannot
+  /// be null.
+  Directory _workDirectory;
+
+  /// The path to the local [Directory] where the goldctl tool is hosted.
+  ///
+  /// Uses the [platform] environment in this implementation.
+  String get _goldctl => platform.environment[_kGoldctlKey];
+
+  /// The path to the local [Directory] where the service account key is
+  /// hosted.
+  ///
+  /// Uses the [platform] environment in this implementation.
+  String get _serviceAccount => platform.environment[_kServiceAccountKey];
+
+  /// Prepares the local work space for golden file testing and calls the
+  /// goldctl `auth` command.
+  ///
+  /// This ensures that the goldctl tool is authorized and ready for testing. It
+  /// will only be called once for each instance of
+  /// [FlutterSkiaGoldFileComparator].
+  ///
+  /// 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(Directory workDirectory) async {
+    assert(workDirectory != null);
+    _workDirectory = workDirectory;
+    if (_clientIsAuthorized())
+      return;
+
+    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,
+    ];
+
+    final io.ProcessResult authResults = await io.Process.run(
+      _goldctl,
+      authArguments,
+    );
+
+    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.
+  ///
+  /// The `imgtest` command collects and uploads test results to the Skia Gold
+  /// backend, the `init` argument initializes the testing environment.
+  Future<void> imgtestInit() async {
+    final String commitHash = await _getCurrentCommit();
+    final File keys = _workDirectory.childFile('keys.json');
+    final File failures = _workDirectory.childFile('failures.json');
+
+    await keys.writeAsString(_getKeysJSON());
+    await failures.create();
+
+    final List<String> imgtestInitArguments = <String>[
+      'imgtest', 'init',
+      '--instance', 'flutter',
+      '--work-dir', _workDirectory.childDirectory('temp').path,
+      '--commit', commitHash,
+      '--keys-file', keys.path,
+      '--failure-file', failures.path,
+      '--passfail',
+    ];
+
+    if (imgtestInitArguments.contains(null)) {
+      final StringBuffer buf = StringBuffer();
+      buf.writeln('Null argument for Skia Gold imgtest init:');
+      imgtestInitArguments.forEach(buf.writeln);
+      throw NonZeroExitCode(1, buf.toString());
+    }
+
+    final io.ProcessResult imgtestInitResult = await io.Process.run(
+      _goldctl,
+      imgtestInitArguments,
+    );
+
+    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.
+  ///
+  /// The `imgtest` command collects and uploads test results to the Skia Gold
+  /// backend, the `add` argument uploads the current image test. A response is
+  /// returned from the invocation of this command that indicates a pass or fail
+  /// result.
+  ///
+  /// The testName and goldenFile parameters reference the current comparison
+  /// being evaluated by the [FlutterSkiaGoldFileComparator].
+  Future<bool> imgtestAdd(String testName, File goldenFile) async {
+    assert(testName != null);
+    assert(goldenFile != null);
+
+    final List<String> imgtestArguments = <String>[
+      'imgtest', 'add',
+      '--work-dir', _workDirectory.childDirectory('temp').path,
+      '--test-name', testName.split(path.extension(testName.toString()))[0],
+      '--png-file', goldenFile.path,
+    ];
+
+    await io.Process.run(
+      _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;
+  }
+
+  /// Returns the current commit hash of the Flutter repository.
+  Future<String> _getCurrentCommit() async {
+    if (!flutterRoot.existsSync()) {
+      final StringBuffer buf = StringBuffer()
+        ..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,
+      );
+      return revParse.exitCode == 0 ? revParse.stdout.trim() : null;
+    }
+  }
+
+  /// Returns a JSON String with keys value pairs used to uniquely identify the
+  /// configuration that generated the given golden file.
+  ///
+  /// Currently, the only key value pair being tracked is the platform the image
+  /// was rendered on.
+  String _getKeysJSON() {
+    return json.encode(
+      <String, dynamic>{
+        'Platform' : platform.operatingSystem,
+      }
+    );
+  }
+
+  /// Returns a boolean value to prevent the client from re-authorizing itself
+  /// for multiple tests.
+  bool _clientIsAuthorized() {
+    final File authFile = _workDirectory.childFile(super.fs.path.join(
+      'temp',
+      'auth_opt.json',
+    ));
+    return authFile.existsSync();
+  }
+}
diff --git a/packages/flutter_test/lib/src/goldens.dart b/packages/flutter_test/lib/src/goldens.dart
index 6672755..1bc6fcd 100644
--- a/packages/flutter_test/lib/src/goldens.dart
+++ b/packages/flutter_test/lib/src/goldens.dart
@@ -45,6 +45,26 @@
   /// The method by which [golden] is located and by which its bytes are written
   /// is left up to the implementation class.
   Future<void> update(Uri golden, Uint8List imageBytes);
+
+  /// Returns a new golden file [Uri] to incorporate any [version] number with
+  /// the [key].
+  ///
+  /// The [version] is an optional int that can be used to differentiate
+  /// historical golden files.
+  ///
+  /// 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).
+  Uri getTestUri(Uri key, int version) {
+    if (version == null)
+      return key;
+    final String keyString = key.toString();
+    final String extension = path.extension(keyString);
+    return Uri.parse(
+      keyString
+        .split(extension)
+        .join() + '.' + version.toString() + extension
+    );
+  }
 }
 
 /// Compares rasterized image bytes against a golden image file.
@@ -126,6 +146,11 @@
   Future<void> update(Uri golden, Uint8List imageBytes) {
     throw StateError('goldenFileComparator has not been initialized');
   }
+
+  @override
+  Uri getTestUri(Uri key, int version) {
+    return key;
+  }
 }
 
 /// The default [GoldenFileComparator] implementation for `flutter test`.
@@ -140,7 +165,7 @@
 ///
 /// When using `flutter test --update-goldens`, [LocalFileComparator]
 /// updates the files on disk to match the rendering.
-class LocalFileComparator implements GoldenFileComparator {
+class LocalFileComparator extends GoldenFileComparator {
   /// Creates a new [LocalFileComparator] for the specified [testFile].
   ///
   /// Golden file keys will be interpreted as file paths relative to the
diff --git a/packages/flutter_test/lib/src/matchers.dart b/packages/flutter_test/lib/src/matchers.dart
index 9f32de0..a97299c 100644
--- a/packages/flutter_test/lib/src/matchers.dart
+++ b/packages/flutter_test/lib/src/matchers.dart
@@ -1712,7 +1712,7 @@
       imageFuture = _captureImage(elements.single);
     }
 
-    final Uri testNameUri = _getTestNameUri(key, version);
+    final Uri testNameUri = goldenFileComparator.getTestUri(key, version);
 
     final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized();
     return binding.runAsync<String>(() async {
@@ -1734,19 +1734,9 @@
   }
 
   @override
-  Description describe(Description description) =>
-      description.add('one widget whose rasterized image matches golden image "${_getTestNameUri(key, version)}"');
-
-  Uri _getTestNameUri(Uri key, int version) {
-    return version == null ? key : Uri.parse(
-      key
-        .toString()
-        .splitMapJoin(
-          RegExp(r'.png'),
-          onMatch: (Match m) => '${'.' + version.toString() + m.group(0)}',
-          onNonMatch: (String n) => '$n'
-        )
-    );
+  Description describe(Description description) {
+    final Uri testNameUri = goldenFileComparator.getTestUri(key, version);
+    return description.add('one widget whose rasterized image matches golden image "$testNameUri"');
   }
 }
 
diff --git a/packages/flutter_test/test/goldens_test.dart b/packages/flutter_test/test/goldens_test.dart
index 5d03642..004b336 100644
--- a/packages/flutter_test/test/goldens_test.dart
+++ b/packages/flutter_test/test/goldens_test.dart
@@ -182,5 +182,18 @@
         expect(fs.file(fix('/foo.png')).readAsBytesSync(), newBytes);
       });
     });
+
+    group('getTestUri', () {
+      test('updates file name with version number', () {
+        final Uri key = Uri.parse('foo.png');
+        final Uri key1 = comparator.getTestUri(key, 1);
+        expect(key1, Uri.parse('foo.1.png'));
+      });
+      test('does nothing for null version number', () {
+        final Uri key = Uri.parse('foo.png');
+        final Uri keyNull = comparator.getTestUri(key, null);
+        expect(keyNull, Uri.parse('foo.png'));
+      });
+    });
   });
 }
diff --git a/packages/flutter_test/test/matchers_test.dart b/packages/flutter_test/test/matchers_test.dart
index b2108e2..05f5981 100644
--- a/packages/flutter_test/test/matchers_test.dart
+++ b/packages/flutter_test/test/matchers_test.dart
@@ -335,30 +335,6 @@
         expect(comparator.imageBytes, hasLength(greaterThan(0)));
         expect(comparator.golden, Uri.parse('foo.png'));
       });
-
-      testWidgets('Comparator succeeds incorporating version number', (WidgetTester tester) async {
-        await tester.pumpWidget(boilerplate(const Text('hello')));
-        final Finder finder = find.byType(Text);
-        await expectLater(finder, matchesGoldenFile(
-          'foo.png',
-          version: 1,
-        ));
-        expect(comparator.invocation, _ComparatorInvocation.compare);
-        expect(comparator.imageBytes, hasLength(greaterThan(0)));
-        expect(comparator.golden, Uri.parse('foo.1.png'));
-      });
-
-      testWidgets('Comparator succeeds with null version number', (WidgetTester tester) async {
-        await tester.pumpWidget(boilerplate(const Text('hello')));
-        final Finder finder = find.byType(Text);
-        await expectLater(finder, matchesGoldenFile(
-          'foo.png',
-          version: null,
-        ));
-        expect(comparator.invocation, _ComparatorInvocation.compare);
-        expect(comparator.imageBytes, hasLength(greaterThan(0)));
-        expect(comparator.golden, Uri.parse('foo.png'));
-      });
     });
 
     group('does not match', () {
@@ -413,40 +389,6 @@
           expect(error.message, contains('too many widgets'));
         }
       });
-
-      testWidgets('Comparator failure incorporates version number', (WidgetTester tester) async {
-        comparator.behavior = _ComparatorBehavior.returnFalse;
-        await tester.pumpWidget(boilerplate(const Text('hello')));
-        final Finder finder = find.byType(Text);
-        try {
-          await expectLater(finder, matchesGoldenFile(
-            'foo.png',
-            version: 1,
-          ));
-          fail('TestFailure expected but not thrown');
-        } on TestFailure catch (error) {
-          expect(comparator.invocation, _ComparatorInvocation.compare);
-          expect(error.message, contains('does not match'));
-          expect(error.message, contains('foo.1.png'));
-        }
-      });
-
-      testWidgets('Comparator failure with null version number', (WidgetTester tester) async {
-        comparator.behavior = _ComparatorBehavior.returnFalse;
-        await tester.pumpWidget(boilerplate(const Text('hello')));
-        final Finder finder = find.byType(Text);
-        try {
-          await expectLater(finder, matchesGoldenFile(
-            'foo.png',
-            version: null,
-          ));
-          fail('TestFailure expected but not thrown');
-        } on TestFailure catch (error) {
-          expect(comparator.invocation, _ComparatorInvocation.compare);
-          expect(error.message, contains('does not match'));
-          expect(error.message, contains('foo.png'));
-        }
-      });
     });
 
     testWidgets('calls update on comparator if autoUpdateGoldenFiles is true', (WidgetTester tester) async {
@@ -708,6 +650,11 @@
     this.imageBytes = imageBytes;
     return Future<void>.value();
   }
+
+  @override
+  Uri getTestUri(Uri key, int version) {
+    return key;
+  }
 }
 
 class _FakeSemanticsNode extends SemanticsNode {