1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
// Copyright 2025 Umberto Gotti <umberto.gotti@umbertogotti.dev>
// Licensed under the MIT License
// SPDX-License-Identifier: MIT
// Normalising a file once so no rule has to. Both normalisations exist because
// of how a file reaches the disk rather than what it says: git rewrites line
// endings on checkout, and editors add a byte order mark invisibly. A rule that
// compared raw bytes would fail every file on a Windows working copy and never
// on the maintainer's.
use stern4rust::source_file::SourceFile;
// A file of blank lines carries no header either, and treating it as content
// would report a confusing mismatch against whitespace.
#[test]
fn is_empty_of_a_file_holding_only_whitespace_returns_true() {
// Arrange & Act
let file = SourceFile::new("src/a.rs", "\n \n\t\n");
// Assert
assert!(file.is_empty());
}
#[test]
fn is_empty_of_a_file_with_no_contents_returns_true() {
// Arrange & Act
let file = SourceFile::new("src/a.rs", "");
// Assert
assert!(file.is_empty());
}
#[test]
fn is_empty_of_a_file_with_one_line_of_content_returns_false() {
// Arrange & Act
let file = SourceFile::new("src/a.rs", "// header\n");
// Assert
assert!(!file.is_empty());
}
// The mark is only a mark at the very start; the same bytes further in are
// content and must survive.
#[test]
fn new_keeps_a_byte_order_mark_that_is_not_at_the_start() {
// Arrange & Act
let file = SourceFile::new("src/a.rs", "// header\n\u{feff}second");
// Assert
assert_eq!(file.lines()[1], "\u{feff}second");
}
// Reports are read by people and pasted into scripts, so paths are printed the
// same way whichever platform produced them.
#[test]
fn new_reports_a_windows_path_with_forward_slashes() {
// Arrange & Act
let file = SourceFile::new("src\\rules\\header_rule.rs", "");
// Assert
assert_eq!(file.relative_path(), "src/rules/header_rule.rs");
}
#[test]
fn new_splits_the_contents_into_lines() {
// Arrange & Act
let file = SourceFile::new("src/a.rs", "one\ntwo\nthree");
// Assert
assert_eq!(file.lines(), ["one", "two", "three"]);
}
#[test]
fn new_strips_a_leading_byte_order_mark() {
// Arrange & Act
let file = SourceFile::new("src/a.rs", "\u{feff}// header");
// Assert
assert_eq!(file.lines()[0], "// header");
}
#[test]
fn new_strips_the_carriage_return_from_windows_line_endings() {
// Arrange & Act
let file = SourceFile::new("src/a.rs", "one\r\ntwo\r\n");
// Assert
assert_eq!(file.lines()[0], "one");
assert_eq!(file.lines()[1], "two");
}