mod common;
use assert_cmd::Command;
use common::TestHomeGuard;
use kopi::paths::install;
use predicates::prelude::*;
use regex::Regex;
use serial_test::serial;
use std::fs;
use std::path::Path;
fn get_test_command(kopi_home: &Path) -> Command {
let mut cmd = Command::cargo_bin("kopi").unwrap();
cmd.env("KOPI_HOME", kopi_home.to_str().unwrap());
cmd.env("HOME", kopi_home.parent().unwrap());
cmd
}
fn default_install_request() -> &'static str {
if cfg!(all(target_os = "macos", target_arch = "aarch64")) {
"11"
} else {
"8"
}
}
fn resolve_jdk_path(jdk_dir: &Path, relative_path: &str) -> std::path::PathBuf {
if cfg!(target_os = "macos") {
let contents_home = install::bundle_java_home(jdk_dir);
if contents_home.exists() {
contents_home.join(relative_path)
} else {
jdk_dir.join(relative_path)
}
} else {
jdk_dir.join(relative_path)
}
}
fn resolve_jdk_path_with_fallback(jdk_dir: &Path, relative_path: &str) -> std::path::PathBuf {
if cfg!(target_os = "macos") {
let contents_home = install::bundle_java_home(jdk_dir);
if contents_home.exists() {
let path_in_contents = contents_home.join(relative_path);
if path_in_contents.exists() {
path_in_contents
} else {
jdk_dir.join(relative_path)
}
} else {
jdk_dir.join(relative_path)
}
} else {
jdk_dir.join(relative_path)
}
}
#[test]
#[serial]
#[cfg_attr(not(feature = "integration_tests"), ignore)]
fn test_install_basic_version() {
let test_home = TestHomeGuard::new();
test_home.setup_kopi_structure();
let kopi_home = test_home.kopi_home();
let mut cmd = get_test_command(&kopi_home);
cmd.arg("cache").arg("refresh").assert().success();
let mut cmd = get_test_command(&kopi_home);
cmd.arg("install")
.arg("21")
.arg("--dry-run")
.assert()
.success()
.stdout(predicate::str::contains("Would install"));
}
#[test]
#[serial]
#[cfg_attr(not(feature = "integration_tests"), ignore)]
fn test_install_with_distribution() {
let test_home = TestHomeGuard::new();
test_home.setup_kopi_structure();
let kopi_home = test_home.kopi_home();
let mut cmd = get_test_command(&kopi_home);
cmd.arg("cache").arg("refresh").assert().success();
let mut cmd = get_test_command(&kopi_home);
cmd.arg("install")
.arg("corretto@17")
.arg("--dry-run")
.assert()
.success()
.stdout(predicate::str::contains("Would install"));
}
#[test]
#[serial]
fn test_install_invalid_version() {
let test_home = TestHomeGuard::new();
test_home.setup_kopi_structure();
let kopi_home = test_home.kopi_home();
let mut cmd = get_test_command(&kopi_home);
cmd.arg("install")
.arg("999.999.999")
.assert()
.failure()
.stderr(predicate::str::contains("Error:"))
.stderr(predicate::str::contains("Invalid version format"));
}
#[test]
#[serial]
fn test_install_invalid_format() {
let test_home = TestHomeGuard::new();
test_home.setup_kopi_structure();
let kopi_home = test_home.kopi_home();
let mut cmd = get_test_command(&kopi_home);
cmd.arg("install")
.arg("invalid@#$%")
.assert()
.failure()
.stderr(predicate::str::contains("Invalid version format"))
.stderr(predicate::str::contains(
"Suggestion: Version format should be:",
));
}
#[test]
#[serial]
#[cfg_attr(not(feature = "integration_tests"), ignore)]
fn test_install_already_exists() {
let test_home = TestHomeGuard::new();
test_home.setup_kopi_structure();
let kopi_home = test_home.kopi_home();
let mut cmd = get_test_command(&kopi_home);
cmd.arg("cache").arg("refresh").assert().success();
let mut cmd = get_test_command(&kopi_home);
cmd.arg("install").arg("21").assert().success();
let mut cmd = get_test_command(&kopi_home);
cmd.arg("install")
.arg("21")
.assert()
.failure()
.stderr(predicate::str::contains("already installed"))
.stderr(predicate::str::contains("--force"));
}
#[test]
#[serial]
#[cfg_attr(not(feature = "integration_tests"), ignore)]
fn test_install_force_reinstall() {
let test_home = TestHomeGuard::new();
test_home.setup_kopi_structure();
let kopi_home = test_home.kopi_home();
let install_dir = kopi_home.join("jdks").join("temurin-21.0.1");
fs::create_dir_all(&install_dir).unwrap();
let mut cmd = get_test_command(&kopi_home);
cmd.arg("cache").arg("refresh").assert().success();
let mut cmd = get_test_command(&kopi_home);
cmd.arg("install")
.arg("21")
.arg("--force")
.arg("--dry-run")
.assert()
.success()
.stdout(predicate::str::contains("Would install"));
}
#[test]
#[serial]
fn test_install_with_timeout() {
let test_home = TestHomeGuard::new();
test_home.setup_kopi_structure();
let kopi_home = test_home.kopi_home();
let mut cmd = get_test_command(&kopi_home);
cmd.arg("install")
.arg("21")
.arg("--timeout")
.arg("1") .arg("--dry-run")
.assert();
}
#[test]
#[serial]
#[cfg_attr(not(feature = "integration_tests"), ignore)]
fn test_install_no_progress() {
let test_home = TestHomeGuard::new();
test_home.setup_kopi_structure();
let kopi_home = test_home.kopi_home();
let mut cmd = get_test_command(&kopi_home);
cmd.arg("cache").arg("refresh").assert().success();
let mut cmd = get_test_command(&kopi_home);
cmd.arg("install")
.arg("21")
.arg("--no-progress")
.arg("--dry-run")
.assert()
.success();
}
#[test]
#[serial]
fn test_install_verbose_output() {
let test_home = TestHomeGuard::new();
test_home.setup_kopi_structure();
let kopi_home = test_home.kopi_home();
let mut cmd = get_test_command(&kopi_home);
cmd.arg("-vv") .arg("install")
.arg("21")
.arg("--dry-run")
.assert();
}
#[test]
#[serial]
fn test_install_without_cache() {
let test_home = TestHomeGuard::new();
test_home.setup_kopi_structure();
let kopi_home = test_home.kopi_home();
let mut cmd = get_test_command(&kopi_home);
cmd.arg("install")
.arg("21")
.arg("--dry-run") .assert()
.success()
.stdout(predicate::str::contains("Would install"));
}
#[test]
#[serial]
#[cfg(all(unix, not(target_os = "macos")))]
#[cfg_attr(not(feature = "integration_tests"), ignore)]
fn test_install_permission_denied() {
let test_home = TestHomeGuard::new();
test_home.setup_kopi_structure();
let kopi_home = test_home.kopi_home();
let jdks_dir = kopi_home.join("jdks");
fs::create_dir_all(&jdks_dir).unwrap();
use std::os::unix::fs::PermissionsExt;
let mut perms = fs::metadata(&jdks_dir).unwrap().permissions();
perms.set_mode(0o444); fs::set_permissions(&jdks_dir, perms).unwrap();
let mut cmd = get_test_command(&kopi_home);
cmd.arg("cache").arg("refresh").assert().success();
let mut cmd = get_test_command(&kopi_home);
cmd.arg("install")
.arg("21")
.assert()
.failure()
.stderr(predicate::str::contains("Permission denied"))
.stderr(predicate::str::contains("sudo").or(predicate::str::contains("Administrator")));
let mut perms = fs::metadata(&jdks_dir).unwrap().permissions();
perms.set_mode(0o755);
fs::set_permissions(&jdks_dir, perms).unwrap();
}
#[test]
#[serial]
fn test_install_with_javafx() {
let test_home = TestHomeGuard::new();
test_home.setup_kopi_structure();
let kopi_home = test_home.kopi_home();
let mut cmd = get_test_command(&kopi_home);
cmd.arg("cache").arg("refresh").assert().success();
let mut cmd = get_test_command(&kopi_home);
cmd.arg("install")
.arg("liberica@17+fx")
.arg("--dry-run")
.assert();
}
#[test]
#[serial]
#[cfg_attr(not(feature = "integration_tests"), ignore)]
fn test_concurrent_installs() {
use std::thread;
let test_home = TestHomeGuard::new();
test_home.setup_kopi_structure();
let kopi_home = test_home.kopi_home();
let mut cmd = get_test_command(&kopi_home);
cmd.arg("cache").arg("refresh").assert().success();
let kopi_home_1 = kopi_home.clone();
let handle1 = thread::spawn(move || {
let mut cmd = get_test_command(&kopi_home_1);
cmd.arg("install")
.arg("17")
.arg("--dry-run")
.assert()
.success();
});
let kopi_home_2 = kopi_home.clone();
let handle2 = thread::spawn(move || {
let mut cmd = get_test_command(&kopi_home_2);
cmd.arg("install")
.arg("21")
.arg("--dry-run")
.assert()
.success();
});
handle1.join().unwrap();
handle2.join().unwrap();
}
#[test]
#[serial]
fn test_install_specific_version() {
let test_home = TestHomeGuard::new();
test_home.setup_kopi_structure();
let kopi_home = test_home.kopi_home();
let mut cmd = get_test_command(&kopi_home);
cmd.arg("cache").arg("refresh").assert().success();
let mut cmd = get_test_command(&kopi_home);
cmd.arg("install").arg("17.0.9").arg("--dry-run").assert();
}
#[test]
#[serial]
#[cfg_attr(not(feature = "integration_tests"), ignore)]
fn test_install_lts_version() {
let test_home = TestHomeGuard::new();
test_home.setup_kopi_structure();
let kopi_home = test_home.kopi_home();
let mut cmd = get_test_command(&kopi_home);
cmd.arg("cache").arg("refresh").assert().success();
let mut cmd = get_test_command(&kopi_home);
cmd.arg("install")
.arg("21") .arg("--dry-run")
.assert()
.success();
}
#[test]
#[serial]
fn test_exit_codes() {
let test_home = TestHomeGuard::new();
test_home.setup_kopi_structure();
let kopi_home = test_home.kopi_home();
let output = get_test_command(&kopi_home)
.arg("install")
.arg("@@@invalid")
.output()
.unwrap();
assert_eq!(output.status.code(), Some(2));
}
#[test]
#[serial]
#[cfg_attr(not(feature = "integration_tests"), ignore)]
fn test_actual_download() {
let test_home = TestHomeGuard::new();
test_home.setup_kopi_structure();
let kopi_home = test_home.kopi_home();
let mut cmd = get_test_command(&kopi_home);
cmd.arg("cache").arg("refresh").assert().success();
let mut cmd = get_test_command(&kopi_home);
cmd.arg("install")
.arg("11") .arg("--timeout")
.arg("300")
.timeout(std::time::Duration::from_secs(600))
.assert();
}
#[test]
#[serial]
#[cfg_attr(not(feature = "integration_tests"), ignore)]
fn test_install_and_verify_files() {
let test_home = TestHomeGuard::new();
test_home.setup_kopi_structure();
let kopi_home = test_home.kopi_home();
let mut cmd = get_test_command(&kopi_home);
cmd.arg("cache").arg("refresh").assert().success();
let mut cmd = get_test_command(&kopi_home);
let output = cmd
.arg("install")
.arg("11")
.arg("--timeout")
.arg("300")
.timeout(std::time::Duration::from_secs(600))
.output()
.unwrap();
eprintln!(
"Install stdout: {}",
String::from_utf8_lossy(&output.stdout)
);
eprintln!(
"Install stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
eprintln!("Install status: {:?}", output.status);
if !output.status.success() {
eprintln!(
"Install failed: {}",
String::from_utf8_lossy(&output.stderr)
);
panic!("Installation failed");
}
let stdout = String::from_utf8_lossy(&output.stdout);
let version_pattern =
Regex::new(r"Successfully installed .* to .*[/\\]\.kopi[/\\]jdks[/\\](\S+)").unwrap();
let installed_version = version_pattern
.captures(&stdout)
.and_then(|caps| caps.get(1))
.map(|m| m.as_str());
let installed_version = match installed_version {
Some(v) => v.to_string(),
None => {
let jdks_dir = kopi_home.join("jdks");
if jdks_dir.exists() {
let entries: Vec<_> = fs::read_dir(&jdks_dir)
.unwrap()
.filter_map(|e| e.ok())
.filter(|e| e.path().is_dir())
.filter(|e| !e.file_name().to_string_lossy().starts_with('.'))
.collect();
eprintln!("JDKs directory contents:");
for entry in &entries {
eprintln!(" - {:?}", entry.file_name());
}
if let Some(entry) = entries.first() {
entry.file_name().to_string_lossy().to_string()
} else {
panic!("No JDK directories found after installation");
}
} else {
panic!("JDKs directory doesn't exist");
}
}
};
let jdk_dir = kopi_home.join("jdks").join(&installed_version);
assert!(
jdk_dir.exists(),
"JDK directory should exist at {jdk_dir:?}"
);
assert!(jdk_dir.is_dir(), "JDK path should be a directory");
let bin_dir = resolve_jdk_path(&jdk_dir, "bin");
assert!(
bin_dir.exists(),
"bin directory should exist at {bin_dir:?}"
);
assert!(bin_dir.is_dir(), "bin should be a directory");
eprintln!("Files in bin directory:");
if let Ok(entries) = fs::read_dir(&bin_dir) {
for entry in entries.filter_map(|e| e.ok()) {
eprintln!(" - {:?}", entry.file_name());
}
}
let exe_ext = if cfg!(windows) { ".exe" } else { "" };
let java_exe = bin_dir.join(format!("java{exe_ext}"));
assert!(
java_exe.exists(),
"Java executable should exist at {java_exe:?}"
);
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let metadata = fs::metadata(&java_exe).unwrap();
let mode = metadata.permissions().mode();
assert!(mode & 0o111 != 0, "java should be executable");
}
let jdk_executables = vec!["javac", "jar", "javadoc"];
let mut is_jdk = false;
for exe in &jdk_executables {
let exe_path = bin_dir.join(format!("{exe}{exe_ext}"));
if exe_path.exists() {
is_jdk = true;
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let metadata = fs::metadata(&exe_path).unwrap();
let mode = metadata.permissions().mode();
assert!(mode & 0o111 != 0, "{exe} should be executable");
}
}
}
eprintln!("Package type: {}", if is_jdk { "JDK" } else { "JRE" });
let lib_dir = resolve_jdk_path(&jdk_dir, "lib");
assert!(
lib_dir.exists(),
"lib directory should exist at {lib_dir:?}"
);
let release_file = resolve_jdk_path_with_fallback(&jdk_dir, "release");
assert!(
release_file.exists(),
"release file should exist at {release_file:?}"
);
let release_content = fs::read_to_string(&release_file).unwrap();
assert!(
release_content.contains("JAVA_VERSION="),
"release file should contain JAVA_VERSION"
);
}
#[test]
#[serial]
#[cfg_attr(not(feature = "integration_tests"), ignore)]
fn test_install_creates_shims() {
let test_home = TestHomeGuard::new();
test_home.setup_kopi_structure();
let kopi_home = test_home.kopi_home();
let mut cmd = get_test_command(&kopi_home);
cmd.arg("setup").assert().success();
let mut cmd = get_test_command(&kopi_home);
cmd.arg("cache").arg("refresh").assert().success();
let mut cmd = get_test_command(&kopi_home);
cmd.arg("install")
.arg(default_install_request())
.arg("--timeout")
.arg("300")
.timeout(std::time::Duration::from_secs(600))
.assert()
.success();
let shims_dir = kopi_home.join("shims");
assert!(shims_dir.exists(), "Shims directory should exist");
assert!(shims_dir.is_dir(), "Shims directory should be a directory");
let exe_ext = if cfg!(windows) { ".exe" } else { "" };
let default_shims = vec!["java", "javac", "javadoc", "jar", "jshell"];
for shim in &default_shims {
let shim_path = shims_dir.join(format!("{shim}{exe_ext}"));
if shim_path.exists() {
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let metadata = fs::metadata(&shim_path).unwrap();
let mode = metadata.permissions().mode();
assert!(mode & 0o111 != 0, "Shim {shim} should be executable");
}
} else if *shim != "jshell" {
panic!("Shim {shim} should exist at {shim_path:?}");
}
}
}
#[test]
#[serial]
#[cfg_attr(not(feature = "integration_tests"), ignore)]
fn test_install_specific_distribution() {
let test_home = TestHomeGuard::new();
test_home.setup_kopi_structure();
let kopi_home = test_home.kopi_home();
let mut cmd = get_test_command(&kopi_home);
cmd.arg("cache").arg("refresh").assert().success();
let mut cmd = get_test_command(&kopi_home);
let output = cmd
.arg("install")
.arg("corretto@8")
.arg("--timeout")
.arg("300")
.timeout(std::time::Duration::from_secs(600))
.output()
.unwrap();
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
let stdout = String::from_utf8_lossy(&output.stdout);
panic!("Installation failed.\nSTDOUT:\n{stdout}\nSTDERR:\n{stderr}");
}
let jdks_dir = kopi_home.join("jdks");
let entries: Vec<_> = fs::read_dir(&jdks_dir)
.unwrap()
.filter_map(|e| e.ok())
.filter(|e| e.path().is_dir())
.collect();
let corretto_installed = entries
.iter()
.any(|e| e.file_name().to_string_lossy().contains("corretto"));
assert!(corretto_installed, "Corretto JDK should be installed");
}
#[test]
#[serial]
#[cfg_attr(not(feature = "integration_tests"), ignore)]
fn test_install_verifies_disk_space() {
let test_home = TestHomeGuard::new();
test_home.setup_kopi_structure();
let kopi_home = test_home.kopi_home();
let mut cmd = get_test_command(&kopi_home);
cmd.arg("cache").arg("refresh").assert().success();
let mut cmd = get_test_command(&kopi_home);
cmd.arg("install")
.arg(default_install_request())
.arg("--timeout")
.arg("300")
.arg("-v") .timeout(std::time::Duration::from_secs(600))
.assert()
.success();
}
#[test]
#[serial]
#[ignore] fn test_concurrent_same_version_install() {
use std::thread;
let test_home = TestHomeGuard::new();
test_home.setup_kopi_structure();
let kopi_home = test_home.kopi_home();
let mut cmd = get_test_command(&kopi_home);
cmd.arg("cache").arg("refresh").assert().success();
let kopi_home_1 = kopi_home.clone();
let handle1 = thread::spawn(move || {
let mut cmd = get_test_command(&kopi_home_1);
cmd.arg("install")
.arg(default_install_request())
.arg("--timeout")
.arg("300")
.timeout(std::time::Duration::from_secs(600))
.output()
.unwrap()
});
let kopi_home_2 = kopi_home.clone();
let handle2 = thread::spawn(move || {
let mut cmd = get_test_command(&kopi_home_2);
cmd.arg("install")
.arg(default_install_request())
.arg("--timeout")
.arg("300")
.timeout(std::time::Duration::from_secs(600))
.output()
.unwrap()
});
let result1 = handle1.join().unwrap();
let result2 = handle2.join().unwrap();
assert!(
result1.status.success() || result2.status.success(),
"At least one installation should succeed"
);
if !result1.status.success() {
let stderr = String::from_utf8_lossy(&result1.stderr);
assert!(
stderr.contains("already installed")
|| stderr.contains("already exists")
|| stderr.contains("File exists")
|| stderr.contains("Cannot create a file when that file already exists")
|| stderr.contains("failed to rename")
|| stderr.contains("rename")
|| stderr.contains("Directory not empty")
|| stderr.contains("os error 145"), "Failure should be due to existing installation, but got: {stderr}"
);
}
if !result2.status.success() {
let stderr = String::from_utf8_lossy(&result2.stderr);
assert!(
stderr.contains("already installed")
|| stderr.contains("already exists")
|| stderr.contains("File exists")
|| stderr.contains("Cannot create a file when that file already exists")
|| stderr.contains("failed to rename")
|| stderr.contains("rename")
|| stderr.contains("Directory not empty")
|| stderr.contains("os error 145"), "Failure should be due to existing installation, but got: {stderr}"
);
}
}
#[test]
#[serial]
#[cfg_attr(not(feature = "integration_tests"), ignore)]
fn test_install_cleanup_on_failure() {
let test_home = TestHomeGuard::new();
test_home.setup_kopi_structure();
let kopi_home = test_home.kopi_home();
let tmp_dir = kopi_home.join("jdks").join(".tmp");
fs::create_dir_all(&tmp_dir).unwrap();
let mut cmd = get_test_command(&kopi_home);
let _ = cmd
.arg("install")
.arg("21") .arg("--timeout")
.arg("1") .timeout(std::time::Duration::from_secs(10))
.output();
if tmp_dir.exists() {
let entries: Vec<_> = fs::read_dir(&tmp_dir)
.unwrap()
.filter_map(|e| e.ok())
.collect();
for entry in entries {
let name = entry.file_name();
if name.to_string_lossy().starts_with("install-") {
panic!("Found leftover installation directory: {name:?}");
}
}
}
}
#[test]
#[serial]
#[cfg_attr(not(feature = "integration_tests"), ignore)]
fn test_simple_install_debug() {
let test_home = TestHomeGuard::new();
test_home.setup_kopi_structure();
let kopi_home = test_home.kopi_home();
eprintln!("KOPI_HOME: {kopi_home:?}");
let mut cmd = get_test_command(&kopi_home);
let output = cmd
.arg("-vv") .arg("cache")
.arg("refresh")
.output()
.unwrap();
eprintln!(
"Cache refresh stdout: {}",
String::from_utf8_lossy(&output.stdout)
);
eprintln!(
"Cache refresh stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
if !output.status.success() {
panic!("Failed to refresh cache");
}
let mut cmd = get_test_command(&kopi_home);
let output = cmd
.arg("-vv") .arg("install")
.arg(default_install_request())
.arg("--timeout")
.arg("300")
.timeout(std::time::Duration::from_secs(600))
.output()
.unwrap();
eprintln!(
"Install stdout: {}",
String::from_utf8_lossy(&output.stdout)
);
eprintln!(
"Install stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
eprintln!("Install status: {:?}", output.status);
let jdks_dir = kopi_home.join("jdks");
if jdks_dir.exists() {
eprintln!("JDKs directory exists");
for entry in fs::read_dir(&jdks_dir).unwrap().flatten() {
eprintln!(" Found: {:?}", entry.path());
}
} else {
eprintln!("JDKs directory does not exist!");
}
assert!(output.status.success(), "Installation should succeed");
}
#[test]
#[serial]
#[cfg_attr(not(feature = "integration_tests"), ignore)]
fn test_install_jre_package() {
let test_home = TestHomeGuard::new();
test_home.setup_kopi_structure();
let kopi_home = test_home.kopi_home();
let mut cmd = get_test_command(&kopi_home);
cmd.arg("cache").arg("refresh").assert().success();
let mut cmd = get_test_command(&kopi_home);
let output = cmd
.arg("install")
.arg("jre@11")
.arg("--timeout")
.arg("300")
.timeout(std::time::Duration::from_secs(600))
.output()
.unwrap();
eprintln!(
"JRE install stdout: {}",
String::from_utf8_lossy(&output.stdout)
);
eprintln!(
"JRE install stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
eprintln!("JRE install status: {:?}", output.status);
assert!(output.status.success(), "JRE installation should succeed");
let stdout = String::from_utf8_lossy(&output.stdout);
let version_pattern =
Regex::new(r"Successfully installed .* to .*[/\\]\.kopi[/\\]jdks[/\\](\S+)").unwrap();
let installed_version = version_pattern
.captures(&stdout)
.and_then(|caps| caps.get(1))
.map(|m| m.as_str())
.expect("Should find installed version in output");
let jdk_dir = kopi_home.join("jdks").join(installed_version);
assert!(
jdk_dir.exists(),
"JRE directory should exist at {jdk_dir:?}"
);
let bin_dir = resolve_jdk_path(&jdk_dir, "bin");
assert!(
bin_dir.exists(),
"bin directory should exist at {bin_dir:?}"
);
eprintln!("Files in JRE bin directory:");
if let Ok(entries) = fs::read_dir(&bin_dir) {
for entry in entries.filter_map(|e| e.ok()) {
eprintln!(" - {:?}", entry.file_name());
}
}
let exe_ext = if cfg!(windows) { ".exe" } else { "" };
let java_path = bin_dir.join(format!("java{exe_ext}"));
assert!(
java_path.exists(),
"JRE should contain java executable at {java_path:?}"
);
let javac_path = bin_dir.join(format!("javac{exe_ext}"));
assert!(
!javac_path.exists(),
"JRE should NOT contain javac compiler at {javac_path:?}"
);
let jar_path = bin_dir.join(format!("jar{exe_ext}"));
assert!(
!jar_path.exists(),
"JRE should NOT contain jar tool at {jar_path:?}"
);
let javadoc_path = bin_dir.join(format!("javadoc{exe_ext}"));
assert!(
!javadoc_path.exists(),
"JRE should NOT contain javadoc tool at {javadoc_path:?}"
);
let keytool_path = bin_dir.join(format!("keytool{exe_ext}"));
assert!(
keytool_path.exists(),
"JRE should contain keytool at {keytool_path:?}"
);
let lib_dir = resolve_jdk_path(&jdk_dir, "lib");
assert!(
lib_dir.exists(),
"lib directory should exist at {lib_dir:?}"
);
let release_file = resolve_jdk_path_with_fallback(&jdk_dir, "release");
assert!(
release_file.exists(),
"release file should exist at {release_file:?}"
);
let bin_shim_dir = kopi_home.join("bin");
if bin_shim_dir.exists() {
let exe_ext = if cfg!(windows) { ".exe" } else { "" };
let java_shim = bin_shim_dir.join(format!("java{exe_ext}"));
eprintln!("Checking for java shim at: {java_shim:?}");
if java_shim.exists() {
eprintln!("java shim found");
} else {
eprintln!(
"java shim not found - this might be expected if auto_create_shims is disabled"
);
}
} else {
eprintln!("bin directory does not exist - shims might be disabled in test config");
}
}
#[test]
#[serial]
#[cfg_attr(not(feature = "integration_tests"), ignore)]
fn test_install_graalvm() {
let test_home = TestHomeGuard::new();
test_home.setup_kopi_structure();
let kopi_home = test_home.kopi_home();
let mut cmd = get_test_command(&kopi_home);
cmd.arg("cache").arg("refresh").assert().success();
let mut cmd = get_test_command(&kopi_home);
let output = cmd
.arg("install")
.arg("graalvm@21")
.arg("--timeout")
.arg("300")
.timeout(std::time::Duration::from_secs(600))
.output()
.unwrap();
eprintln!(
"GraalVM install stdout: {}",
String::from_utf8_lossy(&output.stdout)
);
eprintln!(
"GraalVM install stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
eprintln!("GraalVM install status: {:?}", output.status);
assert!(
output.status.success(),
"GraalVM installation should succeed"
);
let stdout = String::from_utf8_lossy(&output.stdout);
let version_pattern =
Regex::new(r"Successfully installed .* to .*[/\\]\.kopi[/\\]jdks[/\\](\S+)").unwrap();
let installed_version = version_pattern
.captures(&stdout)
.and_then(|caps| caps.get(1))
.map(|m| m.as_str())
.expect("Should find installed version in output");
let jdk_dir = kopi_home.join("jdks").join(installed_version);
assert!(jdk_dir.exists(), "JDK directory should exist");
let license_file =
resolve_jdk_path_with_fallback(&jdk_dir, "license-information-user-manual.zip");
assert!(
license_file.exists(),
"license-information-user-manual.zip should be extracted at {license_file:?}"
);
let metadata = fs::metadata(&license_file).unwrap();
assert!(
metadata.len() > 0,
"license-information-user-manual.zip should not be empty"
);
let bin_dir = resolve_jdk_path(&jdk_dir, "bin");
assert!(
bin_dir.exists(),
"bin directory should exist at {bin_dir:?}"
);
let exe_ext = if cfg!(windows) { ".exe" } else { "" };
let java_exe = bin_dir.join(format!("java{exe_ext}"));
assert!(java_exe.exists(), "java executable should exist");
let native_image = bin_dir.join(format!("native-image{exe_ext}"));
if native_image.exists() {
eprintln!("native-image tool found in GraalVM");
} else {
eprintln!(
"Note: native-image tool not found in GraalVM Community Edition (this is expected)"
)
}
}