Fix web test flakiness; enable web golden tests (#52789)
* Fix web test flakiness; enable web golden tests The fix is three-part: - Only allow one test to load _and_ test at any point in time. - Use a fresh Chrome instance for each test file. - Increase Cirrus resources. The first two changes only fix the "Unknown error loading" error, but not hanging tests. The resource increase also prevents hanging tests. Other minor changes: - Remove test batching (it's no longer necessary) - Fix the Chrome class, which was using the wrong Completer.
This commit is contained in:
parent
f9b2d42b0e
commit
7b4c195f99
@ -4,9 +4,9 @@
|
|||||||
web_shard_template: &WEB_SHARD_TEMPLATE
|
web_shard_template: &WEB_SHARD_TEMPLATE
|
||||||
only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'packages/flutter_web_plugins/**', 'bin/internal/**') || $CIRRUS_PR == ''"
|
only_if: "changesInclude('.cirrus.yml', 'dev/**', 'packages/flutter/**', 'packages/flutter_test/**', 'packages/flutter_tools/lib/src/test/**', 'packages/flutter_web_plugins/**', 'bin/internal/**') || $CIRRUS_PR == ''"
|
||||||
environment:
|
environment:
|
||||||
# As of October 2019, the Web shards needed more than 6G of RAM.
|
# As of March 2020, the Web shards needed 16G of RAM and 4 CPUs to run all framework tests with goldens without flaking.
|
||||||
CPU: 2
|
CPU: 4
|
||||||
MEMORY: 8G
|
MEMORY: 16G
|
||||||
CHROME_NO_SANDBOX: true
|
CHROME_NO_SANDBOX: true
|
||||||
GOLD_SERVICE_ACCOUNT: ENCRYPTED[3afeea5ac7201151c3d0dc9648862f0462b5e4f55dc600ca8b692319622f7c3eda3d577b1b16cc2ef0311b7314c1c095]
|
GOLD_SERVICE_ACCOUNT: ENCRYPTED[3afeea5ac7201151c3d0dc9648862f0462b5e4f55dc600ca8b692319622f7c3eda3d577b1b16cc2ef0311b7314c1c095]
|
||||||
script:
|
script:
|
||||||
|
@ -53,11 +53,6 @@ const int kDeviceLabShardCount = 4;
|
|||||||
/// The last shard also runs the Web plugin tests.
|
/// The last shard also runs the Web plugin tests.
|
||||||
const int kWebShardCount = 8;
|
const int kWebShardCount = 8;
|
||||||
|
|
||||||
/// Maximum number of Web tests to run in a single `flutter test`. We found that
|
|
||||||
/// large batches can get flaky, possibly because we reuse a single instance of
|
|
||||||
/// the browser, and after many tests the browser's state gets corrupted.
|
|
||||||
const int kWebBatchSize = 20;
|
|
||||||
|
|
||||||
/// Tests that we don't run on Web for various reasons.
|
/// Tests that we don't run on Web for various reasons.
|
||||||
//
|
//
|
||||||
// TODO(yjbanov): we're getting rid of this blacklist as part of https://github.com/flutter/flutter/projects/60
|
// TODO(yjbanov): we're getting rid of this blacklist as part of https://github.com/flutter/flutter/projects/60
|
||||||
@ -685,11 +680,6 @@ Future<void> _runWebDebugTest(String target) async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _runFlutterWebTest(String workingDirectory, List<String> tests) async {
|
Future<void> _runFlutterWebTest(String workingDirectory, List<String> tests) async {
|
||||||
final List<String> batch = <String>[];
|
|
||||||
for (int i = 0; i < tests.length; i += 1) {
|
|
||||||
final String testFilePath = tests[i];
|
|
||||||
batch.add(testFilePath);
|
|
||||||
if (batch.length == kWebBatchSize || i == tests.length - 1) {
|
|
||||||
await runCommand(
|
await runCommand(
|
||||||
flutter,
|
flutter,
|
||||||
<String>[
|
<String>[
|
||||||
@ -699,7 +689,7 @@ Future<void> _runFlutterWebTest(String workingDirectory, List<String> tests) asy
|
|||||||
'-v',
|
'-v',
|
||||||
'--platform=chrome',
|
'--platform=chrome',
|
||||||
...?flutterTestArgs,
|
...?flutterTestArgs,
|
||||||
...batch,
|
...tests,
|
||||||
],
|
],
|
||||||
workingDirectory: workingDirectory,
|
workingDirectory: workingDirectory,
|
||||||
environment: <String, String>{
|
environment: <String, String>{
|
||||||
@ -707,9 +697,6 @@ Future<void> _runFlutterWebTest(String workingDirectory, List<String> tests) asy
|
|||||||
'FLUTTER_LOW_RESOURCE_MODE': 'true',
|
'FLUTTER_LOW_RESOURCE_MODE': 'true',
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
batch.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _pubRunTest(String workingDirectory, {
|
Future<void> _pubRunTest(String workingDirectory, {
|
||||||
|
@ -82,7 +82,7 @@ void main() {
|
|||||||
find.byKey(_painterKey),
|
find.byKey(_painterKey),
|
||||||
matchesGoldenFile('bottom_app_bar_theme.custom_shape.png'),
|
matchesGoldenFile('bottom_app_bar_theme.custom_shape.png'),
|
||||||
);
|
);
|
||||||
}, skip: isBrowser);
|
});
|
||||||
|
|
||||||
testWidgets('BAB theme does not affect defaults', (WidgetTester tester) async {
|
testWidgets('BAB theme does not affect defaults', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(const MaterialApp(
|
await tester.pumpWidget(const MaterialApp(
|
||||||
|
@ -913,7 +913,7 @@ void main() {
|
|||||||
await tester.tap(find.text('Alarm'));
|
await tester.tap(find.text('Alarm'));
|
||||||
await tester.pump(const Duration(seconds: 1));
|
await tester.pump(const Duration(seconds: 1));
|
||||||
expect(Theme.of(tester.element(find.text('Alarm'))).brightness, equals(Brightness.dark));
|
expect(Theme.of(tester.element(find.text('Alarm'))).brightness, equals(Brightness.dark));
|
||||||
}, skip: isBrowser);
|
});
|
||||||
|
|
||||||
testWidgets('BottomNavigationBar iconSize test', (WidgetTester tester) async {
|
testWidgets('BottomNavigationBar iconSize test', (WidgetTester tester) async {
|
||||||
double builderIconSize;
|
double builderIconSize;
|
||||||
@ -1023,7 +1023,7 @@ void main() {
|
|||||||
|
|
||||||
final RenderBox box = tester.renderObject(find.byType(BottomNavigationBar));
|
final RenderBox box = tester.renderObject(find.byType(BottomNavigationBar));
|
||||||
expect(box.size.height, equals(66.0));
|
expect(box.size.height, equals(66.0));
|
||||||
}, skip: isBrowser);
|
});
|
||||||
|
|
||||||
testWidgets('BottomNavigationBar limits width of tiles with long titles', (WidgetTester tester) async {
|
testWidgets('BottomNavigationBar limits width of tiles with long titles', (WidgetTester tester) async {
|
||||||
final Text longTextA = Text(''.padLeft(100, 'A'));
|
final Text longTextA = Text(''.padLeft(100, 'A'));
|
||||||
@ -1055,7 +1055,7 @@ void main() {
|
|||||||
expect(itemBoxA.size, equals(const Size(400.0, 14.0)));
|
expect(itemBoxA.size, equals(const Size(400.0, 14.0)));
|
||||||
final RenderBox itemBoxB = tester.renderObject(find.text(longTextB.data));
|
final RenderBox itemBoxB = tester.renderObject(find.text(longTextB.data));
|
||||||
expect(itemBoxB.size, equals(const Size(400.0, 14.0)));
|
expect(itemBoxB.size, equals(const Size(400.0, 14.0)));
|
||||||
}, skip: isBrowser);
|
});
|
||||||
|
|
||||||
testWidgets('BottomNavigationBar paints circles', (WidgetTester tester) async {
|
testWidgets('BottomNavigationBar paints circles', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@ -1125,7 +1125,7 @@ void main() {
|
|||||||
..translate(x: 400.0)
|
..translate(x: 400.0)
|
||||||
..circle(x: 200.0),
|
..circle(x: 200.0),
|
||||||
);
|
);
|
||||||
}, skip: isBrowser);
|
});
|
||||||
|
|
||||||
testWidgets('BottomNavigationBar inactiveIcon shown', (WidgetTester tester) async {
|
testWidgets('BottomNavigationBar inactiveIcon shown', (WidgetTester tester) async {
|
||||||
const Key filled = Key('filled');
|
const Key filled = Key('filled');
|
||||||
@ -1452,7 +1452,7 @@ void main() {
|
|||||||
find.byType(BottomNavigationBar),
|
find.byType(BottomNavigationBar),
|
||||||
matchesGoldenFile('bottom_navigation_bar.shifting_transition.${pump - 1}.png'),
|
matchesGoldenFile('bottom_navigation_bar.shifting_transition.${pump - 1}.png'),
|
||||||
);
|
);
|
||||||
}, skip: isBrowser); // TODO(yjbanov): web does not support golden tests yet: https://github.com/flutter/flutter/issues/40297
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -132,7 +132,7 @@ void main() {
|
|||||||
find.byKey(_painterKey),
|
find.byKey(_painterKey),
|
||||||
matchesGoldenFile('dialog_theme.dialog_with_custom_border.png'),
|
matchesGoldenFile('dialog_theme.dialog_with_custom_border.png'),
|
||||||
);
|
);
|
||||||
}, skip: isBrowser);
|
});
|
||||||
|
|
||||||
testWidgets('Custom Title Text Style - Constructor Param', (WidgetTester tester) async {
|
testWidgets('Custom Title Text Style - Constructor Param', (WidgetTester tester) async {
|
||||||
const String titleText = 'Title';
|
const String titleText = 'Title';
|
||||||
|
@ -189,7 +189,7 @@ void main() {
|
|||||||
find.ancestor(of: buttonFinder, matching: find.byType(RepaintBoundary)).first,
|
find.ancestor(of: buttonFinder, matching: find.byType(RepaintBoundary)).first,
|
||||||
matchesGoldenFile('dropdown_test.default.png'),
|
matchesGoldenFile('dropdown_test.default.png'),
|
||||||
);
|
);
|
||||||
}, skip: isBrowser);
|
});
|
||||||
|
|
||||||
testWidgets('Expanded dropdown golden', (WidgetTester tester) async {
|
testWidgets('Expanded dropdown golden', (WidgetTester tester) async {
|
||||||
final Key buttonKey = UniqueKey();
|
final Key buttonKey = UniqueKey();
|
||||||
@ -201,7 +201,7 @@ void main() {
|
|||||||
find.ancestor(of: buttonFinder, matching: find.byType(RepaintBoundary)).first,
|
find.ancestor(of: buttonFinder, matching: find.byType(RepaintBoundary)).first,
|
||||||
matchesGoldenFile('dropdown_test.expanded.png'),
|
matchesGoldenFile('dropdown_test.expanded.png'),
|
||||||
);
|
);
|
||||||
}, skip: isBrowser);
|
});
|
||||||
|
|
||||||
testWidgets('Dropdown button control test', (WidgetTester tester) async {
|
testWidgets('Dropdown button control test', (WidgetTester tester) async {
|
||||||
String value = 'one';
|
String value = 'one';
|
||||||
|
@ -85,7 +85,7 @@ void main() {
|
|||||||
find.byType(FlexibleSpaceBar),
|
find.byType(FlexibleSpaceBar),
|
||||||
matchesGoldenFile('flexible_space_bar_stretch_mode.blur_background.png'),
|
matchesGoldenFile('flexible_space_bar_stretch_mode.blur_background.png'),
|
||||||
);
|
);
|
||||||
}, skip: isBrowser);
|
});
|
||||||
|
|
||||||
testWidgets('FlexibleSpaceBar stretch mode fadeTitle', (WidgetTester tester) async {
|
testWidgets('FlexibleSpaceBar stretch mode fadeTitle', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
|
@ -282,7 +282,7 @@ void main() {
|
|||||||
find.byKey(painterKey),
|
find.byKey(painterKey),
|
||||||
matchesGoldenFile('radio.ink_ripple.png'),
|
matchesGoldenFile('radio.ink_ripple.png'),
|
||||||
);
|
);
|
||||||
}, skip: isBrowser);
|
});
|
||||||
|
|
||||||
testWidgets('Radio is focusable and has correct focus color', (WidgetTester tester) async {
|
testWidgets('Radio is focusable and has correct focus color', (WidgetTester tester) async {
|
||||||
final FocusNode focusNode = FocusNode(debugLabel: 'Radio');
|
final FocusNode focusNode = FocusNode(debugLabel: 'Radio');
|
||||||
|
@ -269,7 +269,7 @@ void main() {
|
|||||||
find.byKey(_painterKey),
|
find.byKey(_painterKey),
|
||||||
matchesGoldenFile('tab_bar_theme.tab_indicator_size_tab.png'),
|
matchesGoldenFile('tab_bar_theme.tab_indicator_size_tab.png'),
|
||||||
);
|
);
|
||||||
}, skip: isBrowser);
|
});
|
||||||
|
|
||||||
testWidgets('Tab bar theme overrides tab indicator size (label)', (WidgetTester tester) async {
|
testWidgets('Tab bar theme overrides tab indicator size (label)', (WidgetTester tester) async {
|
||||||
const TabBarTheme tabBarTheme = TabBarTheme(indicatorSize: TabBarIndicatorSize.label);
|
const TabBarTheme tabBarTheme = TabBarTheme(indicatorSize: TabBarIndicatorSize.label);
|
||||||
@ -280,7 +280,7 @@ void main() {
|
|||||||
find.byKey(_painterKey),
|
find.byKey(_painterKey),
|
||||||
matchesGoldenFile('tab_bar_theme.tab_indicator_size_label.png'),
|
matchesGoldenFile('tab_bar_theme.tab_indicator_size_label.png'),
|
||||||
);
|
);
|
||||||
}, skip: isBrowser);
|
});
|
||||||
|
|
||||||
testWidgets('Tab bar theme - custom tab indicator', (WidgetTester tester) async {
|
testWidgets('Tab bar theme - custom tab indicator', (WidgetTester tester) async {
|
||||||
final TabBarTheme tabBarTheme = TabBarTheme(
|
final TabBarTheme tabBarTheme = TabBarTheme(
|
||||||
@ -296,7 +296,7 @@ void main() {
|
|||||||
find.byKey(_painterKey),
|
find.byKey(_painterKey),
|
||||||
matchesGoldenFile('tab_bar_theme.custom_tab_indicator.png'),
|
matchesGoldenFile('tab_bar_theme.custom_tab_indicator.png'),
|
||||||
);
|
);
|
||||||
}, skip: isBrowser);
|
});
|
||||||
|
|
||||||
testWidgets('Tab bar theme - beveled rect indicator', (WidgetTester tester) async {
|
testWidgets('Tab bar theme - beveled rect indicator', (WidgetTester tester) async {
|
||||||
final TabBarTheme tabBarTheme = TabBarTheme(
|
final TabBarTheme tabBarTheme = TabBarTheme(
|
||||||
@ -312,5 +312,5 @@ void main() {
|
|||||||
find.byKey(_painterKey),
|
find.byKey(_painterKey),
|
||||||
matchesGoldenFile('tab_bar_theme.beveled_rect_indicator.png'),
|
matchesGoldenFile('tab_bar_theme.beveled_rect_indicator.png'),
|
||||||
);
|
);
|
||||||
}, skip: isBrowser);
|
});
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,6 @@ void main() {
|
|||||||
matchesGoldenFile('localized_fonts.rich_text.styled_text_span.png'),
|
matchesGoldenFile('localized_fonts.rich_text.styled_text_span.png'),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
skip: isBrowser, // TODO(yjbanov): implement goldens on the Web: https://github.com/flutter/flutter/issues/40297
|
|
||||||
);
|
);
|
||||||
|
|
||||||
testWidgets(
|
testWidgets(
|
||||||
|
@ -45,5 +45,5 @@ void main() {
|
|||||||
find.byType(RepaintBoundary).first,
|
find.byType(RepaintBoundary).first,
|
||||||
matchesGoldenFile('backdrop_filter_test.cull_rect.png'),
|
matchesGoldenFile('backdrop_filter_test.cull_rect.png'),
|
||||||
);
|
);
|
||||||
}, skip: isBrowser);
|
});
|
||||||
}
|
}
|
||||||
|
@ -383,7 +383,7 @@ void main() {
|
|||||||
find.byType(RepaintBoundary).first,
|
find.byType(RepaintBoundary).first,
|
||||||
matchesGoldenFile('clip.ClipRect.png'),
|
matchesGoldenFile('clip.ClipRect.png'),
|
||||||
);
|
);
|
||||||
}, skip: isBrowser);
|
});
|
||||||
|
|
||||||
testWidgets('ClipRect save, overlay, and antialiasing', (WidgetTester tester) async {
|
testWidgets('ClipRect save, overlay, and antialiasing', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@ -423,7 +423,7 @@ void main() {
|
|||||||
find.byType(RepaintBoundary).first,
|
find.byType(RepaintBoundary).first,
|
||||||
matchesGoldenFile('clip.ClipRectOverlay.png'),
|
matchesGoldenFile('clip.ClipRectOverlay.png'),
|
||||||
);
|
);
|
||||||
}, skip: isBrowser);
|
});
|
||||||
|
|
||||||
testWidgets('ClipRRect painting', (WidgetTester tester) async {
|
testWidgets('ClipRRect painting', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@ -472,7 +472,7 @@ void main() {
|
|||||||
find.byType(RepaintBoundary).first,
|
find.byType(RepaintBoundary).first,
|
||||||
matchesGoldenFile('clip.ClipRRect.png'),
|
matchesGoldenFile('clip.ClipRRect.png'),
|
||||||
);
|
);
|
||||||
}, skip: isBrowser);
|
});
|
||||||
|
|
||||||
testWidgets('ClipOval painting', (WidgetTester tester) async {
|
testWidgets('ClipOval painting', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@ -515,7 +515,7 @@ void main() {
|
|||||||
find.byType(RepaintBoundary).first,
|
find.byType(RepaintBoundary).first,
|
||||||
matchesGoldenFile('clip.ClipOval.png'),
|
matchesGoldenFile('clip.ClipOval.png'),
|
||||||
);
|
);
|
||||||
}, skip: isBrowser);
|
});
|
||||||
|
|
||||||
testWidgets('ClipPath painting', (WidgetTester tester) async {
|
testWidgets('ClipPath painting', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@ -563,7 +563,7 @@ void main() {
|
|||||||
find.byType(RepaintBoundary).first,
|
find.byType(RepaintBoundary).first,
|
||||||
matchesGoldenFile('clip.ClipPath.png'),
|
matchesGoldenFile('clip.ClipPath.png'),
|
||||||
);
|
);
|
||||||
}, skip: isBrowser);
|
});
|
||||||
|
|
||||||
Center genPhysicalModel(Clip clipBehavior) {
|
Center genPhysicalModel(Clip clipBehavior) {
|
||||||
return Center(
|
return Center(
|
||||||
@ -608,7 +608,7 @@ void main() {
|
|||||||
find.byType(RepaintBoundary).first,
|
find.byType(RepaintBoundary).first,
|
||||||
matchesGoldenFile('clip.PhysicalModel.antiAlias.png'),
|
matchesGoldenFile('clip.PhysicalModel.antiAlias.png'),
|
||||||
);
|
);
|
||||||
}, skip: isBrowser);
|
});
|
||||||
|
|
||||||
testWidgets('PhysicalModel painting with Clip.hardEdge', (WidgetTester tester) async {
|
testWidgets('PhysicalModel painting with Clip.hardEdge', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(genPhysicalModel(Clip.hardEdge));
|
await tester.pumpWidget(genPhysicalModel(Clip.hardEdge));
|
||||||
@ -616,7 +616,7 @@ void main() {
|
|||||||
find.byType(RepaintBoundary).first,
|
find.byType(RepaintBoundary).first,
|
||||||
matchesGoldenFile('clip.PhysicalModel.hardEdge.png'),
|
matchesGoldenFile('clip.PhysicalModel.hardEdge.png'),
|
||||||
);
|
);
|
||||||
}, skip: isBrowser);
|
});
|
||||||
|
|
||||||
// There will be bleeding edges on the rect edges, but there shouldn't be any bleeding edges on the
|
// There will be bleeding edges on the rect edges, but there shouldn't be any bleeding edges on the
|
||||||
// round corners.
|
// round corners.
|
||||||
@ -626,7 +626,7 @@ void main() {
|
|||||||
find.byType(RepaintBoundary).first,
|
find.byType(RepaintBoundary).first,
|
||||||
matchesGoldenFile('clip.PhysicalModel.antiAliasWithSaveLayer.png'),
|
matchesGoldenFile('clip.PhysicalModel.antiAliasWithSaveLayer.png'),
|
||||||
);
|
);
|
||||||
}, skip: isBrowser);
|
});
|
||||||
|
|
||||||
testWidgets('Default PhysicalModel painting', (WidgetTester tester) async {
|
testWidgets('Default PhysicalModel painting', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@ -668,7 +668,7 @@ void main() {
|
|||||||
find.byType(RepaintBoundary).first,
|
find.byType(RepaintBoundary).first,
|
||||||
matchesGoldenFile('clip.PhysicalModel.default.png'),
|
matchesGoldenFile('clip.PhysicalModel.default.png'),
|
||||||
);
|
);
|
||||||
}, skip: isBrowser);
|
});
|
||||||
|
|
||||||
Center genPhysicalShape(Clip clipBehavior) {
|
Center genPhysicalShape(Clip clipBehavior) {
|
||||||
return Center(
|
return Center(
|
||||||
@ -717,7 +717,7 @@ void main() {
|
|||||||
find.byType(RepaintBoundary).first,
|
find.byType(RepaintBoundary).first,
|
||||||
matchesGoldenFile('clip.PhysicalShape.antiAlias.png'),
|
matchesGoldenFile('clip.PhysicalShape.antiAlias.png'),
|
||||||
);
|
);
|
||||||
}, skip: isBrowser);
|
});
|
||||||
|
|
||||||
testWidgets('PhysicalShape painting with Clip.hardEdge', (WidgetTester tester) async {
|
testWidgets('PhysicalShape painting with Clip.hardEdge', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(genPhysicalShape(Clip.hardEdge));
|
await tester.pumpWidget(genPhysicalShape(Clip.hardEdge));
|
||||||
@ -725,7 +725,7 @@ void main() {
|
|||||||
find.byType(RepaintBoundary).first,
|
find.byType(RepaintBoundary).first,
|
||||||
matchesGoldenFile('clip.PhysicalShape.hardEdge.png'),
|
matchesGoldenFile('clip.PhysicalShape.hardEdge.png'),
|
||||||
);
|
);
|
||||||
}, skip: isBrowser);
|
});
|
||||||
|
|
||||||
testWidgets('PhysicalShape painting with Clip.antiAliasWithSaveLayer', (WidgetTester tester) async {
|
testWidgets('PhysicalShape painting with Clip.antiAliasWithSaveLayer', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(genPhysicalShape(Clip.antiAliasWithSaveLayer));
|
await tester.pumpWidget(genPhysicalShape(Clip.antiAliasWithSaveLayer));
|
||||||
@ -733,7 +733,7 @@ void main() {
|
|||||||
find.byType(RepaintBoundary).first,
|
find.byType(RepaintBoundary).first,
|
||||||
matchesGoldenFile('clip.PhysicalShape.antiAliasWithSaveLayer.png'),
|
matchesGoldenFile('clip.PhysicalShape.antiAliasWithSaveLayer.png'),
|
||||||
);
|
);
|
||||||
}, skip: isBrowser);
|
});
|
||||||
|
|
||||||
testWidgets('PhysicalShape painting', (WidgetTester tester) async {
|
testWidgets('PhysicalShape painting', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@ -779,7 +779,7 @@ void main() {
|
|||||||
find.byType(RepaintBoundary).first,
|
find.byType(RepaintBoundary).first,
|
||||||
matchesGoldenFile('clip.PhysicalShape.default.png'),
|
matchesGoldenFile('clip.PhysicalShape.default.png'),
|
||||||
);
|
);
|
||||||
}, skip: isBrowser);
|
});
|
||||||
|
|
||||||
testWidgets('ClipPath.shape', (WidgetTester tester) async {
|
testWidgets('ClipPath.shape', (WidgetTester tester) async {
|
||||||
final List<String> logs = <String>[];
|
final List<String> logs = <String>[];
|
||||||
|
@ -22,7 +22,7 @@ void main() {
|
|||||||
find.byType(RepaintBoundary),
|
find.byType(RepaintBoundary),
|
||||||
matchesGoldenFile('invert_colors_test.0.png'),
|
matchesGoldenFile('invert_colors_test.0.png'),
|
||||||
);
|
);
|
||||||
}, skip: isBrowser);
|
});
|
||||||
|
|
||||||
testWidgets('InvertColors and ColorFilter', (WidgetTester tester) async {
|
testWidgets('InvertColors and ColorFilter', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(const RepaintBoundary(
|
await tester.pumpWidget(const RepaintBoundary(
|
||||||
@ -40,7 +40,7 @@ void main() {
|
|||||||
find.byType(RepaintBoundary),
|
find.byType(RepaintBoundary),
|
||||||
matchesGoldenFile('invert_colors_test.1.png'),
|
matchesGoldenFile('invert_colors_test.1.png'),
|
||||||
);
|
);
|
||||||
}, skip: isBrowser);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draws a rectangle sized by the parent widget with [color], [colorFilter],
|
// Draws a rectangle sized by the parent widget with [color], [colorFilter],
|
||||||
|
@ -594,7 +594,7 @@ void main() {
|
|||||||
find.byKey(const Key('list_wheel_scroll_view')),
|
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'),
|
||||||
);
|
);
|
||||||
}, skip: isBrowser);
|
});
|
||||||
|
|
||||||
testWidgets('Default middle transform', (WidgetTester tester) async {
|
testWidgets('Default middle transform', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@ -648,7 +648,7 @@ void main() {
|
|||||||
find.byKey(const Key('list_wheel_scroll_view')),
|
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'),
|
||||||
);
|
);
|
||||||
}, skip: isBrowser);
|
});
|
||||||
|
|
||||||
testWidgets('Scrolling, diameterRatio, perspective all changes matrix', (WidgetTester tester) async {
|
testWidgets('Scrolling, diameterRatio, perspective all changes matrix', (WidgetTester tester) async {
|
||||||
final ScrollController controller = ScrollController(initialScrollOffset: 200.0);
|
final ScrollController controller = ScrollController(initialScrollOffset: 200.0);
|
||||||
|
@ -180,7 +180,7 @@ void main() {
|
|||||||
find.byType(RepaintBoundary).first,
|
find.byType(RepaintBoundary).first,
|
||||||
matchesGoldenFile('opacity_test.offset.png'),
|
matchesGoldenFile('opacity_test.offset.png'),
|
||||||
);
|
);
|
||||||
}, skip: isBrowser);
|
});
|
||||||
|
|
||||||
testWidgets('empty opacity does not crash', (WidgetTester tester) async {
|
testWidgets('empty opacity does not crash', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
|
@ -112,7 +112,7 @@ void main() {
|
|||||||
find.byKey(key),
|
find.byKey(key),
|
||||||
matchesGoldenFile('physical_model_overflow.png'),
|
matchesGoldenFile('physical_model_overflow.png'),
|
||||||
);
|
);
|
||||||
}, skip: isBrowser);
|
});
|
||||||
|
|
||||||
group('PhysicalModelLayer checks elevation', () {
|
group('PhysicalModelLayer checks elevation', () {
|
||||||
Future<void> _testStackChildren(
|
Future<void> _testStackChildren(
|
||||||
|
@ -256,10 +256,8 @@ class FlutterWebPlatform extends PlatformPlugin {
|
|||||||
Uint8List bytes;
|
Uint8List bytes;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final Runtime browser = Runtime.chrome;
|
final ChromeTab chromeTab = await _browserManager._browser.chromeConnection.getTab((ChromeTab tab) {
|
||||||
final BrowserManager browserManager = await _browserManagerFor(browser);
|
return tab.url.contains(_browserManager._browser.url);
|
||||||
final ChromeTab chromeTab = await browserManager._browser.chromeConnection.getTab((ChromeTab tab) {
|
|
||||||
return tab.url.contains(browserManager._browser.url);
|
|
||||||
});
|
});
|
||||||
final WipConnection connection = await chromeTab.connect();
|
final WipConnection connection = await chromeTab.connect();
|
||||||
final WipResponse response = await connection.sendCommand('Page.captureScreenshot', <String, Object>{
|
final WipResponse response = await connection.sendCommand('Page.captureScreenshot', <String, Object>{
|
||||||
@ -303,10 +301,7 @@ class FlutterWebPlatform extends PlatformPlugin {
|
|||||||
|
|
||||||
bool get _closed => _closeMemo.hasRun;
|
bool get _closed => _closeMemo.hasRun;
|
||||||
|
|
||||||
// A map from browser identifiers to futures that will complete to the
|
BrowserManager _browserManager;
|
||||||
// [BrowserManager]s for those browsers, or `null` if they failed to load.
|
|
||||||
final Map<Runtime, Future<BrowserManager>> _browserManagers =
|
|
||||||
<Runtime, Future<BrowserManager>>{};
|
|
||||||
|
|
||||||
// A handler that serves wrapper files used to bootstrap tests.
|
// A handler that serves wrapper files used to bootstrap tests.
|
||||||
shelf.Response _wrapperHandler(shelf.Request request) {
|
shelf.Response _wrapperHandler(shelf.Request request) {
|
||||||
@ -330,6 +325,11 @@ class FlutterWebPlatform extends PlatformPlugin {
|
|||||||
return shelf.Response.notFound('Not found.');
|
return shelf.Response.notFound('Not found.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Allows only one test suite (typically one test file) to be loaded and run
|
||||||
|
/// at any given point in time. Loading more than one file at a time is known
|
||||||
|
/// to lead to flaky tests.
|
||||||
|
final Pool _suiteLock = Pool(1);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<RunnerSuite> load(
|
Future<RunnerSuite> load(
|
||||||
String path,
|
String path,
|
||||||
@ -340,17 +340,28 @@ class FlutterWebPlatform extends PlatformPlugin {
|
|||||||
if (_closed) {
|
if (_closed) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
final PoolResource lockResource = await _suiteLock.request();
|
||||||
|
|
||||||
final Runtime browser = platform.runtime;
|
final Runtime browser = platform.runtime;
|
||||||
final BrowserManager browserManager = await _browserManagerFor(browser);
|
try {
|
||||||
if (_closed || browserManager == null) {
|
_browserManager = await _launchBrowser(browser);
|
||||||
|
} on Error catch (_) {
|
||||||
|
await _suiteLock.close();
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_closed) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Uri suiteUrl = url.resolveUri(globals.fs.path.toUri(globals.fs.path.withoutExtension(
|
final Uri suiteUrl = url.resolveUri(globals.fs.path.toUri(globals.fs.path.withoutExtension(
|
||||||
globals.fs.path.relative(path, from: globals.fs.path.join(_root, 'test'))) +
|
globals.fs.path.relative(path, from: globals.fs.path.join(_root, 'test'))) +
|
||||||
'.html'));
|
'.html'));
|
||||||
final RunnerSuite suite = await browserManager
|
final RunnerSuite suite = await _browserManager.load(path, suiteUrl, suiteConfig, message, onDone: () async {
|
||||||
.load(path, suiteUrl, suiteConfig, message);
|
await _browserManager.close();
|
||||||
|
_browserManager = null;
|
||||||
|
lockResource.release();
|
||||||
|
});
|
||||||
if (_closed) {
|
if (_closed) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -364,11 +375,11 @@ class FlutterWebPlatform extends PlatformPlugin {
|
|||||||
/// Returns the [BrowserManager] for [runtime], which should be a browser.
|
/// Returns the [BrowserManager] for [runtime], which should be a browser.
|
||||||
///
|
///
|
||||||
/// If no browser manager is running yet, starts one.
|
/// If no browser manager is running yet, starts one.
|
||||||
Future<BrowserManager> _browserManagerFor(Runtime browser) {
|
Future<BrowserManager> _launchBrowser(Runtime browser) {
|
||||||
final Future<BrowserManager> managerFuture = _browserManagers[browser];
|
if (_browserManager != null) {
|
||||||
if (managerFuture != null) {
|
throw StateError('Another browser is currently running.');
|
||||||
return managerFuture;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final Completer<WebSocketChannel> completer =
|
final Completer<WebSocketChannel> completer =
|
||||||
Completer<WebSocketChannel>.sync();
|
Completer<WebSocketChannel>.sync();
|
||||||
final String path =
|
final String path =
|
||||||
@ -383,48 +394,29 @@ class FlutterWebPlatform extends PlatformPlugin {
|
|||||||
|
|
||||||
globals.printTrace('Serving tests at $hostUrl');
|
globals.printTrace('Serving tests at $hostUrl');
|
||||||
|
|
||||||
final Future<BrowserManager> future = BrowserManager.start(
|
return BrowserManager.start(
|
||||||
browser,
|
browser,
|
||||||
hostUrl,
|
hostUrl,
|
||||||
completer.future,
|
completer.future,
|
||||||
headless: !_config.pauseAfterLoad,
|
headless: !_config.pauseAfterLoad,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Store null values for browsers that error out so we know not to load them
|
|
||||||
// again.
|
|
||||||
_browserManagers[browser] = future.catchError((dynamic _) => null);
|
|
||||||
|
|
||||||
return future;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> closeEphemeral() {
|
Future<void> closeEphemeral() async {
|
||||||
final List<Future<BrowserManager>> managers =
|
if (_browserManager != null) {
|
||||||
_browserManagers.values.toList();
|
await _browserManager.close();
|
||||||
_browserManagers.clear();
|
|
||||||
return Future.wait(managers.map((Future<BrowserManager> manager) async {
|
|
||||||
final BrowserManager result = await manager;
|
|
||||||
if (result == null) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
await result.close();
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> close() => _closeMemo.runOnce(() async {
|
Future<void> close() => _closeMemo.runOnce(() async {
|
||||||
final List<Future<dynamic>> futures = _browserManagers.values
|
await Future.wait<void>(<Future<dynamic>>[
|
||||||
.map<Future<dynamic>>((Future<BrowserManager> future) async {
|
if (_browserManager != null)
|
||||||
final BrowserManager result = await future;
|
_browserManager.close(),
|
||||||
if (result == null) {
|
_server.close(),
|
||||||
return;
|
_testGoldenComparator.close(),
|
||||||
}
|
]);
|
||||||
await result.close();
|
|
||||||
})
|
|
||||||
.toList();
|
|
||||||
futures.add(_server.close());
|
|
||||||
futures.add(_testGoldenComparator.close());
|
|
||||||
await Future.wait<void>(futures);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -578,21 +570,6 @@ class BrowserManager {
|
|||||||
/// This is connected to a page running `static/host.dart`.
|
/// This is connected to a page running `static/host.dart`.
|
||||||
MultiChannel<dynamic> _channel;
|
MultiChannel<dynamic> _channel;
|
||||||
|
|
||||||
/// A pool that ensures that limits the number of initial connections the
|
|
||||||
/// manager will wait for at once.
|
|
||||||
///
|
|
||||||
/// This isn't the *total* number of connections; any number of iframes may be
|
|
||||||
/// loaded in the same browser. However, the browser can only load so many at
|
|
||||||
/// once, and we want a timeout in case they fail so we only wait for so many
|
|
||||||
/// at once.
|
|
||||||
// The number 1 is chosen to disallow multiple iframes in the same browser. This
|
|
||||||
// is because in some environments, such as Cirrus CI, tests end up stuck and
|
|
||||||
// time out eventually. The exact reason for timeouts is unknown, but the
|
|
||||||
// hypothesis is that we were the first ones to attempt to run DDK-compiled
|
|
||||||
// tests concurrently in the browser. DDK is known to produce an order of
|
|
||||||
// magnitude bigger and somewhat slower code, which may overload the browser.
|
|
||||||
final Pool _pool = Pool(1);
|
|
||||||
|
|
||||||
/// The ID of the next suite to be loaded.
|
/// The ID of the next suite to be loaded.
|
||||||
///
|
///
|
||||||
/// This is used to ensure that the suites can be referred to consistently
|
/// This is used to ensure that the suites can be referred to consistently
|
||||||
@ -654,8 +631,8 @@ class BrowserManager {
|
|||||||
|
|
||||||
final Completer<BrowserManager> completer = Completer<BrowserManager>();
|
final Completer<BrowserManager> completer = Completer<BrowserManager>();
|
||||||
|
|
||||||
unawaited(chrome.onExit.then((void _) {
|
unawaited(chrome.onExit.then((int browserExitCode) {
|
||||||
throwToolExit('${runtime.name} exited before connecting.');
|
throwToolExit('${runtime.name} exited with code $browserExitCode before connecting.');
|
||||||
}).catchError((dynamic error, StackTrace stackTrace) {
|
}).catchError((dynamic error, StackTrace stackTrace) {
|
||||||
if (completer.isCompleted) {
|
if (completer.isCompleted) {
|
||||||
return;
|
return;
|
||||||
@ -700,7 +677,9 @@ class BrowserManager {
|
|||||||
String path,
|
String path,
|
||||||
Uri url,
|
Uri url,
|
||||||
SuiteConfiguration suiteConfig,
|
SuiteConfiguration suiteConfig,
|
||||||
Object message,
|
Object message, {
|
||||||
|
Future<void> Function() onDone,
|
||||||
|
}
|
||||||
) async {
|
) async {
|
||||||
url = url.replace(fragment: Uri.encodeFull(jsonEncode(<String, Object>{
|
url = url.replace(fragment: Uri.encodeFull(jsonEncode(<String, Object>{
|
||||||
'metadata': suiteConfig.metadata.serialize(),
|
'metadata': suiteConfig.metadata.serialize(),
|
||||||
@ -726,10 +705,10 @@ class BrowserManager {
|
|||||||
StreamTransformer<dynamic, dynamic>.fromHandlers(handleDone: (EventSink<dynamic> sink) {
|
StreamTransformer<dynamic, dynamic>.fromHandlers(handleDone: (EventSink<dynamic> sink) {
|
||||||
closeIframe();
|
closeIframe();
|
||||||
sink.close();
|
sink.close();
|
||||||
|
onDone();
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
return await _pool.withResource<RunnerSuite>(() async {
|
|
||||||
_channel.sink.add(<String, Object>{
|
_channel.sink.add(<String, Object>{
|
||||||
'command': 'loadSuite',
|
'command': 'loadSuite',
|
||||||
'url': url.toString(),
|
'url': url.toString(),
|
||||||
@ -748,7 +727,6 @@ class BrowserManager {
|
|||||||
closeIframe();
|
closeIframe();
|
||||||
rethrow;
|
rethrow;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An implementation of [Environment.displayPause].
|
/// An implementation of [Environment.displayPause].
|
||||||
|
@ -116,6 +116,10 @@ class ChromeLauncher {
|
|||||||
///
|
///
|
||||||
/// `skipCheck` does not attempt to make a devtools connection before returning.
|
/// `skipCheck` does not attempt to make a devtools connection before returning.
|
||||||
Future<Chrome> launch(String url, { bool headless = false, int debugPort, bool skipCheck = false, Directory dataDir }) async {
|
Future<Chrome> launch(String url, { bool headless = false, int debugPort, bool skipCheck = false, Directory dataDir }) async {
|
||||||
|
if (_currentCompleter.isCompleted) {
|
||||||
|
throwToolExit('Only one instance of chrome can be started.');
|
||||||
|
}
|
||||||
|
|
||||||
// This is a JSON file which contains configuration from the
|
// This is a JSON file which contains configuration from the
|
||||||
// browser session, such as window position. It is located
|
// browser session, such as window position. It is located
|
||||||
// under the Chrome data-dir folder.
|
// under the Chrome data-dir folder.
|
||||||
@ -203,9 +207,6 @@ class ChromeLauncher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Future<Chrome> _connect(Chrome chrome, bool skipCheck) async {
|
static Future<Chrome> _connect(Chrome chrome, bool skipCheck) async {
|
||||||
if (_currentCompleter.isCompleted) {
|
|
||||||
throwToolExit('Only one instance of chrome can be started.');
|
|
||||||
}
|
|
||||||
// The connection is lazy. Try a simple call to make sure the provided
|
// The connection is lazy. Try a simple call to make sure the provided
|
||||||
// connection is valid.
|
// connection is valid.
|
||||||
if (!skipCheck) {
|
if (!skipCheck) {
|
||||||
@ -262,13 +263,11 @@ class Chrome {
|
|||||||
final ChromeConnection chromeConnection;
|
final ChromeConnection chromeConnection;
|
||||||
final Uri remoteDebuggerUri;
|
final Uri remoteDebuggerUri;
|
||||||
|
|
||||||
static Completer<Chrome> _currentCompleter = Completer<Chrome>();
|
Future<int> get onExit => _process.exitCode;
|
||||||
|
|
||||||
Future<void> get onExit => _currentCompleter.future;
|
|
||||||
|
|
||||||
Future<void> close() async {
|
Future<void> close() async {
|
||||||
if (_currentCompleter.isCompleted) {
|
if (ChromeLauncher.hasChromeInstance) {
|
||||||
_currentCompleter = Completer<Chrome>();
|
ChromeLauncher._currentCompleter = Completer<Chrome>();
|
||||||
}
|
}
|
||||||
chromeConnection.close();
|
chromeConnection.close();
|
||||||
_process?.kill();
|
_process?.kill();
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:file/memory.dart';
|
import 'package:file/memory.dart';
|
||||||
|
import 'package:flutter_tools/src/base/common.dart';
|
||||||
import 'package:flutter_tools/src/base/file_system.dart';
|
import 'package:flutter_tools/src/base/file_system.dart';
|
||||||
import 'package:flutter_tools/src/base/logger.dart';
|
import 'package:flutter_tools/src/base/logger.dart';
|
||||||
import 'package:flutter_tools/src/base/os.dart';
|
import 'package:flutter_tools/src/base/os.dart';
|
||||||
@ -62,28 +63,31 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('can launch chrome and connect to the devtools', () async {
|
test('can launch chrome and connect to the devtools', () async {
|
||||||
processManager.addCommand(const FakeCommand(
|
await testLaunchChrome('/.tmp_rand0/flutter_tool.rand0', processManager, chromeLauncher);
|
||||||
command: <String>[
|
});
|
||||||
'example_chrome',
|
|
||||||
'--user-data-dir=/.tmp_rand0/flutter_tool.rand0',
|
|
||||||
'--remote-debugging-port=1234',
|
|
||||||
..._kChromeArgs,
|
|
||||||
'example_url',
|
|
||||||
],
|
|
||||||
stderr: kDevtoolsStderr,
|
|
||||||
));
|
|
||||||
|
|
||||||
await chromeLauncher.launch(
|
test('cannot have two concurrent instances of chrome', () async {
|
||||||
'example_url',
|
await testLaunchChrome('/.tmp_rand0/flutter_tool.rand0', processManager, chromeLauncher);
|
||||||
skipCheck: true,
|
bool pass = false;
|
||||||
);
|
try {
|
||||||
|
await testLaunchChrome('/.tmp_rand0/flutter_tool.rand1', processManager, chromeLauncher);
|
||||||
|
} on ToolExit catch (_) {
|
||||||
|
pass = true;
|
||||||
|
}
|
||||||
|
expect(pass, isTrue);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('can launch new chrome after stopping a previous chrome', () async {
|
||||||
|
final Chrome chrome = await testLaunchChrome('/.tmp_rand0/flutter_tool.rand0', processManager, chromeLauncher);
|
||||||
|
await chrome.close();
|
||||||
|
await testLaunchChrome('/.tmp_rand0/flutter_tool.rand1', processManager, chromeLauncher);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('can launch chrome with a custom debug port', () async {
|
test('can launch chrome with a custom debug port', () async {
|
||||||
processManager.addCommand(const FakeCommand(
|
processManager.addCommand(const FakeCommand(
|
||||||
command: <String>[
|
command: <String>[
|
||||||
'example_chrome',
|
'example_chrome',
|
||||||
'--user-data-dir=/.tmp_rand0/flutter_tool.rand0',
|
'--user-data-dir=/.tmp_rand1/flutter_tool.rand1',
|
||||||
'--remote-debugging-port=10000',
|
'--remote-debugging-port=10000',
|
||||||
..._kChromeArgs,
|
..._kChromeArgs,
|
||||||
'example_url',
|
'example_url',
|
||||||
@ -102,7 +106,7 @@ void main() {
|
|||||||
processManager.addCommand(const FakeCommand(
|
processManager.addCommand(const FakeCommand(
|
||||||
command: <String>[
|
command: <String>[
|
||||||
'example_chrome',
|
'example_chrome',
|
||||||
'--user-data-dir=/.tmp_rand0/flutter_tool.rand0',
|
'--user-data-dir=/.tmp_rand1/flutter_tool.rand1',
|
||||||
'--remote-debugging-port=1234',
|
'--remote-debugging-port=1234',
|
||||||
..._kChromeArgs,
|
..._kChromeArgs,
|
||||||
'--headless',
|
'--headless',
|
||||||
@ -133,7 +137,7 @@ void main() {
|
|||||||
|
|
||||||
processManager.addCommand(FakeCommand(command: const <String>[
|
processManager.addCommand(FakeCommand(command: const <String>[
|
||||||
'example_chrome',
|
'example_chrome',
|
||||||
'--user-data-dir=/.tmp_rand0/flutter_tool.rand0',
|
'--user-data-dir=/.tmp_rand1/flutter_tool.rand1',
|
||||||
'--remote-debugging-port=1234',
|
'--remote-debugging-port=1234',
|
||||||
..._kChromeArgs,
|
..._kChromeArgs,
|
||||||
'example_url',
|
'example_url',
|
||||||
@ -146,7 +150,7 @@ void main() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
final File tempFile = fileSystem
|
final File tempFile = fileSystem
|
||||||
.directory('.tmp_rand0/flutter_tool.rand0')
|
.directory('.tmp_rand1/flutter_tool.rand1')
|
||||||
.childDirectory('Default')
|
.childDirectory('Default')
|
||||||
.childFile('preferences');
|
.childFile('preferences');
|
||||||
|
|
||||||
@ -163,3 +167,21 @@ void main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class MockOperatingSystemUtils extends Mock implements OperatingSystemUtils {}
|
class MockOperatingSystemUtils extends Mock implements OperatingSystemUtils {}
|
||||||
|
|
||||||
|
Future<Chrome> testLaunchChrome(String userDataDir, FakeProcessManager processManager, ChromeLauncher chromeLauncher) {
|
||||||
|
processManager.addCommand(FakeCommand(
|
||||||
|
command: <String>[
|
||||||
|
'example_chrome',
|
||||||
|
'--user-data-dir=$userDataDir',
|
||||||
|
'--remote-debugging-port=1234',
|
||||||
|
..._kChromeArgs,
|
||||||
|
'example_url',
|
||||||
|
],
|
||||||
|
stderr: kDevtoolsStderr,
|
||||||
|
));
|
||||||
|
|
||||||
|
return chromeLauncher.launch(
|
||||||
|
'example_url',
|
||||||
|
skipCheck: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user