@@ -24,168 +24,203 @@ namespace {
2424class ToolchainFileTest : public FileTestBase {
2525 public:
2626 explicit ToolchainFileTest (llvm::StringRef exe_path, std::mutex* output_mutex,
27- llvm::StringRef test_name)
28- : FileTestBase(output_mutex, test_name),
29- component_(GetComponent(test_name)),
30- installation_(InstallPaths::MakeForBazelRunfiles(exe_path)) {}
27+ llvm::StringRef test_name);
3128
32- auto GetArgReplacements () -> llvm::StringMap<std::string> override {
33- return {{" core_package_dir" , installation_.core_package ()}};
34- }
29+ // Adds a replacement for `core_package_dir`.
30+ auto GetArgReplacements () -> llvm::StringMap<std::string> override ;
3531
32+ // Loads files into the VFS and runs the driver.
3633 auto Run (const llvm::SmallVector<llvm::StringRef>& test_args,
3734 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem>& fs,
3835 FILE* input_stream, llvm::raw_pwrite_stream& output_stream,
3936 llvm::raw_pwrite_stream& error_stream)
40- -> ErrorOr<RunResult> override {
41- CARBON_ASSIGN_OR_RETURN (auto prelude, installation_.ReadPreludeManifest ());
42- if (!is_no_prelude ()) {
43- for (const auto & file : prelude) {
44- CARBON_RETURN_IF_ERROR (AddFile (*fs, file));
45- }
46- }
47-
48- Driver driver ({.fs = fs,
49- .installation = &installation_,
50- .input_stream = input_stream,
51- .output_stream = &output_stream,
52- .error_stream = &error_stream});
53- auto driver_result = driver.RunCommand (test_args);
54- // If any diagnostics have been produced, add a trailing newline to make the
55- // last diagnostic match intermediate diagnostics (that have a newline
56- // separator between them). This reduces churn when adding new diagnostics
57- // to test cases.
58- if (error_stream.tell () > 0 ) {
59- error_stream << ' \n ' ;
60- }
61-
62- RunResult result{
63- .success = driver_result.success ,
64- .per_file_success = std::move (driver_result.per_file_success )};
65- // Drop entries that don't look like a file, and entries corresponding to
66- // the prelude. Note this can empty out the list.
67- llvm::erase_if (result.per_file_success ,
68- [&](std::pair<llvm::StringRef, bool > entry) {
69- return entry.first == " ." || entry.first == " -" ||
70- entry.first .starts_with (" not_file" ) ||
71- llvm::is_contained (prelude, entry.first );
72- });
73- return result;
74- }
37+ -> ErrorOr<RunResult> override ;
7538
76- auto GetDefaultArgs () -> llvm::SmallVector<std::string> override {
77- if (component_ == " format" ) {
78- return {" format" , " %s" };
79- }
80-
81- llvm::SmallVector<std::string> args = {
82- " compile" , " --include-diagnostic-kind" , " --phase=" + component_.str ()};
83-
84- if (component_ == " lex" ) {
85- args.insert (args.end (), {" --dump-tokens" , " --omit-file-boundary-tokens" });
86- } else if (component_ == " parse" ) {
87- args.push_back (" --dump-parse-tree" );
88- } else if (component_ == " check" ) {
89- args.push_back (" --dump-sem-ir" );
90- } else if (component_ == " lower" ) {
91- args.push_back (" --dump-llvm-ir" );
92- } else {
93- CARBON_FATAL (" Unexpected test component {0}: {1}" , component_,
94- test_name ());
95- }
96-
97- // For `lex` and `parse`, we don't need to import the prelude; exclude it to
98- // focus errors. In other phases we only do this for explicit "no_prelude"
99- // tests.
100- if (component_ == " lex" || component_ == " parse" || is_no_prelude ()) {
101- args.push_back (" --no-prelude-import" );
102- }
103-
104- args.insert (
105- args.end (),
106- {" --exclude-dump-file-prefix=" + installation_.core_package (), " %s" });
107- return args;
108- }
39+ // Sets different default flags based on the component being tested.
40+ auto GetDefaultArgs () -> llvm::SmallVector<std::string> override ;
10941
42+ // Generally uses the parent implementation, with special handling for lex.
11043 auto GetDefaultFileRE (llvm::ArrayRef<llvm::StringRef> filenames)
111- -> std::optional<RE2> override {
112- if (component_ == " lex" ) {
113- return std::make_optional<RE2>(
114- llvm::formatv (R"( ^- filename: ({0})$)" , llvm::join (filenames, " |" )));
115- }
116- return FileTestBase::GetDefaultFileRE (filenames);
117- }
44+ -> std::optional<RE2> override ;
11845
46+ // Generally uses the parent implementation, with special handling for lex.
11947 auto GetLineNumberReplacements (llvm::ArrayRef<llvm::StringRef> filenames)
120- -> llvm::SmallVector<LineNumberReplacement> override {
121- auto replacements = FileTestBase::GetLineNumberReplacements (filenames);
122- if (component_ == " lex" ) {
123- replacements.push_back ({.has_file = false ,
124- .re = std::make_shared<RE2>(R"( line: (\s*\d+))" ),
125- // The `{{{{` becomes `{{`.
126- .line_formatv = " {{{{ *}}{0}" });
127- }
128- return replacements;
129- }
48+ -> llvm::SmallVector<LineNumberReplacement> override ;
13049
131- auto DoExtraCheckReplacements (std::string& check_line) -> void override {
132- if (component_ == " driver" ) {
133- // TODO: Disable token output, it's not interesting for these tests.
134- if (llvm::StringRef (check_line).starts_with (" // CHECK:STDOUT: {" )) {
135- check_line = " // CHECK:STDOUT: {{.*}}" ;
136- }
137- } else if (component_ == " lex" ) {
138- // Both FileStart and FileEnd regularly have locations on CHECK
139- // comment lines that don't work correctly. The line happens to be correct
140- // for the FileEnd, but we need to avoid checking the column.
141- // The column happens to be right for FileStart, but the line is wrong.
142- static RE2 file_token_re (
143- R"( (FileEnd.*column: |FileStart.*line: )( *\d+))" );
144- RE2::Replace (&check_line, file_token_re, R"( \1{{ *\\d+}})" );
145- } else {
146- FileTestBase::DoExtraCheckReplacements (check_line);
147- }
148- }
50+ // Generally uses the parent implementation, with special handling for lex and
51+ // driver.
52+ auto DoExtraCheckReplacements (std::string& check_line) -> void override ;
14953
15054 private:
15155 // Adds a file to the fs.
15256 auto AddFile (llvm::vfs::InMemoryFileSystem& fs, llvm::StringRef path)
153- -> ErrorOr<Success> {
154- llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> file =
155- llvm::MemoryBuffer::getFile (path);
156- if (file.getError ()) {
157- return ErrorBuilder ()
158- << " Getting `" << path << " `: " << file.getError ().message ();
159- }
160- if (!fs.addFile (path, /* ModificationTime=*/ 0 , std::move (*file))) {
161- return ErrorBuilder () << " Duplicate file: `" << path << " `" ;
162- }
163- return Success ();
164- }
165-
166- // Returns the toolchain subdirectory being tested.
167- static auto GetComponent (llvm::StringRef test_name) -> llvm::StringRef {
168- // This handles cases where the toolchain directory may be copied into a
169- // repository that doesn't put it at the root.
170- auto pos = test_name.find (" toolchain/" );
171- CARBON_CHECK (pos != llvm::StringRef::npos, " {0}" , test_name);
172- test_name = test_name.drop_front (pos + strlen (" toolchain/" ));
173- test_name = test_name.take_front (test_name.find (" /" ));
174- return test_name;
175- }
57+ -> ErrorOr<Success>;
17658
59+ // Controls whether `Run()` includes the prelude.
17760 auto is_no_prelude () const -> bool {
17861 return test_name ().find (" /no_prelude/" ) != llvm::StringRef::npos;
17962 }
18063
64+ // The toolchain component subdirectory, such as `lex` or `language_server`.
18165 const llvm::StringRef component_;
66+ // The toolchain install information.
18267 const InstallPaths installation_;
18368};
18469
18570} // namespace
18671
18772CARBON_FILE_TEST_FACTORY (ToolchainFileTest)
18873
74+ // Returns the toolchain subdirectory being tested.
75+ static auto GetComponent (llvm::StringRef test_name) -> llvm::StringRef {
76+ // This handles cases where the toolchain directory may be copied into a
77+ // repository that doesn't put it at the root.
78+ auto pos = test_name.find (" toolchain/" );
79+ CARBON_CHECK (pos != llvm::StringRef::npos, " {0}" , test_name);
80+ test_name = test_name.drop_front (pos + strlen (" toolchain/" ));
81+ test_name = test_name.take_front (test_name.find (" /" ));
82+ return test_name;
83+ }
84+
85+ ToolchainFileTest::ToolchainFileTest (llvm::StringRef exe_path,
86+ std::mutex* output_mutex,
87+ llvm::StringRef test_name)
88+ : FileTestBase(output_mutex, test_name),
89+ component_ (GetComponent(test_name)),
90+ installation_(InstallPaths::MakeForBazelRunfiles(exe_path)) {}
91+
92+ auto ToolchainFileTest::GetArgReplacements () -> llvm::StringMap<std::string> {
93+ return {{" core_package_dir" , installation_.core_package ()}};
94+ }
95+
96+ auto ToolchainFileTest::Run (
97+ const llvm::SmallVector<llvm::StringRef>& test_args,
98+ llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem>& fs,
99+ FILE* input_stream, llvm::raw_pwrite_stream& output_stream,
100+ llvm::raw_pwrite_stream& error_stream) -> ErrorOr<RunResult> {
101+ CARBON_ASSIGN_OR_RETURN (auto prelude, installation_.ReadPreludeManifest ());
102+ if (!is_no_prelude ()) {
103+ for (const auto & file : prelude) {
104+ CARBON_RETURN_IF_ERROR (AddFile (*fs, file));
105+ }
106+ }
107+
108+ Driver driver ({.fs = fs,
109+ .installation = &installation_,
110+ .input_stream = input_stream,
111+ .output_stream = &output_stream,
112+ .error_stream = &error_stream});
113+ auto driver_result = driver.RunCommand (test_args);
114+ // If any diagnostics have been produced, add a trailing newline to make the
115+ // last diagnostic match intermediate diagnostics (that have a newline
116+ // separator between them). This reduces churn when adding new diagnostics
117+ // to test cases.
118+ if (error_stream.tell () > 0 ) {
119+ error_stream << ' \n ' ;
120+ }
121+
122+ RunResult result{
123+ .success = driver_result.success ,
124+ .per_file_success = std::move (driver_result.per_file_success )};
125+ // Drop entries that don't look like a file, and entries corresponding to
126+ // the prelude. Note this can empty out the list.
127+ llvm::erase_if (result.per_file_success ,
128+ [&](std::pair<llvm::StringRef, bool > entry) {
129+ return entry.first == " ." || entry.first == " -" ||
130+ entry.first .starts_with (" not_file" ) ||
131+ llvm::is_contained (prelude, entry.first );
132+ });
133+ return result;
134+ }
135+
136+ auto ToolchainFileTest::GetDefaultArgs () -> llvm::SmallVector<std::string> {
137+ if (component_ == " format" ) {
138+ return {" format" , " %s" };
139+ }
140+
141+ llvm::SmallVector<std::string> args = {" compile" , " --include-diagnostic-kind" ,
142+ " --phase=" + component_.str ()};
143+
144+ if (component_ == " lex" ) {
145+ args.insert (args.end (), {" --dump-tokens" , " --omit-file-boundary-tokens" });
146+ } else if (component_ == " parse" ) {
147+ args.push_back (" --dump-parse-tree" );
148+ } else if (component_ == " check" ) {
149+ args.push_back (" --dump-sem-ir" );
150+ } else if (component_ == " lower" ) {
151+ args.push_back (" --dump-llvm-ir" );
152+ } else {
153+ CARBON_FATAL (" Unexpected test component {0}: {1}" , component_, test_name ());
154+ }
155+
156+ // For `lex` and `parse`, we don't need to import the prelude; exclude it to
157+ // focus errors. In other phases we only do this for explicit "no_prelude"
158+ // tests.
159+ if (component_ == " lex" || component_ == " parse" || is_no_prelude ()) {
160+ args.push_back (" --no-prelude-import" );
161+ }
162+
163+ args.insert (
164+ args.end (),
165+ {" --exclude-dump-file-prefix=" + installation_.core_package (), " %s" });
166+ return args;
167+ }
168+
169+ auto ToolchainFileTest::GetDefaultFileRE (
170+ llvm::ArrayRef<llvm::StringRef> filenames) -> std::optional<RE2> {
171+ if (component_ == " lex" ) {
172+ return std::make_optional<RE2>(
173+ llvm::formatv (R"( ^- filename: ({0})$)" , llvm::join (filenames, " |" )));
174+ }
175+ return FileTestBase::GetDefaultFileRE (filenames);
176+ }
177+
178+ auto ToolchainFileTest::GetLineNumberReplacements (
179+ llvm::ArrayRef<llvm::StringRef> filenames)
180+ -> llvm::SmallVector<LineNumberReplacement> {
181+ auto replacements = FileTestBase::GetLineNumberReplacements (filenames);
182+ if (component_ == " lex" ) {
183+ replacements.push_back ({.has_file = false ,
184+ .re = std::make_shared<RE2>(R"( line: (\s*\d+))" ),
185+ // The `{{{{` becomes `{{`.
186+ .line_formatv = " {{{{ *}}{0}" });
187+ }
188+ return replacements;
189+ }
190+
191+ auto ToolchainFileTest::DoExtraCheckReplacements (std::string& check_line)
192+ -> void {
193+ if (component_ == " driver" ) {
194+ // TODO: Disable token output, it's not interesting for these tests.
195+ if (llvm::StringRef (check_line).starts_with (" // CHECK:STDOUT: {" )) {
196+ check_line = " // CHECK:STDOUT: {{.*}}" ;
197+ }
198+ } else if (component_ == " lex" ) {
199+ // Both FileStart and FileEnd regularly have locations on CHECK
200+ // comment lines that don't work correctly. The line happens to be correct
201+ // for the FileEnd, but we need to avoid checking the column.
202+ // The column happens to be right for FileStart, but the line is wrong.
203+ static RE2 file_token_re (R"( (FileEnd.*column: |FileStart.*line: )( *\d+))" );
204+ RE2::Replace (&check_line, file_token_re, R"( \1{{ *\\d+}})" );
205+ } else {
206+ FileTestBase::DoExtraCheckReplacements (check_line);
207+ }
208+ }
209+
210+ auto ToolchainFileTest::AddFile (llvm::vfs::InMemoryFileSystem& fs,
211+ llvm::StringRef path) -> ErrorOr<Success> {
212+ llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> file =
213+ llvm::MemoryBuffer::getFile (path);
214+ if (file.getError ()) {
215+ return ErrorBuilder () << " Getting `" << path
216+ << " `: " << file.getError ().message ();
217+ }
218+ if (!fs.addFile (path, /* ModificationTime=*/ 0 , std::move (*file))) {
219+ return ErrorBuilder () << " Duplicate file: `" << path << " `" ;
220+ }
221+ return Success ();
222+ }
223+
189224} // namespace Carbon::Testing
190225
191226#endif // CARBON_TOOLCHAIN_DRIVER_DRIVER_FILE_TEST_BASE_H_
0 commit comments