| Index: tools/gn/builder_unittest.cc
|
| diff --git a/tools/gn/builder_unittest.cc b/tools/gn/builder_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..dfcf49b050e703923483d12ab5253eed540beb9b
|
| --- /dev/null
|
| +++ b/tools/gn/builder_unittest.cc
|
| @@ -0,0 +1,226 @@
|
| +// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +#include "tools/gn/builder.h"
|
| +#include "tools/gn/loader.h"
|
| +#include "tools/gn/target.h"
|
| +#include "tools/gn/test_with_scope.h"
|
| +#include "tools/gn/toolchain.h"
|
| +
|
| +namespace {
|
| +
|
| +class MockLoader : public Loader {
|
| + public:
|
| + MockLoader() {
|
| + }
|
| +
|
| + // Loader implementation:
|
| + virtual void Load(const SourceFile& file,
|
| + const Label& toolchain_name) OVERRIDE {
|
| + files_.push_back(file);
|
| + }
|
| + virtual void ToolchainLoaded(const Toolchain* toolchain) OVERRIDE {
|
| + }
|
| + virtual Label GetDefaultToolchain() const OVERRIDE {
|
| + return Label();
|
| + }
|
| + virtual const Settings* GetToolchainSettings(const Label& label) OVERRIDE {
|
| + return NULL;
|
| + }
|
| +
|
| + bool HasLoadedNone() const {
|
| + return files_.empty();
|
| + }
|
| +
|
| + // Returns true if one load has been requested and it matches the given
|
| + // file. This will clear the records so it will be empty for the next call.
|
| + bool HasLoadedOne(const SourceFile& f) {
|
| + if (files_.size() != 1u) {
|
| + files_.clear();
|
| + return false;
|
| + }
|
| +
|
| + bool match = (files_[0] == f);
|
| + files_.clear();
|
| + return match;
|
| + }
|
| +
|
| + // Like HasLoadedOne above. Accepts any ordering.
|
| + bool HasLoadedTwo(const SourceFile& a, const SourceFile& b) {
|
| + if (files_.size() != 2u) {
|
| + files_.clear();
|
| + return false;
|
| + }
|
| +
|
| + bool match = (
|
| + (files_[0] == a && files_[1] == b) ||
|
| + (files_[0] == b && files_[0] == a));
|
| + files_.clear();
|
| + return match;
|
| + }
|
| +
|
| + private:
|
| + virtual ~MockLoader() {}
|
| +
|
| + std::vector<SourceFile> files_;
|
| +};
|
| +
|
| +class BuilderTest : public testing::Test {
|
| + public:
|
| + BuilderTest()
|
| + : loader_(new MockLoader),
|
| + builder_(new Builder(loader_.get())),
|
| + settings_(&build_settings_, std::string()),
|
| + scope_(&settings_) {
|
| + build_settings_.SetBuildDir(SourceDir("//out/"));
|
| + settings_.set_toolchain_label(Label(SourceDir("//tc/"), "default"));
|
| + settings_.set_default_toolchain_label(settings_.toolchain_label());
|
| + }
|
| +
|
| + Toolchain* DefineToolchain() {
|
| + Toolchain* tc = new Toolchain(&settings_, settings_.toolchain_label());
|
| + builder_->ItemDefined(scoped_ptr<Item>(tc));
|
| + return tc;
|
| + }
|
| +
|
| + protected:
|
| + scoped_refptr<MockLoader> loader_;
|
| + scoped_refptr<Builder> builder_;
|
| + BuildSettings build_settings_;
|
| + Settings settings_;
|
| + Scope scope_;
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +TEST_F(BuilderTest, BasicDeps) {
|
| + SourceDir toolchain_dir = settings_.toolchain_label().dir();
|
| + std::string toolchain_name = settings_.toolchain_label().name();
|
| +
|
| + DefineToolchain();
|
| + BuilderRecord* toolchain_record =
|
| + builder_->GetRecord(settings_.toolchain_label());
|
| + ASSERT_TRUE(toolchain_record);
|
| + EXPECT_EQ(BuilderRecord::ITEM_TOOLCHAIN, toolchain_record->type());
|
| +
|
| + // Construct a dependency chain: A -> B -> C. Define A first with a
|
| + // forward-reference to B, then C, then B to test the different orders that
|
| + // the dependencies are hooked up.
|
| + Label a_label(SourceDir("//a/"), "a", toolchain_dir, toolchain_name);
|
| + Label b_label(SourceDir("//b/"), "b", toolchain_dir, toolchain_name);
|
| + Label c_label(SourceDir("//c/"), "c", toolchain_dir, toolchain_name);
|
| +
|
| + // The builder will take ownership of the pointers.
|
| + Target* a = new Target(&settings_, a_label);
|
| + a->deps().push_back(LabelTargetPair(b_label));
|
| + a->set_output_type(Target::EXECUTABLE);
|
| + builder_->ItemDefined(scoped_ptr<Item>(a));
|
| +
|
| + // Should have requested that B and the toolchain is loaded.
|
| + EXPECT_TRUE(loader_->HasLoadedTwo(SourceFile("//tc/BUILD.gn"),
|
| + SourceFile("//b/BUILD.gn")));
|
| +
|
| + // A should be unresolved with an item
|
| + BuilderRecord* a_record = builder_->GetRecord(a_label);
|
| + EXPECT_TRUE(a_record->item());
|
| + EXPECT_FALSE(a_record->resolved());
|
| + EXPECT_FALSE(a_record->can_resolve());
|
| +
|
| + // B should be unresolved, have no item, and no deps.
|
| + BuilderRecord* b_record = builder_->GetRecord(b_label);
|
| + EXPECT_FALSE(b_record->item());
|
| + EXPECT_FALSE(b_record->resolved());
|
| + EXPECT_FALSE(b_record->can_resolve());
|
| + EXPECT_TRUE(b_record->all_deps().empty());
|
| +
|
| + // A should have two deps: B and the toolchain. Only B should be unresolved.
|
| + EXPECT_EQ(2u, a_record->all_deps().size());
|
| + EXPECT_EQ(1u, a_record->unresolved_deps().size());
|
| + EXPECT_NE(a_record->all_deps().end(),
|
| + a_record->all_deps().find(toolchain_record));
|
| + EXPECT_NE(a_record->all_deps().end(),
|
| + a_record->all_deps().find(b_record));
|
| + EXPECT_NE(a_record->unresolved_deps().end(),
|
| + a_record->unresolved_deps().find(b_record));
|
| +
|
| + // B should be marked as having A waiting on it.
|
| + EXPECT_EQ(1u, b_record->waiting_on_resolution().size());
|
| + EXPECT_NE(b_record->waiting_on_resolution().end(),
|
| + b_record->waiting_on_resolution().find(a_record));
|
| +
|
| + // Add the C target.
|
| + Target* c = new Target(&settings_, c_label);
|
| + c->set_output_type(Target::STATIC_LIBRARY);
|
| + builder_->ItemDefined(scoped_ptr<Item>(c));
|
| +
|
| + // C only depends on the already-loaded toolchain so we shouldn't have
|
| + // requested anything else.
|
| + EXPECT_TRUE(loader_->HasLoadedNone());
|
| +
|
| + // Add the B target.
|
| + Target* b = new Target(&settings_, b_label);
|
| + a->deps().push_back(LabelTargetPair(c_label));
|
| + b->set_output_type(Target::SHARED_LIBRARY);
|
| + builder_->ItemDefined(scoped_ptr<Item>(b));
|
| +
|
| + // B depends only on the already-loaded C and toolchain so we shouldn't have
|
| + // requested anything else.
|
| + EXPECT_TRUE(loader_->HasLoadedNone());
|
| +
|
| + // All targets should now be resolved.
|
| + BuilderRecord* c_record = builder_->GetRecord(c_label);
|
| + EXPECT_TRUE(a_record->resolved());
|
| + EXPECT_TRUE(b_record->resolved());
|
| + EXPECT_TRUE(c_record->resolved());
|
| +
|
| + EXPECT_TRUE(a_record->unresolved_deps().empty());
|
| + EXPECT_TRUE(b_record->unresolved_deps().empty());
|
| + EXPECT_TRUE(c_record->unresolved_deps().empty());
|
| +
|
| + EXPECT_TRUE(a_record->waiting_on_resolution().empty());
|
| + EXPECT_TRUE(b_record->waiting_on_resolution().empty());
|
| + EXPECT_TRUE(c_record->waiting_on_resolution().empty());
|
| +}
|
| +
|
| +// Tests that the should generate bit is set and propogated properly.
|
| +TEST_F(BuilderTest, ShouldGenerate) {
|
| + DefineToolchain();
|
| +
|
| + // Define a secondary toolchain.
|
| + Settings settings2(&build_settings_, "secondary");
|
| + Label toolchain_label2(SourceDir("//tc/"), "secondary");
|
| + settings2.set_toolchain_label(toolchain_label2);
|
| + Toolchain* tc2 = new Toolchain(&settings2, toolchain_label2);
|
| + builder_->ItemDefined(scoped_ptr<Item>(tc2));
|
| +
|
| + // Construct a dependency chain: A -> B. A is in the default toolchain, B
|
| + // is not.
|
| + Label a_label(SourceDir("//foo/"), "a",
|
| + settings_.toolchain_label().dir(), "a");
|
| + Label b_label(SourceDir("//foo/"), "b",
|
| + toolchain_label2.dir(), toolchain_label2.name());
|
| +
|
| + // First define B.
|
| + Target* b = new Target(&settings2, b_label);
|
| + b->set_output_type(Target::EXECUTABLE);
|
| + builder_->ItemDefined(scoped_ptr<Item>(b));
|
| +
|
| + // B should not be marked generated by default.
|
| + BuilderRecord* b_record = builder_->GetRecord(b_label);
|
| + EXPECT_FALSE(b_record->should_generate());
|
| +
|
| + // Define A with a dependency on B.
|
| + Target* a = new Target(&settings_, a_label);
|
| + a->deps().push_back(LabelTargetPair(b_label));
|
| + a->set_output_type(Target::EXECUTABLE);
|
| + builder_->ItemDefined(scoped_ptr<Item>(a));
|
| +
|
| + // A should have the generate bit set since it's in the default toolchain.
|
| + BuilderRecord* a_record = builder_->GetRecord(a_label);
|
| + EXPECT_TRUE(a_record->should_generate());
|
| +
|
| + // It should have gotten pushed to B.
|
| + EXPECT_TRUE(b_record->should_generate());
|
| +}
|
|
|