fix(shorebird_cli): prefer Android Studio java over JAVA_HOME (#1405)

This commit is contained in:
Bryan Oltman
2023-10-16 17:43:20 -04:00
committed by GitHub
parent 37b3a1d792
commit eefb9e8cd6
3 changed files with 200 additions and 102 deletions
@@ -4,6 +4,7 @@ import 'package:collection/collection.dart';
import 'package:path/path.dart' as p;
import 'package:scoped/scoped.dart';
import 'package:shorebird_cli/src/android_studio.dart';
import 'package:shorebird_cli/src/os/os.dart';
import 'package:shorebird_cli/src/platform.dart';
/// A reference to a [Java] instance.
@@ -23,13 +24,21 @@ class Java {
return p.join(javaHome, 'bin', 'java.exe');
}
/// Returns the JAVA_HOME environment variable if set.
/// Otherwise, returns the location where the Android Studio JDK/JRE is installed.
String? get home {
if (platform.environment.containsKey('JAVA_HOME')) {
return platform.environment['JAVA_HOME'];
}
/// Returns the path to the user's JDK, if one is found.
///
/// Our goal is to match the behavior of the flutter tool. As per the docs at
/// https://github.com/flutter/flutter/blob/stable/packages/flutter_tools/lib/src/android/java.dart#L45-L54,
/// we search for Java in the following places, in order:
///
/// 1. the runtime environment bundled with Android Studio;
/// 2. the runtime environment found in the JAVA_HOME env variable, if set; or
/// 3. the java binary found on PATH.
String? get home =>
_androidStudioJavaPath ??
platform.environment['JAVA_HOME'] ??
osInterface.which('java');
String? get _androidStudioJavaPath {
final androidStudioPath = androidStudio.path;
if (androidStudioPath == null) return null;
if (platform.isMacOS) {
@@ -6,6 +6,7 @@ import 'package:platform/platform.dart';
import 'package:scoped/scoped.dart';
import 'package:shorebird_cli/src/android_studio.dart';
import 'package:shorebird_cli/src/executables/executables.dart';
import 'package:shorebird_cli/src/os/os.dart';
import 'package:shorebird_cli/src/platform.dart';
import 'package:test/test.dart';
@@ -14,6 +15,7 @@ import '../mocks.dart';
void main() {
group(Java, () {
late AndroidStudio androidStudio;
late OperatingSystemInterface osInterface;
late Platform platform;
late Java java;
@@ -22,6 +24,7 @@ void main() {
() => body(),
values: {
androidStudioRef.overrideWith(() => androidStudio),
osInterfaceRef.overrideWith(() => osInterface),
platformRef.overrideWith(() => platform),
},
);
@@ -35,120 +38,202 @@ void main() {
setUp(() {
androidStudio = MockAndroidStudio();
osInterface = MockOperatingSystemInterface();
platform = MockPlatform();
java = Java();
when(() => platform.environment).thenReturn({});
when(() => platform.isWindows).thenReturn(false);
when(() => platform.isMacOS).thenReturn(false);
when(() => platform.isLinux).thenReturn(false);
when(() => osInterface.which(any())).thenReturn(null);
});
group('executable', () {
test('returns correct executable on windows', () async {
group('when on Windows', () {
const javaHome = r'C:\Program Files\Java\jdk-11.0.1';
when(() => platform.isWindows).thenReturn(true);
when(() => platform.environment).thenReturn({'JAVA_HOME': javaHome});
expect(
runWithOverrides(() => java.executable),
equals(p.join(javaHome, 'bin', 'java.exe')),
);
setUp(() {
when(() => platform.isWindows).thenReturn(true);
when(() => platform.environment).thenReturn({'JAVA_HOME': javaHome});
});
test('returns correct executable on windows', () async {
expect(
runWithOverrides(() => java.executable),
equals(p.join(javaHome, 'bin', 'java.exe')),
);
});
});
test('returns correct executable on non-windows', () async {
when(() => platform.isWindows).thenReturn(false);
expect(
runWithOverrides(() => java.executable),
equals('java'),
);
group('when on a non-Windows OS', () {
setUp(() {
when(() => platform.isWindows).thenReturn(false);
});
test('returns correct executable on non-windows', () async {
expect(
runWithOverrides(() => java.executable),
equals('java'),
);
});
});
});
group('home', () {
test('returns existing JAVA_HOME if already set', () async {
const javaHome = r'C:\Program Files\Java\jdk-11.0.1';
when(() => platform.environment).thenReturn({'JAVA_HOME': javaHome});
expect(
runWithOverrides(() => java.home),
equals(javaHome),
);
});
group('when Android Studio is installed', () {
late Directory jbrDir;
test(
'returns null if JAVA_HOME is not set and'
' Android Studio is not installed', () async {
when(() => platform.environment).thenReturn({});
when(() => platform.isMacOS).thenReturn(false);
when(() => platform.isWindows).thenReturn(false);
when(() => platform.isLinux).thenReturn(false);
expect(runWithOverrides(() => java.home), isNull);
});
group('when on macOS', () {
setUp(() {
when(() => platform.isMacOS).thenReturn(true);
test('returns correct path on windows', () async {
final tempDir = setUpAppTempDir();
final androidStudioDir = Directory(
p.join(tempDir.path, 'Android', 'Android Studio'),
)..createSync(recursive: true);
when(() => androidStudio.path).thenReturn(androidStudioDir.path);
final jbrDir = Directory(p.join(androidStudioDir.path, 'jbr'))
..createSync();
File(
p.join(tempDir.path, 'android', 'gradlew.bat'),
).createSync(recursive: true);
when(() => platform.isWindows).thenReturn(true);
when(() => platform.isMacOS).thenReturn(false);
when(() => platform.isLinux).thenReturn(false);
when(() => platform.environment).thenReturn({
'PROGRAMFILES': tempDir.path,
'PROGRAMFILES(X86)': tempDir.path,
final tempDir = setUpAppTempDir();
final androidStudioDir = Directory(
p.join(
tempDir.path,
'Applications',
'Android Studio.app',
'Contents',
),
)..createSync(recursive: true);
when(() => androidStudio.path).thenReturn(androidStudioDir.path);
jbrDir = Directory(
p.join(androidStudioDir.path, 'jbr', 'Contents', 'Home'),
)..createSync(recursive: true);
File(
p.join(tempDir.path, 'android', 'gradlew'),
).createSync(recursive: true);
when(() => platform.environment).thenReturn({'HOME': tempDir.path});
});
test('returns correct path', () async {
await expectLater(
runWithOverrides(() => java.home),
equals(jbrDir.path),
);
});
test('does not check JAVA_HOME or PATH', () {
runWithOverrides(() => java.home);
verifyNever(() => osInterface.which(any()));
verifyNever(() => platform.environment);
});
});
group('when on Windows', () {
late Directory jbrDir;
setUp(() {
when(() => platform.isWindows).thenReturn(true);
final tempDir = setUpAppTempDir();
final androidStudioDir = Directory(
p.join(tempDir.path, 'Android', 'Android Studio'),
)..createSync(recursive: true);
when(() => androidStudio.path).thenReturn(androidStudioDir.path);
jbrDir = Directory(p.join(androidStudioDir.path, 'jbr'))
..createSync();
File(
p.join(tempDir.path, 'android', 'gradlew.bat'),
).createSync(recursive: true);
when(() => platform.environment).thenReturn({
'PROGRAMFILES': tempDir.path,
'PROGRAMFILES(X86)': tempDir.path,
});
});
test('returns correct path', () async {
await expectLater(
runWithOverrides(() => java.home),
equals(jbrDir.path),
);
});
test('does not check JAVA_HOME or PATH', () {
runWithOverrides(() => java.home);
verifyNever(() => osInterface.which(any()));
verifyNever(() => platform.environment);
});
});
group('when on Linux', () {
setUp(() {
when(() => platform.isLinux).thenReturn(true);
final tempDir = setUpAppTempDir();
final androidStudioDir = Directory(
p.join(tempDir.path, '.AndroidStudio'),
)..createSync(recursive: true);
when(() => androidStudio.path).thenReturn(androidStudioDir.path);
jbrDir = Directory(p.join(androidStudioDir.path, 'jbr'))
..createSync(recursive: true);
File(
p.join(tempDir.path, 'android', 'gradlew'),
).createSync(recursive: true);
when(() => platform.environment).thenReturn({'HOME': tempDir.path});
});
test('returns correct path', () async {
await expectLater(
runWithOverrides(() => java.home),
equals(jbrDir.path),
);
});
});
await expectLater(
runWithOverrides(() => java.home),
equals(jbrDir.path),
);
});
test('returns correct path on MacOS', () async {
final tempDir = setUpAppTempDir();
final androidStudioDir = Directory(
p.join(
tempDir.path,
'Applications',
'Android Studio.app',
'Contents',
),
)..createSync(recursive: true);
when(() => androidStudio.path).thenReturn(androidStudioDir.path);
final jbrDir = Directory(
p.join(androidStudioDir.path, 'jbr', 'Contents', 'Home'),
)..createSync(recursive: true);
File(
p.join(tempDir.path, 'android', 'gradlew'),
).createSync(recursive: true);
when(() => platform.isWindows).thenReturn(false);
when(() => platform.isMacOS).thenReturn(true);
when(() => platform.isLinux).thenReturn(false);
when(() => platform.environment).thenReturn({'HOME': tempDir.path});
await expectLater(
runWithOverrides(() => java.home),
equals(jbrDir.path),
);
});
group('when Android Studio is not installed', () {
group('when JAVA_HOME is set', () {
const javaHome = r'C:\Program Files\Java\jdk-11.0.1';
setUp(() {
when(() => platform.environment)
.thenReturn({'JAVA_HOME': javaHome});
});
test('returns correct path on Linux', () async {
final tempDir = setUpAppTempDir();
final androidStudioDir = Directory(
p.join(tempDir.path, '.AndroidStudio'),
)..createSync(recursive: true);
when(() => androidStudio.path).thenReturn(androidStudioDir.path);
final jbrDir = Directory(p.join(androidStudioDir.path, 'jbr'))
..createSync(recursive: true);
File(
p.join(tempDir.path, 'android', 'gradlew'),
).createSync(recursive: true);
when(() => platform.isWindows).thenReturn(false);
when(() => platform.isMacOS).thenReturn(false);
when(() => platform.isLinux).thenReturn(true);
when(() => platform.environment).thenReturn({'HOME': tempDir.path});
await expectLater(
runWithOverrides(() => java.home),
equals(jbrDir.path),
);
test('returns value of JAVA_HOME', () {
expect(
runWithOverrides(() => java.home),
equals(javaHome),
);
});
test('does not check PATH', () {
runWithOverrides(() => java.home);
verifyNever(() => osInterface.which(any()));
});
});
group('when JAVA_HOME is not set', () {
group("when java is on the user's path", () {
const javaPath = '/path/to/java';
setUp(() {
when(() => osInterface.which('java')).thenReturn(javaPath);
});
test('returns path to java', () {
expect(
runWithOverrides(() => java.home),
equals('/path/to/java'),
);
});
});
group("when java is not on the user's path", () {
setUp(() {
when(() => osInterface.which('java')).thenReturn(null);
});
test('returns null', () {
expect(runWithOverrides(() => java.home), isNull);
});
});
});
});
});
});
@@ -17,6 +17,7 @@ import 'package:shorebird_cli/src/code_push_client_wrapper.dart';
import 'package:shorebird_cli/src/config/config.dart';
import 'package:shorebird_cli/src/doctor.dart';
import 'package:shorebird_cli/src/executables/executables.dart';
import 'package:shorebird_cli/src/os/os.dart';
import 'package:shorebird_cli/src/patch_diff_checker.dart';
import 'package:shorebird_cli/src/process.dart';
import 'package:shorebird_cli/src/shorebird_env.dart';
@@ -78,6 +79,9 @@ class MockJava extends Mock implements Java {}
class MockLogger extends Mock implements Logger {}
class MockOperatingSystemInterface extends Mock
implements OperatingSystemInterface {}
class MockPatchDiffChecker extends Mock implements PatchDiffChecker {}
class MockPlatform extends Mock implements Platform {}