Skip to content

Commit cb381ce

Browse files
[ci] Pin Chromium version for web tests (flutter#4620)
Switches the web tests from using the version of Chrome installed by the Dockerfile, which is whatever happened to be stable when the image is generated, and thus not hermetic, to a pinned version of Chromium. This uses a slightly modified version of the script that is already used for flutter/packages. Since Chromium doesn't support mp4 playback, this updates the `video_player` integration tests to use WebM on web instead, to avoid having all the tests fail in CI. Part of flutter/flutter#84712
1 parent a10ac2e commit cb381ce

File tree

11 files changed

+194
-62
lines changed

11 files changed

+194
-62
lines changed

.cirrus.yml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -246,13 +246,15 @@ task:
246246
matrix:
247247
CHANNEL: "master"
248248
CHANNEL: "stable"
249+
CHROME_NO_SANDBOX: true
250+
CHROME_DIR: /tmp/web_chromium/
251+
CHROME_EXECUTABLE: $CHROME_DIR/chrome-linux/chrome
249252
install_script:
250-
- git clone https://github.com/flutter/web_installers.git
251-
- cd web_installers/packages/web_drivers/
252-
- dart pub get
253+
# Install a pinned version of Chromium and its corresponding ChromeDriver.
254+
# Setting CHROME_EXECUTABLE above causes this version to be used for tests.
255+
- ./script/install_chromium.sh "$CHROME_DIR"
253256
chromedriver_background_script:
254-
- cd web_installers/packages/web_drivers/
255-
- dart lib/web_driver_installer.dart chromedriver --install-only
257+
- cd "$CHROME_DIR"
256258
- ./chromedriver/chromedriver --port=4444
257259
build_script:
258260
- ./script/tool_runner.sh build-examples --web

packages/video_player/video_player/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
* Fixes integration tests.
1616
* Updates Android compileSdkVersion to 31.
1717
* Fixes a flaky integration test.
18+
* Integration tests now use WebM on web, to allow running with Chromium.
1819

1920
## 2.2.7
2021

Binary file not shown.

packages/video_player/video_player/example/integration_test/controller_swap_test.dart

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,15 @@ void main() {
1717
testWidgets(
1818
'can substitute one controller by another without crashing',
1919
(WidgetTester tester) async {
20-
VideoPlayerController controller = VideoPlayerController.network(
21-
'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4',
20+
// Use WebM for web to allow CI to use Chromium.
21+
final String videoAssetKey =
22+
kIsWeb ? 'assets/Butterfly-209.webm' : 'assets/Butterfly-209.mp4';
23+
24+
VideoPlayerController controller = VideoPlayerController.asset(
25+
videoAssetKey,
2226
);
23-
VideoPlayerController another = VideoPlayerController.network(
24-
'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4',
27+
VideoPlayerController another = VideoPlayerController.asset(
28+
videoAssetKey,
2529
);
2630
await controller.initialize();
2731
await another.initialize();

packages/video_player/video_player/example/integration_test/video_player_test.dart

Lines changed: 82 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,28 @@ import 'package:video_player/video_player.dart';
1616

1717
const Duration _playDuration = Duration(seconds: 1);
1818

19+
// Use WebM for web to allow CI to use Chromium.
20+
final String _videoAssetKey =
21+
kIsWeb ? 'assets/Butterfly-209.webm' : 'assets/Butterfly-209.mp4';
22+
23+
// Returns the URL to load an asset from this example app as a network source.
24+
String getUrlForAssetAsNetworkSource(String assetKey) {
25+
return 'https://github.com/flutter/plugins/blob/'
26+
// This hash can be rolled forward to pick up newly-added assets.
27+
'cba393233e559c925a4daf71b06b4bb01c606762'
28+
'/packages/video_player/video_player/example/'
29+
'$assetKey'
30+
'?raw=true';
31+
}
32+
1933
void main() {
2034
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
2135
late VideoPlayerController _controller;
2236
tearDown(() async => _controller.dispose());
2337

2438
group('asset videos', () {
2539
setUp(() {
26-
_controller = VideoPlayerController.asset('assets/Butterfly-209.mp4');
40+
_controller = VideoPlayerController.asset(_videoAssetKey);
2741
});
2842

2943
testWidgets('can be initialized', (WidgetTester tester) async {
@@ -32,48 +46,11 @@ void main() {
3246
expect(_controller.value.isInitialized, true);
3347
expect(_controller.value.position, const Duration(seconds: 0));
3448
expect(_controller.value.isPlaying, false);
49+
// The WebM version has a slightly different duration than the MP4.
3550
expect(_controller.value.duration,
36-
const Duration(seconds: 7, milliseconds: 540));
51+
Duration(seconds: 7, milliseconds: kIsWeb ? 544 : 540));
3752
});
3853

39-
testWidgets(
40-
'reports buffering status',
41-
(WidgetTester tester) async {
42-
VideoPlayerController networkController = VideoPlayerController.network(
43-
'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4',
44-
);
45-
await networkController.initialize();
46-
// Mute to allow playing without DOM interaction on Web.
47-
// See https://developers.google.com/web/updates/2017/09/autoplay-policy-changes
48-
await networkController.setVolume(0);
49-
final Completer<void> started = Completer();
50-
final Completer<void> ended = Completer();
51-
networkController.addListener(() {
52-
if (!started.isCompleted && networkController.value.isBuffering) {
53-
started.complete();
54-
}
55-
if (started.isCompleted &&
56-
!networkController.value.isBuffering &&
57-
!ended.isCompleted) {
58-
ended.complete();
59-
}
60-
});
61-
62-
await networkController.play();
63-
await networkController.seekTo(const Duration(seconds: 5));
64-
await tester.pumpAndSettle(_playDuration);
65-
await networkController.pause();
66-
67-
expect(networkController.value.isPlaying, false);
68-
expect(networkController.value.position,
69-
(Duration position) => position > const Duration(seconds: 0));
70-
71-
await expectLater(started.future, completes);
72-
await expectLater(ended.future, completes);
73-
},
74-
skip: !(kIsWeb || defaultTargetPlatform == TargetPlatform.android),
75-
);
76-
7754
testWidgets(
7855
'live stream duration != 0',
7956
(WidgetTester tester) async {
@@ -221,23 +198,78 @@ void main() {
221198
skip: kIsWeb || // Web does not support local assets.
222199
// Extremely flaky on iOS: https://github.com/flutter/flutter/issues/86915
223200
defaultTargetPlatform == TargetPlatform.iOS);
201+
});
224202

225-
testWidgets('test video player using static file() method as constructor',
226-
(WidgetTester tester) async {
203+
group('file-based videos', () {
204+
setUp(() async {
205+
// Load the data from the asset.
227206
String tempDir = (await getTemporaryDirectory()).path;
228-
ByteData bytes = await rootBundle.load('assets/Butterfly-209.mp4');
207+
ByteData bytes = await rootBundle.load(_videoAssetKey);
229208

230-
File file = File('$tempDir/Butterfly-209.mp4');
209+
// Write it to a file to use as a source.
210+
final String filename = _videoAssetKey.split('/').last;
211+
File file = File('$tempDir/$filename');
231212
await file.writeAsBytes(bytes.buffer.asInt8List());
232213

233-
VideoPlayerController fileController = VideoPlayerController.file(file);
234-
await fileController.initialize();
214+
_controller = VideoPlayerController.file(file);
215+
});
235216

236-
await fileController.play();
237-
expect(fileController.value.isPlaying, true);
217+
testWidgets('test video player using static file() method as constructor',
218+
(WidgetTester tester) async {
219+
await _controller.initialize();
238220

239-
await fileController.pause();
240-
expect(fileController.value.isPlaying, false);
221+
await _controller.play();
222+
expect(_controller.value.isPlaying, true);
223+
224+
await _controller.pause();
225+
expect(_controller.value.isPlaying, false);
241226
}, skip: kIsWeb);
242227
});
228+
229+
group('network videos', () {
230+
setUp(() {
231+
// TODO(stuartmorgan): Remove this conditional and update the hash in
232+
// getUrlForAssetAsNetworkSource as a follow-up, once the webm asset is
233+
// checked in.
234+
final String videoUrl = kIsWeb
235+
? 'https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm'
236+
: getUrlForAssetAsNetworkSource(_videoAssetKey);
237+
_controller = VideoPlayerController.network(videoUrl);
238+
});
239+
240+
testWidgets(
241+
'reports buffering status',
242+
(WidgetTester tester) async {
243+
await _controller.initialize();
244+
// Mute to allow playing without DOM interaction on Web.
245+
// See https://developers.google.com/web/updates/2017/09/autoplay-policy-changes
246+
await _controller.setVolume(0);
247+
final Completer<void> started = Completer();
248+
final Completer<void> ended = Completer();
249+
_controller.addListener(() {
250+
if (!started.isCompleted && _controller.value.isBuffering) {
251+
started.complete();
252+
}
253+
if (started.isCompleted &&
254+
!_controller.value.isBuffering &&
255+
!ended.isCompleted) {
256+
ended.complete();
257+
}
258+
});
259+
260+
await _controller.play();
261+
await _controller.seekTo(const Duration(seconds: 5));
262+
await tester.pumpAndSettle(_playDuration);
263+
await _controller.pause();
264+
265+
expect(_controller.value.isPlaying, false);
266+
expect(_controller.value.position,
267+
(Duration position) => position > const Duration(seconds: 0));
268+
269+
await expectLater(started.future, completes);
270+
await expectLater(ended.future, completes);
271+
},
272+
skip: !(kIsWeb || defaultTargetPlatform == TargetPlatform.android),
273+
);
274+
});
243275
}

packages/video_player/video_player/example/pubspec.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,6 @@ flutter:
3333
assets:
3434
- assets/flutter-mark-square-64.png
3535
- assets/Butterfly-209.mp4
36+
- assets/Butterfly-209.webm
3637
- assets/bumble_bee_captions.srt
3738
- assets/bumble_bee_captions.vtt

script/install_chromium.sh

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#!/bin/bash
2+
# Copyright 2013 The Flutter Authors. All rights reserved.
3+
# Use of this source code is governed by a BSD-style license that can be
4+
# found in the LICENSE file.
5+
set -e
6+
set -x
7+
8+
readonly TARGET_DIR=$1
9+
10+
# The build of Chromium used to test web functionality.
11+
#
12+
# Chromium builds can be located here: https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html?prefix=Linux_x64/
13+
readonly CHROMIUM_BUILD=768968
14+
# The ChromeDriver version corresponding to the build above. See
15+
# https://chromedriver.chromium.org/downloads
16+
# for versions mappings when updating Chromium.
17+
readonly CHROME_DRIVER_VERSION=84.0.4147.30
18+
19+
# Install Chromium.
20+
mkdir "$TARGET_DIR"
21+
wget --no-verbose "https://www.googleapis.com/download/storage/v1/b/chromium-browser-snapshots/o/Linux_x64%2F${CHROMIUM_BUILD}%2Fchrome-linux.zip?alt=media" -O "$TARGET_DIR"/chromium.zip
22+
unzip "$TARGET_DIR"/chromium.zip -d "$TARGET_DIR"/
23+
24+
# Install ChromeDriver.
25+
readonly DRIVER_ZIP_FILE="$TARGET_DIR/chromedriver.zip"
26+
wget --no-verbose "https://chromedriver.storage.googleapis.com/$CHROME_DRIVER_VERSION/chromedriver_linux64.zip" -O "$DRIVER_ZIP_FILE"
27+
unzip "$DRIVER_ZIP_FILE" -d "$TARGET_DIR/chromedriver"
28+
29+
# Echo info at the end for ease of debugging.
30+
export CHROME_EXECUTABLE="$TARGET_DIR"/chrome-linux/chrome
31+
echo $CHROME_EXECUTABLE
32+
$CHROME_EXECUTABLE --version
33+
echo "ChromeDriver $CHROME_DRIVER_VERSION"

script/tool/CHANGELOG.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@
1010
`--packages=path_provide_ios` now works.
1111
- `--run-on-changed-packages` now includes only the changed packages in a
1212
federated plugin, not all packages in that plugin.
13-
- Fix `federation-safety-check` handling of plugin deletion, and of top-level
13+
- Fixes `federation-safety-check` handling of plugin deletion, and of top-level
1414
files in unfederated plugins whose names match federated plugin heuristics
1515
(e.g., `packages/foo/foo_android.iml`).
16-
- Add an auto-retry for failed Firebase Test Lab tests as a short-term patch
16+
- Adds an auto-retry for failed Firebase Test Lab tests as a short-term patch
1717
for flake issues.
18+
- Adds support for `CHROME_EXECUTABLE` in `drive-examples` to match similar
19+
`flutter` behavior.
1820

1921
## 0.7.3
2022

script/tool/lib/src/drive_examples_command.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,9 @@ class DriveExamplesCommand extends PackageLoopingCommand {
120120
'-d',
121121
'web-server',
122122
'--web-port=7357',
123-
'--browser-name=chrome'
123+
'--browser-name=chrome',
124+
if (platform.environment.containsKey('CHROME_EXECUTABLE'))
125+
'--chrome-binary=${platform.environment['CHROME_EXECUTABLE']}',
124126
],
125127
if (getBoolArg(kPlatformWindows))
126128
kPlatformWindows: <String>['-d', 'windows'],

script/tool/test/drive_examples_command_test.dart

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,58 @@ void main() {
584584
]));
585585
});
586586

587+
test('driving a web plugin with CHROME_EXECUTABLE', () async {
588+
final Directory pluginDirectory = createFakePlugin(
589+
'plugin',
590+
packagesDir,
591+
extraFiles: <String>[
592+
'example/test_driver/plugin_test.dart',
593+
'example/test_driver/plugin.dart',
594+
],
595+
platformSupport: <String, PlatformDetails>{
596+
kPlatformWeb: const PlatformDetails(PlatformSupport.inline),
597+
},
598+
);
599+
600+
final Directory pluginExampleDirectory =
601+
pluginDirectory.childDirectory('example');
602+
603+
mockPlatform.environment['CHROME_EXECUTABLE'] = '/path/to/chrome';
604+
605+
final List<String> output = await runCapturingPrint(runner, <String>[
606+
'drive-examples',
607+
'--web',
608+
]);
609+
610+
expect(
611+
output,
612+
containsAllInOrder(<Matcher>[
613+
contains('Running for plugin'),
614+
contains('No issues found!'),
615+
]),
616+
);
617+
618+
expect(
619+
processRunner.recordedCalls,
620+
orderedEquals(<ProcessCall>[
621+
ProcessCall(
622+
getFlutterCommand(mockPlatform),
623+
const <String>[
624+
'drive',
625+
'-d',
626+
'web-server',
627+
'--web-port=7357',
628+
'--browser-name=chrome',
629+
'--chrome-binary=/path/to/chrome',
630+
'--driver',
631+
'test_driver/plugin_test.dart',
632+
'--target',
633+
'test_driver/plugin.dart'
634+
],
635+
pluginExampleDirectory.path),
636+
]));
637+
});
638+
587639
test('driving when plugin does not suppport Windows is a no-op', () async {
588640
createFakePlugin('plugin', packagesDir, extraFiles: <String>[
589641
'example/test_driver/plugin_test.dart',

0 commit comments

Comments
 (0)