blob: 4ec39428e35cc36a85f5fa923de8d77821daa5ca [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;
Brian Silvermanf3ec38b2022-07-06 20:43:36 -070016use tempfile::{tempdir, TempDir};
Brian Silverman4e662aa2022-05-11 23:10:19 -070017
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
Brian Silvermanf3ec38b2022-07-06 20:43:36 -070034const KEEP_TEMPDIRS: bool = true;
Brian Silverman4e662aa2022-05-11 23:10:19 -070035
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())
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070091 .arg("--gen-cpp")
92 .arg("--generate-cxx-h");
Brian Silverman4e662aa2022-05-11 23:10:19 -070093 cmd.arg(match rs_gen_mode {
94 RsGenMode::Single => "--gen-rs-include",
95 RsGenMode::Archive => "--gen-rs-archive",
96 });
97 for file in files_to_process {
98 cmd.arg(demo_code_dir.join(file));
99 }
100 let output = cmd.output();
101 if let Ok(output) = output {
102 eprintln!("Cmd stdout: {:?}", std::str::from_utf8(&output.stdout));
103 eprintln!("Cmd stderr: {:?}", std::str::from_utf8(&output.stderr));
104 }
105 cmd.assert().success();
106 Ok(())
107}
108
109#[test]
110fn test_gen() -> Result<(), Box<dyn std::error::Error>> {
Brian Silvermanf3ec38b2022-07-06 20:43:36 -0700111 let tmp_dir = tempdir()?;
Brian Silverman4e662aa2022-05-11 23:10:19 -0700112 base_test(&tmp_dir, RsGenMode::Single, |_| {})?;
Brian Silverman4e662aa2022-05-11 23:10:19 -0700113 std::env::set_var("OUT_DIR", tmp_dir.path().to_str().unwrap());
114 let r = build_from_folder(
115 tmp_dir.path(),
116 &tmp_dir.path().join("demo/main.rs"),
117 vec![tmp_dir.path().join("autocxx-ffi-default-gen.rs")],
118 &["gen0.cc"],
119 RsFindMode::AutocxxRs,
120 );
121 if KEEP_TEMPDIRS {
122 println!("Tempdir: {:?}", tmp_dir.into_path().to_str());
123 }
124 r.unwrap();
125 Ok(())
126}
127
128#[test]
129fn test_gen_archive() -> Result<(), Box<dyn std::error::Error>> {
Brian Silvermanf3ec38b2022-07-06 20:43:36 -0700130 let tmp_dir = tempdir()?;
Brian Silverman4e662aa2022-05-11 23:10:19 -0700131 base_test(&tmp_dir, RsGenMode::Archive, |_| {})?;
Brian Silverman4e662aa2022-05-11 23:10:19 -0700132 let r = build_from_folder(
133 tmp_dir.path(),
134 &tmp_dir.path().join("demo/main.rs"),
135 vec![tmp_dir.path().join("gen.rs.json")],
136 &["gen0.cc"],
137 RsFindMode::AutocxxRsArchive,
138 );
139 if KEEP_TEMPDIRS {
140 println!("Tempdir: {:?}", tmp_dir.into_path().to_str());
141 }
142 r.unwrap();
143 Ok(())
144}
145
146#[test]
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700147fn test_gen_archive_first_entry() -> Result<(), Box<dyn std::error::Error>> {
148 let tmp_dir = tempdir()?;
149 base_test(&tmp_dir, RsGenMode::Archive, |_| {})?;
150 let r = build_from_folder(
151 tmp_dir.path(),
152 &tmp_dir.path().join("demo/main.rs"),
153 vec![tmp_dir.path().join("gen.rs.json")],
154 &["gen0.cc"],
155 RsFindMode::Custom(Box::new(|path: &Path| {
156 std::env::set_var(
157 "AUTOCXX_RS_JSON_ARCHIVE",
158 std::env::join_paths([&path.join("gen.rs.json"), Path::new("/nonexistent")])
159 .unwrap(),
160 )
161 })),
162 );
163 if KEEP_TEMPDIRS {
164 println!("Tempdir: {:?}", tmp_dir.into_path().to_str());
165 }
166 r.unwrap();
167 Ok(())
168}
169
170#[test]
171fn test_gen_archive_second_entry() -> Result<(), Box<dyn std::error::Error>> {
172 let tmp_dir = tempdir()?;
173 base_test(&tmp_dir, RsGenMode::Archive, |_| {})?;
174 let r = build_from_folder(
175 tmp_dir.path(),
176 &tmp_dir.path().join("demo/main.rs"),
177 vec![tmp_dir.path().join("gen.rs.json")],
178 &["gen0.cc"],
179 RsFindMode::Custom(Box::new(|path: &Path| {
180 std::env::set_var(
181 "AUTOCXX_RS_JSON_ARCHIVE",
182 std::env::join_paths([Path::new("/nonexistent"), &path.join("gen.rs.json")])
183 .unwrap(),
184 )
185 })),
186 );
187 if KEEP_TEMPDIRS {
188 println!("Tempdir: {:?}", tmp_dir.into_path().to_str());
189 }
190 r.unwrap();
191 Ok(())
192}
193
194#[test]
Brian Silverman4e662aa2022-05-11 23:10:19 -0700195fn test_gen_multiple_in_archive() -> Result<(), Box<dyn std::error::Error>> {
Brian Silvermanf3ec38b2022-07-06 20:43:36 -0700196 let tmp_dir = tempdir()?;
Brian Silverman4e662aa2022-05-11 23:10:19 -0700197
198 let mut files = HashMap::new();
199 files.insert("input2.h", INPUT2_H.as_bytes());
200 files.insert("input3.h", INPUT3_H.as_bytes());
201 files.insert("main.rs", MAIN2_RS.as_bytes());
202 files.insert("directive1.rs", DIRECTIVE1_RS.as_bytes());
203 files.insert("directive2.rs", DIRECTIVE2_RS.as_bytes());
204 base_test_ex(
205 &tmp_dir,
206 RsGenMode::Archive,
207 |cmd| {
208 cmd.arg("--generate-exact").arg("8");
209 },
210 files,
211 vec!["directive1.rs", "directive2.rs"],
212 )?;
Brian Silverman4e662aa2022-05-11 23:10:19 -0700213 // We've asked to create 8 C++ files, mostly blank. Build 'em all.
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700214 let cpp_files = (0..7).map(|id| format!("gen{id}.cc")).collect_vec();
Brian Silverman4e662aa2022-05-11 23:10:19 -0700215 let cpp_files = cpp_files.iter().map(|s| s.as_str()).collect_vec();
216 let r = build_from_folder(
217 tmp_dir.path(),
218 &tmp_dir.path().join("demo/main.rs"),
219 vec![tmp_dir.path().join("gen.rs.json")],
220 &cpp_files,
221 RsFindMode::AutocxxRsArchive,
222 );
223 if KEEP_TEMPDIRS {
224 println!("Tempdir: {:?}", tmp_dir.into_path().to_str());
225 }
226 r.unwrap();
227 Ok(())
228}
229
230#[test]
231fn test_include_prefixes() -> Result<(), Box<dyn std::error::Error>> {
Brian Silvermanf3ec38b2022-07-06 20:43:36 -0700232 let tmp_dir = tempdir()?;
Brian Silverman4e662aa2022-05-11 23:10:19 -0700233 base_test(&tmp_dir, RsGenMode::Single, |cmd| {
234 cmd.arg("--cxx-h-path")
235 .arg("foo/")
236 .arg("--cxxgen-h-path")
237 .arg("bar/")
238 .arg("--generate-exact")
Brian Silvermanf3ec38b2022-07-06 20:43:36 -0700239 .arg("3")
240 .arg("--fix-rs-include-name");
Brian Silverman4e662aa2022-05-11 23:10:19 -0700241 })?;
242 assert_contains(&tmp_dir, "autocxxgen0.h", "foo/cxx.h");
243 // Currently we don't test cxxgen-h-path because we build the demo code
244 // which doesn't refer to generated cxx header code.
245 Ok(())
246}
247
248#[test]
249fn test_gen_fixed_num() -> Result<(), Box<dyn std::error::Error>> {
Brian Silvermanf3ec38b2022-07-06 20:43:36 -0700250 let tmp_dir = tempdir()?;
Brian Silverman4e662aa2022-05-11 23:10:19 -0700251 let depfile = tmp_dir.path().join("test.d");
252 base_test(&tmp_dir, RsGenMode::Single, |cmd| {
253 cmd.arg("--generate-exact")
254 .arg("2")
Brian Silvermanf3ec38b2022-07-06 20:43:36 -0700255 .arg("--fix-rs-include-name")
Brian Silverman4e662aa2022-05-11 23:10:19 -0700256 .arg("--depfile")
257 .arg(depfile);
258 })?;
259 assert_contentful(&tmp_dir, "gen0.cc");
260 assert_contentful(&tmp_dir, "gen0.h");
261 assert_not_contentful(&tmp_dir, "gen1.cc");
262 assert_contentful(&tmp_dir, "autocxxgen0.h");
263 assert_not_contentful(&tmp_dir, "gen1.h");
264 assert_not_contentful(&tmp_dir, "autocxxgen1.h");
Brian Silvermanf3ec38b2022-07-06 20:43:36 -0700265 assert_contentful(&tmp_dir, "gen0.include.rs");
Brian Silverman4e662aa2022-05-11 23:10:19 -0700266 assert_contentful(&tmp_dir, "test.d");
Brian Silverman4e662aa2022-05-11 23:10:19 -0700267 let r = build_from_folder(
268 tmp_dir.path(),
269 &tmp_dir.path().join("demo/main.rs"),
Brian Silvermanf3ec38b2022-07-06 20:43:36 -0700270 vec![tmp_dir.path().join("gen0.include.rs")],
Brian Silverman4e662aa2022-05-11 23:10:19 -0700271 &["gen0.cc"],
Brian Silvermanf3ec38b2022-07-06 20:43:36 -0700272 RsFindMode::AutocxxRsFile,
Brian Silverman4e662aa2022-05-11 23:10:19 -0700273 );
274 if KEEP_TEMPDIRS {
275 println!("Tempdir: {:?}", tmp_dir.into_path().to_str());
276 }
277 r.unwrap();
278 Ok(())
279}
280
281#[test]
282fn test_gen_preprocess() -> Result<(), Box<dyn std::error::Error>> {
Brian Silvermanf3ec38b2022-07-06 20:43:36 -0700283 let tmp_dir = tempdir()?;
Brian Silverman4e662aa2022-05-11 23:10:19 -0700284 let prepro_path = tmp_dir.path().join("preprocessed.h");
285 base_test(&tmp_dir, RsGenMode::Single, |cmd| {
286 cmd.env("AUTOCXX_PREPROCESS", prepro_path.to_str().unwrap());
287 })?;
288 assert_contentful(&tmp_dir, "preprocessed.h");
289 // Check that a random thing from one of the headers in
290 // `ALL_KNOWN_SYSTEM_HEADERS` is included.
291 assert!(std::fs::read_to_string(prepro_path)?.contains("integer_sequence"));
292 Ok(())
293}
294
295#[test]
296fn test_gen_repro() -> Result<(), Box<dyn std::error::Error>> {
Brian Silvermanf3ec38b2022-07-06 20:43:36 -0700297 let tmp_dir = tempdir()?;
Brian Silverman4e662aa2022-05-11 23:10:19 -0700298 let repro_path = tmp_dir.path().join("repro.json");
299 base_test(&tmp_dir, RsGenMode::Single, |cmd| {
300 cmd.env("AUTOCXX_REPRO_CASE", repro_path.to_str().unwrap());
301 })?;
302 assert_contentful(&tmp_dir, "repro.json");
303 // Check that a random thing from one of the headers in
304 // `ALL_KNOWN_SYSTEM_HEADERS` is included.
305 assert!(std::fs::read_to_string(repro_path)?.contains("integer_sequence"));
306 Ok(())
307}
308
309fn write_to_file(dir: &Path, filename: &str, content: &[u8]) {
310 let path = dir.join(filename);
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700311 let mut f = File::create(path).expect("Unable to create file");
Brian Silverman4e662aa2022-05-11 23:10:19 -0700312 f.write_all(content).expect("Unable to write file");
313}
314
315fn assert_contentful(outdir: &TempDir, fname: &str) {
316 let p = outdir.path().join(fname);
317 if !p.exists() {
318 panic!("File {} didn't exist", p.to_string_lossy());
319 }
320 assert!(
321 p.metadata().unwrap().len() > BLANK.len().try_into().unwrap(),
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700322 "File {fname} is empty"
Brian Silverman4e662aa2022-05-11 23:10:19 -0700323 );
324}
325
326fn assert_not_contentful(outdir: &TempDir, fname: &str) {
327 let p = outdir.path().join(fname);
328 if !p.exists() {
329 panic!("File {} didn't exist", p.to_string_lossy());
330 }
331 assert!(
332 p.metadata().unwrap().len() <= BLANK.len().try_into().unwrap(),
333 "File {} is not empty; it contains {}",
334 fname,
335 std::fs::read_to_string(&p).unwrap_or_default()
336 );
337}
338
339fn assert_contains(outdir: &TempDir, fname: &str, pattern: &str) {
340 let p = outdir.path().join(fname);
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700341 let content = std::fs::read_to_string(p).expect(fname);
342 eprintln!("content = {content}");
Brian Silverman4e662aa2022-05-11 23:10:19 -0700343 assert!(content.contains(pattern));
344}