| // 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 'dart:async'; |
| import 'dart:convert'; |
| |
| import 'package:flutter_tools/src/android/android_device.dart'; |
| import 'package:flutter_tools/src/android/android_sdk.dart' show AndroidSdk; |
| import 'package:flutter_tools/src/base/file_system.dart' hide IOSink; |
| import 'package:flutter_tools/src/base/io.dart'; |
| import 'package:flutter_tools/src/build_info.dart'; |
| import 'package:flutter_tools/src/globals.dart' as globals; |
| import 'package:flutter_tools/src/ios/devices.dart'; |
| import 'package:flutter_tools/src/project.dart'; |
| import 'package:flutter_tools/src/version.dart'; |
| import 'package:mockito/mockito.dart'; |
| import 'package:process/process.dart'; |
| |
| import 'fakes.dart'; |
| |
| /// An SDK installation with several SDK levels (19, 22, 23). |
| class MockAndroidSdk extends Mock implements AndroidSdk { |
| static Directory createSdkDirectory({ |
| bool withAndroidN = false, |
| bool withSdkManager = true, |
| bool withPlatformTools = true, |
| bool withBuildTools = true, |
| }) { |
| final Directory dir = globals.fs.systemTempDirectory.createTempSync('flutter_mock_android_sdk.'); |
| final String exe = globals.platform.isWindows ? '.exe' : ''; |
| final String bat = globals.platform.isWindows ? '.bat' : ''; |
| |
| _createDir(dir, 'licenses'); |
| |
| if (withPlatformTools) { |
| _createSdkFile(dir, 'platform-tools/adb$exe'); |
| } |
| |
| if (withBuildTools) { |
| _createSdkFile(dir, 'build-tools/19.1.0/aapt$exe'); |
| _createSdkFile(dir, 'build-tools/22.0.1/aapt$exe'); |
| _createSdkFile(dir, 'build-tools/23.0.2/aapt$exe'); |
| if (withAndroidN) { |
| _createSdkFile(dir, 'build-tools/24.0.0-preview/aapt$exe'); |
| } |
| } |
| |
| _createSdkFile(dir, 'platforms/android-22/android.jar'); |
| _createSdkFile(dir, 'platforms/android-23/android.jar'); |
| if (withAndroidN) { |
| _createSdkFile(dir, 'platforms/android-N/android.jar'); |
| _createSdkFile(dir, 'platforms/android-N/build.prop', contents: _buildProp); |
| } |
| |
| if (withSdkManager) { |
| _createSdkFile(dir, 'tools/bin/sdkmanager$bat'); |
| } |
| |
| return dir; |
| } |
| |
| static void _createSdkFile(Directory dir, String filePath, { String contents }) { |
| final File file = dir.childFile(filePath); |
| file.createSync(recursive: true); |
| if (contents != null) { |
| file.writeAsStringSync(contents, flush: true); |
| } |
| } |
| |
| static void _createDir(Directory dir, String path) { |
| final Directory directory = globals.fs.directory(globals.fs.path.join(dir.path, path)); |
| directory.createSync(recursive: true); |
| } |
| |
| static const String _buildProp = r''' |
| ro.build.version.incremental=1624448 |
| ro.build.version.sdk=24 |
| ro.build.version.codename=REL |
| '''; |
| } |
| |
| /// A strategy for creating Process objects from a list of commands. |
| typedef _ProcessFactory = Process Function(List<String> command); |
| |
| /// A ProcessManager that starts Processes by delegating to a ProcessFactory. |
| class MockProcessManager extends Mock implements ProcessManager { |
| _ProcessFactory processFactory = _defaulProcessFactory; |
| bool canRunSucceeds = true; |
| bool runSucceeds = true; |
| List<String> commands; |
| |
| static Process _defaulProcessFactory(List<String> commands) => FakeProcess(); |
| |
| @override |
| bool canRun(dynamic command, { String workingDirectory }) => canRunSucceeds; |
| |
| @override |
| Future<Process> start( |
| List<dynamic> command, { |
| String workingDirectory, |
| Map<String, String> environment, |
| bool includeParentEnvironment = true, |
| bool runInShell = false, |
| ProcessStartMode mode = ProcessStartMode.normal, |
| }) { |
| final List<String> commands = command.cast<String>(); |
| if (!runSucceeds) { |
| final String executable = commands[0]; |
| final List<String> arguments = commands.length > 1 ? commands.sublist(1) : <String>[]; |
| throw ProcessException(executable, arguments); |
| } |
| |
| this.commands = commands; |
| return Future<Process>.value(processFactory(commands)); |
| } |
| } |
| |
| /// A function that generates a process factory that gives processes that fail |
| /// a given number of times before succeeding. The returned processes will |
| /// fail after a delay if one is supplied. |
| _ProcessFactory flakyProcessFactory({ |
| int flakes, |
| bool Function(List<String> command) filter, |
| Duration delay, |
| Stream<List<int>> Function() stdout, |
| Stream<List<int>> Function() stderr, |
| }) { |
| int flakesLeft = flakes; |
| stdout ??= () => const Stream<List<int>>.empty(); |
| stderr ??= () => const Stream<List<int>>.empty(); |
| return (List<String> command) { |
| if (filter != null && !filter(command)) { |
| return FakeProcess(); |
| } |
| if (flakesLeft == 0) { |
| return FakeProcess( |
| exitCode: Future<int>.value(0), |
| stdout: stdout(), |
| stderr: stderr(), |
| ); |
| } |
| flakesLeft = flakesLeft - 1; |
| Future<int> exitFuture; |
| if (delay == null) { |
| exitFuture = Future<int>.value(-9); |
| } else { |
| exitFuture = Future<int>.delayed(delay, () => Future<int>.value(-9)); |
| } |
| return FakeProcess( |
| exitCode: exitFuture, |
| stdout: stdout(), |
| stderr: stderr(), |
| ); |
| }; |
| } |
| |
| /// Creates a mock process that returns with the given [exitCode], [stdout] and [stderr]. |
| Process createMockProcess({ int exitCode = 0, String stdout = '', String stderr = '' }) { |
| final Stream<List<int>> stdoutStream = Stream<List<int>>.fromIterable(<List<int>>[ |
| utf8.encode(stdout), |
| ]); |
| final Stream<List<int>> stderrStream = Stream<List<int>>.fromIterable(<List<int>>[ |
| utf8.encode(stderr), |
| ]); |
| final Process process = _MockBasicProcess(); |
| |
| when(process.stdout).thenAnswer((_) => stdoutStream); |
| when(process.stderr).thenAnswer((_) => stderrStream); |
| when(process.exitCode).thenAnswer((_) => Future<int>.value(exitCode)); |
| return process; |
| } |
| |
| class _MockBasicProcess extends Mock implements Process {} |
| |
| class MockIosProject extends Mock implements IosProject { |
| static const String bundleId = 'com.example.test'; |
| static const String appBundleName = 'My Super Awesome App.app'; |
| |
| @override |
| Future<String> productBundleIdentifier(BuildInfo buildInfo) async => bundleId; |
| |
| @override |
| Future<String> hostAppBundleName(BuildInfo buildInfo) async => appBundleName; |
| } |
| |
| class MockAndroidDevice extends Mock implements AndroidDevice { |
| @override |
| Future<TargetPlatform> get targetPlatform async => TargetPlatform.android_arm; |
| |
| @override |
| bool isSupported() => true; |
| |
| @override |
| bool get supportsHotRestart => true; |
| |
| @override |
| bool get supportsFlutterExit => false; |
| |
| @override |
| bool isSupportedForProject(FlutterProject flutterProject) => true; |
| } |
| |
| class MockIOSDevice extends Mock implements IOSDevice { |
| @override |
| Future<TargetPlatform> get targetPlatform async => TargetPlatform.ios; |
| |
| @override |
| bool isSupported() => true; |
| |
| @override |
| bool isSupportedForProject(FlutterProject flutterProject) => true; |
| } |
| |
| class MockStdIn extends Mock implements IOSink { |
| final StringBuffer stdInWrites = StringBuffer(); |
| |
| String getAndClear() { |
| final String result = stdInWrites.toString(); |
| stdInWrites.clear(); |
| return result; |
| } |
| |
| @override |
| void write([ Object o = '' ]) { |
| stdInWrites.write(o); |
| } |
| |
| @override |
| void writeln([ Object o = '' ]) { |
| stdInWrites.writeln(o); |
| } |
| } |
| |
| class MockStream extends Mock implements Stream<List<int>> {} |
| class MockFlutterVersion extends Mock implements FlutterVersion {} |