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:
MockLoader()16 MockLoader() {
17 }
18
19 // Loader implementation:
Load(const SourceFile & file,const LocationRange & origin,const Label & toolchain_name)20 virtual 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 virtual void ToolchainLoaded(const Toolchain* toolchain) OVERRIDE {
26 }
GetDefaultToolchain() const27 virtual Label GetDefaultToolchain() const OVERRIDE {
28 return Label();
29 }
GetToolchainSettings(const Label & label) const30 virtual const Settings* GetToolchainSettings(
31 const Label& label) const OVERRIDE {
32 return NULL;
33 }
34
HasLoadedNone() const35 bool HasLoadedNone() const {
36 return files_.empty();
37 }
38
39 // Returns true if two loads have been requested and they match the given
40 // file. This will clear the records so it will be empty for the next call.
HasLoadedTwo(const SourceFile & a,const SourceFile & b)41 bool HasLoadedTwo(const SourceFile& a, const SourceFile& b) {
42 if (files_.size() != 2u) {
43 files_.clear();
44 return false;
45 }
46
47 bool match = (
48 (files_[0] == a && files_[1] == b) ||
49 (files_[0] == b && files_[1] == a));
50 files_.clear();
51 return match;
52 }
53
54 private:
~MockLoader()55 virtual ~MockLoader() {}
56
57 std::vector<SourceFile> files_;
58 };
59
60 class BuilderTest : public testing::Test {
61 public:
BuilderTest()62 BuilderTest()
63 : loader_(new MockLoader),
64 builder_(new Builder(loader_.get())),
65 settings_(&build_settings_, std::string()),
66 scope_(&settings_) {
67 build_settings_.SetBuildDir(SourceDir("//out/"));
68 settings_.set_toolchain_label(Label(SourceDir("//tc/"), "default"));
69 settings_.set_default_toolchain_label(settings_.toolchain_label());
70 }
71
DefineToolchain()72 Toolchain* DefineToolchain() {
73 Toolchain* tc = new Toolchain(&settings_, settings_.toolchain_label());
74 TestWithScope::SetupToolchain(tc);
75 builder_->ItemDefined(scoped_ptr<Item>(tc));
76 return tc;
77 }
78
79 protected:
80 scoped_refptr<MockLoader> loader_;
81 scoped_refptr<Builder> builder_;
82 BuildSettings build_settings_;
83 Settings settings_;
84 Scope scope_;
85 };
86
87 } // namespace
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(scoped_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(),
136 a_record->all_deps().find(b_record));
137 EXPECT_NE(a_record->unresolved_deps().end(),
138 a_record->unresolved_deps().find(b_record));
139
140 // B should be marked as having A waiting on it.
141 EXPECT_EQ(1u, b_record->waiting_on_resolution().size());
142 EXPECT_NE(b_record->waiting_on_resolution().end(),
143 b_record->waiting_on_resolution().find(a_record));
144
145 // Add the C target.
146 Target* c = new Target(&settings_, c_label);
147 c->set_output_type(Target::STATIC_LIBRARY);
148 c->visibility().SetPublic();
149 builder_->ItemDefined(scoped_ptr<Item>(c));
150
151 // C only depends on the already-loaded toolchain so we shouldn't have
152 // requested anything else.
153 EXPECT_TRUE(loader_->HasLoadedNone());
154
155 // Add the B target.
156 Target* b = new Target(&settings_, b_label);
157 a->public_deps().push_back(LabelTargetPair(c_label));
158 b->set_output_type(Target::SHARED_LIBRARY);
159 b->visibility().SetPublic();
160 builder_->ItemDefined(scoped_ptr<Item>(b));
161
162 // B depends only on the already-loaded C and toolchain so we shouldn't have
163 // requested anything else.
164 EXPECT_TRUE(loader_->HasLoadedNone());
165
166 // All targets should now be resolved.
167 BuilderRecord* c_record = builder_->GetRecord(c_label);
168 EXPECT_TRUE(a_record->resolved());
169 EXPECT_TRUE(b_record->resolved());
170 EXPECT_TRUE(c_record->resolved());
171
172 EXPECT_TRUE(a_record->unresolved_deps().empty());
173 EXPECT_TRUE(b_record->unresolved_deps().empty());
174 EXPECT_TRUE(c_record->unresolved_deps().empty());
175
176 EXPECT_TRUE(a_record->waiting_on_resolution().empty());
177 EXPECT_TRUE(b_record->waiting_on_resolution().empty());
178 EXPECT_TRUE(c_record->waiting_on_resolution().empty());
179 }
180
181 // Tests that the should generate bit is set and propogated properly.
TEST_F(BuilderTest,ShouldGenerate)182 TEST_F(BuilderTest, ShouldGenerate) {
183 DefineToolchain();
184
185 // Define a secondary toolchain.
186 Settings settings2(&build_settings_, "secondary/");
187 Label toolchain_label2(SourceDir("//tc/"), "secondary");
188 settings2.set_toolchain_label(toolchain_label2);
189 Toolchain* tc2 = new Toolchain(&settings2, toolchain_label2);
190 TestWithScope::SetupToolchain(tc2);
191 builder_->ItemDefined(scoped_ptr<Item>(tc2));
192
193 // Construct a dependency chain: A -> B. A is in the default toolchain, B
194 // is not.
195 Label a_label(SourceDir("//foo/"), "a",
196 settings_.toolchain_label().dir(), "a");
197 Label b_label(SourceDir("//foo/"), "b",
198 toolchain_label2.dir(), toolchain_label2.name());
199
200 // First define B.
201 Target* b = new Target(&settings2, b_label);
202 b->visibility().SetPublic();
203 b->set_output_type(Target::EXECUTABLE);
204 builder_->ItemDefined(scoped_ptr<Item>(b));
205
206 // B should not be marked generated by default.
207 BuilderRecord* b_record = builder_->GetRecord(b_label);
208 EXPECT_FALSE(b_record->should_generate());
209
210 // Define A with a dependency on B.
211 Target* a = new Target(&settings_, a_label);
212 a->public_deps().push_back(LabelTargetPair(b_label));
213 a->set_output_type(Target::EXECUTABLE);
214 builder_->ItemDefined(scoped_ptr<Item>(a));
215
216 // A should have the generate bit set since it's in the default toolchain.
217 BuilderRecord* a_record = builder_->GetRecord(a_label);
218 EXPECT_TRUE(a_record->should_generate());
219
220 // It should have gotten pushed to B.
221 EXPECT_TRUE(b_record->should_generate());
222 }
223