use crate::{cmd, pkg};
use anyhow::{Result, anyhow};
use colored::*;
use mlua::{Lua, LuaSerdeExt};
pub fn run(args: &cmd::package::build::BuildCommand) -> Result<()> {
println!("Testing package from: {}", args.package_file.display());
let platform = if let Some(p) = args.platform.first() {
p.clone()
} else {
crate::pkg::utils::get_platform()?
};
let pkg_for_meta = pkg::lua::parser::parse_lua_package_for_platform(
args.package_file.to_str().ok_or_else(|| {
anyhow!(
"Path contains invalid UTF-8 characters: {:?}",
args.package_file
)
})?,
&platform,
args.version_override.as_deref(),
None,
false,
)?;
let version = if let Some(v) = &args.version_override {
v.clone()
} else {
pkg::resolve::get_default_version(&pkg_for_meta, None)?
};
let resolved_build_type = match crate::pkg::package::build::resolve_build_type(
args.r#type.as_deref(),
&pkg_for_meta.types,
&pkg_for_meta.name,
)? {
Some(t) => t,
None => {
println!(
"{} Skipping tests for package '{}': no build types supported.",
"::".bold().yellow(),
pkg_for_meta.name
);
return Ok(());
}
};
let build_dir = tempfile::Builder::new()
.prefix(&format!("zoi-test-{}-{}", pkg_for_meta.name, platform))
.tempdir()?;
println!("Using build directory: {}", build_dir.path().display());
let staging_dir = build_dir.path().join("staging");
std::fs::create_dir_all(&staging_dir)?;
let subs_to_test = if let Some(subs) = &args.sub {
subs.clone()
} else if let Some(subs) = &pkg_for_meta.sub_packages {
subs.clone()
} else {
vec!["".to_string()]
};
for sub_package in subs_to_test {
let sub_pkg_name = if sub_package.is_empty() {
None
} else {
Some(sub_package.as_str())
};
if !sub_package.is_empty() {
println!(
"{} Testing sub-package: {}",
"::".bold().blue(),
sub_package.cyan()
);
}
let lua = Lua::new();
pkg::lua::functions::setup_lua_environment(
&lua,
&platform,
Some(&version),
args.package_file.to_str(),
None,
Some(build_dir.path().to_str().unwrap_or("")),
Some(staging_dir.to_str().unwrap_or("")),
sub_pkg_name,
Some(pkg_for_meta.scope),
Some(resolved_build_type.as_str()),
false,
)
.map_err(|e| anyhow!(e.to_string()))?;
let pkg_table = lua
.to_value(&pkg_for_meta)
.map_err(|e| anyhow!(e.to_string()))?;
lua.globals()
.set("PKG", pkg_table)
.map_err(|e| anyhow!(e.to_string()))?;
let lua_code = std::fs::read_to_string(&args.package_file)?;
lua.load(&lua_code)
.exec()
.map_err(|e| anyhow!(e.to_string()))?;
let lua_args = lua.create_table().map_err(|e| anyhow!(e.to_string()))?;
if !sub_package.is_empty() {
lua_args
.set("sub", sub_package.clone())
.map_err(|e| anyhow!(e.to_string()))?;
}
if let Ok(prepare_fn) = lua.globals().get::<mlua::Function>("prepare") {
println!("Running prepare()...");
prepare_fn
.call::<()>(lua_args.clone())
.map_err(|e| anyhow!(e.to_string()))?;
}
if let Ok(package_fn) = lua.globals().get::<mlua::Function>("package") {
println!("Running package()...");
package_fn
.call::<()>(lua_args.clone())
.map_err(|e| anyhow!(e.to_string()))?;
}
if let Ok(test_fn) = lua.globals().get::<mlua::Function>("test") {
println!("Running test()...");
let success: bool = match test_fn.call::<mlua::Value>(lua_args.clone()) {
Ok(mlua::Value::Boolean(b)) => b,
Ok(mlua::Value::Nil) => {
return Err(anyhow!(
"The 'test' function in '{}' returned nil. It must explicitly return a boolean (true or false).",
args.package_file.display()
));
}
Ok(v) => {
return Err(anyhow!(
"The 'test' function in '{}' returned a non-boolean value of type {:?}. It must return true or false.",
args.package_file.display(),
v.type_name()
));
}
Err(e) => return Err(anyhow!(e.to_string())),
};
if !success {
return Err(anyhow!(
"Package tests failed for sub-package '{}'.",
sub_package
));
}
} else if !sub_package.is_empty() {
println!(
"No test() function found for sub-package '{}', skipping.",
sub_package
);
}
}
println!("{}", "All tests passed successfully.".green());
Ok(())
}