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.
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"
16 class HeaderCheckerTest
: public testing::Test
{
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
);
29 a_
.SetToolchain(setup_
.toolchain(), &err
);
30 b_
.SetToolchain(setup_
.toolchain(), &err
);
31 c_
.SetToolchain(setup_
.toolchain(), &err
);
32 d_
.SetToolchain(setup_
.toolchain(), &err
);
34 a_
.public_deps().push_back(LabelTargetPair(&b_
));
35 b_
.public_deps().push_back(LabelTargetPair(&c_
));
37 // Start with all public visibility.
38 a_
.visibility().SetPublic();
39 b_
.visibility().SetPublic();
40 c_
.visibility().SetPublic();
41 d_
.visibility().SetPublic();
48 targets_
.push_back(&a_
);
49 targets_
.push_back(&b_
);
50 targets_
.push_back(&c_
);
51 targets_
.push_back(&d_
);
59 // Some headers that are automatically set up with a public dependency chain.
60 // a -> b -> c. D is unconnected.
66 std::vector
<const Target
*> targets_
;
71 TEST_F(HeaderCheckerTest
, IsDependencyOf
) {
72 scoped_refptr
<HeaderChecker
> checker(
73 new HeaderChecker(setup_
.build_settings(), targets_
));
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.
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();
86 a_
.public_deps().push_back(LabelTargetPair(&p
));
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
));
93 // A depends publicly on B.
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
);
102 // A indirectly depends on C. The "public" dependency path through B should
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
);
113 // C does not depend on A.
115 is_permitted
= false;
116 EXPECT_FALSE(checker
->IsDependencyOf(&a_
, &c_
, &chain
, &is_permitted
));
117 EXPECT_TRUE(chain
.empty());
118 EXPECT_FALSE(is_permitted
);
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.
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
);
132 // P privately depends on C. That dependency should be OK since it's only
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
);
143 TEST_F(HeaderCheckerTest
, CheckInclude
) {
144 InputFile
input_file(SourceFile("//some_file.cc"));
145 input_file
.SetContents(std::string());
146 LocationRange range
; // Dummy value.
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
));
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);
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);
165 // Create another toolchain.
166 Settings
other_settings(setup_
.build_settings(), "other/");
167 Toolchain
other_toolchain(&other_settings
,
168 Label(SourceDir("//toolchain/"), "other"));
169 TestWithScope::SetupToolchain(&other_toolchain
);
170 other_settings
.set_toolchain_label(other_toolchain
.label());
171 other_settings
.set_default_toolchain_label(setup_
.toolchain()->label());
173 // Add a target in the other toolchain with a header in it that is not
174 // connected to any targets in the main toolchain.
175 Target
otc(&other_settings
, Label(SourceDir("//p/"), "otc",
176 other_toolchain
.label().dir(), other_toolchain
.label().name()));
177 otc
.set_output_type(Target::SOURCE_SET
);
179 EXPECT_TRUE(otc
.SetToolchain(&other_toolchain
, &err
));
180 otc
.visibility().SetPublic();
181 targets_
.push_back(&otc
);
183 SourceFile
otc_header("//otc_header.h");
184 otc
.sources().push_back(otc_header
);
185 EXPECT_TRUE(otc
.OnResolved(&err
));
187 scoped_refptr
<HeaderChecker
> checker(
188 new HeaderChecker(setup_
.build_settings(), targets_
));
190 // A file in target A can't include a header from D because A has no
192 EXPECT_FALSE(checker
->CheckInclude(&a_
, input_file
, d_header
, range
, &err
));
193 EXPECT_TRUE(err
.has_error());
195 // A can include the public header in B.
197 EXPECT_TRUE(checker
->CheckInclude(&a_
, input_file
, b_public
, range
, &err
));
198 EXPECT_FALSE(err
.has_error());
200 // Check A depending on the public and private headers in C.
202 EXPECT_TRUE(checker
->CheckInclude(&a_
, input_file
, c_public
, range
, &err
));
203 EXPECT_FALSE(err
.has_error());
204 EXPECT_FALSE(checker
->CheckInclude(&a_
, input_file
, c_private
, range
, &err
));
205 EXPECT_TRUE(err
.has_error());
207 // A can depend on a random file unknown to the build.
209 EXPECT_TRUE(checker
->CheckInclude(&a_
, input_file
, SourceFile("//random.h"),
211 EXPECT_FALSE(err
.has_error());
213 // A can depend on a file present only in another toolchain even with no
216 EXPECT_TRUE(checker
->CheckInclude(&a_
, input_file
, otc_header
, range
, &err
));
217 EXPECT_FALSE(err
.has_error());
220 // A public chain of dependencies should always be identified first, even if
221 // it is longer than a private one.
222 TEST_F(HeaderCheckerTest
, PublicFirst
) {
223 // Now make a A -> Z -> D private dependency chain (one shorter than the
224 // public one to get to D).
225 Target
z(setup_
.settings(), Label(SourceDir("//a/"), "a"));
226 z
.set_output_type(Target::SOURCE_SET
);
228 EXPECT_TRUE(z
.SetToolchain(setup_
.toolchain(), &err
));
229 z
.private_deps().push_back(LabelTargetPair(&d_
));
230 EXPECT_TRUE(z
.OnResolved(&err
));
231 targets_
.push_back(&z
);
233 a_
.private_deps().push_back(LabelTargetPair(&z
));
235 // Check that D can be found from A, but since it's private, it will be
236 // marked as not permitted.
237 bool is_permitted
= false;
238 HeaderChecker::Chain chain
;
239 scoped_refptr
<HeaderChecker
> checker(
240 new HeaderChecker(setup_
.build_settings(), targets_
));
241 EXPECT_TRUE(checker
->IsDependencyOf(&d_
, &a_
, &chain
, &is_permitted
));
243 EXPECT_FALSE(is_permitted
);
244 ASSERT_EQ(3u, chain
.size());
245 EXPECT_EQ(HeaderChecker::ChainLink(&d_
, false), chain
[0]);
246 EXPECT_EQ(HeaderChecker::ChainLink(&z
, false), chain
[1]);
247 EXPECT_EQ(HeaderChecker::ChainLink(&a_
, true), chain
[2]);
249 // Hook up D to the existing public A -> B -> C chain to make a long one, and
250 // search for D again.
251 c_
.public_deps().push_back(LabelTargetPair(&d_
));
252 checker
= new HeaderChecker(setup_
.build_settings(), targets_
);
254 EXPECT_TRUE(checker
->IsDependencyOf(&d_
, &a_
, &chain
, &is_permitted
));
256 // This should have found the long public one.
257 EXPECT_TRUE(is_permitted
);
258 ASSERT_EQ(4u, chain
.size());
259 EXPECT_EQ(HeaderChecker::ChainLink(&d_
, true), chain
[0]);
260 EXPECT_EQ(HeaderChecker::ChainLink(&c_
, true), chain
[1]);
261 EXPECT_EQ(HeaderChecker::ChainLink(&b_
, true), chain
[2]);
262 EXPECT_EQ(HeaderChecker::ChainLink(&a_
, true), chain
[3]);
265 // Checks that the allow_circular_includes_from list works.
266 TEST_F(HeaderCheckerTest
, CheckIncludeAllowCircular
) {
267 InputFile
input_file(SourceFile("//some_file.cc"));
268 input_file
.SetContents(std::string());
269 LocationRange range
; // Dummy value.
271 // Add an include file to A.
272 SourceFile
a_public("//a_public.h");
273 a_
.sources().push_back(a_public
);
275 scoped_refptr
<HeaderChecker
> checker(
276 new HeaderChecker(setup_
.build_settings(), targets_
));
278 // A depends on B. So B normally can't include headers from A.
280 EXPECT_FALSE(checker
->CheckInclude(&b_
, input_file
, a_public
, range
, &err
));
281 EXPECT_TRUE(err
.has_error());
283 // Add an allow_circular_includes_from on A that lists B.
284 a_
.allow_circular_includes_from().insert(b_
.label());
286 // Now the include from B to A should be allowed.
288 EXPECT_TRUE(checker
->CheckInclude(&b_
, input_file
, a_public
, range
, &err
));
289 EXPECT_FALSE(err
.has_error());