add web_long_running_tests shard containing long-running web tests (#67324)
This commit is contained in:
parent
7b0f38b117
commit
2fa03438de
@ -128,10 +128,8 @@ Future<Command> startCommand(String executable, List<String> arguments, {
|
|||||||
.transform(const Utf8Encoder());
|
.transform(const Utf8Encoder());
|
||||||
switch (outputMode) {
|
switch (outputMode) {
|
||||||
case OutputMode.print:
|
case OutputMode.print:
|
||||||
await Future.wait<void>(<Future<void>>[
|
stdoutSource.listen(io.stdout.add);
|
||||||
io.stdout.addStream(stdoutSource),
|
process.stderr.listen(io.stderr.add);
|
||||||
io.stderr.addStream(process.stderr),
|
|
||||||
]);
|
|
||||||
break;
|
break;
|
||||||
case OutputMode.capture:
|
case OutputMode.capture:
|
||||||
savedStdout = stdoutSource.toList();
|
savedStdout = stdoutSource.toList();
|
||||||
|
@ -75,6 +75,11 @@ int get webShardCount => Platform.environment.containsKey('WEB_SHARD_COUNT')
|
|||||||
? int.parse(Platform.environment['WEB_SHARD_COUNT'])
|
? int.parse(Platform.environment['WEB_SHARD_COUNT'])
|
||||||
: 8;
|
: 8;
|
||||||
|
|
||||||
|
/// The number of shards the long-running Web tests are split into.
|
||||||
|
///
|
||||||
|
/// WARNING: this number must match the shard count in LUCI configs.
|
||||||
|
const int kWebLongRunningTestShardCount = 3;
|
||||||
|
|
||||||
/// 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 as part of https://github.com/flutter/flutter/projects/60
|
// TODO(yjbanov): we're getting rid of this as part of https://github.com/flutter/flutter/projects/60
|
||||||
@ -122,6 +127,7 @@ Future<void> main(List<String> args) async {
|
|||||||
'tool_tests': _runToolTests,
|
'tool_tests': _runToolTests,
|
||||||
'web_tests': _runWebUnitTests,
|
'web_tests': _runWebUnitTests,
|
||||||
'web_integration_tests': _runWebIntegrationTests,
|
'web_integration_tests': _runWebIntegrationTests,
|
||||||
|
'web_long_running_tests': _runWebLongRunningTests,
|
||||||
});
|
});
|
||||||
} on ExitException catch (error) {
|
} on ExitException catch (error) {
|
||||||
error.apply();
|
error.apply();
|
||||||
@ -813,6 +819,125 @@ Future<void> _runWebUnitTests() async {
|
|||||||
await selectSubshard(subshards);
|
await selectSubshard(subshards);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Coarse-grained integration tests running on the Web.
|
||||||
|
///
|
||||||
|
/// These tests are sharded into [kWebLongRunningTestShardCount] shards.
|
||||||
|
Future<void> _runWebLongRunningTests() async {
|
||||||
|
final List<ShardRunner> tests = <ShardRunner>[
|
||||||
|
() => _runGalleryE2eWebTest('debug'),
|
||||||
|
() => _runGalleryE2eWebTest('debug', canvasKit: true),
|
||||||
|
() => _runGalleryE2eWebTest('profile'),
|
||||||
|
() => _runGalleryE2eWebTest('profile', canvasKit: true),
|
||||||
|
() => _runGalleryE2eWebTest('release'),
|
||||||
|
() => _runGalleryE2eWebTest('release', canvasKit: true),
|
||||||
|
].map(_withChromeDriver).toList();
|
||||||
|
await _selectIndexedSubshard(tests, kWebLongRunningTestShardCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The `chromedriver` process created by this test.
|
||||||
|
//
|
||||||
|
// If an existing chromedriver is already available on port 4444, the existing
|
||||||
|
// process is reused and this variable remains null.
|
||||||
|
Command _chromeDriver;
|
||||||
|
|
||||||
|
/// Creates a shard runner that runs the given [originalRunner] with ChromeDriver
|
||||||
|
/// enabled.
|
||||||
|
ShardRunner _withChromeDriver(ShardRunner originalRunner) {
|
||||||
|
return () async {
|
||||||
|
try {
|
||||||
|
await _ensureChromeDriverIsRunning();
|
||||||
|
await originalRunner();
|
||||||
|
} finally {
|
||||||
|
await _stopChromeDriver();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> _isChromeDriverRunning() async {
|
||||||
|
try {
|
||||||
|
(await Socket.connect('localhost', 4444)).destroy();
|
||||||
|
return true;
|
||||||
|
} on SocketException {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _ensureChromeDriverIsRunning() async {
|
||||||
|
// If we cannot connect to ChromeDriver, assume it is not running. Launch it.
|
||||||
|
if (!await _isChromeDriverRunning()) {
|
||||||
|
print('Starting chromedriver');
|
||||||
|
// Assume chromedriver is in the PATH.
|
||||||
|
_chromeDriver = await startCommand(
|
||||||
|
'chromedriver',
|
||||||
|
<String>['--port=4444'],
|
||||||
|
);
|
||||||
|
while (!await _isChromeDriverRunning()) {
|
||||||
|
await Future<void>.delayed(const Duration(milliseconds: 100));
|
||||||
|
print('Waiting for chromedriver to start up.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final HttpClient client = HttpClient();
|
||||||
|
final Uri chromeDriverUrl = Uri.parse('http://localhost:4444/status');
|
||||||
|
final HttpClientRequest request = await client.getUrl(chromeDriverUrl);
|
||||||
|
final HttpClientResponse response = await request.close();
|
||||||
|
final Map<String, dynamic> webDriverStatus = json.decode(await response.transform(utf8.decoder).join('')) as Map<String, dynamic>;
|
||||||
|
client.close();
|
||||||
|
final bool webDriverReady = webDriverStatus['value']['ready'] as bool;
|
||||||
|
if (!webDriverReady) {
|
||||||
|
throw Exception('WebDriver not available.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _stopChromeDriver() async {
|
||||||
|
if (_chromeDriver == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_chromeDriver.process.kill();
|
||||||
|
while (await _isChromeDriverRunning()) {
|
||||||
|
await Future<void>.delayed(const Duration(milliseconds: 100));
|
||||||
|
print('Waiting for chromedriver to stop.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Exercises the old gallery in a browser for a long period of time, looking
|
||||||
|
/// for memory leaks and dangling pointers.
|
||||||
|
///
|
||||||
|
/// This is not a performance test.
|
||||||
|
///
|
||||||
|
/// If [canvasKit] is set to true, runs the test in CanvasKit mode.
|
||||||
|
///
|
||||||
|
/// The test is written using `package:integration_test` (despite the "e2e" in
|
||||||
|
/// the name, which is there for historic reasons).
|
||||||
|
Future<void> _runGalleryE2eWebTest(String buildMode, { bool canvasKit = false }) async {
|
||||||
|
print('${green}Running flutter_gallery integration test in --$buildMode using ${canvasKit ? 'CanvasKit' : 'HTML'} renderer.$reset');
|
||||||
|
final String testAppDirectory = path.join(flutterRoot, 'dev', 'integration_tests', 'flutter_gallery');
|
||||||
|
await runCommand(
|
||||||
|
flutter,
|
||||||
|
<String>[ 'clean' ],
|
||||||
|
workingDirectory: testAppDirectory,
|
||||||
|
);
|
||||||
|
await runCommand(
|
||||||
|
flutter,
|
||||||
|
<String>[
|
||||||
|
'drive',
|
||||||
|
if (canvasKit)
|
||||||
|
'--dart-define=FLUTTER_WEB_USE_SKIA=true',
|
||||||
|
'--driver=test_driver/transitions_perf_e2e_test.dart',
|
||||||
|
'--target=test_driver/transitions_perf_e2e.dart',
|
||||||
|
'--browser-name=chrome',
|
||||||
|
'-d',
|
||||||
|
'web-server',
|
||||||
|
'--$buildMode',
|
||||||
|
],
|
||||||
|
workingDirectory: testAppDirectory,
|
||||||
|
environment: <String, String>{
|
||||||
|
'FLUTTER_WEB': 'true',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
print('${green}Integration test passed.$reset');
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _runWebIntegrationTests() async {
|
Future<void> _runWebIntegrationTests() async {
|
||||||
await _runWebStackTraceTest('profile', 'lib/stack_trace.dart');
|
await _runWebStackTraceTest('profile', 'lib/stack_trace.dart');
|
||||||
await _runWebStackTraceTest('release', 'lib/stack_trace.dart');
|
await _runWebStackTraceTest('release', 'lib/stack_trace.dart');
|
||||||
|
@ -5,12 +5,21 @@
|
|||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
import 'package:flutter_gallery/demo_lists.dart';
|
import 'package:flutter_gallery/demo_lists.dart';
|
||||||
|
|
||||||
const List<String> kSkippedDemos = <String>[];
|
/// The demos we don't run as part of the integraiton test.
|
||||||
|
///
|
||||||
|
/// Demo names are formatted as 'DEMO_NAME@DEMO_CATEGORY' (see
|
||||||
|
/// `demo_lists.dart` for more examples).
|
||||||
|
final List<String> kSkippedDemos = <String>[
|
||||||
|
// The CI uses Chromium, which lacks the video codecs to run this demo.
|
||||||
|
if (kIsWeb)
|
||||||
|
'Video@Media',
|
||||||
|
];
|
||||||
|
|
||||||
/// Scrolls each demo menu item into view, launches it, then returns to the
|
/// Scrolls each demo menu item into view, launches it, then returns to the
|
||||||
/// home screen twice.
|
/// home screen twice.
|
||||||
|
@ -14,8 +14,6 @@ import 'package:flutter_gallery/demo_lists.dart';
|
|||||||
|
|
||||||
import 'run_demos.dart';
|
import 'run_demos.dart';
|
||||||
|
|
||||||
const List<String> kSkippedDemos = <String>[];
|
|
||||||
|
|
||||||
// All of the gallery demos, identified as "title@category".
|
// All of the gallery demos, identified as "title@category".
|
||||||
//
|
//
|
||||||
// These names are reported by the test app, see _handleMessages()
|
// These names are reported by the test app, see _handleMessages()
|
||||||
|
@ -187,7 +187,11 @@ class WebFlutterDriver extends FlutterDriver {
|
|||||||
class FlutterWebConnection {
|
class FlutterWebConnection {
|
||||||
/// Creates a FlutterWebConnection with WebDriver
|
/// Creates a FlutterWebConnection with WebDriver
|
||||||
/// and whether the WebDriver supports timeline action.
|
/// and whether the WebDriver supports timeline action.
|
||||||
FlutterWebConnection(this._driver, this.supportsTimelineAction);
|
FlutterWebConnection(this._driver, this.supportsTimelineAction) {
|
||||||
|
_driver.logs.get(async_io.LogType.browser).listen((async_io.LogEntry entry) {
|
||||||
|
print('[${entry.level}]: ${entry.message}');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
final async_io.WebDriver _driver;
|
final async_io.WebDriver _driver;
|
||||||
|
|
||||||
|
@ -186,7 +186,10 @@ Map<String, dynamic> getDesiredCapabilities(Browser browser, bool headless, [Str
|
|||||||
return <String, dynamic>{
|
return <String, dynamic>{
|
||||||
'acceptInsecureCerts': true,
|
'acceptInsecureCerts': true,
|
||||||
'browserName': 'chrome',
|
'browserName': 'chrome',
|
||||||
'goog:loggingPrefs': <String, String>{ async_io.LogType.performance: 'ALL'},
|
'goog:loggingPrefs': <String, String>{
|
||||||
|
async_io.LogType.browser: 'INFO',
|
||||||
|
async_io.LogType.performance: 'ALL',
|
||||||
|
},
|
||||||
'chromeOptions': <String, dynamic>{
|
'chromeOptions': <String, dynamic>{
|
||||||
if (chromeBinary != null)
|
if (chromeBinary != null)
|
||||||
'binary': chromeBinary,
|
'binary': chromeBinary,
|
||||||
|
@ -12,7 +12,10 @@ void main() {
|
|||||||
final Map<String, dynamic> expected = <String, dynamic>{
|
final Map<String, dynamic> expected = <String, dynamic>{
|
||||||
'acceptInsecureCerts': true,
|
'acceptInsecureCerts': true,
|
||||||
'browserName': 'chrome',
|
'browserName': 'chrome',
|
||||||
'goog:loggingPrefs': <String, String>{ sync_io.LogType.performance: 'ALL'},
|
'goog:loggingPrefs': <String, String>{
|
||||||
|
sync_io.LogType.browser: 'INFO',
|
||||||
|
sync_io.LogType.performance: 'ALL',
|
||||||
|
},
|
||||||
'chromeOptions': <String, dynamic>{
|
'chromeOptions': <String, dynamic>{
|
||||||
'w3c': false,
|
'w3c': false,
|
||||||
'args': <String>[
|
'args': <String>[
|
||||||
@ -44,7 +47,10 @@ void main() {
|
|||||||
final Map<String, dynamic> expected = <String, dynamic>{
|
final Map<String, dynamic> expected = <String, dynamic>{
|
||||||
'acceptInsecureCerts': true,
|
'acceptInsecureCerts': true,
|
||||||
'browserName': 'chrome',
|
'browserName': 'chrome',
|
||||||
'goog:loggingPrefs': <String, String>{ sync_io.LogType.performance: 'ALL'},
|
'goog:loggingPrefs': <String, String>{
|
||||||
|
sync_io.LogType.browser: 'INFO',
|
||||||
|
sync_io.LogType.performance: 'ALL',
|
||||||
|
},
|
||||||
'chromeOptions': <String, dynamic>{
|
'chromeOptions': <String, dynamic>{
|
||||||
'binary': chromeBinary,
|
'binary': chromeBinary,
|
||||||
'w3c': false,
|
'w3c': false,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user