use super::*;
fn plugin_section<'a>(pom_content: &'a str, artifact_id: &str) -> &'a str {
pom_content
.split(&format!("<artifactId>{artifact_id}</artifactId>"))
.nth(1)
.and_then(|section| section.split("</plugin>").next())
.unwrap_or_else(|| panic!("pom.xml must configure {artifact_id}"))
}
fn excludes_block(section: &str) -> &str {
let start = section
.find("<excludes>")
.unwrap_or_else(|| panic!("expected an <excludes> block in:\n{section}"));
let end = section[start..]
.find("</excludes>")
.unwrap_or_else(|| panic!("<excludes> block is not well-formed in:\n{section}"))
+ start
+ "</excludes>".len();
§ion[start..end]
}
#[test]
fn test_scaffold_java_compiler_plugin_excludes_test_scratch_and_target_dirs() {
let config = test_config();
let api = test_api();
let all_files = scaffold(&api, &config, &[Language::Java]).unwrap();
let files = language_files(&all_files);
let pom = files.iter().find(|f| f.path.ends_with("pom.xml")).unwrap();
let compiler_section = plugin_section(&pom.content, "maven-compiler-plugin");
let excludes = excludes_block(compiler_section);
assert!(
excludes.contains("<exclude>src/test/java/**</exclude>"),
"compiler plugin must exclude hand-written test sources; block:\n{excludes}"
);
assert!(
excludes.contains("<exclude>.alef/**</exclude>"),
"compiler plugin must exclude the .alef/ snippet-validation scratch directory; block:\n{excludes}"
);
assert!(
excludes.contains("<exclude>target/**</exclude>"),
"compiler plugin must exclude its own build output directory; block:\n{excludes}"
);
}
#[test]
fn test_scaffold_java_compiler_plugin_excludes_prevent_alef_scratch_duplicate_class_collision() {
if std::process::Command::new("mvn").arg("--version").output().is_err() {
return;
}
let config = test_config();
let api = test_api();
let all_files = scaffold(&api, &config, &[Language::Java]).unwrap();
let files = language_files(&all_files);
let pom = files.iter().find(|f| f.path.ends_with("pom.xml")).unwrap();
let group_id = config.java_group_id();
let source_root = group_id.split('.').next().unwrap_or("dev");
let compile = |pom_content: &str| -> std::process::Output {
let project_dir = tempfile::tempdir().expect("temp project dir");
std::fs::write(project_dir.path().join("pom.xml"), pom_content).expect("write pom");
let package_dir = project_dir.path().join(source_root).join("scratchcompile");
std::fs::create_dir_all(&package_dir).expect("package dir");
std::fs::write(
package_dir.join("RealSource.java"),
"package scratchcompile;\n\npublic final class RealSource {\n}\n",
)
.expect("write real source");
for hash in ["deadbeefcafefeed", "feedfacecafebabe"] {
let scratch_dir = project_dir.path().join(".alef/snippets/sessions").join(hash);
std::fs::create_dir_all(&scratch_dir).expect("scratch session dir");
std::fs::write(scratch_dir.join("Example.java"), "public final class Example {\n}\n")
.expect("write scratch source");
}
std::process::Command::new("mvn")
.args(["-q", "compile", "-Dcheckstyle.skip=true"])
.current_dir(project_dir.path())
.output()
.expect("mvn runs")
};
let fixed_output = compile(&pom.content);
assert!(
fixed_output.status.success(),
"compiler-plugin excludes must keep colliding .alef scratch sources out of `mvn compile`; \
stdout:\n{}\nstderr:\n{}",
String::from_utf8_lossy(&fixed_output.stdout),
String::from_utf8_lossy(&fixed_output.stderr),
);
let compiler_section = plugin_section(&pom.content, "maven-compiler-plugin");
let excludes = excludes_block(compiler_section);
let broken_pom = pom.content.replacen(excludes, "", 1);
let broken_output = compile(&broken_pom);
let broken_diagnostics = format!(
"{}{}",
String::from_utf8_lossy(&broken_output.stdout),
String::from_utf8_lossy(&broken_output.stderr)
);
assert!(
!broken_output.status.success(),
"without the compiler-plugin excludes, colliding .alef scratch classes should fail \
`mvn compile`; stdout:\n{}\nstderr:\n{}",
String::from_utf8_lossy(&broken_output.stdout),
String::from_utf8_lossy(&broken_output.stderr),
);
assert!(
broken_diagnostics.contains("duplicate class"),
"the failure without excludes must be the duplicate-class collision this fix targets, \
not some other break; diagnostics:\n{broken_diagnostics}"
);
}
#[test]
fn test_scaffold_java_javadoc_plugin_source_file_includes_prevent_test_source_leak() {
if std::process::Command::new("mvn").arg("--version").output().is_err() {
return;
}
let config = test_config();
let api = test_api();
let all_files = scaffold(&api, &config, &[Language::Java]).unwrap();
let files = language_files(&all_files);
let pom = files.iter().find(|f| f.path.ends_with("pom.xml")).unwrap();
let group_id = config.java_group_id();
let source_root = group_id.split('.').next().unwrap_or("dev");
let run_javadoc = |pom_content: &str| -> std::process::Output {
let project_dir = tempfile::tempdir().expect("temp project dir");
std::fs::write(project_dir.path().join("pom.xml"), pom_content).expect("write pom");
let package_dir = project_dir.path().join(source_root).join("scratchjavadoc");
std::fs::create_dir_all(&package_dir).expect("main package dir");
std::fs::write(
package_dir.join("RealSource.java"),
"package scratchjavadoc;\n\npublic final class RealSource {\n public int value() {\n return 1;\n }\n}\n",
)
.expect("write real source");
let test_package_dir = project_dir.path().join("src/test/java/scratchjavadoc");
std::fs::create_dir_all(&test_package_dir).expect("test package dir");
std::fs::write(
test_package_dir.join("RealSourceTest.java"),
"package scratchjavadoc;\n\nimport org.junit.jupiter.api.Test;\n\nclass RealSourceTest {\n @Test\n void works() {\n }\n}\n",
)
.expect("write test source");
std::process::Command::new("mvn")
.args(["-q", "javadoc:javadoc", "-Dcheckstyle.skip=true"])
.current_dir(project_dir.path())
.output()
.expect("mvn runs")
};
let fixed_output = run_javadoc(&pom.content);
assert!(
fixed_output.status.success(),
"javadoc plugin must build cleanly against a consumer with Java tests; stdout:\n{}\nstderr:\n{}",
String::from_utf8_lossy(&fixed_output.stdout),
String::from_utf8_lossy(&fixed_output.stderr),
);
let javadoc_section = plugin_section(&pom.content, "maven-javadoc-plugin");
let start = javadoc_section
.find("<sourceFileIncludes>")
.expect("javadoc plugin must declare <sourceFileIncludes>");
let end = javadoc_section[start..]
.find("</sourceFileIncludes>")
.expect("<sourceFileIncludes> block is not well-formed")
+ start
+ "</sourceFileIncludes>".len();
let source_file_includes = &javadoc_section[start..end];
let broken_pom = pom.content.replacen(source_file_includes, "", 1);
let broken_output = run_javadoc(&broken_pom);
let broken_diagnostics = format!(
"{}{}",
String::from_utf8_lossy(&broken_output.stdout),
String::from_utf8_lossy(&broken_output.stderr)
);
assert!(
!broken_output.status.success(),
"without sourceFileIncludes, javadoc should sweep in src/test/java and fail; \
stdout:\n{}\nstderr:\n{}",
String::from_utf8_lossy(&broken_output.stdout),
String::from_utf8_lossy(&broken_output.stderr),
);
assert!(
broken_diagnostics.contains("org.junit.jupiter.api does not exist"),
"the failure without sourceFileIncludes must be the test-source classpath leak this \
fix targets, not some other break; diagnostics:\n{broken_diagnostics}"
);
}