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 14 namespace { 15 16 class 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 71 TEST_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 143 TEST_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. 195 TEST_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. 239 TEST_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