blob: d0e671e31dc4dfd011c5fb950b545d5856e0fd90 [file] [log] [blame]
Brian Silverman4e662aa2022-05-11 23:10:19 -07001// Copyright 2020 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use std::{convert::TryInto, fs::File, io::Write, path::Path};
10
11use indexmap::map::IndexMap as HashMap;
12
13use assert_cmd::Command;
14use autocxx_integration_tests::{build_from_folder, RsFindMode};
15use itertools::Itertools;
16use tempdir::TempDir;
17
18static MAIN_RS: &str = concat!(
19 include_str!("../../../demo/src/main.rs"),
20 "#[link(name = \"autocxx-demo\")]\nextern \"C\" {}"
21);
22static INPUT_H: &str = include_str!("../../../demo/src/input.h");
23static BLANK: &str = "// Blank autocxx placeholder";
24
25static MAIN2_RS: &str = concat!(
26 include_str!("data/main2.rs"),
27 "#[link(name = \"autocxx-demo\")]\nextern \"C\" {}"
28);
29static DIRECTIVE1_RS: &str = include_str!("data/directive1.rs");
30static DIRECTIVE2_RS: &str = include_str!("data/directive2.rs");
31static INPUT2_H: &str = include_str!("data/input2.h");
32static INPUT3_H: &str = include_str!("data/input3.h");
33
34const KEEP_TEMPDIRS: bool = false;
35
36#[test]
37fn test_help() -> Result<(), Box<dyn std::error::Error>> {
38 let mut cmd = Command::cargo_bin("autocxx-gen")?;
39 cmd.arg("-h").assert().success();
40 Ok(())
41}
42
43enum RsGenMode {
44 Single,
45 Archive,
46}
47
48fn base_test<F>(
49 tmp_dir: &TempDir,
50 rs_gen_mode: RsGenMode,
51 arg_modifier: F,
52) -> Result<(), Box<dyn std::error::Error>>
53where
54 F: FnOnce(&mut Command),
55{
56 let mut standard_files = HashMap::new();
57 standard_files.insert("input.h", INPUT_H.as_bytes());
58 standard_files.insert("main.rs", MAIN_RS.as_bytes());
59 let result = base_test_ex(
60 tmp_dir,
61 rs_gen_mode,
62 arg_modifier,
63 standard_files,
64 vec!["main.rs"],
65 );
66 assert_contentful(tmp_dir, "gen0.cc");
67 result
68}
69
70fn base_test_ex<F>(
71 tmp_dir: &TempDir,
72 rs_gen_mode: RsGenMode,
73 arg_modifier: F,
74 files_to_write: HashMap<&str, &[u8]>,
75 files_to_process: Vec<&str>,
76) -> Result<(), Box<dyn std::error::Error>>
77where
78 F: FnOnce(&mut Command),
79{
80 let demo_code_dir = tmp_dir.path().join("demo");
81 std::fs::create_dir(&demo_code_dir).unwrap();
82 for (filename, content) in files_to_write {
83 write_to_file(&demo_code_dir, filename, content);
84 }
85 let mut cmd = Command::cargo_bin("autocxx-gen")?;
86 arg_modifier(&mut cmd);
87 cmd.arg("--inc")
88 .arg(demo_code_dir.to_str().unwrap())
89 .arg("--outdir")
90 .arg(tmp_dir.path().to_str().unwrap())
91 .arg("--gen-cpp");
92 cmd.arg(match rs_gen_mode {
93 RsGenMode::Single => "--gen-rs-include",
94 RsGenMode::Archive => "--gen-rs-archive",
95 });
96 for file in files_to_process {
97 cmd.arg(demo_code_dir.join(file));
98 }
99 let output = cmd.output();
100 if let Ok(output) = output {
101 eprintln!("Cmd stdout: {:?}", std::str::from_utf8(&output.stdout));
102 eprintln!("Cmd stderr: {:?}", std::str::from_utf8(&output.stderr));
103 }
104 cmd.assert().success();
105 Ok(())
106}
107
108#[test]
109fn test_gen() -> Result<(), Box<dyn std::error::Error>> {
110 let tmp_dir = TempDir::new("example")?;
111 base_test(&tmp_dir, RsGenMode::Single, |_| {})?;
112 File::create(tmp_dir.path().join("cxx.h"))
113 .and_then(|mut cxx_h| cxx_h.write_all(autocxx_engine::HEADER.as_bytes()))?;
114 std::env::set_var("OUT_DIR", tmp_dir.path().to_str().unwrap());
115 let r = build_from_folder(
116 tmp_dir.path(),
117 &tmp_dir.path().join("demo/main.rs"),
118 vec![tmp_dir.path().join("autocxx-ffi-default-gen.rs")],
119 &["gen0.cc"],
120 RsFindMode::AutocxxRs,
121 );
122 if KEEP_TEMPDIRS {
123 println!("Tempdir: {:?}", tmp_dir.into_path().to_str());
124 }
125 r.unwrap();
126 Ok(())
127}
128
129#[test]
130fn test_gen_archive() -> Result<(), Box<dyn std::error::Error>> {
131 let tmp_dir = TempDir::new("example")?;
132 base_test(&tmp_dir, RsGenMode::Archive, |_| {})?;
133 File::create(tmp_dir.path().join("cxx.h"))
134 .and_then(|mut cxx_h| cxx_h.write_all(autocxx_engine::HEADER.as_bytes()))?;
135 let r = build_from_folder(
136 tmp_dir.path(),
137 &tmp_dir.path().join("demo/main.rs"),
138 vec![tmp_dir.path().join("gen.rs.json")],
139 &["gen0.cc"],
140 RsFindMode::AutocxxRsArchive,
141 );
142 if KEEP_TEMPDIRS {
143 println!("Tempdir: {:?}", tmp_dir.into_path().to_str());
144 }
145 r.unwrap();
146 Ok(())
147}
148
149#[test]
150fn test_gen_multiple_in_archive() -> Result<(), Box<dyn std::error::Error>> {
151 let tmp_dir = TempDir::new("example")?;
152
153 let mut files = HashMap::new();
154 files.insert("input2.h", INPUT2_H.as_bytes());
155 files.insert("input3.h", INPUT3_H.as_bytes());
156 files.insert("main.rs", MAIN2_RS.as_bytes());
157 files.insert("directive1.rs", DIRECTIVE1_RS.as_bytes());
158 files.insert("directive2.rs", DIRECTIVE2_RS.as_bytes());
159 base_test_ex(
160 &tmp_dir,
161 RsGenMode::Archive,
162 |cmd| {
163 cmd.arg("--generate-exact").arg("8");
164 },
165 files,
166 vec!["directive1.rs", "directive2.rs"],
167 )?;
168 File::create(tmp_dir.path().join("cxx.h"))
169 .and_then(|mut cxx_h| cxx_h.write_all(autocxx_engine::HEADER.as_bytes()))?;
170 // We've asked to create 8 C++ files, mostly blank. Build 'em all.
171 let cpp_files = (0..7).map(|id| format!("gen{}.cc", id)).collect_vec();
172 let cpp_files = cpp_files.iter().map(|s| s.as_str()).collect_vec();
173 let r = build_from_folder(
174 tmp_dir.path(),
175 &tmp_dir.path().join("demo/main.rs"),
176 vec![tmp_dir.path().join("gen.rs.json")],
177 &cpp_files,
178 RsFindMode::AutocxxRsArchive,
179 );
180 if KEEP_TEMPDIRS {
181 println!("Tempdir: {:?}", tmp_dir.into_path().to_str());
182 }
183 r.unwrap();
184 Ok(())
185}
186
187#[test]
188fn test_include_prefixes() -> Result<(), Box<dyn std::error::Error>> {
189 let tmp_dir = TempDir::new("example")?;
190 base_test(&tmp_dir, RsGenMode::Single, |cmd| {
191 cmd.arg("--cxx-h-path")
192 .arg("foo/")
193 .arg("--cxxgen-h-path")
194 .arg("bar/")
195 .arg("--generate-exact")
196 .arg("3");
197 })?;
198 assert_contains(&tmp_dir, "autocxxgen0.h", "foo/cxx.h");
199 // Currently we don't test cxxgen-h-path because we build the demo code
200 // which doesn't refer to generated cxx header code.
201 Ok(())
202}
203
204#[test]
205fn test_gen_fixed_num() -> Result<(), Box<dyn std::error::Error>> {
206 let tmp_dir = TempDir::new("example")?;
207 let depfile = tmp_dir.path().join("test.d");
208 base_test(&tmp_dir, RsGenMode::Single, |cmd| {
209 cmd.arg("--generate-exact")
210 .arg("2")
211 .arg("--depfile")
212 .arg(depfile);
213 })?;
214 assert_contentful(&tmp_dir, "gen0.cc");
215 assert_contentful(&tmp_dir, "gen0.h");
216 assert_not_contentful(&tmp_dir, "gen1.cc");
217 assert_contentful(&tmp_dir, "autocxxgen0.h");
218 assert_not_contentful(&tmp_dir, "gen1.h");
219 assert_not_contentful(&tmp_dir, "autocxxgen1.h");
220 assert_contentful(&tmp_dir, "autocxx-ffi-default-gen.rs");
221 assert_contentful(&tmp_dir, "test.d");
222 File::create(tmp_dir.path().join("cxx.h"))
223 .and_then(|mut cxx_h| cxx_h.write_all(autocxx_engine::HEADER.as_bytes()))?;
224 let r = build_from_folder(
225 tmp_dir.path(),
226 &tmp_dir.path().join("demo/main.rs"),
227 vec![tmp_dir.path().join("autocxx-ffi-default-gen.rs")],
228 &["gen0.cc"],
229 RsFindMode::AutocxxRs,
230 );
231 if KEEP_TEMPDIRS {
232 println!("Tempdir: {:?}", tmp_dir.into_path().to_str());
233 }
234 r.unwrap();
235 Ok(())
236}
237
238#[test]
239fn test_gen_preprocess() -> Result<(), Box<dyn std::error::Error>> {
240 let tmp_dir = TempDir::new("example")?;
241 let prepro_path = tmp_dir.path().join("preprocessed.h");
242 base_test(&tmp_dir, RsGenMode::Single, |cmd| {
243 cmd.env("AUTOCXX_PREPROCESS", prepro_path.to_str().unwrap());
244 })?;
245 assert_contentful(&tmp_dir, "preprocessed.h");
246 // Check that a random thing from one of the headers in
247 // `ALL_KNOWN_SYSTEM_HEADERS` is included.
248 assert!(std::fs::read_to_string(prepro_path)?.contains("integer_sequence"));
249 Ok(())
250}
251
252#[test]
253fn test_gen_repro() -> Result<(), Box<dyn std::error::Error>> {
254 let tmp_dir = TempDir::new("example")?;
255 let repro_path = tmp_dir.path().join("repro.json");
256 base_test(&tmp_dir, RsGenMode::Single, |cmd| {
257 cmd.env("AUTOCXX_REPRO_CASE", repro_path.to_str().unwrap());
258 })?;
259 assert_contentful(&tmp_dir, "repro.json");
260 // Check that a random thing from one of the headers in
261 // `ALL_KNOWN_SYSTEM_HEADERS` is included.
262 assert!(std::fs::read_to_string(repro_path)?.contains("integer_sequence"));
263 Ok(())
264}
265
266fn write_to_file(dir: &Path, filename: &str, content: &[u8]) {
267 let path = dir.join(filename);
268 let mut f = File::create(&path).expect("Unable to create file");
269 f.write_all(content).expect("Unable to write file");
270}
271
272fn assert_contentful(outdir: &TempDir, fname: &str) {
273 let p = outdir.path().join(fname);
274 if !p.exists() {
275 panic!("File {} didn't exist", p.to_string_lossy());
276 }
277 assert!(
278 p.metadata().unwrap().len() > BLANK.len().try_into().unwrap(),
279 "File {} is empty",
280 fname
281 );
282}
283
284fn assert_not_contentful(outdir: &TempDir, fname: &str) {
285 let p = outdir.path().join(fname);
286 if !p.exists() {
287 panic!("File {} didn't exist", p.to_string_lossy());
288 }
289 assert!(
290 p.metadata().unwrap().len() <= BLANK.len().try_into().unwrap(),
291 "File {} is not empty; it contains {}",
292 fname,
293 std::fs::read_to_string(&p).unwrap_or_default()
294 );
295}
296
297fn assert_contains(outdir: &TempDir, fname: &str, pattern: &str) {
298 let p = outdir.path().join(fname);
299 let content = std::fs::read_to_string(&p).expect(fname);
300 eprintln!("content = {}", content);
301 assert!(content.contains(pattern));
302}