| // 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. | |
| #include "verilog/analysis/verilog_project.h" | |
| #include <memory> | |
| #include "absl/strings/match.h" | |
| #include "common/text/text_structure.h" | |
| #include "common/util/file_util.h" | |
| #include "common/util/logging.h" | |
| #include "common/util/range.h" | |
| #include "gmock/gmock.h" | |
| #include "gtest/gtest.h" | |
| #include "verilog/CST/module.h" | |
| namespace verilog { | |
| namespace { | |
| using verible::TextStructureView; | |
| using verible::file::Basename; | |
| using verible::file::CreateDir; | |
| using verible::file::JoinPath; | |
| using verible::file::testing::ScopedTestFile; | |
| class TempDirFile : public ScopedTestFile { | |
| public: | |
| TempDirFile(absl::string_view content) | |
| : ScopedTestFile(::testing::TempDir(), content) {} | |
| }; | |
| TEST(VerilogSourceFileTest, Initialization) { | |
| const VerilogSourceFile file("a.sv", "x/y/a.sv", ""); | |
| // no attempt to open this file yet | |
| EXPECT_EQ(file.ReferencedPath(), "a.sv"); | |
| EXPECT_EQ(file.ResolvedPath(), "x/y/a.sv"); | |
| EXPECT_TRUE(file.Status().ok()); | |
| EXPECT_EQ(file.GetTextStructure(), nullptr); | |
| } | |
| TEST(VerilogSourceFileTest, OpenExistingFile) { | |
| constexpr absl::string_view text("localparam int p = 1;\n"); | |
| TempDirFile tf(text); | |
| const absl::string_view basename(Basename(tf.filename())); | |
| VerilogSourceFile file(basename, tf.filename(), ""); | |
| EXPECT_TRUE(file.Open().ok()); | |
| EXPECT_TRUE(file.Status().ok()); | |
| EXPECT_EQ(file.ReferencedPath(), basename); | |
| EXPECT_EQ(file.ResolvedPath(), tf.filename()); | |
| const absl::string_view owned_string_range(file.GetContent()); | |
| EXPECT_EQ(owned_string_range, text); | |
| // Re-opening doesn't change anything | |
| EXPECT_TRUE(file.Open().ok()); | |
| EXPECT_TRUE(file.Status().ok()); | |
| EXPECT_TRUE(verible::BoundsEqual(file.GetContent(), owned_string_range)); | |
| } | |
| TEST(VerilogSourceFileTest, NonExistingFile) { | |
| VerilogSourceFile file("aa.sv", "/does/not/exist/aa.sv", ""); | |
| EXPECT_FALSE(file.Open().ok()); | |
| EXPECT_FALSE(file.Status().ok()); | |
| EXPECT_EQ(file.GetTextStructure(), nullptr); | |
| // Still not there. | |
| EXPECT_FALSE(file.Open().ok()); | |
| EXPECT_FALSE(file.Status().ok()); | |
| EXPECT_EQ(file.GetTextStructure(), nullptr); | |
| } | |
| TEST(VerilogSourceFileTest, ParseValidFile) { | |
| constexpr absl::string_view text("localparam int p = 1;\n"); | |
| TempDirFile tf(text); | |
| const absl::string_view basename(Basename(tf.filename())); | |
| VerilogSourceFile file(basename, tf.filename(), ""); | |
| // Parse automatically opens. | |
| EXPECT_TRUE(file.Parse().ok()); | |
| EXPECT_TRUE(file.Status().ok()); | |
| const TextStructureView* text_structure = | |
| ABSL_DIE_IF_NULL(file.GetTextStructure()); | |
| const absl::string_view owned_string_range(text_structure->Contents()); | |
| EXPECT_EQ(owned_string_range, text); | |
| const auto* tokens = &text_structure->TokenStream(); | |
| EXPECT_NE(tokens, nullptr); | |
| const auto* tree = &text_structure->SyntaxTree(); | |
| EXPECT_NE(tree, nullptr); | |
| // Re-parsing doesn't change anything | |
| EXPECT_TRUE(file.Parse().ok()); | |
| EXPECT_TRUE(file.Status().ok()); | |
| EXPECT_EQ(file.GetTextStructure(), text_structure); | |
| EXPECT_EQ(&text_structure->TokenStream(), tokens); | |
| EXPECT_EQ(&text_structure->SyntaxTree(), tree); | |
| } | |
| TEST(VerilogSourceFileTest, ParseInvalidFile) { | |
| constexpr absl::string_view text("localparam 1 = p;\n"); | |
| TempDirFile tf(text); | |
| const absl::string_view basename(Basename(tf.filename())); | |
| VerilogSourceFile file(basename, tf.filename(), ""); | |
| // Parse automatically opens. | |
| EXPECT_FALSE(file.Parse().ok()); | |
| EXPECT_FALSE(file.Status().ok()); | |
| const TextStructureView* text_structure = | |
| ABSL_DIE_IF_NULL(file.GetTextStructure()); | |
| const absl::string_view owned_string_range(text_structure->Contents()); | |
| EXPECT_EQ(owned_string_range, text); | |
| const auto* tokens = &text_structure->TokenStream(); | |
| EXPECT_NE(tokens, nullptr); | |
| const auto* tree = &text_structure->SyntaxTree(); | |
| EXPECT_NE(tree, nullptr); | |
| // but syntax tree may be empty, depends on error-recovery | |
| // Re-parsing doesn't change anything | |
| EXPECT_FALSE(file.Parse().ok()); | |
| EXPECT_FALSE(file.Status().ok()); | |
| EXPECT_EQ(file.GetTextStructure(), text_structure); | |
| EXPECT_EQ(&text_structure->TokenStream(), tokens); | |
| EXPECT_EQ(&text_structure->SyntaxTree(), tree); | |
| } | |
| TEST(VerilogSourceFileTest, StreamPrint) { | |
| constexpr absl::string_view text("localparam foo = bar;\n"); | |
| TempDirFile tf(text); | |
| const absl::string_view basename(Basename(tf.filename())); | |
| VerilogSourceFile file(basename, tf.filename(), ""); | |
| { | |
| std::ostringstream stream; | |
| stream << file; | |
| const std::string str(stream.str()); | |
| EXPECT_TRUE( | |
| absl::StrContains(str, absl::StrCat("referenced path: ", basename))); | |
| EXPECT_TRUE( | |
| absl::StrContains(str, absl::StrCat("resolved path: ", tf.filename()))); | |
| EXPECT_TRUE(absl::StrContains(str, "status: ok")); | |
| EXPECT_TRUE(absl::StrContains(str, "have text structure? no")); | |
| } | |
| { // After parsng, we have a text structure. | |
| EXPECT_TRUE(file.Parse().ok()); | |
| std::ostringstream stream; | |
| stream << file; | |
| const std::string str(stream.str()); | |
| EXPECT_TRUE(absl::StrContains(str, "have text structure? yes")); | |
| } | |
| } | |
| TEST(InMemoryVerilogSourceFileTest, ParseValidFile) { | |
| constexpr absl::string_view text("localparam int p = 1;\n"); | |
| InMemoryVerilogSourceFile file("/not/using/file/system.v", text); | |
| // Parse automatically opens. | |
| EXPECT_TRUE(file.Parse().ok()); | |
| EXPECT_TRUE(file.Status().ok()); | |
| const TextStructureView* text_structure = | |
| ABSL_DIE_IF_NULL(file.GetTextStructure()); | |
| const absl::string_view owned_string_range(text_structure->Contents()); | |
| EXPECT_EQ(owned_string_range, text); | |
| const auto* tokens = &text_structure->TokenStream(); | |
| EXPECT_NE(tokens, nullptr); | |
| const auto* tree = &text_structure->SyntaxTree(); | |
| EXPECT_NE(tree, nullptr); | |
| // Re-parsing doesn't change anything | |
| EXPECT_TRUE(file.Parse().ok()); | |
| EXPECT_TRUE(file.Status().ok()); | |
| EXPECT_EQ(file.GetTextStructure(), text_structure); | |
| EXPECT_EQ(&text_structure->TokenStream(), tokens); | |
| EXPECT_EQ(&text_structure->SyntaxTree(), tree); | |
| } | |
| TEST(InMemoryVerilogSourceFileTest, ParseInvalidFile) { | |
| constexpr absl::string_view text("class \"dismissed\"!\n"); | |
| InMemoryVerilogSourceFile file("/not/using/file/system.v", text); | |
| // Parse automatically opens. | |
| EXPECT_FALSE(file.Parse().ok()); | |
| EXPECT_FALSE(file.Status().ok()); | |
| const TextStructureView* text_structure = | |
| ABSL_DIE_IF_NULL(file.GetTextStructure()); | |
| const absl::string_view owned_string_range(text_structure->Contents()); | |
| EXPECT_EQ(owned_string_range, text); | |
| const auto* tokens = &text_structure->TokenStream(); | |
| EXPECT_NE(tokens, nullptr); | |
| const auto* tree = &text_structure->SyntaxTree(); | |
| EXPECT_NE(tree, nullptr); | |
| // but syntax tree may be empty, depends on error-recovery | |
| // Re-parsing doesn't change anything | |
| EXPECT_FALSE(file.Parse().ok()); | |
| EXPECT_FALSE(file.Status().ok()); | |
| EXPECT_EQ(file.GetTextStructure(), text_structure); | |
| EXPECT_EQ(&text_structure->TokenStream(), tokens); | |
| EXPECT_EQ(&text_structure->SyntaxTree(), tree); | |
| } | |
| TEST(ParsedVerilogSourceFileTest, ParseValidFile) { | |
| constexpr absl::string_view text("localparam int p = 1;\n"); | |
| std::unique_ptr<VerilogAnalyzer> analyzed_structure = | |
| std::make_unique<VerilogAnalyzer>(text, "internal"); | |
| absl::Status status = analyzed_structure->Analyze(); | |
| EXPECT_TRUE(status.ok()); | |
| const TextStructureView& input_text_structure = analyzed_structure->Data(); | |
| ParsedVerilogSourceFile file("internal", &input_text_structure); | |
| // Parse automatically opens. | |
| EXPECT_TRUE(file.Parse().ok()); | |
| EXPECT_TRUE(file.Status().ok()); | |
| const TextStructureView* text_structure = | |
| ABSL_DIE_IF_NULL(file.GetTextStructure()); | |
| EXPECT_EQ(&input_text_structure, text_structure); | |
| const absl::string_view owned_string_range(text_structure->Contents()); | |
| EXPECT_EQ(owned_string_range, text); | |
| const auto* tokens = &text_structure->TokenStream(); | |
| EXPECT_NE(tokens, nullptr); | |
| const auto* tree = &text_structure->SyntaxTree(); | |
| EXPECT_NE(tree, nullptr); | |
| // Re-parsing doesn't change anything | |
| EXPECT_TRUE(file.Parse().ok()); | |
| EXPECT_TRUE(file.Status().ok()); | |
| EXPECT_EQ(file.GetTextStructure(), text_structure); | |
| EXPECT_EQ(&text_structure->TokenStream(), tokens); | |
| EXPECT_EQ(&text_structure->SyntaxTree(), tree); | |
| } | |
| TEST(VerilogProjectTest, NonexistentTranslationUnit) { | |
| const auto tempdir = ::testing::TempDir(); | |
| VerilogProject project(tempdir, {tempdir}); | |
| const auto status_or_file = project.OpenTranslationUnit("never-there.v"); | |
| EXPECT_FALSE(status_or_file.ok()); | |
| } | |
| TEST(VerilogProjectTest, NonexistentIncludeFile) { | |
| const auto tempdir = ::testing::TempDir(); | |
| VerilogProject project(tempdir, {tempdir}); | |
| const auto status_or_file = project.OpenIncludedFile("nope.svh"); | |
| EXPECT_FALSE(status_or_file.ok()); | |
| } | |
| TEST(VerilogProjectTest, NonexistentFileLookup) { | |
| const auto tempdir = ::testing::TempDir(); | |
| VerilogProject project(tempdir, {tempdir}); | |
| { // non-const-lookup overload | |
| VerilogSourceFile* file = project.LookupRegisteredFile("never-there.v"); | |
| EXPECT_EQ(file, nullptr); | |
| } | |
| { | |
| // const-lookup overload | |
| const VerilogProject& cproject(project); | |
| const VerilogSourceFile* file = | |
| cproject.LookupRegisteredFile("never-there.v"); | |
| EXPECT_EQ(file, nullptr); | |
| } | |
| } | |
| TEST(VerilogProjectTest, LookupFileOriginTest) { | |
| const auto tempdir = ::testing::TempDir(); | |
| const std::string sources_dir = JoinPath(tempdir, "srcs"); | |
| EXPECT_TRUE(CreateDir(sources_dir).ok()); | |
| VerilogProject project(sources_dir, {}); | |
| // no files yet | |
| { | |
| constexpr absl::string_view foreign_text("not from any file"); | |
| EXPECT_EQ(project.LookupFileOrigin(foreign_text), nullptr); | |
| } | |
| // Add one file. Don't even need to parse it. | |
| const ScopedTestFile tf(sources_dir, "module m;\nendmodule\n"); | |
| const auto status_or_file = | |
| project.OpenTranslationUnit(Basename(tf.filename())); | |
| VerilogSourceFile* verilog_source_file = *status_or_file; | |
| const absl::string_view content1(verilog_source_file->GetContent()); | |
| { | |
| constexpr absl::string_view foreign_text("still not from any file"); | |
| EXPECT_EQ(project.LookupFileOrigin(foreign_text), nullptr); | |
| } | |
| // Pick a substring known to come from that file. | |
| EXPECT_EQ(project.LookupFileOrigin(content1.substr(2, 4)), | |
| verilog_source_file); | |
| // Add one more file. | |
| const ScopedTestFile tf2(sources_dir, "class c;\nendclass\n"); | |
| const auto status_or_file2 = | |
| project.OpenTranslationUnit(Basename(tf2.filename())); | |
| VerilogSourceFile* verilog_source_file2 = *status_or_file2; | |
| const absl::string_view content2(verilog_source_file2->GetContent()); | |
| // Pick substrings known to come from those files. | |
| EXPECT_EQ(project.LookupFileOrigin(content1.substr(5, 5)), | |
| verilog_source_file); | |
| EXPECT_EQ(project.LookupFileOrigin(content2.substr(9, 4)), | |
| verilog_source_file2); | |
| } | |
| TEST(VerilogProjectTest, LookupFileOriginTestMoreFiles) { | |
| const auto tempdir = ::testing::TempDir(); | |
| const std::string sources_dir = | |
| JoinPath(tempdir, "LookupFileOriginTestMoreFiles"); | |
| EXPECT_TRUE(CreateDir(sources_dir).ok()); | |
| VerilogProject project(sources_dir, {}); | |
| // no files yet | |
| constexpr absl::string_view foreign_text("not from any file"); | |
| // WArning: Test size is quadratic in N, but linear in memory in N. | |
| constexpr int N = 50; | |
| std::vector<std::unique_ptr<ScopedTestFile>> test_files; | |
| std::vector<const VerilogSourceFile*> sources; | |
| for (int i = 0; i < N; ++i) { | |
| // Write files, need not be parse-able. | |
| test_files.emplace_back(std::make_unique<ScopedTestFile>( | |
| sources_dir, "sa89*(98<Na! 89 89891231!@#ajk jasoij(*&^ asaissd0afd ")); | |
| const auto status_or_file = | |
| project.OpenTranslationUnit(Basename(test_files.back()->filename())); | |
| const VerilogSourceFile* source_file = *status_or_file; | |
| ASSERT_NE(source_file, nullptr); | |
| sources.push_back(source_file); | |
| for (const auto& source : sources) { | |
| // Pick substrings known to come from those files. | |
| EXPECT_EQ(project.LookupFileOrigin(source->GetContent().substr(15, 12)), | |
| source); | |
| } | |
| EXPECT_EQ(project.LookupFileOrigin(foreign_text), nullptr); | |
| } | |
| } | |
| TEST(VerilogProjectTest, ValidTranslationUnit) { | |
| const auto tempdir = ::testing::TempDir(); | |
| const std::string sources_dir = JoinPath(tempdir, "srcs"); | |
| const std::string includes_dir = JoinPath(tempdir, "includes"); | |
| EXPECT_TRUE(CreateDir(sources_dir).ok()); | |
| EXPECT_TRUE(CreateDir(includes_dir).ok()); | |
| VerilogProject project(sources_dir, {includes_dir}); | |
| constexpr absl::string_view text("module m;\nendmodule\n"); | |
| const ScopedTestFile tf(sources_dir, text); | |
| const auto status_or_file = | |
| project.OpenTranslationUnit(Basename(tf.filename())); | |
| VerilogSourceFile* verilog_source_file = *status_or_file; | |
| EXPECT_TRUE(verilog_source_file->Status().ok()); | |
| EXPECT_EQ(verilog_source_file->ReferencedPath(), Basename(tf.filename())); | |
| EXPECT_EQ(verilog_source_file->ResolvedPath(), tf.filename()); | |
| EXPECT_EQ(project.LookupRegisteredFile(Basename(tf.filename())), | |
| verilog_source_file); | |
| const absl::string_view content(verilog_source_file->GetContent()); | |
| { // const-lookup overload | |
| const VerilogProject& cproject(project); | |
| EXPECT_EQ(cproject.LookupRegisteredFile(Basename(tf.filename())), | |
| verilog_source_file); | |
| EXPECT_EQ(cproject.LookupFileOrigin(content.substr(2, 4)), | |
| verilog_source_file); | |
| } | |
| EXPECT_TRUE(verilog_source_file->Parse().ok()); | |
| const TextStructureView& text_structure( | |
| *verilog_source_file->GetTextStructure()); | |
| const auto* tree = ABSL_DIE_IF_NULL(text_structure.SyntaxTree().get()); | |
| EXPECT_EQ(FindAllModuleDeclarations(*tree).size(), 1); | |
| { | |
| // Re-parsing the file changes nothing. | |
| EXPECT_TRUE(verilog_source_file->Parse().ok()); | |
| const auto* tree2 = ABSL_DIE_IF_NULL(text_structure.SyntaxTree().get()); | |
| EXPECT_EQ(tree2, tree); | |
| EXPECT_EQ(FindAllModuleDeclarations(*tree).size(), 1); | |
| } | |
| { // Re-opening the file changes nothing. | |
| const auto status_or_file2 = | |
| project.OpenTranslationUnit(Basename(tf.filename())); | |
| VerilogSourceFile* verilog_source_file2 = *status_or_file2; | |
| EXPECT_EQ(verilog_source_file2, verilog_source_file); | |
| EXPECT_TRUE(verilog_source_file2->Status().ok()); | |
| } | |
| // Testing begin/end iteration. | |
| for (auto& file : project) { | |
| EXPECT_TRUE(file.second->Parse().ok()); | |
| } | |
| for (const auto& file : project) { | |
| EXPECT_TRUE(file.second->Status().ok()); | |
| } | |
| } | |
| TEST(VerilogProjectTest, ValidIncludeFile) { | |
| const auto tempdir = ::testing::TempDir(); | |
| const std::string sources_dir = JoinPath(tempdir, "srcs"); | |
| const std::string includes_dir = JoinPath(tempdir, "includes"); | |
| EXPECT_TRUE(CreateDir(sources_dir).ok()); | |
| EXPECT_TRUE(CreateDir(includes_dir).ok()); | |
| VerilogProject project(sources_dir, {includes_dir}); | |
| constexpr absl::string_view text("`define FOO 1\n"); | |
| const ScopedTestFile tf(includes_dir, text); | |
| const absl::string_view basename(Basename(tf.filename())); | |
| const auto status_or_file = project.OpenIncludedFile(basename); | |
| VerilogSourceFile* verilog_source_file = *status_or_file; | |
| EXPECT_TRUE(verilog_source_file->Status().ok()); | |
| EXPECT_EQ(verilog_source_file->ReferencedPath(), basename); | |
| EXPECT_EQ(verilog_source_file->ResolvedPath(), tf.filename()); | |
| EXPECT_EQ(project.LookupRegisteredFile(Basename(tf.filename())), | |
| verilog_source_file); | |
| { // const-lookup overload | |
| const VerilogProject& cproject(project); | |
| EXPECT_EQ(cproject.LookupRegisteredFile(Basename(tf.filename())), | |
| verilog_source_file); | |
| } | |
| // Re-opening same file, changes nothing | |
| { | |
| const auto status_or_file2 = project.OpenIncludedFile(basename); | |
| VerilogSourceFile* verilog_source_file2 = *status_or_file2; | |
| EXPECT_EQ(verilog_source_file2, verilog_source_file); | |
| EXPECT_TRUE(verilog_source_file2->Status().ok()); | |
| } | |
| // includes aren't required to be parse-able, so just open | |
| EXPECT_TRUE(verilog_source_file->Open().ok()); | |
| EXPECT_FALSE(verilog_source_file->GetContent().empty()); | |
| // re-opening the file changes nothing | |
| EXPECT_TRUE(verilog_source_file->Open().ok()); | |
| EXPECT_FALSE(verilog_source_file->GetContent().empty()); | |
| } | |
| TEST(VerilogProjectTest, OpenVirtualIncludeFile) { | |
| const auto tempdir = ::testing::TempDir(); | |
| const std::string sources_dir = JoinPath(tempdir, "srcs"); | |
| const std::string includes_dir = JoinPath(tempdir, "includes"); | |
| EXPECT_TRUE(CreateDir(sources_dir).ok()); | |
| EXPECT_TRUE(CreateDir(includes_dir).ok()); | |
| VerilogProject project(sources_dir, {includes_dir}); | |
| constexpr absl::string_view text("`define FOO 1\n"); | |
| const std::string basename = "virtual_include_file1"; | |
| const std::string full_path = JoinPath(includes_dir, basename); | |
| // The virtual file is added by its full path. But the include is opened by | |
| // the basename. | |
| project.AddVirtualFile(full_path, text); | |
| const auto status_or_file = project.OpenIncludedFile(basename); | |
| VerilogSourceFile* verilog_source_file = *status_or_file; | |
| EXPECT_TRUE(verilog_source_file->Status().ok()); | |
| EXPECT_EQ(verilog_source_file->ReferencedPath(), full_path); | |
| EXPECT_EQ(verilog_source_file->ResolvedPath(), full_path); | |
| EXPECT_EQ(project.LookupRegisteredFile(basename), verilog_source_file); | |
| { // const-lookup overload | |
| const VerilogProject& cproject(project); | |
| EXPECT_EQ(cproject.LookupRegisteredFile(basename), verilog_source_file); | |
| } | |
| // Re-opening same file, changes nothing | |
| { | |
| const auto status_or_file2 = project.OpenIncludedFile(basename); | |
| VerilogSourceFile* verilog_source_file2 = *status_or_file2; | |
| EXPECT_EQ(verilog_source_file2, verilog_source_file); | |
| EXPECT_TRUE(verilog_source_file2->Status().ok()); | |
| } | |
| // includes aren't required to be parse-able, so just open | |
| EXPECT_TRUE(verilog_source_file->Open().ok()); | |
| EXPECT_FALSE(verilog_source_file->GetContent().empty()); | |
| // re-opening the file changes nothing | |
| EXPECT_TRUE(verilog_source_file->Open().ok()); | |
| EXPECT_FALSE(verilog_source_file->GetContent().empty()); | |
| } | |
| TEST(VerilogProjectTest, TranslationUnitNotFound) { | |
| const auto tempdir = ::testing::TempDir(); | |
| const std::string sources_dir = JoinPath(tempdir, "srcs"); | |
| const std::string includes_dir = JoinPath(tempdir, "includes"); | |
| EXPECT_TRUE(CreateDir(sources_dir).ok()); | |
| EXPECT_TRUE(CreateDir(includes_dir).ok()); | |
| VerilogProject project(sources_dir, {includes_dir}); | |
| constexpr absl::string_view text("module m;\nendmodule\n"); | |
| // deliberately plant this file in the includes dir != sources dir | |
| const ScopedTestFile tf(includes_dir, text); | |
| { | |
| const auto status_or_file = | |
| project.OpenTranslationUnit(Basename(tf.filename())); | |
| EXPECT_FALSE(status_or_file.ok()); | |
| } | |
| { // try again, still fail | |
| const auto status_or_file = | |
| project.OpenTranslationUnit(Basename(tf.filename())); | |
| EXPECT_FALSE(status_or_file.ok()); | |
| } | |
| } | |
| TEST(VerilogProjectTest, IncludeFileNotFound) { | |
| const auto tempdir = ::testing::TempDir(); | |
| const std::string sources_dir = JoinPath(tempdir, "srcs"); | |
| const std::string includes_dir = JoinPath(tempdir, "includes"); | |
| EXPECT_TRUE(CreateDir(sources_dir).ok()); | |
| EXPECT_TRUE(CreateDir(includes_dir).ok()); | |
| VerilogProject project(sources_dir, {includes_dir}); | |
| constexpr absl::string_view text("module m;\nendmodule\n"); | |
| // deliberately plant this file in the sources dir != include dir | |
| const ScopedTestFile tf(sources_dir, text); | |
| { | |
| const auto status_or_file = | |
| project.OpenIncludedFile(Basename(tf.filename())); | |
| EXPECT_FALSE(status_or_file.ok()); | |
| } | |
| { // try again, still fail | |
| const auto status_or_file = | |
| project.OpenIncludedFile(Basename(tf.filename())); | |
| EXPECT_FALSE(status_or_file.ok()); | |
| } | |
| } | |
| TEST(VerilogProjectTest, AddVirtualFile) { | |
| const auto tempdir = ::testing::TempDir(); | |
| const std::string sources_dir = JoinPath(tempdir, "srcs"); | |
| const std::string includes_dir = JoinPath(tempdir, "includes"); | |
| EXPECT_TRUE(CreateDir(sources_dir).ok()); | |
| EXPECT_TRUE(CreateDir(includes_dir).ok()); | |
| VerilogProject project(sources_dir, {includes_dir}); | |
| const std::string file_path = "/some/file"; | |
| const std::string file_content = "virtual file content"; | |
| project.AddVirtualFile(file_path, file_content); | |
| auto* stored_file = project.LookupRegisteredFile(file_path); | |
| ASSERT_NE(stored_file, nullptr); | |
| EXPECT_TRUE(stored_file->Open().ok()); | |
| EXPECT_TRUE(stored_file->Status().ok()); | |
| EXPECT_EQ(stored_file->GetContent(), file_content); | |
| } | |
| } // namespace | |
| } // namespace verilog |