| // Copyright 2017-2020 The Verible Authors. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| // This tests the VerilogLinter class and its associated functions, end-to-end. | |
| // | |
| // Tests for individual lint rules can be found in | |
| // verilog/analysis/checkers/. | |
| #include "verilog/analysis/verilog_linter.h" | |
| #include <algorithm> | |
| #include <array> | |
| #include <fstream> | |
| #include <iterator> | |
| #include <memory> | |
| #include <sstream> // IWYU pragma: keep // for ostringstream | |
| #include <string> | |
| #include <utility> | |
| #include <vector> | |
| #include "absl/status/status.h" | |
| #include "absl/status/statusor.h" | |
| #include "absl/strings/match.h" | |
| #include "absl/strings/string_view.h" | |
| #include "common/analysis/violation_handler.h" | |
| #include "common/util/file_util.h" | |
| #include "common/util/logging.h" | |
| #include "gmock/gmock.h" | |
| #include "gtest/gtest.h" | |
| #include "verilog/analysis/default_rules.h" | |
| #include "verilog/analysis/verilog_analyzer.h" | |
| #include "verilog/analysis/verilog_linter_configuration.h" | |
| namespace verilog { | |
| namespace { | |
| using ::testing::EndsWith; | |
| using ::testing::StartsWith; | |
| using verible::ViolationFixer; | |
| using verible::ViolationPrinter; | |
| using verible::file::GetContents; | |
| using verible::file::testing::ScopedTestFile; | |
| class DefaultLinterConfigTestFixture { | |
| public: | |
| // Test code using the default rule set. | |
| DefaultLinterConfigTestFixture() : config_() { | |
| config_.UseRuleSet(RuleSet::kDefault); | |
| } | |
| protected: | |
| LinterConfiguration config_; | |
| }; | |
| class LintOneFileTest : public DefaultLinterConfigTestFixture, | |
| public testing::Test { | |
| public: | |
| LintOneFileTest() = default; | |
| }; | |
| // Tests that nonexistent file is handled as a fatal error. | |
| TEST_F(LintOneFileTest, FileNotFound) { | |
| std::ostringstream output; | |
| ViolationPrinter violation_printer(&output); | |
| const int exit_code = | |
| LintOneFile(&output, "FileNotFound.sv", config_, &violation_printer, true, | |
| false, false, false); | |
| EXPECT_EQ(exit_code, 2); | |
| } | |
| // Tests that clean code exits 0 (success). | |
| TEST_F(LintOneFileTest, LintCleanFiles) { | |
| constexpr absl::string_view kTestCases[] = { | |
| "", // empty file | |
| "\n", | |
| "class foo;\n" | |
| "endclass : foo\n", | |
| }; | |
| for (const auto test_code : kTestCases) { | |
| const ScopedTestFile temp_file(testing::TempDir(), test_code); | |
| { | |
| std::ostringstream output; | |
| ViolationPrinter violation_printer(&output); | |
| const int exit_code = | |
| LintOneFile(&output, temp_file.filename(), config_, | |
| &violation_printer, true, false, false, false); | |
| EXPECT_EQ(exit_code, 0); | |
| EXPECT_TRUE(output.str().empty()); // silence | |
| } | |
| { // enable additional error context printing | |
| std::ostringstream output; | |
| ViolationPrinter violation_printer(&output); | |
| const int exit_code = | |
| LintOneFile(&output, temp_file.filename(), config_, | |
| &violation_printer, true, false, false, true); | |
| EXPECT_EQ(exit_code, 0); | |
| EXPECT_TRUE(output.str().empty()); // silence | |
| } | |
| } | |
| } | |
| // Tests that invalid code is handled according to 'parse_fatal' parameter. | |
| TEST_F(LintOneFileTest, SyntaxError) { | |
| constexpr absl::string_view kTestCases[] = { | |
| "class foo;\n", // no endclass | |
| "endclass : foo\n", // no begin class | |
| "module 444bad_name; endmodule\n", // lexical error | |
| }; | |
| for (const auto test_code : kTestCases) { | |
| const ScopedTestFile temp_file(testing::TempDir(), test_code); | |
| { // continue even with syntax error | |
| std::ostringstream output; | |
| ViolationPrinter violation_printer(&output); | |
| const int exit_code = | |
| LintOneFile(&output, temp_file.filename(), config_, | |
| &violation_printer, true, false, false, false); | |
| EXPECT_EQ(exit_code, 0); | |
| EXPECT_FALSE(output.str().empty()); | |
| } | |
| { // continue even with syntax error with additional error context | |
| std::ostringstream output; | |
| ViolationPrinter violation_printer(&output); | |
| const int exit_code = | |
| LintOneFile(&output, temp_file.filename(), config_, | |
| &violation_printer, true, false, false, true); | |
| EXPECT_EQ(exit_code, 0); | |
| EXPECT_FALSE(output.str().empty()); | |
| } | |
| { // abort on syntax error | |
| std::ostringstream output; | |
| ViolationPrinter violation_printer(&output); | |
| const int exit_code = | |
| LintOneFile(&output, temp_file.filename(), config_, | |
| &violation_printer, true, true, false, false); | |
| EXPECT_EQ(exit_code, 1); | |
| EXPECT_FALSE(output.str().empty()); | |
| } | |
| { // ignore syntax error | |
| std::ostringstream output; | |
| ViolationPrinter violation_printer(&output); | |
| const int exit_code = | |
| LintOneFile(&output, temp_file.filename(), config_, | |
| &violation_printer, false, false, false, false); | |
| EXPECT_EQ(exit_code, 0); | |
| EXPECT_TRUE(output.str().empty()); // silence | |
| } | |
| } | |
| } | |
| TEST_F(LintOneFileTest, LintError) { | |
| constexpr absl::string_view kTestCases[] = { | |
| "task automatic foo;\n" | |
| " $psprintf(\"blah\");\n" // forbidden function | |
| "endtask\n", | |
| }; | |
| for (const auto test_code : kTestCases) { | |
| const ScopedTestFile temp_file(testing::TempDir(), test_code); | |
| { // continue even with lint error | |
| std::ostringstream output; | |
| ViolationPrinter violation_printer(&output); | |
| const int exit_code = | |
| LintOneFile(&output, temp_file.filename(), config_, | |
| &violation_printer, true, false, false, false); | |
| EXPECT_EQ(exit_code, 0) << "output:\n" << output.str(); | |
| EXPECT_FALSE(output.str().empty()); | |
| } | |
| { // abort on lint error | |
| std::ostringstream output; | |
| ViolationPrinter violation_printer(&output); | |
| const int exit_code = | |
| LintOneFile(&output, temp_file.filename(), config_, | |
| &violation_printer, true, false, true, false); | |
| EXPECT_EQ(exit_code, 1) << "output:\n" << output.str(); | |
| EXPECT_FALSE(output.str().empty()); | |
| } | |
| } | |
| } | |
| class VerilogLinterTest : public DefaultLinterConfigTestFixture, | |
| public testing::Test { | |
| public: | |
| // Test code using the default rule set. | |
| VerilogLinterTest() = default; | |
| protected: | |
| // Returns diagnostic text from analyzing source code. | |
| std::pair<absl::Status, std::string> LintAnalyzeText( | |
| absl::string_view filename, absl::string_view content) const { | |
| // Run the analyzer to produce a syntax tree from source code. | |
| const auto analyzer = std::make_unique<VerilogAnalyzer>(content, filename); | |
| const absl::Status status = ABSL_DIE_IF_NULL(analyzer)->Analyze(); | |
| std::ostringstream diagnostics; | |
| if (!status.ok()) { | |
| bool with_diagnostic_contex = false; | |
| const std::vector<std::string> syntax_error_messages( | |
| analyzer->LinterTokenErrorMessages(with_diagnostic_contex)); | |
| for (const auto& message : syntax_error_messages) { | |
| diagnostics << message << std::endl; | |
| } | |
| with_diagnostic_contex = true; | |
| const std::vector<std::string> syntax_error_messages_with_context( | |
| analyzer->LinterTokenErrorMessages(with_diagnostic_contex)); | |
| for (const auto& message : syntax_error_messages_with_context) { | |
| diagnostics << message << std::endl; | |
| } | |
| } | |
| const auto& text_structure = analyzer->Data(); | |
| // For testing purposes we want the status returned to reflect | |
| // lint success, so as long as we have a syntax tree (even if there | |
| // are errors), run the lint checks. | |
| const absl::StatusOr<std::vector<verible::LintRuleStatus>> lint_result = | |
| VerilogLintTextStructure(filename, config_, text_structure); | |
| verilog::ViolationPrinter violation_printer(&diagnostics); | |
| const std::set<verible::LintViolationWithStatus> violations = | |
| GetSortedViolations(lint_result.value()); | |
| violation_printer.HandleViolations(violations, text_structure.Contents(), | |
| filename); | |
| return {lint_result.status(), diagnostics.str()}; | |
| } | |
| }; | |
| // This test verifies that VerilogLintTextStructure runs on an empty tree. | |
| TEST_F(VerilogLinterTest, AnonymousEmptyTree) { | |
| const auto diagnostics = LintAnalyzeText("", ""); | |
| EXPECT_TRUE(diagnostics.first.ok()); | |
| EXPECT_EQ(diagnostics.second, ""); | |
| } | |
| // This test verifies that VerilogLintTextStructure runs on complete source, | |
| // that is lint-clean. | |
| TEST_F(VerilogLinterTest, NoLintViolation) { | |
| const auto diagnostics = LintAnalyzeText("good.sv", | |
| "task automatic foo;\n" | |
| " $display(\"blah\");\n" | |
| "endtask\n"); | |
| EXPECT_TRUE(diagnostics.first.ok()); | |
| EXPECT_EQ(diagnostics.second, ""); | |
| } | |
| // This test verifies that VerilogLintTextStructure runs on complete source, | |
| // with one syntax tree lint finding. | |
| TEST_F(VerilogLinterTest, KnownTreeLintViolation) { | |
| const auto diagnostics = LintAnalyzeText("bad.sv", | |
| "task automatic foo;\n" | |
| " $psprintf(\"blah\");\n" | |
| "endtask\n"); | |
| EXPECT_TRUE(diagnostics.first.ok()); | |
| const auto expected = | |
| "bad.sv:2:3-11: $psprintf is a forbidden system function " | |
| "or task, please use $sformatf instead"; | |
| EXPECT_THAT(diagnostics.second, StartsWith(expected)); | |
| EXPECT_THAT(diagnostics.second, EndsWith("[invalid-system-task-function]\n")); | |
| } | |
| // This test verifies that VerilogLintTextStructure runs on complete source, | |
| // with one syntax tree lint finding that is waived (next-line). | |
| TEST_F(VerilogLinterTest, KnownTreeLintViolationWaivedNextLine) { | |
| const auto diagnostics = | |
| LintAnalyzeText("bad.sv", | |
| "task automatic foo;\n" | |
| " // verilog_lint: waive invalid-system-task-function\n" | |
| " $psprintf(\"blah\");\n" | |
| "endtask\n"); | |
| EXPECT_TRUE(diagnostics.first.ok()); | |
| EXPECT_EQ(diagnostics.second, ""); | |
| } | |
| // This test verifies that VerilogLintTextStructure runs on complete source, | |
| // with one syntax tree lint finding that is waived (same-line). | |
| TEST_F(VerilogLinterTest, KnownTreeLintViolationWaivedSameLine) { | |
| const auto diagnostics = | |
| LintAnalyzeText("bad.sv", | |
| "task automatic foo;\n" | |
| " $psprintf(\"blah\"); // verilog_lint: waive " | |
| "invalid-system-task-function\n" | |
| "endtask\n"); | |
| EXPECT_TRUE(diagnostics.first.ok()); | |
| EXPECT_EQ(diagnostics.second, ""); | |
| } | |
| // This test verifies that VerilogLintTextStructure runs on complete source, | |
| // with one syntax tree lint finding that is waived (line-range). | |
| TEST_F(VerilogLinterTest, KnownTreeLintViolationWaivedLineRange) { | |
| const auto diagnostics = LintAnalyzeText( | |
| "bad.sv", | |
| "task automatic foo;\n" | |
| " // verilog_lint: waive-start invalid-system-task-function\n" | |
| " $psprintf(\"blah\");\n" | |
| " // verilog_lint: waive-end invalid-system-task-function\n" | |
| "endtask\n"); | |
| EXPECT_TRUE(diagnostics.first.ok()); | |
| EXPECT_EQ(diagnostics.second, ""); | |
| } | |
| // This test verifies that VerilogLintTextStructure runs on complete source, | |
| // with one token stream lint finding. | |
| TEST_F(VerilogLinterTest, KnownTokenStreamLintViolation) { | |
| using analysis::kDefaultRuleSet; | |
| // TODO(fangism): Remove this conditional check or choose a different | |
| // token-stream based lint rule that is enabled by default. | |
| // (Just so happens that we may not want endif-comment enabled by default.) | |
| if (std::find(std::begin(kDefaultRuleSet), std::end(kDefaultRuleSet), | |
| "endif-comment") != std::end(kDefaultRuleSet)) { | |
| const auto diagnostics = LintAnalyzeText("endif.sv", | |
| "`ifdef SIM\n" | |
| "module foo;\n" | |
| "endmodule\n" | |
| "`endif\n"); | |
| static const auto expect_message = | |
| "endif.sv:4:1: `endif should be followed on the same line by a " | |
| "comment that matches the opening `ifdef/`ifndef. (SIM) "; | |
| EXPECT_TRUE(diagnostics.first.ok()); | |
| EXPECT_THAT(diagnostics.second, StartsWith(expect_message)); | |
| EXPECT_THAT(diagnostics.second, EndsWith("[endif-comment]\n")); | |
| } | |
| } | |
| // This test verifies that VerilogLintTextStructure runs on complete source, | |
| // with one line-lint-rule finding. | |
| TEST_F(VerilogLinterTest, KnownLineLintViolation) { | |
| const auto diagnostics = LintAnalyzeText("tab.sv", | |
| "`include \"blah.svh\";\n" | |
| "\n" | |
| "module\ttab;\n" // bad tab here | |
| "endmodule\n"); | |
| EXPECT_TRUE(diagnostics.first.ok()); | |
| EXPECT_THAT(diagnostics.second, | |
| StartsWith("tab.sv:3:7: Use spaces, not tabs.")); | |
| EXPECT_THAT(diagnostics.second, EndsWith("[no-tabs]\n")); | |
| } | |
| // This test verifies that VerilogLintTextStructure runs on complete source, | |
| // with one text structure lint rule finding (line-length). | |
| TEST_F(VerilogLinterTest, KnownTextStructureLintViolation) { | |
| const auto diagnostics = LintAnalyzeText( | |
| "long.sv", | |
| "module long;\n" | |
| "initial xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx = " | |
| "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy[777777777777];\n" | |
| "endmodule\n"); | |
| EXPECT_TRUE(diagnostics.first.ok()); | |
| EXPECT_THAT(diagnostics.second, | |
| StartsWith("long.sv:2:101-114: Line length exceeds " | |
| "max: 100; is: 114")); | |
| EXPECT_THAT(diagnostics.second, EndsWith("[line-length]\n")); | |
| } | |
| // This test verifies that VerilogLintTextStructure runs on complete source, | |
| // with one waived lint rule finding (line-length). | |
| TEST_F(VerilogLinterTest, KnownTextStructureLintViolationWaived) { | |
| const auto diagnostics = LintAnalyzeText( | |
| "long.sv", | |
| "module long;\n" | |
| "initial xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx = " | |
| "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy[777777777777]; " | |
| "// verilog_lint: waive line-length\n" | |
| "endmodule\n"); | |
| EXPECT_TRUE(diagnostics.first.ok()); | |
| EXPECT_EQ(diagnostics.second, ""); | |
| } | |
| // This test verifies that VerilogLintTextStructure runs on a module-body | |
| // source, with one lint rule finding (line-length). | |
| TEST_F(VerilogLinterTest, ModuleBodyLineLength) { | |
| const auto diagnostics = LintAnalyzeText( | |
| "module-body.sv", | |
| "// verilog_syntax: parse-as-module-body\n" | |
| "\n" | |
| "initial xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx = " | |
| "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy[777777777777];\n"); | |
| EXPECT_TRUE(diagnostics.first.ok()); | |
| EXPECT_THAT( | |
| diagnostics.second, | |
| StartsWith("module-body.sv:3:101-114: Line length exceeds max: ")); | |
| EXPECT_THAT(diagnostics.second, EndsWith("[line-length]\n")); | |
| } | |
| // This test verifies that VerilogLintTextStructure runs on a module-body | |
| // source, with one waived lint rule finding (line-length). | |
| TEST_F(VerilogLinterTest, ModuleBodyLineLengthWaived) { | |
| const auto diagnostics = LintAnalyzeText( | |
| "module-body.sv", | |
| "// verilog_syntax: parse-as-module-body\n" | |
| "\n" | |
| "initial xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx = " | |
| "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy[777777777777]; " | |
| "// verilog_lint: waive line-length\n"); | |
| EXPECT_TRUE(diagnostics.first.ok()); | |
| EXPECT_EQ(diagnostics.second, ""); | |
| } | |
| TEST_F(VerilogLinterTest, MultiByteUTF8CharactersAreOnlyCountedOnce) { | |
| // Typical comment that might be found in verilog: some ASCII-art diagram | |
| // except that the 'ˉ'-'overscore' is actually a two-byte UTF8 character. | |
| constexpr char comment_with_utf8[] = | |
| "module utf8_short;\n" | |
| R"(initial a = 42; // __/ˉˉˉˉˉˉˉˉˉ\___/ˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉˉ\___/ˉˉˉˉˉ)" | |
| "\nendmodule\n"; | |
| const auto diagnostics = LintAnalyzeText("utf8_short.sv", comment_with_utf8); | |
| EXPECT_TRUE(diagnostics.first.ok()); | |
| EXPECT_EQ(diagnostics.second, ""); | |
| } | |
| TEST(VerilogLinterDocumentationTest, AllRulesHelpDescriptions) { | |
| std::ostringstream stream; | |
| verilog::GetLintRuleDescriptionsHelpFlag(&stream, "all"); | |
| // Spot-check a few patterns, must mostly make sure generation | |
| // works without any fatal errors. | |
| EXPECT_TRUE(absl::StrContains(stream.str(), "line-length")); | |
| EXPECT_TRUE(absl::StrContains(stream.str(), "posix-eof")); | |
| EXPECT_TRUE(absl::StrContains(stream.str(), "Enabled by default:")); | |
| } | |
| TEST(VerilogLinterDocumentationTest, AllRulesMarkdown) { | |
| std::ostringstream stream; | |
| verilog::GetLintRuleDescriptionsMarkdown(&stream); | |
| // Spot-check a few patterns, must mostly make sure generation | |
| // works without any fatal errors. | |
| EXPECT_TRUE(absl::StrContains(stream.str(), "line-length")); | |
| EXPECT_TRUE(absl::StrContains(stream.str(), "posix-eof")); | |
| EXPECT_TRUE(absl::StrContains(stream.str(), "Enabled by default:")); | |
| } | |
| class ViolationFixerTest : public testing::Test { | |
| public: | |
| ViolationFixerTest() : config_() { | |
| config_.UseRuleSet(RuleSet::kNone); | |
| config_.TurnOn("forbid-consecutive-null-statements"); | |
| config_.TurnOn("no-trailing-spaces"); | |
| config_.TurnOn("posix-eof"); | |
| } | |
| protected: | |
| LinterConfiguration config_; | |
| absl::Status LintAnalyzeFixText(absl::string_view content, | |
| ViolationFixer& violation_fixer, | |
| std::string* fixed_content) const { | |
| const ScopedTestFile temp_file(testing::TempDir(), content); | |
| // Run the analyzer to produce a syntax tree from source code. | |
| const auto analyzer = | |
| std::make_unique<VerilogAnalyzer>(content, temp_file.filename()); | |
| const absl::Status status = ABSL_DIE_IF_NULL(analyzer)->Analyze(); | |
| const auto& text_structure = analyzer->Data(); | |
| const absl::StatusOr<std::vector<verible::LintRuleStatus>> lint_result = | |
| VerilogLintTextStructure(temp_file.filename(), config_, text_structure); | |
| const std::set<verible::LintViolationWithStatus> violations = | |
| GetSortedViolations(lint_result.value()); | |
| violation_fixer.HandleViolations(violations, text_structure.Contents(), | |
| temp_file.filename()); | |
| const auto ok = GetContents(temp_file.filename(), fixed_content); | |
| return lint_result.status(); | |
| } | |
| void DoFixerTest( | |
| std::initializer_list<ViolationFixer::Answer> choices, | |
| std::initializer_list<absl::string_view> expected_fixed_sources) const { | |
| static constexpr std::array<const absl::string_view, 3> input_sources{ | |
| // Input source 0: | |
| // :2:10: no-trailing-spaces | |
| // :3:10: forbid-consecutive-null-statements | |
| // :4:10: forbid-consecutive-null-statements | |
| // :4:11: no-trailing-spaces | |
| // :5:10: forbid-consecutive-null-statements | |
| // :6:10: forbid-consecutive-null-statements | |
| // :7:10: no-trailing-spaces | |
| // :7:14: posix-eof | |
| "module Autofix; \n" | |
| " wire a;;\n" | |
| " wire b;; \n" | |
| " wire c;;\n" | |
| " wire d;;\n" | |
| "endmodule ", | |
| // Input source 1: | |
| // (no issues) | |
| "module AutofixTwo;\n" | |
| "endmodule\n", | |
| // Input source 2: | |
| // :1:21: forbid-consecutive-null-statements | |
| // :2:10: no-trailing-spaces | |
| "module AutofixThree;;\n" | |
| " wire a; \n" | |
| "endmodule\n", | |
| }; | |
| EXPECT_EQ(expected_fixed_sources.size(), input_sources.size()); | |
| std::initializer_list<ViolationFixer::Answer>::iterator choice_it; | |
| const ViolationFixer::AnswerChooser answer_chooser = | |
| [&choice_it, &choices](const verible::LintViolation&, | |
| absl::string_view) { | |
| EXPECT_NE(choice_it, choices.end()) | |
| << "AnswerChooser called more times than expected."; | |
| return *choice_it++; | |
| }; | |
| // In-place fixing | |
| { | |
| choice_it = choices.begin(); | |
| // intentionally unopened, diagnostics are discarded | |
| std::ofstream diagnostics; | |
| ViolationFixer violation_fixer(&diagnostics, nullptr, answer_chooser); | |
| std::vector<std::string> fixed_sources(input_sources.size()); | |
| for (size_t i = 0; i < input_sources.size(); ++i) { | |
| const absl::string_view input_source = input_sources[i]; | |
| std::string& fixed_source = fixed_sources[i]; | |
| const absl::Status status = | |
| LintAnalyzeFixText(input_source, violation_fixer, &fixed_source); | |
| EXPECT_TRUE(status.ok()); | |
| } | |
| EXPECT_EQ(choice_it, choices.end()) | |
| << "AnswerChooser called fewer times than expected."; | |
| for (size_t i = 0; i < input_sources.size(); ++i) { | |
| const std::string& fixed_source = fixed_sources[i]; | |
| const absl::string_view expected_fixed_source = | |
| *(expected_fixed_sources.begin() + i); | |
| EXPECT_EQ(fixed_source, expected_fixed_source); | |
| } | |
| } | |
| // Patch generation | |
| { | |
| choice_it = choices.begin(); | |
| // intentionally unopened, diagnostics are discarded | |
| std::ofstream diagnostics; | |
| std::ostringstream patch; | |
| ViolationFixer violation_fixer(&diagnostics, &patch, answer_chooser); | |
| std::vector<std::string> fixed_sources(input_sources.size()); | |
| for (size_t i = 0; i < input_sources.size(); ++i) { | |
| const absl::string_view input_source = input_sources[i]; | |
| std::string& fixed_source = fixed_sources[i]; | |
| const absl::Status status = | |
| LintAnalyzeFixText(input_source, violation_fixer, &fixed_source); | |
| EXPECT_TRUE(status.ok()); | |
| } | |
| EXPECT_EQ(choice_it, choices.end()) | |
| << "AnswerChooser called fewer times than expected."; | |
| bool expect_empty_patch = true; | |
| for (size_t i = 0; i < input_sources.size(); ++i) { | |
| const absl::string_view input_source = input_sources[i]; | |
| const std::string& fixed_source = fixed_sources[i]; | |
| const absl::string_view expected_fixed_source = | |
| *(expected_fixed_sources.begin() + i); | |
| EXPECT_EQ(input_source, fixed_source); | |
| if (input_source != expected_fixed_source) { | |
| expect_empty_patch = false; | |
| } | |
| } | |
| EXPECT_EQ(patch.str().empty(), expect_empty_patch); | |
| } | |
| } | |
| }; | |
| TEST_F(ViolationFixerTest, ApplyAll) { | |
| DoFixerTest( | |
| { | |
| {ViolationFixer::AnswerChoice::kApplyAll, 0}, | |
| }, | |
| { | |
| "module Autofix;\n" | |
| " wire a;\n" | |
| " wire b;\n" | |
| " wire c;\n" | |
| " wire d;\n" | |
| "endmodule\n", | |
| "module AutofixTwo;\n" | |
| "endmodule\n", | |
| "module AutofixThree;\n" | |
| " wire a;\n" | |
| "endmodule\n", | |
| }); | |
| } | |
| TEST_F(ViolationFixerTest, RejectAll) { | |
| DoFixerTest( | |
| { | |
| {ViolationFixer::AnswerChoice::kRejectAll, 0}, | |
| }, | |
| { | |
| "module Autofix; \n" | |
| " wire a;;\n" | |
| " wire b;; \n" | |
| " wire c;;\n" | |
| " wire d;;\n" | |
| "endmodule ", | |
| "module AutofixTwo;\n" | |
| "endmodule\n", | |
| "module AutofixThree;;\n" | |
| " wire a; \n" | |
| "endmodule\n", | |
| }); | |
| } | |
| TEST_F(ViolationFixerTest, Reject) { | |
| DoFixerTest( | |
| { | |
| {ViolationFixer::AnswerChoice::kReject, 0}, | |
| {ViolationFixer::AnswerChoice::kApplyAll, 0}, | |
| }, | |
| { | |
| "module Autofix; \n" | |
| " wire a;\n" | |
| " wire b;\n" | |
| " wire c;\n" | |
| " wire d;\n" | |
| "endmodule\n", | |
| "module AutofixTwo;\n" | |
| "endmodule\n", | |
| "module AutofixThree;\n" | |
| " wire a;\n" | |
| "endmodule\n", | |
| }); | |
| } | |
| TEST_F(ViolationFixerTest, Apply) { | |
| DoFixerTest( | |
| { | |
| {ViolationFixer::AnswerChoice::kApply, 0}, | |
| {ViolationFixer::AnswerChoice::kRejectAll, 0}, | |
| }, | |
| { | |
| "module Autofix;\n" | |
| " wire a;;\n" | |
| " wire b;; \n" | |
| " wire c;;\n" | |
| " wire d;;\n" | |
| "endmodule ", | |
| "module AutofixTwo;\n" | |
| "endmodule\n", | |
| "module AutofixThree;;\n" | |
| " wire a; \n" | |
| "endmodule\n", | |
| }); | |
| } | |
| TEST_F(ViolationFixerTest, ApplyAllForRule) { | |
| DoFixerTest( | |
| { | |
| // Input source 0: | |
| // :2:10: no-trailing-spaces | |
| {ViolationFixer::AnswerChoice::kApplyAllForRule}, | |
| // :3:10: forbid-consecutive-null-statements | |
| {ViolationFixer::AnswerChoice::kReject}, | |
| // :4:10: forbid-consecutive-null-statements | |
| {ViolationFixer::AnswerChoice::kReject}, | |
| // :4:11: no-trailing-spaces | |
| // AUTOMATICALLY APPLIED due to kApplyAllForRule | |
| // :5:10: forbid-consecutive-null-statements | |
| {ViolationFixer::AnswerChoice::kReject}, | |
| // :6:10: forbid-consecutive-null-statements | |
| {ViolationFixer::AnswerChoice::kReject}, | |
| // :7:10: no-trailing-spaces | |
| // AUTOMATICALLY APPLIED due to kApplyAllForRule | |
| // :7:14: posix-eof | |
| {ViolationFixer::AnswerChoice::kReject}, | |
| // Input source 2: | |
| // :1:21: forbid-consecutive-null-statements | |
| {ViolationFixer::AnswerChoice::kReject}, | |
| // :2:10: no-trailing-spaces | |
| // AUTOMATICALLY APPLIED due to kApplyAllForRule | |
| }, | |
| { | |
| "module Autofix;\n" | |
| " wire a;;\n" | |
| " wire b;;\n" | |
| " wire c;;\n" | |
| " wire d;;\n" | |
| "endmodule", | |
| "module AutofixTwo;\n" | |
| "endmodule\n", | |
| "module AutofixThree;;\n" | |
| " wire a;\n" | |
| "endmodule\n", | |
| }); | |
| } | |
| TEST_F(ViolationFixerTest, RejectAllForRule) { | |
| DoFixerTest( | |
| { | |
| // Input source 0: | |
| // :2:10: no-trailing-spaces | |
| {ViolationFixer::AnswerChoice::kRejectAllForRule}, | |
| // :3:10: forbid-consecutive-null-statements | |
| {ViolationFixer::AnswerChoice::kApply}, | |
| // :4:10: forbid-consecutive-null-statements | |
| {ViolationFixer::AnswerChoice::kApply}, | |
| // :4:11: no-trailing-spaces | |
| // AUTOMATICALLY REJECTED due to kApplyAllForRule | |
| // :5:10: forbid-consecutive-null-statements | |
| {ViolationFixer::AnswerChoice::kApply}, | |
| // :6:10: forbid-consecutive-null-statements | |
| {ViolationFixer::AnswerChoice::kApply}, | |
| // :7:10: no-trailing-spaces | |
| // AUTOMATICALLY REJECTED due to kApplyAllForRule | |
| // :7:14: posix-eof | |
| {ViolationFixer::AnswerChoice::kApply}, | |
| // Input source 2: | |
| // :1:21: forbid-consecutive-null-statements | |
| {ViolationFixer::AnswerChoice::kApply}, | |
| // :2:10: no-trailing-spaces | |
| // AUTOMATICALLY REJECTED due to kApplyAllForRule | |
| }, | |
| { | |
| "module Autofix; \n" | |
| " wire a;\n" | |
| " wire b; \n" | |
| " wire c;\n" | |
| " wire d;\n" | |
| "endmodule \n", | |
| "module AutofixTwo;\n" | |
| "endmodule\n", | |
| "module AutofixThree;\n" | |
| " wire a; \n" | |
| "endmodule\n", | |
| }); | |
| } | |
| TEST_F(ViolationFixerTest, RejectAllForRuleApplyAllForRule) { | |
| DoFixerTest( | |
| { | |
| // Input source 0: | |
| // :2:10: no-trailing-spaces | |
| {ViolationFixer::AnswerChoice::kRejectAllForRule}, | |
| // :3:10: forbid-consecutive-null-statements | |
| {ViolationFixer::AnswerChoice::kApplyAllForRule}, | |
| // :4:10: forbid-consecutive-null-statements | |
| // AUTOMATICALLY APPLIED due to kApplyAllForRule | |
| // :4:11: no-trailing-spaces | |
| // AUTOMATICALLY REJECTED due to kApplyAllForRule | |
| // :5:10: forbid-consecutive-null-statements | |
| // AUTOMATICALLY APPLIED due to kApplyAllForRule | |
| // :6:10: forbid-consecutive-null-statements | |
| // AUTOMATICALLY APPLIED due to kApplyAllForRule | |
| // :7:10: no-trailing-spaces | |
| // AUTOMATICALLY REJECTED due to kApplyAllForRule | |
| // :7:14: posix-eof | |
| {ViolationFixer::AnswerChoice::kReject}, | |
| // Input source 2: | |
| // :1:21: forbid-consecutive-null-statements | |
| // AUTOMATICALLY APPLIED due to kApplyAllForRule | |
| // :2:10: no-trailing-spaces | |
| // AUTOMATICALLY REJECTED due to kApplyAllForRule | |
| }, | |
| { | |
| "module Autofix; \n" | |
| " wire a;\n" | |
| " wire b; \n" | |
| " wire c;\n" | |
| " wire d;\n" | |
| "endmodule ", | |
| "module AutofixTwo;\n" | |
| "endmodule\n", | |
| "module AutofixThree;\n" | |
| " wire a; \n" | |
| "endmodule\n", | |
| }); | |
| } | |
| TEST_F(ViolationFixerTest, PrintFix) { | |
| // Just checks that kPrintFix doesn't affect choices | |
| DoFixerTest( | |
| { | |
| // Input source 0: | |
| // :2:10: no-trailing-spaces | |
| {ViolationFixer::AnswerChoice::kApply}, | |
| {ViolationFixer::AnswerChoice::kPrintFix}, | |
| {ViolationFixer::AnswerChoice::kPrintFix}, | |
| // :3:10: forbid-consecutive-null-statements | |
| {ViolationFixer::AnswerChoice::kReject}, | |
| // :4:10: forbid-consecutive-null-statements | |
| {ViolationFixer::AnswerChoice::kReject}, | |
| {ViolationFixer::AnswerChoice::kPrintFix}, | |
| // :4:11: no-trailing-spaces | |
| {ViolationFixer::AnswerChoice::kApply}, | |
| {ViolationFixer::AnswerChoice::kPrintFix}, | |
| // :5:10: forbid-consecutive-null-statements | |
| {ViolationFixer::AnswerChoice::kApply}, | |
| // :6:10: forbid-consecutive-null-statements | |
| {ViolationFixer::AnswerChoice::kApply}, | |
| {ViolationFixer::AnswerChoice::kPrintFix}, | |
| {ViolationFixer::AnswerChoice::kPrintFix}, | |
| // :7:10: no-trailing-spaces | |
| {ViolationFixer::AnswerChoice::kApply}, | |
| // :7:14: posix-eof | |
| {ViolationFixer::AnswerChoice::kReject}, | |
| {ViolationFixer::AnswerChoice::kPrintFix}, | |
| // Input source 2: | |
| // :1:21: forbid-consecutive-null-statements | |
| {ViolationFixer::AnswerChoice::kApply}, | |
| {ViolationFixer::AnswerChoice::kPrintFix}, | |
| // :2:10: no-trailing-spaces | |
| {ViolationFixer::AnswerChoice::kReject}, | |
| }, | |
| { | |
| "module Autofix;\n" | |
| " wire a;;\n" | |
| " wire b;;\n" | |
| " wire c;\n" | |
| " wire d;\n" | |
| "endmodule", | |
| "module AutofixTwo;\n" | |
| "endmodule\n", | |
| "module AutofixThree;\n" | |
| " wire a; \n" | |
| "endmodule\n", | |
| }); | |
| } | |
| TEST_F(ViolationFixerTest, PrintAppliedFixes) { | |
| // Just checks that kPrintAppliedFixes doesn't affect choices | |
| DoFixerTest( | |
| { | |
| // Input source 0: | |
| // :2:10: no-trailing-spaces | |
| {ViolationFixer::AnswerChoice::kApply}, | |
| {ViolationFixer::AnswerChoice::kPrintAppliedFixes}, | |
| {ViolationFixer::AnswerChoice::kPrintAppliedFixes}, | |
| // :3:10: forbid-consecutive-null-statements | |
| {ViolationFixer::AnswerChoice::kReject}, | |
| // :4:10: forbid-consecutive-null-staments | |
| {ViolationFixer::AnswerChoice::kReject}, | |
| {ViolationFixer::AnswerChoice::kPrintAppliedFixes}, | |
| // :4:11: no-trailing-spaces | |
| {ViolationFixer::AnswerChoice::kApply}, | |
| {ViolationFixer::AnswerChoice::kPrintAppliedFixes}, | |
| // :5:10: forbid-consecutive-null-statements | |
| {ViolationFixer::AnswerChoice::kApply}, | |
| // :6:10: forbid-consecutive-null-statements | |
| {ViolationFixer::AnswerChoice::kApply}, | |
| {ViolationFixer::AnswerChoice::kPrintAppliedFixes}, | |
| {ViolationFixer::AnswerChoice::kPrintAppliedFixes}, | |
| // :7:10: no-trailing-spaces | |
| {ViolationFixer::AnswerChoice::kApply}, | |
| // :7:14: posix-eof | |
| {ViolationFixer::AnswerChoice::kReject}, | |
| {ViolationFixer::AnswerChoice::kPrintAppliedFixes}, | |
| // Input source 2: | |
| // :1:21: forbid-consecutive-null-statements | |
| {ViolationFixer::AnswerChoice::kApply}, | |
| {ViolationFixer::AnswerChoice::kPrintAppliedFixes}, | |
| // :2:10: no-trailing-spaces | |
| {ViolationFixer::AnswerChoice::kReject}, | |
| }, | |
| { | |
| "module Autofix;\n" | |
| " wire a;;\n" | |
| " wire b;;\n" | |
| " wire c;\n" | |
| " wire d;\n" | |
| "endmodule", | |
| "module AutofixTwo;\n" | |
| "endmodule\n", | |
| "module AutofixThree;\n" | |
| " wire a; \n" | |
| "endmodule\n", | |
| }); | |
| } | |
| } // namespace | |
| } // namespace verilog |