blob: f1f62033a826139b71f47851bc7d61a3ad5c71aa [file] [log] [blame]
// 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 <sstream>
#include <string>
#include <vector>
#include "absl/status/status.h"
#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "common/text/text_structure.h"
#include "common/util/file_util.h"
#include "common/util/logging.h"
#include "common/util/range.h"
#include "gtest/gtest.h"
#include "verilog/CST/module.h"
#include "verilog/analysis/verilog_analyzer.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:
explicit 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, PreparsedValidFile) {
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());
ParsedVerilogSourceFile file("internal", "resolved", *analyzed_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(&analyzed_structure->Data(), 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(ParsedVerilogSourceFileTest, PreparsedInvalidValidFile) {
constexpr absl::string_view text("localp_TYPO_aram int p = 1;\n");
std::unique_ptr<VerilogAnalyzer> analyzed_structure =
std::make_unique<VerilogAnalyzer>(text, "internal");
absl::Status status = analyzed_structure->Analyze();
EXPECT_FALSE(status.ok());
ParsedVerilogSourceFile file("internal", "resolved", *analyzed_structure);
EXPECT_TRUE(file.Open().ok()); // Already successfully implicitly opened.
EXPECT_FALSE(file.Parse().ok()); // We expect the same parse failure.
EXPECT_FALSE(file.Status().ok());
const TextStructureView *text_structure =
ABSL_DIE_IF_NULL(file.GetTextStructure());
EXPECT_EQ(&analyzed_structure->Data(), text_structure);
const absl::string_view owned_string_range(text_structure->Contents());
EXPECT_EQ(owned_string_range, text);
}
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, UpdateFileContents) {
// The update file content is used typically by the language server.
// By default, we load a file from the filesystem unless we get it from the
// editor, which then overrides it. If removed, we should fall back to file
// contents.
const auto tempdir = ::testing::TempDir();
const std::string project_root_dir = JoinPath(tempdir, "srcs");
EXPECT_TRUE(CreateDir(project_root_dir).ok());
// root is director with sources.
VerilogProject project(project_root_dir, {});
// Prepare file to be auto-loaded later
constexpr absl::string_view file_content("module foo();\nendmodule\n");
const ScopedTestFile tf(project_root_dir, file_content);
const absl::string_view reference_name = Basename(tf.filename());
VerilogSourceFile *from_file;
absl::string_view search_substring;
// Push a local analyzed name under the name of the file.
constexpr absl::string_view external_content("localparam int p = 1;\n");
std::unique_ptr<VerilogAnalyzer> analyzed_structure =
std::make_unique<VerilogAnalyzer>(external_content, "internal");
project.UpdateFileContents(tf.filename(), analyzed_structure.get());
// Look up the file and see that content is the external content
from_file = *project.OpenTranslationUnit(reference_name);
EXPECT_EQ(from_file->GetContent(), external_content);
// ... and we find our file given the substring.
search_substring = from_file->GetContent().substr(5);
EXPECT_EQ(project.LookupFileOrigin(search_substring), from_file);
// Updating with empty content, i.e. removing the memory virtual file
// force reading from file.
project.UpdateFileContents(tf.filename(), nullptr);
// Should be file content.
from_file = *project.OpenTranslationUnit(reference_name);
EXPECT_EQ(from_file->GetContent(), file_content);
// Looking up by the old substring should not find anything anymore.
EXPECT_EQ(project.LookupFileOrigin(search_substring), nullptr);
// But we find our file from our substring.
search_substring = from_file->GetContent().substr(5);
EXPECT_EQ(project.LookupFileOrigin(search_substring), from_file);
}
TEST(VerilogProjectTest, UpdateFileContentsEmptyFile) {
// Users can add empty files to the filelist and subsequently
// remove them.
const auto tempdir = ::testing::TempDir();
const std::string project_root_dir = JoinPath(tempdir, "srcs");
EXPECT_TRUE(CreateDir(project_root_dir).ok());
// root is directory with sources.
VerilogProject project(project_root_dir, {});
// Prepare file to be auto-loaded later
const ScopedTestFile tf(project_root_dir, "");
const absl::string_view reference_name = Basename(tf.filename());
// Push a local analyzed name under the name of the file.
constexpr absl::string_view external_content("localparam int p = 1;\n");
std::unique_ptr<VerilogAnalyzer> analyzed_structure =
std::make_unique<VerilogAnalyzer>(external_content, "internal");
project.UpdateFileContents(tf.filename(), analyzed_structure.get());
// Look up the file and see that content is the external content
VerilogSourceFile *from_file;
absl::string_view search_substring;
from_file = *project.OpenTranslationUnit(reference_name);
EXPECT_EQ(from_file->GetContent(), external_content);
// ... and we find our file given the substring.
search_substring = from_file->GetContent().substr(5);
EXPECT_EQ(project.LookupFileOrigin(search_substring), from_file);
// Prepare an empty file
constexpr absl::string_view empty_file_content("");
const ScopedTestFile empty_file(project_root_dir, empty_file_content);
const absl::string_view empty_file_reference =
Basename(empty_file.filename());
// Push the empty file into the project
std::unique_ptr<VerilogAnalyzer> analyzed_empty_structure =
std::make_unique<VerilogAnalyzer>(empty_file_content, "internal");
project.UpdateFileContents(empty_file.filename(),
analyzed_empty_structure.get());
// Check the content from the two files are present
from_file = *project.OpenTranslationUnit(reference_name);
EXPECT_EQ(from_file->GetContent(), external_content);
from_file = *project.OpenTranslationUnit(empty_file_reference);
EXPECT_EQ(from_file->GetContent(), empty_file_content);
// Remove the empty file
project.UpdateFileContents(empty_file.filename(), nullptr);
// Make sure the remaining file is still present
from_file = *project.OpenTranslationUnit(reference_name);
EXPECT_EQ(from_file->GetContent(), external_content);
// Make sure the empty file was removed
EXPECT_EQ(project.LookupFileOrigin(empty_file.filename()), 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