zoi_cli/pkg/package/
test.rs1use anyhow::{Result, anyhow};
2use colored::Colorize;
3use mlua::{Lua, LuaSerdeExt};
4
5use crate::{cmd, pkg};
6
7pub fn run(args: &cmd::package::build::BuildCommand) -> Result<()> {
14 println!("Testing package from: {}", args.package_file.display());
15
16 let platform = if let Some(p) = args.platform.first() {
17 p.clone()
18 } else {
19 crate::pkg::utils::get_platform()?
20 };
21
22 let pkg_for_meta = pkg::lua::parser::parse_lua_package_for_platform(
23 args.package_file.to_str().ok_or_else(|| {
24 anyhow!(
25 "Path contains invalid UTF-8 characters: {}",
26 args.package_file.display()
27 )
28 })?,
29 &platform,
30 args.version_override.as_deref(),
31 None,
32 false
33 )?;
34
35 let version = if let Some(v) = &args.version_override {
36 v.clone()
37 } else {
38 pkg::resolve::get_default_version(&pkg_for_meta, None)?
39 };
40
41 let Some(resolved_build_type) =
42 crate::pkg::package::build::resolve_build_type(
43 args.r#type.as_deref(),
44 &pkg_for_meta.types,
45 &pkg_for_meta.name
46 )?
47 else {
48 println!(
49 "{} Skipping tests for package '{}': no build types supported.",
50 "::".bold().yellow(),
51 pkg_for_meta.name
52 );
53 return Ok(());
54 };
55
56 let build_dir = tempfile::Builder::new()
57 .prefix(&format!("zoi-test-{}-{}", pkg_for_meta.name, platform))
58 .tempdir()?;
59 println!("Using build directory: {}", build_dir.path().display());
60 let staging_dir = build_dir.path().join("staging");
61 std::fs::create_dir_all(&staging_dir)?;
62
63 let subs_to_test = if let Some(subs) = &args.sub {
64 subs.clone()
65 } else if let Some(subs) = &pkg_for_meta.sub_packages {
66 subs.clone()
67 } else {
68 vec![String::new()]
69 };
70
71 for sub_package in subs_to_test {
72 let sub_pkg_name = if sub_package.is_empty() {
73 None
74 } else {
75 Some(sub_package.as_str())
76 };
77
78 if !sub_package.is_empty() {
79 println!(
80 "{} Testing sub-package: {}",
81 "::".bold().blue(),
82 sub_package.cyan()
83 );
84 }
85
86 let lua = Lua::new();
87 pkg::lua::functions::setup_lua_environment(
88 &lua,
89 &platform,
90 Some(&version),
91 args.package_file.to_str(),
92 None,
93 Some(build_dir.path().to_str().unwrap_or("")),
94 Some(staging_dir.to_str().unwrap_or("")),
95 sub_pkg_name,
96 Some(pkg_for_meta.scope),
97 Some(resolved_build_type.as_str()),
98 false
99 )
100 .map_err(|e| anyhow!(e.to_string()))?;
101
102 let pkg_table = lua
103 .to_value(&pkg_for_meta)
104 .map_err(|e| anyhow!(e.to_string()))?;
105 lua.globals()
106 .set("PKG", pkg_table)
107 .map_err(|e| anyhow!(e.to_string()))?;
108
109 let lua_code = std::fs::read_to_string(&args.package_file)?;
110 lua.load(&lua_code)
111 .exec()
112 .map_err(|e| anyhow!(e.to_string()))?;
113
114 let lua_args =
115 lua.create_table().map_err(|e| anyhow!(e.to_string()))?;
116 if !sub_package.is_empty() {
117 lua_args
118 .set("sub", sub_package.clone())
119 .map_err(|e| anyhow!(e.to_string()))?;
120 }
121
122 if let Ok(prepare_fn) = lua.globals().get::<mlua::Function>("prepare") {
123 println!("Running prepare()...");
124 prepare_fn
125 .call::<()>(lua_args.clone())
126 .map_err(|e| anyhow!(e.to_string()))?;
127 }
128
129 if let Ok(package_fn) = lua.globals().get::<mlua::Function>("package") {
130 println!("Running package()...");
131 package_fn
132 .call::<()>(lua_args.clone())
133 .map_err(|e| anyhow!(e.to_string()))?;
134 }
135
136 if let Ok(test_fn) = lua.globals().get::<mlua::Function>("test") {
137 println!("Running test()...");
138 let success: bool = match test_fn
139 .call::<mlua::Value>(lua_args.clone())
140 {
141 Ok(mlua::Value::Boolean(b)) => b,
142 Ok(mlua::Value::Nil) => {
143 return Err(anyhow!(
144 "The 'test' function in '{}' returned nil. It must \
145 explicitly return a boolean (true or false).",
146 args.package_file.display()
147 ));
148 }
149 Ok(v) => {
150 return Err(anyhow!(
151 "The 'test' function in '{}' returned a non-boolean \
152 value of type {:?}. It must return true or false.",
153 args.package_file.display(),
154 v.type_name()
155 ));
156 }
157 Err(e) => return Err(anyhow!(e.to_string()))
158 };
159 if !success {
160 return Err(anyhow!(
161 "Package tests failed for sub-package '{sub_package}'."
162 ));
163 }
164 } else if !sub_package.is_empty() {
165 println!(
166 "No test() function found for sub-package '{sub_package}', \
167 skipping."
168 );
169 }
170 }
171
172 println!("{}", "All tests passed successfully.".green());
173 Ok(())
174}