mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
De-flake web tool tests (#93948)
This commit is contained in:
parent
544527caa5
commit
ca62470796
@ -198,19 +198,19 @@ class WebAssetServer implements AssetReader {
|
|||||||
address = (await InternetAddress.lookup(hostname)).first;
|
address = (await InternetAddress.lookup(hostname)).first;
|
||||||
}
|
}
|
||||||
HttpServer httpServer;
|
HttpServer httpServer;
|
||||||
dynamic lastError;
|
const int kMaxRetries = 4;
|
||||||
for (int i = 0; i < 5; i += 1) {
|
for (int i = 0; i <= kMaxRetries; i++) {
|
||||||
try {
|
try {
|
||||||
httpServer = await HttpServer.bind(address, port ?? await globals.os.findFreePort());
|
httpServer = await HttpServer.bind(address, port ?? await globals.os.findFreePort());
|
||||||
break;
|
break;
|
||||||
} on SocketException catch (error) {
|
} on SocketException catch (e, s) {
|
||||||
lastError = error;
|
if (i >= kMaxRetries) {
|
||||||
|
globals.printError('Failed to bind web development server:\n$e', stackTrace: s);
|
||||||
|
throwToolExit('Failed to bind web development server:\n$e');
|
||||||
|
}
|
||||||
await Future<void>.delayed(const Duration(milliseconds: 100));
|
await Future<void>.delayed(const Duration(milliseconds: 100));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (httpServer == null) {
|
|
||||||
throwToolExit('Failed to bind web development server:\n$lastError');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allow rendering in a iframe.
|
// Allow rendering in a iframe.
|
||||||
httpServer.defaultResponseHeaders.remove('x-frame-options', 'SAMEORIGIN');
|
httpServer.defaultResponseHeaders.remove('x-frame-options', 'SAMEORIGIN');
|
||||||
@ -240,7 +240,11 @@ class WebAssetServer implements AssetReader {
|
|||||||
webBuildDirectory: getWebBuildDirectory(),
|
webBuildDirectory: getWebBuildDirectory(),
|
||||||
basePath: server.basePath,
|
basePath: server.basePath,
|
||||||
);
|
);
|
||||||
shelf.serveRequests(httpServer, releaseAssetServer.handle);
|
runZonedGuarded(() {
|
||||||
|
shelf.serveRequests(httpServer, releaseAssetServer.handle);
|
||||||
|
}, (Object e, StackTrace s) {
|
||||||
|
globals.printTrace('Release asset server: error serving requests: $e:$s');
|
||||||
|
});
|
||||||
return server;
|
return server;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,9 +270,8 @@ class WebAssetServer implements AssetReader {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
logging.Logger.root.onRecord.listen((logging.LogRecord event) {
|
logging.Logger.root.level = logging.Level.ALL;
|
||||||
globals.printTrace('${event.loggerName}: ${event.message}');
|
logging.Logger.root.onRecord.listen(_log);
|
||||||
});
|
|
||||||
|
|
||||||
// In debug builds, spin up DWDS and the full asset server.
|
// In debug builds, spin up DWDS and the full asset server.
|
||||||
final Dwds dwds = await dwdsLauncher(
|
final Dwds dwds = await dwdsLauncher(
|
||||||
@ -302,7 +305,11 @@ class WebAssetServer implements AssetReader {
|
|||||||
pipeline.addHandler(server.handleRequest);
|
pipeline.addHandler(server.handleRequest);
|
||||||
final shelf.Cascade cascade =
|
final shelf.Cascade cascade =
|
||||||
shelf.Cascade().add(dwds.handler).add(dwdsHandler);
|
shelf.Cascade().add(dwds.handler).add(dwdsHandler);
|
||||||
shelf.serveRequests(httpServer, cascade.handler);
|
runZonedGuarded(() {
|
||||||
|
shelf.serveRequests(httpServer, cascade.handler);
|
||||||
|
}, (Object e, StackTrace s) {
|
||||||
|
globals.printTrace('Dwds server: error serving requests: $e:$s');
|
||||||
|
});
|
||||||
server.dwds = dwds;
|
server.dwds = dwds;
|
||||||
return server;
|
return server;
|
||||||
}
|
}
|
||||||
@ -995,6 +1002,17 @@ class ReleaseAssetServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _log(logging.LogRecord event) {
|
||||||
|
final String error = event.error == null? '': 'Error: ${event.error}';
|
||||||
|
if (event.level >= logging.Level.SEVERE) {
|
||||||
|
globals.printError('${event.loggerName}: ${event.message}$error', stackTrace: event.stackTrace);
|
||||||
|
} else if (event.level == logging.Level.WARNING) {
|
||||||
|
globals.printWarning('${event.loggerName}: ${event.message}$error');
|
||||||
|
} else {
|
||||||
|
globals.printTrace('${event.loggerName}: ${event.message}$error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<Directory> _loadDwdsDirectory(
|
Future<Directory> _loadDwdsDirectory(
|
||||||
FileSystem fileSystem, Logger logger) async {
|
FileSystem fileSystem, Logger logger) async {
|
||||||
final String toolPackagePath =
|
final String toolPackagePath =
|
||||||
|
@ -266,11 +266,13 @@ class ChromiumLauncher {
|
|||||||
// only required for flutter_test --platform=chrome and not flutter run.
|
// only required for flutter_test --platform=chrome and not flutter run.
|
||||||
bool hitGlibcBug = false;
|
bool hitGlibcBug = false;
|
||||||
bool shouldRetry = false;
|
bool shouldRetry = false;
|
||||||
|
final List<String> errors = <String>[];
|
||||||
await process.stderr
|
await process.stderr
|
||||||
.transform(utf8.decoder)
|
.transform(utf8.decoder)
|
||||||
.transform(const LineSplitter())
|
.transform(const LineSplitter())
|
||||||
.map((String line) {
|
.map((String line) {
|
||||||
_logger.printTrace('[CHROME]:$line');
|
_logger.printTrace('[CHROME]: $line');
|
||||||
|
errors.add('[CHROME]:$line');
|
||||||
if (line.contains(_kGlibcError)) {
|
if (line.contains(_kGlibcError)) {
|
||||||
hitGlibcBug = true;
|
hitGlibcBug = true;
|
||||||
shouldRetry = true;
|
shouldRetry = true;
|
||||||
@ -287,7 +289,8 @@ class ChromiumLauncher {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
if (retry >= kMaxRetries) {
|
if (retry >= kMaxRetries) {
|
||||||
_logger.printTrace('Failed to launch browser after $kMaxRetries tries. Command used to launch it: ${args.join(' ')}');
|
errors.forEach(_logger.printError);
|
||||||
|
_logger.printError('Failed to launch browser after $kMaxRetries tries. Command used to launch it: ${args.join(' ')}');
|
||||||
throw ToolExit(
|
throw ToolExit(
|
||||||
'Failed to launch browser. Make sure you are using an up-to-date '
|
'Failed to launch browser. Make sure you are using an up-to-date '
|
||||||
'Chrome or Edge. Otherwise, consider using -d web-server instead '
|
'Chrome or Edge. Otherwise, consider using -d web-server instead '
|
||||||
@ -395,7 +398,8 @@ class ChromiumLauncher {
|
|||||||
// connection is valid.
|
// connection is valid.
|
||||||
if (!skipCheck) {
|
if (!skipCheck) {
|
||||||
try {
|
try {
|
||||||
await chrome.chromeConnection.getTabs();
|
await chrome.chromeConnection.getTab(
|
||||||
|
(ChromeTab tab) => true, retryFor: const Duration(seconds: 2));
|
||||||
} on Exception catch (error, stackTrace) {
|
} on Exception catch (error, stackTrace) {
|
||||||
_logger.printError('$error', stackTrace: stackTrace);
|
_logger.printError('$error', stackTrace: stackTrace);
|
||||||
await chrome.close();
|
await chrome.close();
|
||||||
|
@ -168,7 +168,7 @@ Future<void> evaluateComplexReturningExpressions(FlutterTestDriver flutter) asyn
|
|||||||
// Ensure we got a reasonable approximation. The more accurate we try to
|
// Ensure we got a reasonable approximation. The more accurate we try to
|
||||||
// make this, the more likely it'll fail due to differences in the time
|
// make this, the more likely it'll fail due to differences in the time
|
||||||
// in the remote VM and the local VM at the time the code runs.
|
// in the remote VM and the local VM at the time the code runs.
|
||||||
final InstanceRef res = await flutter.evaluate(resp.id, r'"$year-$month-$day"');
|
final ObjRef res = await flutter.evaluate(resp.id, r'"$year-$month-$day"');
|
||||||
expectValue(res, '${now.year}-${now.month}-${now.day}');
|
expectValue(res, '${now.year}-${now.month}-${now.day}');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,6 +58,7 @@ abstract class FlutterTestDriver {
|
|||||||
VmService? _vmService;
|
VmService? _vmService;
|
||||||
String get lastErrorInfo => _errorBuffer.toString();
|
String get lastErrorInfo => _errorBuffer.toString();
|
||||||
Stream<String> get stdout => _stdout.stream;
|
Stream<String> get stdout => _stdout.stream;
|
||||||
|
Stream<String> get stderr => _stderr.stream;
|
||||||
int? get vmServicePort => _vmServiceWsUri?.port;
|
int? get vmServicePort => _vmServiceWsUri?.port;
|
||||||
bool get hasExited => _hasExited;
|
bool get hasExited => _hasExited;
|
||||||
Uri? get vmServiceWsUri => _vmServiceWsUri;
|
Uri? get vmServiceWsUri => _vmServiceWsUri;
|
||||||
@ -355,9 +356,9 @@ abstract class FlutterTestDriver {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<InstanceRef> evaluate(String targetId, String expression) async {
|
Future<ObjRef> evaluate(String targetId, String expression) async {
|
||||||
return _timeoutWithMessages<InstanceRef>(
|
return _timeoutWithMessages<ObjRef>(
|
||||||
() async => await _vmService!.evaluate(await _getFlutterIsolateId(), targetId, expression) as InstanceRef,
|
() async => await _vmService!.evaluate(await _getFlutterIsolateId(), targetId, expression) as ObjRef,
|
||||||
task: 'Evaluating expression ($expression for $targetId)',
|
task: 'Evaluating expression ($expression for $targetId)',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -490,6 +490,15 @@ void main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
testWithoutContext('gives up retrying when an error happens more than 3 times', () async {
|
testWithoutContext('gives up retrying when an error happens more than 3 times', () async {
|
||||||
|
final BufferLogger logger = BufferLogger.test();
|
||||||
|
final ChromiumLauncher chromiumLauncher = ChromiumLauncher(
|
||||||
|
fileSystem: fileSystem,
|
||||||
|
platform: platform,
|
||||||
|
processManager: processManager,
|
||||||
|
operatingSystemUtils: operatingSystemUtils,
|
||||||
|
browserFinder: findChromeExecutable,
|
||||||
|
logger: logger,
|
||||||
|
);
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
processManager.addCommand(const FakeCommand(
|
processManager.addCommand(const FakeCommand(
|
||||||
command: <String>[
|
command: <String>[
|
||||||
@ -508,13 +517,14 @@ void main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await expectToolExitLater(
|
await expectToolExitLater(
|
||||||
chromeLauncher.launch(
|
chromiumLauncher.launch(
|
||||||
'example_url',
|
'example_url',
|
||||||
skipCheck: true,
|
skipCheck: true,
|
||||||
headless: true,
|
headless: true,
|
||||||
),
|
),
|
||||||
contains('Failed to launch browser.'),
|
contains('Failed to launch browser.'),
|
||||||
);
|
);
|
||||||
|
expect(logger.errorText, contains('nothing in the std error indicating glibc error'));
|
||||||
});
|
});
|
||||||
|
|
||||||
testWithoutContext('Logs an error and exits if connection check fails.', () async {
|
testWithoutContext('Logs an error and exits if connection check fails.', () async {
|
||||||
|
81
packages/flutter_tools/test/web.shard/output_web_test.dart
Normal file
81
packages/flutter_tools/test/web.shard/output_web_test.dart
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
// @dart = 2.8
|
||||||
|
|
||||||
|
import 'package:file/file.dart';
|
||||||
|
import 'package:flutter_tools/src/base/file_system.dart';
|
||||||
|
import 'package:vm_service/vm_service.dart';
|
||||||
|
import 'package:vm_service/vm_service_io.dart';
|
||||||
|
|
||||||
|
import '../integration.shard/test_data/basic_project.dart';
|
||||||
|
import '../integration.shard/test_driver.dart';
|
||||||
|
import '../integration.shard/test_utils.dart';
|
||||||
|
import '../src/common.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
Directory tempDir;
|
||||||
|
final BasicProjectWithUnaryMain project = BasicProjectWithUnaryMain();
|
||||||
|
FlutterRunTestDriver flutter;
|
||||||
|
|
||||||
|
setUp(() async {
|
||||||
|
tempDir = createResolvedTempDirectorySync('run_test.');
|
||||||
|
await project.setUpIn(tempDir);
|
||||||
|
flutter = FlutterRunTestDriver(tempDir);
|
||||||
|
//flutter.stdout.listen(print);
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() async {
|
||||||
|
await flutter.stop();
|
||||||
|
tryToDelete(tempDir);
|
||||||
|
});
|
||||||
|
|
||||||
|
Future<void> start({bool verbose = false}) async {
|
||||||
|
// The non-test project has a loop around its breakpoints.
|
||||||
|
// No need to start paused as all breakpoint would be eventually reached.
|
||||||
|
await flutter.run(
|
||||||
|
withDebugger: true,
|
||||||
|
chrome: true,
|
||||||
|
expressionEvaluation: true,
|
||||||
|
additionalCommandArgs: <String>[
|
||||||
|
if (verbose) '--verbose',
|
||||||
|
'--web-renderer=html',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> evaluate() async {
|
||||||
|
final ObjRef res =
|
||||||
|
await flutter.evaluate('package:characters/characters.dart', 'true');
|
||||||
|
expect(res, isA<InstanceRef>()
|
||||||
|
.having((InstanceRef o) => o.kind, 'kind', 'Bool'));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> sendEvent(Map<String, Object> event) async {
|
||||||
|
final VmService client = await vmServiceConnectUri(
|
||||||
|
'${flutter.vmServiceWsUri}');
|
||||||
|
final Response result = await client.callServiceExtension(
|
||||||
|
'ext.dwds.sendEvent',
|
||||||
|
args: event,
|
||||||
|
);
|
||||||
|
expect(result, isA<Success>());
|
||||||
|
await client.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
testWithoutContext('flutter run outputs info messages from dwds in verbose mode', () async {
|
||||||
|
final Future<dynamic> info = expectLater(
|
||||||
|
flutter.stdout, emitsThrough(contains('Loaded debug metadata')));
|
||||||
|
await start(verbose: true);
|
||||||
|
await evaluate();
|
||||||
|
await flutter.stop();
|
||||||
|
await info;
|
||||||
|
});
|
||||||
|
|
||||||
|
testWithoutContext('flutter run outputs warning messages from dwds in non-verbose mode', () async {
|
||||||
|
final Future<dynamic> warning = expectLater(
|
||||||
|
flutter.stderr, emitsThrough(contains('Ignoring unknown event')));
|
||||||
|
await start();
|
||||||
|
await sendEvent(<String, Object>{'type': 'DevtoolsEvent'});
|
||||||
|
await warning;
|
||||||
|
});
|
||||||
|
}
|
@ -62,7 +62,7 @@ void main() {
|
|||||||
validateFlutterVersion(client1),
|
validateFlutterVersion(client1),
|
||||||
validateFlutterVersion(client2)]
|
validateFlutterVersion(client2)]
|
||||||
);
|
);
|
||||||
}, skip: true); // DDS failure: https://github.com/dart-lang/sdk/issues/46696
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
group('Clients of flutter run on web with DDS disabled', () {
|
group('Clients of flutter run on web with DDS disabled', () {
|
||||||
|
Loading…
Reference in New Issue
Block a user