mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-24 22:02:23 +01:00
244 lines
9.5 KiB
C++
244 lines
9.5 KiB
C++
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
|
// Exceptions. See /LICENSE for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
#include "toolchain/driver/driver.h"
|
|
|
|
#include <gmock/gmock.h>
|
|
#include <gtest/gtest.h>
|
|
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
#include "llvm/Support/SourceMgr.h"
|
|
#include "toolchain/common/yaml_test_helpers.h"
|
|
#include "toolchain/diagnostics/diagnostic_emitter.h"
|
|
|
|
namespace Carbon::Testing {
|
|
namespace {
|
|
|
|
using ::testing::ElementsAre;
|
|
using ::testing::HasSubstr;
|
|
using ::testing::StrEq;
|
|
|
|
/// A raw_ostream that makes it easy to repeatedly check streamed output.
|
|
class RawTestOstream : public llvm::raw_ostream {
|
|
public:
|
|
~RawTestOstream() override {
|
|
flush();
|
|
if (!buffer_.empty()) {
|
|
ADD_FAILURE() << "Unchecked output:\n" << buffer_;
|
|
}
|
|
}
|
|
|
|
/// Flushes the stream and returns the contents so far, clearing the stream
|
|
/// back to empty.
|
|
auto TakeStr() -> std::string {
|
|
flush();
|
|
std::string result = std::move(buffer_);
|
|
buffer_.clear();
|
|
return result;
|
|
}
|
|
|
|
private:
|
|
void write_impl(const char* ptr, size_t size) override {
|
|
buffer_.append(ptr, ptr + size);
|
|
}
|
|
|
|
[[nodiscard]] auto current_pos() const -> uint64_t override {
|
|
return buffer_.size();
|
|
}
|
|
|
|
std::string buffer_;
|
|
};
|
|
|
|
TEST(DriverTest, FullCommandErrors) {
|
|
RawTestOstream test_output_stream;
|
|
RawTestOstream test_error_stream;
|
|
Driver driver = Driver(test_output_stream, test_error_stream);
|
|
|
|
EXPECT_FALSE(driver.RunFullCommand({}));
|
|
EXPECT_THAT(test_error_stream.TakeStr(), HasSubstr("ERROR"));
|
|
|
|
EXPECT_FALSE(driver.RunFullCommand({"foo"}));
|
|
EXPECT_THAT(test_error_stream.TakeStr(), HasSubstr("ERROR"));
|
|
|
|
EXPECT_FALSE(driver.RunFullCommand({"foo --bar --baz"}));
|
|
EXPECT_THAT(test_error_stream.TakeStr(), HasSubstr("ERROR"));
|
|
}
|
|
|
|
TEST(DriverTest, Help) {
|
|
RawTestOstream test_output_stream;
|
|
RawTestOstream test_error_stream;
|
|
Driver driver = Driver(test_output_stream, test_error_stream);
|
|
|
|
EXPECT_TRUE(driver.RunHelpSubcommand(ConsoleDiagnosticConsumer(), {}));
|
|
EXPECT_THAT(test_error_stream.TakeStr(), StrEq(""));
|
|
auto help_text = test_output_stream.TakeStr();
|
|
|
|
// Help text should mention each subcommand.
|
|
#define CARBON_SUBCOMMAND(Name, Spelling, ...) \
|
|
EXPECT_THAT(help_text, HasSubstr(Spelling));
|
|
#include "toolchain/driver/flags.def"
|
|
|
|
// Check that the subcommand dispatch works.
|
|
EXPECT_TRUE(driver.RunFullCommand({"help"}));
|
|
EXPECT_THAT(test_error_stream.TakeStr(), StrEq(""));
|
|
EXPECT_THAT(test_output_stream.TakeStr(), StrEq(help_text));
|
|
}
|
|
|
|
TEST(DriverTest, HelpErrors) {
|
|
RawTestOstream test_output_stream;
|
|
RawTestOstream test_error_stream;
|
|
Driver driver = Driver(test_output_stream, test_error_stream);
|
|
|
|
EXPECT_FALSE(driver.RunHelpSubcommand(ConsoleDiagnosticConsumer(), {"foo"}));
|
|
EXPECT_THAT(test_output_stream.TakeStr(), StrEq(""));
|
|
EXPECT_THAT(test_error_stream.TakeStr(), HasSubstr("ERROR"));
|
|
|
|
EXPECT_FALSE(driver.RunHelpSubcommand(ConsoleDiagnosticConsumer(), {"help"}));
|
|
EXPECT_THAT(test_output_stream.TakeStr(), StrEq(""));
|
|
EXPECT_THAT(test_error_stream.TakeStr(), HasSubstr("ERROR"));
|
|
|
|
EXPECT_FALSE(
|
|
driver.RunHelpSubcommand(ConsoleDiagnosticConsumer(), {"--xyz"}));
|
|
EXPECT_THAT(test_output_stream.TakeStr(), StrEq(""));
|
|
EXPECT_THAT(test_error_stream.TakeStr(), HasSubstr("ERROR"));
|
|
}
|
|
|
|
auto CreateTestFile(llvm::StringRef text) -> std::string {
|
|
int fd = -1;
|
|
llvm::SmallString<1024> path;
|
|
auto ec = llvm::sys::fs::createTemporaryFile("test_file", ".txt", fd, path);
|
|
if (ec) {
|
|
llvm::report_fatal_error(llvm::Twine("Failed to create temporary file: ") +
|
|
ec.message());
|
|
}
|
|
|
|
llvm::raw_fd_ostream s(fd, /*shouldClose=*/true);
|
|
s << text;
|
|
s.close();
|
|
|
|
return path.str().str();
|
|
}
|
|
|
|
TEST(DriverTest, DumpTokens) {
|
|
RawTestOstream test_output_stream;
|
|
RawTestOstream test_error_stream;
|
|
Driver driver = Driver(test_output_stream, test_error_stream);
|
|
|
|
auto test_file_path = CreateTestFile("Hello World");
|
|
EXPECT_TRUE(driver.RunDumpTokensSubcommand(ConsoleDiagnosticConsumer(),
|
|
{test_file_path}));
|
|
EXPECT_THAT(test_error_stream.TakeStr(), StrEq(""));
|
|
auto tokenized_text = test_output_stream.TakeStr();
|
|
|
|
EXPECT_THAT(Yaml::Value::FromText(tokenized_text),
|
|
ElementsAre(Yaml::MappingValue{
|
|
{"token", Yaml::MappingValue{{"index", "0"},
|
|
{"kind", "Identifier"},
|
|
{"line", "1"},
|
|
{"column", "1"},
|
|
{"indent", "1"},
|
|
{"spelling", "Hello"},
|
|
{"identifier", "0"},
|
|
{"has_trailing_space", "true"}}},
|
|
{"token", Yaml::MappingValue{{"index", "1"},
|
|
{"kind", "Identifier"},
|
|
{"line", "1"},
|
|
{"column", "7"},
|
|
{"indent", "1"},
|
|
{"spelling", "World"},
|
|
{"identifier", "1"},
|
|
{"has_trailing_space", "true"}}},
|
|
{"token", Yaml::MappingValue{{"index", "2"},
|
|
{"kind", "EndOfFile"},
|
|
{"line", "1"},
|
|
{"column", "12"},
|
|
{"indent", "1"},
|
|
{"spelling", ""}}}}));
|
|
|
|
// Check that the subcommand dispatch works.
|
|
EXPECT_TRUE(driver.RunFullCommand({"dump-tokens", test_file_path}));
|
|
EXPECT_THAT(test_error_stream.TakeStr(), StrEq(""));
|
|
EXPECT_THAT(test_output_stream.TakeStr(), StrEq(tokenized_text));
|
|
}
|
|
|
|
TEST(DriverTest, DumpTokenErrors) {
|
|
RawTestOstream test_output_stream;
|
|
RawTestOstream test_error_stream;
|
|
Driver driver = Driver(test_output_stream, test_error_stream);
|
|
|
|
EXPECT_FALSE(driver.RunDumpTokensSubcommand(ConsoleDiagnosticConsumer(), {}));
|
|
EXPECT_THAT(test_output_stream.TakeStr(), StrEq(""));
|
|
EXPECT_THAT(test_error_stream.TakeStr(), HasSubstr("ERROR"));
|
|
|
|
EXPECT_FALSE(
|
|
driver.RunDumpTokensSubcommand(ConsoleDiagnosticConsumer(), {"--xyz"}));
|
|
EXPECT_THAT(test_output_stream.TakeStr(), StrEq(""));
|
|
EXPECT_THAT(test_error_stream.TakeStr(), HasSubstr("ERROR"));
|
|
|
|
EXPECT_FALSE(driver.RunDumpTokensSubcommand(ConsoleDiagnosticConsumer(),
|
|
{"/not/a/real/file/name"}));
|
|
EXPECT_THAT(test_output_stream.TakeStr(), StrEq(""));
|
|
EXPECT_THAT(test_error_stream.TakeStr(), HasSubstr("ERROR"));
|
|
}
|
|
|
|
TEST(DriverTest, DumpParseTree) {
|
|
RawTestOstream test_output_stream;
|
|
RawTestOstream test_error_stream;
|
|
Driver driver = Driver(test_output_stream, test_error_stream);
|
|
|
|
auto test_file_path = CreateTestFile("var v: Int = 42;");
|
|
EXPECT_TRUE(driver.RunDumpParseTreeSubcommand(ConsoleDiagnosticConsumer(),
|
|
{test_file_path}));
|
|
EXPECT_THAT(test_error_stream.TakeStr(), StrEq(""));
|
|
auto tokenized_text = test_output_stream.TakeStr();
|
|
|
|
EXPECT_THAT(
|
|
Yaml::Value::FromText(tokenized_text),
|
|
ElementsAre(Yaml::SequenceValue{
|
|
Yaml::MappingValue{
|
|
{"node_index", "6"},
|
|
{"kind", "VariableDeclaration"},
|
|
{"text", "var"},
|
|
{"subtree_size", "7"},
|
|
{"children",
|
|
Yaml::SequenceValue{
|
|
Yaml::MappingValue{
|
|
{"node_index", "2"},
|
|
{"kind", "PatternBinding"},
|
|
{"text", ":"},
|
|
{"subtree_size", "3"},
|
|
{"children",
|
|
Yaml::SequenceValue{
|
|
Yaml::MappingValue{{"node_index", "0"},
|
|
{"kind", "DeclaredName"},
|
|
{"text", "v"}},
|
|
Yaml::MappingValue{{"node_index", "1"},
|
|
{"kind", "NameReference"},
|
|
{"text", "Int"}}}}},
|
|
Yaml::MappingValue{{"node_index", "4"},
|
|
{"kind", "VariableInitializer"},
|
|
{"text", "="},
|
|
{"subtree_size", "2"},
|
|
{"children", //
|
|
Yaml::SequenceValue{Yaml::MappingValue{
|
|
{"node_index", "3"},
|
|
{"kind", "Literal"},
|
|
{"text", "42"}}}}},
|
|
Yaml::MappingValue{{"node_index", "5"},
|
|
{"kind", "DeclarationEnd"},
|
|
{"text", ";"}}}}},
|
|
Yaml::MappingValue{{"node_index", "7"}, //
|
|
{"kind", "FileEnd"},
|
|
{"text", ""}}}));
|
|
|
|
// Check that the subcommand dispatch works.
|
|
EXPECT_TRUE(driver.RunFullCommand({"dump-parse-tree", test_file_path}));
|
|
EXPECT_THAT(test_error_stream.TakeStr(), StrEq(""));
|
|
EXPECT_THAT(test_output_stream.TakeStr(), StrEq(tokenized_text));
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace Carbon::Testing
|