Fix crash on vswhere search from flutter doctor (#40263)
Fixes a crash introduced on #40011 due to an incorrect type in the vswhere search
Fixes #40238
diff --git a/packages/flutter_tools/lib/src/windows/visual_studio.dart b/packages/flutter_tools/lib/src/windows/visual_studio.dart
index 31fd027..60b693b 100644
--- a/packages/flutter_tools/lib/src/windows/visual_studio.dart
+++ b/packages/flutter_tools/lib/src/windows/visual_studio.dart
@@ -35,10 +35,6 @@
String get displayVersion =>
_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.
String get installLocation => _bestVisualStudioDetails[_installationPathKey];
@@ -47,14 +43,21 @@
/// For instance: "15.4.27004.2002".
String get fullVersion => _bestVisualStudioDetails[_fullVersionKey];
+ // Properties that determine the status of the installation. There might be
+ // Visual Studio versions that don't include them, so default to a "valid" value to
+ // avoid false negatives.
+
/// True there is complete installation of Visual Studio.
- bool get isComplete => _bestVisualStudioDetails[_isCompleteKey];
+ bool get isComplete => _bestVisualStudioDetails[_isCompleteKey] ?? false;
/// True if Visual Studio is launchable.
- bool get isLaunchable => _bestVisualStudioDetails[_isLaunchableKey];
+ bool get isLaunchable => _bestVisualStudioDetails[_isLaunchableKey] ?? false;
+
+ /// True if the Visual Studio installation is as pre-release version.
+ bool get isPrerelease => _bestVisualStudioDetails[_isPrereleaseKey] ?? false;
/// True if a reboot is required to complete the Visual Studio installation.
- bool get isRebootRequired => _bestVisualStudioDetails[_isRebootRequiredKey];
+ bool get isRebootRequired => _bestVisualStudioDetails[_isRebootRequiredKey] ?? false;
/// The name of the recommended Visual Studio installer workload.
String get workloadDescription => 'Desktop development with C++';
@@ -141,16 +144,14 @@
/// The 'catalog' entry containing more details.
static const String _catalogKey = 'catalog';
+ /// The key for a pre-release version.
+ static const String _isPrereleaseKey = 'isPrerelease';
+
/// The user-friendly version.
///
/// This key is under the 'catalog' entry.
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';
@@ -189,15 +190,24 @@
}
/// Checks if the given installation has issues that the user must resolve.
+ ///
+ /// Returns false if the required information is missing since older versions
+ /// of Visual Studio might not include them.
bool installationHasIssues(Map<String, dynamic>installationDetails) {
assert(installationDetails != null);
- assert(installationDetails[_isCompleteKey] != null);
- assert(installationDetails[_isRebootRequiredKey] != null);
- assert(installationDetails[_isLaunchableKey] != null);
+ if (installationDetails[_isCompleteKey] != null && !installationDetails[_isCompleteKey]) {
+ return true;
+ }
- return installationDetails[_isCompleteKey] == false ||
- installationDetails[_isRebootRequiredKey] == true ||
- installationDetails[_isLaunchableKey] == false;
+ if (installationDetails[_isLaunchableKey] != null && !installationDetails[_isLaunchableKey]) {
+ return true;
+ }
+
+ if (installationDetails[_isRebootRequiredKey] != null && installationDetails[_isRebootRequiredKey]) {
+ return true;
+ }
+
+ return false;
}
/// Returns the details dictionary for the latest version of Visual Studio
diff --git a/packages/flutter_tools/test/general.shard/windows/visual_studio_test.dart b/packages/flutter_tools/test/general.shard/windows/visual_studio_test.dart
index d6da8bf..8ebeb33 100644
--- a/packages/flutter_tools/test/general.shard/windows/visual_studio_test.dart
+++ b/packages/flutter_tools/test/general.shard/windows/visual_studio_test.dart
@@ -40,9 +40,20 @@
'isRebootRequired': false,
'isComplete': true,
'isLaunchable': true,
+ 'isPrerelease': false,
'catalog': <String, dynamic>{
'productDisplayVersion': '15.9.12',
- 'productMilestoneIsPreRelease': true,
+ }
+ };
+
+ // A version of a response that doesn't include certain installation status
+ // information that might be missing in older Visual Studio versions.
+ const Map<String, dynamic> _oldResponse = <String, dynamic>{
+ 'installationPath': visualStudioPath,
+ 'displayName': 'Visual Studio Community 2017',
+ 'installationVersion': '15.9.28307.665',
+ 'catalog': <String, dynamic>{
+ 'productDisplayVersion': '15.9.12',
}
};
@@ -173,6 +184,19 @@
ProcessManager: () => mockProcessManager,
});
+ testUsingContext('isInstalled returns true even with missing status information', () {
+ setMockCompatibleVisualStudioInstallation(null);
+ setMockPrereleaseVisualStudioInstallation(null);
+ setMockAnyVisualStudioInstallation(_oldResponse);
+
+ visualStudio = VisualStudio();
+ expect(visualStudio.isInstalled, true);
+ }, overrides: <Type, Generator>{
+ FileSystem: () => memoryFilesystem,
+ Platform: () => windowsPlatform,
+ ProcessManager: () => mockProcessManager,
+ });
+
testUsingContext('isInstalled returns true when VS is present but missing components', () {
setMockCompatibleVisualStudioInstallation(null);
setMockPrereleaseVisualStudioInstallation(null);
@@ -191,15 +215,9 @@
setMockAnyVisualStudioInstallation(null);
final Map<String, dynamic> response = Map<String, dynamic>.from(_defaultResponse)
- ..addAll(<String, dynamic>{
- 'catalog': <String, dynamic>{
- 'productDisplayVersion': '15.9.12',
- 'productMilestoneIsPreRelease': true,
- }
- });
+ ..['isPrerelease'] = true;
setMockPrereleaseVisualStudioInstallation(response);
-
visualStudio = VisualStudio();
expect(visualStudio.isInstalled, true);
expect(visualStudio.isPrerelease, true);