@@ -7,55 +7,20 @@ use std::fs;
77use  std:: io:: Write ; 
88use  std:: path:: { Path ,  PathBuf } ; 
99
10- const  EXPECTED_TEST_FILE_EXTENSIONS :  & [ & str ]  = & [ 
11-     "rs" ,      // test source files 
12-     "stderr" ,  // expected stderr file, corresponds to a rs file 
13-     "svg" ,     // expected svg file, corresponds to a rs file, equivalent to stderr 
14-     "stdout" ,  // expected stdout file, corresponds to a rs file 
15-     "fixed" ,   // expected source file after applying fixes 
16-     "md" ,      // test directory descriptions 
17-     "ftl" ,     // translation tests 
18- ] ; 
19- 
20- const  EXTENSION_EXCEPTION_PATHS :  & [ & str ]  = & [ 
21-     "tests/ui/asm/named-asm-labels.s" ,  // loading an external asm file to test named labels lint 
22-     "tests/ui/codegen/mismatched-data-layout.json" ,  // testing mismatched data layout w/ custom targets 
23-     "tests/ui/check-cfg/my-awesome-platform.json" ,   // testing custom targets with cfgs 
24-     "tests/ui/argfile/commandline-argfile-badutf8.args" ,  // passing args via a file 
25-     "tests/ui/argfile/commandline-argfile.args" ,     // passing args via a file 
26-     "tests/ui/crate-loading/auxiliary/libfoo.rlib" ,  // testing loading a manually created rlib 
27-     "tests/ui/include-macros/data.bin" ,  // testing including data with the include macros 
28-     "tests/ui/include-macros/file.txt" ,  // testing including data with the include macros 
29-     "tests/ui/macros/macro-expanded-include/file.txt" ,  // testing including data with the include macros 
30-     "tests/ui/macros/not-utf8.bin" ,  // testing including data with the include macros 
31-     "tests/ui/macros/syntax-extension-source-utils-files/includeme.fragment" ,  // more include 
32-     "tests/ui/proc-macro/auxiliary/included-file.txt" ,  // more include 
33-     "tests/ui/unpretty/auxiliary/data.txt" ,  // more include 
34-     "tests/ui/invalid/foo.natvis.xml" ,  // sample debugger visualizer 
35-     "tests/ui/sanitizer/dataflow-abilist.txt" ,  // dataflow sanitizer ABI list file 
36-     "tests/ui/shell-argfiles/shell-argfiles.args" ,  // passing args via a file 
37-     "tests/ui/shell-argfiles/shell-argfiles-badquotes.args" ,  // passing args via a file 
38-     "tests/ui/shell-argfiles/shell-argfiles-via-argfile-shell.args" ,  // passing args via a file 
39-     "tests/ui/shell-argfiles/shell-argfiles-via-argfile.args" ,  // passing args via a file 
40-     "tests/ui/std/windows-bat-args1.bat" ,  // tests escaping arguments through batch files 
41-     "tests/ui/std/windows-bat-args2.bat" ,  // tests escaping arguments through batch files 
42-     "tests/ui/std/windows-bat-args3.bat" ,  // tests escaping arguments through batch files 
43- ] ; 
44- 
45- pub  fn  check ( root_path :  & Path ,  bless :  bool ,  bad :  & mut  bool )  { 
46-     let  issues_txt_header = r#"============================================================ 
10+ const  ISSUES_TXT_HEADER :  & str  = r#"============================================================ 
4711    ⚠️⚠️⚠️NOTHING SHOULD EVER BE ADDED TO THIS LIST⚠️⚠️⚠️ 
4812============================================================ 
4913"# ; 
5014
15+ pub  fn  check ( root_path :  & Path ,  bless :  bool ,  bad :  & mut  bool )  { 
5116    let  path = & root_path. join ( "tests" ) ; 
5217
5318    // the list of files in ui tests that are allowed to start with `issue-XXXX` 
5419    // BTreeSet because we would like a stable ordering so --bless works 
5520    let  mut  prev_line = "" ; 
5621    let  mut  is_sorted = true ; 
5722    let  allowed_issue_names:  BTreeSet < _ >  = include_str ! ( "issues.txt" ) 
58-         . strip_prefix ( issues_txt_header ) 
23+         . strip_prefix ( ISSUES_TXT_HEADER ) 
5924        . unwrap ( ) 
6025        . lines ( ) 
6126        . inspect ( |& line| { 
@@ -75,73 +40,9 @@ pub fn check(root_path: &Path, bless: bool, bad: &mut bool) {
7540        ) ; 
7641    } 
7742
78-     let  mut  remaining_issue_names:  BTreeSet < & str >  = allowed_issue_names. clone ( ) ; 
79- 
80-     let  ( ui,  ui_fulldeps)  = ( path. join ( "ui" ) ,  path. join ( "ui-fulldeps" ) ) ; 
81-     let  paths = [ ui. as_path ( ) ,  ui_fulldeps. as_path ( ) ] ; 
82-     crate :: walk:: walk_no_read ( & paths,  |_,  _| false ,  & mut  |entry| { 
83-         let  file_path = entry. path ( ) ; 
84-         if  let  Some ( ext)  = file_path. extension ( ) . and_then ( OsStr :: to_str)  { 
85-             // files that are neither an expected extension or an exception should not exist 
86-             // they're probably typos or not meant to exist 
87-             if  !( EXPECTED_TEST_FILE_EXTENSIONS . contains ( & ext) 
88-                 || EXTENSION_EXCEPTION_PATHS . iter ( ) . any ( |path| file_path. ends_with ( path) ) ) 
89-             { 
90-                 tidy_error ! ( bad,  "file {} has unexpected extension {}" ,  file_path. display( ) ,  ext) ; 
91-             } 
92- 
93-             // NB: We do not use file_stem() as some file names have multiple `.`s and we 
94-             // must strip all of them. 
95-             let  testname =
96-                 file_path. file_name ( ) . unwrap ( ) . to_str ( ) . unwrap ( ) . split_once ( '.' ) . unwrap ( ) . 0 ; 
97-             if  ext == "stderr"  || ext == "stdout"  || ext == "fixed"  { 
98-                 // Test output filenames have one of the formats: 
99-                 // ``` 
100-                 // $testname.stderr 
101-                 // $testname.$mode.stderr 
102-                 // $testname.$revision.stderr 
103-                 // $testname.$revision.$mode.stderr 
104-                 // ``` 
105-                 // 
106-                 // For now, just make sure that there is a corresponding 
107-                 // `$testname.rs` file. 
108- 
109-                 if  !file_path. with_file_name ( testname) . with_extension ( "rs" ) . exists ( ) 
110-                     && !testname. contains ( "ignore-tidy" ) 
111-                 { 
112-                     tidy_error ! ( bad,  "Stray file with UI testing output: {:?}" ,  file_path) ; 
113-                 } 
114- 
115-                 if  let  Ok ( metadata)  = fs:: metadata ( file_path) 
116-                     && metadata. len ( )  == 0 
117-                 { 
118-                     tidy_error ! ( bad,  "Empty file with UI testing output: {:?}" ,  file_path) ; 
119-                 } 
120-             } 
43+     deny_new_top_level_ui_tests ( bad,  & path. join ( "ui" ) ) ; 
12144
122-             if  ext == "rs" 
123-                 && let  Some ( test_name)  = static_regex ! ( r"^issues?[-_]?(\d{3,})" ) . captures ( testname) 
124-             { 
125-                 // these paths are always relative to the passed `path` and always UTF8 
126-                 let  stripped_path = file_path
127-                     . strip_prefix ( path) 
128-                     . unwrap ( ) 
129-                     . to_str ( ) 
130-                     . unwrap ( ) 
131-                     . replace ( std:: path:: MAIN_SEPARATOR_STR ,  "/" ) ; 
132- 
133-                 if  !remaining_issue_names. remove ( stripped_path. as_str ( ) ) 
134-                     && !stripped_path. starts_with ( "ui/issues/" ) 
135-                 { 
136-                     tidy_error ! ( 
137-                         bad, 
138-                         "file `tests/{stripped_path}` must begin with a descriptive name, consider `{{reason}}-issue-{issue_n}.rs`" , 
139-                         issue_n = & test_name[ 1 ] , 
140-                     ) ; 
141-                 } 
142-             } 
143-         } 
144-     } ) ; 
45+     let  remaining_issue_names = recursively_check_ui_tests ( bad,  path,  & allowed_issue_names) ; 
14546
14647    // if there are any file names remaining, they were moved on the fs. 
14748    // our data must remain up to date, so it must be removed from issues.txt 
@@ -152,7 +53,7 @@ pub fn check(root_path: &Path, bless: bool, bad: &mut bool) {
15253        // so we don't bork things on panic or a contributor using Ctrl+C 
15354        let  blessed_issues_path = tidy_src. join ( "issues_blessed.txt" ) ; 
15455        let  mut  blessed_issues_txt = fs:: File :: create ( & blessed_issues_path) . unwrap ( ) ; 
155-         blessed_issues_txt. write_all ( issues_txt_header . as_bytes ( ) ) . unwrap ( ) ; 
56+         blessed_issues_txt. write_all ( ISSUES_TXT_HEADER . as_bytes ( ) ) . unwrap ( ) ; 
15657        // If we changed paths to use the OS separator, reassert Unix chauvinism for blessing. 
15758        for  filename in  allowed_issue_names. difference ( & remaining_issue_names)  { 
15859            writeln ! ( blessed_issues_txt,  "{filename}" ) . unwrap ( ) ; 
@@ -171,3 +72,170 @@ pub fn check(root_path: &Path, bless: bool, bad: &mut bool) {
17172        } 
17273    } 
17374} 
75+ 
76+ fn  deny_new_top_level_ui_tests ( bad :  & mut  bool ,  tests_path :  & Path )  { 
77+     // See <https://github.com/rust-lang/compiler-team/issues/902> where we propose banning adding 
78+     // new ui tests *directly* under `tests/ui/`. For more context, see: 
79+     // 
80+     // - <https://github.com/rust-lang/rust/issues/73494> 
81+     // - <https://github.com/rust-lang/rust/issues/133895> 
82+ 
83+     let  top_level_ui_tests = walkdir:: WalkDir :: new ( tests_path) 
84+         . min_depth ( 1 ) 
85+         . max_depth ( 1 ) 
86+         . follow_links ( false ) 
87+         . same_file_system ( true ) 
88+         . into_iter ( ) 
89+         . flatten ( ) 
90+         . filter ( |e| { 
91+             let  file_name = e. file_name ( ) ; 
92+             file_name != ".gitattributes"  && file_name != "README.md" 
93+         } ) 
94+         . filter ( |e| !e. file_type ( ) . is_dir ( ) ) ; 
95+     for  entry in  top_level_ui_tests { 
96+         tidy_error ! ( 
97+             bad, 
98+             "ui tests should be added under meaningful subdirectories: `{}`" , 
99+             entry. path( ) . display( ) 
100+         ) 
101+     } 
102+ } 
103+ 
104+ fn  recursively_check_ui_tests < ' issues > ( 
105+     bad :  & mut  bool , 
106+     path :  & Path , 
107+     allowed_issue_names :  & ' issues  BTreeSet < & ' issues  str > , 
108+ )  -> BTreeSet < & ' issues  str >  { 
109+     let  mut  remaining_issue_names:  BTreeSet < & str >  = allowed_issue_names. clone ( ) ; 
110+ 
111+     let  ( ui,  ui_fulldeps)  = ( path. join ( "ui" ) ,  path. join ( "ui-fulldeps" ) ) ; 
112+     let  paths = [ ui. as_path ( ) ,  ui_fulldeps. as_path ( ) ] ; 
113+     crate :: walk:: walk_no_read ( & paths,  |_,  _| false ,  & mut  |entry| { 
114+         let  file_path = entry. path ( ) ; 
115+         if  let  Some ( ext)  = file_path. extension ( ) . and_then ( OsStr :: to_str)  { 
116+             check_unexpected_extension ( bad,  file_path,  ext) ; 
117+ 
118+             // NB: We do not use file_stem() as some file names have multiple `.`s and we 
119+             // must strip all of them. 
120+             let  testname =
121+                 file_path. file_name ( ) . unwrap ( ) . to_str ( ) . unwrap ( ) . split_once ( '.' ) . unwrap ( ) . 0 ; 
122+             if  ext == "stderr"  || ext == "stdout"  || ext == "fixed"  { 
123+                 check_stray_output_snapshot ( bad,  file_path,  testname) ; 
124+                 check_empty_output_snapshot ( bad,  file_path) ; 
125+             } 
126+ 
127+             deny_new_nondescriptive_test_names ( 
128+                 bad, 
129+                 path, 
130+                 & mut  remaining_issue_names, 
131+                 file_path, 
132+                 testname, 
133+                 ext, 
134+             ) ; 
135+         } 
136+     } ) ; 
137+     remaining_issue_names
138+ } 
139+ 
140+ fn  check_unexpected_extension ( bad :  & mut  bool ,  file_path :  & Path ,  ext :  & str )  { 
141+     const  EXPECTED_TEST_FILE_EXTENSIONS :  & [ & str ]  = & [ 
142+         "rs" ,      // test source files 
143+         "stderr" ,  // expected stderr file, corresponds to a rs file 
144+         "svg" ,     // expected svg file, corresponds to a rs file, equivalent to stderr 
145+         "stdout" ,  // expected stdout file, corresponds to a rs file 
146+         "fixed" ,   // expected source file after applying fixes 
147+         "md" ,      // test directory descriptions 
148+         "ftl" ,     // translation tests 
149+     ] ; 
150+ 
151+     const  EXTENSION_EXCEPTION_PATHS :  & [ & str ]  = & [ 
152+         "tests/ui/asm/named-asm-labels.s" ,  // loading an external asm file to test named labels lint 
153+         "tests/ui/codegen/mismatched-data-layout.json" ,  // testing mismatched data layout w/ custom targets 
154+         "tests/ui/check-cfg/my-awesome-platform.json" ,   // testing custom targets with cfgs 
155+         "tests/ui/argfile/commandline-argfile-badutf8.args" ,  // passing args via a file 
156+         "tests/ui/argfile/commandline-argfile.args" ,     // passing args via a file 
157+         "tests/ui/crate-loading/auxiliary/libfoo.rlib" ,  // testing loading a manually created rlib 
158+         "tests/ui/include-macros/data.bin" ,  // testing including data with the include macros 
159+         "tests/ui/include-macros/file.txt" ,  // testing including data with the include macros 
160+         "tests/ui/macros/macro-expanded-include/file.txt" ,  // testing including data with the include macros 
161+         "tests/ui/macros/not-utf8.bin" ,  // testing including data with the include macros 
162+         "tests/ui/macros/syntax-extension-source-utils-files/includeme.fragment" ,  // more include 
163+         "tests/ui/proc-macro/auxiliary/included-file.txt" ,  // more include 
164+         "tests/ui/unpretty/auxiliary/data.txt" ,  // more include 
165+         "tests/ui/invalid/foo.natvis.xml" ,  // sample debugger visualizer 
166+         "tests/ui/sanitizer/dataflow-abilist.txt" ,  // dataflow sanitizer ABI list file 
167+         "tests/ui/shell-argfiles/shell-argfiles.args" ,  // passing args via a file 
168+         "tests/ui/shell-argfiles/shell-argfiles-badquotes.args" ,  // passing args via a file 
169+         "tests/ui/shell-argfiles/shell-argfiles-via-argfile-shell.args" ,  // passing args via a file 
170+         "tests/ui/shell-argfiles/shell-argfiles-via-argfile.args" ,  // passing args via a file 
171+         "tests/ui/std/windows-bat-args1.bat" ,  // tests escaping arguments through batch files 
172+         "tests/ui/std/windows-bat-args2.bat" ,  // tests escaping arguments through batch files 
173+         "tests/ui/std/windows-bat-args3.bat" ,  // tests escaping arguments through batch files 
174+     ] ; 
175+ 
176+     // files that are neither an expected extension or an exception should not exist 
177+     // they're probably typos or not meant to exist 
178+     if  !( EXPECTED_TEST_FILE_EXTENSIONS . contains ( & ext) 
179+         || EXTENSION_EXCEPTION_PATHS . iter ( ) . any ( |path| file_path. ends_with ( path) ) ) 
180+     { 
181+         tidy_error ! ( bad,  "file {} has unexpected extension {}" ,  file_path. display( ) ,  ext) ; 
182+     } 
183+ } 
184+ 
185+ fn  check_stray_output_snapshot ( bad :  & mut  bool ,  file_path :  & Path ,  testname :  & str )  { 
186+     // Test output filenames have one of the formats: 
187+     // ``` 
188+     // $testname.stderr 
189+     // $testname.$mode.stderr 
190+     // $testname.$revision.stderr 
191+     // $testname.$revision.$mode.stderr 
192+     // ``` 
193+     // 
194+     // For now, just make sure that there is a corresponding 
195+     // `$testname.rs` file. 
196+ 
197+     if  !file_path. with_file_name ( testname) . with_extension ( "rs" ) . exists ( ) 
198+         && !testname. contains ( "ignore-tidy" ) 
199+     { 
200+         tidy_error ! ( bad,  "Stray file with UI testing output: {:?}" ,  file_path) ; 
201+     } 
202+ } 
203+ 
204+ fn  check_empty_output_snapshot ( bad :  & mut  bool ,  file_path :  & Path )  { 
205+     if  let  Ok ( metadata)  = fs:: metadata ( file_path) 
206+         && metadata. len ( )  == 0 
207+     { 
208+         tidy_error ! ( bad,  "Empty file with UI testing output: {:?}" ,  file_path) ; 
209+     } 
210+ } 
211+ 
212+ fn  deny_new_nondescriptive_test_names ( 
213+     bad :  & mut  bool , 
214+     path :  & Path , 
215+     remaining_issue_names :  & mut  BTreeSet < & str > , 
216+     file_path :  & Path , 
217+     testname :  & str , 
218+     ext :  & str , 
219+ )  { 
220+     if  ext == "rs" 
221+         && let  Some ( test_name)  = static_regex ! ( r"^issues?[-_]?(\d{3,})" ) . captures ( testname) 
222+     { 
223+         // these paths are always relative to the passed `path` and always UTF8 
224+         let  stripped_path = file_path
225+             . strip_prefix ( path) 
226+             . unwrap ( ) 
227+             . to_str ( ) 
228+             . unwrap ( ) 
229+             . replace ( std:: path:: MAIN_SEPARATOR_STR ,  "/" ) ; 
230+ 
231+         if  !remaining_issue_names. remove ( stripped_path. as_str ( ) ) 
232+             && !stripped_path. starts_with ( "ui/issues/" ) 
233+         { 
234+             tidy_error ! ( 
235+                 bad, 
236+                 "file `tests/{stripped_path}` must begin with a descriptive name, consider `{{reason}}-issue-{issue_n}.rs`" , 
237+                 issue_n = & test_name[ 1 ] , 
238+             ) ; 
239+         } 
240+     } 
241+ } 
0 commit comments