summaryrefslogtreecommitdiff
path: root/src/missing_deps_test.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/missing_deps_test.cc')
-rw-r--r--src/missing_deps_test.cc162
1 files changed, 162 insertions, 0 deletions
diff --git a/src/missing_deps_test.cc b/src/missing_deps_test.cc
new file mode 100644
index 0000000..db66885
--- /dev/null
+++ b/src/missing_deps_test.cc
@@ -0,0 +1,162 @@
+// Copyright 2019 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <memory>
+
+#include "deps_log.h"
+#include "graph.h"
+#include "missing_deps.h"
+#include "state.h"
+#include "test.h"
+
+const char kTestDepsLogFilename[] = "MissingDepTest-tempdepslog";
+
+class MissingDependencyTestDelegate : public MissingDependencyScannerDelegate {
+ void OnMissingDep(Node* node, const std::string& path,
+ const Rule& generator) {}
+};
+
+struct MissingDependencyScannerTest : public testing::Test {
+ MissingDependencyScannerTest()
+ : generator_rule_("generator_rule"), compile_rule_("compile_rule"),
+ scanner_(&delegate_, &deps_log_, &state_, &filesystem_) {
+ std::string err;
+ deps_log_.OpenForWrite(kTestDepsLogFilename, &err);
+ ASSERT_EQ("", err);
+ }
+
+ MissingDependencyScanner& scanner() { return scanner_; }
+
+ void RecordDepsLogDep(const std::string& from, const std::string& to) {
+ Node* node_deps[] = { state_.LookupNode(to) };
+ deps_log_.RecordDeps(state_.LookupNode(from), 0, 1, node_deps);
+ }
+
+ void ProcessAllNodes() {
+ std::string err;
+ std::vector<Node*> nodes = state_.RootNodes(&err);
+ EXPECT_EQ("", err);
+ for (std::vector<Node*>::iterator it = nodes.begin(); it != nodes.end();
+ ++it) {
+ scanner().ProcessNode(*it);
+ }
+ }
+
+ void CreateInitialState() {
+ EvalString deps_type;
+ deps_type.AddText("gcc");
+ compile_rule_.AddBinding("deps", deps_type);
+ generator_rule_.AddBinding("deps", deps_type);
+ Edge* header_edge = state_.AddEdge(&generator_rule_);
+ state_.AddOut(header_edge, "generated_header", 0);
+ Edge* compile_edge = state_.AddEdge(&compile_rule_);
+ state_.AddOut(compile_edge, "compiled_object", 0);
+ }
+
+ void CreateGraphDependencyBetween(const char* from, const char* to) {
+ Node* from_node = state_.LookupNode(from);
+ Edge* from_edge = from_node->in_edge();
+ state_.AddIn(from_edge, to, 0);
+ }
+
+ void AssertMissingDependencyBetween(const char* flaky, const char* generated,
+ Rule* rule) {
+ Node* flaky_node = state_.LookupNode(flaky);
+ ASSERT_EQ(1u, scanner().nodes_missing_deps_.count(flaky_node));
+ Node* generated_node = state_.LookupNode(generated);
+ ASSERT_EQ(1u, scanner().generated_nodes_.count(generated_node));
+ ASSERT_EQ(1u, scanner().generator_rules_.count(rule));
+ }
+
+ MissingDependencyTestDelegate delegate_;
+ Rule generator_rule_;
+ Rule compile_rule_;
+ DepsLog deps_log_;
+ State state_;
+ VirtualFileSystem filesystem_;
+ MissingDependencyScanner scanner_;
+};
+
+TEST_F(MissingDependencyScannerTest, EmptyGraph) {
+ ProcessAllNodes();
+ ASSERT_FALSE(scanner().HadMissingDeps());
+}
+
+TEST_F(MissingDependencyScannerTest, NoMissingDep) {
+ CreateInitialState();
+ ProcessAllNodes();
+ ASSERT_FALSE(scanner().HadMissingDeps());
+}
+
+TEST_F(MissingDependencyScannerTest, MissingDepPresent) {
+ CreateInitialState();
+ // compiled_object uses generated_header, without a proper dependency
+ RecordDepsLogDep("compiled_object", "generated_header");
+ ProcessAllNodes();
+ ASSERT_TRUE(scanner().HadMissingDeps());
+ ASSERT_EQ(1u, scanner().nodes_missing_deps_.size());
+ ASSERT_EQ(1u, scanner().missing_dep_path_count_);
+ AssertMissingDependencyBetween("compiled_object", "generated_header",
+ &generator_rule_);
+}
+
+TEST_F(MissingDependencyScannerTest, MissingDepFixedDirect) {
+ CreateInitialState();
+ // Adding the direct dependency fixes the missing dep
+ CreateGraphDependencyBetween("compiled_object", "generated_header");
+ RecordDepsLogDep("compiled_object", "generated_header");
+ ProcessAllNodes();
+ ASSERT_FALSE(scanner().HadMissingDeps());
+}
+
+TEST_F(MissingDependencyScannerTest, MissingDepFixedIndirect) {
+ CreateInitialState();
+ // Adding an indirect dependency also fixes the issue
+ Edge* intermediate_edge = state_.AddEdge(&generator_rule_);
+ state_.AddOut(intermediate_edge, "intermediate", 0);
+ CreateGraphDependencyBetween("compiled_object", "intermediate");
+ CreateGraphDependencyBetween("intermediate", "generated_header");
+ RecordDepsLogDep("compiled_object", "generated_header");
+ ProcessAllNodes();
+ ASSERT_FALSE(scanner().HadMissingDeps());
+}
+
+TEST_F(MissingDependencyScannerTest, CyclicMissingDep) {
+ CreateInitialState();
+ RecordDepsLogDep("generated_header", "compiled_object");
+ RecordDepsLogDep("compiled_object", "generated_header");
+ // In case of a cycle, both paths are reported (and there is
+ // no way to fix the issue by adding deps).
+ ProcessAllNodes();
+ ASSERT_TRUE(scanner().HadMissingDeps());
+ ASSERT_EQ(2u, scanner().nodes_missing_deps_.size());
+ ASSERT_EQ(2u, scanner().missing_dep_path_count_);
+ AssertMissingDependencyBetween("compiled_object", "generated_header",
+ &generator_rule_);
+ AssertMissingDependencyBetween("generated_header", "compiled_object",
+ &compile_rule_);
+}
+
+TEST_F(MissingDependencyScannerTest, CycleInGraph) {
+ CreateInitialState();
+ CreateGraphDependencyBetween("compiled_object", "generated_header");
+ CreateGraphDependencyBetween("generated_header", "compiled_object");
+ // The missing-deps tool doesn't deal with cycles in the graph, because
+ // there will be an error loading the graph before we get to the tool.
+ // This test is to illustrate that.
+ std::string err;
+ std::vector<Node*> nodes = state_.RootNodes(&err);
+ ASSERT_NE("", err);
+}
+