Home | History | Annotate | Download | only in gn
      1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "testing/gtest/include/gtest/gtest.h"
      6 #include "tools/gn/builder.h"
      7 #include "tools/gn/loader.h"
      8 #include "tools/gn/target.h"
      9 #include "tools/gn/test_with_scope.h"
     10 #include "tools/gn/toolchain.h"
     11 
     12 namespace {
     13 
     14 class MockLoader : public Loader {
     15  public:
     16   MockLoader() {
     17   }
     18 
     19   // Loader implementation:
     20   virtual void Load(const SourceFile& file,
     21                     const Label& toolchain_name) OVERRIDE {
     22     files_.push_back(file);
     23   }
     24   virtual void ToolchainLoaded(const Toolchain* toolchain) OVERRIDE {
     25   }
     26   virtual Label GetDefaultToolchain() const OVERRIDE {
     27     return Label();
     28   }
     29   virtual const Settings* GetToolchainSettings(const Label& label) OVERRIDE {
     30     return NULL;
     31   }
     32 
     33   bool HasLoadedNone() const {
     34     return files_.empty();
     35   }
     36 
     37   // Returns true if one load has been requested and it matches the given
     38   // file. This will clear the records so it will be empty for the next call.
     39   bool HasLoadedOne(const SourceFile& f) {
     40     if (files_.size() != 1u) {
     41       files_.clear();
     42       return false;
     43     }
     44 
     45     bool match = (files_[0] == f);
     46     files_.clear();
     47     return match;
     48   }
     49 
     50   // Like HasLoadedOne above. Accepts any ordering.
     51   bool HasLoadedTwo(const SourceFile& a, const SourceFile& b) {
     52     if (files_.size() != 2u) {
     53       files_.clear();
     54       return false;
     55     }
     56 
     57     bool match = (
     58         (files_[0] == a && files_[1] == b) ||
     59         (files_[0] == b && files_[0] == a));
     60     files_.clear();
     61     return match;
     62   }
     63 
     64  private:
     65   virtual ~MockLoader() {}
     66 
     67   std::vector<SourceFile> files_;
     68 };
     69 
     70 class BuilderTest : public testing::Test {
     71  public:
     72   BuilderTest()
     73       : loader_(new MockLoader),
     74         builder_(new Builder(loader_.get())),
     75         settings_(&build_settings_, std::string()),
     76         scope_(&settings_) {
     77     build_settings_.SetBuildDir(SourceDir("//out/"));
     78     settings_.set_toolchain_label(Label(SourceDir("//tc/"), "default"));
     79     settings_.set_default_toolchain_label(settings_.toolchain_label());
     80   }
     81 
     82   Toolchain* DefineToolchain() {
     83     Toolchain* tc = new Toolchain(&settings_, settings_.toolchain_label());
     84     builder_->ItemDefined(scoped_ptr<Item>(tc));
     85     return tc;
     86   }
     87 
     88  protected:
     89   scoped_refptr<MockLoader> loader_;
     90   scoped_refptr<Builder> builder_;
     91   BuildSettings build_settings_;
     92   Settings settings_;
     93   Scope scope_;
     94 };
     95 
     96 }  // namespace
     97 
     98 TEST_F(BuilderTest, BasicDeps) {
     99   SourceDir toolchain_dir = settings_.toolchain_label().dir();
    100   std::string toolchain_name = settings_.toolchain_label().name();
    101 
    102   DefineToolchain();
    103   BuilderRecord* toolchain_record =
    104       builder_->GetRecord(settings_.toolchain_label());
    105   ASSERT_TRUE(toolchain_record);
    106   EXPECT_EQ(BuilderRecord::ITEM_TOOLCHAIN, toolchain_record->type());
    107 
    108   // Construct a dependency chain: A -> B -> C. Define A first with a
    109   // forward-reference to B, then C, then B to test the different orders that
    110   // the dependencies are hooked up.
    111   Label a_label(SourceDir("//a/"), "a", toolchain_dir, toolchain_name);
    112   Label b_label(SourceDir("//b/"), "b", toolchain_dir, toolchain_name);
    113   Label c_label(SourceDir("//c/"), "c", toolchain_dir, toolchain_name);
    114 
    115   // The builder will take ownership of the pointers.
    116   Target* a = new Target(&settings_, a_label);
    117   a->deps().push_back(LabelTargetPair(b_label));
    118   a->set_output_type(Target::EXECUTABLE);
    119   builder_->ItemDefined(scoped_ptr<Item>(a));
    120 
    121   // Should have requested that B and the toolchain is loaded.
    122   EXPECT_TRUE(loader_->HasLoadedTwo(SourceFile("//tc/BUILD.gn"),
    123                                     SourceFile("//b/BUILD.gn")));
    124 
    125   // A should be unresolved with an item
    126   BuilderRecord* a_record = builder_->GetRecord(a_label);
    127   EXPECT_TRUE(a_record->item());
    128   EXPECT_FALSE(a_record->resolved());
    129   EXPECT_FALSE(a_record->can_resolve());
    130 
    131   // B should be unresolved, have no item, and no deps.
    132   BuilderRecord* b_record = builder_->GetRecord(b_label);
    133   EXPECT_FALSE(b_record->item());
    134   EXPECT_FALSE(b_record->resolved());
    135   EXPECT_FALSE(b_record->can_resolve());
    136   EXPECT_TRUE(b_record->all_deps().empty());
    137 
    138   // A should have two deps: B and the toolchain. Only B should be unresolved.
    139   EXPECT_EQ(2u, a_record->all_deps().size());
    140   EXPECT_EQ(1u, a_record->unresolved_deps().size());
    141   EXPECT_NE(a_record->all_deps().end(),
    142             a_record->all_deps().find(toolchain_record));
    143   EXPECT_NE(a_record->all_deps().end(),
    144             a_record->all_deps().find(b_record));
    145   EXPECT_NE(a_record->unresolved_deps().end(),
    146             a_record->unresolved_deps().find(b_record));
    147 
    148   // B should be marked as having A waiting on it.
    149   EXPECT_EQ(1u, b_record->waiting_on_resolution().size());
    150   EXPECT_NE(b_record->waiting_on_resolution().end(),
    151             b_record->waiting_on_resolution().find(a_record));
    152 
    153   // Add the C target.
    154   Target* c = new Target(&settings_, c_label);
    155   c->set_output_type(Target::STATIC_LIBRARY);
    156   builder_->ItemDefined(scoped_ptr<Item>(c));
    157 
    158   // C only depends on the already-loaded toolchain so we shouldn't have
    159   // requested anything else.
    160   EXPECT_TRUE(loader_->HasLoadedNone());
    161 
    162   // Add the B target.
    163   Target* b = new Target(&settings_, b_label);
    164   a->deps().push_back(LabelTargetPair(c_label));
    165   b->set_output_type(Target::SHARED_LIBRARY);
    166   builder_->ItemDefined(scoped_ptr<Item>(b));
    167 
    168   // B depends only on the already-loaded C and toolchain so we shouldn't have
    169   // requested anything else.
    170   EXPECT_TRUE(loader_->HasLoadedNone());
    171 
    172   // All targets should now be resolved.
    173   BuilderRecord* c_record = builder_->GetRecord(c_label);
    174   EXPECT_TRUE(a_record->resolved());
    175   EXPECT_TRUE(b_record->resolved());
    176   EXPECT_TRUE(c_record->resolved());
    177 
    178   EXPECT_TRUE(a_record->unresolved_deps().empty());
    179   EXPECT_TRUE(b_record->unresolved_deps().empty());
    180   EXPECT_TRUE(c_record->unresolved_deps().empty());
    181 
    182   EXPECT_TRUE(a_record->waiting_on_resolution().empty());
    183   EXPECT_TRUE(b_record->waiting_on_resolution().empty());
    184   EXPECT_TRUE(c_record->waiting_on_resolution().empty());
    185 }
    186 
    187 // Tests that the should generate bit is set and propogated properly.
    188 TEST_F(BuilderTest, ShouldGenerate) {
    189   DefineToolchain();
    190 
    191   // Define a secondary toolchain.
    192   Settings settings2(&build_settings_, "secondary");
    193   Label toolchain_label2(SourceDir("//tc/"), "secondary");
    194   settings2.set_toolchain_label(toolchain_label2);
    195   Toolchain* tc2 = new Toolchain(&settings2, toolchain_label2);
    196   builder_->ItemDefined(scoped_ptr<Item>(tc2));
    197 
    198   // Construct a dependency chain: A -> B. A is in the default toolchain, B
    199   // is not.
    200   Label a_label(SourceDir("//foo/"), "a",
    201                 settings_.toolchain_label().dir(), "a");
    202   Label b_label(SourceDir("//foo/"), "b",
    203                 toolchain_label2.dir(), toolchain_label2.name());
    204 
    205   // First define B.
    206   Target* b = new Target(&settings2, b_label);
    207   b->set_output_type(Target::EXECUTABLE);
    208   builder_->ItemDefined(scoped_ptr<Item>(b));
    209 
    210   // B should not be marked generated by default.
    211   BuilderRecord* b_record = builder_->GetRecord(b_label);
    212   EXPECT_FALSE(b_record->should_generate());
    213 
    214   // Define A with a dependency on B.
    215   Target* a = new Target(&settings_, a_label);
    216   a->deps().push_back(LabelTargetPair(b_label));
    217   a->set_output_type(Target::EXECUTABLE);
    218   builder_->ItemDefined(scoped_ptr<Item>(a));
    219 
    220   // A should have the generate bit set since it's in the default toolchain.
    221   BuilderRecord* a_record = builder_->GetRecord(a_label);
    222   EXPECT_TRUE(a_record->should_generate());
    223 
    224   // It should have gotten pushed to B.
    225   EXPECT_TRUE(b_record->should_generate());
    226 }
    227