• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 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 "gn/analyzer.h"
6 
7 #include "gn/c_tool.h"
8 #include "gn/build_settings.h"
9 #include "gn/builder.h"
10 #include "gn/config.h"
11 #include "gn/general_tool.h"
12 #include "gn/loader.h"
13 #include "gn/pool.h"
14 #include "gn/settings.h"
15 #include "gn/source_file.h"
16 #include "gn/substitution_list.h"
17 #include "gn/target.h"
18 #include "gn/tool.h"
19 #include "gn/toolchain.h"
20 #include "util/test/test.h"
21 
22 namespace gn_analyzer_unittest {
23 
24 class MockLoader : public Loader {
25  public:
26   MockLoader() = default;
27 
Load(const SourceFile & file,const LocationRange & origin,const Label & toolchain_name)28   void Load(const SourceFile& file,
29             const LocationRange& origin,
30             const Label& toolchain_name) override {}
ToolchainLoaded(const Toolchain * toolchain)31   void ToolchainLoaded(const Toolchain* toolchain) override {}
GetDefaultToolchain() const32   Label GetDefaultToolchain() const override {
33     return Label(SourceDir("//tc/"), "default");
34   }
GetToolchainSettings(const Label & label) const35   const Settings* GetToolchainSettings(const Label& label) const override {
36     return nullptr;
37   }
38 
39  private:
40   ~MockLoader() override = default;
41 };
42 
43 class AnalyzerTest : public testing::Test {
44  public:
AnalyzerTest()45   AnalyzerTest()
46       : loader_(new MockLoader),
47         builder_(loader_.get()),
48         settings_(&build_settings_, std::string()) {
49     build_settings_.SetBuildDir(SourceDir("//out/"));
50     settings_.set_toolchain_label(Label(SourceDir("//tc/"), "default"));
51     settings_.set_default_toolchain_label(settings_.toolchain_label());
52     tc_dir_ = settings_.toolchain_label().dir();
53     tc_name_ = settings_.toolchain_label().name();
54   }
55 
MakeTarget(const std::string & dir,const std::string & name)56   std::unique_ptr<Target> MakeTarget(const std::string& dir,
57                                      const std::string& name) {
58     Label label(SourceDir(dir), name, tc_dir_, tc_name_);
59     return std::make_unique<Target>(&settings_, label);
60   }
61 
MakeConfig(const std::string & dir,const std::string & name)62   std::unique_ptr<Config> MakeConfig(const std::string& dir,
63                                      const std::string& name) {
64     Label label(SourceDir(dir), name, tc_dir_, tc_name_);
65     return std::make_unique<Config>(&settings_, label);
66   }
67 
MakePool(const std::string & dir,const std::string & name)68   std::unique_ptr<Pool> MakePool(const std::string& dir,
69                                  const std::string& name) {
70     Label label(SourceDir(dir), name, tc_dir_, tc_name_);
71     return std::make_unique<Pool>(&settings_, label);
72   }
73 
RunAnalyzerTest(const std::string & input,const std::string & expected_output)74   void RunAnalyzerTest(const std::string& input,
75                        const std::string& expected_output) {
76     Analyzer analyzer(builder_, SourceFile("//build/config/BUILDCONFIG.gn"),
77                       SourceFile("//.gn"),
78                       {SourceFile("//out/debug/args.gn"),
79                        SourceFile("//build/default_args.gn")});
80     Err err;
81     std::string actual_output = analyzer.Analyze(input, &err);
82     EXPECT_EQ(err.has_error(), false);
83     EXPECT_EQ(expected_output, actual_output);
84   }
85 
86  protected:
87   scoped_refptr<MockLoader> loader_;
88   Builder builder_;
89   BuildSettings build_settings_;
90   Settings settings_;
91   SourceDir tc_dir_;
92   std::string tc_name_;
93 };
94 
95 // Tests that a target is marked as affected if its sources are modified.
TEST_F(AnalyzerTest,TargetRefersToSources)96 TEST_F(AnalyzerTest, TargetRefersToSources) {
97   std::unique_ptr<Target> t = MakeTarget("//dir", "target_name");
98   Target* t_raw = t.get();
99   builder_.ItemDefined(std::move(t));
100   RunAnalyzerTest(
101       R"({
102        "files": [ "//dir/file_name.cc" ],
103        "additional_compile_targets": [ "all" ],
104        "test_targets": [ "//dir:target_name" ]
105        })",
106       "{"
107       R"("compile_targets":[],)"
108       R"/("status":"No dependency",)/"
109       R"("test_targets":[])"
110       "}");
111 
112   t_raw->sources().push_back(SourceFile("//dir/file_name.cc"));
113   RunAnalyzerTest(
114       R"({
115        "files": [ "//dir/file_name.cc" ],
116        "additional_compile_targets": [ "all" ],
117        "test_targets": [ "//dir:target_name" ]
118        })",
119       "{"
120       R"("compile_targets":["all"],)"
121       R"/("status":"Found dependency",)/"
122       R"("test_targets":["//dir:target_name"])"
123       "}");
124 }
125 
126 // Tests that a target is marked as affected if its public headers are modified.
TEST_F(AnalyzerTest,TargetRefersToPublicHeaders)127 TEST_F(AnalyzerTest, TargetRefersToPublicHeaders) {
128   std::unique_ptr<Target> t = MakeTarget("//dir", "target_name");
129   Target* t_raw = t.get();
130   builder_.ItemDefined(std::move(t));
131   RunAnalyzerTest(
132       R"({
133        "files": [ "//dir/header_name.h" ],
134        "additional_compile_targets": [ "all" ],
135        "test_targets": [ "//dir:target_name" ]
136        })",
137       "{"
138       R"("compile_targets":[],)"
139       R"/("status":"No dependency",)/"
140       R"("test_targets":[])"
141       "}");
142 
143   t_raw->public_headers().push_back(SourceFile("//dir/header_name.h"));
144   RunAnalyzerTest(
145       R"({
146        "files": [ "//dir/header_name.h" ],
147        "additional_compile_targets": [ "all" ],
148        "test_targets": [ "//dir:target_name" ]
149        })",
150       "{"
151       R"("compile_targets":["all"],)"
152       R"/("status":"Found dependency",)/"
153       R"("test_targets":["//dir:target_name"])"
154       "}");
155 }
156 
157 // Tests that a target is marked as affected if its inputs are modified.
TEST_F(AnalyzerTest,TargetRefersToInputs)158 TEST_F(AnalyzerTest, TargetRefersToInputs) {
159   std::unique_ptr<Target> t = MakeTarget("//dir", "target_name");
160   Target* t_raw = t.get();
161   builder_.ItemDefined(std::move(t));
162   RunAnalyzerTest(
163       R"({
164        "files": [ "//dir/extra_input.cc" ],
165        "additional_compile_targets": [ "all" ],
166        "test_targets": [ "//dir:target_name" ]
167        })",
168       "{"
169       R"("compile_targets":[],)"
170       R"/("status":"No dependency",)/"
171       R"("test_targets":[])"
172       "}");
173 
174   SourceFile extra_input(SourceFile("//dir/extra_input.cc"));
175   t_raw->config_values().inputs().push_back(extra_input);
176   RunAnalyzerTest(
177       R"({
178        "files": [ "//dir/extra_input.cc" ],
179        "additional_compile_targets": [ "all" ],
180        "test_targets": [ "//dir:target_name" ]
181        })",
182       "{"
183       R"("compile_targets":["all"],)"
184       R"/("status":"Found dependency",)/"
185       R"("test_targets":["//dir:target_name"])"
186       "}");
187 
188   t_raw->config_values().inputs().clear();
189   std::unique_ptr<Config> c = MakeConfig("//dir", "config_name");
190   c->own_values().inputs().push_back(extra_input);
191   t_raw->configs().push_back(LabelConfigPair(c.get()));
192   builder_.ItemDefined(std::move(c));
193 
194   RunAnalyzerTest(
195       R"({
196        "files": [ "//dir/extra_input.cc" ],
197        "additional_compile_targets": [ "all" ],
198        "test_targets": [ "//dir:target_name" ]
199        })",
200       "{"
201       R"("compile_targets":["all"],)"
202       R"/("status":"Found dependency",)/"
203       R"("test_targets":["//dir:target_name"])"
204       "}");
205 }
206 
207 // Tests that a target is marked as affected if its data are modified.
TEST_F(AnalyzerTest,TargetRefersToData)208 TEST_F(AnalyzerTest, TargetRefersToData) {
209   std::unique_ptr<Target> t = MakeTarget("//dir", "target_name");
210   Target* t_raw = t.get();
211   builder_.ItemDefined(std::move(t));
212   RunAnalyzerTest(
213       R"({
214        "files": [ "//dir/data.html" ],
215        "additional_compile_targets": [ "all" ],
216        "test_targets": [ "//dir:target_name" ]
217        })",
218       "{"
219       R"("compile_targets":[],)"
220       R"/("status":"No dependency",)/"
221       R"("test_targets":[])"
222       "}");
223 
224   t_raw->data().push_back("//dir/data.html");
225   RunAnalyzerTest(
226       R"({
227        "files": [ "//dir/data.html" ],
228        "additional_compile_targets": [ "all" ],
229        "test_targets": [ "//dir:target_name" ]
230        })",
231       "{"
232       R"("compile_targets":["all"],)"
233       R"/("status":"Found dependency",)/"
234       R"("test_targets":["//dir:target_name"])"
235       "}");
236 }
237 
238 // Tests that a target is marked as affected if the target is an action and its
239 // action script is modified.
TEST_F(AnalyzerTest,TargetRefersToActionScript)240 TEST_F(AnalyzerTest, TargetRefersToActionScript) {
241   std::unique_ptr<Target> t = MakeTarget("//dir", "target_name");
242   Target* t_raw = t.get();
243   t->set_output_type(Target::ACTION);
244   builder_.ItemDefined(std::move(t));
245   RunAnalyzerTest(
246       R"({
247        "files": [ "//dir/script.py" ],
248        "additional_compile_targets": [ "all" ],
249        "test_targets": [ "//dir:target_name" ]
250        })",
251       "{"
252       R"("compile_targets":[],)"
253       R"/("status":"No dependency",)/"
254       R"("test_targets":[])"
255       "}");
256 
257   t_raw->action_values().set_script(SourceFile("//dir/script.py"));
258   RunAnalyzerTest(
259       R"({
260        "files": [ "//dir/script.py" ],
261        "additional_compile_targets": [ "all" ],
262        "test_targets": [ "//dir:target_name" ]
263        })",
264       "{"
265       R"("compile_targets":["all"],)"
266       R"/("status":"Found dependency",)/"
267       R"("test_targets":["//dir:target_name"])"
268       "}");
269 }
270 
271 // Tests that a target is marked as affected if its build dependency files are
272 // modified.
TEST_F(AnalyzerTest,TargetRefersToBuildDependencyFiles)273 TEST_F(AnalyzerTest, TargetRefersToBuildDependencyFiles) {
274   std::unique_ptr<Target> t = MakeTarget("//dir", "target_name");
275   Target* t_raw = t.get();
276   builder_.ItemDefined(std::move(t));
277   RunAnalyzerTest(
278       R"({
279        "files": [ "//dir/BUILD.gn" ],
280        "additional_compile_targets": [ "all" ],
281        "test_targets": [ "//dir:target_name" ]
282        })",
283       "{"
284       R"("compile_targets":[],)"
285       R"/("status":"No dependency",)/"
286       R"("test_targets":[])"
287       "}");
288 
289   t_raw->build_dependency_files().insert(SourceFile("//dir/BUILD.gn"));
290   RunAnalyzerTest(
291       R"({
292        "files": [ "//dir/BUILD.gn" ],
293        "additional_compile_targets": [ "all" ],
294        "test_targets": [ "//dir:target_name" ]
295        })",
296       "{"
297       R"("compile_targets":["all"],)"
298       R"/("status":"Found dependency",)/"
299       R"("test_targets":["//dir:target_name"])"
300       "}");
301 }
302 
303 // Tests that if a target is marked as affected, then it propagates to dependent
304 // test_targets.
TEST_F(AnalyzerTest,AffectedTargetpropagatesToDependentTargets)305 TEST_F(AnalyzerTest, AffectedTargetpropagatesToDependentTargets) {
306   std::unique_ptr<Target> t1 = MakeTarget("//dir", "target_name1");
307   std::unique_ptr<Target> t2 = MakeTarget("//dir", "target_name2");
308   std::unique_ptr<Target> t3 = MakeTarget("//dir", "target_name3");
309   t1->private_deps().push_back(LabelTargetPair(t2.get()));
310   t2->private_deps().push_back(LabelTargetPair(t3.get()));
311   builder_.ItemDefined(std::move(t1));
312   builder_.ItemDefined(std::move(t2));
313 
314   Target* t3_raw = t3.get();
315   builder_.ItemDefined(std::move(t3));
316 
317   RunAnalyzerTest(
318       R"({
319        "files": [ "//dir/BUILD.gn" ],
320        "additional_compile_targets": [ "all" ],
321        "test_targets": [ "//dir:target_name1", "//dir:target_name2" ]
322        })",
323       "{"
324       R"("compile_targets":[],)"
325       R"/("status":"No dependency",)/"
326       R"("test_targets":[])"
327       "}");
328 
329   t3_raw->build_dependency_files().insert(SourceFile("//dir/BUILD.gn"));
330   RunAnalyzerTest(
331       R"({
332        "files": [ "//dir/BUILD.gn" ],
333        "additional_compile_targets": [ "all" ],
334        "test_targets": [ "//dir:target_name1", "//dir:target_name2" ]
335        })",
336       "{"
337       R"("compile_targets":["all"],)"
338       R"/("status":"Found dependency",)/"
339       R"("test_targets":["//dir:target_name1","//dir:target_name2"])"
340       "}");
341 }
342 
343 // Tests that if a config is marked as affected, then it propagates to dependent
344 // test_targets.
TEST_F(AnalyzerTest,AffectedConfigpropagatesToDependentTargets)345 TEST_F(AnalyzerTest, AffectedConfigpropagatesToDependentTargets) {
346   std::unique_ptr<Config> c = MakeConfig("//dir", "config_name");
347   Config* c_raw = c.get();
348   std::unique_ptr<Target> t = MakeTarget("//dir", "target_name");
349   t->configs().push_back(LabelConfigPair(c.get()));
350   builder_.ItemDefined(std::move(t));
351   builder_.ItemDefined(std::move(c));
352   RunAnalyzerTest(
353       R"({
354        "files": [ "//dir/BUILD.gn" ],
355        "additional_compile_targets": [ "all" ],
356        "test_targets": [ "//dir:target_name" ]
357        })",
358       "{"
359       R"("compile_targets":[],)"
360       R"/("status":"No dependency",)/"
361       R"("test_targets":[])"
362       "}");
363 
364   c_raw->build_dependency_files().insert(SourceFile("//dir/BUILD.gn"));
365   RunAnalyzerTest(
366       R"({
367        "files": [ "//dir/BUILD.gn" ],
368        "additional_compile_targets": [ "all" ],
369        "test_targets": [ "//dir:target_name" ]
370        })",
371       "{"
372       R"("compile_targets":["all"],)"
373       R"/("status":"Found dependency",)/"
374       R"("test_targets":["//dir:target_name"])"
375       "}");
376 }
377 
378 // Tests that if toolchain is marked as affected, then it propagates to
379 // dependent test_targets.
TEST_F(AnalyzerTest,AffectedToolchainpropagatesToDependentTargets)380 TEST_F(AnalyzerTest, AffectedToolchainpropagatesToDependentTargets) {
381   std::unique_ptr<Target> target = MakeTarget("//dir", "target_name");
382   target->set_output_type(Target::EXECUTABLE);
383   Toolchain* toolchain = new Toolchain(&settings_, settings_.toolchain_label());
384 
385   // The tool is not used anywhere, but is required to construct the dependency
386   // between a target and the toolchain.
387   std::unique_ptr<Tool> fake_tool = Tool::CreateTool(CTool::kCToolLink);
388   fake_tool->set_outputs(
389       SubstitutionList::MakeForTest("//out/debug/output.txt"));
390   toolchain->SetTool(std::move(fake_tool));
391   builder_.ItemDefined(std::move(target));
392   builder_.ItemDefined(std::unique_ptr<Item>(toolchain));
393 
394   RunAnalyzerTest(
395       R"({
396          "files": [ "//tc/BUILD.gn" ],
397          "additional_compile_targets": [ "all" ],
398          "test_targets": [ "//dir:target_name" ]
399          })",
400       "{"
401       R"("compile_targets":[],)"
402       R"/("status":"No dependency",)/"
403       R"("test_targets":[])"
404       "}");
405 
406   toolchain->build_dependency_files().insert(SourceFile("//tc/BUILD.gn"));
407   RunAnalyzerTest(
408       R"({
409        "files": [ "//tc/BUILD.gn" ],
410        "additional_compile_targets": [ "all" ],
411        "test_targets": [ "//dir:target_name" ]
412        })",
413       "{"
414       R"("compile_targets":["all"],)"
415       R"/("status":"Found dependency",)/"
416       R"("test_targets":["//dir:target_name"])"
417       "}");
418 }
419 
420 // Tests that if a pool is marked as affected, then it propagates to dependent
421 // targets.
TEST_F(AnalyzerTest,AffectedPoolpropagatesToDependentTargets)422 TEST_F(AnalyzerTest, AffectedPoolpropagatesToDependentTargets) {
423   std::unique_ptr<Target> t = MakeTarget("//dir", "target_name");
424   t->set_output_type(Target::ACTION);
425   std::unique_ptr<Pool> p = MakePool("//dir", "pool_name");
426   Pool* p_raw = p.get();
427   t->action_values().set_pool(LabelPtrPair<Pool>(p.get()));
428 
429   builder_.ItemDefined(std::move(t));
430   builder_.ItemDefined(std::move(p));
431 
432   RunAnalyzerTest(
433       R"({
434        "files": [ "//dir/BUILD.gn" ],
435        "additional_compile_targets": [ "all" ],
436        "test_targets": [ "//dir:target_name" ]
437        })",
438       "{"
439       R"("compile_targets":[],)"
440       R"/("status":"No dependency",)/"
441       R"("test_targets":[])"
442       "}");
443 
444   p_raw->build_dependency_files().insert(SourceFile("//dir/BUILD.gn"));
445   RunAnalyzerTest(
446       R"({
447        "files": [ "//dir/BUILD.gn" ],
448        "additional_compile_targets": [ "all" ],
449        "test_targets": [ "//dir:target_name" ]
450        })",
451       "{"
452       R"("compile_targets":["all"],)"
453       R"/("status":"Found dependency",)/"
454       R"("test_targets":["//dir:target_name"])"
455       "}");
456 }
457 
458 // Tests that when dependency was found, the "compile_targets" in the output is
459 // not "all".
TEST_F(AnalyzerTest,CompileTargetsAllWasPruned)460 TEST_F(AnalyzerTest, CompileTargetsAllWasPruned) {
461   std::unique_ptr<Target> t1 = MakeTarget("//dir", "target_name1");
462   std::unique_ptr<Target> t2 = MakeTarget("//dir", "target_name2");
463   t2->build_dependency_files().insert(SourceFile("//dir/BUILD.gn"));
464   builder_.ItemDefined(std::move(t1));
465   builder_.ItemDefined(std::move(t2));
466 
467   RunAnalyzerTest(
468       R"({
469        "files": [ "//dir/BUILD.gn" ],
470        "additional_compile_targets": [ "all" ],
471        "test_targets": []
472        })",
473       "{"
474       R"("compile_targets":["//dir:target_name2"],)"
475       R"/("status":"Found dependency",)/"
476       R"("test_targets":[])"
477       "}");
478 }
479 
480 // Tests that output is "No dependency" when no dependency is found.
TEST_F(AnalyzerTest,NoDependency)481 TEST_F(AnalyzerTest, NoDependency) {
482   std::unique_ptr<Target> t = MakeTarget("//dir", "target_name");
483   builder_.ItemDefined(std::move(t));
484 
485   RunAnalyzerTest(
486       R"({
487        "files": [ "//dir/BUILD.gn" ],
488        "additional_compile_targets": [ "all" ],
489        "test_targets": []
490        })",
491       "{"
492       R"("compile_targets":[],)"
493       R"/("status":"No dependency",)/"
494       R"("test_targets":[])"
495       "}");
496 }
497 
498 // Tests that output is "No dependency" when no files or targets are provided.
TEST_F(AnalyzerTest,NoFilesNoTargets)499 TEST_F(AnalyzerTest, NoFilesNoTargets) {
500   RunAnalyzerTest(
501       R"({
502        "files": [],
503        "additional_compile_targets": [],
504        "test_targets": []
505       })",
506       "{"
507       R"("compile_targets":[],)"
508       R"("status":"No dependency",)"
509       R"("test_targets":[])"
510       "}");
511 }
512 
513 // Tests that output displays proper error message when given files aren't
514 // source-absolute or absolute path.
TEST_F(AnalyzerTest,FilesArentSourceAbsolute)515 TEST_F(AnalyzerTest, FilesArentSourceAbsolute) {
516   RunAnalyzerTest(
517       R"({
518        "files": [ "a.cc" ],
519        "additional_compile_targets": [],
520        "test_targets": [ "//dir:target_name" ]
521       })",
522       "{"
523       R"("error":)"
524       R"("\"a.cc\" is not a source-absolute or absolute path.",)"
525       R"("invalid_targets":[])"
526       "}");
527 }
528 
529 // Tests that output displays proper error message when input is ill-formed.
TEST_F(AnalyzerTest,WrongInputFields)530 TEST_F(AnalyzerTest, WrongInputFields) {
531   RunAnalyzerTest(
532       R"({
533        "files": [ "//a.cc" ],
534        "compile_targets": [],
535        "test_targets": [ "//dir:target_name" ]
536       })",
537       "{"
538       R"("error":)"
539       R"("Input does not have a key named )"
540       R"(\"additional_compile_targets\" with a list value.",)"
541       R"("invalid_targets":[])"
542       "}");
543 }
544 
545 // Bails out early with "Found dependency (all)" if dot file is modified.
TEST_F(AnalyzerTest,DotFileWasModified)546 TEST_F(AnalyzerTest, DotFileWasModified) {
547   std::unique_ptr<Target> t = MakeTarget("//dir", "target_name");
548   builder_.ItemDefined(std::move(t));
549 
550   RunAnalyzerTest(
551       R"({
552        "files": [ "//.gn" ],
553        "additional_compile_targets": [],
554        "test_targets": [ "//dir:target_name" ]
555       })",
556       "{"
557       R"("compile_targets":["//dir:target_name"],)"
558       R"/("status":"Found dependency (all)",)/"
559       R"("test_targets":["//dir:target_name"])"
560       "}");
561 }
562 
563 // Bails out early with "Found dependency (all)" if master build config file is
564 // modified.
TEST_F(AnalyzerTest,BuildConfigFileWasModified)565 TEST_F(AnalyzerTest, BuildConfigFileWasModified) {
566   std::unique_ptr<Target> t = MakeTarget("//dir", "target_name");
567   builder_.ItemDefined(std::move(t));
568 
569   RunAnalyzerTest(
570       R"({
571        "files": [ "//build/config/BUILDCONFIG.gn" ],
572        "additional_compile_targets": [],
573        "test_targets": [ "//dir:target_name" ]
574       })",
575       "{"
576       R"("compile_targets":["//dir:target_name"],)"
577       R"/("status":"Found dependency (all)",)/"
578       R"("test_targets":["//dir:target_name"])"
579       "}");
580 }
581 
582 // Bails out early with "Found dependency (all)" if a build args dependency file
583 // is modified.
TEST_F(AnalyzerTest,BuildArgsDependencyFileWasModified)584 TEST_F(AnalyzerTest, BuildArgsDependencyFileWasModified) {
585   std::unique_ptr<Target> t = MakeTarget("//dir", "target_name");
586   builder_.ItemDefined(std::move(t));
587 
588   RunAnalyzerTest(
589       R"({
590        "files": [ "//build/default_args.gn" ],
591        "additional_compile_targets": [],
592        "test_targets": [ "//dir:target_name" ]
593       })",
594       "{"
595       R"("compile_targets":["//dir:target_name"],)"
596       R"/("status":"Found dependency (all)",)/"
597       R"("test_targets":["//dir:target_name"])"
598       "}");
599 }
600 
601 }  // namespace gn_analyzer_unittest
602