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;
|
||||
}
|
||||
HttpServer httpServer;
|
||||
dynamic lastError;
|
||||
for (int i = 0; i < 5; i += 1) {
|
||||
const int kMaxRetries = 4;
|
||||
for (int i = 0; i <= kMaxRetries; i++) {
|
||||
try {
|
||||
httpServer = await HttpServer.bind(address, port ?? await globals.os.findFreePort());
|
||||
break;
|
||||
} on SocketException catch (error) {
|
||||
lastError = error;
|
||||
} on SocketException catch (e, s) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
if (httpServer == null) {
|
||||
throwToolExit('Failed to bind web development server:\n$lastError');
|
||||
}
|
||||
|
||||
// Allow rendering in a iframe.
|
||||
httpServer.defaultResponseHeaders.remove('x-frame-options', 'SAMEORIGIN');
|
||||
@ -240,7 +240,11 @@ class WebAssetServer implements AssetReader {
|
||||
webBuildDirectory: getWebBuildDirectory(),
|
||||
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;
|
||||
}
|
||||
|
||||
@ -266,9 +270,8 @@ class WebAssetServer implements AssetReader {
|
||||
};
|
||||
}
|
||||
|
||||
logging.Logger.root.onRecord.listen((logging.LogRecord event) {
|
||||
globals.printTrace('${event.loggerName}: ${event.message}');
|
||||
});
|
||||
logging.Logger.root.level = logging.Level.ALL;
|
||||
logging.Logger.root.onRecord.listen(_log);
|
||||
|
||||
// In debug builds, spin up DWDS and the full asset server.
|
||||
final Dwds dwds = await dwdsLauncher(
|
||||
@ -302,7 +305,11 @@ class WebAssetServer implements AssetReader {
|
||||
pipeline.addHandler(server.handleRequest);
|
||||
final shelf.Cascade cascade =
|
||||
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;
|
||||
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(
|
||||
FileSystem fileSystem, Logger logger) async {
|
||||
final String toolPackagePath =
|
||||
|
@ -266,11 +266,13 @@ class ChromiumLauncher {
|
||||
// only required for flutter_test --platform=chrome and not flutter run.
|
||||
bool hitGlibcBug = false;
|
||||
bool shouldRetry = false;
|
||||
final List<String> errors = <String>[];
|
||||
await process.stderr
|
||||
.transform(utf8.decoder)
|
||||
.transform(const LineSplitter())
|
||||
.map((String line) {
|
||||
_logger.printTrace('[CHROME]:$line');
|
||||
_logger.printTrace('[CHROME]: $line');
|
||||
errors.add('[CHROME]:$line');
|
||||
if (line.contains(_kGlibcError)) {
|
||||
hitGlibcBug = true;
|
||||
shouldRetry = true;
|
||||
@ -287,7 +289,8 @@ class ChromiumLauncher {
|
||||
return '';
|
||||
}
|
||||
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(
|
||||
'Failed to launch browser. Make sure you are using an up-to-date '
|
||||
'Chrome or Edge. Otherwise, consider using -d web-server instead '
|
||||
@ -395,7 +398,8 @@ class ChromiumLauncher {
|
||||
// connection is valid.
|
||||
if (!skipCheck) {
|
||||
try {
|
||||
await chrome.chromeConnection.getTabs();
|
||||
await chrome.chromeConnection.getTab(
|
||||
(ChromeTab tab) => true, retryFor: const Duration(seconds: 2));
|
||||
} on Exception catch (error, stackTrace) {
|
||||
_logger.printError('$error', stackTrace: stackTrace);
|
||||
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
|
||||
// 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.
|
||||
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}');
|
||||
}
|
||||
|
||||
|
@ -58,6 +58,7 @@ abstract class FlutterTestDriver {
|
||||
VmService? _vmService;
|
||||
String get lastErrorInfo => _errorBuffer.toString();
|
||||
Stream<String> get stdout => _stdout.stream;
|
||||
Stream<String> get stderr => _stderr.stream;
|
||||
int? get vmServicePort => _vmServiceWsUri?.port;
|
||||
bool get hasExited => _hasExited;
|
||||
Uri? get vmServiceWsUri => _vmServiceWsUri;
|
||||
@ -355,9 +356,9 @@ abstract class FlutterTestDriver {
|
||||
);
|
||||
}
|
||||
|
||||
Future<InstanceRef> evaluate(String targetId, String expression) async {
|
||||
return _timeoutWithMessages<InstanceRef>(
|
||||
() async => await _vmService!.evaluate(await _getFlutterIsolateId(), targetId, expression) as InstanceRef,
|
||||
Future<ObjRef> evaluate(String targetId, String expression) async {
|
||||
return _timeoutWithMessages<ObjRef>(
|
||||
() async => await _vmService!.evaluate(await _getFlutterIsolateId(), targetId, expression) as ObjRef,
|
||||
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 {
|
||||
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++) {
|
||||
processManager.addCommand(const FakeCommand(
|
||||
command: <String>[
|
||||
@ -508,13 +517,14 @@ void main() {
|
||||
}
|
||||
|
||||
await expectToolExitLater(
|
||||
chromeLauncher.launch(
|
||||
chromiumLauncher.launch(
|
||||
'example_url',
|
||||
skipCheck: true,
|
||||
headless: true,
|
||||
),
|
||||
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 {
|
||||
|
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(client2)]
|
||||
);
|
||||
}, skip: true); // DDS failure: https://github.com/dart-lang/sdk/issues/46696
|
||||
});
|
||||
});
|
||||
|
||||
group('Clients of flutter run on web with DDS disabled', () {
|
||||
|
Loading…
Reference in New Issue
Block a user