Make it possible to add observers to an Input Graph, so that files returned from an Input Graph can be examined before they are passed to Resolver. To implement some PE/COFF features we need to know all the symbols that *can* be solved, including ones in archive files that are not yet to be read. Currently, Resolver only maintains a set of symbols that are already read. It has no knowledge on symbols in skipped files in an archive file. There are many ways to implement that. I chose to apply the observer pattern here because it seems most non-intrusive. We don't want to mess up Resolver with architecture specific features. Even in PE/COFF, the feature that needs this mechanism is minor. So I chose not to modify Resolver, but add a hook to Input Graph. Differential Revision: http://reviews.llvm.org/D3735 llvm-svn: 208753
209 lines
5.8 KiB
C++
209 lines
5.8 KiB
C++
//===- lld/unittest/InputGraphTest.cpp -----------------------------------===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
///
|
|
/// \file
|
|
/// \brief InputGraph Tests
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "gtest/gtest.h"
|
|
|
|
#include "lld/Core/InputGraph.h"
|
|
#include "lld/Core/Resolver.h"
|
|
#include "lld/ReaderWriter/Simple.h"
|
|
|
|
using namespace lld;
|
|
|
|
namespace {
|
|
|
|
class TestLinkingContext : public LinkingContext {
|
|
public:
|
|
Writer &writer() const override { llvm_unreachable("no writer!"); }
|
|
bool validateImpl(raw_ostream &) override { return true; }
|
|
};
|
|
|
|
class TestFileNode : public SimpleFileNode {
|
|
public:
|
|
TestFileNode(StringRef path) : SimpleFileNode(path) {}
|
|
void resetNextIndex() override { FileNode::resetNextIndex(); }
|
|
};
|
|
|
|
class TestExpandFileNode : public SimpleFileNode {
|
|
public:
|
|
TestExpandFileNode(StringRef path) : SimpleFileNode(path) {}
|
|
|
|
/// Returns the elements replacing this node
|
|
bool getReplacements(InputGraph::InputElementVectorT &result) override {
|
|
for (std::unique_ptr<InputElement> &elt : _expandElements)
|
|
result.push_back(std::move(elt));
|
|
return true;
|
|
}
|
|
|
|
void addElement(std::unique_ptr<InputElement> element) {
|
|
_expandElements.push_back(std::move(element));
|
|
}
|
|
|
|
private:
|
|
InputGraph::InputElementVectorT _expandElements;
|
|
};
|
|
|
|
class InputGraphTest : public testing::Test {
|
|
public:
|
|
InputGraphTest() {
|
|
_ctx.setInputGraph(std::unique_ptr<InputGraph>(new InputGraph()));
|
|
_graph = &_ctx.getInputGraph();
|
|
}
|
|
|
|
StringRef getNext() {
|
|
ErrorOr<File &> file = _graph->getNextFile();
|
|
EXPECT_TRUE(!file.getError());
|
|
return file.get().path();
|
|
}
|
|
|
|
void expectEnd() {
|
|
ErrorOr<File &> file = _graph->getNextFile();
|
|
EXPECT_EQ(InputGraphError::no_more_files, file.getError());
|
|
}
|
|
|
|
protected:
|
|
TestLinkingContext _ctx;
|
|
InputGraph *_graph;
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
static std::unique_ptr<TestFileNode> createFile1(StringRef name) {
|
|
std::vector<std::unique_ptr<File>> files;
|
|
files.push_back(std::unique_ptr<SimpleFile>(new SimpleFile(name)));
|
|
std::unique_ptr<TestFileNode> file(new TestFileNode("filenode"));
|
|
file->addFiles(std::move(files));
|
|
return file;
|
|
}
|
|
|
|
static std::unique_ptr<TestFileNode> createFile2(StringRef name1,
|
|
StringRef name2) {
|
|
std::vector<std::unique_ptr<File>> files;
|
|
files.push_back(std::unique_ptr<SimpleFile>(new SimpleFile(name1)));
|
|
files.push_back(std::unique_ptr<SimpleFile>(new SimpleFile(name2)));
|
|
std::unique_ptr<TestFileNode> file(new TestFileNode("filenode"));
|
|
file->addFiles(std::move(files));
|
|
return file;
|
|
}
|
|
|
|
TEST_F(InputGraphTest, Empty) {
|
|
expectEnd();
|
|
}
|
|
|
|
TEST_F(InputGraphTest, File) {
|
|
_graph->addInputElement(createFile1("file1"));
|
|
EXPECT_EQ("file1", getNext());
|
|
expectEnd();
|
|
}
|
|
|
|
TEST_F(InputGraphTest, Files) {
|
|
_graph->addInputElement(createFile2("file1", "file2"));
|
|
EXPECT_EQ("file1", getNext());
|
|
EXPECT_EQ("file2", getNext());
|
|
expectEnd();
|
|
}
|
|
|
|
TEST_F(InputGraphTest, Group) {
|
|
_graph->addInputElement(createFile2("file1", "file2"));
|
|
|
|
std::unique_ptr<Group> group(new Group());
|
|
group->addFile(createFile2("file3", "file4"));
|
|
group->addFile(createFile1("file5"));
|
|
group->addFile(createFile1("file6"));
|
|
_graph->addInputElement(std::move(group));
|
|
|
|
EXPECT_EQ("file1", getNext());
|
|
EXPECT_EQ("file2", getNext());
|
|
EXPECT_EQ("file3", getNext());
|
|
EXPECT_EQ("file4", getNext());
|
|
EXPECT_EQ("file5", getNext());
|
|
EXPECT_EQ("file6", getNext());
|
|
expectEnd();
|
|
}
|
|
|
|
// Iterate through the group
|
|
TEST_F(InputGraphTest, GroupIteration) {
|
|
_graph->addInputElement(createFile2("file1", "file2"));
|
|
|
|
std::unique_ptr<Group> group(new Group());
|
|
group->addFile(createFile2("file3", "file4"));
|
|
group->addFile(createFile1("file5"));
|
|
group->addFile(createFile1("file6"));
|
|
_graph->addInputElement(std::move(group));
|
|
|
|
EXPECT_EQ("file1", getNext());
|
|
EXPECT_EQ("file2", getNext());
|
|
|
|
EXPECT_EQ("file3", getNext());
|
|
EXPECT_EQ("file4", getNext());
|
|
EXPECT_EQ("file5", getNext());
|
|
EXPECT_EQ("file6", getNext());
|
|
_graph->notifyProgress();
|
|
|
|
EXPECT_EQ("file3", getNext());
|
|
EXPECT_EQ("file4", getNext());
|
|
_graph->notifyProgress();
|
|
EXPECT_EQ("file5", getNext());
|
|
EXPECT_EQ("file6", getNext());
|
|
|
|
EXPECT_EQ("file3", getNext());
|
|
EXPECT_EQ("file4", getNext());
|
|
EXPECT_EQ("file5", getNext());
|
|
EXPECT_EQ("file6", getNext());
|
|
expectEnd();
|
|
}
|
|
|
|
// Node expansion tests
|
|
TEST_F(InputGraphTest, Normalize) {
|
|
_graph->addInputElement(createFile2("file1", "file2"));
|
|
|
|
std::unique_ptr<TestExpandFileNode> expandFile(
|
|
new TestExpandFileNode("node"));
|
|
expandFile->addElement(createFile1("file3"));
|
|
expandFile->addElement(createFile1("file4"));
|
|
_graph->addInputElement(std::move(expandFile));
|
|
|
|
std::unique_ptr<Group> group(new Group());
|
|
std::unique_ptr<TestExpandFileNode> expandFile2(
|
|
new TestExpandFileNode("node"));
|
|
expandFile2->addElement(createFile1("file5"));
|
|
group->addFile(std::move(expandFile2));
|
|
_graph->addInputElement(std::move(group));
|
|
|
|
_graph->addInputElement(createFile1("file6"));
|
|
_graph->normalize();
|
|
|
|
EXPECT_EQ("file1", getNext());
|
|
EXPECT_EQ("file2", getNext());
|
|
EXPECT_EQ("file3", getNext());
|
|
EXPECT_EQ("file4", getNext());
|
|
EXPECT_EQ("file5", getNext());
|
|
EXPECT_EQ("file6", getNext());
|
|
expectEnd();
|
|
}
|
|
|
|
TEST_F(InputGraphTest, Observer) {
|
|
std::vector<std::string> files;
|
|
_graph->registerObserver([&](File *file) { files.push_back(file->path()); });
|
|
|
|
_graph->addInputElement(createFile1("file1"));
|
|
_graph->addInputElement(createFile1("file2"));
|
|
EXPECT_EQ("file1", getNext());
|
|
EXPECT_EQ("file2", getNext());
|
|
expectEnd();
|
|
|
|
EXPECT_EQ(2U, files.size());
|
|
EXPECT_EQ("file1", files[0]);
|
|
EXPECT_EQ("file2", files[1]);
|
|
}
|