1695b41eeSopenharmony_ci// Copyright 2019 Google Inc. All Rights Reserved.
2695b41eeSopenharmony_ci//
3695b41eeSopenharmony_ci// Licensed under the Apache License, Version 2.0 (the "License");
4695b41eeSopenharmony_ci// you may not use this file except in compliance with the License.
5695b41eeSopenharmony_ci// You may obtain a copy of the License at
6695b41eeSopenharmony_ci//
7695b41eeSopenharmony_ci//     http://www.apache.org/licenses/LICENSE-2.0
8695b41eeSopenharmony_ci//
9695b41eeSopenharmony_ci// Unless required by applicable law or agreed to in writing, software
10695b41eeSopenharmony_ci// distributed under the License is distributed on an "AS IS" BASIS,
11695b41eeSopenharmony_ci// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12695b41eeSopenharmony_ci// See the License for the specific language governing permissions and
13695b41eeSopenharmony_ci// limitations under the License.
14695b41eeSopenharmony_ci
15695b41eeSopenharmony_ci#include <memory>
16695b41eeSopenharmony_ci
17695b41eeSopenharmony_ci#include "deps_log.h"
18695b41eeSopenharmony_ci#include "graph.h"
19695b41eeSopenharmony_ci#include "missing_deps.h"
20695b41eeSopenharmony_ci#include "state.h"
21695b41eeSopenharmony_ci#include "test.h"
22695b41eeSopenharmony_ci
23695b41eeSopenharmony_ciconst char kTestDepsLogFilename[] = "MissingDepTest-tempdepslog";
24695b41eeSopenharmony_ci
25695b41eeSopenharmony_ciclass MissingDependencyTestDelegate : public MissingDependencyScannerDelegate {
26695b41eeSopenharmony_ci    void OnMissingDep(Node* node, const std::string& path,
27695b41eeSopenharmony_ci                                        const Rule& generator) {}
28695b41eeSopenharmony_ci};
29695b41eeSopenharmony_ci
30695b41eeSopenharmony_cistruct MissingDependencyScannerTest : public testing::Test {
31695b41eeSopenharmony_ci    MissingDependencyScannerTest()
32695b41eeSopenharmony_ci            : generator_rule_("generator_rule"), compile_rule_("compile_rule"),
33695b41eeSopenharmony_ci                scanner_(&delegate_, &deps_log_, &state_, &filesystem_) {
34695b41eeSopenharmony_ci        std::string err;
35695b41eeSopenharmony_ci        deps_log_.OpenForWrite(kTestDepsLogFilename, &err);
36695b41eeSopenharmony_ci        EXPECT_EQ("", err);
37695b41eeSopenharmony_ci    }
38695b41eeSopenharmony_ci
39695b41eeSopenharmony_ci    ~MissingDependencyScannerTest() {
40695b41eeSopenharmony_ci        // Remove test file.
41695b41eeSopenharmony_ci        deps_log_.Close();
42695b41eeSopenharmony_ci    }
43695b41eeSopenharmony_ci
44695b41eeSopenharmony_ci    MissingDependencyScanner& scanner() { return scanner_; }
45695b41eeSopenharmony_ci
46695b41eeSopenharmony_ci    void RecordDepsLogDep(const std::string& from, const std::string& to) {
47695b41eeSopenharmony_ci        Node* node_deps[] = { state_.LookupNode(to) };
48695b41eeSopenharmony_ci        deps_log_.RecordDeps(state_.LookupNode(from), 0, 1, node_deps);
49695b41eeSopenharmony_ci    }
50695b41eeSopenharmony_ci
51695b41eeSopenharmony_ci    void ProcessAllNodes() {
52695b41eeSopenharmony_ci        std::string err;
53695b41eeSopenharmony_ci        std::vector<Node*> nodes = state_.RootNodes(&err);
54695b41eeSopenharmony_ci        EXPECT_EQ("", err);
55695b41eeSopenharmony_ci        for (std::vector<Node*>::iterator it = nodes.begin(); it != nodes.end();
56695b41eeSopenharmony_ci                  ++it) {
57695b41eeSopenharmony_ci            scanner().ProcessNode(*it);
58695b41eeSopenharmony_ci        }
59695b41eeSopenharmony_ci    }
60695b41eeSopenharmony_ci
61695b41eeSopenharmony_ci    void CreateInitialState() {
62695b41eeSopenharmony_ci        EvalString deps_type;
63695b41eeSopenharmony_ci        deps_type.AddText("gcc");
64695b41eeSopenharmony_ci        compile_rule_.AddBinding("deps", deps_type);
65695b41eeSopenharmony_ci        generator_rule_.AddBinding("deps", deps_type);
66695b41eeSopenharmony_ci        Edge* header_edge = state_.AddEdge(&generator_rule_);
67695b41eeSopenharmony_ci        state_.AddOut(header_edge, "generated_header", 0);
68695b41eeSopenharmony_ci        Edge* compile_edge = state_.AddEdge(&compile_rule_);
69695b41eeSopenharmony_ci        state_.AddOut(compile_edge, "compiled_object", 0);
70695b41eeSopenharmony_ci    }
71695b41eeSopenharmony_ci
72695b41eeSopenharmony_ci    void CreateGraphDependencyBetween(const char* from, const char* to) {
73695b41eeSopenharmony_ci        Node* from_node = state_.LookupNode(from);
74695b41eeSopenharmony_ci        Edge* from_edge = from_node->in_edge();
75695b41eeSopenharmony_ci        state_.AddIn(from_edge, to, 0);
76695b41eeSopenharmony_ci    }
77695b41eeSopenharmony_ci
78695b41eeSopenharmony_ci    void AssertMissingDependencyBetween(const char* flaky, const char* generated,
79695b41eeSopenharmony_ci                                                                            Rule* rule) {
80695b41eeSopenharmony_ci        Node* flaky_node = state_.LookupNode(flaky);
81695b41eeSopenharmony_ci        ASSERT_EQ(1u, scanner().nodes_missing_deps_.count(flaky_node));
82695b41eeSopenharmony_ci        Node* generated_node = state_.LookupNode(generated);
83695b41eeSopenharmony_ci        ASSERT_EQ(1u, scanner().generated_nodes_.count(generated_node));
84695b41eeSopenharmony_ci        ASSERT_EQ(1u, scanner().generator_rules_.count(rule));
85695b41eeSopenharmony_ci    }
86695b41eeSopenharmony_ci
87695b41eeSopenharmony_ci    ScopedFilePath scoped_file_path_ = kTestDepsLogFilename;
88695b41eeSopenharmony_ci    MissingDependencyTestDelegate delegate_;
89695b41eeSopenharmony_ci    Rule generator_rule_;
90695b41eeSopenharmony_ci    Rule compile_rule_;
91695b41eeSopenharmony_ci    DepsLog deps_log_;
92695b41eeSopenharmony_ci    State state_;
93695b41eeSopenharmony_ci    VirtualFileSystem filesystem_;
94695b41eeSopenharmony_ci    MissingDependencyScanner scanner_;
95695b41eeSopenharmony_ci};
96695b41eeSopenharmony_ci
97695b41eeSopenharmony_ciTEST_F(MissingDependencyScannerTest, EmptyGraph) {
98695b41eeSopenharmony_ci    ProcessAllNodes();
99695b41eeSopenharmony_ci    ASSERT_FALSE(scanner().HadMissingDeps());
100695b41eeSopenharmony_ci}
101695b41eeSopenharmony_ci
102695b41eeSopenharmony_ciTEST_F(MissingDependencyScannerTest, NoMissingDep) {
103695b41eeSopenharmony_ci    CreateInitialState();
104695b41eeSopenharmony_ci    ProcessAllNodes();
105695b41eeSopenharmony_ci    ASSERT_FALSE(scanner().HadMissingDeps());
106695b41eeSopenharmony_ci}
107695b41eeSopenharmony_ci
108695b41eeSopenharmony_ciTEST_F(MissingDependencyScannerTest, MissingDepPresent) {
109695b41eeSopenharmony_ci    CreateInitialState();
110695b41eeSopenharmony_ci    // compiled_object uses generated_header, without a proper dependency
111695b41eeSopenharmony_ci    RecordDepsLogDep("compiled_object", "generated_header");
112695b41eeSopenharmony_ci    ProcessAllNodes();
113695b41eeSopenharmony_ci    ASSERT_TRUE(scanner().HadMissingDeps());
114695b41eeSopenharmony_ci    ASSERT_EQ(1u, scanner().nodes_missing_deps_.size());
115695b41eeSopenharmony_ci    ASSERT_EQ(1u, scanner().missing_dep_path_count_);
116695b41eeSopenharmony_ci    AssertMissingDependencyBetween("compiled_object", "generated_header",
117695b41eeSopenharmony_ci                                                                  &generator_rule_);
118695b41eeSopenharmony_ci}
119695b41eeSopenharmony_ci
120695b41eeSopenharmony_ciTEST_F(MissingDependencyScannerTest, MissingDepFixedDirect) {
121695b41eeSopenharmony_ci    CreateInitialState();
122695b41eeSopenharmony_ci    // Adding the direct dependency fixes the missing dep
123695b41eeSopenharmony_ci    CreateGraphDependencyBetween("compiled_object", "generated_header");
124695b41eeSopenharmony_ci    RecordDepsLogDep("compiled_object", "generated_header");
125695b41eeSopenharmony_ci    ProcessAllNodes();
126695b41eeSopenharmony_ci    ASSERT_FALSE(scanner().HadMissingDeps());
127695b41eeSopenharmony_ci}
128695b41eeSopenharmony_ci
129695b41eeSopenharmony_ciTEST_F(MissingDependencyScannerTest, MissingDepFixedIndirect) {
130695b41eeSopenharmony_ci    CreateInitialState();
131695b41eeSopenharmony_ci    // Adding an indirect dependency also fixes the issue
132695b41eeSopenharmony_ci    Edge* intermediate_edge = state_.AddEdge(&generator_rule_);
133695b41eeSopenharmony_ci    state_.AddOut(intermediate_edge, "intermediate", 0);
134695b41eeSopenharmony_ci    CreateGraphDependencyBetween("compiled_object", "intermediate");
135695b41eeSopenharmony_ci    CreateGraphDependencyBetween("intermediate", "generated_header");
136695b41eeSopenharmony_ci    RecordDepsLogDep("compiled_object", "generated_header");
137695b41eeSopenharmony_ci    ProcessAllNodes();
138695b41eeSopenharmony_ci    ASSERT_FALSE(scanner().HadMissingDeps());
139695b41eeSopenharmony_ci}
140695b41eeSopenharmony_ci
141695b41eeSopenharmony_ciTEST_F(MissingDependencyScannerTest, CyclicMissingDep) {
142695b41eeSopenharmony_ci    CreateInitialState();
143695b41eeSopenharmony_ci    RecordDepsLogDep("generated_header", "compiled_object");
144695b41eeSopenharmony_ci    RecordDepsLogDep("compiled_object", "generated_header");
145695b41eeSopenharmony_ci    // In case of a cycle, both paths are reported (and there is
146695b41eeSopenharmony_ci    // no way to fix the issue by adding deps).
147695b41eeSopenharmony_ci    ProcessAllNodes();
148695b41eeSopenharmony_ci    ASSERT_TRUE(scanner().HadMissingDeps());
149695b41eeSopenharmony_ci    ASSERT_EQ(2u, scanner().nodes_missing_deps_.size());
150695b41eeSopenharmony_ci    ASSERT_EQ(2u, scanner().missing_dep_path_count_);
151695b41eeSopenharmony_ci    AssertMissingDependencyBetween("compiled_object", "generated_header",
152695b41eeSopenharmony_ci                                                                  &generator_rule_);
153695b41eeSopenharmony_ci    AssertMissingDependencyBetween("generated_header", "compiled_object",
154695b41eeSopenharmony_ci                                                                  &compile_rule_);
155695b41eeSopenharmony_ci}
156695b41eeSopenharmony_ci
157695b41eeSopenharmony_ciTEST_F(MissingDependencyScannerTest, CycleInGraph) {
158695b41eeSopenharmony_ci    CreateInitialState();
159695b41eeSopenharmony_ci    CreateGraphDependencyBetween("compiled_object", "generated_header");
160695b41eeSopenharmony_ci    CreateGraphDependencyBetween("generated_header", "compiled_object");
161695b41eeSopenharmony_ci    // The missing-deps tool doesn't deal with cycles in the graph, because
162695b41eeSopenharmony_ci    // there will be an error loading the graph before we get to the tool.
163695b41eeSopenharmony_ci    // This test is to illustrate that.
164695b41eeSopenharmony_ci    std::string err;
165695b41eeSopenharmony_ci    std::vector<Node*> nodes = state_.RootNodes(&err);
166695b41eeSopenharmony_ci    ASSERT_NE("", err);
167695b41eeSopenharmony_ci}
168