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