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

[flutter_tools] update visual studio and validator to remove globals, update test cases (#51680)

Updates VisualStudio and VisualStudioValidator to use constructors instead of global injection. Updates VisualStudio test cases to prefer FakeProcessManager
Updates build_windows test to work without injected VisualStudio
parent 53dce17b
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
import 'dart:async'; import 'dart:async';
import 'package:meta/meta.dart';
import '../base/common.dart'; import '../base/common.dart';
import '../build_info.dart'; import '../build_info.dart';
import '../cache.dart'; import '../cache.dart';
...@@ -12,6 +14,7 @@ import '../globals.dart' as globals; ...@@ -12,6 +14,7 @@ import '../globals.dart' as globals;
import '../project.dart'; import '../project.dart';
import '../runner/flutter_command.dart' show FlutterCommandResult; import '../runner/flutter_command.dart' show FlutterCommandResult;
import '../windows/build_windows.dart'; import '../windows/build_windows.dart';
import '../windows/visual_studio.dart';
import 'build.dart'; import 'build.dart';
/// A command to build a windows desktop target through a build shell script. /// A command to build a windows desktop target through a build shell script.
...@@ -36,6 +39,9 @@ class BuildWindowsCommand extends BuildSubCommand { ...@@ -36,6 +39,9 @@ class BuildWindowsCommand extends BuildSubCommand {
@override @override
String get description => 'build the desktop Windows target.'; String get description => 'build the desktop Windows target.';
@visibleForTesting
VisualStudio visualStudioOverride;
@override @override
Future<FlutterCommandResult> runCommand() async { Future<FlutterCommandResult> runCommand() async {
Cache.releaseLockEarly(); Cache.releaseLockEarly();
...@@ -47,7 +53,12 @@ class BuildWindowsCommand extends BuildSubCommand { ...@@ -47,7 +53,12 @@ class BuildWindowsCommand extends BuildSubCommand {
if (!globals.platform.isWindows) { if (!globals.platform.isWindows) {
throwToolExit('"build windows" only supported on Windows hosts.'); throwToolExit('"build windows" only supported on Windows hosts.');
} }
await buildWindows(flutterProject.windows, buildInfo, target: targetFile); await buildWindows(
flutterProject.windows,
buildInfo,
target: targetFile,
visualStudioOverride: visualStudioOverride,
);
return FlutterCommandResult.success(); return FlutterCommandResult.success();
} }
} }
...@@ -188,8 +188,15 @@ Future<T> runInContext<T>( ...@@ -188,8 +188,15 @@ Future<T> runInContext<T>(
runningOnBot: runningOnBot, runningOnBot: runningOnBot,
), ),
UserMessages: () => UserMessages(), UserMessages: () => UserMessages(),
VisualStudio: () => VisualStudio(), VisualStudioValidator: () => VisualStudioValidator(
VisualStudioValidator: () => const VisualStudioValidator(), userMessages: globals.userMessages,
visualStudio: VisualStudio(
fileSystem: globals.fs,
platform: globals.platform,
logger: globals.logger,
processManager: globals.processManager,
)
),
WebWorkflow: () => const WebWorkflow(), WebWorkflow: () => const WebWorkflow(),
WindowsWorkflow: () => const WindowsWorkflow(), WindowsWorkflow: () => const WindowsWorkflow(),
Xcode: () => Xcode( Xcode: () => Xcode(
......
...@@ -16,7 +16,10 @@ import 'property_sheet.dart'; ...@@ -16,7 +16,10 @@ import 'property_sheet.dart';
import 'visual_studio.dart'; import 'visual_studio.dart';
/// Builds the Windows project using msbuild. /// Builds the Windows project using msbuild.
Future<void> buildWindows(WindowsProject windowsProject, BuildInfo buildInfo, {String target}) async { Future<void> buildWindows(WindowsProject windowsProject, BuildInfo buildInfo, {
String target,
VisualStudio visualStudioOverride,
}) async {
if (!windowsProject.solutionFile.existsSync()) { if (!windowsProject.solutionFile.existsSync()) {
throwToolExit( throwToolExit(
'No Windows desktop project configured. ' 'No Windows desktop project configured. '
...@@ -28,6 +31,12 @@ Future<void> buildWindows(WindowsProject windowsProject, BuildInfo buildInfo, {S ...@@ -28,6 +31,12 @@ Future<void> buildWindows(WindowsProject windowsProject, BuildInfo buildInfo, {S
_writeGeneratedFlutterProperties(windowsProject, buildInfo, target); _writeGeneratedFlutterProperties(windowsProject, buildInfo, target);
createPluginSymlinks(windowsProject.project); createPluginSymlinks(windowsProject.project);
final VisualStudio visualStudio = visualStudioOverride ?? VisualStudio(
fileSystem: globals.fs,
platform: globals.platform,
logger: globals.logger,
processManager: globals.processManager,
);
final String vcvarsScript = visualStudio.vcvarsPath; final String vcvarsScript = visualStudio.vcvarsPath;
if (vcvarsScript == null) { if (vcvarsScript == null) {
throwToolExit('Unable to find suitable Visual Studio toolchain. ' throwToolExit('Unable to find suitable Visual Studio toolchain. '
......
...@@ -2,16 +2,31 @@ ...@@ -2,16 +2,31 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import '../base/context.dart'; import 'package:meta/meta.dart';
import 'package:platform/platform.dart';
import 'package:process/process.dart';
import '../base/file_system.dart';
import '../base/io.dart'; import '../base/io.dart';
import '../base/logger.dart';
import '../base/process.dart'; import '../base/process.dart';
import '../convert.dart'; import '../convert.dart';
import '../globals.dart' as globals;
VisualStudio get visualStudio => context.get<VisualStudio>();
/// Encapsulates information about the installed copy of Visual Studio, if any. /// Encapsulates information about the installed copy of Visual Studio, if any.
class VisualStudio { class VisualStudio {
VisualStudio({
@required FileSystem fileSystem,
@required ProcessManager processManager,
@required Platform platform,
@required Logger logger,
}) : _platform = platform,
_fileSystem = fileSystem,
_processUtils = ProcessUtils(processManager: processManager, logger: logger);
final FileSystem _fileSystem;
final Platform _platform;
final ProcessUtils _processUtils;
/// True if Visual Studio installation was found. /// True if Visual Studio installation was found.
/// ///
/// Versions older than 2017 Update 2 won't be detected, so error messages to /// Versions older than 2017 Update 2 won't be detected, so error messages to
...@@ -107,7 +122,7 @@ class VisualStudio { ...@@ -107,7 +122,7 @@ class VisualStudio {
if (details.isEmpty) { if (details.isEmpty) {
return null; return null;
} }
return globals.fs.path.join( return _fileSystem.path.join(
_usableVisualStudioDetails[_installationPathKey] as String, _usableVisualStudioDetails[_installationPathKey] as String,
'VC', 'VC',
'Auxiliary', 'Auxiliary',
...@@ -125,8 +140,8 @@ class VisualStudio { ...@@ -125,8 +140,8 @@ class VisualStudio {
/// present then there isn't a new enough installation of VS. This path is /// present then there isn't a new enough installation of VS. This path is
/// not user-controllable, unlike the install location of Visual Studio /// not user-controllable, unlike the install location of Visual Studio
/// itself. /// itself.
final String _vswherePath = globals.fs.path.join( String get _vswherePath => _fileSystem.path.join(
globals.platform.environment['PROGRAMFILES(X86)'], _platform.environment['PROGRAMFILES(X86)'],
'Microsoft Visual Studio', 'Microsoft Visual Studio',
'Installer', 'Installer',
'vswhere.exe', 'vswhere.exe',
...@@ -215,7 +230,7 @@ class VisualStudio { ...@@ -215,7 +230,7 @@ class VisualStudio {
'-utf8', '-utf8',
'-latest', '-latest',
]; ];
final RunResult whereResult = processUtils.runSync(<String>[ final RunResult whereResult = _processUtils.runSync(<String>[
_vswherePath, _vswherePath,
...defaultArguments, ...defaultArguments,
...?additionalArguments, ...?additionalArguments,
......
...@@ -2,15 +2,25 @@ ...@@ -2,15 +2,25 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:meta/meta.dart';
import '../base/context.dart'; import '../base/context.dart';
import '../base/user_messages.dart'; import '../base/user_messages.dart' hide userMessages;
import '../doctor.dart'; import '../doctor.dart';
import 'visual_studio.dart'; import 'visual_studio.dart';
VisualStudioValidator get visualStudioValidator => context.get<VisualStudioValidator>(); VisualStudioValidator get visualStudioValidator => context.get<VisualStudioValidator>();
class VisualStudioValidator extends DoctorValidator { class VisualStudioValidator extends DoctorValidator {
const VisualStudioValidator() : super('Visual Studio - develop for Windows'); const VisualStudioValidator({
@required VisualStudio visualStudio,
@required UserMessages userMessages,
}) : _visualStudio = visualStudio,
_userMessages = userMessages,
super('Visual Studio - develop for Windows');
final VisualStudio _visualStudio;
final UserMessages _userMessages;
@override @override
Future<ValidationResult> validate() async { Future<ValidationResult> validate() async {
...@@ -18,57 +28,57 @@ class VisualStudioValidator extends DoctorValidator { ...@@ -18,57 +28,57 @@ class VisualStudioValidator extends DoctorValidator {
ValidationType status = ValidationType.missing; ValidationType status = ValidationType.missing;
String versionInfo; String versionInfo;
if (visualStudio.isInstalled) { if (_visualStudio.isInstalled) {
status = ValidationType.installed; status = ValidationType.installed;
messages.add(ValidationMessage( messages.add(ValidationMessage(
userMessages.visualStudioLocation(visualStudio.installLocation) _userMessages.visualStudioLocation(_visualStudio.installLocation)
)); ));
messages.add(ValidationMessage(userMessages.visualStudioVersion( messages.add(ValidationMessage(_userMessages.visualStudioVersion(
visualStudio.displayName, _visualStudio.displayName,
visualStudio.fullVersion, _visualStudio.fullVersion,
))); )));
if (visualStudio.isPrerelease) { if (_visualStudio.isPrerelease) {
messages.add(ValidationMessage(userMessages.visualStudioIsPrerelease)); messages.add(ValidationMessage(_userMessages.visualStudioIsPrerelease));
} }
// Messages for faulty installations. // Messages for faulty installations.
if (!visualStudio.isAtLeastMinimumVersion) { if (!_visualStudio.isAtLeastMinimumVersion) {
status = ValidationType.partial; status = ValidationType.partial;
messages.add(ValidationMessage.error( messages.add(ValidationMessage.error(
userMessages.visualStudioTooOld( _userMessages.visualStudioTooOld(
visualStudio.minimumVersionDescription, _visualStudio.minimumVersionDescription,
visualStudio.workloadDescription, _visualStudio.workloadDescription,
visualStudio.necessaryComponentDescriptions(), _visualStudio.necessaryComponentDescriptions(),
), ),
)); ));
} else if (visualStudio.isRebootRequired) { } else if (_visualStudio.isRebootRequired) {
status = ValidationType.partial; status = ValidationType.partial;
messages.add(ValidationMessage.error(userMessages.visualStudioRebootRequired)); messages.add(ValidationMessage.error(_userMessages.visualStudioRebootRequired));
} else if (!visualStudio.isComplete) { } else if (!_visualStudio.isComplete) {
status = ValidationType.partial; status = ValidationType.partial;
messages.add(ValidationMessage.error(userMessages.visualStudioIsIncomplete)); messages.add(ValidationMessage.error(_userMessages.visualStudioIsIncomplete));
} else if (!visualStudio.isLaunchable) { } else if (!_visualStudio.isLaunchable) {
status = ValidationType.partial; status = ValidationType.partial;
messages.add(ValidationMessage.error(userMessages.visualStudioNotLaunchable)); messages.add(ValidationMessage.error(_userMessages.visualStudioNotLaunchable));
} else if (!visualStudio.hasNecessaryComponents) { } else if (!_visualStudio.hasNecessaryComponents) {
status = ValidationType.partial; status = ValidationType.partial;
messages.add(ValidationMessage.error( messages.add(ValidationMessage.error(
userMessages.visualStudioMissingComponents( _userMessages.visualStudioMissingComponents(
visualStudio.workloadDescription, _visualStudio.workloadDescription,
visualStudio.necessaryComponentDescriptions(), _visualStudio.necessaryComponentDescriptions(),
), ),
)); ));
} }
versionInfo = '${visualStudio.displayName} ${visualStudio.displayVersion}'; versionInfo = '${_visualStudio.displayName} ${_visualStudio.displayVersion}';
} else { } else {
status = ValidationType.missing; status = ValidationType.missing;
messages.add(ValidationMessage.error( messages.add(ValidationMessage.error(
userMessages.visualStudioMissing( _userMessages.visualStudioMissing(
visualStudio.workloadDescription, _visualStudio.workloadDescription,
visualStudio.necessaryComponentDescriptions(), _visualStudio.necessaryComponentDescriptions(),
), ),
)); ));
} }
......
...@@ -8,7 +8,6 @@ import 'package:platform/platform.dart'; ...@@ -8,7 +8,6 @@ import 'package:platform/platform.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/io.dart'; import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/build.dart';
import 'package:flutter_tools/src/commands/build_windows.dart'; import 'package:flutter_tools/src/commands/build_windows.dart';
import 'package:flutter_tools/src/convert.dart'; import 'package:flutter_tools/src/convert.dart';
import 'package:flutter_tools/src/features.dart'; import 'package:flutter_tools/src/features.dart';
...@@ -23,15 +22,28 @@ import '../../src/context.dart'; ...@@ -23,15 +22,28 @@ import '../../src/context.dart';
import '../../src/mocks.dart'; import '../../src/mocks.dart';
import '../../src/testbed.dart'; import '../../src/testbed.dart';
const String solutionPath = r'C:\windows\Runner.sln';
const String visualStudioPath = r'C:\Program Files (x86)\Microsoft Visual Studio\2017\Community';
const String vcvarsPath = visualStudioPath + r'\VC\Auxiliary\Build\vcvars64.bat';
final Platform windowsPlatform = FakePlatform(
operatingSystem: 'windows',
environment: <String, String>{
'PROGRAMFILES(X86)': r'C:\Program Files (x86)\',
'FLUTTER_ROOT': r'C:\',
}
);
final Platform notWindowsPlatform = FakePlatform(
operatingSystem: 'linux',
environment: <String, String>{
'FLUTTER_ROOT': r'C:\',
}
);
void main() { void main() {
MockProcessManager mockProcessManager; MockProcessManager mockProcessManager;
MockProcess mockProcess; MockProcess mockProcess;
MockPlatform windowsPlatform;
MockPlatform notWindowsPlatform;
MockVisualStudio mockVisualStudio; MockVisualStudio mockVisualStudio;
const String solutionPath = r'C:\windows\Runner.sln';
const String visualStudioPath = r'C:\Program Files (x86)\Microsoft Visual Studio\2017\Community';
const String vcvarsPath = visualStudioPath + r'\VC\Auxiliary\Build\vcvars64.bat';
setUpAll(() { setUpAll(() {
Cache.disableLocking(); Cache.disableLocking();
...@@ -40,9 +52,6 @@ void main() { ...@@ -40,9 +52,6 @@ void main() {
setUp(() { setUp(() {
mockProcessManager = MockProcessManager(); mockProcessManager = MockProcessManager();
mockProcess = MockProcess(); mockProcess = MockProcess();
windowsPlatform = MockPlatform()
..environment['PROGRAMFILES(X86)'] = r'C:\Program Files (x86)\';
notWindowsPlatform = MockPlatform();
mockVisualStudio = MockVisualStudio(); mockVisualStudio = MockVisualStudio();
when(mockProcess.exitCode).thenAnswer((Invocation invocation) async { when(mockProcess.exitCode).thenAnswer((Invocation invocation) async {
return 0; return 0;
...@@ -53,8 +62,6 @@ void main() { ...@@ -53,8 +62,6 @@ void main() {
when(mockProcess.stdout).thenAnswer((Invocation invocation) { when(mockProcess.stdout).thenAnswer((Invocation invocation) {
return Stream<List<int>>.fromIterable(<List<int>>[utf8.encode('STDOUT STUFF')]); return Stream<List<int>>.fromIterable(<List<int>>[utf8.encode('STDOUT STUFF')]);
}); });
when(windowsPlatform.isWindows).thenReturn(true);
when(notWindowsPlatform.isWindows).thenReturn(false);
}); });
// Creates the mock files necessary to look like a Flutter project. // Creates the mock files necessary to look like a Flutter project.
...@@ -71,55 +78,58 @@ void main() { ...@@ -71,55 +78,58 @@ void main() {
} }
testUsingContext('Windows build fails when there is no vcvars64.bat', () async { testUsingContext('Windows build fails when there is no vcvars64.bat', () async {
final BuildCommand command = BuildCommand(); final BuildWindowsCommand command = BuildWindowsCommand()
..visualStudioOverride = mockVisualStudio;
applyMocksToCommand(command); applyMocksToCommand(command);
setUpMockProjectFilesForBuild(); setUpMockProjectFilesForBuild();
expect(createTestCommandRunner(command).run( expect(createTestCommandRunner(command).run(
const <String>['build', 'windows'] const <String>['windows']
), throwsToolExit()); ), throwsToolExit());
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Platform: () => windowsPlatform, Platform: () => windowsPlatform,
FileSystem: () => MemoryFileSystem(style: FileSystemStyle.windows), FileSystem: () => MemoryFileSystem(style: FileSystemStyle.windows),
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
VisualStudio: () => mockVisualStudio,
FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true), FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true),
}); });
testUsingContext('Windows build fails when there is no windows project', () async { testUsingContext('Windows build fails when there is no windows project', () async {
final BuildCommand command = BuildCommand(); final BuildWindowsCommand command = BuildWindowsCommand()
..visualStudioOverride = mockVisualStudio;
applyMocksToCommand(command); applyMocksToCommand(command);
setUpMockCoreProjectFiles(); setUpMockCoreProjectFiles();
when(mockVisualStudio.vcvarsPath).thenReturn(vcvarsPath); when(mockVisualStudio.vcvarsPath).thenReturn(vcvarsPath);
expect(createTestCommandRunner(command).run( expect(createTestCommandRunner(command).run(
const <String>['build', 'windows'] const <String>['windows']
), throwsToolExit(message: 'No Windows desktop project configured')); ), throwsToolExit(message: 'No Windows desktop project configured'));
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Platform: () => windowsPlatform, Platform: () => windowsPlatform,
FileSystem: () => MemoryFileSystem(style: FileSystemStyle.windows), FileSystem: () => MemoryFileSystem(style: FileSystemStyle.windows),
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
VisualStudio: () => mockVisualStudio,
FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true), FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true),
}); });
testUsingContext('Windows build fails on non windows platform', () async { testUsingContext('Windows build fails on non windows platform', () async {
final BuildCommand command = BuildCommand(); final BuildWindowsCommand command = BuildWindowsCommand()
..visualStudioOverride = mockVisualStudio;
applyMocksToCommand(command); applyMocksToCommand(command);
setUpMockProjectFilesForBuild(); setUpMockProjectFilesForBuild();
when(mockVisualStudio.vcvarsPath).thenReturn(vcvarsPath); when(mockVisualStudio.vcvarsPath).thenReturn(vcvarsPath);
expect(createTestCommandRunner(command).run( expect(createTestCommandRunner(command).run(
const <String>['build', 'windows'] const <String>['windows']
), throwsToolExit()); ), throwsToolExit());
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Platform: () => notWindowsPlatform, Platform: () => notWindowsPlatform,
FileSystem: () => MemoryFileSystem(style: FileSystemStyle.windows), FileSystem: () => MemoryFileSystem(style: FileSystemStyle.windows),
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
VisualStudio: () => mockVisualStudio,
FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true), FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true),
}); });
testUsingContext('Windows build does not spew stdout to status logger', () async { testUsingContext('Windows build does not spew stdout to status logger', () async {
final BuildCommand command = BuildCommand(); final BuildWindowsCommand command = BuildWindowsCommand()
..visualStudioOverride = mockVisualStudio;
applyMocksToCommand(command); applyMocksToCommand(command);
setUpMockProjectFilesForBuild(); setUpMockProjectFilesForBuild();
when(mockVisualStudio.vcvarsPath).thenReturn(vcvarsPath); when(mockVisualStudio.vcvarsPath).thenReturn(vcvarsPath);
...@@ -134,7 +144,7 @@ void main() { ...@@ -134,7 +144,7 @@ void main() {
}); });
await createTestCommandRunner(command).run( await createTestCommandRunner(command).run(
const <String>['build', 'windows'] const <String>['windows']
); );
expect(testLogger.statusText, isNot(contains('STDOUT STUFF'))); expect(testLogger.statusText, isNot(contains('STDOUT STUFF')));
expect(testLogger.traceText, contains('STDOUT STUFF')); expect(testLogger.traceText, contains('STDOUT STUFF'));
...@@ -142,12 +152,12 @@ void main() { ...@@ -142,12 +152,12 @@ void main() {
FileSystem: () => MemoryFileSystem(style: FileSystemStyle.windows), FileSystem: () => MemoryFileSystem(style: FileSystemStyle.windows),
ProcessManager: () => mockProcessManager, ProcessManager: () => mockProcessManager,
Platform: () => windowsPlatform, Platform: () => windowsPlatform,
VisualStudio: () => mockVisualStudio,
FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true), FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true),
}); });
testUsingContext('Windows build invokes msbuild and writes generated files', () async { testUsingContext('Windows build invokes msbuild and writes generated files', () async {
final BuildCommand command = BuildCommand(); final BuildWindowsCommand command = BuildWindowsCommand()
..visualStudioOverride = mockVisualStudio;
applyMocksToCommand(command); applyMocksToCommand(command);
setUpMockProjectFilesForBuild(); setUpMockProjectFilesForBuild();
when(mockVisualStudio.vcvarsPath).thenReturn(vcvarsPath); when(mockVisualStudio.vcvarsPath).thenReturn(vcvarsPath);
...@@ -162,7 +172,7 @@ void main() { ...@@ -162,7 +172,7 @@ void main() {
}); });
await createTestCommandRunner(command).run( await createTestCommandRunner(command).run(
const <String>['build', 'windows'] const <String>['windows']
); );
// Spot-check important elements from the properties file. // Spot-check important elements from the properties file.
...@@ -176,12 +186,12 @@ void main() { ...@@ -176,12 +186,12 @@ void main() {
FileSystem: () => MemoryFileSystem(style: FileSystemStyle.windows), FileSystem: () => MemoryFileSystem(style: FileSystemStyle.windows),
ProcessManager: () => mockProcessManager, ProcessManager: () => mockProcessManager,
Platform: () => windowsPlatform, Platform: () => windowsPlatform,
VisualStudio: () => mockVisualStudio,
FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true), FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true),
}); });
testUsingContext('Release build prints an under-construction warning', () async { testUsingContext('Release build prints an under-construction warning', () async {
final BuildCommand command = BuildCommand(); final BuildWindowsCommand command = BuildWindowsCommand()
..visualStudioOverride = mockVisualStudio;
applyMocksToCommand(command); applyMocksToCommand(command);
setUpMockProjectFilesForBuild(); setUpMockProjectFilesForBuild();
when(mockVisualStudio.vcvarsPath).thenReturn(vcvarsPath); when(mockVisualStudio.vcvarsPath).thenReturn(vcvarsPath);
...@@ -196,7 +206,7 @@ void main() { ...@@ -196,7 +206,7 @@ void main() {
}); });
await createTestCommandRunner(command).run( await createTestCommandRunner(command).run(
const <String>['build', 'windows'] const <String>['windows']
); );
expect(testLogger.statusText, contains('🚧')); expect(testLogger.statusText, contains('🚧'));
...@@ -204,35 +214,24 @@ void main() { ...@@ -204,35 +214,24 @@ void main() {
FileSystem: () => MemoryFileSystem(style: FileSystemStyle.windows), FileSystem: () => MemoryFileSystem(style: FileSystemStyle.windows),
ProcessManager: () => mockProcessManager, ProcessManager: () => mockProcessManager,
Platform: () => windowsPlatform, Platform: () => windowsPlatform,
VisualStudio: () => mockVisualStudio,
FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true), FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true),
}); });
testUsingContext('hidden when not enabled on Windows host', () { testUsingContext('hidden when not enabled on Windows host', () {
when(globals.platform.isWindows).thenReturn(true);
expect(BuildWindowsCommand().hidden, true); expect(BuildWindowsCommand().hidden, true);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: false), FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: false),
Platform: () => MockPlatform(), Platform: () => windowsPlatform,
}); });
testUsingContext('Not hidden when enabled and on Windows host', () { testUsingContext('Not hidden when enabled and on Windows host', () {
when(globals.platform.isWindows).thenReturn(true);
expect(BuildWindowsCommand().hidden, false); expect(BuildWindowsCommand().hidden, false);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true), FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true),
Platform: () => MockPlatform(), Platform: () => windowsPlatform,
}); });
} }
class MockProcessManager extends Mock implements ProcessManager {} class MockProcessManager extends Mock implements ProcessManager {}
class MockProcess extends Mock implements Process {} class MockProcess extends Mock implements Process {}
class MockPlatform extends Mock implements Platform {
@override
Map<String, String> environment = <String, String>{
'FLUTTER_ROOT': r'C:\',
};
}
class MockVisualStudio extends Mock implements VisualStudio {} class MockVisualStudio extends Mock implements VisualStudio {}
...@@ -3,39 +3,33 @@ ...@@ -3,39 +3,33 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'package:file/memory.dart'; import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/file_system.dart'; import 'package:mockito/mockito.dart';
import 'package:flutter_tools/src/base/io.dart' show ProcessException, ProcessResult; import 'package:platform/platform.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart' show ProcessException;
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/convert.dart'; import 'package:flutter_tools/src/convert.dart';
import 'package:flutter_tools/src/windows/visual_studio.dart'; import 'package:flutter_tools/src/windows/visual_studio.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:mockito/mockito.dart';
import 'package:process/process.dart';
import 'package:platform/platform.dart';
import '../../src/common.dart'; import '../../src/common.dart';
import '../../src/context.dart'; import '../../src/context.dart';
import '../../src/mocks.dart';
class MockPlatform extends Mock implements Platform { const String programFilesPath = r'C:\Program Files (x86)';
@override const String visualStudioPath = programFilesPath + r'\Microsoft Visual Studio\2017\Community';
Map<String, String> environment = <String, String>{}; const String vcvarsPath = visualStudioPath + r'\VC\Auxiliary\Build\vcvars64.bat';
} const String vswherePath = programFilesPath + r'\Microsoft Visual Studio\Installer\vswhere.exe';
class MockProcessManager extends Mock implements ProcessManager {}
class MockProcessResult extends Mock implements ProcessResult {}
void main() { final Platform windowsPlatform = FakePlatform(
const String programFilesPath = r'C:\Program Files (x86)'; operatingSystem: 'windows',
const String visualStudioPath = programFilesPath + r'\Microsoft Visual Studio\2017\Community'; environment: <String, String>{
const String vcvarsPath = visualStudioPath + r'\VC\Auxiliary\Build\vcvars64.bat'; 'PROGRAMFILES(X86)': r'C:\Program Files (x86)\',
const String vswherePath = programFilesPath + r'\Microsoft Visual Studio\Installer\vswhere.exe'; },
);
final MockPlatform windowsPlatform = MockPlatform()
..environment['PROGRAMFILES(X86)'] = r'C:\Program Files (x86)\'; // A minimum version of a response where a VS installation was found.
MockProcessManager mockProcessManager; const Map<String, dynamic> _defaultResponse = <String, dynamic>{
final MemoryFileSystem memoryFilesystem = MemoryFileSystem(style: FileSystemStyle.windows);
// A minimum version of a response where a VS installation was found.
const Map<String, dynamic> _defaultResponse = <String, dynamic>{
'installationPath': visualStudioPath, 'installationPath': visualStudioPath,
'displayName': 'Visual Studio Community 2019', 'displayName': 'Visual Studio Community 2019',
'installationVersion': '16.2.29306.81', 'installationVersion': '16.2.29306.81',
...@@ -46,10 +40,10 @@ void main() { ...@@ -46,10 +40,10 @@ void main() {
'catalog': <String, dynamic>{ 'catalog': <String, dynamic>{
'productDisplayVersion': '16.2.5', 'productDisplayVersion': '16.2.5',
}, },
}; };
// A response for a VS installation that's too old. // A response for a VS installation that's too old.
const Map<String, dynamic> _tooOldResponse = <String, dynamic>{ const Map<String, dynamic> _tooOldResponse = <String, dynamic>{
'installationPath': visualStudioPath, 'installationPath': visualStudioPath,
'displayName': 'Visual Studio Community 2017', 'displayName': 'Visual Studio Community 2017',
'installationVersion': '15.9.28307.665', 'installationVersion': '15.9.28307.665',
...@@ -60,50 +54,47 @@ void main() { ...@@ -60,50 +54,47 @@ void main() {
'catalog': <String, dynamic>{ 'catalog': <String, dynamic>{
'productDisplayVersion': '15.9.12', 'productDisplayVersion': '15.9.12',
}, },
}; };
// A version of a response that doesn't include certain installation status // A version of a response that doesn't include certain installation status
// information that might be missing in older vswhere. // information that might be missing in older vswhere.
const Map<String, dynamic> _missingStatusResponse = <String, dynamic>{ const Map<String, dynamic> _missingStatusResponse = <String, dynamic>{
'installationPath': visualStudioPath, 'installationPath': visualStudioPath,
'displayName': 'Visual Studio Community 2017', 'displayName': 'Visual Studio Community 2017',
'installationVersion': '16.4.29609.76', 'installationVersion': '16.4.29609.76',
'catalog': <String, dynamic>{ 'catalog': <String, dynamic>{
'productDisplayVersion': '16.4.1', 'productDisplayVersion': '16.4.1',
}, },
}; };
// Arguments for a vswhere query to search for an installation with the required components. // Arguments for a vswhere query to search for an installation with the required components.
const List<String> _requiredComponents = <String>[ const List<String> _requiredComponents = <String>[
'Microsoft.Component.MSBuild', 'Microsoft.Component.MSBuild',
'Microsoft.VisualStudio.Component.VC.Tools.x86.x64', 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64',
'Microsoft.VisualStudio.Component.Windows10SDK.17763', 'Microsoft.VisualStudio.Component.Windows10SDK.17763',
]; ];
// Sets up the mock environment so that searching for Visual Studio with // Sets up the mock environment so that searching for Visual Studio with
// exactly the given required components will provide a result. By default it // exactly the given required components will provide a result. By default it
// return a preset installation, but the response can be overridden. // return a preset installation, but the response can be overridden.
void setMockVswhereResponse([ void setMockVswhereResponse(
FileSystem fileSystem,
FakeProcessManager processManager, [
List<String> requiredComponents, List<String> requiredComponents,
List<String> additionalArguments, List<String> additionalArguments,
Map<String, dynamic> response, Map<String, dynamic> response,
String responseOverride, String responseOverride,
]) { ]) {
globals.fs.file(vswherePath).createSync(recursive: true); fileSystem.file(vswherePath).createSync(recursive: true);
globals.fs.file(vcvarsPath).createSync(recursive: true); fileSystem.file(vcvarsPath).createSync(recursive: true);
final String finalResponse = responseOverride
final MockProcessResult result = MockProcessResult(); ?? json.encode(<Map<String, dynamic>>[response]);
when(result.exitCode).thenReturn(0);
final String finalResponse = responseOverride ??
json.encode(<Map<String, dynamic>>[response]);
when<String>(result.stdout as String).thenReturn(finalResponse);
when<String>(result.stderr as String).thenReturn('');
final List<String> requirementArguments = requiredComponents == null final List<String> requirementArguments = requiredComponents == null
? <String>[] ? <String>[]
: <String>['-requires', ...requiredComponents]; : <String>['-requires', ...requiredComponents];
when(mockProcessManager.runSync(
<String>[ processManager.addCommand(FakeCommand(
command: <String>[
vswherePath, vswherePath,
'-format', '-format',
'json', 'json',
...@@ -112,107 +103,165 @@ void main() { ...@@ -112,107 +103,165 @@ void main() {
...?additionalArguments, ...?additionalArguments,
...?requirementArguments, ...?requirementArguments,
], ],
workingDirectory: anyNamed('workingDirectory'), stdout: finalResponse,
environment: anyNamed('environment'), ));
)).thenAnswer((Invocation invocation) { }
return result;
});
}
// Sets whether or not a vswhere query with the required components will
// return an installation.
void setMockCompatibleVisualStudioInstallation(Map<String, dynamic>response) {
setMockVswhereResponse(_requiredComponents, <String>['-version', '16'], response);
}
// Sets whether or not a vswhere query with the required components will
// return a pre-release installation.
void setMockPrereleaseVisualStudioInstallation(Map<String, dynamic>response) {
setMockVswhereResponse(_requiredComponents, <String>['-version', '16', '-prerelease'], response);
}
// Sets whether or not a vswhere query searching for 'all' and 'prerelease'
// versions will return an installation.
void setMockAnyVisualStudioInstallation(Map<String, dynamic> response) {
setMockVswhereResponse(null, <String>['-prerelease', '-all'], response);
}
// Set a pre-encoded query result.
void setMockEncodedAnyVisualStudioInstallation(String response) {
setMockVswhereResponse(null, <String>['-prerelease', '-all'], null, response);
}
group('Visual Studio', () { // Sets whether or not a vswhere query with the required components will
VisualStudio visualStudio; // return an installation.
void setMockCompatibleVisualStudioInstallation(
Map<String, dynamic> response,
FileSystem fileSystem,
FakeProcessManager processManager,
) {
setMockVswhereResponse(
fileSystem,
processManager,
_requiredComponents,
<String>['-version', '16'],
response,
);
}
setUp(() { // Sets whether or not a vswhere query with the required components will
mockProcessManager = MockProcessManager(); // return a pre-release installation.
}); void setMockPrereleaseVisualStudioInstallation(
Map<String, dynamic> response,
FileSystem fileSystem,
FakeProcessManager processManager,
) {
setMockVswhereResponse(
fileSystem,
processManager,
_requiredComponents,
<String>['-version', '16', '-prerelease'],
response,
);
}
testUsingContext('isInstalled returns false when vswhere is missing', () { // Sets whether or not a vswhere query searching for 'all' and 'prerelease'
// versions will return an installation.
void setMockAnyVisualStudioInstallation(
Map<String, dynamic> response,
FileSystem fileSystem,
FakeProcessManager processManager,
) {
setMockVswhereResponse(
fileSystem,
processManager,
null,
<String>['-prerelease', '-all'],
response,
);
}
// Set a pre-encoded query result.
void setMockEncodedAnyVisualStudioInstallation(
String response,
FileSystem fileSystem,
FakeProcessManager processManager,
) {
setMockVswhereResponse(
fileSystem,
processManager,
null,
<String>['-prerelease', '-all'],
null,
response,
);
}
// Create a visual studio instance with a FakeProcessManager.
VisualStudioFixture setUpVisualStudio() {
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[]);
final FileSystem fileSystem = MemoryFileSystem.test(style: FileSystemStyle.windows);
final BufferLogger logger = BufferLogger.test();
final VisualStudio visualStudio = VisualStudio(
fileSystem: fileSystem,
platform: windowsPlatform,
logger: logger,
processManager: processManager,
);
return VisualStudioFixture(visualStudio, fileSystem, processManager);
}
void main() {
group('Visual Studio', () {
testWithoutContext('isInstalled returns false when vswhere is missing', () {
final MockProcessManager mockProcessManager = MockProcessManager();
when(mockProcessManager.runSync( when(mockProcessManager.runSync(
any, any,
workingDirectory: anyNamed('workingDirectory'), workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'), environment: anyNamed('environment'),
)).thenThrow(const ProcessException('vswhere', <String>[])); )).thenThrow(const ProcessException('vswhere', <String>[]));
final VisualStudio visualStudio = VisualStudio(
logger: BufferLogger.test(),
fileSystem: MemoryFileSystem.test(style: FileSystemStyle.windows),
platform: windowsPlatform,
processManager: mockProcessManager,
);
visualStudio = VisualStudio();
expect(visualStudio.isInstalled, false); expect(visualStudio.isInstalled, false);
}, overrides: <Type, Generator>{
FileSystem: () => memoryFilesystem,
ProcessManager: () => mockProcessManager,
Platform: () => windowsPlatform,
}); });
testUsingContext('vcvarsPath returns null when vswhere is missing', () { testWithoutContext('vcvarsPath returns null when vswhere is missing', () {
final MockProcessManager mockProcessManager = MockProcessManager();
when(mockProcessManager.runSync( when(mockProcessManager.runSync(
any, any,
workingDirectory: anyNamed('workingDirectory'), workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'), environment: anyNamed('environment'),
)).thenThrow(const ProcessException('vswhere', <String>[])); )).thenThrow(const ProcessException('vswhere', <String>[]));
final VisualStudio visualStudio = VisualStudio(
logger: BufferLogger.test(),
fileSystem: MemoryFileSystem.test(style: FileSystemStyle.windows),
platform: windowsPlatform,
processManager: mockProcessManager,
);
visualStudio = VisualStudio();
expect(visualStudio.vcvarsPath, isNull); expect(visualStudio.vcvarsPath, isNull);
}, overrides: <Type, Generator>{
FileSystem: () => memoryFilesystem,
ProcessManager: () => mockProcessManager,
Platform: () => windowsPlatform,
}); });
testUsingContext('isInstalled returns false when vswhere returns non-zero', () { testWithoutContext(
'isInstalled returns false when vswhere returns non-zero', () {
when(mockProcessManager.runSync( final MockProcessManager mockProcessManager = MockProcessManager();
any,
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).thenThrow(const ProcessException('vswhere', <String>[]));
final MockProcessResult result = MockProcessResult();
when(result.exitCode).thenReturn(1);
when(mockProcessManager.runSync( when(mockProcessManager.runSync(
any, any,
workingDirectory: anyNamed('workingDirectory'), workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'), environment: anyNamed('environment'),
)).thenAnswer((Invocation invocation) { )).thenAnswer((Invocation invocation) {
return result; return FakeProcessResult(exitCode: 1, stderr: '', stdout: '');
}); });
when<String>(result.stdout as String).thenReturn(''); final VisualStudio visualStudio = VisualStudio(
when<String>(result.stderr as String).thenReturn(''); logger: BufferLogger.test(),
fileSystem: MemoryFileSystem.test(style: FileSystemStyle.windows),
platform: windowsPlatform,
processManager: mockProcessManager,
);
visualStudio = VisualStudio();
expect(visualStudio.isInstalled, false); expect(visualStudio.isInstalled, false);
}, overrides: <Type, Generator>{ expect(visualStudio.isInstalled, false);
FileSystem: () => memoryFilesystem,
ProcessManager: () => mockProcessManager,
Platform: () => windowsPlatform,
}); });
testUsingContext('VisualStudio getters return the right values if no installation is found', () { testWithoutContext('VisualStudio getters return the right values if no installation is found', () {
setMockCompatibleVisualStudioInstallation(null); final VisualStudioFixture fixture = setUpVisualStudio();
setMockPrereleaseVisualStudioInstallation(null); final VisualStudio visualStudio = fixture.visualStudio;
setMockAnyVisualStudioInstallation(null);
setMockCompatibleVisualStudioInstallation(
null,
fixture.fileSystem,
fixture.processManager,
);
setMockPrereleaseVisualStudioInstallation(
null,
fixture.fileSystem,
fixture.processManager,
);
setMockAnyVisualStudioInstallation(
null,
fixture.fileSystem,
fixture.processManager,
);
visualStudio = VisualStudio();
expect(visualStudio.isInstalled, false); expect(visualStudio.isInstalled, false);
expect(visualStudio.isAtLeastMinimumVersion, false); expect(visualStudio.isAtLeastMinimumVersion, false);
expect(visualStudio.hasNecessaryComponents, false); expect(visualStudio.hasNecessaryComponents, false);
...@@ -223,260 +272,414 @@ void main() { ...@@ -223,260 +272,414 @@ void main() {
expect(visualStudio.displayVersion, null); expect(visualStudio.displayVersion, null);
expect(visualStudio.installLocation, null); expect(visualStudio.installLocation, null);
expect(visualStudio.fullVersion, null); expect(visualStudio.fullVersion, null);
}, overrides: <Type, Generator>{
FileSystem: () => memoryFilesystem,
ProcessManager: () => mockProcessManager,
Platform: () => windowsPlatform,
}); });
testUsingContext('necessaryComponentDescriptions suggest the right VS tools on major version 16', () { testWithoutContext('necessaryComponentDescriptions suggest the right VS tools on major version 16', () {
setMockCompatibleVisualStudioInstallation(_defaultResponse); final VisualStudioFixture fixture = setUpVisualStudio();
final VisualStudio visualStudio = fixture.visualStudio;
setMockCompatibleVisualStudioInstallation(
_defaultResponse,
fixture.fileSystem,
fixture.processManager,
);
visualStudio = VisualStudio();
final String toolsString = visualStudio.necessaryComponentDescriptions()[1]; final String toolsString = visualStudio.necessaryComponentDescriptions()[1];
expect(toolsString.contains('v142'), true); expect(toolsString.contains('v142'), true);
}, overrides: <Type, Generator>{
FileSystem: () => memoryFilesystem,
ProcessManager: () => mockProcessManager,
Platform: () => windowsPlatform,
}); });
testUsingContext('necessaryComponentDescriptions suggest the right VS tools on an old version', () { testWithoutContext('necessaryComponentDescriptions suggest the right VS tools on an old version', () {
setMockCompatibleVisualStudioInstallation(null); final VisualStudioFixture fixture = setUpVisualStudio();
setMockPrereleaseVisualStudioInstallation(null); final VisualStudio visualStudio = fixture.visualStudio;
setMockAnyVisualStudioInstallation(_tooOldResponse);
setMockCompatibleVisualStudioInstallation(
null,
fixture.fileSystem,
fixture.processManager,
);
setMockPrereleaseVisualStudioInstallation(
null,
fixture.fileSystem,
fixture.processManager,
);
setMockAnyVisualStudioInstallation(
_tooOldResponse,
fixture.fileSystem,
fixture.processManager,
);
visualStudio = VisualStudio();
final String toolsString = visualStudio.necessaryComponentDescriptions()[1]; final String toolsString = visualStudio.necessaryComponentDescriptions()[1];
expect(toolsString.contains('v142'), true); expect(toolsString.contains('v142'), true);
}, overrides: <Type, Generator>{
FileSystem: () => memoryFilesystem,
ProcessManager: () => mockProcessManager,
Platform: () => windowsPlatform,
}); });
testUsingContext('isInstalled returns true even with missing status information', () { testWithoutContext('isInstalled returns true even with missing status information', () {
setMockCompatibleVisualStudioInstallation(null); final VisualStudioFixture fixture = setUpVisualStudio();
setMockPrereleaseVisualStudioInstallation(null); final VisualStudio visualStudio = fixture.visualStudio;
setMockAnyVisualStudioInstallation(_missingStatusResponse);
setMockCompatibleVisualStudioInstallation(
null,
fixture.fileSystem,
fixture.processManager,
);
setMockPrereleaseVisualStudioInstallation(
null,
fixture.fileSystem,
fixture.processManager,
);
setMockAnyVisualStudioInstallation(
_missingStatusResponse,
fixture.fileSystem,
fixture.processManager,
);
visualStudio = VisualStudio();
expect(visualStudio.isInstalled, true); expect(visualStudio.isInstalled, true);
}, overrides: <Type, Generator>{
FileSystem: () => memoryFilesystem,
ProcessManager: () => mockProcessManager,
Platform: () => windowsPlatform,
}); });
testUsingContext('isInstalled returns true when VS is present but missing components', () { testWithoutContext('isInstalled returns true when VS is present but missing components', () {
setMockCompatibleVisualStudioInstallation(null); final VisualStudioFixture fixture = setUpVisualStudio();
setMockPrereleaseVisualStudioInstallation(null); final VisualStudio visualStudio = fixture.visualStudio;
setMockAnyVisualStudioInstallation(_defaultResponse);
setMockCompatibleVisualStudioInstallation(
null,
fixture.fileSystem,
fixture.processManager,
);
setMockPrereleaseVisualStudioInstallation(
null,
fixture.fileSystem,
fixture.processManager,
);
setMockAnyVisualStudioInstallation(
_defaultResponse,
fixture.fileSystem,
fixture.processManager,
);
visualStudio = VisualStudio();
expect(visualStudio.isInstalled, true); expect(visualStudio.isInstalled, true);
}, overrides: <Type, Generator>{
FileSystem: () => memoryFilesystem,
ProcessManager: () => mockProcessManager,
Platform: () => windowsPlatform,
}); });
testUsingContext('isInstalled returns true when VS is present but too old', () { testWithoutContext('isInstalled returns true when VS is present but too old', () {
setMockCompatibleVisualStudioInstallation(null); final VisualStudioFixture fixture = setUpVisualStudio();
setMockPrereleaseVisualStudioInstallation(null); final VisualStudio visualStudio = fixture.visualStudio;
setMockAnyVisualStudioInstallation(_tooOldResponse);
setMockCompatibleVisualStudioInstallation(
null,
fixture.fileSystem,
fixture.processManager,
);
setMockPrereleaseVisualStudioInstallation(
null,
fixture.fileSystem,
fixture.processManager,
);
setMockAnyVisualStudioInstallation(
_tooOldResponse,
fixture.fileSystem,
fixture.processManager,
);
visualStudio = VisualStudio();
expect(visualStudio.isInstalled, true); expect(visualStudio.isInstalled, true);
}, overrides: <Type, Generator>{
FileSystem: () => memoryFilesystem,
ProcessManager: () => mockProcessManager,
Platform: () => windowsPlatform,
}); });
testUsingContext('isInstalled returns true when a prerelease version of VS is present', () { testWithoutContext('isInstalled returns true when a prerelease version of VS is present', () {
setMockCompatibleVisualStudioInstallation(null); final VisualStudioFixture fixture = setUpVisualStudio();
setMockAnyVisualStudioInstallation(null); final VisualStudio visualStudio = fixture.visualStudio;
final Map<String, dynamic> response = Map<String, dynamic>.from(_defaultResponse) final Map<String, dynamic> response = Map<String, dynamic>.from(_defaultResponse)
..['isPrerelease'] = true; ..['isPrerelease'] = true;
setMockPrereleaseVisualStudioInstallation(response); setMockCompatibleVisualStudioInstallation(
null,
fixture.fileSystem,
fixture.processManager,
);
setMockPrereleaseVisualStudioInstallation(
response,
fixture.fileSystem,
fixture.processManager,
);
setMockAnyVisualStudioInstallation(
null,
fixture.fileSystem,
fixture.processManager,
);
visualStudio = VisualStudio();
expect(visualStudio.isInstalled, true); expect(visualStudio.isInstalled, true);
expect(visualStudio.isPrerelease, true); expect(visualStudio.isPrerelease, true);
}, overrides: <Type, Generator>{
FileSystem: () => memoryFilesystem,
ProcessManager: () => mockProcessManager,
Platform: () => windowsPlatform,
}); });
testUsingContext('isAtLeastMinimumVersion returns false when the version found is too old', () { testWithoutContext('isAtLeastMinimumVersion returns false when the version found is too old', () {
setMockCompatibleVisualStudioInstallation(null); final VisualStudioFixture fixture = setUpVisualStudio();
setMockPrereleaseVisualStudioInstallation(null); final VisualStudio visualStudio = fixture.visualStudio;
setMockAnyVisualStudioInstallation(_tooOldResponse);
setMockCompatibleVisualStudioInstallation(
null,
fixture.fileSystem,
fixture.processManager,
);
setMockPrereleaseVisualStudioInstallation(
null,
fixture.fileSystem,
fixture.processManager,
);
setMockAnyVisualStudioInstallation(
_tooOldResponse,
fixture.fileSystem,
fixture.processManager,
);
visualStudio = VisualStudio();
expect(visualStudio.isInstalled, true); expect(visualStudio.isInstalled, true);
expect(visualStudio.isAtLeastMinimumVersion, false); expect(visualStudio.isAtLeastMinimumVersion, false);
}, overrides: <Type, Generator>{
FileSystem: () => memoryFilesystem,
ProcessManager: () => mockProcessManager,
Platform: () => windowsPlatform,
}); });
testUsingContext('isComplete returns false when an incomplete installation is found', () { testWithoutContext('isComplete returns false when an incomplete installation is found', () {
setMockCompatibleVisualStudioInstallation(null); final VisualStudioFixture fixture = setUpVisualStudio();
setMockPrereleaseVisualStudioInstallation(null); final VisualStudio visualStudio = fixture.visualStudio;
setMockCompatibleVisualStudioInstallation(
null,
fixture.fileSystem,
fixture.processManager,
);
setMockPrereleaseVisualStudioInstallation(
null,
fixture.fileSystem,
fixture.processManager,
);
final Map<String, dynamic> response = Map<String, dynamic>.from(_defaultResponse) final Map<String, dynamic> response = Map<String, dynamic>.from(_defaultResponse)
..['isComplete'] = false; ..['isComplete'] = false;
setMockAnyVisualStudioInstallation(response); setMockAnyVisualStudioInstallation(
response,
fixture.fileSystem,
fixture.processManager,
);
visualStudio = VisualStudio();
expect(visualStudio.isInstalled, true); expect(visualStudio.isInstalled, true);
expect(visualStudio.isComplete, false); expect(visualStudio.isComplete, false);
}, overrides: <Type, Generator>{
FileSystem: () => memoryFilesystem,
ProcessManager: () => mockProcessManager,
Platform: () => windowsPlatform,
}); });
testUsingContext("isLaunchable returns false if the installation can't be launched", () { testWithoutContext(
setMockCompatibleVisualStudioInstallation(null); "isLaunchable returns false if the installation can't be launched", () {
setMockPrereleaseVisualStudioInstallation(null); final VisualStudioFixture fixture = setUpVisualStudio();
final VisualStudio visualStudio = fixture.visualStudio;
setMockCompatibleVisualStudioInstallation(
null,
fixture.fileSystem,
fixture.processManager,
);
setMockPrereleaseVisualStudioInstallation(
null,
fixture.fileSystem,
fixture.processManager,
);
final Map<String, dynamic> response = Map<String, dynamic>.from(_defaultResponse) final Map<String, dynamic> response = Map<String, dynamic>.from(_defaultResponse)
..['isLaunchable'] = false; ..['isLaunchable'] = false;
setMockAnyVisualStudioInstallation(response); setMockAnyVisualStudioInstallation(
response,
fixture.fileSystem,
fixture.processManager,
);
visualStudio = VisualStudio();
expect(visualStudio.isInstalled, true); expect(visualStudio.isInstalled, true);
expect(visualStudio.isLaunchable, false); expect(visualStudio.isLaunchable, false);
}, overrides: <Type, Generator>{
FileSystem: () => memoryFilesystem,
ProcessManager: () => mockProcessManager,
Platform: () => windowsPlatform,
}); });
testUsingContext('isRebootRequired returns true if the installation needs a reboot', () { testWithoutContext('isRebootRequired returns true if the installation needs a reboot', () {
setMockCompatibleVisualStudioInstallation(null); final VisualStudioFixture fixture = setUpVisualStudio();
setMockPrereleaseVisualStudioInstallation(null); final VisualStudio visualStudio = fixture.visualStudio;
setMockCompatibleVisualStudioInstallation(
null,
fixture.fileSystem,
fixture.processManager,
);
setMockPrereleaseVisualStudioInstallation(
null,
fixture.fileSystem,
fixture.processManager,
);
final Map<String, dynamic> response = Map<String, dynamic>.from(_defaultResponse) final Map<String, dynamic> response = Map<String, dynamic>.from(_defaultResponse)
..['isRebootRequired'] = true; ..['isRebootRequired'] = true;
setMockAnyVisualStudioInstallation(response); setMockAnyVisualStudioInstallation(
response,
fixture.fileSystem,
fixture.processManager,
);
visualStudio = VisualStudio();
expect(visualStudio.isInstalled, true); expect(visualStudio.isInstalled, true);
expect(visualStudio.isRebootRequired, true); expect(visualStudio.isRebootRequired, true);
}, overrides: <Type, Generator>{
FileSystem: () => memoryFilesystem,
ProcessManager: () => mockProcessManager,
Platform: () => windowsPlatform,
}); });
testWithoutContext('hasNecessaryComponents returns false when VS is present but missing components', () {
final VisualStudioFixture fixture = setUpVisualStudio();
final VisualStudio visualStudio = fixture.visualStudio;
setMockCompatibleVisualStudioInstallation(
null,
fixture.fileSystem,
fixture.processManager,
);
setMockPrereleaseVisualStudioInstallation(
null,
fixture.fileSystem,
fixture.processManager,
);
setMockAnyVisualStudioInstallation(
_defaultResponse,
fixture.fileSystem,
fixture.processManager,
);
testUsingContext('hasNecessaryComponents returns false when VS is present but missing components', () {
setMockCompatibleVisualStudioInstallation(null);
setMockPrereleaseVisualStudioInstallation(null);
setMockAnyVisualStudioInstallation(_defaultResponse);
visualStudio = VisualStudio();
expect(visualStudio.hasNecessaryComponents, false); expect(visualStudio.hasNecessaryComponents, false);
}, overrides: <Type, Generator>{
FileSystem: () => memoryFilesystem,
ProcessManager: () => mockProcessManager,
Platform: () => windowsPlatform,
}); });
testUsingContext('vcvarsPath returns null when VS is present but missing components', () { testWithoutContext('vcvarsPath returns null when VS is present but missing components', () {
setMockCompatibleVisualStudioInstallation(null); final VisualStudioFixture fixture = setUpVisualStudio();
setMockPrereleaseVisualStudioInstallation(null); final VisualStudio visualStudio = fixture.visualStudio;
setMockAnyVisualStudioInstallation(_defaultResponse);
setMockCompatibleVisualStudioInstallation(
null,
fixture.fileSystem,
fixture.processManager,
);
setMockPrereleaseVisualStudioInstallation(
null,
fixture.fileSystem,
fixture.processManager,
);
setMockAnyVisualStudioInstallation(
_defaultResponse,
fixture.fileSystem,
fixture.processManager,
);
visualStudio = VisualStudio();
expect(visualStudio.vcvarsPath, isNull); expect(visualStudio.vcvarsPath, isNull);
}, overrides: <Type, Generator>{
FileSystem: () => memoryFilesystem,
ProcessManager: () => mockProcessManager,
Platform: () => windowsPlatform,
}); });
testUsingContext('vcvarsPath returns null when VS is present but with require components but installation is faulty', () { testWithoutContext('vcvarsPath returns null when VS is present but with require components but installation is faulty', () {
final VisualStudioFixture fixture = setUpVisualStudio();
final VisualStudio visualStudio = fixture.visualStudio;
final Map<String, dynamic> response = Map<String, dynamic>.from(_defaultResponse) final Map<String, dynamic> response = Map<String, dynamic>.from(_defaultResponse)
..['isRebootRequired'] = true; ..['isRebootRequired'] = true;
setMockCompatibleVisualStudioInstallation(response); setMockCompatibleVisualStudioInstallation(
setMockPrereleaseVisualStudioInstallation(null); response,
fixture.fileSystem,
fixture.processManager,
);
setMockPrereleaseVisualStudioInstallation(
null,
fixture.fileSystem,
fixture.processManager,
);
visualStudio = VisualStudio();
expect(visualStudio.vcvarsPath, isNull); expect(visualStudio.vcvarsPath, isNull);
}, overrides: <Type, Generator>{
FileSystem: () => memoryFilesystem,
ProcessManager: () => mockProcessManager,
Platform: () => windowsPlatform,
}); });
testUsingContext('hasNecessaryComponents returns false when VS is present with required components but installation is faulty', () { testWithoutContext('hasNecessaryComponents returns false when VS is present with required components but installation is faulty', () {
final VisualStudioFixture fixture = setUpVisualStudio();
final VisualStudio visualStudio = fixture.visualStudio;
final Map<String, dynamic> response = Map<String, dynamic>.from(_defaultResponse) final Map<String, dynamic> response = Map<String, dynamic>.from(_defaultResponse)
..['isRebootRequired'] = true; ..['isRebootRequired'] = true;
setMockCompatibleVisualStudioInstallation(response); setMockCompatibleVisualStudioInstallation(
setMockPrereleaseVisualStudioInstallation(null); response,
fixture.fileSystem,
fixture.processManager,
);
setMockPrereleaseVisualStudioInstallation(
null,
fixture.fileSystem,
fixture.processManager,
);
visualStudio = VisualStudio();
expect(visualStudio.hasNecessaryComponents, false); expect(visualStudio.hasNecessaryComponents, false);
}, overrides: <Type, Generator>{
FileSystem: () => memoryFilesystem,
ProcessManager: () => mockProcessManager,
Platform: () => windowsPlatform,
}); });
testUsingContext('VS metadata is available when VS is present, even if missing components', () { testWithoutContext('VS metadata is available when VS is present, even if missing components', () {
setMockCompatibleVisualStudioInstallation(null); final VisualStudioFixture fixture = setUpVisualStudio();
setMockPrereleaseVisualStudioInstallation(null); final VisualStudio visualStudio = fixture.visualStudio;
setMockAnyVisualStudioInstallation(_defaultResponse);
setMockCompatibleVisualStudioInstallation(
null,
fixture.fileSystem,
fixture.processManager,
);
setMockPrereleaseVisualStudioInstallation(
null,
fixture.fileSystem,
fixture.processManager,
);
setMockAnyVisualStudioInstallation(
_defaultResponse,
fixture.fileSystem,
fixture.processManager,
);
visualStudio = VisualStudio();
expect(visualStudio.displayName, equals('Visual Studio Community 2019')); expect(visualStudio.displayName, equals('Visual Studio Community 2019'));
expect(visualStudio.displayVersion, equals('16.2.5')); expect(visualStudio.displayVersion, equals('16.2.5'));
expect(visualStudio.installLocation, equals(visualStudioPath)); expect(visualStudio.installLocation, equals(visualStudioPath));
expect(visualStudio.fullVersion, equals('16.2.29306.81')); expect(visualStudio.fullVersion, equals('16.2.29306.81'));
}, overrides: <Type, Generator>{
FileSystem: () => memoryFilesystem,
ProcessManager: () => mockProcessManager,
Platform: () => windowsPlatform,
}); });
testUsingContext('vcvarsPath returns null when VS is present but when vswhere returns invalid JSON', () { testWithoutContext('vcvarsPath returns null when VS is present but when vswhere returns invalid JSON', () {
setMockCompatibleVisualStudioInstallation(null); final VisualStudioFixture fixture = setUpVisualStudio();
setMockPrereleaseVisualStudioInstallation(null); final VisualStudio visualStudio = fixture.visualStudio;
setMockEncodedAnyVisualStudioInstallation('{');
setMockCompatibleVisualStudioInstallation(
null,
fixture.fileSystem,
fixture.processManager,
);
setMockPrereleaseVisualStudioInstallation(
null,
fixture.fileSystem,
fixture.processManager,
);
setMockEncodedAnyVisualStudioInstallation(
'{',
fixture.fileSystem,
fixture.processManager,
);
visualStudio = VisualStudio();
expect(visualStudio.vcvarsPath, isNull); expect(visualStudio.vcvarsPath, isNull);
}, overrides: <Type, Generator>{
FileSystem: () => memoryFilesystem,
ProcessManager: () => mockProcessManager,
Platform: () => windowsPlatform,
}); });
testUsingContext('Everything returns good values when VS is present with all components', () { testWithoutContext('Everything returns good values when VS is present with all components', () {
setMockCompatibleVisualStudioInstallation(_defaultResponse); final VisualStudioFixture fixture = setUpVisualStudio();
setMockPrereleaseVisualStudioInstallation(null); final VisualStudio visualStudio = fixture.visualStudio;
setMockAnyVisualStudioInstallation(null);
setMockCompatibleVisualStudioInstallation(
_defaultResponse,
fixture.fileSystem,
fixture.processManager,
);
setMockPrereleaseVisualStudioInstallation(
null,
fixture.fileSystem,
fixture.processManager,
);
setMockAnyVisualStudioInstallation(
null,
fixture.fileSystem,
fixture.processManager,
);
visualStudio = VisualStudio();
expect(visualStudio.isInstalled, true); expect(visualStudio.isInstalled, true);
expect(visualStudio.isAtLeastMinimumVersion, true); expect(visualStudio.isAtLeastMinimumVersion, true);
expect(visualStudio.hasNecessaryComponents, true); expect(visualStudio.hasNecessaryComponents, true);
expect(visualStudio.vcvarsPath, equals(vcvarsPath)); expect(visualStudio.vcvarsPath, equals(vcvarsPath));
}, overrides: <Type, Generator>{
FileSystem: () => memoryFilesystem,
ProcessManager: () => mockProcessManager,
Platform: () => windowsPlatform,
}); });
testUsingContext('Metadata is for compatible version when latest is missing components', () { testWithoutContext('Metadata is for compatible version when latest is missing components', () {
final VisualStudioFixture fixture = setUpVisualStudio();
final VisualStudio visualStudio = fixture.visualStudio;
// Return a different version for queries without the required packages. // Return a different version for queries without the required packages.
final Map<String, dynamic> olderButCompleteVersionResponse = <String, dynamic>{ final Map<String, dynamic> olderButCompleteVersionResponse = <String, dynamic>{
'installationPath': visualStudioPath, 'installationPath': visualStudioPath,
...@@ -487,8 +690,16 @@ void main() { ...@@ -487,8 +690,16 @@ void main() {
}, },
}; };
setMockCompatibleVisualStudioInstallation(olderButCompleteVersionResponse); setMockCompatibleVisualStudioInstallation(
setMockPrereleaseVisualStudioInstallation(null); olderButCompleteVersionResponse,
fixture.fileSystem,
fixture.processManager,
);
setMockPrereleaseVisualStudioInstallation(
null,
fixture.fileSystem,
fixture.processManager,
);
// Return a different version for queries without the required packages. // Return a different version for queries without the required packages.
final Map<String, dynamic> incompleteVersionResponse = <String, dynamic>{ final Map<String, dynamic> incompleteVersionResponse = <String, dynamic>{
'installationPath': visualStudioPath, 'installationPath': visualStudioPath,
...@@ -498,15 +709,24 @@ void main() { ...@@ -498,15 +709,24 @@ void main() {
'productDisplayVersion': '16.1', 'productDisplayVersion': '16.1',
}, },
}; };
setMockAnyVisualStudioInstallation(incompleteVersionResponse); setMockAnyVisualStudioInstallation(
incompleteVersionResponse,
fixture.fileSystem,
fixture.processManager,
);
visualStudio = VisualStudio();
expect(visualStudio.displayName, equals('Visual Studio Community 2017')); expect(visualStudio.displayName, equals('Visual Studio Community 2017'));
expect(visualStudio.displayVersion, equals('15.9.12')); expect(visualStudio.displayVersion, equals('15.9.12'));
}, overrides: <Type, Generator>{
FileSystem: () => memoryFilesystem,
ProcessManager: () => mockProcessManager,
Platform: () => windowsPlatform,
}); });
}); });
} }
class VisualStudioFixture {
VisualStudioFixture(this.visualStudio, this.fileSystem, this.processManager);
final VisualStudio visualStudio;
final FileSystem fileSystem;
final FakeProcessManager processManager;
}
class MockProcessManager extends Mock implements ProcessManager {}
...@@ -2,16 +2,15 @@ ...@@ -2,16 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter_tools/src/base/user_messages.dart'; import 'package:flutter_tools/src/base/user_messages.dart' hide userMessages;
import 'package:flutter_tools/src/doctor.dart'; import 'package:flutter_tools/src/doctor.dart';
import 'package:flutter_tools/src/windows/visual_studio.dart'; import 'package:flutter_tools/src/windows/visual_studio.dart';
import 'package:flutter_tools/src/windows/visual_studio_validator.dart'; import 'package:flutter_tools/src/windows/visual_studio_validator.dart';
import 'package:mockito/mockito.dart'; import 'package:mockito/mockito.dart';
import '../../src/common.dart'; import '../../src/common.dart';
import '../../src/context.dart';
class MockVisualStudio extends Mock implements VisualStudio {} final UserMessages userMessages = UserMessages();
void main() { void main() {
group('Visual Studio validation', () { group('Visual Studio validation', () {
...@@ -62,111 +61,131 @@ void main() { ...@@ -62,111 +61,131 @@ void main() {
when(mockVisualStudio.hasNecessaryComponents).thenReturn(false); when(mockVisualStudio.hasNecessaryComponents).thenReturn(false);
} }
testUsingContext('Emits a message when Visual Studio is a pre-release version', () async { testWithoutContext('Emits a message when Visual Studio is a pre-release version', () async {
final VisualStudioValidator validator = VisualStudioValidator(
userMessages: userMessages,
visualStudio: mockVisualStudio,
);
_configureMockVisualStudioAsInstalled(); _configureMockVisualStudioAsInstalled();
when(visualStudio.isPrerelease).thenReturn(true); when(mockVisualStudio.isPrerelease).thenReturn(true);
const VisualStudioValidator validator = VisualStudioValidator();
final ValidationResult result = await validator.validate(); final ValidationResult result = await validator.validate();
final ValidationMessage expectedMessage = ValidationMessage(userMessages.visualStudioIsPrerelease); final ValidationMessage expectedMessage = ValidationMessage(userMessages.visualStudioIsPrerelease);
expect(result.messages.contains(expectedMessage), true); expect(result.messages.contains(expectedMessage), true);
}, overrides: <Type, Generator>{
VisualStudio: () => mockVisualStudio,
}); });
testUsingContext('Emits a partial status when Visual Studio installation is incomplete', () async { testWithoutContext('Emits a partial status when Visual Studio installation is incomplete', () async {
final VisualStudioValidator validator = VisualStudioValidator(
userMessages: userMessages,
visualStudio: mockVisualStudio,
);
_configureMockVisualStudioAsInstalled(); _configureMockVisualStudioAsInstalled();
when(visualStudio.isComplete).thenReturn(false); when(mockVisualStudio.isComplete).thenReturn(false);
const VisualStudioValidator validator = VisualStudioValidator();
final ValidationResult result = await validator.validate(); final ValidationResult result = await validator.validate();
final ValidationMessage expectedMessage = ValidationMessage.error(userMessages.visualStudioIsIncomplete); final ValidationMessage expectedMessage = ValidationMessage.error(userMessages.visualStudioIsIncomplete);
expect(result.messages.contains(expectedMessage), true); expect(result.messages.contains(expectedMessage), true);
expect(result.type, ValidationType.partial); expect(result.type, ValidationType.partial);
}, overrides: <Type, Generator>{
VisualStudio: () => mockVisualStudio,
}); });
testUsingContext('Emits a partial status when Visual Studio installation needs rebooting', () async { testWithoutContext('Emits a partial status when Visual Studio installation needs rebooting', () async {
final VisualStudioValidator validator = VisualStudioValidator(
userMessages: userMessages,
visualStudio: mockVisualStudio,
);
_configureMockVisualStudioAsInstalled(); _configureMockVisualStudioAsInstalled();
when(visualStudio.isRebootRequired).thenReturn(true); when(mockVisualStudio.isRebootRequired).thenReturn(true);
const VisualStudioValidator validator = VisualStudioValidator();
final ValidationResult result = await validator.validate(); final ValidationResult result = await validator.validate();
final ValidationMessage expectedMessage = ValidationMessage.error(userMessages.visualStudioRebootRequired); final ValidationMessage expectedMessage = ValidationMessage.error(userMessages.visualStudioRebootRequired);
expect(result.messages.contains(expectedMessage), true); expect(result.messages.contains(expectedMessage), true);
expect(result.type, ValidationType.partial); expect(result.type, ValidationType.partial);
}, overrides: <Type, Generator>{
VisualStudio: () => mockVisualStudio,
}); });
testUsingContext('Emits a partial status when Visual Studio installation is not launchable', () async { testWithoutContext('Emits a partial status when Visual Studio installation is not launchable', () async {
final VisualStudioValidator validator = VisualStudioValidator(
userMessages: userMessages,
visualStudio: mockVisualStudio,
);
_configureMockVisualStudioAsInstalled(); _configureMockVisualStudioAsInstalled();
when(visualStudio.isLaunchable).thenReturn(false); when(mockVisualStudio.isLaunchable).thenReturn(false);
const VisualStudioValidator validator = VisualStudioValidator();
final ValidationResult result = await validator.validate(); final ValidationResult result = await validator.validate();
final ValidationMessage expectedMessage = ValidationMessage.error(userMessages.visualStudioNotLaunchable); final ValidationMessage expectedMessage = ValidationMessage.error(userMessages.visualStudioNotLaunchable);
expect(result.messages.contains(expectedMessage), true); expect(result.messages.contains(expectedMessage), true);
expect(result.type, ValidationType.partial); expect(result.type, ValidationType.partial);
}, overrides: <Type, Generator>{
VisualStudio: () => mockVisualStudio,
}); });
testUsingContext('Emits partial status when Visual Studio is installed but too old', () async { testWithoutContext('Emits partial status when Visual Studio is installed but too old', () async {
final VisualStudioValidator validator = VisualStudioValidator(
userMessages: userMessages,
visualStudio: mockVisualStudio,
);
_configureMockVisualStudioAsTooOld(); _configureMockVisualStudioAsTooOld();
const VisualStudioValidator validator = VisualStudioValidator();
final ValidationResult result = await validator.validate(); final ValidationResult result = await validator.validate();
final ValidationMessage expectedMessage = ValidationMessage.error( final ValidationMessage expectedMessage = ValidationMessage.error(
userMessages.visualStudioTooOld( userMessages.visualStudioTooOld(
visualStudio.minimumVersionDescription, mockVisualStudio.minimumVersionDescription,
visualStudio.workloadDescription, mockVisualStudio.workloadDescription,
visualStudio.necessaryComponentDescriptions(), mockVisualStudio.necessaryComponentDescriptions(),
), ),
); );
expect(result.messages.contains(expectedMessage), true); expect(result.messages.contains(expectedMessage), true);
expect(result.type, ValidationType.partial); expect(result.type, ValidationType.partial);
}, overrides: <Type, Generator>{
VisualStudio: () => mockVisualStudio,
}); });
testWithoutContext('Emits partial status when Visual Studio is installed without necessary components', () async {
testUsingContext('Emits partial status when Visual Studio is installed without necessary components', () async { final VisualStudioValidator validator = VisualStudioValidator(
userMessages: userMessages,
visualStudio: mockVisualStudio,
);
_configureMockVisualStudioAsInstalled(); _configureMockVisualStudioAsInstalled();
when(visualStudio.hasNecessaryComponents).thenReturn(false); when(mockVisualStudio.hasNecessaryComponents).thenReturn(false);
const VisualStudioValidator validator = VisualStudioValidator();
final ValidationResult result = await validator.validate(); final ValidationResult result = await validator.validate();
expect(result.type, ValidationType.partial); expect(result.type, ValidationType.partial);
}, overrides: <Type, Generator>{
VisualStudio: () => mockVisualStudio,
}); });
testUsingContext('Emits installed status when Visual Studio is installed with necessary components', () async { testWithoutContext('Emits installed status when Visual Studio is installed with necessary components', () async {
final VisualStudioValidator validator = VisualStudioValidator(
userMessages: userMessages,
visualStudio: mockVisualStudio,
);
_configureMockVisualStudioAsInstalled(); _configureMockVisualStudioAsInstalled();
const VisualStudioValidator validator = VisualStudioValidator();
final ValidationResult result = await validator.validate(); final ValidationResult result = await validator.validate();
final ValidationMessage expectedDisplayNameMessage = ValidationMessage( final ValidationMessage expectedDisplayNameMessage = ValidationMessage(
userMessages.visualStudioVersion(visualStudio.displayName, visualStudio.fullVersion)); userMessages.visualStudioVersion(mockVisualStudio.displayName, mockVisualStudio.fullVersion));
expect(result.messages.contains(expectedDisplayNameMessage), true); expect(result.messages.contains(expectedDisplayNameMessage), true);
expect(result.type, ValidationType.installed); expect(result.type, ValidationType.installed);
}, overrides: <Type, Generator>{
VisualStudio: () => mockVisualStudio,
}); });
testUsingContext('Emits missing status when Visual Studio is not installed', () async { testWithoutContext('Emits missing status when Visual Studio is not installed', () async {
final VisualStudioValidator validator = VisualStudioValidator(
userMessages: userMessages,
visualStudio: mockVisualStudio,
);
_configureMockVisualStudioAsNotInstalled(); _configureMockVisualStudioAsNotInstalled();
const VisualStudioValidator validator = VisualStudioValidator();
final ValidationResult result = await validator.validate(); final ValidationResult result = await validator.validate();
final ValidationMessage expectedMessage = ValidationMessage.error( final ValidationMessage expectedMessage = ValidationMessage.error(
userMessages.visualStudioMissing( userMessages.visualStudioMissing(
visualStudio.workloadDescription, mockVisualStudio.workloadDescription,
visualStudio.necessaryComponentDescriptions(), mockVisualStudio.necessaryComponentDescriptions(),
), ),
); );
expect(result.messages.contains(expectedMessage), true); expect(result.messages.contains(expectedMessage), true);
expect(result.type, ValidationType.missing); expect(result.type, ValidationType.missing);
}, overrides: <Type, Generator>{
VisualStudio: () => mockVisualStudio,
}); });
}); });
} }
class MockVisualStudio extends Mock implements VisualStudio {}
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