// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use file_header::*;
use std::{fs, io, path};
#[test]
fn single_line_checker_finds_header_when_present() {
let input = r#"foo
some license
bar"#;
assert!(test_checker().check(&mut input.as_bytes()).unwrap())
}
#[test]
fn single_line_checker_doesnt_find_header_when_missing() {
let input = r#"foo
wrong license
bar"#;
assert!(!test_checker().check(&mut input.as_bytes()).unwrap())
}
#[test]
fn adds_header_with_empty_delimiters() {
let file = tempfile::Builder::new().suffix(".rs").tempfile().unwrap();
fs::write(file.path(), r#"not a license"#).unwrap();
test_header().add_header_if_missing(file.path()).unwrap();
assert_eq!(
"// some license etc etc etc
not a license",
fs::read_to_string(file.path()).unwrap()
);
}
#[test]
fn adds_header_with_nonempty_delimiters() {
let file = tempfile::Builder::new().suffix(".c").tempfile().unwrap();
fs::write(file.path(), r#"not a license"#).unwrap();
test_header().add_header_if_missing(file.path()).unwrap();
assert_eq!(
"/*
* some license etc etc etc
*/
not a license",
fs::read_to_string(file.path()).unwrap()
);
}
#[test]
fn adds_header_trim_trailing_whitespace() {
let file = tempfile::Builder::new().suffix(".c").tempfile().unwrap();
fs::write(file.path(), r#"not a license"#).unwrap();
test_header_with_blank_lines_and_trailing_whitespace()
.add_header_if_missing(file.path())
.unwrap();
assert_eq!(
"/*
* some license
* line with trailing whitespace.
*
* etc
*/
not a license",
fs::read_to_string(file.path()).unwrap()
);
}
#[test]
fn doesnt_add_header_when_already_present() {
let file = tempfile::Builder::new().suffix(".rs").tempfile().unwrap();
let initial_content = r#"
// some license etc etc etc already present
not a license"#;
fs::write(file.path(), initial_content).unwrap();
test_header().add_header_if_missing(file.path()).unwrap();
assert_eq!(initial_content, fs::read_to_string(file.path()).unwrap());
}
#[test]
fn adds_header_after_magic_first_line() {
let file = tempfile::Builder::new().suffix(".xml").tempfile().unwrap();
fs::write(
file.path(),
r#"
"#,
)
.unwrap();
test_header().add_header_if_missing(file.path()).unwrap();
assert_eq!(
r#"
"#,
fs::read_to_string(file.path()).unwrap()
);
}
#[test]
fn header_present_on_binary_file_produces_error_invalid_data() {
let file = tempfile::Builder::new().suffix(".xml").tempfile().unwrap();
fs::write(file.path(), [0xFF_u8; 100]).unwrap();
assert_eq!(
io::ErrorKind::InvalidData,
test_header()
.header_present(&mut fs::File::open(file.path()).unwrap())
.unwrap_err()
.kind()
);
}
#[test]
fn deletes_header_with_empty_delimiters() {
let file = tempfile::Builder::new().suffix(".rs").tempfile().unwrap();
fs::write(
file.path(),
r#"// some license etc etc etc
not a license"#,
)
.unwrap();
let ok = test_header().delete_header_if_present(file.path()).unwrap();
assert!(ok);
assert_eq!("not a license", fs::read_to_string(file.path()).unwrap());
}
#[test]
fn deletes_header_with_nonempty_delimiters() {
let file = tempfile::Builder::new().suffix(".c").tempfile().unwrap();
fs::write(
file.path(),
r#"/*
* some license etc etc etc
*/
not a license"#,
)
.unwrap();
let ok = test_header().delete_header_if_present(file.path()).unwrap();
assert!(ok);
assert_eq!("not a license", fs::read_to_string(file.path()).unwrap());
}
#[test]
fn deletes_header_trim_trailing_whitespace() {
let file = tempfile::Builder::new().suffix(".c").tempfile().unwrap();
fs::write(
file.path(),
r#"/*
* some license
* line with trailing whitespace.
*
* etc
*/
not a license"#,
)
.unwrap();
let ok = test_header_with_blank_lines_and_trailing_whitespace()
.delete_header_if_present(file.path())
.unwrap();
assert!(ok);
assert_eq!("not a license", fs::read_to_string(file.path()).unwrap());
}
#[test]
fn deletes_header_after_magic_first_line() {
let file = tempfile::Builder::new().suffix(".xml").tempfile().unwrap();
fs::write(
file.path(),
r#"
"#,
)
.unwrap();
let ok = test_header().delete_header_if_present(file.path()).unwrap();
assert!(ok);
assert_eq!(
r#"
"#,
fs::read_to_string(file.path()).unwrap()
);
}
#[test]
fn deletes_header_without_touching_contents() {
let file = tempfile::Builder::new().suffix(".rs").tempfile().unwrap();
fs::write(
file.path(),
r#"// some license etc etc etc
license in file:
// some license etc etc etc
contents after license"#,
)
.unwrap();
let ok = test_header().delete_header_if_present(file.path()).unwrap();
assert!(ok);
assert_eq!(
r#"license in file:
// some license etc etc etc
contents after license"#,
fs::read_to_string(file.path()).unwrap()
);
}
#[test]
fn deletes_header_requires_exact_wrapped_header() {
let file = tempfile::Builder::new().suffix(".rs").tempfile().unwrap();
// does not have // prefix for the wrapped header, so the checker will find it, but it shouldn't
// actually be deleted
let orig = r#"some license etc etc etc
not a license"#;
fs::write(file.path(), orig).unwrap();
let ok = test_header().delete_header_if_present(file.path()).unwrap();
assert!(!ok);
assert_eq!(orig, fs::read_to_string(file.path()).unwrap());
}
#[test]
fn check_recursively_finds_no_header_file() {
let header = test_header();
let root = path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("resources/test/example_check");
let results = check_headers_recursively(&root, |_p| true, header, 4).unwrap();
assert_eq!(
vec![path::PathBuf::from("no_header.rs")],
results
.no_header_files
.iter()
.map(|p| p.strip_prefix(&root).unwrap().to_path_buf())
.collect::>()
);
}
#[test]
fn check_recursively_detects_binary_file() {
let header = test_header();
let root = tempfile::tempdir().unwrap();
let no_header = root.path().join("no_header.rs");
fs::write(&no_header, "// no header\n").unwrap();
let binary = root.path().join("binary.rs");
fs::write(&binary, [0xFF; 100]).unwrap();
let results = check_headers_recursively(root.path(), |_p| true, header, 4).unwrap();
assert_eq!(
vec![path::PathBuf::from("no_header.rs")],
results
.no_header_files
.iter()
.map(|p| p.strip_prefix(&root).unwrap().to_path_buf())
.collect::>()
);
assert_eq!(
vec![path::PathBuf::from("binary.rs")],
results
.binary_files
.iter()
.map(|p| p.strip_prefix(&root).unwrap().to_path_buf())
.collect::>()
);
}
#[test]
fn add_recursively_adds_where_needed() {
let header = test_header();
let root = tempfile::tempdir().unwrap();
let no_header = root.path().join("no_header.rs");
fs::write(&no_header, "// no header\n").unwrap();
let with_header = root.path().join("with_header.rs");
let mut contents = "some license etc etc etc".to_string();
contents.push_str("\n// has a header\n");
fs::write(&with_header, &contents).unwrap();
// should not have header since it will fail the path predicate
let ignored = root.path().join("ignored.txt");
fs::write(&ignored, "// no header\n").unwrap();
assert_eq!(
vec![path::PathBuf::from("no_header.rs")],
add_headers_recursively(
root.path(),
|p| p.extension().map(|ext| ext == "rs").unwrap_or(false),
header
)
.map(|paths| paths
.iter()
.map(|p| p.strip_prefix(&root).unwrap().to_path_buf())
.collect::>())
.unwrap()
);
assert_eq!(
"// some license etc etc etc\n\n// no header\n",
String::from_utf8(fs::read(&no_header).unwrap()).unwrap()
);
}
#[test]
fn doesnt_delete_header_when_missing() {
let file = tempfile::Builder::new().suffix(".rs").tempfile().unwrap();
let initial_content = "not a license";
fs::write(file.path(), initial_content).unwrap();
let ok = test_header().delete_header_if_present(file.path()).unwrap();
assert!(!ok);
assert_eq!(initial_content, fs::read_to_string(file.path()).unwrap());
}
#[test]
fn delete_recursively() {
let header = test_header();
let root = tempfile::tempdir().unwrap();
let mut no_header = root.path().to_path_buf();
no_header.push("no_header.rs");
fs::write(&no_header, "// no header\n").unwrap();
let mut with_header = root.path().to_path_buf();
with_header.push("with_header.rs");
let mut contents = "// some license etc etc etc".to_string();
contents.push_str("\n\n// has a header\n");
fs::write(&with_header, &contents).unwrap();
assert_eq!(
vec![path::PathBuf::from("with_header.rs")],
delete_headers_recursively(root.path(), |_| true, header)
.map(|paths| paths
.iter()
.map(|p| p.strip_prefix(&root).unwrap().to_path_buf())
.collect::>())
.unwrap()
);
assert_eq!(
"// has a header\n",
fs::read_to_string(with_header).unwrap()
);
}
fn test_checker() -> SingleLineChecker {
SingleLineChecker::new("some license".to_string(), 100)
}
fn test_header() -> Header {
Header::new(test_checker(), r#"some license etc etc etc"#.to_string())
}
fn test_header_with_blank_lines_and_trailing_whitespace() -> Header {
Header::new(
test_checker(),
"some license\nline with trailing whitespace. \n\netc".to_string(),
)
}