Adam Barth | bdd2066 | 2015-10-11 19:42:50 -0700 | [diff] [blame] | 1 | // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
John McCutchan | 9cb7001 | 2016-03-04 15:01:26 -0800 | [diff] [blame] | 5 | import 'dart:async'; |
Chris Bracken | 538ba52 | 2018-01-24 17:16:30 -0800 | [diff] [blame] | 6 | import 'dart:convert'; |
Jonah Williams | 5e77d65 | 2019-04-30 15:42:16 -0700 | [diff] [blame] | 7 | import 'dart:io' as io show IOSink, ProcessSignal; |
Devon Carew | 5ad6a57 | 2016-03-11 08:49:55 -0800 | [diff] [blame] | 8 | |
Devon Carew | 67046f9 | 2016-02-20 22:00:11 -0800 | [diff] [blame] | 9 | import 'package:flutter_tools/src/android/android_device.dart'; |
Chris Bracken | 74478d1 | 2018-01-24 20:40:25 -0800 | [diff] [blame] | 10 | import 'package:flutter_tools/src/android/android_sdk.dart' show AndroidSdk; |
Adam Barth | da0a12c | 2015-11-10 13:18:34 -0800 | [diff] [blame] | 11 | import 'package:flutter_tools/src/application_package.dart'; |
Chris Bracken | 74478d1 | 2018-01-24 20:40:25 -0800 | [diff] [blame] | 12 | import 'package:flutter_tools/src/base/file_system.dart' hide IOSink; |
Chris Bracken | 538ba52 | 2018-01-24 17:16:30 -0800 | [diff] [blame] | 13 | import 'package:flutter_tools/src/base/io.dart'; |
Dan Field | 15f2119 | 2019-02-23 09:56:57 -0800 | [diff] [blame] | 14 | import 'package:flutter_tools/src/base/platform.dart'; |
Jason Simmons | a590ee2 | 2016-05-12 12:22:15 -0700 | [diff] [blame] | 15 | import 'package:flutter_tools/src/build_info.dart'; |
Chris Bracken | 251e82d | 2018-08-31 13:31:56 -0700 | [diff] [blame] | 16 | import 'package:flutter_tools/src/compile.dart'; |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame^] | 17 | import 'package:flutter_tools/src/devfs.dart'; |
Adam Barth | da0a12c | 2015-11-10 13:18:34 -0800 | [diff] [blame] | 18 | import 'package:flutter_tools/src/device.dart'; |
Devon Carew | 67046f9 | 2016-02-20 22:00:11 -0800 | [diff] [blame] | 19 | import 'package:flutter_tools/src/ios/devices.dart'; |
| 20 | import 'package:flutter_tools/src/ios/simulators.dart'; |
Mikkel Nygaard Ravn | 22832d3 | 2018-08-30 16:18:44 +0200 | [diff] [blame] | 21 | import 'package:flutter_tools/src/project.dart'; |
Adam Barth | 9662d49 | 2015-11-28 21:07:16 -0800 | [diff] [blame] | 22 | import 'package:flutter_tools/src/runner/flutter_command.dart'; |
Todd Volkert | 5d29737 | 2017-03-10 13:53:22 -0800 | [diff] [blame] | 23 | import 'package:mockito/mockito.dart'; |
Chris Bracken | 538ba52 | 2018-01-24 17:16:30 -0800 | [diff] [blame] | 24 | import 'package:process/process.dart'; |
Ian Hickson | 686d8f8 | 2018-08-14 20:33:58 -0700 | [diff] [blame] | 25 | |
| 26 | import 'common.dart'; |
Adam Barth | bdd2066 | 2015-10-11 19:42:50 -0700 | [diff] [blame] | 27 | |
| 28 | class MockApplicationPackageStore extends ApplicationPackageStore { |
| 29 | MockApplicationPackageStore() : super( |
Alexandre Ardhuin | d927c93 | 2018-09-12 08:29:29 +0200 | [diff] [blame] | 30 | android: AndroidApk( |
Adam Barth | e2744e9 | 2016-03-11 12:48:12 -0800 | [diff] [blame] | 31 | id: 'io.flutter.android.mock', |
Sigurd Meldgaard | 2d3a5c7 | 2018-07-20 08:00:30 +0200 | [diff] [blame] | 32 | file: fs.file('/mock/path/to/android/SkyShell.apk'), |
Stanislav Baranov | 55f3da7 | 2018-12-19 16:27:47 -0800 | [diff] [blame] | 33 | versionCode: 1, |
Alexandre Ardhuin | 387f885 | 2019-03-01 08:17:55 +0100 | [diff] [blame] | 34 | launchActivity: 'io.flutter.android.mock.MockActivity', |
Adam Barth | e2744e9 | 2016-03-11 12:48:12 -0800 | [diff] [blame] | 35 | ), |
Alexandre Ardhuin | d927c93 | 2018-09-12 08:29:29 +0200 | [diff] [blame] | 36 | iOS: BuildableIOSApp(MockIosProject()) |
Devon Carew | 3463d89 | 2016-02-22 12:37:08 -0800 | [diff] [blame] | 37 | ); |
Adam Barth | bdd2066 | 2015-10-11 19:42:50 -0700 | [diff] [blame] | 38 | } |
| 39 | |
Chris Bracken | 74478d1 | 2018-01-24 20:40:25 -0800 | [diff] [blame] | 40 | /// An SDK installation with several SDK levels (19, 22, 23). |
| 41 | class MockAndroidSdk extends Mock implements AndroidSdk { |
| 42 | static Directory createSdkDirectory({ |
Alexandre Ardhuin | 09276be | 2018-06-05 08:50:40 +0200 | [diff] [blame] | 43 | bool withAndroidN = false, |
Chris Bracken | 74478d1 | 2018-01-24 20:40:25 -0800 | [diff] [blame] | 44 | String withNdkDir, |
Vyacheslav Egorov | 8f65ee9 | 2019-01-16 21:30:37 +0100 | [diff] [blame] | 45 | int ndkVersion = 16, |
Alexandre Ardhuin | 09276be | 2018-06-05 08:50:40 +0200 | [diff] [blame] | 46 | bool withNdkSysroot = false, |
| 47 | bool withSdkManager = true, |
Dan Field | 15f2119 | 2019-02-23 09:56:57 -0800 | [diff] [blame] | 48 | bool withPlatformTools = true, |
| 49 | bool withBuildTools = true, |
Chris Bracken | 74478d1 | 2018-01-24 20:40:25 -0800 | [diff] [blame] | 50 | }) { |
Ian Hickson | 3dec6a6 | 2018-08-17 13:17:23 -0700 | [diff] [blame] | 51 | final Directory dir = fs.systemTempDirectory.createTempSync('flutter_mock_android_sdk.'); |
Dan Field | 15f2119 | 2019-02-23 09:56:57 -0800 | [diff] [blame] | 52 | final String exe = platform.isWindows ? '.exe' : ''; |
| 53 | final String bat = platform.isWindows ? '.bat' : ''; |
Chris Bracken | 74478d1 | 2018-01-24 20:40:25 -0800 | [diff] [blame] | 54 | |
Dan Field | 15f2119 | 2019-02-23 09:56:57 -0800 | [diff] [blame] | 55 | _createDir(dir, 'licenses'); |
Chris Bracken | 74478d1 | 2018-01-24 20:40:25 -0800 | [diff] [blame] | 56 | |
Dan Field | 15f2119 | 2019-02-23 09:56:57 -0800 | [diff] [blame] | 57 | if (withPlatformTools) { |
| 58 | _createSdkFile(dir, 'platform-tools/adb$exe'); |
| 59 | } |
| 60 | |
| 61 | if (withBuildTools) { |
| 62 | _createSdkFile(dir, 'build-tools/19.1.0/aapt$exe'); |
| 63 | _createSdkFile(dir, 'build-tools/22.0.1/aapt$exe'); |
| 64 | _createSdkFile(dir, 'build-tools/23.0.2/aapt$exe'); |
| 65 | if (withAndroidN) |
| 66 | _createSdkFile(dir, 'build-tools/24.0.0-preview/aapt$exe'); |
| 67 | } |
Chris Bracken | 74478d1 | 2018-01-24 20:40:25 -0800 | [diff] [blame] | 68 | |
| 69 | _createSdkFile(dir, 'platforms/android-22/android.jar'); |
| 70 | _createSdkFile(dir, 'platforms/android-23/android.jar'); |
| 71 | if (withAndroidN) { |
| 72 | _createSdkFile(dir, 'platforms/android-N/android.jar'); |
| 73 | _createSdkFile(dir, 'platforms/android-N/build.prop', contents: _buildProp); |
| 74 | } |
| 75 | |
| 76 | if (withSdkManager) |
Dan Field | 15f2119 | 2019-02-23 09:56:57 -0800 | [diff] [blame] | 77 | _createSdkFile(dir, 'tools/bin/sdkmanager$bat'); |
Chris Bracken | 74478d1 | 2018-01-24 20:40:25 -0800 | [diff] [blame] | 78 | |
| 79 | if (withNdkDir != null) { |
Vyacheslav Egorov | 8f65ee9 | 2019-01-16 21:30:37 +0100 | [diff] [blame] | 80 | final String ndkToolchainBin = fs.path.join( |
Ian Hickson | 3dec6a6 | 2018-08-17 13:17:23 -0700 | [diff] [blame] | 81 | 'ndk-bundle', |
| 82 | 'toolchains', |
| 83 | 'arm-linux-androideabi-4.9', |
| 84 | 'prebuilt', |
| 85 | withNdkDir, |
| 86 | 'bin', |
Vyacheslav Egorov | 8f65ee9 | 2019-01-16 21:30:37 +0100 | [diff] [blame] | 87 | ); |
| 88 | final String ndkCompiler = fs.path.join( |
| 89 | ndkToolchainBin, |
Ian Hickson | 3dec6a6 | 2018-08-17 13:17:23 -0700 | [diff] [blame] | 90 | 'arm-linux-androideabi-gcc', |
| 91 | ); |
Vyacheslav Egorov | 8f65ee9 | 2019-01-16 21:30:37 +0100 | [diff] [blame] | 92 | final String ndkLinker = fs.path.join( |
| 93 | ndkToolchainBin, |
| 94 | 'arm-linux-androideabi-ld', |
| 95 | ); |
Chris Bracken | 74478d1 | 2018-01-24 20:40:25 -0800 | [diff] [blame] | 96 | _createSdkFile(dir, ndkCompiler); |
Vyacheslav Egorov | 8f65ee9 | 2019-01-16 21:30:37 +0100 | [diff] [blame] | 97 | _createSdkFile(dir, ndkLinker); |
| 98 | _createSdkFile(dir, fs.path.join('ndk-bundle', 'source.properties'), contents: ''' |
| 99 | Pkg.Desc = Android NDK[] |
| 100 | Pkg.Revision = $ndkVersion.1.5063045 |
| 101 | |
| 102 | '''); |
Chris Bracken | 74478d1 | 2018-01-24 20:40:25 -0800 | [diff] [blame] | 103 | } |
| 104 | if (withNdkSysroot) { |
Ian Hickson | 3dec6a6 | 2018-08-17 13:17:23 -0700 | [diff] [blame] | 105 | final String armPlatform = fs.path.join( |
| 106 | 'ndk-bundle', |
| 107 | 'platforms', |
| 108 | 'android-9', |
| 109 | 'arch-arm', |
| 110 | ); |
Chris Bracken | 74478d1 | 2018-01-24 20:40:25 -0800 | [diff] [blame] | 111 | _createDir(dir, armPlatform); |
| 112 | } |
| 113 | |
| 114 | return dir; |
| 115 | } |
| 116 | |
| 117 | static void _createSdkFile(Directory dir, String filePath, { String contents }) { |
| 118 | final File file = dir.childFile(filePath); |
| 119 | file.createSync(recursive: true); |
| 120 | if (contents != null) { |
| 121 | file.writeAsStringSync(contents, flush: true); |
| 122 | } |
| 123 | } |
| 124 | |
| 125 | static void _createDir(Directory dir, String path) { |
| 126 | final Directory directory = fs.directory(fs.path.join(dir.path, path)); |
| 127 | directory.createSync(recursive: true); |
| 128 | } |
| 129 | |
| 130 | static const String _buildProp = r''' |
| 131 | ro.build.version.incremental=1624448 |
| 132 | ro.build.version.sdk=24 |
| 133 | ro.build.version.codename=REL |
| 134 | '''; |
| 135 | } |
| 136 | |
Chris Bracken | 538ba52 | 2018-01-24 17:16:30 -0800 | [diff] [blame] | 137 | /// A strategy for creating Process objects from a list of commands. |
Alexandre Ardhuin | a07d371 | 2018-09-14 21:06:19 +0200 | [diff] [blame] | 138 | typedef ProcessFactory = Process Function(List<String> command); |
Chris Bracken | 538ba52 | 2018-01-24 17:16:30 -0800 | [diff] [blame] | 139 | |
| 140 | /// A ProcessManager that starts Processes by delegating to a ProcessFactory. |
| 141 | class MockProcessManager implements ProcessManager { |
Alexandre Ardhuin | d927c93 | 2018-09-12 08:29:29 +0200 | [diff] [blame] | 142 | ProcessFactory processFactory = (List<String> commands) => MockProcess(); |
Chris Bracken | d3705f3 | 2018-01-24 22:27:24 -0800 | [diff] [blame] | 143 | bool succeed = true; |
Chris Bracken | 538ba52 | 2018-01-24 17:16:30 -0800 | [diff] [blame] | 144 | List<String> commands; |
| 145 | |
| 146 | @override |
Chris Bracken | d3705f3 | 2018-01-24 22:27:24 -0800 | [diff] [blame] | 147 | bool canRun(dynamic command, { String workingDirectory }) => succeed; |
| 148 | |
| 149 | @override |
Chris Bracken | 538ba52 | 2018-01-24 17:16:30 -0800 | [diff] [blame] | 150 | Future<Process> start( |
| 151 | List<dynamic> command, { |
| 152 | String workingDirectory, |
| 153 | Map<String, String> environment, |
Alexandre Ardhuin | 09276be | 2018-06-05 08:50:40 +0200 | [diff] [blame] | 154 | bool includeParentEnvironment = true, |
| 155 | bool runInShell = false, |
Leaf Petersen | 32f9444 | 2018-07-20 15:07:24 -0700 | [diff] [blame] | 156 | ProcessStartMode mode = ProcessStartMode.normal, |
Chris Bracken | 538ba52 | 2018-01-24 17:16:30 -0800 | [diff] [blame] | 157 | }) { |
Chris Bracken | d3705f3 | 2018-01-24 22:27:24 -0800 | [diff] [blame] | 158 | if (!succeed) { |
| 159 | final String executable = command[0]; |
| 160 | final List<String> arguments = command.length > 1 ? command.sublist(1) : <String>[]; |
Alexandre Ardhuin | d927c93 | 2018-09-12 08:29:29 +0200 | [diff] [blame] | 161 | throw ProcessException(executable, arguments); |
Chris Bracken | d3705f3 | 2018-01-24 22:27:24 -0800 | [diff] [blame] | 162 | } |
| 163 | |
Chris Bracken | 538ba52 | 2018-01-24 17:16:30 -0800 | [diff] [blame] | 164 | commands = command; |
Alexandre Ardhuin | d927c93 | 2018-09-12 08:29:29 +0200 | [diff] [blame] | 165 | return Future<Process>.value(processFactory(command)); |
Chris Bracken | 538ba52 | 2018-01-24 17:16:30 -0800 | [diff] [blame] | 166 | } |
| 167 | |
| 168 | @override |
| 169 | dynamic noSuchMethod(Invocation invocation) => null; |
| 170 | } |
| 171 | |
| 172 | /// A process that exits successfully with no output and ignores all input. |
| 173 | class MockProcess extends Mock implements Process { |
| 174 | MockProcess({ |
Alexandre Ardhuin | 09276be | 2018-06-05 08:50:40 +0200 | [diff] [blame] | 175 | this.pid = 1, |
Chris Bracken | 538ba52 | 2018-01-24 17:16:30 -0800 | [diff] [blame] | 176 | Future<int> exitCode, |
| 177 | Stream<List<int>> stdin, |
Alexandre Ardhuin | 09276be | 2018-06-05 08:50:40 +0200 | [diff] [blame] | 178 | this.stdout = const Stream<List<int>>.empty(), |
| 179 | this.stderr = const Stream<List<int>>.empty(), |
Alexandre Ardhuin | d927c93 | 2018-09-12 08:29:29 +0200 | [diff] [blame] | 180 | }) : exitCode = exitCode ?? Future<int>.value(0), |
| 181 | stdin = stdin ?? MemoryIOSink(); |
Chris Bracken | 538ba52 | 2018-01-24 17:16:30 -0800 | [diff] [blame] | 182 | |
| 183 | @override |
| 184 | final int pid; |
| 185 | |
| 186 | @override |
| 187 | final Future<int> exitCode; |
| 188 | |
| 189 | @override |
| 190 | final io.IOSink stdin; |
| 191 | |
| 192 | @override |
| 193 | final Stream<List<int>> stdout; |
| 194 | |
| 195 | @override |
| 196 | final Stream<List<int>> stderr; |
| 197 | } |
| 198 | |
Jonah Williams | 5e77d65 | 2019-04-30 15:42:16 -0700 | [diff] [blame] | 199 | /// A fake process implemenation which can be provided all necessary values. |
| 200 | class FakeProcess implements Process { |
| 201 | FakeProcess({ |
| 202 | this.pid = 1, |
| 203 | Future<int> exitCode, |
| 204 | Stream<List<int>> stdin, |
| 205 | this.stdout = const Stream<List<int>>.empty(), |
| 206 | this.stderr = const Stream<List<int>>.empty(), |
| 207 | }) : exitCode = exitCode ?? Future<int>.value(0), |
| 208 | stdin = stdin ?? MemoryIOSink(); |
| 209 | |
| 210 | @override |
| 211 | final int pid; |
| 212 | |
| 213 | @override |
| 214 | final Future<int> exitCode; |
| 215 | |
| 216 | @override |
| 217 | final io.IOSink stdin; |
| 218 | |
| 219 | @override |
| 220 | final Stream<List<int>> stdout; |
| 221 | |
| 222 | @override |
| 223 | final Stream<List<int>> stderr; |
| 224 | |
| 225 | @override |
| 226 | bool kill([io.ProcessSignal signal = io.ProcessSignal.sigterm]) { |
| 227 | return true; |
| 228 | } |
| 229 | } |
| 230 | |
Chris Bracken | 538ba52 | 2018-01-24 17:16:30 -0800 | [diff] [blame] | 231 | /// A process that prompts the user to proceed, then asynchronously writes |
| 232 | /// some lines to stdout before it exits. |
| 233 | class PromptingProcess implements Process { |
Alexandre Ardhuin | 2d3ff10 | 2018-10-05 07:54:56 +0200 | [diff] [blame] | 234 | Future<void> showPrompt(String prompt, List<String> outputLines) async { |
Jason Simmons | 466d154 | 2018-03-12 11:06:32 -0700 | [diff] [blame] | 235 | _stdoutController.add(utf8.encode(prompt)); |
Chris Bracken | 538ba52 | 2018-01-24 17:16:30 -0800 | [diff] [blame] | 236 | final List<int> bytesOnStdin = await _stdin.future; |
| 237 | // Echo stdin to stdout. |
| 238 | _stdoutController.add(bytesOnStdin); |
Jason Simmons | 466d154 | 2018-03-12 11:06:32 -0700 | [diff] [blame] | 239 | if (bytesOnStdin[0] == utf8.encode('y')[0]) { |
Chris Bracken | 538ba52 | 2018-01-24 17:16:30 -0800 | [diff] [blame] | 240 | for (final String line in outputLines) |
Jason Simmons | 466d154 | 2018-03-12 11:06:32 -0700 | [diff] [blame] | 241 | _stdoutController.add(utf8.encode('$line\n')); |
Chris Bracken | 538ba52 | 2018-01-24 17:16:30 -0800 | [diff] [blame] | 242 | } |
| 243 | await _stdoutController.close(); |
| 244 | } |
| 245 | |
Alexandre Ardhuin | d927c93 | 2018-09-12 08:29:29 +0200 | [diff] [blame] | 246 | final StreamController<List<int>> _stdoutController = StreamController<List<int>>(); |
| 247 | final CompleterIOSink _stdin = CompleterIOSink(); |
Chris Bracken | 538ba52 | 2018-01-24 17:16:30 -0800 | [diff] [blame] | 248 | |
| 249 | @override |
| 250 | Stream<List<int>> get stdout => _stdoutController.stream; |
| 251 | |
| 252 | @override |
| 253 | Stream<List<int>> get stderr => const Stream<List<int>>.empty(); |
| 254 | |
| 255 | @override |
| 256 | IOSink get stdin => _stdin; |
| 257 | |
| 258 | @override |
| 259 | Future<int> get exitCode async { |
| 260 | await _stdoutController.done; |
| 261 | return 0; |
| 262 | } |
| 263 | |
| 264 | @override |
| 265 | dynamic noSuchMethod(Invocation invocation) => null; |
| 266 | } |
| 267 | |
| 268 | /// An IOSink that completes a future with the first line written to it. |
| 269 | class CompleterIOSink extends MemoryIOSink { |
Alexandre Ardhuin | d927c93 | 2018-09-12 08:29:29 +0200 | [diff] [blame] | 270 | final Completer<List<int>> _completer = Completer<List<int>>(); |
Chris Bracken | 538ba52 | 2018-01-24 17:16:30 -0800 | [diff] [blame] | 271 | |
| 272 | Future<List<int>> get future => _completer.future; |
| 273 | |
| 274 | @override |
| 275 | void add(List<int> data) { |
| 276 | if (!_completer.isCompleted) |
| 277 | _completer.complete(data); |
| 278 | super.add(data); |
| 279 | } |
| 280 | } |
| 281 | |
| 282 | /// An IOSink that collects whatever is written to it. |
| 283 | class MemoryIOSink implements IOSink { |
| 284 | @override |
Jason Simmons | 466d154 | 2018-03-12 11:06:32 -0700 | [diff] [blame] | 285 | Encoding encoding = utf8; |
Chris Bracken | 538ba52 | 2018-01-24 17:16:30 -0800 | [diff] [blame] | 286 | |
| 287 | final List<List<int>> writes = <List<int>>[]; |
| 288 | |
| 289 | @override |
| 290 | void add(List<int> data) { |
| 291 | writes.add(data); |
| 292 | } |
| 293 | |
| 294 | @override |
Alexandre Ardhuin | 2d3ff10 | 2018-10-05 07:54:56 +0200 | [diff] [blame] | 295 | Future<void> addStream(Stream<List<int>> stream) { |
| 296 | final Completer<void> completer = Completer<void>(); |
Chris Bracken | 538ba52 | 2018-01-24 17:16:30 -0800 | [diff] [blame] | 297 | stream.listen((List<int> data) { |
| 298 | add(data); |
Alexandre Ardhuin | 2d3ff10 | 2018-10-05 07:54:56 +0200 | [diff] [blame] | 299 | }).onDone(() => completer.complete()); |
Chris Bracken | 538ba52 | 2018-01-24 17:16:30 -0800 | [diff] [blame] | 300 | return completer.future; |
| 301 | } |
| 302 | |
| 303 | @override |
| 304 | void writeCharCode(int charCode) { |
| 305 | add(<int>[charCode]); |
| 306 | } |
| 307 | |
| 308 | @override |
| 309 | void write(Object obj) { |
| 310 | add(encoding.encode('$obj')); |
| 311 | } |
| 312 | |
| 313 | @override |
Alexandre Ardhuin | 5169ab5 | 2019-02-21 09:27:07 +0100 | [diff] [blame] | 314 | void writeln([ Object obj = '' ]) { |
Chris Bracken | 538ba52 | 2018-01-24 17:16:30 -0800 | [diff] [blame] | 315 | add(encoding.encode('$obj\n')); |
| 316 | } |
| 317 | |
| 318 | @override |
Alexandre Ardhuin | 5169ab5 | 2019-02-21 09:27:07 +0100 | [diff] [blame] | 319 | void writeAll(Iterable<dynamic> objects, [ String separator = '' ]) { |
Chris Bracken | 538ba52 | 2018-01-24 17:16:30 -0800 | [diff] [blame] | 320 | bool addSeparator = false; |
| 321 | for (dynamic object in objects) { |
| 322 | if (addSeparator) { |
| 323 | write(separator); |
| 324 | } |
| 325 | write(object); |
| 326 | addSeparator = true; |
| 327 | } |
| 328 | } |
| 329 | |
| 330 | @override |
Alexandre Ardhuin | 5169ab5 | 2019-02-21 09:27:07 +0100 | [diff] [blame] | 331 | void addError(dynamic error, [ StackTrace stackTrace ]) { |
Alexandre Ardhuin | d927c93 | 2018-09-12 08:29:29 +0200 | [diff] [blame] | 332 | throw UnimplementedError(); |
Chris Bracken | 538ba52 | 2018-01-24 17:16:30 -0800 | [diff] [blame] | 333 | } |
| 334 | |
| 335 | @override |
Alexandre Ardhuin | 2d3ff10 | 2018-10-05 07:54:56 +0200 | [diff] [blame] | 336 | Future<void> get done => close(); |
Chris Bracken | 538ba52 | 2018-01-24 17:16:30 -0800 | [diff] [blame] | 337 | |
| 338 | @override |
Alexandre Ardhuin | 8b0de38 | 2018-10-17 11:01:37 +0200 | [diff] [blame] | 339 | Future<void> close() async { } |
Chris Bracken | 538ba52 | 2018-01-24 17:16:30 -0800 | [diff] [blame] | 340 | |
| 341 | @override |
Alexandre Ardhuin | 8b0de38 | 2018-10-17 11:01:37 +0200 | [diff] [blame] | 342 | Future<void> flush() async { } |
Chris Bracken | 538ba52 | 2018-01-24 17:16:30 -0800 | [diff] [blame] | 343 | } |
| 344 | |
| 345 | /// A Stdio that collects stdout and supports simulated stdin. |
| 346 | class MockStdio extends Stdio { |
Alexandre Ardhuin | d927c93 | 2018-09-12 08:29:29 +0200 | [diff] [blame] | 347 | final MemoryIOSink _stdout = MemoryIOSink(); |
Greg Spencer | 7caa659 | 2018-09-19 15:22:43 -0700 | [diff] [blame] | 348 | final MemoryIOSink _stderr = MemoryIOSink(); |
Alexandre Ardhuin | d927c93 | 2018-09-12 08:29:29 +0200 | [diff] [blame] | 349 | final StreamController<List<int>> _stdin = StreamController<List<int>>(); |
Chris Bracken | 538ba52 | 2018-01-24 17:16:30 -0800 | [diff] [blame] | 350 | |
| 351 | @override |
| 352 | IOSink get stdout => _stdout; |
| 353 | |
| 354 | @override |
Greg Spencer | 7caa659 | 2018-09-19 15:22:43 -0700 | [diff] [blame] | 355 | IOSink get stderr => _stderr; |
| 356 | |
| 357 | @override |
Chris Bracken | 538ba52 | 2018-01-24 17:16:30 -0800 | [diff] [blame] | 358 | Stream<List<int>> get stdin => _stdin.stream; |
| 359 | |
| 360 | void simulateStdin(String line) { |
Jason Simmons | 466d154 | 2018-03-12 11:06:32 -0700 | [diff] [blame] | 361 | _stdin.add(utf8.encode('$line\n')); |
Chris Bracken | 538ba52 | 2018-01-24 17:16:30 -0800 | [diff] [blame] | 362 | } |
| 363 | |
Alexandre Ardhuin | f62afdc | 2018-10-01 21:29:08 +0200 | [diff] [blame] | 364 | List<String> get writtenToStdout => _stdout.writes.map<String>(_stdout.encoding.decode).toList(); |
| 365 | List<String> get writtenToStderr => _stderr.writes.map<String>(_stderr.encoding.decode).toList(); |
Chris Bracken | 538ba52 | 2018-01-24 17:16:30 -0800 | [diff] [blame] | 366 | } |
| 367 | |
Devon Carew | 9ac2e44 | 2017-04-27 09:30:47 -0700 | [diff] [blame] | 368 | class MockPollingDeviceDiscovery extends PollingDeviceDiscovery { |
Alexandre Ardhuin | 2ea1d81 | 2018-10-04 07:28:07 +0200 | [diff] [blame] | 369 | MockPollingDeviceDiscovery() : super('mock'); |
| 370 | |
Alexandre Ardhuin | 0783ec9 | 2017-09-01 22:18:51 +0200 | [diff] [blame] | 371 | final List<Device> _devices = <Device>[]; |
Alexandre Ardhuin | d927c93 | 2018-09-12 08:29:29 +0200 | [diff] [blame] | 372 | final StreamController<Device> _onAddedController = StreamController<Device>.broadcast(); |
| 373 | final StreamController<Device> _onRemovedController = StreamController<Device>.broadcast(); |
Devon Carew | 9ac2e44 | 2017-04-27 09:30:47 -0700 | [diff] [blame] | 374 | |
Devon Carew | 9ac2e44 | 2017-04-27 09:30:47 -0700 | [diff] [blame] | 375 | @override |
Chris Bracken | 1d9f009 | 2017-06-19 13:14:57 -0700 | [diff] [blame] | 376 | Future<List<Device>> pollingGetDevices() async => _devices; |
Devon Carew | 9ac2e44 | 2017-04-27 09:30:47 -0700 | [diff] [blame] | 377 | |
| 378 | @override |
| 379 | bool get supportsPlatform => true; |
| 380 | |
Todd Volkert | 6a4b08b | 2017-05-03 16:12:08 -0700 | [diff] [blame] | 381 | @override |
| 382 | bool get canListAnything => true; |
| 383 | |
Devon Carew | 9ac2e44 | 2017-04-27 09:30:47 -0700 | [diff] [blame] | 384 | void addDevice(MockAndroidDevice device) { |
| 385 | _devices.add(device); |
| 386 | |
| 387 | _onAddedController.add(device); |
| 388 | } |
| 389 | |
| 390 | @override |
Chris Bracken | 1d9f009 | 2017-06-19 13:14:57 -0700 | [diff] [blame] | 391 | Future<List<Device>> get devices async => _devices; |
Devon Carew | 9ac2e44 | 2017-04-27 09:30:47 -0700 | [diff] [blame] | 392 | |
| 393 | @override |
| 394 | Stream<Device> get onAdded => _onAddedController.stream; |
| 395 | |
| 396 | @override |
| 397 | Stream<Device> get onRemoved => _onRemovedController.stream; |
| 398 | } |
| 399 | |
Mikkel Nygaard Ravn | 22832d3 | 2018-08-30 16:18:44 +0200 | [diff] [blame] | 400 | class MockIosProject extends Mock implements IosProject { |
| 401 | @override |
| 402 | String get productBundleIdentifier => 'com.example.test'; |
| 403 | |
| 404 | @override |
| 405 | String get hostAppBundleName => 'Runner.app'; |
| 406 | } |
| 407 | |
Adam Barth | bdd2066 | 2015-10-11 19:42:50 -0700 | [diff] [blame] | 408 | class MockAndroidDevice extends Mock implements AndroidDevice { |
Hixie | 797e27e | 2016-03-14 13:31:43 -0700 | [diff] [blame] | 409 | @override |
Todd Volkert | 60c5ffc | 2017-04-25 17:23:00 -0700 | [diff] [blame] | 410 | Future<TargetPlatform> get targetPlatform async => TargetPlatform.android_arm; |
Hixie | 797e27e | 2016-03-14 13:31:43 -0700 | [diff] [blame] | 411 | |
| 412 | @override |
Devon Carew | 37290d8 | 2016-02-24 10:06:59 -0800 | [diff] [blame] | 413 | bool isSupported() => true; |
Jonah Williams | 6b19184 | 2019-04-25 12:25:12 -0700 | [diff] [blame] | 414 | |
| 415 | @override |
| 416 | bool isSupportedForProject(FlutterProject flutterProject) => true; |
Adam Barth | bdd2066 | 2015-10-11 19:42:50 -0700 | [diff] [blame] | 417 | } |
| 418 | |
| 419 | class MockIOSDevice extends Mock implements IOSDevice { |
Hixie | 797e27e | 2016-03-14 13:31:43 -0700 | [diff] [blame] | 420 | @override |
Todd Volkert | 60c5ffc | 2017-04-25 17:23:00 -0700 | [diff] [blame] | 421 | Future<TargetPlatform> get targetPlatform async => TargetPlatform.ios; |
Hixie | 797e27e | 2016-03-14 13:31:43 -0700 | [diff] [blame] | 422 | |
| 423 | @override |
Devon Carew | 37290d8 | 2016-02-24 10:06:59 -0800 | [diff] [blame] | 424 | bool isSupported() => true; |
Jonah Williams | 6b19184 | 2019-04-25 12:25:12 -0700 | [diff] [blame] | 425 | |
| 426 | @override |
| 427 | bool isSupportedForProject(FlutterProject flutterProject) => true; |
Adam Barth | bdd2066 | 2015-10-11 19:42:50 -0700 | [diff] [blame] | 428 | } |
| 429 | |
| 430 | class MockIOSSimulator extends Mock implements IOSSimulator { |
Hixie | 797e27e | 2016-03-14 13:31:43 -0700 | [diff] [blame] | 431 | @override |
Todd Volkert | 60c5ffc | 2017-04-25 17:23:00 -0700 | [diff] [blame] | 432 | Future<TargetPlatform> get targetPlatform async => TargetPlatform.ios; |
Hixie | 797e27e | 2016-03-14 13:31:43 -0700 | [diff] [blame] | 433 | |
| 434 | @override |
Devon Carew | 37290d8 | 2016-02-24 10:06:59 -0800 | [diff] [blame] | 435 | bool isSupported() => true; |
Jonah Williams | 6b19184 | 2019-04-25 12:25:12 -0700 | [diff] [blame] | 436 | |
| 437 | @override |
| 438 | bool isSupportedForProject(FlutterProject flutterProject) => true; |
Adam Barth | bdd2066 | 2015-10-11 19:42:50 -0700 | [diff] [blame] | 439 | } |
| 440 | |
John McCutchan | 9cb7001 | 2016-03-04 15:01:26 -0800 | [diff] [blame] | 441 | class MockDeviceLogReader extends DeviceLogReader { |
Hixie | 797e27e | 2016-03-14 13:31:43 -0700 | [diff] [blame] | 442 | @override |
John McCutchan | 9cb7001 | 2016-03-04 15:01:26 -0800 | [diff] [blame] | 443 | String get name => 'MockLogReader'; |
| 444 | |
Alexandre Ardhuin | d927c93 | 2018-09-12 08:29:29 +0200 | [diff] [blame] | 445 | final StreamController<String> _linesController = StreamController<String>.broadcast(); |
John McCutchan | 9cb7001 | 2016-03-04 15:01:26 -0800 | [diff] [blame] | 446 | |
Hixie | 797e27e | 2016-03-14 13:31:43 -0700 | [diff] [blame] | 447 | @override |
Devon Carew | b0dca79 | 2016-04-27 14:43:42 -0700 | [diff] [blame] | 448 | Stream<String> get logLines => _linesController.stream; |
John McCutchan | 9cb7001 | 2016-03-04 15:01:26 -0800 | [diff] [blame] | 449 | |
Devon Carew | b0dca79 | 2016-04-27 14:43:42 -0700 | [diff] [blame] | 450 | void addLine(String line) => _linesController.add(line); |
Dan Rubel | 672d04e | 2016-09-13 14:15:36 -0400 | [diff] [blame] | 451 | |
| 452 | void dispose() { |
| 453 | _linesController.close(); |
| 454 | } |
John McCutchan | 9cb7001 | 2016-03-04 15:01:26 -0800 | [diff] [blame] | 455 | } |
| 456 | |
Yegor Jbanov | 677e63b | 2016-02-25 15:58:09 -0800 | [diff] [blame] | 457 | void applyMocksToCommand(FlutterCommand command) { |
Adam Barth | bdd2066 | 2015-10-11 19:42:50 -0700 | [diff] [blame] | 458 | command |
Alexandre Ardhuin | d927c93 | 2018-09-12 08:29:29 +0200 | [diff] [blame] | 459 | ..applicationPackages = MockApplicationPackageStore(); |
Adam Barth | bdd2066 | 2015-10-11 19:42:50 -0700 | [diff] [blame] | 460 | } |
John McCutchan | 0de6916 | 2016-07-20 12:59:30 -0700 | [diff] [blame] | 461 | |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 462 | /// Common functionality for tracking mock interaction |
| 463 | class BasicMock { |
Alexandre Ardhuin | 329e52c | 2017-03-08 23:57:31 +0100 | [diff] [blame] | 464 | final List<String> messages = <String>[]; |
John McCutchan | 0de6916 | 2016-07-20 12:59:30 -0700 | [diff] [blame] | 465 | |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 466 | void expectMessages(List<String> expectedMessages) { |
Alexandre Ardhuin | d927c93 | 2018-09-12 08:29:29 +0200 | [diff] [blame] | 467 | final List<String> actualMessages = List<String>.from(messages); |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 468 | messages.clear(); |
| 469 | expect(actualMessages, unorderedEquals(expectedMessages)); |
| 470 | } |
| 471 | |
John McCutchan | 0de6916 | 2016-07-20 12:59:30 -0700 | [diff] [blame] | 472 | bool contains(String match) { |
John McCutchan | b664473 | 2016-07-22 06:59:24 -0700 | [diff] [blame] | 473 | print('Checking for `$match` in:'); |
| 474 | print(messages); |
Chris Bracken | 7a09316 | 2017-03-03 17:50:46 -0800 | [diff] [blame] | 475 | final bool result = messages.contains(match); |
John McCutchan | 0de6916 | 2016-07-20 12:59:30 -0700 | [diff] [blame] | 476 | messages.clear(); |
| 477 | return result; |
| 478 | } |
Dan Rubel | 0295def | 2017-01-22 10:37:10 -0500 | [diff] [blame] | 479 | } |
John McCutchan | 0de6916 | 2016-07-20 12:59:30 -0700 | [diff] [blame] | 480 | |
Jonah Williams | 27876e0 | 2019-05-31 13:17:12 -0700 | [diff] [blame^] | 481 | class MockDevFSOperations extends BasicMock implements DevFSOperations { |
| 482 | Map<Uri, DevFSContent> devicePathToContent = <Uri, DevFSContent>{}; |
| 483 | |
| 484 | @override |
| 485 | Future<Uri> create(String fsName) async { |
| 486 | messages.add('create $fsName'); |
| 487 | return Uri.parse('file:///$fsName'); |
| 488 | } |
| 489 | |
| 490 | @override |
| 491 | Future<dynamic> destroy(String fsName) async { |
| 492 | messages.add('destroy $fsName'); |
| 493 | } |
| 494 | |
| 495 | @override |
| 496 | Future<dynamic> writeFile(String fsName, Uri deviceUri, DevFSContent content) async { |
| 497 | String message = 'writeFile $fsName $deviceUri'; |
| 498 | if (content is DevFSFileContent) { |
| 499 | message += ' ${content.file.path}'; |
| 500 | } |
| 501 | messages.add(message); |
| 502 | devicePathToContent[deviceUri] = content; |
| 503 | } |
| 504 | } |
Chris Bracken | 251e82d | 2018-08-31 13:31:56 -0700 | [diff] [blame] | 505 | |
| 506 | class MockResidentCompiler extends BasicMock implements ResidentCompiler { |
| 507 | @override |
Alexandre Ardhuin | a0d1f93 | 2019-03-09 09:03:11 +0100 | [diff] [blame] | 508 | void accept() { } |
Chris Bracken | 251e82d | 2018-08-31 13:31:56 -0700 | [diff] [blame] | 509 | |
| 510 | @override |
Alexander Aprelev | 030dc3f | 2019-01-31 17:43:32 -0800 | [diff] [blame] | 511 | Future<CompilerOutput> reject() async { return null; } |
Chris Bracken | 251e82d | 2018-08-31 13:31:56 -0700 | [diff] [blame] | 512 | |
| 513 | @override |
Alexandre Ardhuin | a0d1f93 | 2019-03-09 09:03:11 +0100 | [diff] [blame] | 514 | void reset() { } |
Chris Bracken | 251e82d | 2018-08-31 13:31:56 -0700 | [diff] [blame] | 515 | |
| 516 | @override |
Alexandre Ardhuin | a0d1f93 | 2019-03-09 09:03:11 +0100 | [diff] [blame] | 517 | Future<dynamic> shutdown() async { } |
Chris Bracken | 251e82d | 2018-08-31 13:31:56 -0700 | [diff] [blame] | 518 | |
| 519 | @override |
| 520 | Future<CompilerOutput> compileExpression( |
| 521 | String expression, |
| 522 | List<String> definitions, |
| 523 | List<String> typeDefinitions, |
| 524 | String libraryUri, |
| 525 | String klass, |
Alexandre Ardhuin | 387f885 | 2019-03-01 08:17:55 +0100 | [diff] [blame] | 526 | bool isStatic, |
Chris Bracken | 251e82d | 2018-08-31 13:31:56 -0700 | [diff] [blame] | 527 | ) async { |
| 528 | return null; |
| 529 | } |
| 530 | @override |
Alexander Aprelev | 12c4e05 | 2019-03-20 21:58:15 -0700 | [diff] [blame] | 531 | Future<CompilerOutput> recompile(String mainPath, List<Uri> invalidatedFiles, { String outputPath, String packagesFilePath }) async { |
Chris Bracken | 251e82d | 2018-08-31 13:31:56 -0700 | [diff] [blame] | 532 | fs.file(outputPath).createSync(recursive: true); |
| 533 | fs.file(outputPath).writeAsStringSync('compiled_kernel_output'); |
Ben Konyi | e8b98f9 | 2019-03-19 20:01:03 -0700 | [diff] [blame] | 534 | return CompilerOutput(outputPath, 0, <Uri>[]); |
Chris Bracken | 251e82d | 2018-08-31 13:31:56 -0700 | [diff] [blame] | 535 | } |
| 536 | } |