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
12namespace {
13
14class MockLoader : public Loader {
15 public:
16  MockLoader() {
17  }
18
19  // Loader implementation:
20  virtual void Load(const SourceFile& file,
21                    const LocationRange& origin,
22                    const Label& toolchain_name) OVERRIDE {
23    files_.push_back(file);
24  }
25  virtual void ToolchainLoaded(const Toolchain* toolchain) OVERRIDE {
26  }
27  virtual Label GetDefaultToolchain() const OVERRIDE {
28    return Label();
29  }
30  virtual const Settings* GetToolchainSettings(
31      const Label& label) const OVERRIDE {
32    return NULL;
33  }
34
35  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.
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:
55  virtual ~MockLoader() {}
56
57  std::vector<SourceFile> files_;
58};
59
60class BuilderTest : public testing::Test {
61 public:
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
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
89TEST_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.
182TEST_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