Ninja
missing_deps_test.cc
Go to the documentation of this file.
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 
23 const char kTestDepsLogFilename[] = "MissingDepTest-tempdepslog";
24 
26  void OnMissingDep(Node* node, const std::string& path,
27  const Rule& generator) {}
28 };
29 
30 struct MissingDependencyScannerTest : public testing::Test {
32  : generator_rule_("generator_rule"), compile_rule_("compile_rule"),
34  std::string err;
36  EXPECT_EQ("", err);
37  }
38 
40  // Remove test file.
41  deps_log_.Close();
42  }
43 
45 
46  void RecordDepsLogDep(const std::string& from, const std::string& to) {
47  Node* node_deps[] = { state_.LookupNode(to) };
48  deps_log_.RecordDeps(state_.LookupNode(from), 0, 1, node_deps);
49  }
50 
51  void ProcessAllNodes() {
52  std::string err;
53  std::vector<Node*> nodes = state_.RootNodes(&err);
54  EXPECT_EQ("", err);
55  for (std::vector<Node*>::iterator it = nodes.begin(); it != nodes.end();
56  ++it) {
57  scanner().ProcessNode(*it);
58  }
59  }
60 
62  EvalString deps_type;
63  deps_type.AddText("gcc");
64  compile_rule_.AddBinding("deps", deps_type);
65  generator_rule_.AddBinding("deps", deps_type);
66  Edge* header_edge = state_.AddEdge(&generator_rule_);
67  state_.AddOut(header_edge, "generated_header", 0, nullptr);
68  Edge* compile_edge = state_.AddEdge(&compile_rule_);
69  state_.AddOut(compile_edge, "compiled_object", 0, nullptr);
70  }
71 
72  void CreateGraphDependencyBetween(const char* from, const char* to) {
73  Node* from_node = state_.LookupNode(from);
74  Edge* from_edge = from_node->in_edge();
75  state_.AddIn(from_edge, to, 0);
76  }
77 
78  void AssertMissingDependencyBetween(const char* flaky, const char* generated,
79  Rule* rule) {
80  Node* flaky_node = state_.LookupNode(flaky);
81  ASSERT_EQ(1u, scanner().nodes_missing_deps_.count(flaky_node));
82  Node* generated_node = state_.LookupNode(generated);
83  ASSERT_EQ(1u, scanner().generated_nodes_.count(generated_node));
84  ASSERT_EQ(1u, scanner().generator_rules_.count(rule));
85  }
86 
95 };
96 
98  ProcessAllNodes();
99  ASSERT_FALSE(scanner().HadMissingDeps());
100 }
101 
103  CreateInitialState();
104  ProcessAllNodes();
105  ASSERT_FALSE(scanner().HadMissingDeps());
106 }
107 
109  CreateInitialState();
110  // compiled_object uses generated_header, without a proper dependency
111  RecordDepsLogDep("compiled_object", "generated_header");
112  ProcessAllNodes();
113  ASSERT_TRUE(scanner().HadMissingDeps());
114  ASSERT_EQ(1u, scanner().nodes_missing_deps_.size());
115  ASSERT_EQ(1u, scanner().missing_dep_path_count_);
116  AssertMissingDependencyBetween("compiled_object", "generated_header",
117  &generator_rule_);
118 }
119 
120 TEST_F(MissingDependencyScannerTest, MissingDepFixedDirect) {
121  CreateInitialState();
122  // Adding the direct dependency fixes the missing dep
123  CreateGraphDependencyBetween("compiled_object", "generated_header");
124  RecordDepsLogDep("compiled_object", "generated_header");
125  ProcessAllNodes();
126  ASSERT_FALSE(scanner().HadMissingDeps());
127 }
128 
129 TEST_F(MissingDependencyScannerTest, MissingDepFixedIndirect) {
130  CreateInitialState();
131  // Adding an indirect dependency also fixes the issue
132  Edge* intermediate_edge = state_.AddEdge(&generator_rule_);
133  state_.AddOut(intermediate_edge, "intermediate", 0, nullptr);
134  CreateGraphDependencyBetween("compiled_object", "intermediate");
135  CreateGraphDependencyBetween("intermediate", "generated_header");
136  RecordDepsLogDep("compiled_object", "generated_header");
137  ProcessAllNodes();
138  ASSERT_FALSE(scanner().HadMissingDeps());
139 }
140 
142  CreateInitialState();
143  RecordDepsLogDep("generated_header", "compiled_object");
144  RecordDepsLogDep("compiled_object", "generated_header");
145  // In case of a cycle, both paths are reported (and there is
146  // no way to fix the issue by adding deps).
147  ProcessAllNodes();
148  ASSERT_TRUE(scanner().HadMissingDeps());
149  ASSERT_EQ(2u, scanner().nodes_missing_deps_.size());
150  ASSERT_EQ(2u, scanner().missing_dep_path_count_);
151  AssertMissingDependencyBetween("compiled_object", "generated_header",
152  &generator_rule_);
153  AssertMissingDependencyBetween("generated_header", "compiled_object",
154  &compile_rule_);
155 }
156 
158  CreateInitialState();
159  CreateGraphDependencyBetween("compiled_object", "generated_header");
160  CreateGraphDependencyBetween("generated_header", "compiled_object");
161  // The missing-deps tool doesn't deal with cycles in the graph, because
162  // there will be an error loading the graph before we get to the tool.
163  // This test is to illustrate that.
164  std::string err;
165  std::vector<Node*> nodes = state_.RootNodes(&err);
166  ASSERT_NE("", err);
167 }
void OnMissingDep(Node *node, const std::string &path, const Rule &generator)
const char kTestDepsLogFilename[]
TEST_F(MissingDependencyScannerTest, EmptyGraph)
As build commands run they can output extra dependency information (e.g.
Definition: deps_log.h:68
void Close()
Definition: deps_log.cc:147
bool OpenForWrite(const std::string &path, std::string *err)
Definition: deps_log.cc:51
bool RecordDeps(Node *node, TimeStamp mtime, const std::vector< Node * > &nodes)
An edge in the dependency graph; links between Nodes using Rules.
Definition: graph.h:175
A tokenized string that contains variable references.
Definition: eval_env.h:35
void AddText(StringPiece text)
Definition: eval_env.cc:126
MissingDependencyScanner scanner_
void CreateGraphDependencyBetween(const char *from, const char *to)
MissingDependencyScanner & scanner()
MissingDependencyTestDelegate delegate_
void AssertMissingDependencyBetween(const char *flaky, const char *generated, Rule *rule)
void RecordDepsLogDep(const std::string &from, const std::string &to)
void ProcessNode(Node *node)
Definition: missing_deps.cc:78
Information about a node in the dependency graph: the file, whether it's dirty, mtime,...
Definition: graph.h:42
Edge * in_edge() const
Definition: graph.h:100
An invocable build command and associated metadata (description, etc.).
Definition: eval_env.h:66
void AddBinding(const std::string &key, const EvalString &val)
Definition: eval_env.cc:55
A class that records a file path and ensures that it is removed on destruction.
Definition: test.h:106
Global state (file status) for a single run.
Definition: state.h:95
std::vector< Node * > RootNodes(std::string *error) const
Definition: state.cc:169
Edge * AddEdge(const Rule *rule)
Definition: state.cc:85
void AddIn(Edge *edge, StringPiece path, uint64_t slash_bits)
Add input / output / validation nodes to a given edge.
Definition: state.cc:128
Node * LookupNode(StringPiece path) const
Definition: state.cc:104
bool AddOut(Edge *edge, StringPiece path, uint64_t slash_bits, std::string *err)
Definition: state.cc:135
An implementation of DiskInterface that uses an in-memory representation of disk state.
Definition: test.h:51