1// Copyright 2014 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 <vector>
6
7#include "testing/gtest/include/gtest/gtest.h"
8#include "tools/gn/config.h"
9#include "tools/gn/header_checker.h"
10#include "tools/gn/scheduler.h"
11#include "tools/gn/target.h"
12#include "tools/gn/test_with_scope.h"
13
14namespace {
15
16class HeaderCheckerTest : public testing::Test {
17 public:
18  HeaderCheckerTest()
19      : a_(setup_.settings(), Label(SourceDir("//a/"), "a")),
20        b_(setup_.settings(), Label(SourceDir("//b/"), "b")),
21        c_(setup_.settings(), Label(SourceDir("//c/"), "c")),
22        d_(setup_.settings(), Label(SourceDir("//d/"), "d")) {
23    a_.set_output_type(Target::SOURCE_SET);
24    b_.set_output_type(Target::SOURCE_SET);
25    c_.set_output_type(Target::SOURCE_SET);
26    d_.set_output_type(Target::SOURCE_SET);
27
28    Err err;
29    a_.SetToolchain(setup_.toolchain(), &err);
30    b_.SetToolchain(setup_.toolchain(), &err);
31    c_.SetToolchain(setup_.toolchain(), &err);
32    d_.SetToolchain(setup_.toolchain(), &err);
33
34    a_.public_deps().push_back(LabelTargetPair(&b_));
35    b_.public_deps().push_back(LabelTargetPair(&c_));
36
37    // Start with all public visibility.
38    a_.visibility().SetPublic();
39    b_.visibility().SetPublic();
40    c_.visibility().SetPublic();
41    d_.visibility().SetPublic();
42
43    d_.OnResolved(&err);
44    c_.OnResolved(&err);
45    b_.OnResolved(&err);
46    a_.OnResolved(&err);
47
48    targets_.push_back(&a_);
49    targets_.push_back(&b_);
50    targets_.push_back(&c_);
51    targets_.push_back(&d_);
52  }
53
54 protected:
55  Scheduler scheduler_;
56
57  TestWithScope setup_;
58
59  // Some headers that are automatically set up with a public dependency chain.
60  // a -> b -> c. D is unconnected.
61  Target a_;
62  Target b_;
63  Target c_;
64  Target d_;
65
66  std::vector<const Target*> targets_;
67};
68
69}  // namespace
70
71TEST_F(HeaderCheckerTest, IsDependencyOf) {
72  scoped_refptr<HeaderChecker> checker(
73      new HeaderChecker(setup_.build_settings(), targets_));
74
75  // Add a target P ("private") that privately depends on C, and hook up the
76  // chain so that A -> P -> C. A will depend on C via two different paths.
77  Err err;
78  Target p(setup_.settings(), Label(SourceDir("//p/"), "p"));
79  p.set_output_type(Target::SOURCE_SET);
80  p.SetToolchain(setup_.toolchain(), &err);
81  EXPECT_FALSE(err.has_error());
82  p.private_deps().push_back(LabelTargetPair(&c_));
83  p.visibility().SetPublic();
84  p.OnResolved(&err);
85
86  a_.public_deps().push_back(LabelTargetPair(&p));
87
88  // A does not depend on itself.
89  bool is_permitted = false;
90  HeaderChecker::Chain chain;
91  EXPECT_FALSE(checker->IsDependencyOf(&a_, &a_, &chain, &is_permitted));
92
93  // A depends publicly on B.
94  chain.clear();
95  is_permitted = false;
96  EXPECT_TRUE(checker->IsDependencyOf(&b_, &a_, &chain, &is_permitted));
97  ASSERT_EQ(2u, chain.size());
98  EXPECT_EQ(HeaderChecker::ChainLink(&b_, true), chain[0]);
99  EXPECT_EQ(HeaderChecker::ChainLink(&a_, true), chain[1]);
100  EXPECT_TRUE(is_permitted);
101
102  // A indirectly depends on C. The "public" dependency path through B should
103  // be identified.
104  chain.clear();
105  is_permitted = false;
106  EXPECT_TRUE(checker->IsDependencyOf(&c_, &a_, &chain, &is_permitted));
107  ASSERT_EQ(3u, chain.size());
108  EXPECT_EQ(HeaderChecker::ChainLink(&c_, true), chain[0]);
109  EXPECT_EQ(HeaderChecker::ChainLink(&b_, true), chain[1]);
110  EXPECT_EQ(HeaderChecker::ChainLink(&a_, true), chain[2]);
111  EXPECT_TRUE(is_permitted);
112
113  // C does not depend on A.
114  chain.clear();
115  is_permitted = false;
116  EXPECT_FALSE(checker->IsDependencyOf(&a_, &c_, &chain, &is_permitted));
117  EXPECT_TRUE(chain.empty());
118  EXPECT_FALSE(is_permitted);
119
120  // Remove the B -> C public dependency, leaving P's private dep on C the only
121  // path from A to C. This should now be found.
122  chain.clear();
123  EXPECT_EQ(&c_, b_.public_deps()[0].ptr);  // Validate it's the right one.
124  b_.public_deps().erase(b_.public_deps().begin());
125  EXPECT_TRUE(checker->IsDependencyOf(&c_, &a_, &chain, &is_permitted));
126  EXPECT_EQ(3u, chain.size());
127  EXPECT_EQ(HeaderChecker::ChainLink(&c_, false), chain[0]);
128  EXPECT_EQ(HeaderChecker::ChainLink(&p, true), chain[1]);
129  EXPECT_EQ(HeaderChecker::ChainLink(&a_, true), chain[2]);
130  EXPECT_FALSE(is_permitted);
131
132  // P privately depends on C. That dependency should be OK since it's only
133  // one hop.
134  chain.clear();
135  is_permitted = false;
136  EXPECT_TRUE(checker->IsDependencyOf(&c_, &p, &chain, &is_permitted));
137  ASSERT_EQ(2u, chain.size());
138  EXPECT_EQ(HeaderChecker::ChainLink(&c_, false), chain[0]);
139  EXPECT_EQ(HeaderChecker::ChainLink(&p, true), chain[1]);
140  EXPECT_TRUE(is_permitted);
141}
142
143TEST_F(HeaderCheckerTest, CheckInclude) {
144  InputFile input_file(SourceFile("//some_file.cc"));
145  input_file.SetContents(std::string());
146  LocationRange range;  // Dummy value.
147
148  // Add a disconnected target d with a header to check that you have to have
149  // to depend on a target listing a header.
150  SourceFile d_header("//d_header.h");
151  d_.sources().push_back(SourceFile(d_header));
152
153  // Add a header on B and say everything in B is public.
154  SourceFile b_public("//b_public.h");
155  b_.sources().push_back(b_public);
156  c_.set_all_headers_public(true);
157
158  // Add a public and private header on C.
159  SourceFile c_public("//c_public.h");
160  SourceFile c_private("//c_private.h");
161  c_.sources().push_back(c_private);
162  c_.public_headers().push_back(c_public);
163  c_.set_all_headers_public(false);
164
165  scoped_refptr<HeaderChecker> checker(
166      new HeaderChecker(setup_.build_settings(), targets_));
167
168  // A file in target A can't include a header from D because A has no
169  // dependency on D.
170  Err err;
171  EXPECT_FALSE(checker->CheckInclude(&a_, input_file, d_header, range, &err));
172  EXPECT_TRUE(err.has_error());
173
174  // A can include the public header in B.
175  err = Err();
176  EXPECT_TRUE(checker->CheckInclude(&a_, input_file, b_public, range, &err));
177  EXPECT_FALSE(err.has_error());
178
179  // Check A depending on the public and private headers in C.
180  err = Err();
181  EXPECT_TRUE(checker->CheckInclude(&a_, input_file, c_public, range, &err));
182  EXPECT_FALSE(err.has_error());
183  EXPECT_FALSE(checker->CheckInclude(&a_, input_file, c_private, range, &err));
184  EXPECT_TRUE(err.has_error());
185
186  // A can depend on a random file unknown to the build.
187  err = Err();
188  EXPECT_TRUE(checker->CheckInclude(&a_, input_file, SourceFile("//random.h"),
189                                    range, &err));
190  EXPECT_FALSE(err.has_error());
191}
192
193// A public chain of dependencies should always be identified first, even if
194// it is longer than a private one.
195TEST_F(HeaderCheckerTest, PublicFirst) {
196  // Now make a A -> Z -> D private dependency chain (one shorter than the
197  // public one to get to D).
198  Target z(setup_.settings(), Label(SourceDir("//a/"), "a"));
199  z.set_output_type(Target::SOURCE_SET);
200  Err err;
201  EXPECT_TRUE(z.SetToolchain(setup_.toolchain(), &err));
202  z.private_deps().push_back(LabelTargetPair(&d_));
203  EXPECT_TRUE(z.OnResolved(&err));
204  targets_.push_back(&z);
205
206  a_.private_deps().push_back(LabelTargetPair(&z));
207
208  // Check that D can be found from A, but since it's private, it will be
209  // marked as not permitted.
210  bool is_permitted = false;
211  HeaderChecker::Chain chain;
212  scoped_refptr<HeaderChecker> checker(
213      new HeaderChecker(setup_.build_settings(), targets_));
214  EXPECT_TRUE(checker->IsDependencyOf(&d_, &a_, &chain, &is_permitted));
215
216  EXPECT_FALSE(is_permitted);
217  ASSERT_EQ(3u, chain.size());
218  EXPECT_EQ(HeaderChecker::ChainLink(&d_, false), chain[0]);
219  EXPECT_EQ(HeaderChecker::ChainLink(&z, false), chain[1]);
220  EXPECT_EQ(HeaderChecker::ChainLink(&a_, true), chain[2]);
221
222  // Hook up D to the existing public A -> B -> C chain to make a long one, and
223  // search for D again.
224  c_.public_deps().push_back(LabelTargetPair(&d_));
225  checker = new HeaderChecker(setup_.build_settings(), targets_);
226  chain.clear();
227  EXPECT_TRUE(checker->IsDependencyOf(&d_, &a_, &chain, &is_permitted));
228
229  // This should have found the long public one.
230  EXPECT_TRUE(is_permitted);
231  ASSERT_EQ(4u, chain.size());
232  EXPECT_EQ(HeaderChecker::ChainLink(&d_, true), chain[0]);
233  EXPECT_EQ(HeaderChecker::ChainLink(&c_, true), chain[1]);
234  EXPECT_EQ(HeaderChecker::ChainLink(&b_, true), chain[2]);
235  EXPECT_EQ(HeaderChecker::ChainLink(&a_, true), chain[3]);
236}
237
238// Checks that the allow_circular_includes_from list works.
239TEST_F(HeaderCheckerTest, CheckIncludeAllowCircular) {
240  InputFile input_file(SourceFile("//some_file.cc"));
241  input_file.SetContents(std::string());
242  LocationRange range;  // Dummy value.
243
244  // Add an include file to A.
245  SourceFile a_public("//a_public.h");
246  a_.sources().push_back(a_public);
247
248  scoped_refptr<HeaderChecker> checker(
249      new HeaderChecker(setup_.build_settings(), targets_));
250
251  // A depends on B. So B normally can't include headers from A.
252  Err err;
253  EXPECT_FALSE(checker->CheckInclude(&b_, input_file, a_public, range, &err));
254  EXPECT_TRUE(err.has_error());
255
256  // Add an allow_circular_includes_from on A that lists B.
257  a_.allow_circular_includes_from().insert(b_.label());
258
259  // Now the include from B to A should be allowed.
260  err = Err();
261  EXPECT_TRUE(checker->CheckInclude(&b_, input_file, a_public, range, &err));
262  EXPECT_FALSE(err.has_error());
263}
264