Add support for build statement implicit outputs
[platform/upstream/ninja.git] / src / graph_test.cc
index 382d352..723e8ea 100644 (file)
@@ -105,6 +105,50 @@ TEST_F(GraphTest, ExplicitImplicit) {
   EXPECT_TRUE(GetNode("out.o")->dirty());
 }
 
+TEST_F(GraphTest, ImplicitOutputParse) {
+  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
+"build out | out.imp: cat in\n"));
+
+  Edge* edge = GetNode("out")->in_edge();
+  EXPECT_EQ(2, edge->outputs_.size());
+  EXPECT_EQ("out", edge->outputs_[0]->path());
+  EXPECT_EQ("out.imp", edge->outputs_[1]->path());
+  EXPECT_EQ(1, edge->implicit_outs_);
+  EXPECT_EQ(edge, GetNode("out.imp")->in_edge());
+}
+
+TEST_F(GraphTest, ImplicitOutputMissing) {
+  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
+"build out | out.imp: cat in\n"));
+  fs_.Create("in", "");
+  fs_.Create("out", "");
+
+  Edge* edge = GetNode("out")->in_edge();
+  string err;
+  EXPECT_TRUE(scan_.RecomputeDirty(edge, &err));
+  ASSERT_EQ("", err);
+
+  EXPECT_TRUE(GetNode("out")->dirty());
+  EXPECT_TRUE(GetNode("out.imp")->dirty());
+}
+
+TEST_F(GraphTest, ImplicitOutputOutOfDate) {
+  ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
+"build out | out.imp: cat in\n"));
+  fs_.Create("out.imp", "");
+  fs_.Tick();
+  fs_.Create("in", "");
+  fs_.Create("out", "");
+
+  Edge* edge = GetNode("out")->in_edge();
+  string err;
+  EXPECT_TRUE(scan_.RecomputeDirty(edge, &err));
+  ASSERT_EQ("", err);
+
+  EXPECT_TRUE(GetNode("out")->dirty());
+  EXPECT_TRUE(GetNode("out.imp")->dirty());
+}
+
 TEST_F(GraphTest, PathWithCurrentDirectory) {
   ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
 "rule catdep\n"
@@ -153,7 +197,7 @@ TEST_F(GraphTest, VarInOutPathEscaping) {
 #endif
 }
 
-// Regression test for https://github.com/martine/ninja/issues/380
+// Regression test for https://github.com/ninja-build/ninja/issues/380
 TEST_F(GraphTest, DepfileWithCanonicalizablePath) {
   ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
 "rule catdep\n"
@@ -172,7 +216,7 @@ TEST_F(GraphTest, DepfileWithCanonicalizablePath) {
   EXPECT_FALSE(GetNode("out.o")->dirty());
 }
 
-// Regression test for https://github.com/martine/ninja/issues/404
+// Regression test for https://github.com/ninja-build/ninja/issues/404
 TEST_F(GraphTest, DepfileRemoved) {
   ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
 "rule catdep\n"
@@ -252,6 +296,81 @@ TEST_F(GraphTest, NestedPhonyPrintsDone) {
   ASSERT_FALSE(plan_.more_to_do());
 }
 
+// Verify that cycles in graphs with multiple outputs are handled correctly
+// in RecomputeDirty() and don't cause deps to be loaded multiple times.
+TEST_F(GraphTest, CycleWithLengthZeroFromDepfile) {
+  AssertParse(&state_,
+"rule deprule\n"
+"   depfile = dep.d\n"
+"   command = unused\n"
+"build a b: deprule\n"
+  );
+  fs_.Create("dep.d", "a: b\n");
+
+  string err;
+  Edge* edge = GetNode("a")->in_edge();
+  EXPECT_TRUE(scan_.RecomputeDirty(edge, &err));
+  ASSERT_EQ("", err);
+
+  // Despite the depfile causing edge to be a cycle (it has outputs a and b,
+  // but the depfile also adds b as an input), the deps should have been loaded
+  // only once:
+  EXPECT_EQ(1, edge->inputs_.size());
+  EXPECT_EQ("b", edge->inputs_[0]->path());
+}
+
+// Like CycleWithLengthZeroFromDepfile but with a higher cycle length.
+TEST_F(GraphTest, CycleWithLengthOneFromDepfile) {
+  AssertParse(&state_,
+"rule deprule\n"
+"   depfile = dep.d\n"
+"   command = unused\n"
+"rule r\n"
+"   command = unused\n"
+"build a b: deprule\n"
+"build c: r b\n"
+  );
+  fs_.Create("dep.d", "a: c\n");
+
+  string err;
+  Edge* edge = GetNode("a")->in_edge();
+  EXPECT_TRUE(scan_.RecomputeDirty(edge, &err));
+  ASSERT_EQ("", err);
+
+  // Despite the depfile causing edge to be a cycle (|edge| has outputs a and b,
+  // but c's in_edge has b as input but the depfile also adds |edge| as
+  // output)), the deps should have been loaded only once:
+  EXPECT_EQ(1, edge->inputs_.size());
+  EXPECT_EQ("c", edge->inputs_[0]->path());
+}
+
+// Like CycleWithLengthOneFromDepfile but building a node one hop away from
+// the cycle.
+TEST_F(GraphTest, CycleWithLengthOneFromDepfileOneHopAway) {
+  AssertParse(&state_,
+"rule deprule\n"
+"   depfile = dep.d\n"
+"   command = unused\n"
+"rule r\n"
+"   command = unused\n"
+"build a b: deprule\n"
+"build c: r b\n"
+"build d: r a\n"
+  );
+  fs_.Create("dep.d", "a: c\n");
+
+  string err;
+  EXPECT_TRUE(scan_.RecomputeDirty(GetNode("d")->in_edge(), &err));
+  ASSERT_EQ("", err);
+
+  // Despite the depfile causing edge to be a cycle (|edge| has outputs a and b,
+  // but c's in_edge has b as input but the depfile also adds |edge| as
+  // output)), the deps should have been loaded only once:
+  Edge* edge = GetNode("a")->in_edge();
+  EXPECT_EQ(1, edge->inputs_.size());
+  EXPECT_EQ("c", edge->inputs_[0]->path());
+}
+
 #ifdef _WIN32
 TEST_F(GraphTest, Decanonicalize) {
   ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,