1 // Copyright 2023 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! Various utility functions used in tests.
16 
17 // This file is included directly into integration tests in the
18 // `tests/` directory. These tests are compiled without access to the
19 // rest of the `pdl` crate. To make this work, avoid `use crate::`
20 // statements below.
21 
22 use googletest::prelude::{assert_that, eq};
23 use std::fs;
24 use std::path::Path;
25 
26 /// Format Rust code in `input`.
format_rust(input: &str) -> String27 pub fn format_rust(input: &str) -> String {
28     let syntax_tree = syn::parse_file(input).expect("Could not parse {input:#?} as Rust code");
29     let formatted = prettyplease::unparse(&syntax_tree);
30     format!("#![rustfmt::skip]\n{formatted}")
31 }
32 
33 /// Compare a string with a snapshot file.
34 ///
35 /// The `snapshot_path` is relative to the current working directory
36 /// of the test binary. This depends on how you execute the tests:
37 ///
38 /// * When using `atest`: The current working directory is a random
39 ///   temporary directory. You need to ensure that the snapshot file
40 ///   is installed into this directory. You do this by adding the
41 ///   snapshot to the `data` attribute of your test rule
42 ///
43 /// * When using Cargo: The current working directory is set to
44 ///   `CARGO_MANIFEST_DIR`, which is where the `Cargo.toml` file is
45 ///   found.
46 ///
47 /// If you run the test with Cargo and the `UPDATE_SNAPSHOTS`
48 /// environment variable is set, then the `actual_content` will be
49 /// written to `snapshot_path`. Otherwise the content is compared and
50 /// a panic is triggered if they differ.
51 #[track_caller]
assert_snapshot_eq<P: AsRef<Path>>(snapshot_path: P, actual_content: &str)52 pub fn assert_snapshot_eq<P: AsRef<Path>>(snapshot_path: P, actual_content: &str) {
53     let update_snapshots = std::env::var("UPDATE_SNAPSHOTS").is_ok();
54     let snapshot = snapshot_path.as_ref();
55     let snapshot_content = match fs::read(snapshot) {
56         Ok(content) => content,
57         Err(_) if update_snapshots => Vec::new(),
58         Err(err) => panic!("Could not read snapshot from {}: {}", snapshot.display(), err),
59     };
60     let snapshot_content = String::from_utf8(snapshot_content).expect("Snapshot was not UTF-8");
61 
62     // Normal comparison if UPDATE_SNAPSHOTS is unset.
63     if !update_snapshots {
64         assert_that!(actual_content, eq(&snapshot_content));
65     }
66 
67     // Bail out if we are not using Cargo.
68     if std::env::var("CARGO_MANIFEST_DIR").is_err() {
69         panic!("Please unset UPDATE_SNAPSHOTS if you are not using Cargo");
70     }
71 
72     if actual_content != snapshot_content {
73         eprintln!(
74             "Updating snapshot {}: {} -> {} bytes",
75             snapshot.display(),
76             snapshot_content.len(),
77             actual_content.len()
78         );
79         fs::write(&snapshot_path, actual_content).unwrap_or_else(|err| {
80             panic!("Could not write snapshot to {}: {}", snapshot.display(), err)
81         });
82     }
83 }
84