Unverified Commit b3c319e4 authored by Danny Tuppeny's avatar Danny Tuppeny Committed by GitHub

Add integration tests to ensure flutter run does not quit at startup (#19473)

* Add tests to ensure flutter run does not quit at startup

These tests require https://github.com/dart-lang/vm_service_client/pull/38 to be merged and currently the third test (pause-on-exceptions) fails because of a bug.

* Remove TODO

Probably makes most sense this way for now.

* Address PR comments
parent 2a679bd8
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:file/file.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:test/test.dart';
import 'test_data/basic_project.dart';
import 'test_driver.dart';
BasicProject _project = new BasicProject();
FlutterTestDriver _flutter;
/// This duration is arbitrary but is ideally:
/// a) long enough to ensure that if the app is crashing at startup, we notice
/// b) as short as possible, to avoid inflating build times
const Duration requiredLifespan = Duration(seconds: 5);
void main() {
group('flutter run', () {
setUp(() async {
final Directory tempDir = await fs.systemTempDirectory.createTemp('test_app');
await _project.setUpIn(tempDir);
_flutter = new FlutterTestDriver(tempDir);
});
tearDown(() async {
await _flutter.stop();
_project.cleanup();
});
test('does not terminate when a debugger is attached', () async {
await _flutter.run(withDebugger: true);
await new Future<void>.delayed(requiredLifespan);
expect(_flutter.hasExited, equals(false));
});
test('does not terminate when a debugger is attached and pause-on-exceptions', () async {
await _flutter.run(withDebugger: true, pauseOnExceptions: true);
await new Future<void>.delayed(requiredLifespan);
expect(_flutter.hasExited, equals(false));
});
}, timeout: const Timeout.factor(3));
}
...@@ -34,12 +34,14 @@ class FlutterTestDriver { ...@@ -34,12 +34,14 @@ class FlutterTestDriver {
String _currentRunningAppId; String _currentRunningAppId;
Uri _vmServiceWsUri; Uri _vmServiceWsUri;
int _vmServicePort; int _vmServicePort;
bool _hasExited = false;
FlutterTestDriver(this._projectFolder); FlutterTestDriver(this._projectFolder);
VMServiceClient vmService; VMServiceClient vmService;
String get lastErrorInfo => _errorBuffer.toString(); String get lastErrorInfo => _errorBuffer.toString();
int get vmServicePort => _vmServicePort; int get vmServicePort => _vmServicePort;
bool get hasExited => _hasExited;
String _debugPrint(String msg) { String _debugPrint(String msg) {
const int maxLength = 500; const int maxLength = 500;
...@@ -52,19 +54,16 @@ class FlutterTestDriver { ...@@ -52,19 +54,16 @@ class FlutterTestDriver {
return msg; return msg;
} }
// TODO(dantup): Is there a better way than spawning a proc? This breaks debugging.. Future<void> run({bool withDebugger = false, bool pauseOnExceptions = false}) async {
// However, there's a lot of logic inside RunCommand that wouldn't be good
// to duplicate here.
Future<void> run({bool withDebugger = false}) async {
await _setupProcess(<String>[ await _setupProcess(<String>[
'run', 'run',
'--machine', '--machine',
'-d', '-d',
'flutter-tester', 'flutter-tester',
], withDebugger: withDebugger); ], withDebugger: withDebugger, pauseOnExceptions: pauseOnExceptions);
} }
Future<void> attach(int port, {bool withDebugger = false}) async { Future<void> attach(int port, {bool withDebugger = false, bool pauseOnExceptions = false}) async {
await _setupProcess(<String>[ await _setupProcess(<String>[
'attach', 'attach',
'--machine', '--machine',
...@@ -72,10 +71,10 @@ class FlutterTestDriver { ...@@ -72,10 +71,10 @@ class FlutterTestDriver {
'flutter-tester', 'flutter-tester',
'--debug-port', '--debug-port',
'$port', '$port',
], withDebugger: withDebugger); ], withDebugger: withDebugger, pauseOnExceptions: pauseOnExceptions);
} }
Future<void> _setupProcess(List<String> args, {bool withDebugger = false}) async { Future<void> _setupProcess(List<String> args, {bool withDebugger = false, bool pauseOnExceptions = false}) async {
final String flutterBin = fs.path.join(getFlutterRoot(), 'bin', 'flutter'); final String flutterBin = fs.path.join(getFlutterRoot(), 'bin', 'flutter');
_debugPrint('Spawning flutter $args in ${_projectFolder.path}'); _debugPrint('Spawning flutter $args in ${_projectFolder.path}');
...@@ -88,6 +87,7 @@ class FlutterTestDriver { ...@@ -88,6 +87,7 @@ class FlutterTestDriver {
workingDirectory: _projectFolder.path, workingDirectory: _projectFolder.path,
environment: <String, String>{'FLUTTER_TEST': 'true'}); environment: <String, String>{'FLUTTER_TEST': 'true'});
_proc.exitCode.then((_) => _hasExited = true);
_transformToLines(_proc.stdout).listen((String line) => _stdout.add(line)); _transformToLines(_proc.stdout).listen((String line) => _stdout.add(line));
_transformToLines(_proc.stderr).listen((String line) => _stderr.add(line)); _transformToLines(_proc.stderr).listen((String line) => _stderr.add(line));
...@@ -128,6 +128,9 @@ class FlutterTestDriver { ...@@ -128,6 +128,9 @@ class FlutterTestDriver {
// expected by tests. Tests will reload/restart as required if they need // expected by tests. Tests will reload/restart as required if they need
// to hit breakpoints, etc. // to hit breakpoints, etc.
await waitForPause(); await waitForPause();
if (pauseOnExceptions) {
await (await getFlutterIsolate()).setExceptionPauseMode(VMExceptionPauseMode.unhandled);
}
await resume(wait: false); await resume(wait: false);
} }
...@@ -188,9 +191,15 @@ class FlutterTestDriver { ...@@ -188,9 +191,15 @@ class FlutterTestDriver {
return _proc.exitCode; return _proc.exitCode;
} }
Future<void> addBreakpoint(String path, int line) async { Future<VMIsolate> getFlutterIsolate() async {
// Currently these tests only have a single isolate. If this
// ceases to be the case, this code will need changing.
final VM vm = await vmService.getVM(); final VM vm = await vmService.getVM();
final VMIsolate isolate = await vm.isolates.first.load(); return await vm.isolates.first.load();
}
Future<void> addBreakpoint(String path, int line) async {
final VMIsolate isolate = await getFlutterIsolate();
_debugPrint('Sending breakpoint for $path:$line'); _debugPrint('Sending breakpoint for $path:$line');
await isolate.addBreakpoint(path, line); await isolate.addBreakpoint(path, line);
} }
......
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