Unverified Commit bfe16a86 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] remove even more mocks (#81618)

parent 67aaa0e7
...@@ -4,21 +4,15 @@ ...@@ -4,21 +4,15 @@
// @dart = 2.8 // @dart = 2.8
import 'dart:async';
import 'package:fake_async/fake_async.dart';
import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/base/process.dart'; import 'package:flutter_tools/src/base/process.dart';
import 'package:flutter_tools/src/base/terminal.dart'; import 'package:flutter_tools/src/base/terminal.dart';
import 'package:mockito/mockito.dart';
import '../../src/common.dart'; import '../../src/common.dart';
import '../../src/fake_process_manager.dart'; import '../../src/fake_process_manager.dart';
import '../../src/fakes.dart'; import '../../src/fakes.dart';
import '../../src/mocks.dart' show MockProcessManager,
flakyProcessFactory;
void main() { void main() {
group('process exceptions', () { group('process exceptions', () {
...@@ -64,66 +58,44 @@ void main() { ...@@ -64,66 +58,44 @@ void main() {
}); });
group('output formatting', () { group('output formatting', () {
MockProcessManager mockProcessManager; FakeProcessManager processManager;
ProcessUtils processUtils; ProcessUtils processUtils;
BufferLogger mockLogger; BufferLogger logger;
setUp(() { setUp(() {
mockProcessManager = MockProcessManager(); processManager = FakeProcessManager.empty();
mockLogger = BufferLogger( logger = BufferLogger.test();
terminal: AnsiTerminal(
stdio: FakeStdio(),
platform: FakePlatform(stdoutSupportsAnsi: false),
),
outputPreferences: OutputPreferences(wrapText: true, wrapColumn: 40),
);
processUtils = ProcessUtils( processUtils = ProcessUtils(
processManager: mockProcessManager, processManager: processManager,
logger: mockLogger, logger: logger,
); );
}); });
FakeProcess Function(List<String>) processMetaFactory(List<String> stdout, {
List<String> stderr = const <String>[],
}) {
final Stream<List<int>> stdoutStream = Stream<List<int>>.fromIterable(
stdout.map<List<int>>((String s) => s.codeUnits,
));
final Stream<List<int>> stderrStream = Stream<List<int>>.fromIterable(
stderr.map<List<int>>((String s) => s.codeUnits,
));
return (List<String> command) => FakeProcess(stdout: stdoutStream, stderr: stderrStream);
}
testWithoutContext('Command output is not wrapped.', () async { testWithoutContext('Command output is not wrapped.', () async {
final List<String> testString = <String>['0123456789' * 10]; final List<String> testString = <String>['0123456789' * 10];
mockProcessManager.processFactory = processMetaFactory(testString, stderr: testString); processManager.addCommand(FakeCommand(
command: const <String>['command'],
stdout: testString.join(''),
stderr: testString.join(''),
));
await processUtils.stream(<String>['command']); await processUtils.stream(<String>['command']);
expect(mockLogger.statusText, equals('${testString[0]}\n'));
expect(mockLogger.errorText, equals('${testString[0]}\n')); expect(logger.statusText, equals('${testString[0]}\n'));
expect(logger.errorText, equals('${testString[0]}\n'));
}); });
}); });
group('run', () { group('run', () {
const Duration delay = Duration(seconds: 2);
MockProcessManager flakyProcessManager;
FakeProcessManager fakeProcessManager; FakeProcessManager fakeProcessManager;
ProcessUtils processUtils; ProcessUtils processUtils;
ProcessUtils flakyProcessUtils;
setUp(() { setUp(() {
// MockProcessManager has an implementation of start() that returns the
// result of processFactory.
flakyProcessManager = MockProcessManager();
fakeProcessManager = FakeProcessManager.empty(); fakeProcessManager = FakeProcessManager.empty();
processUtils = ProcessUtils( processUtils = ProcessUtils(
processManager: fakeProcessManager, processManager: fakeProcessManager,
logger: BufferLogger.test(), logger: BufferLogger.test(),
); );
flakyProcessUtils = ProcessUtils(
processManager: flakyProcessManager,
logger: BufferLogger.test(),
);
}); });
testWithoutContext(' succeeds on success', () async { testWithoutContext(' succeeds on success', () async {
...@@ -189,68 +161,6 @@ void main() { ...@@ -189,68 +161,6 @@ void main() {
throwsA(isA<ProcessException>()), throwsA(isA<ProcessException>()),
); );
}); });
testWithoutContext(' flaky process fails without retry', () async {
flakyProcessManager.processFactory = flakyProcessFactory(
flakes: 1,
delay: delay,
);
await FakeAsync().run((FakeAsync time) async {
final Duration timeout = delay + const Duration(seconds: 1);
final RunResult result = await flakyProcessUtils.run(
<String>['dummy'],
timeout: timeout,
);
time.elapse(timeout);
expect(result.exitCode, -9);
});
}, skip: true); // TODO(jonahwilliams): clean up with https://github.com/flutter/flutter/issues/60675
testWithoutContext(' flaky process succeeds with retry', () async {
flakyProcessManager.processFactory = flakyProcessFactory(
flakes: 1,
delay: delay,
);
await FakeAsync().run((FakeAsync time) async {
final Duration timeout = delay - const Duration(milliseconds: 500);
final RunResult result = await flakyProcessUtils.run(
<String>['dummy'],
timeout: timeout,
timeoutRetries: 1,
);
time.elapse(timeout);
expect(result.exitCode, 0);
});
}, skip: true); // TODO(jonahwilliams): clean up with https://github.com/flutter/flutter/issues/60675
testWithoutContext(' flaky process generates ProcessException on timeout', () async {
final Completer<List<int>> flakyStderr = Completer<List<int>>();
final Completer<List<int>> flakyStdout = Completer<List<int>>();
flakyProcessManager.processFactory = flakyProcessFactory(
flakes: 1,
delay: delay,
stderr: () => Stream<List<int>>.fromFuture(flakyStderr.future),
stdout: () => Stream<List<int>>.fromFuture(flakyStdout.future),
);
when(flakyProcessManager.killPid(any)).thenAnswer((_) {
// Don't let the stderr stream stop until the process is killed. This
// ensures that runAsync() does not delay killing the process until
// stdout and stderr are drained (which won't happen).
flakyStderr.complete(<int>[]);
flakyStdout.complete(<int>[]);
return true;
});
await FakeAsync().run((FakeAsync time) async {
final Duration timeout = delay - const Duration(milliseconds: 500);
expect(() => flakyProcessUtils.run(
<String>['dummy'],
timeout: timeout,
timeoutRetries: 0,
), throwsA(isA<ProcessException>()));
time.elapse(timeout);
});
}, skip: true); // TODO(jonahwilliams): clean up with https://github.com/flutter/flutter/issues/60675
}); });
group('runSync', () { group('runSync', () {
...@@ -404,50 +314,55 @@ void main() { ...@@ -404,50 +314,55 @@ void main() {
}); });
group('exitsHappySync', () { group('exitsHappySync', () {
MockProcessManager mockProcessManager; FakeProcessManager processManager;
ProcessUtils processUtils; ProcessUtils processUtils;
setUp(() { setUp(() {
mockProcessManager = MockProcessManager(); processManager = FakeProcessManager.empty();
processUtils = ProcessUtils( processUtils = ProcessUtils(
processManager: mockProcessManager, processManager: processManager,
logger: BufferLogger.test(), logger: BufferLogger.test(),
); );
}); });
testWithoutContext(' succeeds on success', () async { testWithoutContext('succeeds on success', () async {
when(mockProcessManager.runSync(<String>['whoohoo'])).thenReturn( processManager.addCommand(const FakeCommand(
ProcessResult(0, 0, '', '') command: <String>['whoohoo'],
); ));
expect(processUtils.exitsHappySync(<String>['whoohoo']), isTrue); expect(processUtils.exitsHappySync(<String>['whoohoo']), isTrue);
}); });
testWithoutContext(' fails on failure', () async { testWithoutContext('fails on failure', () async {
when(mockProcessManager.runSync(<String>['boohoo'])).thenReturn( processManager.addCommand(const FakeCommand(
ProcessResult(0, 1, '', '') command: <String>['boohoo'],
); exitCode: 1,
));
expect(processUtils.exitsHappySync(<String>['boohoo']), isFalse); expect(processUtils.exitsHappySync(<String>['boohoo']), isFalse);
}); });
testWithoutContext('catches Exception and returns false', () { testWithoutContext('catches Exception and returns false', () {
when(mockProcessManager.runSync(<String>['boohoo'])).thenThrow( processManager.addCommand(const FakeCommand(
const ProcessException('Process failed', <String>[]), command: <String>['boohoo'],
); exception: ProcessException('Process failed', <String>[]),
));
expect(processUtils.exitsHappySync(<String>['boohoo']), isFalse); expect(processUtils.exitsHappySync(<String>['boohoo']), isFalse);
}); });
testWithoutContext('does not throw Exception and returns false if binary cannot run', () { testWithoutContext('does not throw Exception and returns false if binary cannot run', () {
mockProcessManager.canRunSucceeds = false; processManager.excludedExecutables.add('nonesuch');
expect(processUtils.exitsHappySync(<String>['nonesuch']), isFalse); expect(processUtils.exitsHappySync(<String>['nonesuch']), isFalse);
verifyNever(
mockProcessManager.runSync(any, environment: anyNamed('environment')),
);
}); });
testWithoutContext('does not catch ArgumentError', () async { testWithoutContext('does not catch ArgumentError', () async {
when(mockProcessManager.runSync(<String>['invalid'])).thenThrow( processManager.addCommand(FakeCommand(
ArgumentError('Bad input'), command: const <String>['invalid'],
); exception: ArgumentError('Bad input'),
));
expect( expect(
() => processUtils.exitsHappySync(<String>['invalid']), () => processUtils.exitsHappySync(<String>['invalid']),
throwsArgumentError, throwsArgumentError,
...@@ -456,50 +371,55 @@ void main() { ...@@ -456,50 +371,55 @@ void main() {
}); });
group('exitsHappy', () { group('exitsHappy', () {
MockProcessManager mockProcessManager; FakeProcessManager processManager;
ProcessUtils processUtils; ProcessUtils processUtils;
setUp(() { setUp(() {
mockProcessManager = MockProcessManager(); processManager = FakeProcessManager.empty();
processUtils = ProcessUtils( processUtils = ProcessUtils(
processManager: mockProcessManager, processManager: processManager,
logger: BufferLogger.test(), logger: BufferLogger.test(),
); );
}); });
testWithoutContext('succeeds on success', () async { testWithoutContext('succeeds on success', () async {
when(mockProcessManager.run(<String>['whoohoo'])).thenAnswer((_) { processManager.addCommand(const FakeCommand(
return Future<ProcessResult>.value(ProcessResult(0, 0, '', '')); command: <String>['whoohoo']
}); ));
expect(await processUtils.exitsHappy(<String>['whoohoo']), isTrue); expect(await processUtils.exitsHappy(<String>['whoohoo']), isTrue);
}); });
testWithoutContext('fails on failure', () async { testWithoutContext('fails on failure', () async {
when(mockProcessManager.run(<String>['boohoo'])).thenAnswer((_) { processManager.addCommand(const FakeCommand(
return Future<ProcessResult>.value(ProcessResult(0, 1, '', '')); command: <String>['boohoo'],
}); exitCode: 1,
));
expect(await processUtils.exitsHappy(<String>['boohoo']), isFalse); expect(await processUtils.exitsHappy(<String>['boohoo']), isFalse);
}); });
testWithoutContext('catches Exception and returns false', () async { testWithoutContext('catches Exception and returns false', () async {
when(mockProcessManager.run(<String>['boohoo'])).thenThrow( processManager.addCommand(const FakeCommand(
const ProcessException('Process failed', <String>[]), command: <String>['boohoo'],
); exception: ProcessException('Process failed', <String>[])
));
expect(await processUtils.exitsHappy(<String>['boohoo']), isFalse); expect(await processUtils.exitsHappy(<String>['boohoo']), isFalse);
}); });
testWithoutContext('does not throw Exception and returns false if binary cannot run', () async { testWithoutContext('does not throw Exception and returns false if binary cannot run', () async {
mockProcessManager.canRunSucceeds = false; processManager.excludedExecutables.add('nonesuch');
expect(await processUtils.exitsHappy(<String>['nonesuch']), isFalse); expect(await processUtils.exitsHappy(<String>['nonesuch']), isFalse);
verifyNever(
mockProcessManager.runSync(any, environment: anyNamed('environment')),
);
}); });
testWithoutContext('does not catch ArgumentError', () async { testWithoutContext('does not catch ArgumentError', () async {
when(mockProcessManager.run(<String>['invalid'])).thenThrow( processManager.addCommand(FakeCommand(
ArgumentError('Bad input'), command: const <String>['invalid'],
); exception: ArgumentError('Bad input')
));
expect( expect(
() async => processUtils.exitsHappy(<String>['invalid']), () async => processUtils.exitsHappy(<String>['invalid']),
throwsArgumentError, throwsArgumentError,
......
...@@ -4,21 +4,16 @@ ...@@ -4,21 +4,16 @@
// @dart = 2.8 // @dart = 2.8
import 'dart:convert';
import 'dart:io' hide Directory, File;
import 'package:file/memory.dart'; import 'package:file/memory.dart';
import 'package:flutter_tools/src/artifacts.dart'; import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/base/terminal.dart';
import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/build_system/build_system.dart'; import 'package:flutter_tools/src/build_system/build_system.dart';
import 'package:flutter_tools/src/build_system/targets/icon_tree_shaker.dart'; import 'package:flutter_tools/src/build_system/targets/icon_tree_shaker.dart';
import 'package:flutter_tools/src/devfs.dart'; import 'package:flutter_tools/src/devfs.dart';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:mockito/mockito.dart';
import '../../../src/common.dart'; import '../../../src/common.dart';
import '../../../src/fake_process_manager.dart'; import '../../../src/fake_process_manager.dart';
...@@ -34,8 +29,7 @@ const String relativePath = 'fonts/MaterialIcons-Regular.otf'; ...@@ -34,8 +29,7 @@ const String relativePath = 'fonts/MaterialIcons-Regular.otf';
void main() { void main() {
BufferLogger logger; BufferLogger logger;
MemoryFileSystem fileSystem; MemoryFileSystem fileSystem;
MockProcessManager mockProcessManager; FakeProcessManager processManager;
MockProcess fontSubsetProcess;
Artifacts artifacts; Artifacts artifacts;
DevFSStringContent fontManifestContent; DevFSStringContent fontManifestContent;
...@@ -59,9 +53,12 @@ void main() { ...@@ -59,9 +53,12 @@ void main() {
String stdout = '', String stdout = '',
String stderr = '', String stderr = '',
}) { }) {
when(mockProcessManager.run(_getConstFinderArgs(appDillPath))).thenAnswer((_) async { processManager.addCommand(FakeCommand(
return ProcessResult(0, exitCode, stdout, stderr); command: _getConstFinderArgs(appDillPath),
}); exitCode: exitCode,
stdout: stdout,
stderr: stderr,
));
} }
void _resetFontSubsetInvocation({ void _resetFontSubsetInvocation({
...@@ -72,30 +69,21 @@ void main() { ...@@ -72,30 +69,21 @@ void main() {
}) { }) {
assert(stdinSink != null); assert(stdinSink != null);
stdinSink.clear(); stdinSink.clear();
when(fontSubsetProcess.exitCode).thenAnswer((_) async => exitCode); processManager.addCommand(FakeCommand(
when(fontSubsetProcess.stdout).thenAnswer((_) => Stream<List<int>>.fromIterable(<List<int>>[utf8.encode(stdout)])); command: fontSubsetArgs,
when(fontSubsetProcess.stderr).thenAnswer((_) => Stream<List<int>>.fromIterable(<List<int>>[utf8.encode(stderr)])); exitCode: exitCode,
when(fontSubsetProcess.stdin).thenReturn(stdinSink); stdout: stdout,
when(mockProcessManager.start(fontSubsetArgs)).thenAnswer((_) async { stderr: stderr,
return fontSubsetProcess; stdin: stdinSink,
}); ));
} }
setUp(() { setUp(() {
processManager = FakeProcessManager.empty();
fontManifestContent = DevFSStringContent(validFontManifestJson); fontManifestContent = DevFSStringContent(validFontManifestJson);
mockProcessManager = MockProcessManager();
fontSubsetProcess = MockProcess();
artifacts = Artifacts.test(); artifacts = Artifacts.test();
fileSystem = MemoryFileSystem.test(); fileSystem = MemoryFileSystem.test();
logger = BufferLogger( logger = BufferLogger.test();
terminal: AnsiTerminal(
stdio: FakeStdio(),
platform: kNoAnsiPlatform,
),
outputPreferences: OutputPreferences.test(showColor: false),
);
dartPath = artifacts.getHostArtifact(HostArtifact.engineDartBinary).path; dartPath = artifacts.getHostArtifact(HostArtifact.engineDartBinary).path;
constFinderPath = artifacts.getArtifactPath(Artifact.constFinder); constFinderPath = artifacts.getArtifactPath(Artifact.constFinder);
fontSubsetPath = artifacts.getArtifactPath(Artifact.fontSubset); fontSubsetPath = artifacts.getArtifactPath(Artifact.fontSubset);
...@@ -135,7 +123,7 @@ void main() { ...@@ -135,7 +123,7 @@ void main() {
environment, environment,
fontManifestContent, fontManifestContent,
logger: logger, logger: logger,
processManager: mockProcessManager, processManager: processManager,
fileSystem: fileSystem, fileSystem: fileSystem,
artifacts: artifacts, artifacts: artifacts,
); );
...@@ -153,9 +141,7 @@ void main() { ...@@ -153,9 +141,7 @@ void main() {
relativePath: relativePath, relativePath: relativePath,
); );
expect(subsets, false); expect(subsets, false);
expect(processManager, hasNoRemainingExpectations);
verifyNever(mockProcessManager.run(any));
verifyNever(mockProcessManager.start(any));
}); });
testWithoutContext('Does not get enabled without font manifest', () { testWithoutContext('Does not get enabled without font manifest', () {
...@@ -168,7 +154,7 @@ void main() { ...@@ -168,7 +154,7 @@ void main() {
environment, environment,
null, null,
logger: logger, logger: logger,
processManager: mockProcessManager, processManager: processManager,
fileSystem: fileSystem, fileSystem: fileSystem,
artifacts: artifacts, artifacts: artifacts,
); );
...@@ -178,8 +164,7 @@ void main() { ...@@ -178,8 +164,7 @@ void main() {
isEmpty, isEmpty,
); );
expect(iconTreeShaker.enabled, false); expect(iconTreeShaker.enabled, false);
verifyNever(mockProcessManager.run(any)); expect(processManager, hasNoRemainingExpectations);
verifyNever(mockProcessManager.start(any));
}); });
testWithoutContext('Gets enabled', () { testWithoutContext('Gets enabled', () {
...@@ -192,7 +177,7 @@ void main() { ...@@ -192,7 +177,7 @@ void main() {
environment, environment,
fontManifestContent, fontManifestContent,
logger: logger, logger: logger,
processManager: mockProcessManager, processManager: processManager,
fileSystem: fileSystem, fileSystem: fileSystem,
artifacts: artifacts, artifacts: artifacts,
); );
...@@ -202,8 +187,7 @@ void main() { ...@@ -202,8 +187,7 @@ void main() {
isEmpty, isEmpty,
); );
expect(iconTreeShaker.enabled, true); expect(iconTreeShaker.enabled, true);
verifyNever(mockProcessManager.run(any)); expect(processManager, hasNoRemainingExpectations);
verifyNever(mockProcessManager.start(any));
}); });
test('No app.dill throws exception', () async { test('No app.dill throws exception', () async {
...@@ -216,7 +200,7 @@ void main() { ...@@ -216,7 +200,7 @@ void main() {
environment, environment,
fontManifestContent, fontManifestContent,
logger: logger, logger: logger,
processManager: mockProcessManager, processManager: processManager,
fileSystem: fileSystem, fileSystem: fileSystem,
artifacts: artifacts, artifacts: artifacts,
); );
...@@ -229,6 +213,7 @@ void main() { ...@@ -229,6 +213,7 @@ void main() {
), ),
throwsA(isA<IconTreeShakerException>()), throwsA(isA<IconTreeShakerException>()),
); );
expect(processManager, hasNoRemainingExpectations);
}); });
testWithoutContext('Can subset a font', () async { testWithoutContext('Can subset a font', () async {
...@@ -243,7 +228,7 @@ void main() { ...@@ -243,7 +228,7 @@ void main() {
environment, environment,
fontManifestContent, fontManifestContent,
logger: logger, logger: logger,
processManager: mockProcessManager, processManager: processManager,
fileSystem: fileSystem, fileSystem: fileSystem,
artifacts: artifacts, artifacts: artifacts,
); );
...@@ -268,9 +253,7 @@ void main() { ...@@ -268,9 +253,7 @@ void main() {
); );
expect(subsetted, true); expect(subsetted, true);
expect(stdinSink.getAndClear(), '59470\n'); expect(stdinSink.getAndClear(), '59470\n');
expect(processManager, hasNoRemainingExpectations);
verify(mockProcessManager.run(_getConstFinderArgs(appDill.path))).called(1);
verify(mockProcessManager.start(fontSubsetArgs)).called(2);
}); });
testWithoutContext('Does not subset a non-supported font', () async { testWithoutContext('Does not subset a non-supported font', () async {
...@@ -285,7 +268,7 @@ void main() { ...@@ -285,7 +268,7 @@ void main() {
environment, environment,
fontManifestContent, fontManifestContent,
logger: logger, logger: logger,
processManager: mockProcessManager, processManager: processManager,
fileSystem: fileSystem, fileSystem: fileSystem,
artifacts: artifacts, artifacts: artifacts,
); );
...@@ -303,9 +286,6 @@ void main() { ...@@ -303,9 +286,6 @@ void main() {
relativePath: relativePath, relativePath: relativePath,
); );
expect(subsetted, false); expect(subsetted, false);
verifyNever(mockProcessManager.run(_getConstFinderArgs(appDill.path)));
verifyNever(mockProcessManager.start(fontSubsetArgs));
}); });
testWithoutContext('Does not subset an invalid ttf font', () async { testWithoutContext('Does not subset an invalid ttf font', () async {
...@@ -320,7 +300,7 @@ void main() { ...@@ -320,7 +300,7 @@ void main() {
environment, environment,
fontManifestContent, fontManifestContent,
logger: logger, logger: logger,
processManager: mockProcessManager, processManager: processManager,
fileSystem: fileSystem, fileSystem: fileSystem,
artifacts: artifacts, artifacts: artifacts,
); );
...@@ -338,8 +318,6 @@ void main() { ...@@ -338,8 +318,6 @@ void main() {
); );
expect(subsetted, false); expect(subsetted, false);
verifyNever(mockProcessManager.run(_getConstFinderArgs(appDill.path)));
verifyNever(mockProcessManager.start(fontSubsetArgs));
}); });
testWithoutContext('Non-constant instances', () async { testWithoutContext('Non-constant instances', () async {
...@@ -354,7 +332,7 @@ void main() { ...@@ -354,7 +332,7 @@ void main() {
environment, environment,
fontManifestContent, fontManifestContent,
logger: logger, logger: logger,
processManager: mockProcessManager, processManager: processManager,
fileSystem: fileSystem, fileSystem: fileSystem,
artifacts: artifacts, artifacts: artifacts,
); );
...@@ -362,7 +340,7 @@ void main() { ...@@ -362,7 +340,7 @@ void main() {
_addConstFinderInvocation(appDill.path, stdout: constFinderResultWithInvalid); _addConstFinderInvocation(appDill.path, stdout: constFinderResultWithInvalid);
await expectLater( await expectLater(
() async => iconTreeShaker.subsetFont( () => iconTreeShaker.subsetFont(
input: fileSystem.file(inputPath), input: fileSystem.file(inputPath),
outputPath: outputPath, outputPath: outputPath,
relativePath: relativePath, relativePath: relativePath,
...@@ -373,9 +351,7 @@ void main() { ...@@ -373,9 +351,7 @@ void main() {
' again with --no-tree-shake-icons.', ' again with --no-tree-shake-icons.',
), ),
); );
expect(processManager, hasNoRemainingExpectations);
verify(mockProcessManager.run(_getConstFinderArgs(appDill.path))).called(1);
verifyNever(mockProcessManager.start(fontSubsetArgs));
}); });
testWithoutContext('Non-zero font-subset exit code', () async { testWithoutContext('Non-zero font-subset exit code', () async {
...@@ -391,7 +367,7 @@ void main() { ...@@ -391,7 +367,7 @@ void main() {
environment, environment,
fontManifestContent, fontManifestContent,
logger: logger, logger: logger,
processManager: mockProcessManager, processManager: processManager,
fileSystem: fileSystem, fileSystem: fileSystem,
artifacts: artifacts, artifacts: artifacts,
); );
...@@ -401,16 +377,14 @@ void main() { ...@@ -401,16 +377,14 @@ void main() {
_resetFontSubsetInvocation(exitCode: -1, stdinSink: stdinSink); _resetFontSubsetInvocation(exitCode: -1, stdinSink: stdinSink);
await expectLater( await expectLater(
() async => iconTreeShaker.subsetFont( () => iconTreeShaker.subsetFont(
input: fileSystem.file(inputPath), input: fileSystem.file(inputPath),
outputPath: outputPath, outputPath: outputPath,
relativePath: relativePath, relativePath: relativePath,
), ),
throwsA(isA<IconTreeShakerException>()), throwsA(isA<IconTreeShakerException>()),
); );
expect(processManager, hasNoRemainingExpectations);
verify(mockProcessManager.run(_getConstFinderArgs(appDill.path))).called(1);
verify(mockProcessManager.start(fontSubsetArgs)).called(1);
}); });
testWithoutContext('font-subset throws on write to sdtin', () async { testWithoutContext('font-subset throws on write to sdtin', () async {
...@@ -425,7 +399,7 @@ void main() { ...@@ -425,7 +399,7 @@ void main() {
environment, environment,
fontManifestContent, fontManifestContent,
logger: logger, logger: logger,
processManager: mockProcessManager, processManager: processManager,
fileSystem: fileSystem, fileSystem: fileSystem,
artifacts: artifacts, artifacts: artifacts,
); );
...@@ -435,16 +409,14 @@ void main() { ...@@ -435,16 +409,14 @@ void main() {
_resetFontSubsetInvocation(exitCode: -1, stdinSink: stdinSink); _resetFontSubsetInvocation(exitCode: -1, stdinSink: stdinSink);
await expectLater( await expectLater(
() async => iconTreeShaker.subsetFont( () => iconTreeShaker.subsetFont(
input: fileSystem.file(inputPath), input: fileSystem.file(inputPath),
outputPath: outputPath, outputPath: outputPath,
relativePath: relativePath, relativePath: relativePath,
), ),
throwsA(isA<IconTreeShakerException>()), throwsA(isA<IconTreeShakerException>()),
); );
expect(processManager, hasNoRemainingExpectations);
verify(mockProcessManager.run(_getConstFinderArgs(appDill.path))).called(1);
verify(mockProcessManager.start(fontSubsetArgs)).called(1);
}); });
testWithoutContext('Invalid font manifest', () async { testWithoutContext('Invalid font manifest', () async {
...@@ -461,7 +433,7 @@ void main() { ...@@ -461,7 +433,7 @@ void main() {
environment, environment,
fontManifestContent, fontManifestContent,
logger: logger, logger: logger,
processManager: mockProcessManager, processManager: processManager,
fileSystem: fileSystem, fileSystem: fileSystem,
artifacts: artifacts, artifacts: artifacts,
); );
...@@ -469,16 +441,14 @@ void main() { ...@@ -469,16 +441,14 @@ void main() {
_addConstFinderInvocation(appDill.path, stdout: validConstFinderResult); _addConstFinderInvocation(appDill.path, stdout: validConstFinderResult);
await expectLater( await expectLater(
() async => iconTreeShaker.subsetFont( () => iconTreeShaker.subsetFont(
input: fileSystem.file(inputPath), input: fileSystem.file(inputPath),
outputPath: outputPath, outputPath: outputPath,
relativePath: relativePath, relativePath: relativePath,
), ),
throwsA(isA<IconTreeShakerException>()), throwsA(isA<IconTreeShakerException>()),
); );
expect(processManager, hasNoRemainingExpectations);
verify(mockProcessManager.run(_getConstFinderArgs(appDill.path))).called(1);
verifyNever(mockProcessManager.start(fontSubsetArgs));
}); });
testWithoutContext('ConstFinder non-zero exit', () async { testWithoutContext('ConstFinder non-zero exit', () async {
...@@ -495,7 +465,7 @@ void main() { ...@@ -495,7 +465,7 @@ void main() {
environment, environment,
fontManifestContent, fontManifestContent,
logger: logger, logger: logger,
processManager: mockProcessManager, processManager: processManager,
fileSystem: fileSystem, fileSystem: fileSystem,
artifacts: artifacts, artifacts: artifacts,
); );
...@@ -510,9 +480,7 @@ void main() { ...@@ -510,9 +480,7 @@ void main() {
), ),
throwsA(isA<IconTreeShakerException>()), throwsA(isA<IconTreeShakerException>()),
); );
expect(processManager, hasNoRemainingExpectations);
verify(mockProcessManager.run(_getConstFinderArgs(appDill.path))).called(1);
verifyNever(mockProcessManager.start(fontSubsetArgs));
}); });
} }
...@@ -589,6 +557,3 @@ const String invalidFontManifestJson = ''' ...@@ -589,6 +557,3 @@ const String invalidFontManifestJson = '''
] ]
} }
'''; ''';
class MockProcessManager extends Mock implements ProcessManager {}
class MockProcess extends Mock implements Process {}
...@@ -20,7 +20,7 @@ import 'package:flutter_tools/src/ios/devices.dart'; ...@@ -20,7 +20,7 @@ import 'package:flutter_tools/src/ios/devices.dart';
import 'package:flutter_tools/src/ios/ios_deploy.dart'; import 'package:flutter_tools/src/ios/ios_deploy.dart';
import 'package:flutter_tools/src/ios/iproxy.dart'; import 'package:flutter_tools/src/ios/iproxy.dart';
import 'package:flutter_tools/src/ios/mac.dart'; import 'package:flutter_tools/src/ios/mac.dart';
import 'package:mockito/mockito.dart'; import 'package:test/fake.dart';
import '../../src/common.dart'; import '../../src/common.dart';
import '../../src/fake_devices.dart'; import '../../src/fake_devices.dart';
...@@ -86,12 +86,10 @@ stdout: '(lldb) run\nsuccess', ...@@ -86,12 +86,10 @@ stdout: '(lldb) run\nsuccess',
); );
void main() { void main() {
// TODO(jonahwilliams): This test doesn't really belong here but
// I don't have a better place for it for now.
testWithoutContext('disposing device disposes the portForwarder and logReader', () async { testWithoutContext('disposing device disposes the portForwarder and logReader', () async {
final IOSDevice device = setUpIOSDevice(); final IOSDevice device = setUpIOSDevice();
final DevicePortForwarder devicePortForwarder = MockDevicePortForwarder(); final FakeDevicePortForwarder devicePortForwarder = FakeDevicePortForwarder();
final DeviceLogReader deviceLogReader = MockDeviceLogReader(); final FakeDeviceLogReader deviceLogReader = FakeDeviceLogReader();
final IOSApp iosApp = PrebuiltIOSApp( final IOSApp iosApp = PrebuiltIOSApp(
projectBundleId: 'app', projectBundleId: 'app',
bundleName: 'Runner', bundleName: 'Runner',
...@@ -101,8 +99,8 @@ void main() { ...@@ -101,8 +99,8 @@ void main() {
device.setLogReader(iosApp, deviceLogReader); device.setLogReader(iosApp, deviceLogReader);
await device.dispose(); await device.dispose();
verify(deviceLogReader.dispose()).called(1); expect(deviceLogReader.disposed, true);
verify(devicePortForwarder.dispose()).called(1); expect(devicePortForwarder.disposed, true);
}); });
testWithoutContext('IOSDevice.startApp attaches in debug mode via log reading on iOS 13+', () async { testWithoutContext('IOSDevice.startApp attaches in debug mode via log reading on iOS 13+', () async {
...@@ -384,5 +382,11 @@ IOSDevice setUpIOSDevice({ ...@@ -384,5 +382,11 @@ IOSDevice setUpIOSDevice({
); );
} }
class MockDevicePortForwarder extends Mock implements DevicePortForwarder {} class FakeDevicePortForwarder extends Fake implements DevicePortForwarder {
class MockDeviceLogReader extends Mock implements DeviceLogReader {} bool disposed = false;
@override
Future<void> dispose() async {
disposed = true;
}
}
...@@ -16,55 +16,17 @@ import 'package:flutter_tools/src/ios/xcode_build_settings.dart'; ...@@ -16,55 +16,17 @@ import 'package:flutter_tools/src/ios/xcode_build_settings.dart';
import 'package:flutter_tools/src/ios/xcodeproj.dart'; import 'package:flutter_tools/src/ios/xcodeproj.dart';
import 'package:flutter_tools/src/project.dart'; import 'package:flutter_tools/src/project.dart';
import 'package:flutter_tools/src/reporting/reporting.dart'; import 'package:flutter_tools/src/reporting/reporting.dart';
import 'package:mockito/mockito.dart';
import '../../src/common.dart'; import '../../src/common.dart';
import '../../src/context.dart'; import '../../src/context.dart';
import '../../src/mocks.dart' as mocks;
const String xcodebuild = '/usr/bin/xcodebuild'; const String xcodebuild = '/usr/bin/xcodebuild';
void main() { void main() {
group('MockProcessManager', () { group('MockProcessManager', () {
mocks.MockProcessManager processManager;
XcodeProjectInterpreter xcodeProjectInterpreter;
FakePlatform platform;
BufferLogger logger;
setUp(() { setUp(() {
processManager = mocks.MockProcessManager();
platform = FakePlatform(operatingSystem: 'macos');
final FileSystem fileSystem = MemoryFileSystem.test(); final FileSystem fileSystem = MemoryFileSystem.test();
fileSystem.file(xcodebuild).createSync(recursive: true); fileSystem.file(xcodebuild).createSync(recursive: true);
logger = BufferLogger.test();
xcodeProjectInterpreter = XcodeProjectInterpreter(
logger: logger,
fileSystem: fileSystem,
platform: platform,
processManager: processManager,
usage: null,
);
});
testUsingContext('xcodebuild build settings flakes', () async {
const Duration delay = Duration(seconds: 1);
processManager.processFactory = mocks.flakyProcessFactory(
flakes: 1,
delay: delay + const Duration(seconds: 1),
);
platform.environment = const <String, String>{};
when(processManager.runSync(<String>['which', 'sysctl']))
.thenReturn(ProcessResult(0, 0, '', ''));
when(processManager.runSync(<String>['sysctl', 'hw.optional.arm64']))
.thenReturn(ProcessResult(0, 1, '', ''));
expect(await xcodeProjectInterpreter.getBuildSettings('', buildContext: const XcodeProjectBuildContext(scheme: 'Runner'), timeout: delay),
const <String, String>{});
// build settings times out and is killed once, then succeeds.
verify(processManager.killPid(any)).called(1);
// The verbose logs should tell us something timed out.
expect(logger.traceText, contains('timed out'));
}); });
}); });
......
...@@ -7,66 +7,60 @@ ...@@ -7,66 +7,60 @@
import 'dart:async'; import 'dart:async';
import 'package:file/memory.dart'; import 'package:file/memory.dart';
import 'package:flutter_tools/src/application_package.dart';
import 'package:flutter_tools/src/base/common.dart'; import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/time.dart';
import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/build_system/build_system.dart'; import 'package:flutter_tools/src/build_system/build_system.dart';
import 'package:flutter_tools/src/devfs.dart';
import 'package:flutter_tools/src/device.dart'; import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/globals_null_migrated.dart' as globals;
import 'package:flutter_tools/src/isolated/devfs_web.dart'; import 'package:flutter_tools/src/isolated/devfs_web.dart';
import 'package:flutter_tools/src/isolated/resident_web_runner.dart'; import 'package:flutter_tools/src/isolated/resident_web_runner.dart';
import 'package:flutter_tools/src/project.dart'; import 'package:flutter_tools/src/project.dart';
import 'package:flutter_tools/src/reporting/reporting.dart';
import 'package:flutter_tools/src/resident_runner.dart'; import 'package:flutter_tools/src/resident_runner.dart';
import 'package:flutter_tools/src/web/chrome.dart'; import 'package:flutter_tools/src/vmservice.dart';
import 'package:flutter_tools/src/web/web_device.dart'; import 'package:test/fake.dart';
import 'package:mockito/mockito.dart';
import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart';
import '../src/common.dart'; import '../src/common.dart';
import '../src/context.dart'; import '../src/context.dart';
import '../src/fakes.dart';
import '../src/test_build_system.dart'; import '../src/test_build_system.dart';
void main() { void main() {
ResidentWebRunner residentWebRunner; FakeFlutterDevice mockFlutterDevice;
MockFlutterDevice mockFlutterDevice; FakeWebDevFS mockWebDevFS;
MockWebDevFS mockWebDevFS; FileSystem fileSystem;
setUp(() { setUp(() {
mockWebDevFS = MockWebDevFS(); fileSystem = MemoryFileSystem.test();
final MockWebDevice mockWebDevice = MockWebDevice(); mockWebDevFS = FakeWebDevFS();
mockFlutterDevice = MockFlutterDevice(); final FakeWebDevice mockWebDevice = FakeWebDevice();
when(mockFlutterDevice.device).thenReturn(mockWebDevice); mockFlutterDevice = FakeFlutterDevice(mockWebDevice);
when(mockFlutterDevice.devFS).thenReturn(mockWebDevFS); mockFlutterDevice._devFS = mockWebDevFS;
when(mockWebDevFS.sources).thenReturn(<Uri>[]);
fileSystem.file('.packages').writeAsStringSync('\n');
fileSystem.file('pubspec.yaml').createSync();
fileSystem.file(fileSystem.path.join('lib', 'main.dart')).createSync(recursive: true);
fileSystem.file(fileSystem.path.join('web', 'index.html')).createSync(recursive: true);
}); });
void _setupMocks() { testUsingContext('Can successfully run and connect without vmservice', () async {
globals.fs.file('.packages').writeAsStringSync('\n'); final FlutterProject project = FlutterProject.fromDirectoryTest(fileSystem.currentDirectory);
globals.fs.file('pubspec.yaml').createSync(); final ResidentWebRunner residentWebRunner = ResidentWebRunner(
globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true);
globals.fs.file(globals.fs.path.join('web', 'index.html')).createSync(recursive: true);
final FlutterProject project = FlutterProject.fromDirectoryTest(globals.fs.currentDirectory);
residentWebRunner = ResidentWebRunner(
mockFlutterDevice, mockFlutterDevice,
flutterProject: project, flutterProject: project,
debuggingOptions: DebuggingOptions.disabled(BuildInfo.release), debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
ipv6: true, ipv6: true,
stayResident: true, stayResident: true,
urlTunneller: null, urlTunneller: null,
fileSystem: globals.fs, fileSystem: fileSystem,
logger: globals.logger, logger: BufferLogger.test(),
systemClock: globals.systemClock, systemClock: SystemClock.fixed(DateTime(0, 0, 0)),
usage: globals.flutterUsage, usage: TestUsage(),
); );
}
testUsingContext('Can successfully run and connect without vmservice', () async {
_setupMocks();
final FakeStatusLogger fakeStatusLogger = globals.logger as FakeStatusLogger;
final MockStatus mockStatus = MockStatus();
fakeStatusLogger.status = mockStatus;
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run( unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter, connectionInfoCompleter: connectionInfoCompleter,
...@@ -74,40 +68,74 @@ void main() { ...@@ -74,40 +68,74 @@ void main() {
final DebugConnectionInfo debugConnectionInfo = await connectionInfoCompleter.future; final DebugConnectionInfo debugConnectionInfo = await connectionInfoCompleter.future;
expect(debugConnectionInfo.wsUri, null); expect(debugConnectionInfo.wsUri, null);
verify(mockStatus.stop()).called(1);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
BuildSystem: () => TestBuildSystem.all(BuildResult(success: true)), BuildSystem: () => TestBuildSystem.all(BuildResult(success: true)),
Logger: () => FakeStatusLogger(BufferLogger.test()), FileSystem: () => fileSystem,
FileSystem: () => MemoryFileSystem.test(),
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
}); });
// Regression test for https://github.com/flutter/flutter/issues/60613 // Regression test for https://github.com/flutter/flutter/issues/60613
testUsingContext('ResidentWebRunner calls appFailedToStart if initial compilation fails', () async { testUsingContext('ResidentWebRunner calls appFailedToStart if initial compilation fails', () async {
_setupMocks(); final FlutterProject project = FlutterProject.fromDirectoryTest(fileSystem.currentDirectory);
final ResidentWebRunner residentWebRunner = ResidentWebRunner(
mockFlutterDevice,
flutterProject: project,
debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
ipv6: true,
stayResident: true,
urlTunneller: null,
fileSystem: fileSystem,
logger: BufferLogger.test(),
systemClock: SystemClock.fixed(DateTime(0, 0, 0)),
usage: TestUsage(),
);
expect(() async => residentWebRunner.run(), throwsToolExit()); expect(() => residentWebRunner.run(), throwsToolExit());
expect(await residentWebRunner.waitForAppToFinish(), 1); expect(await residentWebRunner.waitForAppToFinish(), 1);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
BuildSystem: () => TestBuildSystem.all(BuildResult(success: false)), BuildSystem: () => TestBuildSystem.all(BuildResult(success: false)),
FileSystem: () => MemoryFileSystem.test(), FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
}); });
// Regression test for https://github.com/flutter/flutter/issues/60613 // Regression test for https://github.com/flutter/flutter/issues/60613
testUsingContext('ResidentWebRunner calls appFailedToStart if error is thrown during startup', () async { testUsingContext('ResidentWebRunner calls appFailedToStart if error is thrown during startup', () async {
_setupMocks(); final FlutterProject project = FlutterProject.fromDirectoryTest(fileSystem.currentDirectory);
final ResidentWebRunner residentWebRunner = ResidentWebRunner(
mockFlutterDevice,
flutterProject: project,
debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
ipv6: true,
stayResident: true,
urlTunneller: null,
fileSystem: fileSystem,
logger: BufferLogger.test(),
systemClock: SystemClock.fixed(DateTime(0, 0, 0)),
usage: TestUsage(),
);
expect(() async => residentWebRunner.run(), throwsA(isA<Exception>())); expect(() async => residentWebRunner.run(), throwsA(isA<Exception>()));
expect(await residentWebRunner.waitForAppToFinish(), 1); expect(await residentWebRunner.waitForAppToFinish(), 1);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
BuildSystem: () => TestBuildSystem.error(Exception('foo')), BuildSystem: () => TestBuildSystem.error(Exception('foo')),
FileSystem: () => MemoryFileSystem.test(), FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
}); });
testUsingContext('Can full restart after attaching', () async { testUsingContext('Can full restart after attaching', () async {
_setupMocks(); final FlutterProject project = FlutterProject.fromDirectoryTest(fileSystem.currentDirectory);
final ResidentWebRunner residentWebRunner = ResidentWebRunner(
mockFlutterDevice,
flutterProject: project,
debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
ipv6: true,
stayResident: true,
urlTunneller: null,
fileSystem: fileSystem,
logger: BufferLogger.test(),
systemClock: SystemClock.fixed(DateTime(0, 0, 0)),
usage: TestUsage(),
);
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run( unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter, connectionInfoCompleter: connectionInfoCompleter,
...@@ -118,12 +146,24 @@ void main() { ...@@ -118,12 +146,24 @@ void main() {
expect(result.code, 0); expect(result.code, 0);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
BuildSystem: () => TestBuildSystem.all(BuildResult(success: true)), BuildSystem: () => TestBuildSystem.all(BuildResult(success: true)),
FileSystem: () => MemoryFileSystem.test(), FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
}); });
testUsingContext('Fails on compilation errors in hot restart', () async { testUsingContext('Fails on compilation errors in hot restart', () async {
_setupMocks(); final FlutterProject project = FlutterProject.fromDirectoryTest(fileSystem.currentDirectory);
final ResidentWebRunner residentWebRunner = ResidentWebRunner(
mockFlutterDevice,
flutterProject: project,
debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
ipv6: true,
stayResident: true,
urlTunneller: null,
fileSystem: fileSystem,
logger: BufferLogger.test(),
systemClock: SystemClock.fixed(DateTime(0, 0, 0)),
usage: TestUsage(),
);
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>(); final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run( unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter, connectionInfoCompleter: connectionInfoCompleter,
...@@ -138,55 +178,63 @@ void main() { ...@@ -138,55 +178,63 @@ void main() {
BuildResult(success: true), BuildResult(success: true),
BuildResult(success: false), BuildResult(success: false),
]), ]),
FileSystem: () => MemoryFileSystem.test(), FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
}); });
}
testUsingContext('Correctly performs a full refresh on attached chrome device.', () async { class FakeWebDevFS extends Fake implements WebDevFS {
_setupMocks(); @override
final MockChromeDevice chromeDevice = MockChromeDevice(); List<Uri> get sources => <Uri>[];
final MockChrome chrome = MockChrome();
final MockChromeConnection mockChromeConnection = MockChromeConnection();
final MockChromeTab mockChromeTab = MockChromeTab();
final MockWipConnection mockWipConnection = MockWipConnection();
final MockChromiumLauncher chromiumLauncher = MockChromiumLauncher();
when(mockChromeConnection.getTab(any)).thenAnswer((Invocation invocation) async {
return mockChromeTab;
});
when(mockChromeTab.connect()).thenAnswer((Invocation invocation) async {
return mockWipConnection;
});
when(chromiumLauncher.connectedInstance).thenAnswer((Invocation invocation) async {
return chrome;
});
when(chrome.chromeConnection).thenReturn(mockChromeConnection);
when(chromeDevice.chromeLauncher).thenReturn(chromiumLauncher);
when(mockFlutterDevice.device).thenReturn(chromeDevice);
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter,
));
await connectionInfoCompleter.future;
final OperationResult result = await residentWebRunner.restart(fullRestart: true);
expect(result.code, 0); @override
verify(mockWipConnection.sendCommand('Page.reload', <String, Object>{ Future<Uri> create() async {
'ignoreCache': true, return Uri.base;
})).called(1); }
}, overrides: <Type, Generator>{ }
BuildSystem: () => TestBuildSystem.all(BuildResult(success: true)),
FileSystem: () => MemoryFileSystem.test(), class FakeWebDevice extends Fake implements Device {
ProcessManager: () => FakeProcessManager.any(), @override
}); String get name => 'web';
@override
Future<bool> stopApp(
covariant ApplicationPackage app, {
String userIdentifier,
}) async {
return true;
}
@override
Future<LaunchResult> startApp(
covariant ApplicationPackage package, {
String mainPath,
String route,
DebuggingOptions debuggingOptions,
Map<String, dynamic> platformArgs,
bool prebuiltApplication = false,
bool ipv6 = false,
String userIdentifier,
}) async {
return LaunchResult.succeeded();
}
} }
class MockWebDevFS extends Mock implements WebDevFS {} class FakeFlutterDevice extends Fake implements FlutterDevice {
class MockWebDevice extends Mock implements Device {} FakeFlutterDevice(this.device);
class MockStatus extends Mock implements Status {}
class MockFlutterDevice extends Mock implements FlutterDevice {} @override
class MockChromeDevice extends Mock implements ChromiumDevice {} final FakeWebDevice device;
class MockChrome extends Mock implements Chromium {}
class MockChromeConnection extends Mock implements ChromeConnection {}
class MockChromeTab extends Mock implements ChromeTab {} DevFS _devFS;
class MockWipConnection extends Mock implements WipConnection {}
class MockChromiumLauncher extends Mock implements ChromiumLauncher {} @override
DevFS get devFS => _devFS;
@override
set devFS(DevFS value) { }
@override
FlutterVmService vmService;
}
...@@ -184,6 +184,8 @@ class FakeDeviceLogReader extends DeviceLogReader { ...@@ -184,6 +184,8 @@ class FakeDeviceLogReader extends DeviceLogReader {
StreamController<String> _cachedLinesController; StreamController<String> _cachedLinesController;
bool disposed = false;
final List<String> _lineQueue = <String>[]; final List<String> _lineQueue = <String>[];
StreamController<String> get _linesController { StreamController<String> get _linesController {
_cachedLinesController ??= StreamController<String> _cachedLinesController ??= StreamController<String>
...@@ -209,5 +211,6 @@ class FakeDeviceLogReader extends DeviceLogReader { ...@@ -209,5 +211,6 @@ class FakeDeviceLogReader extends DeviceLogReader {
Future<void> dispose() async { Future<void> dispose() async {
_lineQueue.clear(); _lineQueue.clear();
await _linesController.close(); await _linesController.close();
disposed = true;
} }
} }
// 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 'package:flutter_tools/src/base/io.dart';
import 'package:mockito/mockito.dart';
import 'package:process/process.dart';
import 'fakes.dart';
/// 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(),
);
};
}
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