Home | History | Annotate | Download | only in gn
      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