| //===- unittests/Frontend/FrontendActionTest.cpp FrontendAction tests-----===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "flang/Frontend/CompilerInstance.h" |
| #include "flang/Frontend/CompilerInvocation.h" |
| #include "flang/Frontend/FrontendOptions.h" |
| #include "flang/FrontendTool/Utils.h" |
| #include "llvm/Support/FileSystem.h" |
| #include "llvm/Support/raw_ostream.h" |
| |
| #include "gtest/gtest.h" |
| |
| using namespace Fortran::frontend; |
| |
| namespace { |
| |
| class FrontendActionTest : public ::testing::Test { |
| protected: |
| // AllSources (which is used to manage files inside every compiler |
| // instance), works with paths. So we need a filename and a path for the |
| // input file. |
| // TODO: We could use `-` for inputFilePath_, but then we'd need a way to |
| // write to stdin that's then read by AllSources. Ideally, AllSources should |
| // be capable of reading from any stream. |
| std::string inputFileName_; |
| std::string inputFilePath_; |
| // The output stream for the input file. Use this to populate the input. |
| std::unique_ptr<llvm::raw_fd_ostream> inputFileOs_; |
| |
| std::error_code ec_; |
| |
| CompilerInstance compInst_; |
| std::shared_ptr<CompilerInvocation> invocation_; |
| |
| void SetUp() override { |
| // Generate a unique test file name. |
| const testing::TestInfo *const test_info = |
| testing::UnitTest::GetInstance()->current_test_info(); |
| inputFileName_ = std::string(test_info->name()) + "_test-file.f90"; |
| |
| // Create the input file stream. Note that this stream is populated |
| // separately in every test (i.e. the input is test specific). |
| inputFileOs_ = std::make_unique<llvm::raw_fd_ostream>( |
| inputFileName_, ec_, llvm::sys::fs::OF_None); |
| if (ec_) |
| FAIL() << "Failed to create the input file"; |
| |
| // Get the path of the input file. |
| llvm::SmallString<256> cwd; |
| if (std::error_code ec_ = llvm::sys::fs::current_path(cwd)) |
| FAIL() << "Failed to obtain the current working directory"; |
| inputFilePath_ = cwd.c_str(); |
| inputFilePath_ += "/" + inputFileName_; |
| |
| // Prepare the compiler (CompilerInvocation + CompilerInstance) |
| compInst_.CreateDiagnostics(); |
| invocation_ = std::make_shared<CompilerInvocation>(); |
| |
| compInst_.set_invocation(std::move(invocation_)); |
| compInst_.frontendOpts().inputs.push_back( |
| FrontendInputFile(inputFilePath_, Language::Fortran)); |
| } |
| |
| void TearDown() override { |
| // Clear the input file. |
| llvm::sys::fs::remove(inputFileName_); |
| |
| // Clear the output files. |
| // Note that these tests use an output buffer (as opposed to an output |
| // file), hence there are no physical output files to delete and |
| // `EraseFiles` is set to `false`. Also, some actions (e.g. |
| // `ParseSyntaxOnly`) don't generated output. In such cases there's no |
| // output to clear and `ClearOutputFile` returns immediately. |
| compInst_.ClearOutputFiles(/*EraseFiles=*/false); |
| } |
| }; |
| |
| TEST_F(FrontendActionTest, TestInputOutput) { |
| // Populate the input file with the pre-defined input and flush it. |
| *(inputFileOs_) << "End Program arithmetic"; |
| inputFileOs_.reset(); |
| |
| // Set-up the action kind. |
| compInst_.invocation().frontendOpts().programAction = InputOutputTest; |
| |
| // Set-up the output stream. Using output buffer wrapped as an output |
| // stream, as opposed to an actual file (or a file descriptor). |
| llvm::SmallVector<char, 256> outputFileBuffer; |
| std::unique_ptr<llvm::raw_pwrite_stream> outputFileStream( |
| new llvm::raw_svector_ostream(outputFileBuffer)); |
| compInst_.set_outputStream(std::move(outputFileStream)); |
| |
| // Execute the action. |
| bool success = ExecuteCompilerInvocation(&compInst_); |
| |
| // Validate the expected output. |
| EXPECT_TRUE(success); |
| EXPECT_TRUE(!outputFileBuffer.empty()); |
| EXPECT_TRUE(llvm::StringRef(outputFileBuffer.data()) |
| .startswith("End Program arithmetic")); |
| } |
| |
| TEST_F(FrontendActionTest, PrintPreprocessedInput) { |
| // Populate the input file with the pre-defined input and flush it. |
| *(inputFileOs_) << "#ifdef NEW\n" |
| << " Program A \n" |
| << "#else\n" |
| << " Program B\n" |
| << "#endif"; |
| inputFileOs_.reset(); |
| |
| // Set-up the action kind. |
| compInst_.invocation().frontendOpts().programAction = PrintPreprocessedInput; |
| compInst_.invocation().preprocessorOpts().noReformat = true; |
| |
| // Set-up the output stream. We are using output buffer wrapped as an output |
| // stream, as opposed to an actual file (or a file descriptor). |
| llvm::SmallVector<char, 256> outputFileBuffer; |
| std::unique_ptr<llvm::raw_pwrite_stream> outputFileStream( |
| new llvm::raw_svector_ostream(outputFileBuffer)); |
| compInst_.set_outputStream(std::move(outputFileStream)); |
| |
| // Execute the action. |
| bool success = ExecuteCompilerInvocation(&compInst_); |
| |
| // Validate the expected output. |
| EXPECT_TRUE(success); |
| EXPECT_TRUE(!outputFileBuffer.empty()); |
| EXPECT_TRUE( |
| llvm::StringRef(outputFileBuffer.data()).startswith("program b\n")); |
| } |
| |
| TEST_F(FrontendActionTest, ParseSyntaxOnly) { |
| // Populate the input file with the pre-defined input and flush it. |
| *(inputFileOs_) << "IF (A > 0.0) IF (B < 0.0) A = LOG (A)\n" |
| << "END"; |
| inputFileOs_.reset(); |
| |
| // Set-up the action kind. |
| compInst_.invocation().frontendOpts().programAction = ParseSyntaxOnly; |
| |
| // Set-up the output stream for the semantic diagnostics. |
| llvm::SmallVector<char, 256> outputDiagBuffer; |
| std::unique_ptr<llvm::raw_pwrite_stream> outputStream( |
| new llvm::raw_svector_ostream(outputDiagBuffer)); |
| compInst_.set_semaOutputStream(std::move(outputStream)); |
| |
| // Execute the action. |
| bool success = ExecuteCompilerInvocation(&compInst_); |
| |
| // Validate the expected output. |
| EXPECT_FALSE(success); |
| EXPECT_TRUE(!outputDiagBuffer.empty()); |
| EXPECT_TRUE( |
| llvm::StringRef(outputDiagBuffer.data()) |
| .contains( |
| ":1:14: error: IF statement is not allowed in IF statement\n")); |
| } |
| } // namespace |