Skip to content

Commit e939cb8

Browse files
Merge pull request #51 from not-my-profile/cli-test
Improve cli integration test
2 parents b71ebf8 + 1ac8edb commit e939cb8

28 files changed

+316
-213
lines changed

Cargo.lock

Lines changed: 47 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ regex = "1.12.2"
5454

5555
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
5656
assert_cmd = "2.1.1"
57+
libtest-mimic = "0.8.1"
58+
similar-asserts = "1.7.0"
59+
tempfile = "3"
5760

5861
[dev-dependencies]
5962
wasm-bindgen-test = "0.3"
@@ -65,3 +68,8 @@ server = ["axum", "tokio", "tower-http", "tower"]
6568
[profile.release]
6669
codegen-units = 1
6770
lto = true
71+
72+
[[test]]
73+
name = "cli"
74+
harness = false
75+
path = "tests/cli.rs"

src/diagram.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -441,8 +441,9 @@ impl Diagram {
441441
svg.push_str(" </g>\n");
442442
}
443443
}
444+
for id in &self.order {
445+
let node = self.nodes.get(id).unwrap();
444446

445-
for (id, node) in &self.nodes {
446447
let position = geometry
447448
.positions
448449
.get(id)

tests/cli.rs

Lines changed: 113 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,83 +1,130 @@
1-
#![cfg(not(target_arch = "wasm32"))]
2-
1+
// With the custom test harness we unfortunately cannot use #![cfg(not(target_arch = "wasm32"))].
2+
use std::ffi::OsStr;
33
use std::fs;
44
use std::path::PathBuf;
55

6+
#[cfg(not(target_arch = "wasm32"))]
67
use assert_cmd::cargo::cargo_bin_cmd;
7-
8-
#[test]
9-
fn generates_svg_for_all_fixtures() -> Result<(), Box<dyn std::error::Error>> {
8+
#[cfg(not(target_arch = "wasm32"))]
9+
use libtest_mimic::Failed;
10+
#[cfg(not(target_arch = "wasm32"))]
11+
use similar_asserts::SimpleDiff;
12+
#[cfg(not(target_arch = "wasm32"))]
13+
use tempfile::TempDir;
14+
15+
#[cfg(target_arch = "wasm32")]
16+
fn main() {}
17+
18+
#[cfg(not(target_arch = "wasm32"))]
19+
fn main() {
1020
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
1121
let input_dir = manifest_dir.join("tests/input");
12-
let output_dir = manifest_dir.join("tests/output");
13-
let svg_output_dir = output_dir.join("svg");
14-
let png_output_dir = output_dir.join("png");
1522

16-
assert!(
17-
input_dir.exists(),
18-
"tests/input directory should exist for CLI fixtures"
23+
let in_paths: Vec<_> = fs::read_dir(&input_dir)
24+
.expect("read dir")
25+
.flatten()
26+
.map(|entry| entry.path())
27+
.collect();
28+
29+
assert!(in_paths.len() > 0, "expected tests/input to contain files");
30+
31+
assert_eq!(
32+
in_paths
33+
.iter()
34+
.filter(|input| input.extension().and_then(OsStr::to_str) != Some("mmd"))
35+
.map(|input| input.file_name().unwrap())
36+
.collect::<Vec<_>>(),
37+
Vec::<&OsStr>::new(),
38+
"expected files in tests/input/ to have the .mmd extension"
1939
);
2040

21-
fs::create_dir_all(&svg_output_dir)?;
22-
fs::create_dir_all(&png_output_dir)?;
23-
24-
for entry in fs::read_dir(&input_dir)? {
25-
let entry = entry?;
26-
let path = entry.path();
27-
28-
if path.extension().and_then(|ext| ext.to_str()) != Some("mmd") {
29-
continue;
30-
}
31-
32-
let stem = path
33-
.file_stem()
34-
.and_then(|s| s.to_str())
35-
.ok_or("failed to read fixture stem")?;
36-
let svg_output_path = svg_output_dir.join(format!("{stem}.svg"));
37-
let png_output_path = png_output_dir.join(format!("{stem}.png"));
38-
39-
if svg_output_path.exists() {
40-
fs::remove_file(&svg_output_path)?;
41-
}
42-
43-
if png_output_path.exists() {
44-
fs::remove_file(&png_output_path)?;
45-
}
46-
47-
let mut cmd = cargo_bin_cmd!("oxdraw");
48-
cmd.arg("--input")
49-
.arg(&path)
50-
.arg("--output")
51-
.arg(&svg_output_path)
52-
.arg("--output-format")
53-
.arg("svg");
54-
55-
cmd.assert().success();
56-
57-
let svg_contents = fs::read_to_string(&svg_output_path)?;
58-
assert!(
59-
svg_contents.contains("<svg"),
60-
"{} output should contain an <svg> element",
61-
svg_output_path.display()
62-
);
41+
let tests: Vec<_> = in_paths
42+
.into_iter()
43+
.flat_map(|in_path| {
44+
let stem = in_path.file_stem().unwrap().to_str().unwrap();
45+
[
46+
libtest_mimic::Trial::test(format!("svg_{stem}"), {
47+
let in_path = in_path.clone();
48+
let expected_path = manifest_dir
49+
.join("tests/expected")
50+
.join(format!("{stem}.svg"));
51+
move || test_svg(in_path, expected_path)
52+
}),
53+
libtest_mimic::Trial::test(format!("png_{stem}"), {
54+
let in_path = in_path.clone();
55+
move || smoke_test_png(in_path)
56+
}),
57+
]
58+
})
59+
.collect();
60+
61+
let args = libtest_mimic::Arguments::from_args();
62+
libtest_mimic::run(&args, tests).exit();
63+
}
6364

64-
let mut png_cmd = cargo_bin_cmd!("oxdraw");
65-
png_cmd
66-
.arg("--input")
67-
.arg(&path)
68-
.arg("--output")
69-
.arg(&png_output_path)
70-
.arg("--png");
65+
#[cfg(not(target_arch = "wasm32"))]
66+
fn test_svg(in_path: PathBuf, expected_path: PathBuf) -> Result<(), Failed> {
67+
let temp_dir = TempDir::new().expect("create temp dir");
7168

72-
png_cmd.assert().success();
69+
let stem = in_path.file_stem().unwrap().to_str().unwrap();
70+
let out_path = temp_dir.path().join(format!("{stem}.svg"));
7371

74-
let png_bytes = fs::read(&png_output_path)?;
75-
assert!(
76-
png_bytes.starts_with(b"\x89PNG\r\n\x1a\n"),
77-
"{} output should begin with the PNG magic header",
78-
png_output_path.display()
72+
let mut cmd = cargo_bin_cmd!("oxdraw");
73+
cmd.arg("--input")
74+
.arg(in_path)
75+
.arg("--output")
76+
.arg(&out_path)
77+
.arg("--output-format")
78+
.arg("svg");
79+
80+
cmd.assert().success();
81+
82+
let actual = fs::read_to_string(&out_path)?;
83+
84+
if std::env::var("UPDATE_EXPECTED").is_ok() {
85+
fs::write(expected_path, actual)?;
86+
return Ok(());
87+
}
88+
89+
let expected = fs::read_to_string(&expected_path)?;
90+
91+
if expected != actual {
92+
let diff = format!(
93+
"{}",
94+
SimpleDiff::from_str(&actual, &expected, "actual", "expected")
7995
);
96+
return Err(diff.into());
8097
}
8198

8299
Ok(())
83100
}
101+
102+
#[cfg(not(target_arch = "wasm32"))]
103+
fn smoke_test_png(in_path: PathBuf) -> Result<(), Failed> {
104+
let temp_dir = TempDir::new().expect("create temp dir");
105+
106+
let stem = in_path.file_stem().unwrap().to_str().unwrap();
107+
let out_path = temp_dir.path().join(format!("{stem}.png"));
108+
109+
let mut cmd = cargo_bin_cmd!("oxdraw");
110+
cmd.arg("--input")
111+
.arg(&in_path)
112+
.arg("--output")
113+
.arg(&out_path)
114+
.arg("--output-format")
115+
.arg("png")
116+
.arg("--scale=1"); // greatly speeds up tests
117+
118+
cmd.assert().success();
119+
120+
let png_bytes = fs::read(&out_path)?;
121+
let starts_with_header = png_bytes.starts_with(b"\x89PNG\r\n\x1a\n");
122+
if !starts_with_header {
123+
let _ = temp_dir.keep();
124+
panic!(
125+
"{} output should begin with the PNG magic header",
126+
out_path.display()
127+
);
128+
}
129+
Ok(())
130+
}
Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -41,34 +41,34 @@
4141
<rect x="273.8" y="2154.8" width="38.2" height="28.0" rx="6" ry="6" fill="white" fill-opacity="0.96" stroke="#2d3748" stroke-width="1" />
4242
<text x="292.9" y="2168.8" fill="#2d3748" font-size="13" text-anchor="middle" dominant-baseline="middle" xml:space="preserve">无反馈</text>
4343
</g>
44-
<rect x="188.6" y="1601.0" width="148.8" height="50.0" rx="30" ry="30" fill="#c4f1f9" stroke="#2d3748" stroke-width="2" />
45-
<text x="263.0" y="1626.0" fill="#1a202c" font-size="14" text-anchor="middle" dominant-baseline="middle">6.3 规范文档生成引擎</text>
46-
<rect x="173.8" y="617.0" width="178.4" height="50.0" rx="30" ry="30" fill="#c4f1f9" stroke="#2d3748" stroke-width="2" />
47-
<text x="263.0" y="642.0" fill="#1a202c" font-size="14" text-anchor="middle" dominant-baseline="middle">6.1.1 跨模态融合与冲突检测</text>
44+
<ellipse cx="263.0" cy="150.0" rx="70.0" ry="70.0" fill="#e9d8fd" stroke="#2d3748" stroke-width="2" />
45+
<text x="263.0" y="150.0" fill="#1a202c" font-size="14" text-anchor="middle" dominant-baseline="middle">开始</text>
46+
<rect x="193.0" y="289.0" width="140.0" height="50.0" rx="8" ry="8" fill="#fde68a" stroke="#2d3748" stroke-width="2" />
47+
<text x="263.0" y="314.0" fill="#1a202c" font-size="14" text-anchor="middle" dominant-baseline="middle">接收多模态输入</text>
4848
<rect x="184.9" y="453.0" width="156.2" height="50.0" rx="30" ry="30" fill="#c4f1f9" stroke="#2d3748" stroke-width="2" />
4949
<text x="263.0" y="478.0" fill="#1a202c" font-size="14" text-anchor="middle" dominant-baseline="middle">6.1 多模态输入理解引擎</text>
50-
<rect x="192.3" y="1929.0" width="141.4" height="50.0" rx="30" ry="30" fill="#c4f1f9" stroke="#2d3748" stroke-width="2" />
51-
<text x="263.0" y="1954.0" fill="#1a202c" font-size="14" text-anchor="middle" dominant-baseline="middle">6.4 规范一致性验证</text>
50+
<rect x="173.8" y="617.0" width="178.4" height="50.0" rx="30" ry="30" fill="#c4f1f9" stroke="#2d3748" stroke-width="2" />
51+
<text x="263.0" y="642.0" fill="#1a202c" font-size="14" text-anchor="middle" dominant-baseline="middle">6.1.1 跨模态融合与冲突检测</text>
52+
<rect x="193.0" y="781.0" width="140.0" height="50.0" rx="8" ry="8" fill="#fde68a" stroke="#2d3748" stroke-width="2" />
53+
<text x="263.0" y="806.0" fill="#1a202c" font-size="14" text-anchor="middle" dominant-baseline="middle">生成统一需求表示</text>
54+
<rect x="184.9" y="945.0" width="156.2" height="50.0" rx="30" ry="30" fill="#c4f1f9" stroke="#2d3748" stroke-width="2" />
55+
<text x="263.0" y="970.0" fill="#1a202c" font-size="14" text-anchor="middle" dominant-baseline="middle">6.2 需求完整性检查引擎</text>
5256
<polygon points="263.0,1109.0 333.0,1134.0 263.0,1159.0 193.0,1134.0" fill="#fbcfe8" stroke="#2d3748" stroke-width="2" />
5357
<text x="263.0" y="1134.0" fill="#1a202c" font-size="14" text-anchor="middle" dominant-baseline="middle">发现缺失或冲突?</text>
54-
<ellipse cx="343.7" cy="2282.0" rx="70.0" ry="70.0" fill="#e9d8fd" stroke="#2d3748" stroke-width="2" />
55-
<text x="343.7" y="2282.0" fill="#1a202c" font-size="14" text-anchor="middle" dominant-baseline="middle">结束</text>
56-
<rect x="193.0" y="1437.0" width="140.0" height="50.0" rx="30" ry="30" fill="#c4f1f9" stroke="#2d3748" stroke-width="2" />
57-
<text x="263.0" y="1462.0" fill="#1a202c" font-size="14" text-anchor="middle" dominant-baseline="middle">等待用户澄清</text>
5858
<rect x="193.0" y="1273.0" width="140.0" height="50.0" rx="8" ry="8" fill="#fde68a" stroke="#2d3748" stroke-width="2" />
5959
<text x="263.0" y="1298.0" fill="#1a202c" font-size="14" text-anchor="middle" dominant-baseline="middle">生成澄清问题列表</text>
60-
<ellipse cx="263.0" cy="150.0" rx="70.0" ry="70.0" fill="#e9d8fd" stroke="#2d3748" stroke-width="2" />
61-
<text x="263.0" y="150.0" fill="#1a202c" font-size="14" text-anchor="middle" dominant-baseline="middle">开始</text>
62-
<rect x="184.9" y="945.0" width="156.2" height="50.0" rx="30" ry="30" fill="#c4f1f9" stroke="#2d3748" stroke-width="2" />
63-
<text x="263.0" y="970.0" fill="#1a202c" font-size="14" text-anchor="middle" dominant-baseline="middle">6.2 需求完整性检查引擎</text>
60+
<rect x="193.0" y="1437.0" width="140.0" height="50.0" rx="30" ry="30" fill="#c4f1f9" stroke="#2d3748" stroke-width="2" />
61+
<text x="263.0" y="1462.0" fill="#1a202c" font-size="14" text-anchor="middle" dominant-baseline="middle">等待用户澄清</text>
62+
<rect x="188.6" y="1601.0" width="148.8" height="50.0" rx="30" ry="30" fill="#c4f1f9" stroke="#2d3748" stroke-width="2" />
63+
<text x="263.0" y="1626.0" fill="#1a202c" font-size="14" text-anchor="middle" dominant-baseline="middle">6.3 规范文档生成引擎</text>
6464
<rect x="193.0" y="1765.0" width="140.0" height="50.0" rx="8" ry="8" fill="#fde68a" stroke="#2d3748" stroke-width="2" />
6565
<text x="263.0" y="1790.0" fill="#1a202c" font-size="14" text-anchor="middle" dominant-baseline="middle">生成完整规范文档</text>
66-
<rect x="112.3" y="2257.0" width="141.4" height="50.0" rx="30" ry="30" fill="#c4f1f9" stroke="#2d3748" stroke-width="2" />
67-
<text x="183.0" y="2282.0" fill="#1a202c" font-size="14" text-anchor="middle" dominant-baseline="middle">6.5 交互式规范细化</text>
66+
<rect x="192.3" y="1929.0" width="141.4" height="50.0" rx="30" ry="30" fill="#c4f1f9" stroke="#2d3748" stroke-width="2" />
67+
<text x="263.0" y="1954.0" fill="#1a202c" font-size="14" text-anchor="middle" dominant-baseline="middle">6.4 规范一致性验证</text>
6868
<polygon points="263.0,2093.0 333.0,2118.0 263.0,2143.0 193.0,2118.0" fill="#fbcfe8" stroke="#2d3748" stroke-width="2" />
6969
<text x="263.0" y="2118.0" fill="#1a202c" font-size="14" text-anchor="middle" dominant-baseline="middle">等待用户反馈</text>
70-
<rect x="193.0" y="781.0" width="140.0" height="50.0" rx="8" ry="8" fill="#fde68a" stroke="#2d3748" stroke-width="2" />
71-
<text x="263.0" y="806.0" fill="#1a202c" font-size="14" text-anchor="middle" dominant-baseline="middle">生成统一需求表示</text>
72-
<rect x="193.0" y="289.0" width="140.0" height="50.0" rx="8" ry="8" fill="#fde68a" stroke="#2d3748" stroke-width="2" />
73-
<text x="263.0" y="314.0" fill="#1a202c" font-size="14" text-anchor="middle" dominant-baseline="middle">接收多模态输入</text>
70+
<rect x="112.3" y="2257.0" width="141.4" height="50.0" rx="30" ry="30" fill="#c4f1f9" stroke="#2d3748" stroke-width="2" />
71+
<text x="183.0" y="2282.0" fill="#1a202c" font-size="14" text-anchor="middle" dominant-baseline="middle">6.5 交互式规范细化</text>
72+
<ellipse cx="343.7" cy="2282.0" rx="70.0" ry="70.0" fill="#e9d8fd" stroke="#2d3748" stroke-width="2" />
73+
<text x="343.7" y="2282.0" fill="#1a202c" font-size="14" text-anchor="middle" dominant-baseline="middle">结束</text>
7474
</svg>

0 commit comments

Comments
 (0)