Skip to content

Commit ea5c5a5

Browse files
committed
Moves test generation to run-time
1 parent d7e3d63 commit ea5c5a5

File tree

2 files changed

+165
-188
lines changed

2 files changed

+165
-188
lines changed

testing-macros/src/qemu.rs

+17-183
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,18 @@
11
use proc_macro2::{Span, TokenStream};
22
use quote::ToTokens;
3-
use serde::{Deserialize, Serialize};
4-
use std::collections::HashMap;
53
use std::env::VarError;
6-
use std::fmt::Write;
7-
use std::fs::create_dir_all;
8-
use std::path::{Path, PathBuf};
94
use syn::{parse_quote, Error, ItemFn};
105

116
pub fn parse_qemu_attribute(mut item: ItemFn) -> Result<TokenStream, Error> {
12-
// Get path for the integration test data.
13-
let root = match std::env::var("CARGO_TARGET_TMPDIR") {
14-
Ok(v) => PathBuf::from(v),
15-
Err(e) => match e {
16-
VarError::NotPresent => {
17-
// We are not running by the integration test, keep the original function body.
18-
return Ok(item.into_token_stream());
19-
}
20-
VarError::NotUnicode(_) => {
21-
return Err(Error::new(
22-
Span::call_site(),
23-
"non-unicode value in CARGO_TARGET_TMPDIR",
24-
))
25-
}
26-
},
27-
};
28-
29-
// Generate a test project.
30-
let name = item.sig.ident.to_string();
31-
let body = item.block.brace_token.span.join().source_text().unwrap();
32-
33-
generate_test(root.join("project"), &name, &body)?;
34-
35-
// Construct a new body.
36-
let root = root.to_str().unwrap();
37-
38-
item.block = Box::new(parse_quote!({
39-
let dest = std::path::PathBuf::from(env!("CARGO_TARGET_TMPDIR"));
40-
::zfi_testing::gen_qemu_test(dest, #name, #body);
41-
::zfi_testing::run_qemu_test(::std::path::Path::new(#root));
42-
}));
43-
44-
Ok(item.into_token_stream())
45-
}
46-
47-
fn generate_test<P: AsRef<Path>>(dir: P, name: &str, body: &str) -> Result<(), Error> {
48-
// Create project directory.
49-
let dir = dir.as_ref();
50-
51-
create_dir_all(dir).unwrap();
7+
// Do nothing if we are not running by the integration test.
8+
if std::env::var("CARGO_TARGET_TMPDIR").is_err_and(|e| e == VarError::NotPresent) {
9+
// Keep the original function body.
10+
return Ok(item.into_token_stream());
11+
}
5212

5313
// Get path to the project.
5414
let proj = match std::env::var("CARGO_MANIFEST_DIR") {
55-
Ok(v) => PathBuf::from(v),
15+
Ok(v) => v,
5616
Err(e) => {
5717
return Err(match e {
5818
VarError::NotPresent => {
@@ -65,144 +25,18 @@ fn generate_test<P: AsRef<Path>>(dir: P, name: &str, body: &str) -> Result<(), E
6525
}
6626
};
6727

68-
// Copy application dependencies.
69-
let cargo = std::fs::read_to_string(proj.join("Cargo.toml")).unwrap();
70-
let mut cargo = toml::from_str::<Cargo>(&cargo).unwrap();
71-
72-
cargo.dev_dependencies.remove("zfi-testing");
73-
cargo.dependencies.extend(cargo.dev_dependencies.drain());
74-
75-
// Fix relative path.
76-
for dep in cargo.dependencies.values_mut() {
77-
let path = match dep {
78-
Dependency::Complex {
79-
version: _,
80-
path: Some(v),
81-
} => v,
82-
_ => continue,
83-
};
84-
85-
if !Path::new(path.as_str()).is_relative() {
86-
continue;
87-
}
88-
89-
*path = proj
90-
.join(path.as_str())
91-
.canonicalize()
92-
.unwrap()
93-
.into_os_string()
94-
.into_string()
95-
.unwrap();
96-
}
97-
98-
// Check if the test being run is our test.
99-
if cargo.package.take().unwrap().name == "zfi" {
100-
cargo.dependencies.remove("zfi-macros");
101-
cargo.dependencies.insert(
102-
String::from("zfi"),
103-
Dependency::Complex {
104-
version: None,
105-
path: Some(proj.into_os_string().into_string().unwrap()),
106-
},
107-
);
108-
}
109-
110-
// Create Cargo.toml.
111-
let mut data = String::new();
112-
113-
writeln!(data, r#"[package]"#).unwrap();
114-
writeln!(data, r#"name = "{name}""#).unwrap();
115-
writeln!(data, r#"version = "0.1.0""#).unwrap();
116-
writeln!(data, r#"edition = "2021""#).unwrap();
117-
writeln!(data).unwrap();
118-
data.push_str(&toml::to_string_pretty(&cargo).unwrap());
119-
writeln!(data).unwrap();
120-
writeln!(data, r#"[workspace]"#).unwrap();
121-
writeln!(data, r#"members = []"#).unwrap();
122-
123-
std::fs::write(dir.join("Cargo.toml"), data).unwrap();
124-
125-
// Create src directory.
126-
let mut path = dir.join("src");
127-
128-
create_dir_all(&path).unwrap();
129-
130-
// Generate src/main.rs.
131-
let mut data = String::new();
132-
133-
writeln!(data, r#"#![no_std]"#).unwrap();
134-
writeln!(data, r#"#![no_main]"#).unwrap();
135-
writeln!(data).unwrap();
136-
writeln!(data, r#"#[::zfi::main(no_ph)]"#).unwrap();
137-
writeln!(data, r#"fn main() -> ::zfi::Status {{"#).unwrap();
138-
writeln!(data, r#"{}"#, &body[1..(body.len() - 1)]).unwrap();
139-
writeln!(data, r#" ::zfi::println!("zfi:ok");"#).unwrap();
140-
writeln!(data, r#" loop {{}}"#).unwrap();
141-
writeln!(data, r#"}}"#).unwrap();
142-
writeln!(data).unwrap();
143-
writeln!(data, r#"#[panic_handler]"#).unwrap();
144-
writeln!(
145-
data,
146-
r#"fn panic_handler(i: &::core::panic::PanicInfo) -> ! {{"#
147-
)
148-
.unwrap();
149-
writeln!(data, r#" let l = i.location().unwrap();"#).unwrap();
150-
writeln!(data).unwrap();
151-
writeln!(
152-
data,
153-
r#" ::zfi::println!("zfi:panic:{{}}:{{}}:{{}}", l.file(), l.line(), l.column());"#
154-
)
155-
.unwrap();
156-
writeln!(data).unwrap();
157-
writeln!(
158-
data,
159-
r#" if let Some(&p) = i.payload().downcast_ref::<&str>() {{"#
160-
)
161-
.unwrap();
162-
writeln!(data, r#" ::zfi::println!("{{p}}");"#).unwrap();
163-
writeln!(
164-
data,
165-
r#" }} else if let Some(p) = i.payload().downcast_ref::<::alloc::string::String>() {{"#
166-
)
167-
.unwrap();
168-
writeln!(data, r#" ::zfi::println!("{{p}}");"#).unwrap();
169-
writeln!(data, r#" }} else {{"#).unwrap();
170-
writeln!(data, r#" ::zfi::println!("{{i}}");"#).unwrap();
171-
writeln!(data, r#" }}"#).unwrap();
172-
writeln!(data).unwrap();
173-
writeln!(data, r#" ::zfi::println!("zfi:end");"#).unwrap();
174-
writeln!(data).unwrap();
175-
writeln!(data, r#" loop {{}}"#).unwrap();
176-
writeln!(data, r#"}}"#).unwrap();
177-
178-
// Write src/main.rs.
179-
path.push("main.rs");
180-
181-
std::fs::write(&path, data).unwrap();
182-
183-
Ok(())
184-
}
185-
186-
#[derive(Serialize, Deserialize)]
187-
struct Cargo {
188-
package: Option<Package>,
189-
dependencies: HashMap<String, Dependency>,
28+
// Generate a test project.
29+
let name = item.sig.ident.to_string();
30+
let body = item.block.brace_token.span.join().source_text().unwrap();
19031

191-
#[serde(rename = "dev-dependencies")]
192-
dev_dependencies: HashMap<String, Dependency>,
193-
}
32+
// Construct a new body.
33+
item.block = Box::new(parse_quote!({
34+
let proj = std::path::PathBuf::from(#proj);
35+
let dest = std::path::PathBuf::from(env!("CARGO_TARGET_TMPDIR"));
36+
let root = ::zfi_testing::gen_qemu_test(proj, dest, #name, #body);
19437

195-
#[derive(Serialize, Deserialize)]
196-
struct Package {
197-
name: String,
198-
}
38+
::zfi_testing::run_qemu_test(root);
39+
}));
19940

200-
#[derive(Serialize, Deserialize)]
201-
#[serde(untagged)]
202-
enum Dependency {
203-
Simple(String),
204-
Complex {
205-
version: Option<String>,
206-
path: Option<String>,
207-
},
41+
Ok(item.into_token_stream())
20842
}

testing/src/qemu/mod.rs

+148-5
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use gpt::mbr::ProtectiveMBR;
44
use gpt::partition_types::EFI;
55
use gpt::GptConfig;
66
use regex::Regex;
7-
use serde::Deserialize;
7+
use serde::{Deserialize, Serialize};
88
use std::collections::{BTreeMap, HashMap};
99
use std::fmt::Write;
1010
use std::fs::{create_dir_all, File};
@@ -19,10 +19,129 @@ use std::sync::{Arc, Mutex};
1919
/// `efi_main`.
2020
///
2121
/// The project will be created as a sub-directory of `dest`.
22-
pub fn gen_qemu_test(dest: impl AsRef<Path>, name: &str, body: &str) -> PathBuf {
23-
let root = dest
24-
.as_ref()
25-
.join(format!("{}-{}", name, blake3::hash(body.as_bytes())));
22+
pub fn gen_qemu_test(proj: PathBuf, mut root: PathBuf, name: &str, body: &str) -> PathBuf {
23+
// Generate a unique name for root directory.
24+
root.push(format!("{}-{}", name, blake3::hash(body.as_bytes())));
25+
26+
// Create project directory.
27+
let dest = root.join("project");
28+
29+
create_dir_all(&dest).unwrap();
30+
31+
// Copy dev-dependencies from the project.
32+
let cargo = std::fs::read_to_string(proj.join("Cargo.toml")).unwrap();
33+
let mut cargo = toml::from_str::<Cargo>(&cargo).unwrap();
34+
35+
cargo.dev_dependencies.remove("zfi-testing");
36+
cargo.dependencies.extend(cargo.dev_dependencies.drain());
37+
38+
// Convert relative path to full path.
39+
for dep in cargo.dependencies.values_mut() {
40+
let path = match dep {
41+
Dependency::Complex {
42+
version: _,
43+
path: Some(v),
44+
} => v,
45+
_ => continue,
46+
};
47+
48+
if !Path::new(path.as_str()).is_relative() {
49+
continue;
50+
}
51+
52+
*path = proj
53+
.join(path.as_str())
54+
.canonicalize()
55+
.unwrap()
56+
.into_os_string()
57+
.into_string()
58+
.unwrap();
59+
}
60+
61+
// Check if the test being run is our test.
62+
if cargo.package.take().unwrap().name == "zfi" {
63+
cargo.dependencies.remove("zfi-macros");
64+
cargo.dependencies.insert(
65+
String::from("zfi"),
66+
Dependency::Complex {
67+
version: None,
68+
path: Some(proj.into_os_string().into_string().unwrap()),
69+
},
70+
);
71+
}
72+
73+
// Create Cargo.toml.
74+
let mut data = String::new();
75+
76+
writeln!(data, r#"[package]"#).unwrap();
77+
writeln!(data, r#"name = "{name}""#).unwrap();
78+
writeln!(data, r#"version = "0.1.0""#).unwrap();
79+
writeln!(data, r#"edition = "2021""#).unwrap();
80+
writeln!(data).unwrap();
81+
data.push_str(&toml::to_string_pretty(&cargo).unwrap());
82+
writeln!(data).unwrap();
83+
writeln!(data, r#"[workspace]"#).unwrap();
84+
writeln!(data, r#"members = []"#).unwrap();
85+
86+
std::fs::write(dest.join("Cargo.toml"), data).unwrap();
87+
88+
// Create src directory.
89+
let mut path = dest.join("src");
90+
91+
create_dir_all(&path).unwrap();
92+
93+
// Generate src/main.rs.
94+
let mut data = String::new();
95+
96+
writeln!(data, r#"#![no_std]"#).unwrap();
97+
writeln!(data, r#"#![no_main]"#).unwrap();
98+
writeln!(data).unwrap();
99+
writeln!(data, r#"#[::zfi::main(no_ph)]"#).unwrap();
100+
writeln!(data, r#"fn main() -> ::zfi::Status {{"#).unwrap();
101+
writeln!(data, r#"{}"#, &body[1..(body.len() - 1)]).unwrap();
102+
writeln!(data, r#" ::zfi::println!("zfi:ok");"#).unwrap();
103+
writeln!(data, r#" loop {{}}"#).unwrap();
104+
writeln!(data, r#"}}"#).unwrap();
105+
writeln!(data).unwrap();
106+
writeln!(data, r#"#[panic_handler]"#).unwrap();
107+
writeln!(
108+
data,
109+
r#"fn panic_handler(i: &::core::panic::PanicInfo) -> ! {{"#
110+
)
111+
.unwrap();
112+
writeln!(data, r#" let l = i.location().unwrap();"#).unwrap();
113+
writeln!(data).unwrap();
114+
writeln!(
115+
data,
116+
r#" ::zfi::println!("zfi:panic:{{}}:{{}}:{{}}", l.file(), l.line(), l.column());"#
117+
)
118+
.unwrap();
119+
writeln!(data).unwrap();
120+
writeln!(
121+
data,
122+
r#" if let Some(&p) = i.payload().downcast_ref::<&str>() {{"#
123+
)
124+
.unwrap();
125+
writeln!(data, r#" ::zfi::println!("{{p}}");"#).unwrap();
126+
writeln!(
127+
data,
128+
r#" }} else if let Some(p) = i.payload().downcast_ref::<::alloc::string::String>() {{"#
129+
)
130+
.unwrap();
131+
writeln!(data, r#" ::zfi::println!("{{p}}");"#).unwrap();
132+
writeln!(data, r#" }} else {{"#).unwrap();
133+
writeln!(data, r#" ::zfi::println!("{{i}}");"#).unwrap();
134+
writeln!(data, r#" }}"#).unwrap();
135+
writeln!(data).unwrap();
136+
writeln!(data, r#" ::zfi::println!("zfi:end");"#).unwrap();
137+
writeln!(data).unwrap();
138+
writeln!(data, r#" loop {{}}"#).unwrap();
139+
writeln!(data, r#"}}"#).unwrap();
140+
141+
// Write src/main.rs.
142+
path.push("main.rs");
143+
144+
std::fs::write(&path, data).unwrap();
26145

27146
root
28147
}
@@ -305,6 +424,30 @@ fn report_failure(msg: &str) {
305424
});
306425
}
307426

427+
#[derive(Serialize, Deserialize)]
428+
struct Cargo {
429+
package: Option<Package>,
430+
dependencies: HashMap<String, Dependency>,
431+
432+
#[serde(rename = "dev-dependencies")]
433+
dev_dependencies: HashMap<String, Dependency>,
434+
}
435+
436+
#[derive(Serialize, Deserialize)]
437+
struct Package {
438+
name: String,
439+
}
440+
441+
#[derive(Serialize, Deserialize)]
442+
#[serde(untagged)]
443+
enum Dependency {
444+
Simple(String),
445+
Complex {
446+
version: Option<String>,
447+
path: Option<String>,
448+
},
449+
}
450+
308451
#[derive(Deserialize)]
309452
struct Config {
310453
qemu: HashMap<String, QemuConfig>,

0 commit comments

Comments
 (0)