Unverified Commit 362cde43 authored by Francisco Magdaleno's avatar Francisco Magdaleno Committed by GitHub

[windows] Searches for pre-release and 'all' Visual Studio installations (#40011)

parent f7d86a5b
...@@ -193,6 +193,12 @@ class UserMessages { ...@@ -193,6 +193,12 @@ class UserMessages {
String get visualStudioMissing => String get visualStudioMissing =>
'Visual Studio not installed; this is necessary for Windows development.\n' 'Visual Studio not installed; this is necessary for Windows development.\n'
'Download at https://visualstudio.microsoft.com/downloads/.'; 'Download at https://visualstudio.microsoft.com/downloads/.';
String get visualStudioIsPrerelease => 'The current Visual Studio installation is a pre-release version. It may not be '
'supported by Flutter yet.';
String get visualStudioNotLaunchable =>
'The current Visual Studio installation is not launchable. Please reinstall Visual Studio.';
String get visualStudioIsIncomplete => 'The current Visual Studio installation is incomplete. Please reinstall Visual Studio.';
String get visualStudioRebootRequired => 'Visual Studio requires a reboot of your system to complete installation.';
// Messages used in FlutterCommand // Messages used in FlutterCommand
String flutterElapsedTime(String name, String elapsedTime) => '"flutter $name" took $elapsedTime.'; String flutterElapsedTime(String name, String elapsedTime) => '"flutter $name" took $elapsedTime.';
......
...@@ -35,6 +35,10 @@ class VisualStudio { ...@@ -35,6 +35,10 @@ class VisualStudio {
String get displayVersion => String get displayVersion =>
_bestVisualStudioDetails[_catalogKey][_catalogDisplayVersionKey]; _bestVisualStudioDetails[_catalogKey][_catalogDisplayVersionKey];
/// True if the Visual Studio installation is as pre-release version.
bool get isPrerelease =>
_bestVisualStudioDetails[_catalogKey][_isPrereleaseKey];
/// The directory where Visual Studio is installed. /// The directory where Visual Studio is installed.
String get installLocation => _bestVisualStudioDetails[_installationPathKey]; String get installLocation => _bestVisualStudioDetails[_installationPathKey];
...@@ -43,6 +47,15 @@ class VisualStudio { ...@@ -43,6 +47,15 @@ class VisualStudio {
/// For instance: "15.4.27004.2002". /// For instance: "15.4.27004.2002".
String get fullVersion => _bestVisualStudioDetails[_fullVersionKey]; String get fullVersion => _bestVisualStudioDetails[_fullVersionKey];
/// True there is complete installation of Visual Studio.
bool get isComplete => _bestVisualStudioDetails[_isCompleteKey];
/// True if Visual Studio is launchable.
bool get isLaunchable => _bestVisualStudioDetails[_isLaunchableKey];
/// True if a reboot is required to complete the Visual Studio installation.
bool get isRebootRequired => _bestVisualStudioDetails[_isRebootRequiredKey];
/// The name of the recommended Visual Studio installer workload. /// The name of the recommended Visual Studio installer workload.
String get workloadDescription => 'Desktop development with C++'; String get workloadDescription => 'Desktop development with C++';
...@@ -120,6 +133,11 @@ class VisualStudio { ...@@ -120,6 +133,11 @@ class VisualStudio {
/// The complete version. /// The complete version.
static const String _fullVersionKey = 'installationVersion'; static const String _fullVersionKey = 'installationVersion';
/// Keys for the status of the installation.
static const String _isCompleteKey = 'isComplete';
static const String _isLaunchableKey = 'isLaunchable';
static const String _isRebootRequiredKey = 'isRebootRequired';
/// The 'catalog' entry containing more details. /// The 'catalog' entry containing more details.
static const String _catalogKey = 'catalog'; static const String _catalogKey = 'catalog';
...@@ -128,23 +146,36 @@ class VisualStudio { ...@@ -128,23 +146,36 @@ class VisualStudio {
/// This key is under the 'catalog' entry. /// This key is under the 'catalog' entry.
static const String _catalogDisplayVersionKey = 'productDisplayVersion'; static const String _catalogDisplayVersionKey = 'productDisplayVersion';
/// The key for a pre-release version.
///
/// This key is under the 'catalog' entry.
static const String _isPrereleaseKey = 'productMilestoneIsPreRelease';
/// vswhere argument keys
static const String _prereleaseKey = '-prerelease';
/// Returns the details dictionary for the newest version of Visual Studio /// Returns the details dictionary for the newest version of Visual Studio
/// that includes all of [requiredComponents], if there is one. /// that includes all of [requiredComponents], if there is one.
Map<String, dynamic> _visualStudioDetails({Iterable<String> requiredComponents}) { Map<String, dynamic> _visualStudioDetails(
{Iterable<String> requiredComponents, List<String> additionalArguments}) {
final List<String> requirementArguments = requiredComponents == null final List<String> requirementArguments = requiredComponents == null
? <String>[] ? <String>[]
: <String>['-requires', ...requiredComponents]; : <String>['-requires', ...requiredComponents];
try { try {
final ProcessResult whereResult = processManager.runSync(<String>[ final List<String> defaultArguments = <String>[
_vswherePath,
'-format', 'json', '-format', 'json',
'-utf8', '-utf8',
'-latest', '-latest',
];
final ProcessResult whereResult = processManager.runSync(<String>[
_vswherePath,
...defaultArguments,
...?additionalArguments,
...?requirementArguments, ...?requirementArguments,
]); ]);
if (whereResult.exitCode == 0) { if (whereResult.exitCode == 0) {
final List<Map<String, dynamic>> installations = json.decode(whereResult.stdout) final List<Map<String, dynamic>> installations =
.cast<Map<String, dynamic>>(); json.decode(whereResult.stdout).cast<Map<String, dynamic>>();
if (installations.isNotEmpty) { if (installations.isNotEmpty) {
return installations[0]; return installations[0];
} }
...@@ -157,12 +188,34 @@ class VisualStudio { ...@@ -157,12 +188,34 @@ class VisualStudio {
return null; return null;
} }
/// Checks if the given installation has issues that the user must resolve.
bool installationHasIssues(Map<String, dynamic>installationDetails) {
assert(installationDetails != null);
assert(installationDetails[_isCompleteKey] != null);
assert(installationDetails[_isRebootRequiredKey] != null);
assert(installationDetails[_isLaunchableKey] != null);
return installationDetails[_isCompleteKey] == false ||
installationDetails[_isRebootRequiredKey] == true ||
installationDetails[_isLaunchableKey] == false;
}
/// Returns the details dictionary for the latest version of Visual Studio /// Returns the details dictionary for the latest version of Visual Studio
/// that has all required components. /// that has all required components.
Map<String, dynamic> _cachedUsableVisualStudioDetails; Map<String, dynamic> _cachedUsableVisualStudioDetails;
Map<String, dynamic> get _usableVisualStudioDetails { Map<String, dynamic> get _usableVisualStudioDetails {
_cachedUsableVisualStudioDetails ??= _cachedUsableVisualStudioDetails ??=
_visualStudioDetails(requiredComponents: _requiredComponents().keys); _visualStudioDetails(requiredComponents: _requiredComponents().keys);
// If a stable version is not found, try searching for a pre-release version.
_cachedUsableVisualStudioDetails ??= _visualStudioDetails(
requiredComponents: _requiredComponents().keys,
additionalArguments: <String>[_prereleaseKey]);
if (_cachedUsableVisualStudioDetails != null) {
if (installationHasIssues(_cachedUsableVisualStudioDetails)) {
_cachedAnyVisualStudioDetails = _cachedUsableVisualStudioDetails;
return null;
}
}
return _cachedUsableVisualStudioDetails; return _cachedUsableVisualStudioDetails;
} }
...@@ -170,7 +223,9 @@ class VisualStudio { ...@@ -170,7 +223,9 @@ class VisualStudio {
/// regardless of components. /// regardless of components.
Map<String, dynamic> _cachedAnyVisualStudioDetails; Map<String, dynamic> _cachedAnyVisualStudioDetails;
Map<String, dynamic> get _anyVisualStudioDetails { Map<String, dynamic> get _anyVisualStudioDetails {
_cachedAnyVisualStudioDetails ??= _visualStudioDetails(); // Search for all types of installations.
_cachedAnyVisualStudioDetails ??= _visualStudioDetails(
additionalArguments: <String>[_prereleaseKey, '-all']);
return _cachedAnyVisualStudioDetails; return _cachedAnyVisualStudioDetails;
} }
......
...@@ -30,7 +30,21 @@ class VisualStudioValidator extends DoctorValidator { ...@@ -30,7 +30,21 @@ class VisualStudioValidator extends DoctorValidator {
visualStudio.fullVersion, visualStudio.fullVersion,
))); )));
if (!visualStudio.hasNecessaryComponents) { if (visualStudio.isPrerelease) {
messages.add(ValidationMessage(userMessages.visualStudioIsPrerelease));
}
// Messages for faulty installations.
if (visualStudio.isRebootRequired) {
status = ValidationType.partial;
messages.add(ValidationMessage.error(userMessages.visualStudioRebootRequired));
} else if (!visualStudio.isComplete) {
status = ValidationType.partial;
messages.add(ValidationMessage.error(userMessages.visualStudioIsIncomplete));
} else if (!visualStudio.isLaunchable) {
status = ValidationType.partial;
messages.add(ValidationMessage.error(userMessages.visualStudioNotLaunchable));
} else if (!visualStudio.hasNecessaryComponents) {
status = ValidationType.partial; status = ValidationType.partial;
final int majorVersion = int.tryParse(visualStudio.fullVersion.split('.')[0]); final int majorVersion = int.tryParse(visualStudio.fullVersion.split('.')[0]);
messages.add(ValidationMessage.error( messages.add(ValidationMessage.error(
......
...@@ -32,55 +32,75 @@ void main() { ...@@ -32,55 +32,75 @@ void main() {
MockProcessManager mockProcessManager; MockProcessManager mockProcessManager;
final MemoryFileSystem memoryFilesystem = MemoryFileSystem(style: FileSystemStyle.windows); 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,
'displayName': 'Visual Studio Community 2017',
'installationVersion': '15.9.28307.665',
'isRebootRequired': false,
'isComplete': true,
'isLaunchable': true,
'catalog': <String, dynamic>{
'productDisplayVersion': '15.9.12',
'productMilestoneIsPreRelease': true,
}
};
// Arguments for a vswhere query to search for an installation with the required components.
const List<String> _requiredComponents = <String>[
'Microsoft.Component.MSBuild',
'Microsoft.VisualStudio.Component.VC.Tools.x86.x64',
'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([List<String> requiredComponents, String response]) { void setMockVswhereResponse(
[List<String> requiredComponents,
List<String> additionalArguments,
Map<String, dynamic> response]) {
fs.file(vswherePath).createSync(recursive: true); fs.file(vswherePath).createSync(recursive: true);
fs.file(vcvarsPath).createSync(recursive: true); fs.file(vcvarsPath).createSync(recursive: true);
final MockProcessResult result = MockProcessResult(); final MockProcessResult result = MockProcessResult();
when(result.exitCode).thenReturn(0); when(result.exitCode).thenReturn(0);
when<String>(result.stdout).thenReturn(response ??
json.encode(<Map<String, dynamic>>[
<String, dynamic>{
'installationPath': visualStudioPath,
'displayName': 'Visual Studio Community 2017',
'installationVersion': '15.9.28307.665',
'catalog': <String, String>{
'productDisplayVersion': '15.9.12',
},
},
]));
final String finalResponse =
json.encode(<Map<String, dynamic>>[response]);
when<String>(result.stdout).thenReturn(finalResponse);
final List<String> requirementArguments = requiredComponents == null final List<String> requirementArguments = requiredComponents == null
? <String>[] ? <String>[]
: <String>['-requires', ...requiredComponents]; : <String>['-requires', ...requiredComponents];
when(mockProcessManager.runSync(<String>[ when(mockProcessManager.runSync(<String>[
vswherePath, vswherePath,
'-format', 'json', '-format',
'json',
'-utf8', '-utf8',
'-latest', '-latest',
...?additionalArguments,
...?requirementArguments, ...?requirementArguments,
])).thenAnswer((Invocation invocation) { ])).thenAnswer((Invocation invocation) {
return result; return result;
}); });
} }
// Sets whether or not a vswhere query without components will return an // Sets whether or not a vswhere query with the required components will
// installation. // return an installation.
void setMockIncompleteVisualStudioExists(bool exists) { void setMockCompatibleVisualStudioInstallation(Map<String, dynamic>response) {
setMockVswhereResponse(null, exists ? null : '[]'); setMockVswhereResponse(_requiredComponents, null, response);
} }
// Sets whether or not a vswhere query with the required components will // Sets whether or not a vswhere query with the required components will
// return an installation. // return a pre-release installation.
void setMockCompatibleVisualStudioExists(bool exists) { void setMockPrereleaseVisualStudioInstallation(Map<String, dynamic>response) {
setMockVswhereResponse(<String>[ setMockVswhereResponse(_requiredComponents, <String>['-prerelease'], response);
'Microsoft.Component.MSBuild', }
'Microsoft.VisualStudio.Component.VC.Tools.x86.x64',
'Microsoft.VisualStudio.Component.Windows10SDK.17763', // Sets whether or not a vswhere query searching for 'all' and 'prerelease'
], exists ? null : '[]'); // versions will return an installation.
void setMockAnyVisualStudioInstallation(Map<String, dynamic>response) {
setMockVswhereResponse(null, <String>['-prerelease', '-all'], response);
} }
group('Visual Studio', () { group('Visual Studio', () {
...@@ -132,20 +152,97 @@ void main() { ...@@ -132,20 +152,97 @@ void main() {
}); });
testUsingContext('isInstalled returns true when VS is present but missing components', () { testUsingContext('isInstalled returns true when VS is present but missing components', () {
setMockIncompleteVisualStudioExists(true); setMockCompatibleVisualStudioInstallation(null);
setMockCompatibleVisualStudioExists(false); setMockPrereleaseVisualStudioInstallation(null);
setMockAnyVisualStudioInstallation(_defaultResponse);
visualStudio = VisualStudio();
expect(visualStudio.isInstalled, true);
}, overrides: <Type, Generator>{
FileSystem: () => memoryFilesystem,
Platform: () => windowsPlatform,
ProcessManager: () => mockProcessManager,
});
testUsingContext('isInstalled returns true when a prerelease version of VS is present', () {
setMockCompatibleVisualStudioInstallation(null);
setMockAnyVisualStudioInstallation(null);
final Map<String, dynamic> response = Map<String, dynamic>.from(_defaultResponse)
..addAll(<String, dynamic>{
'catalog': <String, dynamic>{
'productDisplayVersion': '15.9.12',
'productMilestoneIsPreRelease': true,
}
});
setMockPrereleaseVisualStudioInstallation(response);
visualStudio = VisualStudio();
expect(visualStudio.isInstalled, true);
expect(visualStudio.isPrerelease, true);
}, overrides: <Type, Generator>{
FileSystem: () => memoryFilesystem,
Platform: () => windowsPlatform,
ProcessManager: () => mockProcessManager,
});
testUsingContext('isComplete returns false when an incomplete installation is found', () {
setMockCompatibleVisualStudioInstallation(null);
setMockPrereleaseVisualStudioInstallation(null);
final Map<String, dynamic> response = Map<String, dynamic>.from(_defaultResponse)
..['isComplete'] = false;
setMockAnyVisualStudioInstallation(response);
visualStudio = VisualStudio();
expect(visualStudio.isInstalled, true);
expect(visualStudio.isComplete, false);
}, overrides: <Type, Generator>{
FileSystem: () => memoryFilesystem,
Platform: () => windowsPlatform,
ProcessManager: () => mockProcessManager,
});
testUsingContext('isLaunchable returns false if the installation can\'t be launched', () {
setMockCompatibleVisualStudioInstallation(null);
setMockPrereleaseVisualStudioInstallation(null);
final Map<String, dynamic> response = Map<String, dynamic>.from(_defaultResponse)
..['isLaunchable'] = false;
setMockAnyVisualStudioInstallation(response);
visualStudio = VisualStudio();
expect(visualStudio.isInstalled, true);
expect(visualStudio.isLaunchable, false);
}, overrides: <Type, Generator>{
FileSystem: () => memoryFilesystem,
Platform: () => windowsPlatform,
ProcessManager: () => mockProcessManager,
});
testUsingContext('isRebootRequired returns true if the installation needs a reboot', () {
setMockCompatibleVisualStudioInstallation(null);
setMockPrereleaseVisualStudioInstallation(null);
final Map<String, dynamic> response = Map<String, dynamic>.from(_defaultResponse)
..['isRebootRequired'] = true;
setMockAnyVisualStudioInstallation(response);
visualStudio = VisualStudio(); visualStudio = VisualStudio();
expect(visualStudio.isInstalled, true); expect(visualStudio.isInstalled, true);
expect(visualStudio.isRebootRequired, true);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => memoryFilesystem, FileSystem: () => memoryFilesystem,
Platform: () => windowsPlatform, Platform: () => windowsPlatform,
ProcessManager: () => mockProcessManager, ProcessManager: () => mockProcessManager,
}); });
testUsingContext('hasNecessaryComponents returns false when VS is present but missing components', () { testUsingContext('hasNecessaryComponents returns false when VS is present but missing components', () {
setMockIncompleteVisualStudioExists(true); setMockCompatibleVisualStudioInstallation(null);
setMockCompatibleVisualStudioExists(false); setMockPrereleaseVisualStudioInstallation(null);
setMockAnyVisualStudioInstallation(_defaultResponse);
visualStudio = VisualStudio(); visualStudio = VisualStudio();
expect(visualStudio.hasNecessaryComponents, false); expect(visualStudio.hasNecessaryComponents, false);
...@@ -156,8 +253,9 @@ void main() { ...@@ -156,8 +253,9 @@ void main() {
}); });
testUsingContext('vcvarsPath returns null when VS is present but missing components', () { testUsingContext('vcvarsPath returns null when VS is present but missing components', () {
setMockIncompleteVisualStudioExists(true); setMockCompatibleVisualStudioInstallation(null);
setMockCompatibleVisualStudioExists(false); setMockPrereleaseVisualStudioInstallation(null);
setMockAnyVisualStudioInstallation(_defaultResponse);
visualStudio = VisualStudio(); visualStudio = VisualStudio();
expect(visualStudio.vcvarsPath, isNull); expect(visualStudio.vcvarsPath, isNull);
...@@ -167,28 +265,44 @@ void main() { ...@@ -167,28 +265,44 @@ void main() {
ProcessManager: () => mockProcessManager, ProcessManager: () => mockProcessManager,
}); });
testUsingContext('VS metadata is available when VS is present, even if missing components', () { testUsingContext('vcvarsPath returns null when VS is present but with require components but installation is faulty', () {
setMockIncompleteVisualStudioExists(true); final Map<String, dynamic> response = Map<String, dynamic>.from(_defaultResponse)
setMockCompatibleVisualStudioExists(false); ..['isRebootRequired'] = true;
setMockCompatibleVisualStudioInstallation(response);
setMockPrereleaseVisualStudioInstallation(null);
visualStudio = VisualStudio(); visualStudio = VisualStudio();
expect(visualStudio.displayName, equals('Visual Studio Community 2017')); expect(visualStudio.vcvarsPath, isNull);
expect(visualStudio.displayVersion, equals('15.9.12'));
expect(visualStudio.installLocation, equals(visualStudioPath));
expect(visualStudio.fullVersion, equals('15.9.28307.665'));
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => memoryFilesystem, FileSystem: () => memoryFilesystem,
Platform: () => windowsPlatform, Platform: () => windowsPlatform,
ProcessManager: () => mockProcessManager, ProcessManager: () => mockProcessManager,
}); });
testUsingContext('hasNecessaryComponents returns false when VS is present with required components but installation is faulty', () {
final Map<String, dynamic> response = Map<String, dynamic>.from(_defaultResponse)
..['isRebootRequired'] = true;
setMockCompatibleVisualStudioInstallation(response);
setMockPrereleaseVisualStudioInstallation(null);
testUsingContext('isInstalled returns true when VS is present but missing components', () { visualStudio = VisualStudio();
setMockIncompleteVisualStudioExists(true); expect(visualStudio.hasNecessaryComponents, false);
setMockCompatibleVisualStudioExists(false); }, overrides: <Type, Generator>{
FileSystem: () => memoryFilesystem,
Platform: () => windowsPlatform,
ProcessManager: () => mockProcessManager,
});
testUsingContext('VS metadata is available when VS is present, even if missing components', () {
setMockCompatibleVisualStudioInstallation(null);
setMockPrereleaseVisualStudioInstallation(null);
setMockAnyVisualStudioInstallation(_defaultResponse);
visualStudio = VisualStudio(); visualStudio = VisualStudio();
expect(visualStudio.isInstalled, true); expect(visualStudio.displayName, equals('Visual Studio Community 2017'));
expect(visualStudio.displayVersion, equals('15.9.12'));
expect(visualStudio.installLocation, equals(visualStudioPath));
expect(visualStudio.fullVersion, equals('15.9.28307.665'));
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => memoryFilesystem, FileSystem: () => memoryFilesystem,
Platform: () => windowsPlatform, Platform: () => windowsPlatform,
...@@ -196,7 +310,9 @@ void main() { ...@@ -196,7 +310,9 @@ void main() {
}); });
testUsingContext('Everything returns good values when VS is present with all components', () { testUsingContext('Everything returns good values when VS is present with all components', () {
setMockCompatibleVisualStudioExists(true); setMockCompatibleVisualStudioInstallation(_defaultResponse);
setMockPrereleaseVisualStudioInstallation(null);
setMockAnyVisualStudioInstallation(null);
visualStudio = VisualStudio(); visualStudio = VisualStudio();
expect(visualStudio.isInstalled, true); expect(visualStudio.isInstalled, true);
...@@ -209,19 +325,18 @@ void main() { ...@@ -209,19 +325,18 @@ void main() {
}); });
testUsingContext('Metadata is for compatible version when latest is missing components', () { testUsingContext('Metadata is for compatible version when latest is missing components', () {
setMockCompatibleVisualStudioExists(true); setMockCompatibleVisualStudioInstallation(_defaultResponse);
setMockPrereleaseVisualStudioInstallation(null);
// Return a different version for queries without the required packages. // Return a different version for queries without the required packages.
final String incompleteVersionResponse = json.encode(<Map<String, dynamic>>[ final Map<String, dynamic> incompleteVersionResponse = <String, dynamic>{
<String, dynamic>{
'installationPath': visualStudioPath, 'installationPath': visualStudioPath,
'displayName': 'Visual Studio Community 2019', 'displayName': 'Visual Studio Community 2019',
'installationVersion': '16.1.1.1', 'installationVersion': '16.1.1.1',
'catalog': <String, String>{ 'catalog': <String, String>{
'productDisplayVersion': '16.1', 'productDisplayVersion': '16.1',
}, },
} };
]); setMockAnyVisualStudioInstallation(incompleteVersionResponse);
setMockVswhereResponse(null, incompleteVersionResponse);
visualStudio = VisualStudio(); visualStudio = VisualStudio();
expect(visualStudio.displayName, equals('Visual Studio Community 2017')); expect(visualStudio.displayName, equals('Visual Studio Community 2017'));
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// 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/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';
...@@ -18,6 +19,24 @@ void main() { ...@@ -18,6 +19,24 @@ void main() {
setUp(() { setUp(() {
mockVisualStudio = MockVisualStudio(); mockVisualStudio = MockVisualStudio();
// Mock a valid VS installation.
when(mockVisualStudio.isInstalled).thenReturn(true);
when(mockVisualStudio.isPrerelease).thenReturn(false);
when(mockVisualStudio.isComplete).thenReturn(true);
when(mockVisualStudio.isLaunchable).thenReturn(true);
when(mockVisualStudio.isRebootRequired).thenReturn(false);
when(mockVisualStudio.hasNecessaryComponents).thenReturn(true);
});
testUsingContext('Emits a message when Visual Studio is a pre-release version', () async {
when(visualStudio.isPrerelease).thenReturn(true);
const VisualStudioValidator validator = VisualStudioValidator();
final ValidationResult result = await validator.validate();
final ValidationMessage expectedMessage = ValidationMessage(userMessages.visualStudioIsPrerelease);
expect(result.messages.contains(expectedMessage), true);
}, overrides: <Type, Generator>{
VisualStudio: () => mockVisualStudio,
}); });
testUsingContext('Emits missing status when Visual Studio is not installed', () async { testUsingContext('Emits missing status when Visual Studio is not installed', () async {
...@@ -29,8 +48,44 @@ void main() { ...@@ -29,8 +48,44 @@ void main() {
VisualStudio: () => mockVisualStudio, VisualStudio: () => mockVisualStudio,
}); });
testUsingContext('Emits a partial status when Visual Studio installation is incomplete', () async {
when(visualStudio.isComplete).thenReturn(false);
const VisualStudioValidator validator = VisualStudioValidator();
final ValidationResult result = await validator.validate();
final ValidationMessage expectedMessage = ValidationMessage.error(userMessages.visualStudioIsIncomplete);
expect(result.messages.contains(expectedMessage), true);
expect(result.type, ValidationType.partial);
}, overrides: <Type, Generator>{
VisualStudio: () => mockVisualStudio,
});
testUsingContext('Emits a partial status when Visual Studio installation needs rebooting', () async {
when(visualStudio.isRebootRequired).thenReturn(true);
const VisualStudioValidator validator = VisualStudioValidator();
final ValidationResult result = await validator.validate();
final ValidationMessage expectedMessage = ValidationMessage.error(userMessages.visualStudioRebootRequired);
expect(result.messages.contains(expectedMessage), true);
expect(result.type, ValidationType.partial);
}, overrides: <Type, Generator>{
VisualStudio: () => mockVisualStudio,
});
testUsingContext('Emits a partial status when Visual Studio installation is not launchable', () async {
when(visualStudio.isLaunchable).thenReturn(false);
const VisualStudioValidator validator = VisualStudioValidator();
final ValidationResult result = await validator.validate();
final ValidationMessage expectedMessage = ValidationMessage.error(userMessages.visualStudioNotLaunchable);
expect(result.messages.contains(expectedMessage), true);
expect(result.type, ValidationType.partial);
}, overrides: <Type, Generator>{
VisualStudio: () => mockVisualStudio,
});
testUsingContext('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 {
when(visualStudio.isInstalled).thenReturn(true);
when(visualStudio.hasNecessaryComponents).thenReturn(false); when(visualStudio.hasNecessaryComponents).thenReturn(false);
when(visualStudio.workloadDescription).thenReturn('Desktop development'); when(visualStudio.workloadDescription).thenReturn('Desktop development');
when(visualStudio.necessaryComponentDescriptions(any)).thenReturn(<String>['A', 'B']); when(visualStudio.necessaryComponentDescriptions(any)).thenReturn(<String>['A', 'B']);
...@@ -43,13 +98,22 @@ void main() { ...@@ -43,13 +98,22 @@ void main() {
}); });
testUsingContext('Emits installed status when Visual Studio is installed with necessary components', () async { testUsingContext('Emits installed status when Visual Studio is installed with necessary components', () async {
when(visualStudio.isInstalled).thenReturn(true);
when(visualStudio.hasNecessaryComponents).thenReturn(true);
const VisualStudioValidator validator = VisualStudioValidator(); const VisualStudioValidator validator = VisualStudioValidator();
final ValidationResult result = await validator.validate(); final ValidationResult result = await validator.validate();
expect(result.type, ValidationType.installed); expect(result.type, ValidationType.installed);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
VisualStudio: () => mockVisualStudio, VisualStudio: () => mockVisualStudio,
}); });
testUsingContext('Emits missing status when Visual Studio is not installed', () async {
when(visualStudio.isInstalled).thenReturn(false);
const VisualStudioValidator validator = VisualStudioValidator();
final ValidationResult result = await validator.validate();
final ValidationMessage expectedMessage = ValidationMessage.error(userMessages.visualStudioMissing);
expect(result.messages.contains(expectedMessage), true);
expect(result.type, ValidationType.missing);
}, overrides: <Type, Generator>{
VisualStudio: () => mockVisualStudio,
});
}); });
} }
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