Add exception to FakeCommand (#75545)

This commit is contained in:
Jenn Magder 2021-02-09 10:07:46 -08:00 committed by GitHub
parent bbc1614aa9
commit 152d88a3df
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 129 additions and 181 deletions

View file

@ -184,19 +184,17 @@ void main() {
}); });
testUsingContext('fetchLatestVersion throws toolExit if HEAD is detached', () async { testUsingContext('fetchLatestVersion throws toolExit if HEAD is detached', () async {
processManager.addCommands(<FakeCommand>[ processManager.addCommands(const <FakeCommand>[
const FakeCommand(command: <String>[ FakeCommand(command: <String>[
'git', 'fetch', '--tags' 'git', 'fetch', '--tags'
]), ]),
FakeCommand( FakeCommand(
command: const <String>['git', 'rev-parse', '--verify', '@{u}'], command: <String>['git', 'rev-parse', '--verify', '@{u}'],
onRun: () { exception: ProcessException(
throw const ProcessException(
'git', 'git',
<String>['rev-parse', '--verify', '@{u}'], <String>['rev-parse', '--verify', '@{u}'],
'fatal: HEAD does not point to a branch', 'fatal: HEAD does not point to a branch',
); ),
}
), ),
]); ]);
@ -211,19 +209,17 @@ void main() {
}); });
testUsingContext('fetchRemoteRevision throws toolExit if no upstream configured', () async { testUsingContext('fetchRemoteRevision throws toolExit if no upstream configured', () async {
processManager.addCommands(<FakeCommand>[ processManager.addCommands(const <FakeCommand>[
const FakeCommand(command: <String>[ FakeCommand(command: <String>[
'git', 'fetch', '--tags' 'git', 'fetch', '--tags'
]), ]),
FakeCommand( FakeCommand(
command: const <String>['git', 'rev-parse', '--verify', '@{u}'], command: <String>['git', 'rev-parse', '--verify', '@{u}'],
onRun: () { exception: ProcessException(
throw const ProcessException(
'git', 'git',
<String>['rev-parse', '--verify', '@{u}'], <String>['rev-parse', '--verify', '@{u}'],
'fatal: no upstream configured for branch', 'fatal: no upstream configured for branch',
); ),
},
), ),
]); ]);
@ -242,18 +238,16 @@ void main() {
testUsingContext('git exception during attemptReset throwsToolExit', () async { testUsingContext('git exception during attemptReset throwsToolExit', () async {
const String revision = 'abc123'; const String revision = 'abc123';
const String errorMessage = 'fatal: Could not parse object ´$revision´'; const String errorMessage = 'fatal: Could not parse object ´$revision´';
processManager.addCommands(<FakeCommand>[ processManager.addCommand(
FakeCommand( const FakeCommand(
command: const <String>['git', 'reset', '--hard', revision], command: <String>['git', 'reset', '--hard', revision],
onRun: () { exception: ProcessException(
throw const ProcessException(
'git', 'git',
<String>['reset', '--hard', revision], <String>['reset', '--hard', revision],
errorMessage, errorMessage,
);
},
), ),
]); ),
);
await expectLater( await expectLater(
() async => await realCommandRunner.attemptReset(revision), () async => await realCommandRunner.attemptReset(revision),

View file

@ -46,14 +46,12 @@ void main() {
}); });
testUsingContext('AndroidStudioValidator gives doctor error on java crash', () async { testUsingContext('AndroidStudioValidator gives doctor error on java crash', () async {
fakeProcessManager.addCommand(FakeCommand( fakeProcessManager.addCommand(const FakeCommand(
command: const <String>[ command: <String>[
'/opt/android-studio-with-cheese-5.0/jre/bin/java', '/opt/android-studio-with-cheese-5.0/jre/bin/java',
'-version', '-version',
], ],
onRun: () { exception: ProcessException('java', <String>['-version']),
throw const ProcessException('java', <String>['-version']);
},
)); ));
const String installPath = '/opt/android-studio-with-cheese-5.0'; const String installPath = '/opt/android-studio-with-cheese-5.0';
const String studioHome = '$home/.AndroidStudioWithCheese5.0'; const String studioHome = '$home/.AndroidStudioWithCheese5.0';

View file

@ -228,8 +228,8 @@ void main() {
], ],
stdout: 'devtools 0.9.6', stdout: 'devtools 0.9.6',
), ),
FakeCommand( const FakeCommand(
command: const <String>[ command: <String>[
'pub', 'pub',
'global', 'global',
'run', 'run',
@ -237,9 +237,7 @@ void main() {
'--no-launch-browser', '--no-launch-browser',
'--vm-uri=http://127.0.0.1:1234/abcdefg', '--vm-uri=http://127.0.0.1:1234/abcdefg',
], ],
onRun: () { exception: ProcessException('pub', <String>[]),
throw const ProcessException('pub', <String>[]);
}
) )
]), ]),
); );
@ -273,8 +271,8 @@ void main() {
], ],
stdout: 'devtools 0.9.6', stdout: 'devtools 0.9.6',
), ),
FakeCommand( const FakeCommand(
command: const <String>[ command: <String>[
'pub', 'pub',
'global', 'global',
'run', 'run',
@ -282,9 +280,7 @@ void main() {
'--no-launch-browser', '--no-launch-browser',
'--vm-uri=http://127.0.0.1:1234/abcdefg', '--vm-uri=http://127.0.0.1:1234/abcdefg',
], ],
onRun: () { exception: ProcessException('pub', <String>[]),
throw const ProcessException('pub', <String>[]);
}
) )
]), ]),
); );

View file

@ -137,9 +137,7 @@ void main() {
], environment: const <String, String>{ ], environment: const <String, String>{
'PATH': '/usr/bin:null', 'PATH': '/usr/bin:null',
...kDyLdLibEntry, ...kDyLdLibEntry,
}, onRun: () { }, exception: const ProcessException('ios-deploy', <String>[])),
throw const ProcessException('ios-deploy', <String>[]);
})
]); ]);
final IOSDevice device = setUpIOSDevice(processManager: processManager, artifacts: artifacts); final IOSDevice device = setUpIOSDevice(processManager: processManager, artifacts: artifacts);
final bool isAppInstalled = await device.isAppInstalled(iosApp); final bool isAppInstalled = await device.isAppInstalled(iosApp);
@ -244,9 +242,7 @@ void main() {
], environment: const <String, String>{ ], environment: const <String, String>{
'PATH': '/usr/bin:null', 'PATH': '/usr/bin:null',
...kDyLdLibEntry, ...kDyLdLibEntry,
}, onRun: () { }, exception: const ProcessException('ios-deploy', <String>[])),
throw const ProcessException('ios-deploy', <String>[]);
})
]); ]);
final IOSDevice device = setUpIOSDevice(processManager: processManager, artifacts: artifacts); final IOSDevice device = setUpIOSDevice(processManager: processManager, artifacts: artifacts);
final bool wasAppInstalled = await device.installApp(iosApp); final bool wasAppInstalled = await device.installApp(iosApp);
@ -267,9 +263,7 @@ void main() {
], environment: const <String, String>{ ], environment: const <String, String>{
'PATH': '/usr/bin:null', 'PATH': '/usr/bin:null',
...kDyLdLibEntry, ...kDyLdLibEntry,
}, onRun: () { }, exception: const ProcessException('ios-deploy', <String>[])),
throw const ProcessException('ios-deploy', <String>[]);
})
]); ]);
final IOSDevice device = setUpIOSDevice(processManager: processManager, artifacts: artifacts); final IOSDevice device = setUpIOSDevice(processManager: processManager, artifacts: artifacts);
final bool wasAppUninstalled = await device.uninstallApp(iosApp); final bool wasAppUninstalled = await device.uninstallApp(iosApp);

View file

@ -799,17 +799,15 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text'''
}); });
testWithoutContext('.install() handles exceptions', () async { testWithoutContext('.install() handles exceptions', () async {
fakeProcessManager.addCommand(FakeCommand( fakeProcessManager.addCommand(const FakeCommand(
command: const <String>[ command: <String>[
'xcrun', 'xcrun',
'simctl', 'simctl',
'install', 'install',
deviceId, deviceId,
appId, appId,
], ],
onRun: () { exception: ProcessException('xcrun', <String>[]),
throw const ProcessException('xcrun', <String>[]);
},
)); ));
expect( expect(
@ -819,17 +817,15 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text'''
}); });
testWithoutContext('.uninstall() handles exceptions', () async { testWithoutContext('.uninstall() handles exceptions', () async {
fakeProcessManager.addCommand(FakeCommand( fakeProcessManager.addCommand(const FakeCommand(
command: const <String>[ command: <String>[
'xcrun', 'xcrun',
'simctl', 'simctl',
'uninstall', 'uninstall',
deviceId, deviceId,
appId, appId,
], ],
onRun: () { exception: ProcessException('xcrun', <String>[]),
throw const ProcessException('xcrun', <String>[]);
},
)); ));
expect( expect(
@ -839,17 +835,15 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text'''
}); });
testWithoutContext('.launch() handles exceptions', () async { testWithoutContext('.launch() handles exceptions', () async {
fakeProcessManager.addCommand(FakeCommand( fakeProcessManager.addCommand(const FakeCommand(
command: const <String>[ command: <String>[
'xcrun', 'xcrun',
'simctl', 'simctl',
'launch', 'launch',
deviceId, deviceId,
appId, appId,
], ],
onRun: () { exception: ProcessException('xcrun', <String>[]),
throw const ProcessException('xcrun', <String>[]);
},
)); ));
expect( expect(

View file

@ -50,18 +50,6 @@ void main() {
); );
}); });
// Work around https://github.com/flutter/flutter/issues/56415.
testWithoutContext('xcodebuild versionText returns null when xcodebuild is not installed', () {
when(processManager.runSync(<String>['which', 'sysctl']))
.thenReturn(ProcessResult(0, 0, '', ''));
when(processManager.runSync(<String>['sysctl', 'hw.optional.arm64']))
.thenReturn(ProcessResult(0, 1, '', ''));
when(processManager.runSync(<String>['xcrun', 'xcodebuild', '-version']))
.thenThrow(const ProcessException(xcodebuild, <String>['-version']));
expect(xcodeProjectInterpreter.versionText, isNull);
});
testWithoutContext('xcodebuild build settings flakes', () async { testWithoutContext('xcodebuild build settings flakes', () async {
const Duration delay = Duration(seconds: 1); const Duration delay = Duration(seconds: 1);
processManager.processFactory = mocks.flakyProcessFactory( processManager.processFactory = mocks.flakyProcessFactory(
@ -143,6 +131,19 @@ void main() {
expect(fakeProcessManager.hasRemainingExpectations, isFalse); expect(fakeProcessManager.hasRemainingExpectations, isFalse);
}); });
testWithoutContext('xcodebuild versionText returns null when xcodebuild is not installed', () {
fakeProcessManager.addCommands(const <FakeCommand>[
kWhichSysctlCommand,
kARMCheckCommand,
FakeCommand(
command: <String>['xcrun', 'xcodebuild', '-version'],
exception: ProcessException(xcodebuild, <String>['-version']),
),
]);
expect(xcodeProjectInterpreter.versionText, isNull);
});
testWithoutContext('xcodebuild versionText returns formatted version text', () { testWithoutContext('xcodebuild versionText returns formatted version text', () {
fakeProcessManager.addCommands(const <FakeCommand>[ fakeProcessManager.addCommands(const <FakeCommand>[
kWhichSysctlCommand, kWhichSysctlCommand,

View file

@ -7,7 +7,7 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter_tools/src/artifacts.dart'; import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/base/io.dart' show ProcessException, ProcessResult; import 'package:flutter_tools/src/base/io.dart' show ProcessException;
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/build_info.dart'; import 'package:flutter_tools/src/build_info.dart';
@ -29,89 +29,6 @@ void main() {
logger = BufferLogger.test(); logger = BufferLogger.test();
}); });
// Group exists to work around https://github.com/flutter/flutter/issues/56415.
// Do not add more `MockProcessManager` tests.
group('MockProcessManager', () {
ProcessManager processManager;
setUp(() {
processManager = MockProcessManager();
});
group('Xcode', () {
Xcode xcode;
MockXcodeProjectInterpreter mockXcodeProjectInterpreter;
setUp(() {
mockXcodeProjectInterpreter = MockXcodeProjectInterpreter();
xcode = Xcode.test(
processManager: processManager,
xcodeProjectInterpreter: mockXcodeProjectInterpreter,
);
});
testWithoutContext('xcodeSelectPath returns null when xcode-select is not installed', () {
when(processManager.runSync(<String>['/usr/bin/xcode-select', '--print-path']))
.thenThrow(const ProcessException('/usr/bin/xcode-select', <String>['--print-path']));
expect(xcode.xcodeSelectPath, isNull);
when(processManager.runSync(<String>['/usr/bin/xcode-select', '--print-path']))
.thenThrow(ArgumentError('Invalid argument(s): Cannot find executable for /usr/bin/xcode-select'));
expect(xcode.xcodeSelectPath, isNull);
});
testWithoutContext('eulaSigned is false when clang is not installed', () {
when(mockXcodeProjectInterpreter.xcrunCommand()).thenReturn(<String>['xcrun']);
when(processManager.runSync(<String>['which', 'sysctl']))
.thenReturn(ProcessResult(1, 0, '', ''));
when(processManager.runSync(<String>['sysctl', 'hw.optional.arm64']))
.thenReturn(ProcessResult(123, 1, '', ''));
when(processManager.runSync(<String>['xcrun', 'clang']))
.thenThrow(const ProcessException('xcrun', <String>['clang']));
expect(xcode.eulaSigned, isFalse);
});
});
group('xcdevice', () {
XCDevice xcdevice;
Xcode xcode;
setUp(() {
xcode = Xcode.test(
processManager: FakeProcessManager.any(),
xcodeProjectInterpreter: XcodeProjectInterpreter.test(
processManager: FakeProcessManager.any(),
),
);
xcdevice = XCDevice(
processManager: processManager,
logger: logger,
xcode: xcode,
platform: null,
artifacts: Artifacts.test(),
cache: Cache.test(),
iproxy: IProxy.test(logger: logger, processManager: processManager),
);
});
testWithoutContext('available devices xcdevice fails', () async {
when(processManager.run(<String>['xcrun', 'xcdevice', 'list', '--timeout', '2']))
.thenThrow(const ProcessException('xcrun', <String>['xcdevice', 'list', '--timeout', '2']));
expect(await xcdevice.getAvailableIOSDevices(), isEmpty);
});
testWithoutContext('diagnostics xcdevice fails', () async {
when(processManager.run(<String>['xcrun', 'xcdevice', 'list', '--timeout', '2']))
.thenThrow(const ProcessException('xcrun', <String>['xcdevice', 'list', '--timeout', '2']));
expect(await xcdevice.getDiagnostics(), isEmpty);
});
});
});
group('FakeProcessManager', () { group('FakeProcessManager', () {
FakeProcessManager fakeProcessManager; FakeProcessManager fakeProcessManager;
@ -200,6 +117,24 @@ void main() {
expect(fakeProcessManager.hasRemainingExpectations, isFalse); expect(fakeProcessManager.hasRemainingExpectations, isFalse);
}); });
testWithoutContext('xcodeSelectPath returns null when xcode-select is not installed', () {
fakeProcessManager.addCommand(const FakeCommand(
command: <String>['/usr/bin/xcode-select', '--print-path'],
exception: ProcessException('/usr/bin/xcode-select', <String>['--print-path']),
));
expect(xcode.xcodeSelectPath, isNull);
expect(fakeProcessManager.hasRemainingExpectations, isFalse);
fakeProcessManager.addCommand(FakeCommand(
command: const <String>['/usr/bin/xcode-select', '--print-path'],
exception: ArgumentError('Invalid argument(s): Cannot find executable for /usr/bin/xcode-select'),
));
expect(xcode.xcodeSelectPath, isNull);
expect(fakeProcessManager.hasRemainingExpectations, isFalse);
});
testWithoutContext('xcodeVersionSatisfactory is false when version is less than minimum', () { testWithoutContext('xcodeVersionSatisfactory is false when version is less than minimum', () {
when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true); when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
when(mockXcodeProjectInterpreter.majorVersion).thenReturn(9); when(mockXcodeProjectInterpreter.majorVersion).thenReturn(9);
@ -343,6 +278,18 @@ void main() {
expect(fakeProcessManager.hasRemainingExpectations, isFalse); expect(fakeProcessManager.hasRemainingExpectations, isFalse);
}); });
testWithoutContext('eulaSigned is false when clang is not installed', () {
when(mockXcodeProjectInterpreter.xcrunCommand()).thenReturn(<String>['xcrun']);
fakeProcessManager.addCommand(
const FakeCommand(
command: <String>['xcrun', 'clang'],
exception: ProcessException('xcrun', <String>['clang']),
),
);
expect(xcode.eulaSigned, isFalse);
});
testWithoutContext('eulaSigned is true when clang output indicates EULA has been accepted', () { testWithoutContext('eulaSigned is true when clang output indicates EULA has been accepted', () {
fakeProcessManager.addCommands( fakeProcessManager.addCommands(
const <FakeCommand>[ const <FakeCommand>[
@ -609,6 +556,15 @@ void main() {
Artifacts: () => Artifacts.test(), Artifacts: () => Artifacts.test(),
}); });
testWithoutContext('available devices xcdevice fails', () async {
fakeProcessManager.addCommand(const FakeCommand(
command: <String>['xcrun', 'xcdevice', 'list', '--timeout', '2'],
exception: ProcessException('xcrun', <String>['xcdevice', 'list', '--timeout', '2']),
));
expect(await xcdevice.getAvailableIOSDevices(), isEmpty);
});
testWithoutContext('uses timeout', () async { testWithoutContext('uses timeout', () async {
fakeProcessManager.addCommand(const FakeCommand( fakeProcessManager.addCommand(const FakeCommand(
command: <String>['xcrun', 'xcdevice', 'list', '--timeout', '20'], command: <String>['xcrun', 'xcdevice', 'list', '--timeout', '20'],
@ -737,6 +693,15 @@ void main() {
Platform: () => macPlatform, Platform: () => macPlatform,
}); });
testWithoutContext('diagnostics xcdevice fails', () async {
fakeProcessManager.addCommand(const FakeCommand(
command: <String>['xcrun', 'xcdevice', 'list', '--timeout', '2'],
exception: ProcessException('xcrun', <String>['xcdevice', 'list', '--timeout', '2']),
));
expect(await xcdevice.getDiagnostics(), isEmpty);
});
testUsingContext('returns error message', () async { testUsingContext('returns error message', () async {
const String devicesOutput = ''' const String devicesOutput = '''
[ [
@ -843,5 +808,4 @@ void main() {
}); });
} }
class MockProcessManager extends Mock implements ProcessManager {}
class MockXcodeProjectInterpreter extends Mock implements XcodeProjectInterpreter {} class MockXcodeProjectInterpreter extends Mock implements XcodeProjectInterpreter {}

View file

@ -33,6 +33,7 @@ class FakeCommand {
this.stderr = '', this.stderr = '',
this.completer, this.completer,
this.stdin, this.stdin,
this.exception,
}) : assert(command != null), }) : assert(command != null),
assert(duration != null), assert(duration != null),
assert(exitCode != null); assert(exitCode != null);
@ -97,6 +98,9 @@ class FakeCommand {
/// [FakeProcess]. /// [FakeProcess].
final IOSink stdin; final IOSink stdin;
/// If provided, this exception will be thrown when the fake command is run.
final dynamic exception;
void _matches( void _matches(
List<String> command, List<String> command,
String workingDirectory, String workingDirectory,
@ -229,6 +233,9 @@ abstract class FakeProcessManager implements ProcessManager {
) { ) {
_pid += 1; _pid += 1;
final FakeCommand fakeCommand = findCommand(command, workingDirectory, environment, encoding); final FakeCommand fakeCommand = findCommand(command, workingDirectory, environment, encoding);
if (fakeCommand.exception != null) {
throw fakeCommand.exception;
}
if (fakeCommand.onRun != null) { if (fakeCommand.onRun != null) {
fakeCommand.onRun(); fakeCommand.onRun();
} }