#![cfg(all(feature = "desktop", not(alloc_frugal)))]
use rust_widgets::designer::{generate, GenerationRequest, TargetProfile};
const PROJECT: &str = r#"{
"window": {
"id": "root",
"title": "Generated",
"width": 640,
"height": 480,
"layout": {
"type": "vbox",
"spacing": 4,
"children": [
{ "label": { "id": "title", "text": "Hello" } },
{ "button": { "id": "go", "text": "Go", "enabled": false } },
{ "slider": { "id": "level", "value": 30 } }
]
}
}
}"#;
fn generate_for(target: TargetProfile) -> (String, String) {
let request = GenerationRequest {
json: String::from(PROJECT),
target,
width: 640,
height: 480,
function_name: String::from("build_ui"),
};
let generated = generate(&request).expect("the project must parse");
(generated.source, generated.report.summary())
}
fn check_compiles(source: &str, features: &str) -> (String, bool) {
let dir = std::env::temp_dir().join(format!("rw_gen_{}", features.replace(',', "_")));
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(dir.join("src")).expect("create the probe crate");
let manifest_dir = env!("CARGO_MANIFEST_DIR").replace('\\', "/");
std::fs::write(
dir.join("Cargo.toml"),
format!(
"[package]\nname = \"rw_generated_probe\"\nversion = \"0.0.0\"\nedition = \"2021\"\n\
\n[workspace]\n\
\n[dependencies]\n\
rust_widgets = {{ path = \"{manifest_dir}\", default-features = false, features = \
[{features_list}] }}\n",
features_list =
features.split(',').map(|f| format!("\"{f}\"")).collect::<Vec<_>>().join(", ")
),
)
.expect("write the probe manifest");
std::fs::write(dir.join("src/lib.rs"), source).expect("write the generated source");
let shared_target = std::path::Path::new(&manifest_dir).join("target");
let mut child = std::process::Command::new(env!("CARGO"))
.arg("check")
.arg("--quiet")
.env("CARGO_TARGET_DIR", &shared_target)
.current_dir(&dir)
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.spawn()
.expect("cargo check must start");
let deadline = std::time::Instant::now() + std::time::Duration::from_secs(PROBE_TIMEOUT_SECS);
loop {
match child.try_wait() {
Ok(Some(_)) => break,
Ok(None) => {
if std::time::Instant::now() >= deadline {
let _ = child.kill();
let _ = child.wait();
let _ = std::fs::remove_dir_all(&dir);
return (
format!(
"cargo check did not finish within {PROBE_TIMEOUT_SECS}s. \
\n\nIf this is a build-dir lock wait, the assumption documented in \
`check_compiles` no longer holds and the probe needs its own target \
dir (at the cost of ~15s per case)."
),
false,
);
}
std::thread::sleep(std::time::Duration::from_millis(200));
}
Err(error) => {
let _ = std::fs::remove_dir_all(&dir);
return (format!("cargo check could not be waited on: {error}"), false);
}
}
}
let output = child.wait_with_output().expect("collect the probe's output");
let combined = format!(
"{}{}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
);
let _ = std::fs::remove_dir_all(&dir);
(combined, output.status.success())
}
const PROBE_TIMEOUT_SECS: u64 = 600;
#[test]
#[ignore = "compiles a real crate; run by tools/check_generator_output_compiles.sh"]
fn default_template_output_compiles_for_desktop() {
let (source, summary) = generate_for(TargetProfile::Default);
let (output, ok) = check_compiles(&source, "desktop");
assert!(ok, "the generated desktop program must compile.\nreport: {summary}\n{output}");
}
#[test]
#[ignore = "compiles a real crate; run by tools/check_generator_output_compiles.sh"]
fn stripped_template_output_compiles_for_mini() {
let (source, summary) = generate_for(TargetProfile::Stripped);
let (output, ok) = check_compiles(&source, "mini");
assert!(
ok,
"the generated mini program must compile. `mini` gates the `create_*` family behind \
`cfg(not(alloc_frugal))` and removes `crate::json`/`crate::view`, so any of those in the \
output is a real defect.\nreport: {summary}\n{output}"
);
}
#[test]
#[ignore = "compiles a real crate; run by tools/check_generator_output_compiles.sh"]
fn stripped_template_output_compiles_for_embedded() {
let (source, summary) = generate_for(TargetProfile::Stripped);
let (output, ok) = check_compiles(&source, "embedded");
assert!(ok, "the generated embedded program must compile.\nreport: {summary}\n{output}");
}
#[test]
#[ignore = "compiles a real crate; run by tools/check_generator_output_compiles.sh"]
fn default_template_output_compiles_for_tablet_and_mobile() {
for profile in ["tablet", "mobile"] {
let (source, summary) = generate_for(TargetProfile::Default);
let (output, ok) = check_compiles(&source, profile);
assert!(ok, "the generated {profile} program must compile.\nreport: {summary}\n{output}");
}
}