github.com/maruel/nin@v0.0.0-20220112143044-f35891e3ce7e/graph_test.go (about)

     1  // Copyright 2011 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  package nin
    16  
    17  import (
    18  	"runtime"
    19  	"testing"
    20  )
    21  
    22  type GraphTest struct {
    23  	StateTestWithBuiltinRules
    24  	fs   VirtualFileSystem
    25  	scan DependencyScan
    26  }
    27  
    28  func NewGraphTest(t *testing.T) GraphTest {
    29  	g := GraphTest{
    30  		StateTestWithBuiltinRules: NewStateTestWithBuiltinRules(t),
    31  		fs:                        NewVirtualFileSystem(),
    32  	}
    33  	g.scan = NewDependencyScan(&g.state, nil, nil, &g.fs)
    34  	return g
    35  }
    36  
    37  func TestGraphTest_MissingImplicit(t *testing.T) {
    38  	g := NewGraphTest(t)
    39  	g.AssertParse(&g.state, "build out: cat in | implicit\n", ParseManifestOpts{})
    40  	g.fs.Create("in", "")
    41  	g.fs.Create("out", "")
    42  
    43  	if _, err := g.scan.RecomputeDirty(g.GetNode("out")); err != nil {
    44  		t.Fatal(err)
    45  	}
    46  
    47  	// A missing implicit dep *should* make the output dirty.
    48  	// (In fact, a build will fail.)
    49  	// This is a change from prior semantics of ninja.
    50  	if !g.GetNode("out").Dirty {
    51  		t.Fatal("expected true")
    52  	}
    53  }
    54  
    55  func TestGraphTest_ModifiedImplicit(t *testing.T) {
    56  	g := NewGraphTest(t)
    57  	g.AssertParse(&g.state, "build out: cat in | implicit\n", ParseManifestOpts{})
    58  	g.fs.Create("in", "")
    59  	g.fs.Create("out", "")
    60  	g.fs.Tick()
    61  	g.fs.Create("implicit", "")
    62  
    63  	if _, err := g.scan.RecomputeDirty(g.GetNode("out")); err != nil {
    64  		t.Fatal(err)
    65  	}
    66  
    67  	// A modified implicit dep should make the output dirty.
    68  	if !g.GetNode("out").Dirty {
    69  		t.Fatal("expected true")
    70  	}
    71  }
    72  
    73  func TestGraphTest_FunkyMakefilePath(t *testing.T) {
    74  	g := NewGraphTest(t)
    75  	g.AssertParse(&g.state, "rule catdep\n  depfile = $out.d\n  command = cat $in > $out\nbuild out.o: catdep foo.cc\n", ParseManifestOpts{})
    76  	g.fs.Create("foo.cc", "")
    77  	g.fs.Create("out.o.d", "out.o: ./foo/../implicit.h\n")
    78  	g.fs.Create("out.o", "")
    79  	g.fs.Tick()
    80  	g.fs.Create("implicit.h", "")
    81  
    82  	if _, err := g.scan.RecomputeDirty(g.GetNode("out.o")); err != nil {
    83  		t.Fatal(err)
    84  	}
    85  
    86  	// implicit.h has changed, though our depfile refers to it with a
    87  	// non-canonical path; we should still find it.
    88  	if !g.GetNode("out.o").Dirty {
    89  		t.Fatal("expected true")
    90  	}
    91  }
    92  
    93  func TestGraphTest_ExplicitImplicit(t *testing.T) {
    94  	g := NewGraphTest(t)
    95  	g.AssertParse(&g.state, "rule catdep\n  depfile = $out.d\n  command = cat $in > $out\nbuild implicit.h: cat data\nbuild out.o: catdep foo.cc || implicit.h\n", ParseManifestOpts{})
    96  	g.fs.Create("implicit.h", "")
    97  	g.fs.Create("foo.cc", "")
    98  	g.fs.Create("out.o.d", "out.o: implicit.h\n")
    99  	g.fs.Create("out.o", "")
   100  	g.fs.Tick()
   101  	g.fs.Create("data", "")
   102  
   103  	if _, err := g.scan.RecomputeDirty(g.GetNode("out.o")); err != nil {
   104  		t.Fatal(err)
   105  	}
   106  
   107  	// We have both an implicit and an explicit dep on implicit.h.
   108  	// The implicit dep should "win" (in the sense that it should cause
   109  	// the output to be dirty).
   110  	if !g.GetNode("out.o").Dirty {
   111  		t.Fatal("expected true")
   112  	}
   113  }
   114  
   115  func TestGraphTest_ImplicitOutputParse(t *testing.T) {
   116  	g := NewGraphTest(t)
   117  	g.AssertParse(&g.state, "build out | out.imp: cat in\n", ParseManifestOpts{})
   118  
   119  	edge := g.GetNode("out").InEdge
   120  	if 2 != len(edge.Outputs) {
   121  		t.Fatal("expected equal")
   122  	}
   123  	if "out" != edge.Outputs[0].Path {
   124  		t.Fatal("expected equal")
   125  	}
   126  	if "out.imp" != edge.Outputs[1].Path {
   127  		t.Fatal("expected equal")
   128  	}
   129  	if 1 != edge.ImplicitOuts {
   130  		t.Fatal("expected equal")
   131  	}
   132  	if edge != g.GetNode("out.imp").InEdge {
   133  		t.Fatal("expected equal")
   134  	}
   135  }
   136  
   137  func TestGraphTest_ImplicitOutputMissing(t *testing.T) {
   138  	g := NewGraphTest(t)
   139  	g.AssertParse(&g.state, "build out | out.imp: cat in\n", ParseManifestOpts{})
   140  	g.fs.Create("in", "")
   141  	g.fs.Create("out", "")
   142  
   143  	if _, err := g.scan.RecomputeDirty(g.GetNode("out")); err != nil {
   144  		t.Fatal(err)
   145  	}
   146  
   147  	if !g.GetNode("out").Dirty {
   148  		t.Fatal("expected true")
   149  	}
   150  	if !g.GetNode("out.imp").Dirty {
   151  		t.Fatal("expected true")
   152  	}
   153  }
   154  
   155  func TestGraphTest_ImplicitOutputOutOfDate(t *testing.T) {
   156  	g := NewGraphTest(t)
   157  	g.AssertParse(&g.state, "build out | out.imp: cat in\n", ParseManifestOpts{})
   158  	g.fs.Create("out.imp", "")
   159  	g.fs.Tick()
   160  	g.fs.Create("in", "")
   161  	g.fs.Create("out", "")
   162  
   163  	if _, err := g.scan.RecomputeDirty(g.GetNode("out")); err != nil {
   164  		t.Fatal(err)
   165  	}
   166  
   167  	if !g.GetNode("out").Dirty {
   168  		t.Fatal("expected true")
   169  	}
   170  	if !g.GetNode("out.imp").Dirty {
   171  		t.Fatal("expected true")
   172  	}
   173  }
   174  
   175  func TestGraphTest_ImplicitOutputOnlyParse(t *testing.T) {
   176  	g := NewGraphTest(t)
   177  	g.AssertParse(&g.state, "build | out.imp: cat in\n", ParseManifestOpts{})
   178  
   179  	edge := g.GetNode("out.imp").InEdge
   180  	if 1 != len(edge.Outputs) {
   181  		t.Fatal("expected equal")
   182  	}
   183  	if "out.imp" != edge.Outputs[0].Path {
   184  		t.Fatal("expected equal")
   185  	}
   186  	if 1 != edge.ImplicitOuts {
   187  		t.Fatal("expected equal")
   188  	}
   189  	if edge != g.GetNode("out.imp").InEdge {
   190  		t.Fatal("expected equal")
   191  	}
   192  }
   193  
   194  func TestGraphTest_ImplicitOutputOnlyMissing(t *testing.T) {
   195  	g := NewGraphTest(t)
   196  	g.AssertParse(&g.state, "build | out.imp: cat in\n", ParseManifestOpts{})
   197  	g.fs.Create("in", "")
   198  
   199  	if _, err := g.scan.RecomputeDirty(g.GetNode("out.imp")); err != nil {
   200  		t.Fatal(err)
   201  	}
   202  
   203  	if !g.GetNode("out.imp").Dirty {
   204  		t.Fatal("expected true")
   205  	}
   206  }
   207  
   208  func TestGraphTest_ImplicitOutputOnlyOutOfDate(t *testing.T) {
   209  	g := NewGraphTest(t)
   210  	g.AssertParse(&g.state, "build | out.imp: cat in\n", ParseManifestOpts{})
   211  	g.fs.Create("out.imp", "")
   212  	g.fs.Tick()
   213  	g.fs.Create("in", "")
   214  
   215  	if _, err := g.scan.RecomputeDirty(g.GetNode("out.imp")); err != nil {
   216  		t.Fatal(err)
   217  	}
   218  
   219  	if !g.GetNode("out.imp").Dirty {
   220  		t.Fatal("expected true")
   221  	}
   222  }
   223  
   224  func TestGraphTest_PathWithCurrentDirectory(t *testing.T) {
   225  	g := NewGraphTest(t)
   226  	g.AssertParse(&g.state, "rule catdep\n  depfile = $out.d\n  command = cat $in > $out\nbuild ./out.o: catdep ./foo.cc\n", ParseManifestOpts{})
   227  	g.fs.Create("foo.cc", "")
   228  	g.fs.Create("out.o.d", "out.o: foo.cc\n")
   229  	g.fs.Create("out.o", "")
   230  
   231  	if _, err := g.scan.RecomputeDirty(g.GetNode("out.o")); err != nil {
   232  		t.Fatal(err)
   233  	}
   234  
   235  	if g.GetNode("out.o").Dirty {
   236  		t.Fatal("expected false")
   237  	}
   238  }
   239  
   240  func TestGraphTest_RootNodes(t *testing.T) {
   241  	g := NewGraphTest(t)
   242  	g.AssertParse(&g.state, "build out1: cat in1\nbuild mid1: cat in1\nbuild out2: cat mid1\nbuild out3 out4: cat mid1\n", ParseManifestOpts{})
   243  
   244  	rootNodes := g.state.RootNodes()
   245  	if 4 != len(rootNodes) {
   246  		t.Fatal("expected equal")
   247  	}
   248  	for i := 0; i < len(rootNodes); i++ {
   249  		name := rootNodes[i].Path
   250  		if "out" != name[0:3] {
   251  			t.Fatal("expected equal")
   252  		}
   253  	}
   254  }
   255  
   256  func TestGraphTest_VarInOutPathEscaping(t *testing.T) {
   257  	g := NewGraphTest(t)
   258  	g.AssertParse(&g.state, "build a$ b: cat no'space with$ space$$ no\"space2\n", ParseManifestOpts{})
   259  
   260  	edge := g.GetNode("a b").InEdge
   261  	want := "cat 'no'\\''space' 'with space$' 'no\"space2' > 'a b'"
   262  	if runtime.GOOS == "windows" {
   263  		want = "cat no'space \"with space$\" \"no\\\"space2\" > \"a b\""
   264  	}
   265  	if got := edge.EvaluateCommand(false); want != got {
   266  		t.Fatalf("want %q, got %q", want, got)
   267  	}
   268  }
   269  
   270  // Regression test for https://github.com/ninja-build/ninja/issues/380
   271  func TestGraphTest_DepfileWithCanonicalizablePath(t *testing.T) {
   272  	g := NewGraphTest(t)
   273  	g.AssertParse(&g.state, "rule catdep\n  depfile = $out.d\n  command = cat $in > $out\nbuild ./out.o: catdep ./foo.cc\n", ParseManifestOpts{})
   274  	g.fs.Create("foo.cc", "")
   275  	g.fs.Create("out.o.d", "out.o: bar/../foo.cc\n")
   276  	g.fs.Create("out.o", "")
   277  
   278  	if _, err := g.scan.RecomputeDirty(g.GetNode("out.o")); err != nil {
   279  		t.Fatal(err)
   280  	}
   281  
   282  	if g.GetNode("out.o").Dirty {
   283  		t.Fatal("expected false")
   284  	}
   285  }
   286  
   287  // Regression test for https://github.com/ninja-build/ninja/issues/404
   288  func TestGraphTest_DepfileRemoved(t *testing.T) {
   289  	g := NewGraphTest(t)
   290  	g.AssertParse(&g.state, "rule catdep\n  depfile = $out.d\n  command = cat $in > $out\nbuild ./out.o: catdep ./foo.cc\n", ParseManifestOpts{})
   291  	g.fs.Create("foo.h", "")
   292  	g.fs.Create("foo.cc", "")
   293  	g.fs.Tick()
   294  	g.fs.Create("out.o.d", "out.o: foo.h\n")
   295  	g.fs.Create("out.o", "")
   296  
   297  	if _, err := g.scan.RecomputeDirty(g.GetNode("out.o")); err != nil {
   298  		t.Fatal(err)
   299  	}
   300  	if g.GetNode("out.o").Dirty {
   301  		t.Fatal("expected false")
   302  	}
   303  
   304  	g.state.Reset()
   305  	g.fs.RemoveFile("out.o.d")
   306  	if _, err := g.scan.RecomputeDirty(g.GetNode("out.o")); err != nil {
   307  		t.Fatal(err)
   308  	}
   309  	if !g.GetNode("out.o").Dirty {
   310  		t.Fatal("expected true")
   311  	}
   312  }
   313  
   314  // Check that rule-level variables are in scope for eval.
   315  func TestGraphTest_RuleVariablesInScope(t *testing.T) {
   316  	g := NewGraphTest(t)
   317  	g.AssertParse(&g.state, "rule r\n  depfile = x\n  command = depfile is $depfile\nbuild out: r in\n", ParseManifestOpts{})
   318  	edge := g.GetNode("out").InEdge
   319  	if got := edge.EvaluateCommand(false); got != "depfile is x" {
   320  		t.Fatal(got)
   321  	}
   322  }
   323  
   324  // Check that build statements can override rule builtins like depfile.
   325  func TestGraphTest_DepfileOverride(t *testing.T) {
   326  	g := NewGraphTest(t)
   327  	g.AssertParse(&g.state, "rule r\n  depfile = x\n  command = unused\nbuild out: r in\n  depfile = y\n", ParseManifestOpts{})
   328  	edge := g.GetNode("out").InEdge
   329  	if "y" != edge.GetBinding("depfile") {
   330  		t.Fatal("expected equal")
   331  	}
   332  }
   333  
   334  // Check that overridden values show up in expansion of rule-level bindings.
   335  func TestGraphTest_DepfileOverrideParent(t *testing.T) {
   336  	g := NewGraphTest(t)
   337  	g.AssertParse(&g.state, "rule r\n  depfile = x\n  command = depfile is $depfile\nbuild out: r in\n  depfile = y\n", ParseManifestOpts{})
   338  	edge := g.GetNode("out").InEdge
   339  	if "depfile is y" != edge.GetBinding("command") {
   340  		t.Fatal("expected equal")
   341  	}
   342  }
   343  
   344  // Verify that building a nested phony rule prints "no work to do"
   345  func TestGraphTest_NestedPhonyPrintsDone(t *testing.T) {
   346  	g := NewGraphTest(t)
   347  	g.AssertParse(&g.state, "build n1: phony \nbuild n2: phony n1\n", ParseManifestOpts{})
   348  	if _, err := g.scan.RecomputeDirty(g.GetNode("n2")); err != nil {
   349  		t.Fatal(err)
   350  	}
   351  
   352  	plan := newPlan(nil)
   353  	if _, err := plan.addTarget(g.GetNode("n2")); err != nil {
   354  		t.Fatal(err)
   355  	}
   356  
   357  	if 0 != plan.commandEdges {
   358  		t.Fatal("expected equal")
   359  	}
   360  	if plan.moreToDo() {
   361  		t.Fatal("expected false")
   362  	}
   363  }
   364  
   365  func TestGraphTest_PhonySelfReferenceError(t *testing.T) {
   366  	g := NewGraphTest(t)
   367  	var parserOpts ParseManifestOpts
   368  	parserOpts.ErrOnPhonyCycle = true
   369  	g.AssertParse(&g.state, "build a: phony a\n", parserOpts)
   370  
   371  	if _, err := g.scan.RecomputeDirty(g.GetNode("a")); err == nil {
   372  		t.Fatal("expected false")
   373  	} else if err.Error() != "dependency cycle: a -> a [-w phonycycle=err]" {
   374  		t.Fatal(err)
   375  	}
   376  }
   377  
   378  func TestGraphTest_DependencyCycle(t *testing.T) {
   379  	g := NewGraphTest(t)
   380  	g.AssertParse(&g.state, "build out: cat mid\nbuild mid: cat in\nbuild in: cat pre\nbuild pre: cat out\n", ParseManifestOpts{})
   381  
   382  	if _, err := g.scan.RecomputeDirty(g.GetNode("out")); err == nil {
   383  		t.Fatal("expected false")
   384  	} else if err.Error() != "dependency cycle: out -> mid -> in -> pre -> out" {
   385  		t.Fatal(err)
   386  	}
   387  }
   388  
   389  func TestGraphTest_CycleInEdgesButNotInNodes1(t *testing.T) {
   390  	g := NewGraphTest(t)
   391  	g.AssertParse(&g.state, "build a b: cat a\n", ParseManifestOpts{})
   392  	if _, err := g.scan.RecomputeDirty(g.GetNode("b")); err == nil {
   393  		t.Fatal("expected false")
   394  	} else if err.Error() != "dependency cycle: a -> a" {
   395  		t.Fatal(err)
   396  	}
   397  }
   398  
   399  func TestGraphTest_CycleInEdgesButNotInNodes2(t *testing.T) {
   400  	g := NewGraphTest(t)
   401  	g.AssertParse(&g.state, "build b a: cat a\n", ParseManifestOpts{})
   402  	if _, err := g.scan.RecomputeDirty(g.GetNode("b")); err == nil {
   403  		t.Fatal("expected false")
   404  	} else if err.Error() != "dependency cycle: a -> a" {
   405  		t.Fatal(err)
   406  	}
   407  }
   408  
   409  func TestGraphTest_CycleInEdgesButNotInNodes3(t *testing.T) {
   410  	g := NewGraphTest(t)
   411  	g.AssertParse(&g.state, "build a b: cat c\nbuild c: cat a\n", ParseManifestOpts{})
   412  	if _, err := g.scan.RecomputeDirty(g.GetNode("b")); err == nil {
   413  		t.Fatal("expected false")
   414  	} else if err.Error() != "dependency cycle: a -> c -> a" {
   415  		t.Fatal(err)
   416  	}
   417  }
   418  
   419  func TestGraphTest_CycleInEdgesButNotInNodes4(t *testing.T) {
   420  	g := NewGraphTest(t)
   421  	g.AssertParse(&g.state, "build d: cat c\nbuild c: cat b\nbuild b: cat a\nbuild a e: cat d\nbuild f: cat e\n", ParseManifestOpts{})
   422  	if _, err := g.scan.RecomputeDirty(g.GetNode("f")); err == nil {
   423  		t.Fatal("expected false")
   424  	} else if err.Error() != "dependency cycle: a -> d -> c -> b -> a" {
   425  		t.Fatal(err)
   426  	}
   427  }
   428  
   429  // Verify that cycles in graphs with multiple outputs are handled correctly
   430  // in RecomputeDirty() and don't cause deps to be loaded multiple times.
   431  func TestGraphTest_CycleWithLengthZeroFromDepfile(t *testing.T) {
   432  	g := NewGraphTest(t)
   433  	g.AssertParse(&g.state, "rule deprule\n   depfile = dep.d\n   command = unused\nbuild a b: deprule\n", ParseManifestOpts{})
   434  	g.fs.Create("dep.d", "a: b\n")
   435  
   436  	if _, err := g.scan.RecomputeDirty(g.GetNode("a")); err == nil {
   437  		t.Fatal("expected false")
   438  	} else if err.Error() != "dependency cycle: b -> b" {
   439  		t.Fatal(err)
   440  	}
   441  
   442  	// Despite the depfile causing edge to be a cycle (it has outputs a and b,
   443  	// but the depfile also adds b as an input), the deps should have been loaded
   444  	// only once:
   445  	edge := g.GetNode("a").InEdge
   446  	if 1 != len(edge.Inputs) {
   447  		t.Fatal("expected equal")
   448  	}
   449  	if "b" != edge.Inputs[0].Path {
   450  		t.Fatal("expected equal")
   451  	}
   452  }
   453  
   454  // Like CycleWithLengthZeroFromDepfile but with a higher cycle length.
   455  func TestGraphTest_CycleWithLengthOneFromDepfile(t *testing.T) {
   456  	g := NewGraphTest(t)
   457  	g.AssertParse(&g.state, "rule deprule\n   depfile = dep.d\n   command = unused\nrule r\n   command = unused\nbuild a b: deprule\nbuild c: r b\n", ParseManifestOpts{})
   458  	g.fs.Create("dep.d", "a: c\n")
   459  
   460  	if _, err := g.scan.RecomputeDirty(g.GetNode("a")); err == nil {
   461  		t.Fatal("expected false")
   462  	} else if err.Error() != "dependency cycle: b -> c -> b" {
   463  		t.Fatal(err)
   464  	}
   465  
   466  	// Despite the depfile causing edge to be a cycle (|edge| has outputs a and b,
   467  	// but c's in_edge has b as input but the depfile also adds |edge| as
   468  	// output)), the deps should have been loaded only once:
   469  	edge := g.GetNode("a").InEdge
   470  	if 1 != len(edge.Inputs) {
   471  		t.Fatal("expected equal")
   472  	}
   473  	if "c" != edge.Inputs[0].Path {
   474  		t.Fatal("expected equal")
   475  	}
   476  }
   477  
   478  // Like CycleWithLengthOneFromDepfile but building a node one hop away from
   479  // the cycle.
   480  func TestGraphTest_CycleWithLengthOneFromDepfileOneHopAway(t *testing.T) {
   481  	g := NewGraphTest(t)
   482  	g.AssertParse(&g.state, "rule deprule\n   depfile = dep.d\n   command = unused\nrule r\n   command = unused\nbuild a b: deprule\nbuild c: r b\nbuild d: r a\n", ParseManifestOpts{})
   483  	g.fs.Create("dep.d", "a: c\n")
   484  
   485  	if _, err := g.scan.RecomputeDirty(g.GetNode("d")); err == nil {
   486  		t.Fatal("expected false")
   487  	} else if err.Error() != "dependency cycle: b -> c -> b" {
   488  		t.Fatal(err)
   489  	}
   490  
   491  	// Despite the depfile causing edge to be a cycle (|edge| has outputs a and b,
   492  	// but c's in_edge has b as input but the depfile also adds |edge| as
   493  	// output)), the deps should have been loaded only once:
   494  	edge := g.GetNode("a").InEdge
   495  	if 1 != len(edge.Inputs) {
   496  		t.Fatal("expected equal")
   497  	}
   498  	if "c" != edge.Inputs[0].Path {
   499  		t.Fatal("expected equal")
   500  	}
   501  }
   502  
   503  func TestGraphTest_Decanonicalize(t *testing.T) {
   504  	if runtime.GOOS != "windows" {
   505  		t.Skip("windows only")
   506  	}
   507  	g := NewGraphTest(t)
   508  	g.AssertParse(&g.state, "build out\\out1: cat src\\in1\nbuild out\\out2/out3\\out4: cat mid1\nbuild out3 out4\\foo: cat mid1\n", ParseManifestOpts{})
   509  
   510  	rootNodes := g.state.RootNodes()
   511  	if 4 != len(rootNodes) {
   512  		t.Fatal("expected equal")
   513  	}
   514  	if rootNodes[0].Path != "out/out1" {
   515  		t.Fatal("expected equal")
   516  	}
   517  	if rootNodes[1].Path != "out/out2/out3/out4" {
   518  		t.Fatal("expected equal")
   519  	}
   520  	if rootNodes[2].Path != "out3" {
   521  		t.Fatal("expected equal")
   522  	}
   523  	if rootNodes[3].Path != "out4/foo" {
   524  		t.Fatal("expected equal")
   525  	}
   526  	if rootNodes[0].PathDecanonicalized() != "out\\out1" {
   527  		t.Fatal("expected equal")
   528  	}
   529  	if rootNodes[1].PathDecanonicalized() != "out\\out2/out3\\out4" {
   530  		t.Fatal("expected equal")
   531  	}
   532  	if rootNodes[2].PathDecanonicalized() != "out3" {
   533  		t.Fatal("expected equal")
   534  	}
   535  	if rootNodes[3].PathDecanonicalized() != "out4\\foo" {
   536  		t.Fatal("expected equal")
   537  	}
   538  }
   539  
   540  func TestGraphTest_DyndepLoadTrivial(t *testing.T) {
   541  	g := NewGraphTest(t)
   542  	g.AssertParse(&g.state, "rule r\n  command = unused\nbuild out: r in || dd\n  dyndep = dd\n", ParseManifestOpts{})
   543  	g.fs.Create("dd", "ninja_dyndep_version = 1\nbuild out: dyndep\n")
   544  
   545  	if !g.GetNode("dd").DyndepPending {
   546  		t.Fatal("expected true")
   547  	}
   548  	if err := g.scan.LoadDyndeps(g.GetNode("dd"), DyndepFile{}); err != nil {
   549  		t.Fatal(err)
   550  	}
   551  	if g.GetNode("dd").DyndepPending {
   552  		t.Fatal("expected false")
   553  	}
   554  
   555  	edge := g.GetNode("out").InEdge
   556  	if 1 != len(edge.Outputs) {
   557  		t.Fatal("expected equal")
   558  	}
   559  	if "out" != edge.Outputs[0].Path {
   560  		t.Fatal("expected equal")
   561  	}
   562  	if 2 != len(edge.Inputs) {
   563  		t.Fatal(len(edge.Inputs))
   564  	}
   565  	if "in" != edge.Inputs[0].Path {
   566  		t.Fatal("expected equal")
   567  	}
   568  	if "dd" != edge.Inputs[1].Path {
   569  		t.Fatal("expected equal")
   570  	}
   571  	if 0 != edge.ImplicitDeps {
   572  		t.Fatal("expected equal")
   573  	}
   574  	if 1 != edge.OrderOnlyDeps {
   575  		t.Fatal("expected equal")
   576  	}
   577  	if edge.GetBinding("restat") != "" {
   578  		t.Fatal("expected false")
   579  	}
   580  }
   581  
   582  func TestGraphTest_DyndepLoadImplicit(t *testing.T) {
   583  	g := NewGraphTest(t)
   584  	g.AssertParse(&g.state, "rule r\n  command = unused\nbuild out1: r in || dd\n  dyndep = dd\nbuild out2: r in\n", ParseManifestOpts{})
   585  	g.fs.Create("dd", "ninja_dyndep_version = 1\nbuild out1: dyndep | out2\n")
   586  
   587  	if !g.GetNode("dd").DyndepPending {
   588  		t.Fatal("expected true")
   589  	}
   590  	if err := g.scan.LoadDyndeps(g.GetNode("dd"), DyndepFile{}); err != nil {
   591  		t.Fatal(err)
   592  	}
   593  	if g.GetNode("dd").DyndepPending {
   594  		t.Fatal("expected false")
   595  	}
   596  
   597  	edge := g.GetNode("out1").InEdge
   598  	if 1 != len(edge.Outputs) {
   599  		t.Fatal("expected equal")
   600  	}
   601  	if "out1" != edge.Outputs[0].Path {
   602  		t.Fatal("expected equal")
   603  	}
   604  	if 3 != len(edge.Inputs) {
   605  		t.Fatal("expected equal")
   606  	}
   607  	if "in" != edge.Inputs[0].Path {
   608  		t.Fatal("expected equal")
   609  	}
   610  	if "out2" != edge.Inputs[1].Path {
   611  		t.Fatal(edge.Inputs[1].Path)
   612  	}
   613  	if "dd" != edge.Inputs[2].Path {
   614  		t.Fatal("expected equal")
   615  	}
   616  	if 1 != edge.ImplicitDeps {
   617  		t.Fatal("expected equal")
   618  	}
   619  	if 1 != edge.OrderOnlyDeps {
   620  		t.Fatal("expected equal")
   621  	}
   622  	if edge.GetBinding("restat") != "" {
   623  		t.Fatal("expected false")
   624  	}
   625  }
   626  
   627  func TestGraphTest_DyndepLoadMissingFile(t *testing.T) {
   628  	g := NewGraphTest(t)
   629  	g.AssertParse(&g.state, "rule r\n  command = unused\nbuild out: r in || dd\n  dyndep = dd\n", ParseManifestOpts{})
   630  
   631  	if !g.GetNode("dd").DyndepPending {
   632  		t.Fatal("expected true")
   633  	}
   634  	if err := g.scan.LoadDyndeps(g.GetNode("dd"), DyndepFile{}); err == nil {
   635  		t.Fatal("expected false")
   636  	} else if err.Error() != "loading 'dd': file does not exist" {
   637  		t.Fatal(err)
   638  	}
   639  }
   640  
   641  func TestGraphTest_DyndepLoadMissingEntry(t *testing.T) {
   642  	g := NewGraphTest(t)
   643  	g.AssertParse(&g.state, "rule r\n  command = unused\nbuild out: r in || dd\n  dyndep = dd\n", ParseManifestOpts{})
   644  	g.fs.Create("dd", "ninja_dyndep_version = 1\n")
   645  
   646  	if !g.GetNode("dd").DyndepPending {
   647  		t.Fatal("expected true")
   648  	}
   649  	if err := g.scan.LoadDyndeps(g.GetNode("dd"), DyndepFile{}); err == nil {
   650  		t.Fatal("expected false")
   651  	} else if err.Error() != "'out' not mentioned in its dyndep file 'dd'" {
   652  		t.Fatal(err)
   653  	}
   654  }
   655  
   656  func TestGraphTest_DyndepLoadExtraEntry(t *testing.T) {
   657  	g := NewGraphTest(t)
   658  	g.AssertParse(&g.state, "rule r\n  command = unused\nbuild out: r in || dd\n  dyndep = dd\nbuild out2: r in || dd\n", ParseManifestOpts{})
   659  	g.fs.Create("dd", "ninja_dyndep_version = 1\nbuild out: dyndep\nbuild out2: dyndep\n")
   660  
   661  	if !g.GetNode("dd").DyndepPending {
   662  		t.Fatal("expected true")
   663  	}
   664  	if err := g.scan.LoadDyndeps(g.GetNode("dd"), DyndepFile{}); err == nil {
   665  		t.Fatal("expected false")
   666  	} else if err.Error() != "dyndep file 'dd' mentions output 'out2' whose build statement does not have a dyndep binding for the file" {
   667  		t.Fatal(err)
   668  	}
   669  }
   670  
   671  func TestGraphTest_DyndepLoadOutputWithMultipleRules1(t *testing.T) {
   672  	g := NewGraphTest(t)
   673  	g.AssertParse(&g.state, "rule r\n  command = unused\nbuild out1 | out-twice.imp: r in1\nbuild out2: r in2 || dd\n  dyndep = dd\n", ParseManifestOpts{})
   674  	g.fs.Create("dd", "ninja_dyndep_version = 1\nbuild out2 | out-twice.imp: dyndep\n")
   675  
   676  	if !g.GetNode("dd").DyndepPending {
   677  		t.Fatal("expected true")
   678  	}
   679  	if err := g.scan.LoadDyndeps(g.GetNode("dd"), DyndepFile{}); err == nil {
   680  		t.Fatal("expected false")
   681  	} else if err.Error() != "multiple rules generate out-twice.imp" {
   682  		t.Fatal(err)
   683  	}
   684  }
   685  
   686  func TestGraphTest_DyndepLoadOutputWithMultipleRules2(t *testing.T) {
   687  	g := NewGraphTest(t)
   688  	g.AssertParse(&g.state, "rule r\n  command = unused\nbuild out1: r in1 || dd1\n  dyndep = dd1\nbuild out2: r in2 || dd2\n  dyndep = dd2\n", ParseManifestOpts{})
   689  	g.fs.Create("dd1", "ninja_dyndep_version = 1\nbuild out1 | out-twice.imp: dyndep\n")
   690  	g.fs.Create("dd2", "ninja_dyndep_version = 1\nbuild out2 | out-twice.imp: dyndep\n")
   691  
   692  	if !g.GetNode("dd1").DyndepPending {
   693  		t.Fatal("expected true")
   694  	}
   695  	if err := g.scan.LoadDyndeps(g.GetNode("dd1"), DyndepFile{}); err != nil {
   696  		t.Fatal(err)
   697  	}
   698  	if !g.GetNode("dd2").DyndepPending {
   699  		t.Fatal("expected true")
   700  	}
   701  	if err := g.scan.LoadDyndeps(g.GetNode("dd2"), DyndepFile{}); err == nil {
   702  		t.Fatal("expected false")
   703  	} else if err.Error() != "multiple rules generate out-twice.imp" {
   704  		t.Fatal(err)
   705  	}
   706  }
   707  
   708  func TestGraphTest_DyndepLoadMultiple(t *testing.T) {
   709  	g := NewGraphTest(t)
   710  	g.AssertParse(&g.state, "rule r\n  command = unused\nbuild out1: r in1 || dd\n  dyndep = dd\nbuild out2: r in2 || dd\n  dyndep = dd\nbuild outNot: r in3 || dd\n", ParseManifestOpts{})
   711  	g.fs.Create("dd", "ninja_dyndep_version = 1\nbuild out1 | out1imp: dyndep | in1imp\nbuild out2: dyndep | in2imp\n  restat = 1\n")
   712  
   713  	if !g.GetNode("dd").DyndepPending {
   714  		t.Fatal("expected true")
   715  	}
   716  	if err := g.scan.LoadDyndeps(g.GetNode("dd"), DyndepFile{}); err != nil {
   717  		t.Fatal(err)
   718  	}
   719  	if g.GetNode("dd").DyndepPending {
   720  		t.Fatal("expected false")
   721  	}
   722  	edge1 := g.GetNode("out1").InEdge
   723  	if 2 != len(edge1.Outputs) {
   724  		t.Fatal("expected equal")
   725  	}
   726  	if "out1" != edge1.Outputs[0].Path {
   727  		t.Fatal("expected equal")
   728  	}
   729  	if "out1imp" != edge1.Outputs[1].Path {
   730  		t.Fatal("expected equal")
   731  	}
   732  	if 1 != edge1.ImplicitOuts {
   733  		t.Fatal("expected equal")
   734  	}
   735  	if 3 != len(edge1.Inputs) {
   736  		t.Fatal("expected equal")
   737  	}
   738  	if "in1" != edge1.Inputs[0].Path {
   739  		t.Fatal("expected equal")
   740  	}
   741  	if "in1imp" != edge1.Inputs[1].Path {
   742  		t.Fatal(edge1.Inputs[1].Path)
   743  	}
   744  	if "dd" != edge1.Inputs[2].Path {
   745  		t.Fatal("expected equal")
   746  	}
   747  	if 1 != edge1.ImplicitDeps {
   748  		t.Fatal("expected equal")
   749  	}
   750  	if 1 != edge1.OrderOnlyDeps {
   751  		t.Fatal("expected equal")
   752  	}
   753  	if edge1.GetBinding("restat") != "" {
   754  		t.Fatal("expected false")
   755  	}
   756  	if edge1 != g.GetNode("out1imp").InEdge {
   757  		t.Fatal("expected equal")
   758  	}
   759  	in1imp := g.GetNode("in1imp")
   760  	if 1 != len(in1imp.OutEdges) {
   761  		t.Fatal("expected equal")
   762  	}
   763  	if edge1 != in1imp.OutEdges[0] {
   764  		t.Fatal("expected equal")
   765  	}
   766  
   767  	edge2 := g.GetNode("out2").InEdge
   768  	if 1 != len(edge2.Outputs) {
   769  		t.Fatal("expected equal")
   770  	}
   771  	if "out2" != edge2.Outputs[0].Path {
   772  		t.Fatal("expected equal")
   773  	}
   774  	if 0 != edge2.ImplicitOuts {
   775  		t.Fatal("expected equal")
   776  	}
   777  	if 3 != len(edge2.Inputs) {
   778  		t.Fatal("expected equal")
   779  	}
   780  	if "in2" != edge2.Inputs[0].Path {
   781  		t.Fatal("expected equal")
   782  	}
   783  	if "in2imp" != edge2.Inputs[1].Path {
   784  		t.Fatal("expected equal")
   785  	}
   786  	if "dd" != edge2.Inputs[2].Path {
   787  		t.Fatal("expected equal")
   788  	}
   789  	if 1 != edge2.ImplicitDeps {
   790  		t.Fatal("expected equal")
   791  	}
   792  	if 1 != edge2.OrderOnlyDeps {
   793  		t.Fatal("expected equal")
   794  	}
   795  	if edge2.GetBinding("restat") == "" {
   796  		t.Fatal("expected true")
   797  	}
   798  	in2imp := g.GetNode("in2imp")
   799  	if 1 != len(in2imp.OutEdges) {
   800  		t.Fatal("expected equal")
   801  	}
   802  	if edge2 != in2imp.OutEdges[0] {
   803  		t.Fatal("expected equal")
   804  	}
   805  }
   806  
   807  func TestGraphTest_DyndepFileMissing(t *testing.T) {
   808  	g := NewGraphTest(t)
   809  	g.AssertParse(&g.state, "rule r\n  command = unused\nbuild out: r || dd\n  dyndep = dd\n", ParseManifestOpts{})
   810  
   811  	if _, err := g.scan.RecomputeDirty(g.GetNode("out")); err == nil {
   812  		t.Fatal("expected false")
   813  	} else if err.Error() != "loading 'dd': file does not exist" {
   814  		t.Fatal(err)
   815  	}
   816  }
   817  
   818  func TestGraphTest_DyndepFileError(t *testing.T) {
   819  	g := NewGraphTest(t)
   820  	g.AssertParse(&g.state, "rule r\n  command = unused\nbuild out: r || dd\n  dyndep = dd\n", ParseManifestOpts{})
   821  	g.fs.Create("dd", "ninja_dyndep_version = 1\n")
   822  
   823  	if _, err := g.scan.RecomputeDirty(g.GetNode("out")); err == nil {
   824  		t.Fatal("expected false")
   825  	} else if err.Error() != "'out' not mentioned in its dyndep file 'dd'" {
   826  		t.Fatal(err)
   827  	}
   828  }
   829  
   830  func TestGraphTest_DyndepImplicitInputNewer(t *testing.T) {
   831  	g := NewGraphTest(t)
   832  	g.AssertParse(&g.state, "rule r\n  command = unused\nbuild out: r || dd\n  dyndep = dd\n", ParseManifestOpts{})
   833  	g.fs.Create("dd", "ninja_dyndep_version = 1\nbuild out: dyndep | in\n")
   834  	g.fs.Create("out", "")
   835  	g.fs.Tick()
   836  	g.fs.Create("in", "")
   837  
   838  	if _, err := g.scan.RecomputeDirty(g.GetNode("out")); err != nil {
   839  		t.Fatal(err)
   840  	}
   841  
   842  	if g.GetNode("in").Dirty {
   843  		t.Fatal("expected false")
   844  	}
   845  	if g.GetNode("dd").Dirty {
   846  		t.Fatal("expected false")
   847  	}
   848  
   849  	// "out" is dirty due to dyndep-specified implicit input
   850  	if !g.GetNode("out").Dirty {
   851  		t.Fatal("expected true")
   852  	}
   853  }
   854  
   855  func TestGraphTest_DyndepFileReady(t *testing.T) {
   856  	g := NewGraphTest(t)
   857  	g.AssertParse(&g.state, "rule r\n  command = unused\nbuild dd: r dd-in\nbuild out: r || dd\n  dyndep = dd\n", ParseManifestOpts{})
   858  	g.fs.Create("dd-in", "")
   859  	g.fs.Create("dd", "ninja_dyndep_version = 1\nbuild out: dyndep | in\n")
   860  	g.fs.Create("out", "")
   861  	g.fs.Tick()
   862  	g.fs.Create("in", "")
   863  
   864  	if _, err := g.scan.RecomputeDirty(g.GetNode("out")); err != nil {
   865  		t.Fatal(err)
   866  	}
   867  
   868  	if g.GetNode("in").Dirty {
   869  		t.Fatal("expected false")
   870  	}
   871  	if g.GetNode("dd").Dirty {
   872  		t.Fatal("expected false")
   873  	}
   874  	if !g.GetNode("dd").InEdge.OutputsReady {
   875  		t.Fatal("expected true")
   876  	}
   877  
   878  	// "out" is dirty due to dyndep-specified implicit input
   879  	if !g.GetNode("out").Dirty {
   880  		t.Fatal("expected true")
   881  	}
   882  }
   883  
   884  func TestGraphTest_DyndepFileNotClean(t *testing.T) {
   885  	g := NewGraphTest(t)
   886  	g.AssertParse(&g.state, "rule r\n  command = unused\nbuild dd: r dd-in\nbuild out: r || dd\n  dyndep = dd\n", ParseManifestOpts{})
   887  	g.fs.Create("dd", "this-should-not-be-loaded")
   888  	g.fs.Tick()
   889  	g.fs.Create("dd-in", "")
   890  	g.fs.Create("out", "")
   891  
   892  	if _, err := g.scan.RecomputeDirty(g.GetNode("out")); err != nil {
   893  		t.Fatal(err)
   894  	}
   895  
   896  	if !g.GetNode("dd").Dirty {
   897  		t.Fatal("expected true")
   898  	}
   899  	if g.GetNode("dd").InEdge.OutputsReady {
   900  		t.Fatal("expected false")
   901  	}
   902  
   903  	// "out" is clean but not ready since "dd" is not ready
   904  	if g.GetNode("out").Dirty {
   905  		t.Fatal("expected false")
   906  	}
   907  	if g.GetNode("out").InEdge.OutputsReady {
   908  		t.Fatal("expected false")
   909  	}
   910  }
   911  
   912  func TestGraphTest_DyndepFileNotReady(t *testing.T) {
   913  	g := NewGraphTest(t)
   914  	g.AssertParse(&g.state, "rule r\n  command = unused\nbuild tmp: r\nbuild dd: r dd-in || tmp\nbuild out: r || dd\n  dyndep = dd\n", ParseManifestOpts{})
   915  	g.fs.Create("dd", "this-should-not-be-loaded")
   916  	g.fs.Create("dd-in", "")
   917  	g.fs.Tick()
   918  	g.fs.Create("out", "")
   919  
   920  	if _, err := g.scan.RecomputeDirty(g.GetNode("out")); err != nil {
   921  		t.Fatal(err)
   922  	}
   923  
   924  	if g.GetNode("dd").Dirty {
   925  		t.Fatal("expected false")
   926  	}
   927  	if g.GetNode("dd").InEdge.OutputsReady {
   928  		t.Fatal("expected false")
   929  	}
   930  	if g.GetNode("out").Dirty {
   931  		t.Fatal("expected false")
   932  	}
   933  	if g.GetNode("out").InEdge.OutputsReady {
   934  		t.Fatal("expected false")
   935  	}
   936  }
   937  
   938  func TestGraphTest_DyndepFileSecondNotReady(t *testing.T) {
   939  	g := NewGraphTest(t)
   940  	g.AssertParse(&g.state, "rule r\n  command = unused\nbuild dd1: r dd1-in\nbuild dd2-in: r || dd1\n  dyndep = dd1\nbuild dd2: r dd2-in\nbuild out: r || dd2\n  dyndep = dd2\n", ParseManifestOpts{})
   941  	g.fs.Create("dd1", "")
   942  	g.fs.Create("dd2", "")
   943  	g.fs.Create("dd2-in", "")
   944  	g.fs.Tick()
   945  	g.fs.Create("dd1-in", "")
   946  	g.fs.Create("out", "")
   947  
   948  	if _, err := g.scan.RecomputeDirty(g.GetNode("out")); err != nil {
   949  		t.Fatal(err)
   950  	}
   951  
   952  	if !g.GetNode("dd1").Dirty {
   953  		t.Fatal("expected true")
   954  	}
   955  	if g.GetNode("dd1").InEdge.OutputsReady {
   956  		t.Fatal("expected false")
   957  	}
   958  	if g.GetNode("dd2").Dirty {
   959  		t.Fatal("expected false")
   960  	}
   961  	if g.GetNode("dd2").InEdge.OutputsReady {
   962  		t.Fatal("expected false")
   963  	}
   964  	if g.GetNode("out").Dirty {
   965  		t.Fatal("expected false")
   966  	}
   967  	if g.GetNode("out").InEdge.OutputsReady {
   968  		t.Fatal("expected false")
   969  	}
   970  }
   971  
   972  func TestGraphTest_DyndepFileCircular(t *testing.T) {
   973  	g := NewGraphTest(t)
   974  	g.AssertParse(&g.state, "rule r\n  command = unused\nbuild out: r in || dd\n  depfile = out.d\n  dyndep = dd\nbuild in: r circ\n", ParseManifestOpts{})
   975  	g.fs.Create("out.d", "out: inimp\n")
   976  	g.fs.Create("dd", "ninja_dyndep_version = 1\nbuild out | circ: dyndep\n")
   977  	g.fs.Create("out", "")
   978  
   979  	edge := g.GetNode("out").InEdge
   980  	if _, err := g.scan.RecomputeDirty(g.GetNode("out")); err == nil {
   981  		t.Fatal("expected false")
   982  	} else if err.Error() != "dependency cycle: circ -> in -> circ" {
   983  		t.Fatal(err)
   984  	}
   985  
   986  	// Verify that "out.d" was loaded exactly once despite
   987  	// circular reference discovered from dyndep file.
   988  	if 3 != len(edge.Inputs) {
   989  		t.Fatal("expected equal")
   990  	}
   991  	if "in" != edge.Inputs[0].Path {
   992  		t.Fatal("expected equal")
   993  	}
   994  	if "inimp" != edge.Inputs[1].Path {
   995  		t.Fatal("expected equal")
   996  	}
   997  	if "dd" != edge.Inputs[2].Path {
   998  		t.Fatal("expected equal")
   999  	}
  1000  	if 1 != edge.ImplicitDeps {
  1001  		t.Fatal("expected equal")
  1002  	}
  1003  	if 1 != edge.OrderOnlyDeps {
  1004  		t.Fatal("expected equal")
  1005  	}
  1006  }
  1007  
  1008  func TestGraphTest_Validation(t *testing.T) {
  1009  	g := NewGraphTest(t)
  1010  	g.AssertParse(&g.state, "build out: cat in |@ validate\nbuild validate: cat in\n", ParseManifestOpts{})
  1011  
  1012  	g.fs.Create("in", "")
  1013  	validationNodes, err := g.scan.RecomputeDirty(g.GetNode("out"))
  1014  	if err != nil {
  1015  		t.Fatal(err)
  1016  	}
  1017  
  1018  	if len(validationNodes) != 1 || validationNodes[0].Path != "validate" {
  1019  		t.Fatal(validationNodes)
  1020  	}
  1021  
  1022  	if !g.GetNode("out").Dirty {
  1023  		t.Fatal("expected dirty")
  1024  	}
  1025  	if !g.GetNode("validate").Dirty {
  1026  		t.Fatal("expected dirty")
  1027  	}
  1028  }
  1029  
  1030  // Check that phony's dependencies' mtimes are propagated.
  1031  func TestGraphTest_PhonyDepsMtimes(t *testing.T) {
  1032  	g := NewGraphTest(t)
  1033  	g.AssertParse(&g.state, "rule touch\n command = touch $out\nbuild in_ph: phony in1\nbuild out1: touch in_ph\n", ParseManifestOpts{})
  1034  	g.fs.Create("in1", "")
  1035  	g.fs.Create("out1", "")
  1036  	out1 := g.GetNode("out1")
  1037  	in1 := g.GetNode("in1")
  1038  
  1039  	if _, err := g.scan.RecomputeDirty(out1); err != nil {
  1040  		t.Fatal(err)
  1041  	}
  1042  	if out1.Dirty {
  1043  		t.Fatal("expected true")
  1044  	}
  1045  
  1046  	// Get the mtime of out1
  1047  	if err := in1.Stat(&g.fs); err != nil {
  1048  		t.Fatal(err)
  1049  	}
  1050  	if err := out1.Stat(&g.fs); err != nil {
  1051  		t.Fatal(err)
  1052  	}
  1053  	out1Mtime1 := out1.MTime
  1054  	in1Mtime1 := in1.MTime
  1055  
  1056  	// Touch in1. This should cause out1 to be dirty
  1057  	g.state.Reset()
  1058  	g.fs.Tick()
  1059  	g.fs.Create("in1", "")
  1060  
  1061  	if err := in1.Stat(&g.fs); err != nil {
  1062  		t.Fatal(err)
  1063  	}
  1064  	if in1.MTime <= in1Mtime1 {
  1065  		t.Fatal("expected greater")
  1066  	}
  1067  
  1068  	if _, err := g.scan.RecomputeDirty(out1); err != nil {
  1069  		t.Fatal(err)
  1070  	}
  1071  	if in1.MTime <= in1Mtime1 {
  1072  		t.Fatal("expected greater")
  1073  	}
  1074  	if out1.MTime != out1Mtime1 {
  1075  		t.Fatal("expected equal")
  1076  	}
  1077  	if !out1.Dirty {
  1078  		t.Fatal("expected true")
  1079  	}
  1080  }