[tool] In flutter doctor -v
, warn when Android Studio version could not be detected. (#126395)
Fixes #122081. When validating an Android Studio installation, add a warning validation message when we are unable to detect the version. This is because we have logic throughout the tool (JDK/JRE-searching) that is at higher risk of failing when we don't know the version.
This commit is contained in:
parent
a3ac142f26
commit
13db2e4a76
@ -16,12 +16,17 @@ const String _androidStudioPreviewTitle = 'Android Studio Preview';
|
|||||||
const String _androidStudioPreviewId = 'AndroidStudioPreview';
|
const String _androidStudioPreviewId = 'AndroidStudioPreview';
|
||||||
|
|
||||||
class AndroidStudioValidator extends DoctorValidator {
|
class AndroidStudioValidator extends DoctorValidator {
|
||||||
AndroidStudioValidator(this._studio, { required FileSystem fileSystem })
|
AndroidStudioValidator(this._studio, {
|
||||||
: _fileSystem = fileSystem,
|
required FileSystem fileSystem,
|
||||||
|
required UserMessages userMessages,
|
||||||
|
})
|
||||||
|
: _userMessages = userMessages,
|
||||||
|
_fileSystem = fileSystem,
|
||||||
super('Android Studio');
|
super('Android Studio');
|
||||||
|
|
||||||
final AndroidStudio _studio;
|
final AndroidStudio _studio;
|
||||||
final FileSystem _fileSystem;
|
final FileSystem _fileSystem;
|
||||||
|
final UserMessages _userMessages;
|
||||||
|
|
||||||
static const Map<String, String> idToTitle = <String, String>{
|
static const Map<String, String> idToTitle = <String, String>{
|
||||||
_androidStudioId: _androidStudioTitle,
|
_androidStudioId: _androidStudioTitle,
|
||||||
@ -35,7 +40,7 @@ class AndroidStudioValidator extends DoctorValidator {
|
|||||||
NoAndroidStudioValidator(config: config, platform: platform, userMessages: userMessages)
|
NoAndroidStudioValidator(config: config, platform: platform, userMessages: userMessages)
|
||||||
else
|
else
|
||||||
...studios.map<DoctorValidator>(
|
...studios.map<DoctorValidator>(
|
||||||
(AndroidStudio studio) => AndroidStudioValidator(studio, fileSystem: fileSystem)
|
(AndroidStudio studio) => AndroidStudioValidator(studio, fileSystem: fileSystem, userMessages: userMessages)
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@ -45,11 +50,11 @@ class AndroidStudioValidator extends DoctorValidator {
|
|||||||
final List<ValidationMessage> messages = <ValidationMessage>[];
|
final List<ValidationMessage> messages = <ValidationMessage>[];
|
||||||
ValidationType type = ValidationType.missing;
|
ValidationType type = ValidationType.missing;
|
||||||
|
|
||||||
final String? studioVersionText = _studio.version == null
|
final String studioVersionText = _studio.version == null
|
||||||
? null
|
? _userMessages.androidStudioVersion('unknown')
|
||||||
: userMessages.androidStudioVersion(_studio.version.toString());
|
: _userMessages.androidStudioVersion(_studio.version.toString());
|
||||||
messages.add(ValidationMessage(
|
messages.add(ValidationMessage(
|
||||||
userMessages.androidStudioLocation(_studio.directory),
|
_userMessages.androidStudioLocation(_studio.directory),
|
||||||
));
|
));
|
||||||
|
|
||||||
if (_studio.pluginsPath != null) {
|
if (_studio.pluginsPath != null) {
|
||||||
@ -69,6 +74,10 @@ class AndroidStudioValidator extends DoctorValidator {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_studio.version == null) {
|
||||||
|
messages.add(const ValidationMessage.error('Unable to determine Android Studio version.'));
|
||||||
|
}
|
||||||
|
|
||||||
if (_studio.isValid) {
|
if (_studio.isValid) {
|
||||||
type = _hasIssues(messages)
|
type = _hasIssues(messages)
|
||||||
? ValidationType.partial
|
? ValidationType.partial
|
||||||
@ -81,9 +90,9 @@ class AndroidStudioValidator extends DoctorValidator {
|
|||||||
messages.addAll(_studio.validationMessages.map<ValidationMessage>(
|
messages.addAll(_studio.validationMessages.map<ValidationMessage>(
|
||||||
(String m) => ValidationMessage.error(m),
|
(String m) => ValidationMessage.error(m),
|
||||||
));
|
));
|
||||||
messages.add(ValidationMessage(userMessages.androidStudioNeedsUpdate));
|
messages.add(ValidationMessage(_userMessages.androidStudioNeedsUpdate));
|
||||||
if (_studio.configuredPath != null) {
|
if (_studio.configuredPath != null) {
|
||||||
messages.add(ValidationMessage(userMessages.androidStudioResetDir));
|
messages.add(ValidationMessage(_userMessages.androidStudioResetDir));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,13 +23,20 @@ class VsCodeValidator extends DoctorValidator {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<ValidationResult> validate() async {
|
Future<ValidationResult> validate() async {
|
||||||
final String? vsCodeVersionText = _vsCode.version == null
|
final List<ValidationMessage> validationMessages =
|
||||||
? null
|
List<ValidationMessage>.from(_vsCode.validationMessages);
|
||||||
|
|
||||||
|
final String vsCodeVersionText = _vsCode.version == null
|
||||||
|
? userMessages.vsCodeVersion('unknown')
|
||||||
: userMessages.vsCodeVersion(_vsCode.version.toString());
|
: userMessages.vsCodeVersion(_vsCode.version.toString());
|
||||||
|
|
||||||
|
if (_vsCode.version == null) {
|
||||||
|
validationMessages.add(const ValidationMessage.error('Unable to determine VS Code version.'));
|
||||||
|
}
|
||||||
|
|
||||||
return ValidationResult(
|
return ValidationResult(
|
||||||
ValidationType.success,
|
ValidationType.success,
|
||||||
_vsCode.validationMessages.toList(),
|
validationMessages,
|
||||||
statusInfo: vsCodeVersionText,
|
statusInfo: vsCodeVersionText,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -3,14 +3,17 @@
|
|||||||
// 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/android/android_studio.dart';
|
||||||
import 'package:flutter_tools/src/android/android_studio_validator.dart';
|
import 'package:flutter_tools/src/android/android_studio_validator.dart';
|
||||||
import 'package:flutter_tools/src/base/config.dart';
|
import 'package:flutter_tools/src/base/config.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/base/platform.dart';
|
import 'package:flutter_tools/src/base/platform.dart';
|
||||||
import 'package:flutter_tools/src/base/user_messages.dart';
|
import 'package:flutter_tools/src/base/user_messages.dart';
|
||||||
|
import 'package:flutter_tools/src/base/version.dart';
|
||||||
import 'package:flutter_tools/src/doctor_validator.dart';
|
import 'package:flutter_tools/src/doctor_validator.dart';
|
||||||
import 'package:flutter_tools/src/globals.dart' as globals;
|
import 'package:flutter_tools/src/globals.dart' as globals;
|
||||||
|
import 'package:test/fake.dart';
|
||||||
|
|
||||||
import '../../src/common.dart';
|
import '../../src/common.dart';
|
||||||
import '../../src/context.dart';
|
import '../../src/context.dart';
|
||||||
@ -23,6 +26,7 @@ final Platform linuxPlatform = FakePlatform(
|
|||||||
);
|
);
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
|
||||||
late FileSystem fileSystem;
|
late FileSystem fileSystem;
|
||||||
late FakeProcessManager fakeProcessManager;
|
late FakeProcessManager fakeProcessManager;
|
||||||
|
|
||||||
@ -31,49 +35,76 @@ void main() {
|
|||||||
fakeProcessManager = FakeProcessManager.empty();
|
fakeProcessManager = FakeProcessManager.empty();
|
||||||
});
|
});
|
||||||
|
|
||||||
testWithoutContext('NoAndroidStudioValidator shows Android Studio as "not available" when not available.', () async {
|
group(NoAndroidStudioValidator, () {
|
||||||
final Config config = Config.test();
|
testWithoutContext('shows Android Studio as "not available" when not available.', () async {
|
||||||
final NoAndroidStudioValidator validator = NoAndroidStudioValidator(
|
final Config config = Config.test();
|
||||||
config: config,
|
final NoAndroidStudioValidator validator = NoAndroidStudioValidator(
|
||||||
platform: linuxPlatform,
|
config: config,
|
||||||
userMessages: UserMessages(),
|
platform: linuxPlatform,
|
||||||
);
|
userMessages: UserMessages(),
|
||||||
|
);
|
||||||
|
|
||||||
expect((await validator.validate()).type, equals(ValidationType.notAvailable));
|
expect((await validator.validate()).type, equals(ValidationType.notAvailable));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
testUsingContext('AndroidStudioValidator gives doctor error on java crash', () async {
|
group(AndroidStudioValidator, () {
|
||||||
fakeProcessManager.addCommand(const FakeCommand(
|
testUsingContext('gives doctor error on java crash', () async {
|
||||||
command: <String>[
|
fakeProcessManager.addCommand(const FakeCommand(
|
||||||
'/opt/android-studio-with-cheese-5.0/jre/bin/java',
|
command: <String>[
|
||||||
'-version',
|
'/opt/android-studio-with-cheese-5.0/jre/bin/java',
|
||||||
],
|
'-version',
|
||||||
exception: ProcessException('java', <String>['-version']),
|
],
|
||||||
));
|
exception: ProcessException('java', <String>['-version']),
|
||||||
const String installPath = '/opt/android-studio-with-cheese-5.0';
|
));
|
||||||
const String studioHome = '$home/.AndroidStudioWithCheese5.0';
|
const String installPath = '/opt/android-studio-with-cheese-5.0';
|
||||||
const String homeFile = '$studioHome/system/.home';
|
const String studioHome = '$home/.AndroidStudioWithCheese5.0';
|
||||||
globals.fs.directory(installPath).createSync(recursive: true);
|
const String homeFile = '$studioHome/system/.home';
|
||||||
globals.fs.file(homeFile).createSync(recursive: true);
|
globals.fs.directory(installPath).createSync(recursive: true);
|
||||||
globals.fs.file(homeFile).writeAsStringSync(installPath);
|
globals.fs.file(homeFile).createSync(recursive: true);
|
||||||
|
globals.fs.file(homeFile).writeAsStringSync(installPath);
|
||||||
|
|
||||||
// This checks that running the validator doesn't throw an unhandled
|
// This checks that running the validator doesn't throw an unhandled
|
||||||
// exception and that the ProcessException makes it into the error
|
// exception and that the ProcessException makes it into the error
|
||||||
// message list.
|
// message list.
|
||||||
for (final DoctorValidator validator in AndroidStudioValidator.allValidators(globals.config, globals.platform, globals.fs, globals.userMessages)) {
|
for (final DoctorValidator validator in AndroidStudioValidator.allValidators(globals.config, globals.platform, globals.fs, globals.userMessages)) {
|
||||||
|
final ValidationResult result = await validator.validate();
|
||||||
|
expect(result.messages.where((ValidationMessage message) {
|
||||||
|
return message.isError && message.message.contains('ProcessException');
|
||||||
|
}).isNotEmpty, true);
|
||||||
|
}
|
||||||
|
expect(fakeProcessManager, hasNoRemainingExpectations);
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
FileSystem: () => fileSystem,
|
||||||
|
ProcessManager: () => fakeProcessManager,
|
||||||
|
Platform: () => linuxPlatform,
|
||||||
|
FileSystemUtils: () => FileSystemUtils(
|
||||||
|
fileSystem: fileSystem,
|
||||||
|
platform: linuxPlatform,
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
testWithoutContext('warns if version of Android Studio could not be determined', () async {
|
||||||
|
final AndroidStudio studio = _FakeAndroidStudio();
|
||||||
|
final AndroidStudioValidator validator = AndroidStudioValidator(studio, fileSystem: fileSystem, userMessages: UserMessages());
|
||||||
final ValidationResult result = await validator.validate();
|
final ValidationResult result = await validator.validate();
|
||||||
expect(result.messages.where((ValidationMessage message) {
|
expect(result.messages, contains(const ValidationMessage.error('Unable to determine Android Studio version.')));
|
||||||
return message.isError && message.message.contains('ProcessException');
|
expect(result.statusInfo, 'version unknown');
|
||||||
}).isNotEmpty, true);
|
});
|
||||||
}
|
|
||||||
expect(fakeProcessManager, hasNoRemainingExpectations);
|
|
||||||
}, overrides: <Type, Generator>{
|
|
||||||
FileSystem: () => fileSystem,
|
|
||||||
ProcessManager: () => fakeProcessManager,
|
|
||||||
Platform: () => linuxPlatform,
|
|
||||||
FileSystemUtils: () => FileSystemUtils(
|
|
||||||
fileSystem: fileSystem,
|
|
||||||
platform: linuxPlatform,
|
|
||||||
),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _FakeAndroidStudio extends Fake implements AndroidStudio {
|
||||||
|
@override
|
||||||
|
List<String> get validationMessages => <String>[];
|
||||||
|
@override
|
||||||
|
bool get isValid => true;
|
||||||
|
@override
|
||||||
|
String? get pluginsPath => null;
|
||||||
|
@override
|
||||||
|
String get directory => 'android-studio';
|
||||||
|
@override
|
||||||
|
Version? get version => null;
|
||||||
|
@override
|
||||||
|
String get javaPath => 'android-studio/jbr/bin/java';
|
||||||
|
}
|
||||||
|
@ -5,10 +5,15 @@
|
|||||||
import 'package:file/memory.dart';
|
import 'package:file/memory.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/platform.dart';
|
import 'package:flutter_tools/src/base/platform.dart';
|
||||||
|
import 'package:flutter_tools/src/base/user_messages.dart';
|
||||||
|
import 'package:flutter_tools/src/base/version.dart';
|
||||||
|
import 'package:flutter_tools/src/doctor_validator.dart';
|
||||||
import 'package:flutter_tools/src/vscode/vscode.dart';
|
import 'package:flutter_tools/src/vscode/vscode.dart';
|
||||||
|
import 'package:flutter_tools/src/vscode/vscode_validator.dart';
|
||||||
|
import 'package:test/fake.dart';
|
||||||
|
|
||||||
import '../../src/common.dart';
|
import '../../src/common.dart';
|
||||||
import '../../src/fake_process_manager.dart';
|
import '../../src/context.dart';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
testWithoutContext('VsCode search locations on windows supports an empty environment', () {
|
testWithoutContext('VsCode search locations on windows supports an empty environment', () {
|
||||||
@ -20,4 +25,26 @@ void main() {
|
|||||||
|
|
||||||
expect(VsCode.allInstalled(fileSystem, platform, FakeProcessManager.any()), isEmpty);
|
expect(VsCode.allInstalled(fileSystem, platform, FakeProcessManager.any()), isEmpty);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group(VsCodeValidator, () {
|
||||||
|
testUsingContext('Warns if VS Code version could not be found', () async {
|
||||||
|
final VsCodeValidator validator = VsCodeValidator(_FakeVsCode());
|
||||||
|
final ValidationResult result = await validator.validate();
|
||||||
|
expect(result.messages, contains(const ValidationMessage.error('Unable to determine VS Code version.')));
|
||||||
|
expect(result.statusInfo, 'version unknown');
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
UserMessages: () => UserMessages(),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FakeVsCode extends Fake implements VsCode {
|
||||||
|
@override
|
||||||
|
Iterable<ValidationMessage> get validationMessages => <ValidationMessage>[];
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get productName => 'VS Code';
|
||||||
|
|
||||||
|
@override
|
||||||
|
Version? get version => null;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user