1// Copyright 2019 Google Inc. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include <memory>
16
17#include "deps_log.h"
18#include "graph.h"
19#include "missing_deps.h"
20#include "state.h"
21#include "test.h"
22
23const char kTestDepsLogFilename[] = "MissingDepTest-tempdepslog";
24
25class MissingDependencyTestDelegate : public MissingDependencyScannerDelegate {
26 void OnMissingDep(Node* node, const std::string& path,
27 const Rule& generator) {}
28};
29
30struct MissingDependencyScannerTest : public testing::Test {
31 MissingDependencyScannerTest()
32 : generator_rule_("generator_rule"), compile_rule_("compile_rule"),
33 scanner_(&delegate_, &deps_log_, &state_, &filesystem_) {
34 std::string err;
35 deps_log_.OpenForWrite(kTestDepsLogFilename, &err);
36 ASSERT_EQ("", err);
37 }
38
39 MissingDependencyScanner& scanner() { return scanner_; }
40
41 void RecordDepsLogDep(const std::string& from, const std::string& to) {
42 Node* node_deps[] = { state_.LookupNode(to) };
43 deps_log_.RecordDeps(state_.LookupNode(from), 0, 1, node_deps);
44 }
45
46 void ProcessAllNodes() {
47 std::string err;
48 std::vector<Node*> nodes = state_.RootNodes(&err);
49 EXPECT_EQ("", err);
50 for (std::vector<Node*>::iterator it = nodes.begin(); it != nodes.end();
51 ++it) {
52 scanner().ProcessNode(*it);
53 }
54 }
55
56 void CreateInitialState() {
57 EvalString deps_type;
58 deps_type.AddText("gcc");
59 compile_rule_.AddBinding("deps", deps_type);
60 generator_rule_.AddBinding("deps", deps_type);
61 Edge* header_edge = state_.AddEdge(&generator_rule_);
62 state_.AddOut(header_edge, "generated_header", 0);
63 Edge* compile_edge = state_.AddEdge(&compile_rule_);
64 state_.AddOut(compile_edge, "compiled_object", 0);
65 }
66
67 void CreateGraphDependencyBetween(const char* from, const char* to) {
68 Node* from_node = state_.LookupNode(from);
69 Edge* from_edge = from_node->in_edge();
70 state_.AddIn(from_edge, to, 0);
71 }
72
73 void AssertMissingDependencyBetween(const char* flaky, const char* generated,
74 Rule* rule) {
75 Node* flaky_node = state_.LookupNode(flaky);
76 ASSERT_EQ(1u, scanner().nodes_missing_deps_.count(flaky_node));
77 Node* generated_node = state_.LookupNode(generated);
78 ASSERT_EQ(1u, scanner().generated_nodes_.count(generated_node));
79 ASSERT_EQ(1u, scanner().generator_rules_.count(rule));
80 }
81
82 MissingDependencyTestDelegate delegate_;
83 Rule generator_rule_;
84 Rule compile_rule_;
85 DepsLog deps_log_;
86 State state_;
87 VirtualFileSystem filesystem_;
88 MissingDependencyScanner scanner_;
89};
90
91TEST_F(MissingDependencyScannerTest, EmptyGraph) {
92 ProcessAllNodes();
93 ASSERT_FALSE(scanner().HadMissingDeps());
94}
95
96TEST_F(MissingDependencyScannerTest, NoMissingDep) {
97 CreateInitialState();
98 ProcessAllNodes();
99 ASSERT_FALSE(scanner().HadMissingDeps());
100}
101
102TEST_F(MissingDependencyScannerTest, MissingDepPresent) {
103 CreateInitialState();
104 // compiled_object uses generated_header, without a proper dependency
105 RecordDepsLogDep("compiled_object", "generated_header");
106 ProcessAllNodes();
107 ASSERT_TRUE(scanner().HadMissingDeps());
108 ASSERT_EQ(1u, scanner().nodes_missing_deps_.size());
109 ASSERT_EQ(1u, scanner().missing_dep_path_count_);
110 AssertMissingDependencyBetween("compiled_object", "generated_header",
111 &generator_rule_);
112}
113
114TEST_F(MissingDependencyScannerTest, MissingDepFixedDirect) {
115 CreateInitialState();
116 // Adding the direct dependency fixes the missing dep
117 CreateGraphDependencyBetween("compiled_object", "generated_header");
118 RecordDepsLogDep("compiled_object", "generated_header");
119 ProcessAllNodes();
120 ASSERT_FALSE(scanner().HadMissingDeps());
121}
122
123TEST_F(MissingDependencyScannerTest, MissingDepFixedIndirect) {
124 CreateInitialState();
125 // Adding an indirect dependency also fixes the issue
126 Edge* intermediate_edge = state_.AddEdge(&generator_rule_);
127 state_.AddOut(intermediate_edge, "intermediate", 0);
128 CreateGraphDependencyBetween("compiled_object", "intermediate");
129 CreateGraphDependencyBetween("intermediate", "generated_header");
130 RecordDepsLogDep("compiled_object", "generated_header");
131 ProcessAllNodes();
132 ASSERT_FALSE(scanner().HadMissingDeps());
133}
134
135TEST_F(MissingDependencyScannerTest, CyclicMissingDep) {
136 CreateInitialState();
137 RecordDepsLogDep("generated_header", "compiled_object");
138 RecordDepsLogDep("compiled_object", "generated_header");
139 // In case of a cycle, both paths are reported (and there is
140 // no way to fix the issue by adding deps).
141 ProcessAllNodes();
142 ASSERT_TRUE(scanner().HadMissingDeps());
143 ASSERT_EQ(2u, scanner().nodes_missing_deps_.size());
144 ASSERT_EQ(2u, scanner().missing_dep_path_count_);
145 AssertMissingDependencyBetween("compiled_object", "generated_header",
146 &generator_rule_);
147 AssertMissingDependencyBetween("generated_header", "compiled_object",
148 &compile_rule_);
149}
150
151TEST_F(MissingDependencyScannerTest, CycleInGraph) {
152 CreateInitialState();
153 CreateGraphDependencyBetween("compiled_object", "generated_header");
154 CreateGraphDependencyBetween("generated_header", "compiled_object");
155 // The missing-deps tool doesn't deal with cycles in the graph, because
156 // there will be an error loading the graph before we get to the tool.
157 // This test is to illustrate that.
158 std::string err;
159 std::vector<Node*> nodes = state_.RootNodes(&err);
160 ASSERT_NE("", err);
161}
162
163