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