1 //===- unittests/Frontend/FrontendActionTest.cpp FrontendAction tests-----===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "flang/Frontend/CompilerInstance.h"
10 #include "flang/Frontend/CompilerInvocation.h"
11 #include "flang/Frontend/FrontendOptions.h"
12 #include "flang/FrontendTool/Utils.h"
13 #include "llvm/Support/FileSystem.h"
14 #include "llvm/Support/raw_ostream.h"
16 #include "gtest/gtest.h"
18 using namespace Fortran::frontend
;
22 class FrontendActionTest
: public ::testing::Test
{
24 // AllSources (which is used to manage files inside every compiler
25 // instance), works with paths. So we need a filename and a path for the
27 // TODO: We could use `-` for inputFilePath_, but then we'd need a way to
28 // write to stdin that's then read by AllSources. Ideally, AllSources should
29 // be capable of reading from any stream.
30 std::string inputFileName_
;
31 std::string inputFilePath_
;
32 // The output stream for the input file. Use this to populate the input.
33 std::unique_ptr
<llvm::raw_fd_ostream
> inputFileOs_
;
37 CompilerInstance compInst_
;
38 std::shared_ptr
<CompilerInvocation
> invocation_
;
40 void SetUp() override
{
41 // Generate a unique test file name.
42 const testing::TestInfo
*const test_info
=
43 testing::UnitTest::GetInstance()->current_test_info();
44 inputFileName_
= std::string(test_info
->name()) + "_test-file.f90";
46 // Create the input file stream. Note that this stream is populated
47 // separately in every test (i.e. the input is test specific).
48 inputFileOs_
= std::make_unique
<llvm::raw_fd_ostream
>(
49 inputFileName_
, ec_
, llvm::sys::fs::OF_None
);
51 FAIL() << "Failed to create the input file";
53 // Get the path of the input file.
54 llvm::SmallString
<256> cwd
;
55 if (std::error_code ec_
= llvm::sys::fs::current_path(cwd
))
56 FAIL() << "Failed to obtain the current working directory";
57 inputFilePath_
= cwd
.c_str();
58 inputFilePath_
+= "/" + inputFileName_
;
60 // Prepare the compiler (CompilerInvocation + CompilerInstance)
61 compInst_
.CreateDiagnostics();
62 invocation_
= std::make_shared
<CompilerInvocation
>();
64 compInst_
.set_invocation(std::move(invocation_
));
65 compInst_
.frontendOpts().inputs
.push_back(
66 FrontendInputFile(inputFilePath_
, Language::Fortran
));
69 void TearDown() override
{
70 // Clear the input file.
71 llvm::sys::fs::remove(inputFileName_
);
73 // Clear the output files.
74 // Note that these tests use an output buffer (as opposed to an output
75 // file), hence there are no physical output files to delete and
76 // `EraseFiles` is set to `false`. Also, some actions (e.g.
77 // `ParseSyntaxOnly`) don't generated output. In such cases there's no
78 // output to clear and `ClearOutputFile` returns immediately.
79 compInst_
.ClearOutputFiles(/*EraseFiles=*/false);
83 TEST_F(FrontendActionTest
, TestInputOutput
) {
84 // Populate the input file with the pre-defined input and flush it.
85 *(inputFileOs_
) << "End Program arithmetic";
88 // Set-up the action kind.
89 compInst_
.invocation().frontendOpts().programAction
= InputOutputTest
;
91 // Set-up the output stream. Using output buffer wrapped as an output
92 // stream, as opposed to an actual file (or a file descriptor).
93 llvm::SmallVector
<char, 256> outputFileBuffer
;
94 std::unique_ptr
<llvm::raw_pwrite_stream
> outputFileStream(
95 new llvm::raw_svector_ostream(outputFileBuffer
));
96 compInst_
.set_outputStream(std::move(outputFileStream
));
98 // Execute the action.
99 bool success
= ExecuteCompilerInvocation(&compInst_
);
101 // Validate the expected output.
102 EXPECT_TRUE(success
);
103 EXPECT_TRUE(!outputFileBuffer
.empty());
104 EXPECT_TRUE(llvm::StringRef(outputFileBuffer
.data())
105 .startswith("End Program arithmetic"));
108 TEST_F(FrontendActionTest
, PrintPreprocessedInput
) {
109 // Populate the input file with the pre-defined input and flush it.
110 *(inputFileOs_
) << "#ifdef NEW\n"
115 inputFileOs_
.reset();
117 // Set-up the action kind.
118 compInst_
.invocation().frontendOpts().programAction
= PrintPreprocessedInput
;
119 compInst_
.invocation().preprocessorOpts().noReformat
= true;
121 // Set-up the output stream. We are using output buffer wrapped as an output
122 // stream, as opposed to an actual file (or a file descriptor).
123 llvm::SmallVector
<char, 256> outputFileBuffer
;
124 std::unique_ptr
<llvm::raw_pwrite_stream
> outputFileStream(
125 new llvm::raw_svector_ostream(outputFileBuffer
));
126 compInst_
.set_outputStream(std::move(outputFileStream
));
128 // Execute the action.
129 bool success
= ExecuteCompilerInvocation(&compInst_
);
131 // Validate the expected output.
132 EXPECT_TRUE(success
);
133 EXPECT_TRUE(!outputFileBuffer
.empty());
135 llvm::StringRef(outputFileBuffer
.data()).startswith("program b\n"));
138 TEST_F(FrontendActionTest
, ParseSyntaxOnly
) {
139 // Populate the input file with the pre-defined input and flush it.
140 *(inputFileOs_
) << "IF (A > 0.0) IF (B < 0.0) A = LOG (A)\n"
142 inputFileOs_
.reset();
144 // Set-up the action kind.
145 compInst_
.invocation().frontendOpts().programAction
= ParseSyntaxOnly
;
147 // Set-up the output stream for the semantic diagnostics.
148 llvm::SmallVector
<char, 256> outputDiagBuffer
;
149 std::unique_ptr
<llvm::raw_pwrite_stream
> outputStream(
150 new llvm::raw_svector_ostream(outputDiagBuffer
));
151 compInst_
.set_semaOutputStream(std::move(outputStream
));
153 // Execute the action.
154 bool success
= ExecuteCompilerInvocation(&compInst_
);
156 // Validate the expected output.
157 EXPECT_FALSE(success
);
158 EXPECT_TRUE(!outputDiagBuffer
.empty());
160 llvm::StringRef(outputDiagBuffer
.data())
162 ":1:14: error: IF statement is not allowed in IF statement\n"));
165 TEST_F(FrontendActionTest
, EmitLLVM
) {
166 // Populate the input file with the pre-defined input and flush it.
167 *(inputFileOs_
) << "end program";
168 inputFileOs_
.reset();
170 // Set-up the action kind.
171 compInst_
.invocation().frontendOpts().programAction
= EmitLLVM
;
172 compInst_
.invocation().preprocessorOpts().noReformat
= true;
174 // Set-up the output stream. We are using output buffer wrapped as an output
175 // stream, as opposed to an actual file (or a file descriptor).
176 llvm::SmallVector
<char> outputFileBuffer
;
177 std::unique_ptr
<llvm::raw_pwrite_stream
> outputFileStream(
178 new llvm::raw_svector_ostream(outputFileBuffer
));
179 compInst_
.set_outputStream(std::move(outputFileStream
));
181 // Execute the action.
182 bool success
= ExecuteCompilerInvocation(&compInst_
);
184 // Validate the expected output.
185 EXPECT_TRUE(success
);
186 EXPECT_TRUE(!outputFileBuffer
.empty());
188 EXPECT_TRUE(llvm::StringRef(outputFileBuffer
.data())
189 .contains("define void @_QQmain()"));