#[cfg(test)]
mod tests {
use super::super::gradle::{
KotlinAndroidBuildGradleInputs, render_build_gradle_kotlin_android, render_settings_gradle_kotlin_android,
};
#[test]
fn build_gradle_kotlin_android_includes_jackson_module_kotlin() {
let output = render_build_gradle_kotlin_android(&KotlinAndroidBuildGradleInputs {
kotlin_pkg_id: "dev.sample_crate.samplellm.android",
maven_coordinate: "dev.sample_crate:demo-client-android:1.0.0",
dep_mode: crate::e2e::config::DependencyMode::Local,
jni_lib_name: "demo_client_jni",
jni_crate_path: "../../crates/demo-client-jni",
e2e_env: &std::collections::BTreeMap::new(),
capsule_types: &std::collections::HashMap::new(),
test_documents_path: "../../test_documents",
});
assert!(
output.contains("jackson-module-kotlin"),
"build.gradle.kts must depend on jackson-module-kotlin, got:\n{output}"
);
}
#[test]
fn build_gradle_kotlin_android_pins_jackson_annotations_to_its_own_version_scheme() {
let output = render_build_gradle_kotlin_android(&KotlinAndroidBuildGradleInputs {
kotlin_pkg_id: "dev.sample_crate.samplellm.android",
maven_coordinate: "dev.sample_crate:demo-client-android:1.0.0",
dep_mode: crate::e2e::config::DependencyMode::Local,
jni_lib_name: "demo_client_jni",
jni_crate_path: "../../crates/demo-client-jni",
e2e_env: &std::collections::BTreeMap::new(),
capsule_types: &std::collections::HashMap::new(),
test_documents_path: "../../test_documents",
});
let annotations_line = output
.lines()
.find(|line| line.contains("jackson-annotations"))
.expect("jackson-annotations dependency line must be emitted");
assert!(
annotations_line.contains(&format!(
"jackson-annotations:{}\"",
crate::core::template_versions::maven::JACKSON_ANNOTATIONS
)),
"jackson-annotations must use JACKSON_ANNOTATIONS's version scheme, got:\n{annotations_line}"
);
assert!(
!annotations_line.contains(crate::core::template_versions::maven::JACKSON_E2E),
"jackson-annotations must not reuse JACKSON_E2E's patch-versioned scheme, got:\n{annotations_line}"
);
}
#[test]
fn build_gradle_kotlin_android_forwards_e2e_env_to_test_worker() {
let mut env = std::collections::BTreeMap::new();
env.insert("MY_SERVICE_ALLOW_PRIVATE_NETWORK".to_string(), "true".to_string());
for dep_mode in [
crate::e2e::config::DependencyMode::Registry,
crate::e2e::config::DependencyMode::Local,
] {
let output = render_build_gradle_kotlin_android(&KotlinAndroidBuildGradleInputs {
kotlin_pkg_id: "dev.sample_crate",
maven_coordinate: "dev.sample_crate:sample_crate-android:5.0.0-rc.1",
dep_mode,
jni_lib_name: "sample_crate_jni",
jni_crate_path: "../../crates/sample_crate-jni",
e2e_env: &env,
capsule_types: &std::collections::HashMap::new(),
test_documents_path: "../../test_documents",
});
assert!(
output.contains(r#"environment("MY_SERVICE_ALLOW_PRIVATE_NETWORK", "true")"#),
"build.gradle.kts ({dep_mode:?}) must forward e2e env vars to the test worker, got:\n{output}"
);
}
}
#[test]
fn build_gradle_kotlin_android_always_includes_junit_platform_launcher() {
for dep_mode in [
crate::e2e::config::DependencyMode::Registry,
crate::e2e::config::DependencyMode::Local,
] {
let output = render_build_gradle_kotlin_android(&KotlinAndroidBuildGradleInputs {
kotlin_pkg_id: "dev.sample_crate",
maven_coordinate: "dev.sample_crate:sample_crate-android:5.0.0-rc.1",
dep_mode,
jni_lib_name: "sample_crate_jni",
jni_crate_path: "../../crates/sample_crate-jni",
e2e_env: &std::collections::BTreeMap::new(),
capsule_types: &std::collections::HashMap::new(),
test_documents_path: "../../test_documents",
});
assert!(
output.contains(r#"testImplementation("org.junit.platform:junit-platform-launcher:"#),
"build.gradle.kts ({dep_mode:?}) must declare junit-platform-launcher as testImplementation, got:\n{output}"
);
assert!(
output.contains("useJUnitPlatform()"),
"build.gradle.kts ({dep_mode:?}) must call useJUnitPlatform(), got:\n{output}"
);
}
}
#[test]
fn build_gradle_kotlin_android_local_mode_declares_capsule_test_dependency() {
let mut capsule_types = std::collections::HashMap::new();
capsule_types.insert(
"Language".to_string(),
crate::core::config::HostCapsuleTypeConfig {
host_type: "io.github.treesitter.ktreesitter.Language".to_string(),
package: "io.github.tree-sitter:ktreesitter".to_string(),
package_version: "0.25.0".to_string(),
construct_expr: "io.github.treesitter.ktreesitter.Language({ptr})".to_string(),
..Default::default()
},
);
let output = render_build_gradle_kotlin_android(&KotlinAndroidBuildGradleInputs {
kotlin_pkg_id: "dev.sample_crate",
maven_coordinate: "dev.sample_crate:sample_crate-android:5.0.0-rc.1",
dep_mode: crate::e2e::config::DependencyMode::Local,
jni_lib_name: "sample_crate_jni",
jni_crate_path: "../../crates/sample_crate-jni",
e2e_env: &std::collections::BTreeMap::new(),
capsule_types: &capsule_types,
test_documents_path: "../../test_documents",
});
assert!(
output.contains(r#"testImplementation("io.github.tree-sitter:ktreesitter:0.25.0")"#),
"local-mode build.gradle.kts must declare a testImplementation for the capsule host package, got:\n{output}"
);
}
#[test]
fn build_gradle_kotlin_android_registry_mode_omits_capsule_test_dependency() {
let mut capsule_types = std::collections::HashMap::new();
capsule_types.insert(
"Language".to_string(),
crate::core::config::HostCapsuleTypeConfig {
host_type: "io.github.treesitter.ktreesitter.Language".to_string(),
package: "io.github.tree-sitter:ktreesitter".to_string(),
package_version: "0.25.0".to_string(),
construct_expr: "io.github.treesitter.ktreesitter.Language({ptr})".to_string(),
..Default::default()
},
);
let output = render_build_gradle_kotlin_android(&KotlinAndroidBuildGradleInputs {
kotlin_pkg_id: "dev.sample_crate",
maven_coordinate: "dev.sample_crate:sample_crate-android:5.0.0-rc.1",
dep_mode: crate::e2e::config::DependencyMode::Registry,
jni_lib_name: "sample_crate_jni",
jni_crate_path: "../../crates/sample_crate-jni",
e2e_env: &std::collections::BTreeMap::new(),
capsule_types: &capsule_types,
test_documents_path: "../../test_documents",
});
assert!(
!output.contains("io.github.tree-sitter:ktreesitter"),
"registry-mode build.gradle.kts must not duplicate the capsule host package testImplementation, got:\n{output}"
);
}
#[test]
fn build_gradle_kotlin_android_registry_mode_emits_full_maven_coordinate() {
let output = render_build_gradle_kotlin_android(&KotlinAndroidBuildGradleInputs {
kotlin_pkg_id: "dev.sample_crate",
maven_coordinate: "dev.sample_crate:sample_crate-android:5.0.0-rc.1",
dep_mode: crate::e2e::config::DependencyMode::Registry,
jni_lib_name: "sample_crate_jni",
jni_crate_path: "../../crates/sample_crate-jni",
e2e_env: &std::collections::BTreeMap::new(),
capsule_types: &std::collections::HashMap::new(),
test_documents_path: "../../test_documents",
});
assert!(
output.contains(r#"implementation("dev.sample_crate:sample_crate-android:5.0.0-rc.1")"#),
"build.gradle.kts must emit full Maven coordinate with groupId:artifactId:version, got:\n{output}"
);
}
#[test]
fn settings_gradle_kotlin_android_declares_plugin_repositories() {
let output = render_settings_gradle_kotlin_android("demo-client");
assert!(
output.contains("pluginManagement"),
"settings.gradle.kts must declare pluginManagement block, got:\n{output}"
);
assert!(
output.contains("google()"),
"pluginManagement repositories must include google(), got:\n{output}"
);
assert!(
output.contains("gradlePluginPortal()"),
"pluginManagement repositories must include gradlePluginPortal(), got:\n{output}"
);
assert!(
output.contains("rootProject.name = \"demo-client-e2e\""),
"rootProject.name must be derived from pkg_name, got:\n{output}"
);
}
#[test]
fn settings_gradle_kotlin_android_strips_maven_group_from_project_name() {
let output = render_settings_gradle_kotlin_android("dev.sample_crate:demo-markup-android");
assert!(
output.contains("rootProject.name = \"demo-markup-android-e2e\""),
"rootProject.name must strip Maven group prefix, got:\n{output}"
);
let project_name_line = output
.lines()
.find(|line| line.starts_with("rootProject.name"))
.expect("rootProject.name line must be emitted");
assert!(
!project_name_line.contains(':'),
"rootProject.name line must not contain Gradle-reserved ':', got:\n{project_name_line}"
);
}
#[test]
fn build_gradle_kotlin_android_registry_mode_includes_aar_verification_task() {
let output = render_build_gradle_kotlin_android(&KotlinAndroidBuildGradleInputs {
kotlin_pkg_id: "dev.sample_crate",
maven_coordinate: "dev.sample_crate:sample_crate-android:5.0.0-rc.1",
dep_mode: crate::e2e::config::DependencyMode::Registry,
jni_lib_name: "sample_crate_jni",
jni_crate_path: "../../crates/sample_crate-jni",
e2e_env: &std::collections::BTreeMap::new(),
capsule_types: &std::collections::HashMap::new(),
test_documents_path: "../../test_documents",
});
assert!(
output.contains("verifyAarPublished"),
"registry-mode build.gradle.kts must include verifyAarPublished task, got:\n{output}"
);
assert!(
output.contains("startsWith(\"jni/\")"),
"verifyAarPublished task must check for jni/ directory, got:\n{output}"
);
assert!(
output.contains("classes.jar"),
"verifyAarPublished task must check for classes.jar, got:\n{output}"
);
assert!(
output.contains("dependsOn(\"verifyAarPublished\")"),
"Test task must depend on verifyAarPublished, got:\n{output}"
);
}
#[test]
fn build_gradle_kotlin_android_pins_jvm_toolchain_for_jdk25_host_compat() {
for dep_mode in [
crate::e2e::config::DependencyMode::Registry,
crate::e2e::config::DependencyMode::Local,
] {
let output = render_build_gradle_kotlin_android(&KotlinAndroidBuildGradleInputs {
kotlin_pkg_id: "dev.sample_crate",
maven_coordinate: "dev.sample_crate:sample_crate-android:5.0.0-rc.1",
dep_mode,
jni_lib_name: "sample_crate_jni",
jni_crate_path: "../../crates/sample_crate-jni",
e2e_env: &std::collections::BTreeMap::new(),
capsule_types: &std::collections::HashMap::new(),
test_documents_path: "../../test_documents",
});
assert!(
output.contains("jvmToolchain(17)"),
"build.gradle.kts ({dep_mode:?}) must pin jvmToolchain(17) so JDK 25 hosts pick up JDK 17 for gradle, got:\n{output}"
);
}
}
#[test]
fn build_gradle_kotlin_android_local_mode_excludes_aar_verification_task() {
let output = render_build_gradle_kotlin_android(&KotlinAndroidBuildGradleInputs {
kotlin_pkg_id: "dev.sample_crate.samplellm.android",
maven_coordinate: "dev.sample_crate:demo-client-android:1.0.0",
dep_mode: crate::e2e::config::DependencyMode::Local,
jni_lib_name: "demo_client_jni",
jni_crate_path: "../../crates/demo-client-jni",
e2e_env: &std::collections::BTreeMap::new(),
capsule_types: &std::collections::HashMap::new(),
test_documents_path: "../../test_documents",
});
assert!(
!output.contains("verifyAarPublished"),
"local-mode build.gradle.kts must not include verifyAarPublished task, got:\n{output}"
);
}
#[test]
fn build_gradle_kotlin_android_includes_host_jni_tasks() {
for dep_mode in [
crate::e2e::config::DependencyMode::Registry,
crate::e2e::config::DependencyMode::Local,
] {
let output = render_build_gradle_kotlin_android(&KotlinAndroidBuildGradleInputs {
kotlin_pkg_id: "dev.sample_crate",
maven_coordinate: "dev.sample_crate:sample_crate-android:5.0.0-rc.1",
dep_mode,
jni_lib_name: "sample_crate_jni",
jni_crate_path: "../../crates/sample_crate-jni",
e2e_env: &std::collections::BTreeMap::new(),
capsule_types: &std::collections::HashMap::new(),
test_documents_path: "../../test_documents",
});
assert!(
output.contains(r#"tasks.register("buildHostJni", Exec::class)"#),
"build.gradle.kts ({dep_mode:?}) must include buildHostJni task registration, got:\n{output}"
);
assert!(
output.contains(r#"tasks.register("copyHostJni", Copy::class)"#),
"build.gradle.kts ({dep_mode:?}) must include copyHostJni task registration, got:\n{output}"
);
assert!(
output.contains("java.library.path"),
"build.gradle.kts ({dep_mode:?}) must set java.library.path for the Test task, got:\n{output}"
);
assert!(
output.contains(r#"src/test/resources/host-jni"#),
"build.gradle.kts ({dep_mode:?}) must reference src/test/resources/host-jni, got:\n{output}"
);
}
}
#[test]
fn build_gradle_kotlin_android_build_host_jni_uses_parameterized_jni_crate_path() {
let output = render_build_gradle_kotlin_android(&KotlinAndroidBuildGradleInputs {
kotlin_pkg_id: "dev.sample_crate",
maven_coordinate: "dev.sample_crate:sample_crate-android:5.0.0-rc.1",
dep_mode: crate::e2e::config::DependencyMode::Local,
jni_lib_name: "sample_crate_jni",
jni_crate_path: "../../crates/sample_crate-jni",
e2e_env: &std::collections::BTreeMap::new(),
capsule_types: &std::collections::HashMap::new(),
test_documents_path: "../../test_documents",
});
assert!(
output.contains("../../crates/sample_crate-jni/Cargo.toml"),
"buildHostJni must pass the parameterized JNI crate path to cargo build, got:\n{output}"
);
assert!(
output.contains(r#"commandLine("cargo", "build", "--release", "--manifest-path", jniCargoPath)"#),
"buildHostJni must invoke cargo build with --release flag, got:\n{output}"
);
}
#[test]
fn build_gradle_kotlin_android_copy_host_jni_uses_parameterized_jni_lib_name() {
let output = render_build_gradle_kotlin_android(&KotlinAndroidBuildGradleInputs {
kotlin_pkg_id: "dev.sample_crate",
maven_coordinate: "dev.sample_crate:sample_crate-android:5.0.0-rc.1",
dep_mode: crate::e2e::config::DependencyMode::Registry,
jni_lib_name: "sample_crate_jni",
jni_crate_path: "../../crates/sample_crate-jni",
e2e_env: &std::collections::BTreeMap::new(),
capsule_types: &std::collections::HashMap::new(),
test_documents_path: "../../test_documents",
});
assert!(
output.contains("libsample_crate_jni.dylib"),
"copyHostJni must emit macOS library name with parameterized JNI lib name, got:\n{output}"
);
assert!(
output.contains("sample_crate_jni.dll"),
"copyHostJni must emit Windows library name with parameterized JNI lib name, got:\n{output}"
);
assert!(
output.contains("libsample_crate_jni.so"),
"copyHostJni must emit Linux library name with parameterized JNI lib name, got:\n{output}"
);
}
}