Skip to content

Commit 81c9192

Browse files
authored
[web] use resident resident runner in flutter drive (flutter#86381)
* [web] use resident resident runner in flutter drive
1 parent e990f4b commit 81c9192

File tree

5 files changed

+179
-7
lines changed

5 files changed

+179
-7
lines changed

packages/flutter_tools/lib/src/drive/drive_service.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ class FlutterDriverFactory {
4646
DriverService createDriverService(bool web) {
4747
if (web) {
4848
return WebDriverService(
49+
logger: _logger,
4950
processUtils: _processUtils,
5051
dartSdkPath: _dartSdkPath,
5152
);

packages/flutter_tools/lib/src/drive/web_driver_service.dart

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import 'package:package_config/package_config.dart';
1313
import 'package:webdriver/async_io.dart' as async_io;
1414

1515
import '../base/common.dart';
16+
import '../base/logger.dart';
1617
import '../base/process.dart';
1718
import '../build_info.dart';
1819
import '../convert.dart';
@@ -28,15 +29,25 @@ class WebDriverService extends DriverService {
2829
WebDriverService({
2930
@required ProcessUtils processUtils,
3031
@required String dartSdkPath,
32+
@required Logger logger,
3133
}) : _processUtils = processUtils,
32-
_dartSdkPath = dartSdkPath;
34+
_dartSdkPath = dartSdkPath,
35+
_logger = logger;
3336

3437
final ProcessUtils _processUtils;
3538
final String _dartSdkPath;
39+
final Logger _logger;
3640

3741
ResidentRunner _residentRunner;
3842
Uri _webUri;
3943

44+
/// The result of [ResidentRunner.run].
45+
///
46+
/// This is expected to stay `null` throughout the test, as the application
47+
/// must be running until [stop] is called. If it becomes non-null, it likely
48+
/// indicates a bug.
49+
int _runResult;
50+
4051
@override
4152
Future<void> start(
4253
BuildInfo buildInfo,
@@ -69,23 +80,48 @@ class WebDriverService extends DriverService {
6980
port: debuggingOptions.port,
7081
disablePortPublication: debuggingOptions.disablePortPublication,
7182
),
72-
stayResident: false,
83+
stayResident: true,
7384
urlTunneller: null,
7485
flutterProject: FlutterProject.current(),
7586
fileSystem: globals.fs,
7687
usage: globals.flutterUsage,
77-
logger: globals.logger,
88+
logger: _logger,
7889
systemClock: globals.systemClock,
7990
);
8091
final Completer<void> appStartedCompleter = Completer<void>.sync();
81-
final int result = await _residentRunner.run(
92+
final Future<int> runFuture = _residentRunner.run(
8293
appStartedCompleter: appStartedCompleter,
8394
enableDevTools: false,
8495
route: route,
8596
);
97+
98+
bool isAppStarted = false;
99+
await Future.any<Object>(<Future<Object>>[
100+
runFuture.then((int result) {
101+
_runResult = result;
102+
return null;
103+
}),
104+
appStartedCompleter.future.then((_) {
105+
isAppStarted = true;
106+
return null;
107+
}),
108+
]);
109+
110+
if (_runResult != null) {
111+
throw ToolExit(
112+
'Application exited before the test started. Check web driver logs '
113+
'for possible application-side errors.'
114+
);
115+
}
116+
117+
if (!isAppStarted) {
118+
throw ToolExit('Failed to start application');
119+
}
120+
86121
_webUri = _residentRunner.uri;
87-
if (result != 0) {
88-
throwToolExit(null);
122+
123+
if (_webUri == null) {
124+
throw ToolExit('Unable to connect to the app. URL not available.');
89125
}
90126
}
91127

@@ -150,7 +186,16 @@ class WebDriverService extends DriverService {
150186

151187
@override
152188
Future<void> stop({File writeSkslOnExit, String userIdentifier}) async {
189+
final bool appDidFinishPrematurely = _runResult != null;
190+
await _residentRunner.exitApp();
153191
await _residentRunner.cleanupAtFinish();
192+
193+
if (appDidFinishPrematurely) {
194+
throw ToolExit(
195+
'Application exited before the test finished. Check web driver logs '
196+
'for possible application-side errors.'
197+
);
198+
}
154199
}
155200

156201
Map<String, String> _additionalDriverEnvironment(async_io.WebDriver webDriver, String browserName, bool androidEmulator) {

packages/flutter_tools/lib/src/resident_runner.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1376,7 +1376,7 @@ abstract class ResidentRunner extends ResidentHandlers {
13761376

13771377
Future<void> exitApp() async {
13781378
final List<Future<void>> futures = <Future<void>>[
1379-
for (final FlutterDevice device in flutterDevices) device.exitApps(),
1379+
for (final FlutterDevice device in flutterDevices) device.exitApps(),
13801380
];
13811381
await Future.wait(futures);
13821382
appFinished();

packages/flutter_tools/test/general.shard/drive/drive_service_test.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,7 @@ void main() {
460460
testWithoutContext('WebDriver error message includes link to documentation', () async {
461461
const String link = 'https://flutter.dev/docs/testing/integration-tests#running-in-a-browser';
462462
final DriverService driverService = WebDriverService(
463+
logger: BufferLogger.test(),
463464
dartSdkPath: 'dart',
464465
processUtils: ProcessUtils(
465466
processManager: FakeProcessManager.empty(),

packages/flutter_tools/test/general.shard/drive/web_driver_service_test.dart

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,26 @@
44

55
// @dart = 2.8
66

7+
import 'dart:async';
8+
9+
import 'package:file/src/interface/file_system.dart';
10+
import 'package:flutter_tools/src/base/logger.dart';
11+
import 'package:flutter_tools/src/base/net.dart';
12+
import 'package:flutter_tools/src/base/process.dart';
13+
import 'package:flutter_tools/src/base/time.dart';
14+
import 'package:flutter_tools/src/build_info.dart';
15+
import 'package:flutter_tools/src/device.dart';
716
import 'package:flutter_tools/src/drive/web_driver_service.dart';
17+
import 'package:flutter_tools/src/project.dart';
18+
import 'package:flutter_tools/src/reporting/reporting.dart';
19+
import 'package:flutter_tools/src/resident_runner.dart';
20+
import 'package:flutter_tools/src/web/web_runner.dart';
21+
import 'package:test/fake.dart';
822
import 'package:webdriver/sync_io.dart' as sync_io;
923

1024
import '../../src/common.dart';
25+
import '../../src/context.dart';
26+
import '../../src/fake_vm_services.dart';
1127

1228
void main() {
1329
testWithoutContext('getDesiredCapabilities Chrome with headless on', () {
@@ -165,4 +181,113 @@ void main() {
165181

166182
expect(getDesiredCapabilities(Browser.androidChrome, false), expected);
167183
});
184+
185+
testUsingContext('WebDriverService starts and stops an app', () async {
186+
final WebDriverService service = setUpDriverService();
187+
final FakeDevice device = FakeDevice();
188+
await service.start(BuildInfo.profile, device, DebuggingOptions.enabled(BuildInfo.profile), true);
189+
await service.stop();
190+
expect(FakeResidentRunner.instance.callLog, <String>[
191+
'run',
192+
'exitApp',
193+
'cleanupAtFinish',
194+
]);
195+
}, overrides: <Type, Generator>{
196+
WebRunnerFactory: () => FakeWebRunnerFactory(),
197+
});
198+
199+
testUsingContext('WebDriverService forwards exception when run future fails before app starts', () async {
200+
final WebDriverService service = setUpDriverService();
201+
final Device device = FakeDevice();
202+
await expectLater(
203+
service.start(BuildInfo.profile, device, DebuggingOptions.enabled(BuildInfo.profile), true),
204+
throwsA('This is a test error'),
205+
);
206+
}, overrides: <Type, Generator>{
207+
WebRunnerFactory: () => FakeWebRunnerFactory(
208+
doResolveToError: true,
209+
),
210+
});
211+
}
212+
213+
class FakeWebRunnerFactory implements WebRunnerFactory {
214+
FakeWebRunnerFactory({
215+
this.doResolveToError = false,
216+
});
217+
218+
final bool doResolveToError;
219+
220+
@override
221+
ResidentRunner createWebRunner(FlutterDevice device, {String target, bool stayResident, FlutterProject flutterProject, bool ipv6, DebuggingOptions debuggingOptions, UrlTunneller urlTunneller, Logger logger, FileSystem fileSystem, SystemClock systemClock, Usage usage, bool machine = false}) {
222+
expect(stayResident, isTrue);
223+
return FakeResidentRunner(
224+
doResolveToError: doResolveToError,
225+
);
226+
}
227+
}
228+
229+
class FakeResidentRunner extends Fake implements ResidentRunner {
230+
FakeResidentRunner({
231+
this.doResolveToError,
232+
}) {
233+
instance = this;
234+
}
235+
236+
static FakeResidentRunner instance;
237+
238+
final bool doResolveToError;
239+
final Completer<int> _exitCompleter = Completer<int>();
240+
final List<String> callLog = <String>[];
241+
242+
@override
243+
Uri get uri => Uri();
244+
245+
@override
246+
Future<int> run({
247+
Completer<DebugConnectionInfo> connectionInfoCompleter,
248+
Completer<void> appStartedCompleter,
249+
bool enableDevTools = false,
250+
String route,
251+
}) async {
252+
callLog.add('run');
253+
254+
if (doResolveToError) {
255+
return Future<int>.error('This is a test error');
256+
}
257+
258+
appStartedCompleter.complete();
259+
// Emulate stayResident by completing after exitApp is called.
260+
return _exitCompleter.future;
261+
}
262+
263+
@override
264+
Future<void> exitApp() async {
265+
callLog.add('exitApp');
266+
_exitCompleter.complete();
267+
}
268+
269+
@override
270+
Future<void> cleanupAtFinish() async {
271+
callLog.add('cleanupAtFinish');
272+
}
273+
}
274+
275+
WebDriverService setUpDriverService() {
276+
final BufferLogger logger = BufferLogger.test();
277+
return WebDriverService(
278+
logger: logger,
279+
processUtils: ProcessUtils(
280+
logger: logger,
281+
processManager: FakeProcessManager.any(),
282+
),
283+
dartSdkPath: 'dart',
284+
);
285+
}
286+
287+
class FakeDevice extends Fake implements Device {
288+
@override
289+
final PlatformType platformType = PlatformType.web;
290+
291+
@override
292+
Future<TargetPlatform> get targetPlatform async => TargetPlatform.android_arm;
168293
}

0 commit comments

Comments
 (0)