Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 1 | // 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 | |
| 9 | use std::{convert::TryInto, fs::File, io::Write, path::Path}; |
| 10 | |
| 11 | use indexmap::map::IndexMap as HashMap; |
| 12 | |
| 13 | use assert_cmd::Command; |
| 14 | use autocxx_integration_tests::{build_from_folder, RsFindMode}; |
| 15 | use itertools::Itertools; |
Brian Silverman | f3ec38b | 2022-07-06 20:43:36 -0700 | [diff] [blame] | 16 | use tempfile::{tempdir, TempDir}; |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 17 | |
| 18 | static MAIN_RS: &str = concat!( |
| 19 | include_str!("../../../demo/src/main.rs"), |
| 20 | "#[link(name = \"autocxx-demo\")]\nextern \"C\" {}" |
| 21 | ); |
| 22 | static INPUT_H: &str = include_str!("../../../demo/src/input.h"); |
| 23 | static BLANK: &str = "// Blank autocxx placeholder"; |
| 24 | |
| 25 | static MAIN2_RS: &str = concat!( |
| 26 | include_str!("data/main2.rs"), |
| 27 | "#[link(name = \"autocxx-demo\")]\nextern \"C\" {}" |
| 28 | ); |
| 29 | static DIRECTIVE1_RS: &str = include_str!("data/directive1.rs"); |
| 30 | static DIRECTIVE2_RS: &str = include_str!("data/directive2.rs"); |
| 31 | static INPUT2_H: &str = include_str!("data/input2.h"); |
| 32 | static INPUT3_H: &str = include_str!("data/input3.h"); |
| 33 | |
Brian Silverman | f3ec38b | 2022-07-06 20:43:36 -0700 | [diff] [blame] | 34 | const KEEP_TEMPDIRS: bool = true; |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 35 | |
| 36 | #[test] |
| 37 | fn 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 | |
| 43 | enum RsGenMode { |
| 44 | Single, |
| 45 | Archive, |
| 46 | } |
| 47 | |
| 48 | fn base_test<F>( |
| 49 | tmp_dir: &TempDir, |
| 50 | rs_gen_mode: RsGenMode, |
| 51 | arg_modifier: F, |
| 52 | ) -> Result<(), Box<dyn std::error::Error>> |
| 53 | where |
| 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 | |
| 70 | fn 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>> |
| 77 | where |
| 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 Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 91 | .arg("--gen-cpp") |
| 92 | .arg("--generate-cxx-h"); |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 93 | 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] |
| 110 | fn test_gen() -> Result<(), Box<dyn std::error::Error>> { |
Brian Silverman | f3ec38b | 2022-07-06 20:43:36 -0700 | [diff] [blame] | 111 | let tmp_dir = tempdir()?; |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 112 | base_test(&tmp_dir, RsGenMode::Single, |_| {})?; |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 113 | 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] |
| 129 | fn test_gen_archive() -> Result<(), Box<dyn std::error::Error>> { |
Brian Silverman | f3ec38b | 2022-07-06 20:43:36 -0700 | [diff] [blame] | 130 | let tmp_dir = tempdir()?; |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 131 | base_test(&tmp_dir, RsGenMode::Archive, |_| {})?; |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 132 | 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 Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 147 | fn 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] |
| 171 | fn 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 Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 195 | fn test_gen_multiple_in_archive() -> Result<(), Box<dyn std::error::Error>> { |
Brian Silverman | f3ec38b | 2022-07-06 20:43:36 -0700 | [diff] [blame] | 196 | let tmp_dir = tempdir()?; |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 197 | |
| 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 Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 213 | // We've asked to create 8 C++ files, mostly blank. Build 'em all. |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 214 | let cpp_files = (0..7).map(|id| format!("gen{id}.cc")).collect_vec(); |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 215 | 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] |
| 231 | fn test_include_prefixes() -> Result<(), Box<dyn std::error::Error>> { |
Brian Silverman | f3ec38b | 2022-07-06 20:43:36 -0700 | [diff] [blame] | 232 | let tmp_dir = tempdir()?; |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 233 | 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 Silverman | f3ec38b | 2022-07-06 20:43:36 -0700 | [diff] [blame] | 239 | .arg("3") |
| 240 | .arg("--fix-rs-include-name"); |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 241 | })?; |
| 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] |
| 249 | fn test_gen_fixed_num() -> Result<(), Box<dyn std::error::Error>> { |
Brian Silverman | f3ec38b | 2022-07-06 20:43:36 -0700 | [diff] [blame] | 250 | let tmp_dir = tempdir()?; |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 251 | 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 Silverman | f3ec38b | 2022-07-06 20:43:36 -0700 | [diff] [blame] | 255 | .arg("--fix-rs-include-name") |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 256 | .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 Silverman | f3ec38b | 2022-07-06 20:43:36 -0700 | [diff] [blame] | 265 | assert_contentful(&tmp_dir, "gen0.include.rs"); |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 266 | assert_contentful(&tmp_dir, "test.d"); |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 267 | let r = build_from_folder( |
| 268 | tmp_dir.path(), |
| 269 | &tmp_dir.path().join("demo/main.rs"), |
Brian Silverman | f3ec38b | 2022-07-06 20:43:36 -0700 | [diff] [blame] | 270 | vec![tmp_dir.path().join("gen0.include.rs")], |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 271 | &["gen0.cc"], |
Brian Silverman | f3ec38b | 2022-07-06 20:43:36 -0700 | [diff] [blame] | 272 | RsFindMode::AutocxxRsFile, |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 273 | ); |
| 274 | if KEEP_TEMPDIRS { |
| 275 | println!("Tempdir: {:?}", tmp_dir.into_path().to_str()); |
| 276 | } |
| 277 | r.unwrap(); |
| 278 | Ok(()) |
| 279 | } |
| 280 | |
| 281 | #[test] |
| 282 | fn test_gen_preprocess() -> Result<(), Box<dyn std::error::Error>> { |
Brian Silverman | f3ec38b | 2022-07-06 20:43:36 -0700 | [diff] [blame] | 283 | let tmp_dir = tempdir()?; |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 284 | 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] |
| 296 | fn test_gen_repro() -> Result<(), Box<dyn std::error::Error>> { |
Brian Silverman | f3ec38b | 2022-07-06 20:43:36 -0700 | [diff] [blame] | 297 | let tmp_dir = tempdir()?; |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 298 | 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 | |
| 309 | fn write_to_file(dir: &Path, filename: &str, content: &[u8]) { |
| 310 | let path = dir.join(filename); |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 311 | let mut f = File::create(path).expect("Unable to create file"); |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 312 | f.write_all(content).expect("Unable to write file"); |
| 313 | } |
| 314 | |
| 315 | fn 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 Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 322 | "File {fname} is empty" |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 323 | ); |
| 324 | } |
| 325 | |
| 326 | fn 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 | |
| 339 | fn assert_contains(outdir: &TempDir, fname: &str, pattern: &str) { |
| 340 | let p = outdir.path().join(fname); |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 341 | let content = std::fs::read_to_string(p).expect(fname); |
| 342 | eprintln!("content = {content}"); |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 343 | assert!(content.contains(pattern)); |
| 344 | } |