Unverified Commit ca624707 authored by Anna Gringauze's avatar Anna Gringauze Committed by GitHub

De-flake web tool tests (#93948)

parent 544527ca
...@@ -198,18 +198,18 @@ class WebAssetServer implements AssetReader { ...@@ -198,18 +198,18 @@ 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) {
await Future<void>.delayed(const Duration(milliseconds: 100)); 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. // Allow rendering in a iframe.
...@@ -240,7 +240,11 @@ class WebAssetServer implements AssetReader { ...@@ -240,7 +240,11 @@ class WebAssetServer implements AssetReader {
webBuildDirectory: getWebBuildDirectory(), webBuildDirectory: getWebBuildDirectory(),
basePath: server.basePath, basePath: server.basePath,
); );
runZonedGuarded(() {
shelf.serveRequests(httpServer, releaseAssetServer.handle); 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 { ...@@ -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 { ...@@ -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);
runZonedGuarded(() {
shelf.serveRequests(httpServer, cascade.handler); 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 { ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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 ...@@ -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 { ...@@ -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 { ...@@ -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() { ...@@ -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() { ...@@ -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 {
......
// 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() { ...@@ -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', () {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment