1 // Copyright (c) Microsoft. All rights reserved.
2 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3 
4 using System;
5 using System.Collections.Generic;
6 using System.IO;
7 using System.Resources;
8 using System.Threading;
9 
10 using Microsoft.Build.Framework;
11 using Microsoft.Build.Shared;
12 using Microsoft.Build.Tasks;
13 using Microsoft.Build.Utilities;
14 using Xunit;
15 
16 
17 
18 #pragma warning disable 0219
19 
20 #if FEATURE_FILE_TRACKER
21 
22 namespace Microsoft.Build.UnitTests.TrackedDependencies
23 {
24     sealed public class TrackedDependenciesTests
25     {
26         private readonly int _sleepTimeMilliseconds = NativeMethodsShared.IsWindows ? 100 : 1000;
27 
TrackedDependenciesTests()28         public TrackedDependenciesTests()
29         {
30             string tempPath = Path.GetTempPath();
31             string tempTestFilesPath = Path.Combine(tempPath, "TestFiles");
32 
33             if (Directory.Exists("TestFiles"))
34             {
35                 for (int i = 0; i < 5; i++)
36                 {
37                     try
38                     {
39                         Directory.Delete("TestFiles", true /* recursive */);
40                         break;
41                     }
42                     catch (Exception)
43                     {
44                         Thread.Sleep(1000);
45                         // Eat exceptions from the delete
46                     }
47                 }
48             }
49 
50             if (Directory.Exists(tempTestFilesPath))
51             {
52                 for (int i = 0; i < 5; i++)
53                 {
54                     try
55                     {
56                         Directory.Delete(tempTestFilesPath, true /* recursive */);
57                         break;
58                     }
59                     catch (Exception)
60                     {
61                         Thread.Sleep(1000);
62                         // Eat exceptions from the delete
63                     }
64                 }
65             }
66 
67             Directory.CreateDirectory(tempTestFilesPath);
68             Directory.CreateDirectory("TestFiles");
69 
70             // Sleep for a period before each test is run so that
71             // there is enough time for files to have distinct
72             // last modified times - this ensures that the tracking
73             // dependency caching of tracking logs (which is based on
74             // last write time) can be relied upon
75             Thread.Sleep(_sleepTimeMilliseconds);
76         }
77 
78         /// <summary>
79         /// Tests DependencyTableCache.FormatNormalizedTlogRootingMarker, which should do effectively the same
80         /// thing as FileTracker.FormatRootingMarker, except with some extra initial normalization to get rid of
81         /// pesky PIDs and TIDs in the tlog names.
82         /// </summary>
83         [Fact]
FormatNormalizedRootingMarkerTests()84         public void FormatNormalizedRootingMarkerTests()
85         {
86             Dictionary<ITaskItem[], string> tests = new Dictionary<ITaskItem[], string>();
87             tests.Add
88                 (
89                     new ITaskItem[1] { new TaskItem("Debug\\link.9999-cvtres.write.1.tlog") },
90                     Path.Combine(Directory.GetCurrentDirectory(), "Debug\\link.[ID]-cvtres.write.[ID].tlog").ToUpperInvariant()
91                 );
92             tests.Add
93                 (
94                     new ITaskItem[1] { new TaskItem("Debug\\link.0000-cvtres.read.1.tlog") },
95                     Path.Combine(Directory.GetCurrentDirectory(), "Debug\\link.[ID]-cvtres.read.[ID].tlog").ToUpperInvariant()
96                 );
97             tests.Add
98                 (
99                     new ITaskItem[1] { new TaskItem("Debug\\link.4567-cvtres.write.1.tlog") },
100                     Path.Combine(Directory.GetCurrentDirectory(), "Debug\\link.[ID]-cvtres.write.[ID].tlog").ToUpperInvariant()
101                 );
102             tests.Add
103                 (
104                     new ITaskItem[1] { new TaskItem("Debug\\link.9999.write.1.tlog") },
105                     Path.Combine(Directory.GetCurrentDirectory(), "Debug\\link.[ID].write.[ID].tlog").ToUpperInvariant()
106                 );
107             tests.Add
108                 (
109                     new ITaskItem[1] { new TaskItem("Debug\\link.0000.read.1.tlog") },
110                     Path.Combine(Directory.GetCurrentDirectory(), "Debug\\link.[ID].read.[ID].tlog").ToUpperInvariant()
111                 );
112             tests.Add
113                 (
114                     new ITaskItem[1] { new TaskItem("Debug\\link.4567.write.1.tlog") },
115                     Path.Combine(Directory.GetCurrentDirectory(), "Debug\\link.[ID].write.[ID].tlog").ToUpperInvariant()
116                 );
117             tests.Add
118                 (
119                     new ITaskItem[1] { new TaskItem("Debug\\link2345.write.1.tlog") },
120                     Path.Combine(Directory.GetCurrentDirectory(), "Debug\\link2345.write.[ID].tlog").ToUpperInvariant()
121                 );
122             tests.Add
123                 (
124                     new ITaskItem[1] { new TaskItem("link.4567.write.1.tlog") },
125                     Path.Combine(Directory.GetCurrentDirectory(), "link.[ID].write.[ID].tlog").ToUpperInvariant()
126                 );
127             tests.Add
128                 (
129                     new ITaskItem[1] { new TaskItem("Debug\\a.1234.b\\link.4567.write.1.tlog") },
130                     Path.Combine(Directory.GetCurrentDirectory(), "Debug\\a.1234.b\\link.[ID].write.[ID].tlog").ToUpperInvariant()
131                 );
132             tests.Add
133                 (
134                     new ITaskItem[1] { new TaskItem("link.write.tlog") },
135                     Path.Combine(Directory.GetCurrentDirectory(), "link.write.tlog").ToUpperInvariant()
136                 );
137             tests.Add
138                 (
139                     new ITaskItem[1] { new TaskItem("link%20with%20spaces.write.3.tlog") },
140                     Path.Combine(Directory.GetCurrentDirectory(), "link with spaces.write.[ID].tlog").ToUpperInvariant()
141                 );
142             tests.Add
143                 (
144                     new ITaskItem[2] { new TaskItem("link.write.tlog"), new TaskItem("Debug\\link2345.write.1.tlog") },
145                     Path.Combine(Directory.GetCurrentDirectory(), "Debug\\link2345.write.[ID].tlog").ToUpperInvariant() + "|" +
146                     Path.Combine(Directory.GetCurrentDirectory(), "link.write.tlog").ToUpperInvariant()
147                 );
148             tests.Add
149                 (
150                     new ITaskItem[1] { new TaskItem("link.write.tlog1234") },
151                     Path.Combine(Directory.GetCurrentDirectory(), "link.write.tlog1234").ToUpperInvariant()
152                 );
153             tests.Add
154                 (
155                     new ITaskItem[1] { new TaskItem("1234link.write.tlog") },
156                     Path.Combine(Directory.GetCurrentDirectory(), "1234link.write.tlog").ToUpperInvariant()
157                 );
158             tests.Add
159                 (
160                     new ITaskItem[1] { new TaskItem("link-1234.write.tlog") },
161                     Path.Combine(Directory.GetCurrentDirectory(), "link-1234.write.tlog").ToUpperInvariant()
162                 );
163             tests.Add
164                 (
165                     new ITaskItem[1] { new TaskItem("C:\\Debug\\a.1234.b\\link.4567.write.1.tlog") },
166                     "C:\\DEBUG\\A.1234.B\\LINK.[ID].WRITE.[ID].TLOG"
167                 );
168             tests.Add
169                 (
170                     new ITaskItem[1] { new TaskItem("a\\") },
171                     Path.Combine(Directory.GetCurrentDirectory(), "a\\").ToUpperInvariant()
172                 );
173             tests.Add
174                 (
175                     new ITaskItem[1] { new TaskItem("Debug\\link.45\\67.write.1.tlog") },
176                     Path.Combine(Directory.GetCurrentDirectory(), "Debug\\link.45\\67.write.[ID].tlog").ToUpperInvariant()
177                 );
178             tests.Add
179                 (
180                     new ITaskItem[1] { new TaskItem("Debug\\link.4567.write.1.tlog\\") },
181                     Path.Combine(Directory.GetCurrentDirectory(), "Debug\\link.4567.write.1.tlog\\").ToUpperInvariant()
182                 );
183             tests.Add
184                 (
185                     new ITaskItem[0] { },
186                     ""
187                 );
188             tests.Add
189                 (
190                     new ITaskItem[3]
191                         {
192                             new TaskItem("Debug\\link.write.1.tlog"),
193                             new TaskItem("Debug\\link.2345.write.1.tlog"),
194                             new TaskItem("Debug\\link.2345-cvtres.6789-mspdbsrv.1111.write.4.tlog")
195                         },
196                     Path.Combine(Directory.GetCurrentDirectory(), "Debug\\link.write.[ID].tlog").ToUpperInvariant() + "|" +
197                     Path.Combine(Directory.GetCurrentDirectory(), "Debug\\link.[ID]-cvtres.[ID]-mspdbsrv.[ID].write.[ID].tlog").ToUpperInvariant() + "|" +
198                     Path.Combine(Directory.GetCurrentDirectory(), "Debug\\link.[ID].write.[ID].tlog").ToUpperInvariant()
199                 );
200             tests.Add
201                 (
202                     new ITaskItem[3] { new TaskItem("link.1234-write.1.tlog"), new TaskItem("link.1234-write.3.tlog"), new TaskItem("cl.write.2.tlog") },
203                     Path.Combine(Directory.GetCurrentDirectory(), "cl.write.[ID].tlog").ToUpperInvariant() + "|" +
204                     Path.Combine(Directory.GetCurrentDirectory(), "link.[ID]-write.[ID].tlog").ToUpperInvariant()
205                 );
206             tests.Add
207                 (
208                     new ITaskItem[3] { new TaskItem("lINk.1234-write.1.tlog"), new TaskItem("link.1234-WRitE.3.tlog"), new TaskItem("cl.write.2.tlog") },
209                     Path.Combine(Directory.GetCurrentDirectory(), "cl.write.[ID].tlog").ToUpperInvariant() + "|" +
210                     Path.Combine(Directory.GetCurrentDirectory(), "link.[ID]-write.[ID].tlog").ToUpperInvariant()
211                 );
212             tests.Add
213                 (
214                     new ITaskItem[3] { new TaskItem("a\\link.1234-write.1.tlog"), new TaskItem("b\\link.1234-write.3.tlog"), new TaskItem("cl.write.2.tlog") },
215                     Path.Combine(Directory.GetCurrentDirectory(), "a\\link.[ID]-write.[ID].tlog").ToUpperInvariant() + "|" +
216                     Path.Combine(Directory.GetCurrentDirectory(), "b\\link.[ID]-write.[ID].tlog").ToUpperInvariant() + "|" +
217                     Path.Combine(Directory.GetCurrentDirectory(), "cl.write.[ID].tlog").ToUpperInvariant()
218                 );
219             tests.Add
220                 (
221                     new ITaskItem[1] { new TaskItem("foo\\.tlog") },
222                     Path.Combine(Directory.GetCurrentDirectory(), "foo\\.tlog").ToUpperInvariant()
223                 );
224             tests.Add
225                 (
226                     new ITaskItem[1] { new TaskItem("foo\\1.tlog") },
227                     Path.Combine(Directory.GetCurrentDirectory(), "foo\\1.tlog").ToUpperInvariant()
228                 );
229             tests.Add
230                 (
231                     new ITaskItem[1] { new TaskItem("\\1.tlog") },
232                     Path.Combine(Path.GetPathRoot(Directory.GetCurrentDirectory()), "1.tlog").ToUpperInvariant()
233                 );
234             tests.Add
235                 (
236                     new ITaskItem[1] { new TaskItem(".1.tlog") },
237                     Path.Combine(Directory.GetCurrentDirectory(), ".[ID].tlog").ToUpperInvariant()
238                 );
239             tests.Add
240                 (
241                     new ITaskItem[1] { new TaskItem("-2") },
242                     Path.Combine(Directory.GetCurrentDirectory(), "-2").ToUpperInvariant()
243                 );
244             tests.Add
245                 (
246                     new ITaskItem[1] { new TaskItem(".2") },
247                     Path.Combine(Directory.GetCurrentDirectory(), ".2").ToUpperInvariant()
248                 );
249             tests.Add
250                 (
251                     new ITaskItem[1] { new TaskItem("2-") },
252                     Path.Combine(Directory.GetCurrentDirectory(), "2-").ToUpperInvariant()
253                 );
254             tests.Add
255                 (
256                     new ITaskItem[1] { new TaskItem("2.") },
257                     Path.Combine(Directory.GetCurrentDirectory(), "2").ToUpperInvariant()
258                 );
259             tests.Add
260                 (
261                     new ITaskItem[1] { new TaskItem("\\.1.tlog") },
262                     Path.Combine(Path.GetPathRoot(Directory.GetCurrentDirectory()), ".[ID].tlog").ToUpperInvariant()
263                 );
264             tests.Add
265                 (
266                     new ITaskItem[1] { new TaskItem("\\") },
267                     Path.GetPathRoot(Directory.GetCurrentDirectory()).ToUpperInvariant()
268                 );
269             tests.Add
270                 (
271                     new ITaskItem[1] { new TaskItem("\\\\share\\foo.read.8.tlog") },
272                     "\\\\share\\foo.read.[ID].tlog".ToUpperInvariant()
273                 );
274             foreach (KeyValuePair<ITaskItem[], string> test in tests)
275             {
276                 Assert.Equal(test.Value, DependencyTableCache.FormatNormalizedTlogRootingMarker(test.Key)); // "Incorrectly formatted rooting marker"
277             }
278 
279             bool exceptionCaught = false;
280             try
281             {
282                 DependencyTableCache.FormatNormalizedTlogRootingMarker(new ITaskItem[1] { new TaskItem("\\\\") });
283             }
284             catch (ArgumentException)
285             {
286                 exceptionCaught = true;
287             }
288 
289             Assert.True(exceptionCaught); // "Should have failed to format a rooting marker from a malformed UNC path"
290         }
291 
292         [Fact]
CreateTrackedDependencies()293         public void CreateTrackedDependencies()
294         {
295             Console.WriteLine("Test: CreateTrackedDependencies");
296             ITaskItem[] sources = null;
297             ITaskItem[] outputs = null;
298             CanonicalTrackedInputFiles d = new CanonicalTrackedInputFiles
299                 (
300                     DependencyTestHelper.MockTask,
301                     null,
302                     sources,
303                     null,
304                     outputs,
305                     false, /* no minimal rebuild optimization */
306                     false /* shred composite rooting markers */
307                 );
308             Assert.NotNull(d);
309         }
310 
311         [Fact]
SingleCanonicalCL()312         public void SingleCanonicalCL()
313         {
314             Console.WriteLine("Test: SingleCanonicalCL");
315             // Prepare files
316             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.h"), "");
317             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
318             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
319             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
320             File.WriteAllLines(Path.Combine("TestFiles", "one.tlog"), new string[] {
321                 "#Command some-command",
322                 Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
323                 Path.GetFullPath(Path.Combine("TestFiles", "oNe.h")),
324             });
325 
326             // Touch one
327             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
328             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.h"), "");
329 
330             CanonicalTrackedInputFiles d = new CanonicalTrackedInputFiles
331                 (
332                     DependencyTestHelper.MockTask,
333                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.tlog"))),
334                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.cpp"))),
335                     null,
336                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.obj"))),
337                     false, /* no minimal rebuild optimization */
338                     false /* shred composite rooting markers */
339                 );
340 
341             ITaskItem[] outofdate = d.ComputeSourcesNeedingCompilation();
342 
343             Assert.Equal(1, outofdate.Length);
344             Assert.Equal(outofdate[0].ItemSpec, "TestFiles\\one.cpp");
345         }
346 
347         [Fact]
NonExistentTlog()348         public void NonExistentTlog()
349         {
350             Console.WriteLine("Test: NonExistentTlog");
351             // Prepare files
352             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.h"), "");
353             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
354             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
355 
356             // Just to be sure, delete the test tlog.
357             File.Delete(Path.Combine("TestFiles", "one.tlog"));
358 
359             CanonicalTrackedInputFiles d = new CanonicalTrackedInputFiles
360                 (
361                     DependencyTestHelper.MockTask,
362                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.tlog"))),
363                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.cpp"))),
364                     null,
365                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.obj"))),
366                     false, /* no minimal rebuild optimization */
367                     false /* shred composite rooting markers */
368                 );
369 
370             ITaskItem[] outofdate = d.ComputeSourcesNeedingCompilation();
371 
372             Assert.Equal(1, outofdate.Length);
373             Assert.Equal(outofdate[0].ItemSpec, Path.Combine("TestFiles", "one.cpp"));
374         }
375 
376         [Fact]
EmptyTLog()377         public void EmptyTLog()
378         {
379             Console.WriteLine("Test: EmptyTLog");
380 
381             // Prepare files
382             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.h"), "");
383             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
384             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
385             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.tlog"), "");
386 
387             CanonicalTrackedInputFiles d = new CanonicalTrackedInputFiles
388                 (
389                     DependencyTestHelper.MockTask,
390                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.tlog"))),
391                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.cpp"))),
392                     null,
393                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.obj"))),
394                     false, /* no minimal rebuild optimization */
395                     false /* shred composite rooting markers */
396                 );
397 
398             ITaskItem[] outofdate = d.ComputeSourcesNeedingCompilation();
399 
400             Assert.Equal(1, outofdate.Length);
401             Assert.Equal(outofdate[0].ItemSpec, Path.Combine("TestFiles", "one.cpp"));
402         }
403 
404         [Fact]
InvalidReadTLogName()405         public void InvalidReadTLogName()
406         {
407             Console.WriteLine("Test: InvalidReadTLogName");
408 
409             // Prepare files
410             DependencyTestHelper.WriteAll("TestFiles\\one.h", "");
411             DependencyTestHelper.WriteAll("TestFiles\\one.cpp", "");
412             DependencyTestHelper.WriteAll("TestFiles\\one.obj", "");
413             DependencyTestHelper.WriteAll("TestFiles\\one.tlog", "");
414 
415             MockTask task = DependencyTestHelper.MockTask;
416 
417             CanonicalTrackedInputFiles d = new CanonicalTrackedInputFiles
418                 (
419                     task,
420                     DependencyTestHelper.ItemArray(new TaskItem("TestFiles\\|one|.tlog")),
421                     DependencyTestHelper.ItemArray(new TaskItem("TestFiles\\one.cpp")),
422                     null,
423                     DependencyTestHelper.ItemArray(new TaskItem("TestFiles\\one.obj")),
424                     false, /* no minimal rebuild optimization */
425                     false /* shred composite rooting markers */
426                 );
427 
428             ITaskItem[] outofdate = d.ComputeSourcesNeedingCompilation();
429 
430             Assert.Equal(1, ((task as ITask).BuildEngine as MockEngine).Warnings); // "Should have an error."
431             Assert.Equal(0, d.DependencyTable.Count); // "DependencyTable should be empty."
432         }
433 
434         [Fact]
ReadTLogWithInitialEmptyLine()435         public void ReadTLogWithInitialEmptyLine()
436         {
437             Console.WriteLine("Test: ReadTLogWithInitialEmptyLine");
438 
439             // Prepare files
440             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.h"), "");
441             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
442             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
443 
444             File.WriteAllLines(Path.Combine("TestFiles", "one.tlog"), new string[] { "", "^FOO" });
445             MockTask task = DependencyTestHelper.MockTask;
446 
447             CanonicalTrackedInputFiles d = new CanonicalTrackedInputFiles
448                 (
449                     task,
450                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.tlog"))),
451                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.cpp"))),
452                     null,
453                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.obj"))),
454                     false, /* no minimal rebuild optimization */
455                     false /* shred composite rooting markers */
456                 );
457 
458             Assert.Equal(1, ((task as ITask).BuildEngine as MockEngine).Warnings); // "Should have a warning."
459             Assert.Equal(0, d.DependencyTable.Count); // "DependencyTable should be empty."
460         }
461 
462         [Fact]
ReadTLogWithEmptyLineImmediatelyAfterRoot()463         public void ReadTLogWithEmptyLineImmediatelyAfterRoot()
464         {
465             Console.WriteLine("Test: ReadTLogWithEmptyLineImmediatelyAfterRoot");
466 
467             // Prepare files
468             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.h"), "");
469             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
470             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
471 
472             File.WriteAllLines(Path.Combine("TestFiles", "one.tlog"), new string[] { "^FOO", "", "FOO" });
473             MockTask task = DependencyTestHelper.MockTask;
474 
475             CanonicalTrackedInputFiles d = new CanonicalTrackedInputFiles
476                 (
477                     task,
478                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.tlog"))),
479                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.cpp"))),
480                     null,
481                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.obj"))),
482                     false, /* no minimal rebuild optimization */
483                     false /* shred composite rooting markers */
484                 );
485 
486             Assert.Equal(1, ((task as ITask).BuildEngine as MockEngine).Warnings); // "Should have a warning."
487             Assert.Equal(0, d.DependencyTable.Count); // "DependencyTable should be empty."
488         }
489 
490         [Fact]
ReadTLogWithEmptyLineBetweenRoots()491         public void ReadTLogWithEmptyLineBetweenRoots()
492         {
493             Console.WriteLine("Test: ReadTLogWithEmptyLineImmediatelyAfterRoot");
494 
495             // Prepare files
496             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.h"), "");
497             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
498             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
499 
500             File.WriteAllLines(Path.Combine("TestFiles", "one.tlog"), new string[] { "^FOO", "FOO", "", "^BAR", "BAR" });
501             MockTask task = DependencyTestHelper.MockTask;
502 
503             CanonicalTrackedInputFiles d = new CanonicalTrackedInputFiles
504                 (
505                     task,
506                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.tlog"))),
507                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.cpp"))),
508                     null,
509                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.obj"))),
510                     false, /* no minimal rebuild optimization */
511                     false /* shred composite rooting markers */
512                 );
513 
514             Assert.Equal(1, ((task as ITask).BuildEngine as MockEngine).Warnings); // "Should have a warning."
515             Assert.Equal(0, d.DependencyTable.Count); // "DependencyTable should be empty."
516         }
517 
518         [Fact]
ReadTLogWithEmptyRoot()519         public void ReadTLogWithEmptyRoot()
520         {
521             Console.WriteLine("Test: ReadTLogWithEmptyRoot");
522 
523             // Prepare files
524             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.h"), "");
525             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
526             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
527 
528             File.WriteAllLines(Path.Combine("TestFiles", "one.tlog"), new string[] { "^", "FOO" });
529             MockTask task = DependencyTestHelper.MockTask;
530 
531             CanonicalTrackedInputFiles d = new CanonicalTrackedInputFiles
532                 (
533                     task,
534                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.tlog"))),
535                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.cpp"))),
536                     null,
537                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.obj"))),
538                     false, /* no minimal rebuild optimization */
539                     false /* shred composite rooting markers */
540                 );
541 
542             Assert.Equal(1, ((task as ITask).BuildEngine as MockEngine).Warnings); // "Should have a warning."
543             Assert.Equal(0, d.DependencyTable.Count); // "DependencyTable should be empty."
544         }
545 
546         [Fact]
ReadTLogWithDuplicateInRoot()547         public void ReadTLogWithDuplicateInRoot()
548         {
549             Console.WriteLine("Test: ReadTLogWithDuplicateInRoot");
550 
551             //Prepare files
552             DependencyTestHelper.WriteAll("TestFiles\\one.h", "");
553             DependencyTestHelper.WriteAll("TestFiles\\foo.cpp", "");
554             DependencyTestHelper.WriteAll("TestFiles\\one.obj", "");
555 
556             ITaskItem[] sources = new ITaskItem[] { new TaskItem("TestFiles\\foo.cpp"), new TaskItem("TestFiles\\foo.cpp") };
557 
558             File.WriteAllLines("TestFiles\\one.tlog", new string[] { "^TestFiles\\foo.cpp|TestFiles\\foo.cpp", "TestFiles\\bar.cpp", "TestFiles\\foo.cpp" });
559             MockTask task = DependencyTestHelper.MockTask;
560 
561             CanonicalTrackedInputFiles d = new CanonicalTrackedInputFiles
562                 (
563                     task,
564                     DependencyTestHelper.ItemArray(new TaskItem("TestFiles\\one.tlog")),
565                     sources,
566                     null,
567                     DependencyTestHelper.ItemArray(new TaskItem("TestFiles\\one.obj")),
568                     false, /* no minimal rebuild optimization */
569                     false /* shred composite rooting markers */
570                 );
571 
572             Assert.NotEqual(0, d.DependencyTable.Count); // "Dependency Table should not be empty."
573         }
574 
575         [Fact]
InvalidWriteTLogName()576         public void InvalidWriteTLogName()
577         {
578             Console.WriteLine("Test: InvalidWriteTLogName");
579 
580             MockTask task = DependencyTestHelper.MockTask;
581 
582             CanonicalTrackedOutputFiles d = new CanonicalTrackedOutputFiles
583                 (
584                     task,
585                     DependencyTestHelper.ItemArray(new TaskItem("TestFiles\\|one|.write.tlog"))
586                 );
587 
588             Assert.Equal(1, ((task as ITask).BuildEngine as MockEngine).Warnings); // "Should have an error."
589             Assert.Equal(0, d.DependencyTable.Count); // "DependencyTable should be empty."
590         }
591 
592         [Fact]
WriteTLogWithInitialEmptyLine()593         public void WriteTLogWithInitialEmptyLine()
594         {
595             Console.WriteLine("Test: WriteTLogWithInitialEmptyLine");
596 
597             // Prepare files
598             File.WriteAllLines(Path.Combine("TestFiles", "one.write.tlog"), new string[] { "", "^FOO" });
599             MockTask task = DependencyTestHelper.MockTask;
600 
601             CanonicalTrackedOutputFiles d = new CanonicalTrackedOutputFiles
602                 (
603                     task,
604                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.write.tlog")))
605                 );
606 
607             Assert.Equal(1, ((task as ITask).BuildEngine as MockEngine).Warnings); // "Should have a warning."
608             Assert.Equal(0, d.DependencyTable.Count); // "DependencyTable should be empty."
609         }
610 
611         [Fact]
WriteTLogWithEmptyLineImmediatelyAfterRoot()612         public void WriteTLogWithEmptyLineImmediatelyAfterRoot()
613         {
614             Console.WriteLine("Test: ReadTLogWithEmptyLineImmediatelyAfterRoot");
615 
616             // Prepare files
617             File.WriteAllLines(Path.Combine("TestFiles", "one.write.tlog"), new string[] { "^FOO", "", "FOO" });
618             MockTask task = DependencyTestHelper.MockTask;
619 
620             CanonicalTrackedOutputFiles d = new CanonicalTrackedOutputFiles
621                 (
622                     task,
623                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.write.tlog")))
624                 );
625 
626             Assert.Equal(1, ((task as ITask).BuildEngine as MockEngine).Warnings); // "Should have a warning."
627             Assert.Equal(0, d.DependencyTable.Count); // "DependencyTable should be empty."
628         }
629 
630         [Fact]
WriteTLogWithEmptyLineBetweenRoots()631         public void WriteTLogWithEmptyLineBetweenRoots()
632         {
633             Console.WriteLine("Test: WriteTLogWithEmptyLineImmediatelyAfterRoot");
634 
635             // Prepare files
636             File.WriteAllLines(Path.Combine("TestFiles", "one.write.tlog"), new string[] { "^FOO", "FOO", "", "^BAR", "BAR" });
637             MockTask task = DependencyTestHelper.MockTask;
638 
639             CanonicalTrackedOutputFiles d = new CanonicalTrackedOutputFiles
640                 (
641                     task,
642                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.write.tlog")))
643                 );
644 
645             Assert.Equal(1, ((task as ITask).BuildEngine as MockEngine).Warnings); // "Should have a warning."
646             Assert.Equal(0, d.DependencyTable.Count); // "DependencyTable should be empty."
647         }
648 
649         [Fact]
WriteTLogWithEmptyRoot()650         public void WriteTLogWithEmptyRoot()
651         {
652             Console.WriteLine("Test: WriteTLogWithEmptyRoot");
653 
654             // Prepare files
655             File.WriteAllLines(Path.Combine("TestFiles", "one.write.tlog"), new string[] { "^", "FOO" });
656             MockTask task = DependencyTestHelper.MockTask;
657 
658             CanonicalTrackedOutputFiles d = new CanonicalTrackedOutputFiles
659                 (
660                     task,
661                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.write.tlog")))
662                 );
663 
664             Assert.Equal(1, ((task as ITask).BuildEngine as MockEngine).Warnings); // "Should have a warning."
665             Assert.Equal(0, d.DependencyTable.Count); // "DependencyTable should be empty."
666         }
667 
668         [Fact]
PrimarySourceNotInTlog()669         public void PrimarySourceNotInTlog()
670         {
671             Console.WriteLine("Test: PrimarySourceNotInTlog");
672             // Prepare files
673             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.h"), "");
674             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
675             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
676 
677             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
678             // Primary Source; not appearing in this Tlog..
679             File.WriteAllLines(Path.Combine("TestFiles", "one.tlog"), new string[] {
680                 "#Command some-command",
681                 Path.GetFullPath(Path.Combine("TestFiles", "foo.cpp")),
682                 Path.GetFullPath(Path.Combine("TestFiles", "foo.h")),
683             });
684 
685             // Touch the obj - normally this would mean uptodate, but since there
686             // is no tlog entry for the primary source, we want a rebuild of it.
687             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
688             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
689 
690             CanonicalTrackedInputFiles d = new CanonicalTrackedInputFiles
691                 (
692                     DependencyTestHelper.MockTask,
693                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.tlog"))),
694                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.cpp"))),
695                     null,
696                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.obj"))),
697                     false, /* no minimal rebuild optimization */
698                     false /* shred composite rooting markers */
699                 );
700 
701             ITaskItem[] outofdate = d.ComputeSourcesNeedingCompilation();
702 
703             Assert.Equal(1, outofdate.Length);
704             Assert.Equal(outofdate[0].ItemSpec, Path.Combine("TestFiles", "one.cpp"));
705         }
706 
707         [Fact]
MultipleCanonicalCL()708         public void MultipleCanonicalCL()
709         {
710             Console.WriteLine("Test: MultipleCanonicalCL");
711             // Prepare files
712             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one1.h"), "");
713             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
714             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one3.h"), "");
715             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
716             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
717             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
718             File.WriteAllLines(Path.Combine("TestFiles", "one.tlog"), new string[] {
719                 "#Command some-command",
720                 Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
721                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
722                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")),
723                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")),
724             });
725 
726             // Touch one
727             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
728             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
729 
730             CanonicalTrackedInputFiles d = new CanonicalTrackedInputFiles
731                 (
732                     DependencyTestHelper.MockTask,
733                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.tlog"))),
734                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.cpp"))),
735                     null,
736                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.obj"))),
737                     false, /* no minimal rebuild optimization */
738                     false /* shred composite rooting markers */
739                 );
740 
741             ITaskItem[] outofdate = d.ComputeSourcesNeedingCompilation();
742 
743             Assert.Equal(1, outofdate.Length);
744             Assert.Equal(outofdate[0].ItemSpec, Path.Combine("TestFiles", "one.cpp"));
745         }
746 
747         [Fact]
MultipleCanonicalCLCompactMissingOnSuccess()748         public void MultipleCanonicalCLCompactMissingOnSuccess()
749         {
750             Console.WriteLine("Test: MultipleCanonicalCLCompactMissingOnSuccess");
751             // Prepare files
752             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one1.h"), "");
753             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
754             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one3.h"), "");
755             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
756             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
757             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
758             File.WriteAllLines(Path.Combine("TestFiles", "one.read.tlog"), new string[] {
759                 "#Command some-command",
760                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
761                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
762                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")),
763                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")),
764             });
765 
766             File.WriteAllLines(Path.Combine("TestFiles", "one.write.tlog"), new string[] {
767                 "#Command some-command",
768                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
769                 Path.GetFullPath(Path.Combine("TestFiles", "one.obj")),
770                 Path.GetFullPath(Path.Combine("TestFiles", "sometempfile.obj"))
771             });
772 
773             CanonicalTrackedOutputFiles compactOutputs = new CanonicalTrackedOutputFiles(DependencyTestHelper.MockTask, DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.write.tlog"))));
774             compactOutputs.RemoveDependenciesFromEntryIfMissing(new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "one.cpp"))));
775             compactOutputs.SaveTlog();
776 
777             // Compact the read tlog
778             CanonicalTrackedInputFiles compactInputs = new CanonicalTrackedInputFiles
779                 (
780                     DependencyTestHelper.MockTask,
781                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.read.tlog"))),
782                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.cpp"))),
783                     null,
784                     compactOutputs,
785                     false, /* no minimal rebuild optimization */
786                     false /* shred composite rooting markers */
787                 );
788 
789             compactInputs.RemoveDependenciesFromEntryIfMissing(new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "one.cpp"))));
790             compactInputs.SaveTlog();
791 
792             CanonicalTrackedOutputFiles outputs = new CanonicalTrackedOutputFiles(DependencyTestHelper.MockTask,
793                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.write.tlog"))));
794 
795             CanonicalTrackedInputFiles d = new CanonicalTrackedInputFiles
796                 (
797                     DependencyTestHelper.MockTask,
798                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.read.tlog"))),
799                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.cpp"))),
800                     null,
801                     outputs,
802                     false, /* no minimal rebuild optimization */
803                     false /* shred composite rooting markers */
804                 );
805 
806             ITaskItem[] outofdate = d.ComputeSourcesNeedingCompilation();
807 
808             Assert.Equal(0, outofdate.Length);
809         }
810 
811         [Fact]
MultipleCanonicalCLCompactMissingOnSuccessMultiEntry()812         public void MultipleCanonicalCLCompactMissingOnSuccessMultiEntry()
813         {
814             Console.WriteLine("Test: MultipleCanonicalCLCompactMissingOnSuccessMultiEntry");
815             // Prepare files
816             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one1.h"), "");
817             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
818             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one3.h"), "");
819             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
820             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
821             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
822             File.WriteAllLines(Path.Combine("TestFiles", "one.read.tlog"), new string[] {
823                 "#Command some-command",
824                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
825                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
826                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")),
827                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")),
828                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "two.cpp")),
829                 Path.GetFullPath(Path.Combine("TestFiles", "two1.h")),
830                 Path.GetFullPath(Path.Combine("TestFiles", "two2.h")),
831             });
832 
833             File.WriteAllLines(Path.Combine("TestFiles", "one.write.tlog"), new string[] {
834                 "#Command some-command",
835                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
836                 Path.GetFullPath(Path.Combine("TestFiles", "one.obj")),
837                 Path.GetFullPath(Path.Combine("TestFiles", "sometempfile.obj")),
838                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "two.cpp")),
839                 Path.GetFullPath(Path.Combine("TestFiles", "two.obj")),
840                 Path.GetFullPath(Path.Combine("TestFiles", "sometempfile2.obj"))
841             });
842 
843             CanonicalTrackedOutputFiles compactOutputs = new CanonicalTrackedOutputFiles(DependencyTestHelper.MockTask, DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.write.tlog"))));
844             compactOutputs.RemoveDependenciesFromEntryIfMissing(new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "one.cpp"))));
845             compactOutputs.SaveTlog();
846             // Compact the read tlog
847             CanonicalTrackedInputFiles compactInputs = new CanonicalTrackedInputFiles
848                 (
849                     DependencyTestHelper.MockTask,
850                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.read.tlog"))),
851                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.cpp"))),
852                     null,
853                     compactOutputs,
854                     false, /* no minimal rebuild optimization */
855                     false /* shred composite rooting markers */
856                 );
857 
858             compactInputs.RemoveDependenciesFromEntryIfMissing(new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "one.cpp"))));
859             compactInputs.SaveTlog();
860 
861             CanonicalTrackedOutputFiles writtenOutputs = new CanonicalTrackedOutputFiles(DependencyTestHelper.MockTask,
862                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.write.tlog"))));
863 
864             CanonicalTrackedInputFiles writtenInputs = new CanonicalTrackedInputFiles
865                 (
866                     DependencyTestHelper.MockTask,
867                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.read.tlog"))),
868                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.cpp"))),
869                     null,
870                     writtenOutputs,
871                     false, /* no minimal rebuild optimization */
872                     false /* shred composite rooting markers */
873                 );
874 
875             Assert.Equal(1, writtenOutputs.DependencyTable[Path.GetFullPath(Path.Combine("TestFiles", "one.cpp"))].Count);
876             Assert.Equal(4, writtenInputs.DependencyTable[Path.GetFullPath(Path.Combine("TestFiles", "one.cpp"))].Count);
877             // Everything to do with two.cpp should be left intact
878             Assert.Equal(2, writtenOutputs.DependencyTable[Path.GetFullPath(Path.Combine("TestFiles", "two.cpp"))].Count);
879             Assert.Equal(3, writtenInputs.DependencyTable[Path.GetFullPath(Path.Combine("TestFiles", "two.cpp"))].Count);
880         }
881 
882         [Fact]
RemoveDependencyFromEntry()883         public void RemoveDependencyFromEntry()
884         {
885             Console.WriteLine("Test: RemoveDependencyFromEntry");
886             // Prepare files
887             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one1.h"), "");
888             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
889             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one3.h"), "");
890             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
891             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
892             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.tlh"), "");
893             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.tli"), "");
894             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
895             File.WriteAllLines(Path.Combine("TestFiles", "one.read.tlog"), new string[] {
896                 "#Command some-command",
897                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
898                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
899                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")),
900                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")),
901                 Path.GetFullPath(Path.Combine("TestFiles", "one3.obj")),
902                 Path.GetFullPath(Path.Combine("TestFiles", "one3.tlh")),
903                 Path.GetFullPath(Path.Combine("TestFiles", "one3.tli")),
904             });
905 
906             File.WriteAllLines(Path.Combine("TestFiles", "one.write.tlog"), new string[] {
907                 "#Command some-command",
908                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
909                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
910                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")),
911                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")),
912                 Path.GetFullPath(Path.Combine("TestFiles", "one3.obj")),
913                 Path.GetFullPath(Path.Combine("TestFiles", "one3.tlh")),
914                 Path.GetFullPath(Path.Combine("TestFiles", "one3.tli")),
915             });
916 
917             CanonicalTrackedOutputFiles compactOutputs = new CanonicalTrackedOutputFiles(DependencyTestHelper.MockTask, DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.write.tlog"))));
918             compactOutputs.RemoveDependencyFromEntry(new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "one.cpp"))), new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "one3.obj"))));
919             compactOutputs.SaveTlog();
920 
921             CanonicalTrackedOutputFiles writtenOutputs = new CanonicalTrackedOutputFiles(DependencyTestHelper.MockTask,
922                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.write.tlog"))));
923 
924             Assert.False(writtenOutputs.DependencyTable[Path.GetFullPath(Path.Combine("TestFiles", "one.cpp"))].ContainsKey(Path.GetFullPath(Path.Combine("TestFiles", "one3.obj"))));
925 
926             CanonicalTrackedInputFiles compactInputs = new CanonicalTrackedInputFiles
927                 (
928                     DependencyTestHelper.MockTask,
929                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.read.tlog"))),
930                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.cpp"))),
931                     null,
932                     compactOutputs,
933                     false, /* no minimal rebuild optimization */
934                     false /* shred composite rooting markers */
935                 );
936 
937             compactInputs.RemoveDependencyFromEntry(new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "one.cpp"))), new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "one3.obj"))));
938             compactInputs.SaveTlog();
939 
940             CanonicalTrackedInputFiles writtenInputs = new CanonicalTrackedInputFiles
941                 (
942                     DependencyTestHelper.MockTask,
943                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.read.tlog"))),
944                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.cpp"))),
945                     null,
946                     writtenOutputs,
947                     false, /* no minimal rebuild optimization */
948                     false /* shred composite rooting markers */
949                 );
950 
951             Assert.False(writtenInputs.DependencyTable[Path.GetFullPath(Path.Combine("TestFiles", "one.cpp"))].ContainsKey(Path.GetFullPath(Path.Combine("TestFiles", "one3.obj"))));
952         }
953 
954         [Fact]
RemoveDependencyFromEntries()955         public void RemoveDependencyFromEntries()
956         {
957             Console.WriteLine("Test: RemoveDependencyFromEntry");
958             // Prepare files
959             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one1.h"), "");
960             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
961             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one3.h"), "");
962             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
963             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
964             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.tlh"), "");
965             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.tli"), "");
966             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
967 
968             string rootingMarker = Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")) + "|" + Path.GetFullPath(Path.Combine("TestFiles", "three.cpp")) + "|" + Path.GetFullPath(Path.Combine("TestFiles", "two.cpp"));
969 
970             File.WriteAllLines(Path.Combine("TestFiles", "one.read.tlog"), new string[] {
971                 "#Command some-command",
972                 "^" + rootingMarker,
973                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
974                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")),
975                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")),
976                 Path.GetFullPath(Path.Combine("TestFiles", "one3.obj")),
977                 Path.GetFullPath(Path.Combine("TestFiles", "one3.tlh")),
978                 Path.GetFullPath(Path.Combine("TestFiles", "one3.tli")),
979             });
980 
981             File.WriteAllLines(Path.Combine("TestFiles", "one.write.tlog"), new string[] {
982                 "#Command some-command",
983                 "^" + rootingMarker,
984                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
985                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")),
986                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")),
987                 Path.GetFullPath(Path.Combine("TestFiles", "one3.obj")),
988                 Path.GetFullPath(Path.Combine("TestFiles", "one3.tlh")),
989                 Path.GetFullPath(Path.Combine("TestFiles", "one3.tli")),
990             });
991 
992             CanonicalTrackedOutputFiles compactOutputs = new CanonicalTrackedOutputFiles(DependencyTestHelper.MockTask, DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.write.tlog"))));
993             compactOutputs.RemoveDependencyFromEntry(new TaskItem[] { new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "one.cpp"))), new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "two.cpp"))), new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "three.cpp"))) }, new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "one3.obj"))));
994             compactOutputs.SaveTlog();
995 
996             CanonicalTrackedOutputFiles writtenOutputs = new CanonicalTrackedOutputFiles(DependencyTestHelper.MockTask,
997                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.write.tlog"))));
998 
999             Assert.False(writtenOutputs.DependencyTable[rootingMarker].ContainsKey(Path.GetFullPath(Path.Combine("TestFiles", "one3.obj"))));
1000 
1001             CanonicalTrackedInputFiles compactInputs = new CanonicalTrackedInputFiles
1002                 (
1003                     DependencyTestHelper.MockTask,
1004                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.read.tlog"))),
1005                     new TaskItem[] { new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "one.cpp"))), new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "two.cpp"))), new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "three.cpp"))) },
1006                     null,
1007                     compactOutputs,
1008                     false, /* no minimal rebuild optimization */
1009                     true /* shred composite rooting markers */
1010                 );
1011 
1012             compactInputs.RemoveDependencyFromEntry(new TaskItem[] { new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "one.cpp"))), new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "two.cpp"))), new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "three.cpp"))) }, new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "one3.obj"))));
1013             compactInputs.SaveTlog();
1014 
1015             CanonicalTrackedInputFiles writtenInputs = new CanonicalTrackedInputFiles
1016                 (
1017                     DependencyTestHelper.MockTask,
1018                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.read.tlog"))),
1019                     new TaskItem[] { new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "one.cpp"))), new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "two.cpp"))), new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "three.cpp"))) },
1020                     null,
1021                     writtenOutputs,
1022                     false, /* no minimal rebuild optimization */
1023                     true /* shred composite rooting markers */
1024                 );
1025 
1026             Assert.False(writtenInputs.DependencyTable[rootingMarker].ContainsKey(Path.GetFullPath(Path.Combine("TestFiles", "one3.obj"))));
1027         }
1028 
1029         [Fact]
RemoveRootsWithSharedOutputs()1030         public void RemoveRootsWithSharedOutputs()
1031         {
1032             Console.WriteLine("Test: RemoveRootsWithSharedOutputs");
1033 
1034             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one1.h"), "");
1035             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
1036             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one3.h"), "");
1037             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
1038             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
1039             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.tlh"), "");
1040             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.tli"), "");
1041             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
1042 
1043             string rootingMarker1 = Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")) + "|" + Path.GetFullPath(Path.Combine("TestFiles", "three.cpp")) + "|" + Path.GetFullPath(Path.Combine("TestFiles", "two.cpp"));
1044             string rootingMarker2 = Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")) + "|" + Path.GetFullPath(Path.Combine("TestFiles", "three.cpp"));
1045             string rootingMarker3 = Path.GetFullPath(Path.Combine("TestFiles", "one.cpp"));
1046 
1047             File.WriteAllLines(Path.Combine("TestFiles", "one.write.tlog"), new string[] {
1048                 "#Command some-command",
1049                 "^" + rootingMarker1.ToUpperInvariant(),
1050                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
1051                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")),
1052                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")),
1053                 "^" + rootingMarker2.ToUpperInvariant(),
1054                 Path.GetFullPath(Path.Combine("TestFiles", "one3.obj")),
1055                 Path.GetFullPath(Path.Combine("TestFiles", "one3.tlh")),
1056                 Path.GetFullPath(Path.Combine("TestFiles", "one3.tli")),
1057                 "^" + rootingMarker3.ToUpperInvariant(),
1058                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
1059                 Path.GetFullPath(Path.Combine("TestFiles", "one2.obj")),
1060             });
1061 
1062             CanonicalTrackedOutputFiles outputs = new CanonicalTrackedOutputFiles(DependencyTestHelper.MockTask, DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.write.tlog"))));
1063 
1064             Assert.True(outputs.DependencyTable.ContainsKey(rootingMarker1));
1065             Assert.True(outputs.DependencyTable.ContainsKey(rootingMarker2));
1066             Assert.True(outputs.DependencyTable.ContainsKey(rootingMarker3));
1067 
1068             outputs.RemoveRootsWithSharedOutputs(new ITaskItem[] { new TaskItem(Path.Combine("TestFiles", "one.cpp")), new TaskItem(Path.Combine("TestFiles", "three.cpp")), new TaskItem(Path.Combine("TestFiles", "two.cpp")) });
1069 
1070             Assert.True(outputs.DependencyTable.ContainsKey(rootingMarker1));
1071             Assert.True(outputs.DependencyTable.ContainsKey(rootingMarker2));
1072             Assert.False(outputs.DependencyTable.ContainsKey(rootingMarker3));
1073         }
1074 
1075         [Fact]
RemoveRootsWithSharedOutputs_CurrentRootNotInTable()1076         public void RemoveRootsWithSharedOutputs_CurrentRootNotInTable()
1077         {
1078             Console.WriteLine("Test: RemoveRootsWithSharedOutputs");
1079 
1080             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one1.h"), "");
1081             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
1082             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one3.h"), "");
1083             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
1084             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
1085             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.tlh"), "");
1086             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.tli"), "");
1087             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
1088 
1089             string rootingMarker1 = Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")) + "|" + Path.GetFullPath(Path.Combine("TestFiles", "three.cpp")) + "|" + Path.GetFullPath(Path.Combine("TestFiles", "two.cpp"));
1090             string rootingMarker2 = Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")) + "|" + Path.GetFullPath(Path.Combine("TestFiles", "three.cpp"));
1091             string rootingMarker3 = Path.GetFullPath(Path.Combine("TestFiles", "one.cpp"));
1092 
1093             File.WriteAllLines(Path.Combine("TestFiles", "one.write.tlog"), new string[] {
1094                 "#Command some-command",
1095                 "^" + rootingMarker1.ToUpperInvariant(),
1096                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
1097                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")),
1098                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")),
1099                 "^" + rootingMarker2.ToUpperInvariant(),
1100                 Path.GetFullPath(Path.Combine("TestFiles", "one3.obj")),
1101                 Path.GetFullPath(Path.Combine("TestFiles", "one3.tlh")),
1102                 Path.GetFullPath(Path.Combine("TestFiles", "one3.tli")),
1103                 "^" + rootingMarker3.ToUpperInvariant(),
1104                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
1105                 Path.GetFullPath(Path.Combine("TestFiles", "one2.obj")),
1106             });
1107 
1108             CanonicalTrackedOutputFiles outputs = new CanonicalTrackedOutputFiles(DependencyTestHelper.MockTask, DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.write.tlog"))));
1109 
1110             Assert.True(outputs.DependencyTable.ContainsKey(rootingMarker1));
1111             Assert.True(outputs.DependencyTable.ContainsKey(rootingMarker2));
1112             Assert.True(outputs.DependencyTable.ContainsKey(rootingMarker3));
1113 
1114             outputs.RemoveRootsWithSharedOutputs(new ITaskItem[] { new TaskItem(Path.Combine("TestFiles", "four.cpp")), new TaskItem(Path.Combine("TestFiles", "one.cpp")), new TaskItem(Path.Combine("TestFiles", "three.cpp")), new TaskItem(Path.Combine("TestFiles", "two.cpp")) });
1115 
1116             Assert.True(outputs.DependencyTable.ContainsKey(rootingMarker1));
1117             Assert.True(outputs.DependencyTable.ContainsKey(rootingMarker2));
1118             Assert.True(outputs.DependencyTable.ContainsKey(rootingMarker3));
1119         }
1120 
1121         [Fact]
MultipleCanonicalCLMissingDependency()1122         public void MultipleCanonicalCLMissingDependency()
1123         {
1124             Console.WriteLine("Test: MultipleCanonicalCLMissingDependency");
1125             // Prepare files
1126             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one1.h"), "");
1127             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
1128             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one3.h"), "");
1129             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
1130             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
1131             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
1132             File.WriteAllLines(Path.Combine("TestFiles", "one.tlog"), new string[] {
1133                 "#Command some-command",
1134                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
1135                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
1136                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")),
1137                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")),
1138             });
1139 
1140             // Delete one of our dependencies
1141             string missing = Path.GetFullPath(Path.Combine("TestFiles", "one2.h"));
1142             File.Delete(missing);
1143 
1144             CanonicalTrackedInputFiles d = new CanonicalTrackedInputFiles
1145                 (
1146                     DependencyTestHelper.MockTask,
1147                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.tlog"))),
1148                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.cpp"))),
1149                     null,
1150                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.obj"))),
1151                     false, /* no minimal rebuild optimization */
1152                     false /* shred composite rooting markers */
1153                 );
1154 
1155             // We're out of date, since a missing dependency indicates out-of-dateness
1156             ITaskItem[] outofdate = d.ComputeSourcesNeedingCompilation();
1157             Assert.Equal(1, outofdate.Length);
1158 
1159             // The dependency has been recorded and retrieved correctly
1160             Assert.True(d.DependencyTable[Path.GetFullPath(Path.Combine("TestFiles", "one.cpp"))].ContainsKey(missing));
1161 
1162             // Save out the compacted read log - our missing dependency will be compacted away
1163             // The tlog will have to entries compacted, since we're not up to date
1164             d.RemoveEntriesForSource(d.SourcesNeedingCompilation);
1165             d.SaveTlog();
1166 
1167             // read the tlog back in again
1168             d = new CanonicalTrackedInputFiles
1169                 (
1170                     DependencyTestHelper.MockTask,
1171                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.tlog"))),
1172                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.cpp"))),
1173                     null,
1174                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.obj"))),
1175                     false, /* no minimal rebuild optimization */
1176                     false /* shred composite rooting markers */
1177                 );
1178 
1179             // We're out of date, since a missing dependency indicates out-of-dateness
1180             outofdate = d.ComputeSourcesNeedingCompilation();
1181             Assert.Equal(1, outofdate.Length);
1182 
1183             // We have a source outstanding for recompilation, it will not appear in
1184             // the tracking information as it will be written again
1185             Assert.False(d.DependencyTable.ContainsKey(Path.GetFullPath(Path.Combine("TestFiles", "one.cpp"))));
1186         }
1187 
1188         [Fact]
MultipleCanonicalCLMissingOutputDependencyRemoved()1189         public void MultipleCanonicalCLMissingOutputDependencyRemoved()
1190         {
1191             Console.WriteLine("Test: MultipleCanonicalCLMissingOutputDependencyRemoved");
1192             // Prepare files
1193             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one1.h"), "");
1194             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
1195             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one3.h"), "");
1196             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
1197             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two.cpp"), "");
1198             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
1199             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
1200             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two.obj"), "");
1201 
1202             File.WriteAllLines(Path.Combine("TestFiles", "one.read.tlog"), new string[] {
1203                 "#Command some-command",
1204                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
1205                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
1206                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")),
1207                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")),
1208                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "two.cpp")),
1209                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
1210                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")),
1211             });
1212 
1213             File.WriteAllLines(Path.Combine("TestFiles", "one.write.tlog"), new string[] {
1214                 "#Command some-command",
1215                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
1216                 Path.GetFullPath(Path.Combine("TestFiles", "one.obj")),
1217                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "two.cpp")),
1218                 Path.GetFullPath(Path.Combine("TestFiles", "two.obj")),
1219                 Path.GetFullPath(Path.Combine("TestFiles", "sometempfile2.obj"))
1220             });
1221 
1222             string missing = Path.GetFullPath(Path.Combine("TestFiles", "sometempfile2.obj"));
1223 
1224             CanonicalTrackedOutputFiles compactOutputs = new CanonicalTrackedOutputFiles(DependencyTestHelper.MockTask, DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.write.tlog"))));
1225             // Save out the compacted read log - our missing dependency will be compacted away
1226             // Use an anonymous method to encapsulate the contains check for the tlogs
1227             compactOutputs.SaveTlog(delegate (string fullTrackedPath)
1228             {
1229                 // We need to answer the question "should fullTrackedPath be included in the TLog?"
1230                 return (String.Compare(fullTrackedPath, missing, StringComparison.OrdinalIgnoreCase) != 0);
1231             });
1232 
1233             // Read the Tlogs back in..
1234             compactOutputs = new CanonicalTrackedOutputFiles(DependencyTestHelper.MockTask, DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.write.tlog"))));
1235             // Compact the read tlog
1236             CanonicalTrackedInputFiles compactInputs = new CanonicalTrackedInputFiles
1237                 (
1238                     DependencyTestHelper.MockTask,
1239                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.read.tlog"))),
1240                     new TaskItem[] { new TaskItem(Path.Combine("TestFiles", "one.cpp")), new TaskItem(Path.Combine("TestFiles", "two.cpp")) },
1241                     null,
1242                     compactOutputs,
1243                     false, /* no minimal rebuild optimization */
1244                     false /* shred composite rooting markers */
1245                 );
1246 
1247             compactInputs.SaveTlog();
1248 
1249             ITaskItem[] outofDate = compactInputs.ComputeSourcesNeedingCompilation();
1250             Assert.Equal(0, outofDate.Length);
1251         }
1252 
1253 
1254         [Fact]
MultipleCanonicalCLMissingInputDependencyRemoved()1255         public void MultipleCanonicalCLMissingInputDependencyRemoved()
1256         {
1257             Console.WriteLine("Test: MultipleCanonicalCLMissingInputDependencyRemoved");
1258             // Prepare files
1259             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one1.h"), "");
1260             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
1261             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one3.h"), "");
1262             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
1263             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
1264             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
1265             File.WriteAllLines(Path.Combine("TestFiles", "one.tlog"), new string[] {
1266                 "#Command some-command",
1267                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
1268                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
1269                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")),
1270                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")),
1271             });
1272 
1273             // Delete one of our dependencies
1274             string missing = Path.GetFullPath(Path.Combine("TestFiles", "one2.h"));
1275             File.Delete(missing);
1276 
1277             CanonicalTrackedInputFiles d = new CanonicalTrackedInputFiles
1278                 (
1279                     DependencyTestHelper.MockTask,
1280                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.tlog"))),
1281                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.cpp"))),
1282                     null,
1283                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.obj"))),
1284                     false, /* no minimal rebuild optimization */
1285                     false /* shred composite rooting markers */
1286                 );
1287 
1288             // We're out of date, since a missing dependency indicates out-of-dateness
1289             ITaskItem[] outofdate = d.ComputeSourcesNeedingCompilation();
1290             Assert.Equal(1, outofdate.Length);
1291 
1292             // The dependency has been recorded and retrieved correctly
1293             Assert.True(d.DependencyTable[Path.GetFullPath(Path.Combine("TestFiles", "one.cpp"))].ContainsKey(missing));
1294 
1295             // Save out the compacted read log - our missing dependency will be compacted away
1296             // Use an anonymous method to encapsulate the contains check for the tlogs
1297             d.SaveTlog(delegate (string fullTrackedPath)
1298             {
1299                 // We need to answer the question "should fullTrackedPath be included in the TLog?"
1300                 return (String.Compare(fullTrackedPath, missing, StringComparison.OrdinalIgnoreCase) != 0);
1301             });
1302 
1303             // read the tlog back in again
1304             d = new CanonicalTrackedInputFiles
1305                 (
1306                     DependencyTestHelper.MockTask,
1307                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.tlog"))),
1308                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.cpp"))),
1309                     null,
1310                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.obj"))),
1311                     false, /* no minimal rebuild optimization */
1312                     false /* shred composite rooting markers */
1313                 );
1314 
1315             // We're not out of date, since the missing dependency has been removed
1316             outofdate = d.ComputeSourcesNeedingCompilation();
1317             Assert.Equal(0, outofdate.Length);
1318         }
1319 
1320 
1321         [Fact]
MultiplePrimaryCanonicalCL()1322         public void MultiplePrimaryCanonicalCL()
1323         {
1324             Console.WriteLine("Test: MultiplePrimaryCanonicalCL");
1325             // Prepare files
1326             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one1.h"), "");
1327             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
1328             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one3.h"), "");
1329             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
1330             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
1331 
1332             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two1.h"), "");
1333             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two2.h"), "");
1334             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two3.h"), "");
1335             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two.cpp"), "");
1336             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two.obj"), "");
1337 
1338             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
1339             File.WriteAllLines(Path.Combine("TestFiles", "one.tlog"), new string[] {
1340                 "#Command some-command",
1341                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
1342                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
1343                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")),
1344                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")),
1345                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "two.cpp")),
1346                 Path.GetFullPath(Path.Combine("TestFiles", "two1.h")),
1347                 Path.GetFullPath(Path.Combine("TestFiles", "two2.h")),
1348                 Path.GetFullPath(Path.Combine("TestFiles", "two3.h")),
1349             });
1350 
1351             // Touch one
1352             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
1353             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
1354             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
1355             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two2.h"), "");
1356 
1357             CanonicalTrackedInputFiles d = new CanonicalTrackedInputFiles
1358                 (
1359                     DependencyTestHelper.MockTask,
1360                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.tlog"))),
1361                     new ITaskItem[] {
1362                         new TaskItem(Path.Combine("TestFiles", "one.cpp")),
1363                         new TaskItem(Path.Combine("TestFiles", "two.cpp")),
1364                         },
1365                     null,
1366                     new ITaskItem[] {
1367                         new TaskItem(Path.Combine("TestFiles", "one.obj")),
1368                         new TaskItem(Path.Combine("TestFiles", "two.obj")),
1369                         },
1370                     false, /* no minimal rebuild optimization */
1371                     false /* shred composite rooting markers */
1372                 );
1373 
1374             ITaskItem[] outofdate = d.ComputeSourcesNeedingCompilation();
1375 
1376             Assert.True(outofdate.Length == 2);
1377             Assert.True((outofdate[0].ItemSpec == Path.Combine("TestFiles", "one.cpp") && outofdate[1].ItemSpec == Path.Combine("TestFiles", "two.cpp")) ||
1378                              (outofdate[1].ItemSpec == Path.Combine("TestFiles", "one.cpp") && outofdate[0].ItemSpec == Path.Combine("TestFiles", "two.cpp")));
1379         }
1380 
1381         [Fact]
MultiplePrimaryCanonicalCLUnderTemp()1382         public void MultiplePrimaryCanonicalCLUnderTemp()
1383         {
1384             string currentDirectory = Directory.GetCurrentDirectory();
1385             string tempPath = Path.GetTempPath();
1386 
1387             try
1388             {
1389                 Directory.SetCurrentDirectory(tempPath);
1390 
1391                 Console.WriteLine("Test: MultiplePrimaryCanonicalCL");
1392                 // Prepare files
1393                 DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one1.h"), "");
1394                 DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
1395                 DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one3.h"), "");
1396                 DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
1397                 DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
1398 
1399                 DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two1.h"), "");
1400                 DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two2.h"), "");
1401                 DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two3.h"), "");
1402                 DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two.cpp"), "");
1403                 DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two.obj"), "");
1404 
1405                 Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
1406                 File.WriteAllLines(Path.Combine("TestFiles", "one.tlog"), new string[] {
1407                     "#Command some-command",
1408                     "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
1409                     Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
1410                     Path.GetFullPath(Path.Combine("TestFiles", "one2.h")),
1411                     Path.GetFullPath(Path.Combine("TestFiles", "one3.h")),
1412                     "^" + Path.GetFullPath(Path.Combine("TestFiles", "two.cpp")),
1413                     Path.GetFullPath(Path.Combine("TestFiles", "two1.h")),
1414                     Path.GetFullPath(Path.Combine("TestFiles", "two2.h")),
1415                     Path.GetFullPath(Path.Combine("TestFiles", "two3.h")),
1416                 });
1417 
1418                 // Touch one
1419                 Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
1420                 DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
1421                 Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
1422                 DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two2.h"), "");
1423 
1424                 CanonicalTrackedInputFiles d = new CanonicalTrackedInputFiles
1425                     (
1426                         DependencyTestHelper.MockTask,
1427                         DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.tlog"))),
1428                         new ITaskItem[] {
1429                             new TaskItem(Path.Combine("TestFiles", "one.cpp")),
1430                             new TaskItem(Path.Combine("TestFiles", "two.cpp")),
1431                             },
1432                         null,
1433                         new ITaskItem[] {
1434                             new TaskItem(Path.Combine("TestFiles", "one.obj")),
1435                             new TaskItem(Path.Combine("TestFiles", "two.obj")),
1436                             },
1437                         false, /* no minimal rebuild optimization */
1438                         false /* shred composite rooting markers */
1439                     );
1440 
1441                 ITaskItem[] outofdate = d.ComputeSourcesNeedingCompilation();
1442 
1443                 Assert.True(outofdate.Length == 2);
1444                 Assert.True((outofdate[0].ItemSpec == Path.Combine("TestFiles", "one.cpp") && outofdate[1].ItemSpec == Path.Combine("TestFiles", "two.cpp")) ||
1445                                  (outofdate[1].ItemSpec == Path.Combine("TestFiles", "one.cpp") && outofdate[0].ItemSpec == Path.Combine("TestFiles", "two.cpp")));
1446             }
1447             finally
1448             {
1449                 Directory.SetCurrentDirectory(currentDirectory);
1450             }
1451         }
1452 
1453         [Fact]
MultiplePrimaryCanonicalCLSharedDependency()1454         public void MultiplePrimaryCanonicalCLSharedDependency()
1455         {
1456             Console.WriteLine("Test: MultiplePrimaryCanonicalCL");
1457             // Prepare files
1458             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one1.h"), "");
1459             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
1460             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one3.h"), "");
1461             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
1462             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
1463 
1464             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two1.h"), "");
1465             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two3.h"), "");
1466             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two.cpp"), "");
1467             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two.obj"), "");
1468 
1469             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
1470             File.WriteAllLines(Path.Combine("TestFiles", "one.tlog"), new string[] {
1471                 "#Command some-command",
1472                 Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
1473                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
1474                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")), // the shared dependency
1475                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")),
1476                 Path.GetFullPath(Path.Combine("TestFiles", "two.cpp")),
1477                 Path.GetFullPath(Path.Combine("TestFiles", "two1.h")),
1478                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")), // the shared dependency
1479                 Path.GetFullPath(Path.Combine("TestFiles", "two3.h")),
1480             });
1481 
1482             // Touch one
1483             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
1484             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
1485 
1486             CanonicalTrackedInputFiles d = new CanonicalTrackedInputFiles
1487                 (
1488                     DependencyTestHelper.MockTask,
1489                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.tlog"))),
1490                     new ITaskItem[] {
1491                         new TaskItem(Path.Combine("TestFiles", "one.cpp")),
1492                         new TaskItem(Path.Combine("TestFiles", "two.cpp")),
1493                         },
1494                     null,
1495                     new ITaskItem[] {
1496                         new TaskItem(Path.Combine("TestFiles", "one.obj")),
1497                         new TaskItem(Path.Combine("TestFiles", "two.obj")),
1498                         },
1499                     false, /* no minimal rebuild optimization */
1500                     false /* shred composite rooting markers */
1501                 );
1502 
1503             ITaskItem[] outofdate = d.ComputeSourcesNeedingCompilation();
1504 
1505             Assert.True(outofdate.Length == 2);
1506             Assert.True((outofdate[0].ItemSpec == Path.Combine("TestFiles", "one.cpp") && outofdate[1].ItemSpec == Path.Combine("TestFiles", "two.cpp")) ||
1507                              (outofdate[1].ItemSpec == Path.Combine("TestFiles", "one.cpp") && outofdate[0].ItemSpec == Path.Combine("TestFiles", "two.cpp")));
1508         }
1509 
1510         [Fact]
MultipleCanonicalCLAcrossCommand1()1511         public void MultipleCanonicalCLAcrossCommand1()
1512         {
1513             Console.WriteLine("Test: MultipleCanonicalCLAcrossCommand1");
1514             // Prepare files
1515             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one1.h"), "");
1516             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
1517             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one3.h"), "");
1518             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
1519             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
1520             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
1521             File.WriteAllLines(Path.Combine("TestFiles", "one.tlog"), new string[] {
1522                 "#Command some-command",
1523                 Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
1524                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
1525                 "#Command some-command1",
1526                 Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
1527                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")),
1528                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")),
1529             });
1530 
1531             // Touch one
1532             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
1533             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one1.h"), "");
1534 
1535             CanonicalTrackedInputFiles d = new CanonicalTrackedInputFiles
1536                 (
1537                     DependencyTestHelper.MockTask,
1538                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.tlog"))),
1539                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.cpp"))),
1540                     null,
1541                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.obj"))),
1542                     false, /* no minimal rebuild optimization */
1543                     false /* shred composite rooting markers */
1544                 );
1545 
1546             ITaskItem[] outofdate = d.ComputeSourcesNeedingCompilation();
1547 
1548             Assert.True(outofdate.Length == 1);
1549             Assert.True(outofdate[0].ItemSpec == Path.Combine("TestFiles", "one.cpp"));
1550         }
1551 
1552         [Fact]
MultipleCanonicalCLAcrossCommand2()1553         public void MultipleCanonicalCLAcrossCommand2()
1554         {
1555             Console.WriteLine("Test: MultipleCanonicalCLAcrossCommand2");
1556             // Prepare files
1557             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one1.h"), "");
1558             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
1559             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one3.h"), "");
1560             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
1561             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
1562             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
1563             File.WriteAllLines(Path.Combine("TestFiles", "one.tlog"), new string[] {
1564                 "#Command some-command",
1565                 Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
1566                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
1567                 "#Command some-command1",
1568                 Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
1569                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")),
1570                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")),
1571             });
1572 
1573             // Touch one
1574             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
1575             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
1576 
1577             CanonicalTrackedInputFiles d = new CanonicalTrackedInputFiles
1578                 (
1579                     DependencyTestHelper.MockTask,
1580                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.tlog"))),
1581                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.cpp"))),
1582                     null,
1583                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.obj"))),
1584                     false, /* no minimal rebuild optimization */
1585                     false /* shred composite rooting markers */
1586                 );
1587 
1588             ITaskItem[] outofdate = d.ComputeSourcesNeedingCompilation();
1589 
1590             Assert.True(outofdate.Length == 1);
1591             Assert.True(outofdate[0].ItemSpec == Path.Combine("TestFiles", "one.cpp"));
1592         }
1593 
1594         [Fact]
MultipleCanonicalCLAcrossCommandNonDependency()1595         public void MultipleCanonicalCLAcrossCommandNonDependency()
1596         {
1597             Console.WriteLine("Test: MultipleCanonicalCLAcrossCommandNonDependency");
1598             // Prepare files
1599             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one1.h"), "");
1600             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
1601             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
1602             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two1.h"), "");
1603             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two2.h"), "");
1604             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two3.h"), "");
1605             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two.cpp"), "");
1606 
1607             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
1608             File.WriteAllLines(Path.Combine("TestFiles", "one.tlog"), new string[] {
1609                 "#Command some-command",
1610                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
1611                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
1612                 "#Command some-command1",
1613                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "two.cpp")), // this root marker represents the end of the dependencies for one.cpp
1614                 Path.GetFullPath(Path.Combine("TestFiles", "two1.h")),
1615                 Path.GetFullPath(Path.Combine("TestFiles", "two2.h")),
1616                 Path.GetFullPath(Path.Combine("TestFiles", "two3.h")),
1617             });
1618 
1619             // Touch one
1620             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
1621             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two2.h"), "");
1622 
1623             CanonicalTrackedInputFiles d = new CanonicalTrackedInputFiles
1624                 (
1625                     DependencyTestHelper.MockTask,
1626                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.tlog"))),
1627                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.cpp"))),
1628                     null,
1629                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.obj"))),
1630                     false, /* no minimal rebuild optimization */
1631                     false /* shred composite rooting markers */
1632                 );
1633 
1634             ITaskItem[] outofdate = d.ComputeSourcesNeedingCompilation();
1635 
1636             Assert.Equal(0, outofdate.Length);
1637         }
1638 
1639         [Fact]
MultipleCanonicalCLAcrossTlogs1()1640         public void MultipleCanonicalCLAcrossTlogs1()
1641         {
1642             Console.WriteLine("Test: MultipleCanonicalCLAcrossTlogs1");
1643             // Prepare files
1644             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one1.h"), "");
1645             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
1646             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one3.h"), "");
1647             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
1648             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
1649             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
1650             File.WriteAllLines(Path.Combine("TestFiles", "one1.tlog"), new string[] {
1651                 "#Command some-command",
1652                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
1653                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
1654             });
1655 
1656             File.WriteAllLines(Path.Combine("TestFiles", "one2.tlog"), new string[] {
1657                 "#Command some-command1",
1658                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
1659                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")),
1660                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")),
1661             });
1662 
1663             // Touch one
1664             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
1665             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one1.h"), "");
1666 
1667             ITaskItem[] tlogs = {
1668                                     new TaskItem(Path.Combine("TestFiles", "one1.tlog")),
1669                                     new TaskItem(Path.Combine("TestFiles", "one2.tlog"))
1670                                 };
1671 
1672             CanonicalTrackedInputFiles d = new CanonicalTrackedInputFiles
1673                 (
1674                     DependencyTestHelper.MockTask,
1675                     tlogs,
1676                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.cpp"))),
1677                     null,
1678                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.obj"))),
1679                     false, /* no minimal rebuild optimization */
1680                     false /* shred composite rooting markers */
1681                 );
1682 
1683             ITaskItem[] outofdate = d.ComputeSourcesNeedingCompilation();
1684 
1685             Assert.True(outofdate.Length == 1);
1686             Assert.True(outofdate[0].ItemSpec == Path.Combine("TestFiles", "one.cpp"));
1687         }
1688 
1689         [Fact]
MultipleCanonicalCLAcrossTlogs2()1690         public void MultipleCanonicalCLAcrossTlogs2()
1691         {
1692             Console.WriteLine("Test: MultipleCanonicalCLAcrossTlogs2");
1693             // Prepare files
1694             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one1.h"), "");
1695             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
1696             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one3.h"), "");
1697             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
1698             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
1699             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
1700             File.WriteAllLines(Path.Combine("TestFiles", "one1.tlog"), new string[] {
1701                 "#Command some-command",
1702                 Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
1703                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
1704             });
1705 
1706             File.WriteAllLines(Path.Combine("TestFiles", "one2.tlog"), new string[] {
1707                 "#Command some-command1",
1708                 Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
1709                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")),
1710                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")),
1711             });
1712 
1713             // Touch one
1714             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
1715             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
1716 
1717             ITaskItem[] tlogs = {
1718                                     new TaskItem(Path.Combine("TestFiles", "one1.tlog")),
1719                                     new TaskItem(Path.Combine("TestFiles", "one2.tlog"))
1720                                 };
1721 
1722             CanonicalTrackedInputFiles d = new CanonicalTrackedInputFiles
1723                 (
1724                     DependencyTestHelper.MockTask,
1725                     tlogs,
1726                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.cpp"))),
1727                     null,
1728                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.obj"))),
1729                     false, /* no minimal rebuild optimization */
1730                     false /* shred composite rooting markers */
1731                 );
1732 
1733             ITaskItem[] outofdate = d.ComputeSourcesNeedingCompilation();
1734 
1735             Assert.True(outofdate.Length == 1);
1736             Assert.True(outofdate[0].ItemSpec == Path.Combine("TestFiles", "one.cpp"));
1737         }
1738 
1739         [Fact]
SingleRootedCL()1740         public void SingleRootedCL()
1741         {
1742             Console.WriteLine("Test: SingleRootedCL");
1743             // Prepare files
1744             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.h"), "");
1745             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
1746             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
1747             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
1748             File.WriteAllLines(Path.Combine("TestFiles", "one.tlog"), new string[] {
1749                 "#Command some-command",
1750                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
1751                 Path.GetFullPath(Path.Combine("TestFiles", "one.h")),
1752             });
1753 
1754             // Touch one
1755             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
1756             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.h"), "");
1757 
1758             CanonicalTrackedInputFiles d = new CanonicalTrackedInputFiles
1759                 (
1760                     DependencyTestHelper.MockTask,
1761                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.tlog"))),
1762                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.cpp"))),
1763                     null,
1764                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.obj"))),
1765                     false, /* no minimal rebuild optimization */
1766                     false /* shred composite rooting markers */
1767                 );
1768 
1769             ITaskItem[] outofdate = d.ComputeSourcesNeedingCompilation();
1770 
1771             Assert.True(outofdate.Length == 1);
1772             Assert.True(outofdate[0].ItemSpec == Path.Combine("TestFiles", "one.cpp"));
1773         }
1774 
1775         [Fact]
MultipleRootedCLAcrossTlogs1()1776         public void MultipleRootedCLAcrossTlogs1()
1777         {
1778             Console.WriteLine("Test: MultipleRootedCLAcrossTlogs1");
1779             // Prepare files
1780             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one1.h"), "");
1781             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
1782             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one3.h"), "");
1783             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
1784             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
1785             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
1786             File.WriteAllLines(Path.Combine("TestFiles", "one1.tlog"), new string[] {
1787                 "#Command some-command",
1788                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
1789                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
1790             });
1791 
1792             File.WriteAllLines(Path.Combine("TestFiles", "one2.tlog"), new string[] {
1793                 "#Command some-command1",
1794                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
1795                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")),
1796                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")),
1797             });
1798 
1799             // Touch one
1800             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
1801             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one1.h"), "");
1802 
1803             ITaskItem[] tlogs = {
1804                                     new TaskItem(Path.Combine("TestFiles", "one1.tlog")),
1805                                     new TaskItem(Path.Combine("TestFiles", "one2.tlog"))
1806                                 };
1807 
1808             CanonicalTrackedInputFiles d = new CanonicalTrackedInputFiles
1809                 (
1810                     DependencyTestHelper.MockTask,
1811                     tlogs,
1812                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.cpp"))),
1813                     null,
1814                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.obj"))),
1815                     false, /* no minimal rebuild optimization */
1816                     false /* shred composite rooting markers */
1817                 );
1818 
1819             ITaskItem[] outofdate = d.ComputeSourcesNeedingCompilation();
1820 
1821             Assert.True(outofdate.Length == 1);
1822             Assert.True(outofdate[0].ItemSpec == Path.Combine("TestFiles", "one.cpp"));
1823         }
1824 
1825         [Fact]
MultipleRootedCL()1826         public void MultipleRootedCL()
1827         {
1828             Console.WriteLine("Test: MultipleRootedCL");
1829             // Prepare files
1830             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one1.h"), "");
1831             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
1832             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one3.h"), "");
1833             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
1834             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
1835             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two1.h"), "");
1836             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two2.h"), "");
1837             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two3.h"), "");
1838             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two.cpp"), "");
1839 
1840             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
1841             File.WriteAllLines(Path.Combine("TestFiles", "one1.tlog"), new string[] {
1842                 "#Command some-command",
1843                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
1844                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
1845                 "#Command some-command1",
1846                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "two.cpp")),
1847                 Path.GetFullPath(Path.Combine("TestFiles", "two2.h")),
1848                 Path.GetFullPath(Path.Combine("TestFiles", "two3.h")),
1849             });
1850 
1851             // Touch one
1852             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
1853             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two2.h"), "");
1854 
1855             ITaskItem[] tlogs = {
1856                                     new TaskItem(Path.Combine("TestFiles", "one1.tlog")),
1857                                     new TaskItem(Path.Combine("TestFiles", "one2.tlog"))
1858                                 };
1859 
1860             CanonicalTrackedInputFiles d = new CanonicalTrackedInputFiles
1861                 (
1862                     DependencyTestHelper.MockTask,
1863                     tlogs,
1864                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "two.cpp"))),
1865                     null,
1866                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.obj"))),
1867                     false, /* no minimal rebuild optimization */
1868                     false /* shred composite rooting markers */
1869                 );
1870 
1871             ITaskItem[] outofdate = d.ComputeSourcesNeedingCompilation();
1872 
1873             Assert.True(outofdate.Length == 1);
1874             Assert.True(outofdate[0].ItemSpec == Path.Combine("TestFiles", "two.cpp"));
1875         }
1876 
1877         [Fact]
MultipleRootedCLNonDependency()1878         public void MultipleRootedCLNonDependency()
1879         {
1880             Console.WriteLine("Test: MultipleRootedCLNonDependency");
1881             // Prepare files
1882             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one1.h"), "");
1883             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
1884             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one3.h"), "");
1885             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
1886             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
1887             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two1.h"), "");
1888             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two2.h"), "");
1889             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two3.h"), "");
1890             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two.cpp"), "");
1891 
1892             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
1893             File.WriteAllLines(Path.Combine("TestFiles", "one1.tlog"), new string[] {
1894                 "#Command some-command",
1895                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
1896                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
1897                 "#Command some-command1",
1898                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "two.cpp")), // this root marker represents the end of the dependencies for one.cpp
1899                 Path.GetFullPath(Path.Combine("TestFiles", "two2.h")),
1900                 Path.GetFullPath(Path.Combine("TestFiles", "two3.h")),
1901             });
1902 
1903             // Touch one
1904             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
1905             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two2.h"), "");
1906 
1907             ITaskItem[] tlogs = {
1908                                     new TaskItem(Path.Combine("TestFiles", "one1.tlog"))
1909                                 };
1910 
1911             CanonicalTrackedInputFiles d = new CanonicalTrackedInputFiles
1912                 (
1913                     DependencyTestHelper.MockTask,
1914                     tlogs,
1915                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.cpp"))),
1916                     null,
1917                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.obj"))),
1918                     false, /* no minimal rebuild optimization */
1919                     false /* shred composite rooting markers */
1920                 );
1921 
1922             ITaskItem[] outofdate = d.ComputeSourcesNeedingCompilation();
1923 
1924             Assert.Equal(0, outofdate.Length);
1925         }
1926 
1927         [Fact]
MultipleRootedCLAcrossTlogs2()1928         public void MultipleRootedCLAcrossTlogs2()
1929         {
1930             Console.WriteLine("Test: MultipleRootedCLAcrossTlogs2");
1931             // Prepare files
1932             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one1.h"), "");
1933             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
1934             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one3.h"), "");
1935             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
1936             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
1937             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
1938             File.WriteAllLines(Path.Combine("TestFiles", "one1.tlog"), new string[] {
1939                 "#Command some-command",
1940                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
1941                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
1942             });
1943 
1944             File.WriteAllLines(Path.Combine("TestFiles", "one2.tlog"), new string[] {
1945                 "#Command some-command1",
1946                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
1947                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")),
1948                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")),
1949             });
1950 
1951             // Touch one
1952             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
1953             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
1954 
1955             ITaskItem[] tlogs = {
1956                                     new TaskItem(Path.Combine("TestFiles", "one1.tlog")),
1957                                     new TaskItem(Path.Combine("TestFiles", "one2.tlog"))
1958                                 };
1959 
1960             CanonicalTrackedInputFiles d = new CanonicalTrackedInputFiles
1961                 (
1962                     DependencyTestHelper.MockTask,
1963                     tlogs,
1964                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.cpp"))),
1965                     null,
1966                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.obj"))),
1967                     false, /* no minimal rebuild optimization */
1968                     false /* shred composite rooting markers */
1969                 );
1970 
1971             ITaskItem[] outofdate = d.ComputeSourcesNeedingCompilation();
1972 
1973             Assert.True(outofdate.Length == 1);
1974             Assert.True(outofdate[0].ItemSpec == Path.Combine("TestFiles", "one.cpp"));
1975         }
1976 
1977 
1978         [Fact]
OutputSingleCanonicalCL()1979         public void OutputSingleCanonicalCL()
1980         {
1981             Console.WriteLine("Test: OutputSingleCanonicalCL");
1982             // Prepare files
1983             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
1984             File.WriteAllLines(Path.Combine("TestFiles", "one.tlog"), new string[] {
1985                 "#Command some-command",
1986                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
1987                 Path.GetFullPath(Path.Combine("TestFiles", "oNe.obj")),
1988             });
1989 
1990             CanonicalTrackedOutputFiles d = new CanonicalTrackedOutputFiles(DependencyTestHelper.MockTask,
1991                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.tlog"))));
1992 
1993             ITaskItem[] outputs = d.OutputsForSource(new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "one.cpp"))));
1994 
1995             Assert.True(outputs.Length == 1);
1996             Assert.True(outputs[0].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "oNe.obj")));
1997         }
1998 
1999         [Fact]
OutputSingleCanonicalCLAcrossTlogs()2000         public void OutputSingleCanonicalCLAcrossTlogs()
2001         {
2002             Console.WriteLine("Test: OutputSingleCanonicalCLAcrossTlogs");
2003             // Prepare files
2004             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
2005             File.WriteAllLines(Path.Combine("TestFiles", "one.tlog"), new string[] {
2006                 "#Command some-command",
2007                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
2008                 Path.GetFullPath(Path.Combine("TestFiles", "oNe.obj")),
2009             });
2010 
2011             File.WriteAllLines(Path.Combine("TestFiles", "two.tlog"), new string[] {
2012                 "#Command some-command",
2013                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
2014                 Path.GetFullPath(Path.Combine("TestFiles", "one.pch")),
2015             });
2016 
2017             ITaskItem[] tlogs = {
2018                                     new TaskItem(Path.Combine("TestFiles", "one.tlog")),
2019                                     new TaskItem(Path.Combine("TestFiles", "two.tlog"))
2020                                 };
2021 
2022             CanonicalTrackedOutputFiles d = new CanonicalTrackedOutputFiles(DependencyTestHelper.MockTask,
2023                     tlogs);
2024 
2025             ITaskItem[] outputs = d.OutputsForSource(new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "one.cpp"))));
2026 
2027             Assert.True(outputs.Length == 2);
2028             Assert.True(outputs[0].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "oNe.obj")));
2029             Assert.True(outputs[1].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "one.pch")));
2030         }
2031 
2032         [Fact]
OutputNonExistentTlog()2033         public void OutputNonExistentTlog()
2034         {
2035             Console.WriteLine("Test: NonExistentTlog");
2036 
2037             // Just to be sure, delete the test tlog.
2038             File.Delete(Path.Combine("TestFiles", "one.tlog"));
2039 
2040             CanonicalTrackedOutputFiles d = new CanonicalTrackedOutputFiles(DependencyTestHelper.MockTask,
2041                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.tlog"))));
2042 
2043             ITaskItem[] outputs = d.OutputsForSource(new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "one.cpp"))));
2044 
2045             Assert.Null(outputs);
2046         }
2047 
2048         [Fact]
OutputMultipleCanonicalCL()2049         public void OutputMultipleCanonicalCL()
2050         {
2051             Console.WriteLine("Test: OutputMultipleCanonicalCL");
2052 
2053             ITaskItem[] sources = new TaskItem[] {
2054                                     new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "one.cpp"))),
2055                                     new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "two.cpp"))),
2056                                     new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "three.cpp")))};
2057 
2058             // Prepare files
2059             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
2060             File.WriteAllLines(Path.Combine("TestFiles", "one.tlog"), new string[] {
2061                 "#Command some-command",
2062                 "^" + FileTracker.FormatRootingMarker(sources),
2063                 Path.GetFullPath(Path.Combine("TestFiles", "oNe.obj")),
2064                 Path.GetFullPath(Path.Combine("TestFiles", "two.obj")),
2065                 Path.GetFullPath(Path.Combine("TestFiles", "three.obj")),
2066             });
2067 
2068             CanonicalTrackedOutputFiles d = new CanonicalTrackedOutputFiles(DependencyTestHelper.MockTask,
2069                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.tlog"))));
2070 
2071             ITaskItem[] outputs = d.OutputsForSource(sources);
2072 
2073             Assert.True(outputs.Length == 3);
2074             Assert.True(outputs[0].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "oNe.obj")));
2075             Assert.True(outputs[1].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "two.obj")));
2076             Assert.True(outputs[2].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "three.obj")));
2077         }
2078 
2079         [Fact]
OutputMultipleCanonicalCLSubrootMatch()2080         public void OutputMultipleCanonicalCLSubrootMatch()
2081         {
2082             Console.WriteLine("Test: OutputMultipleCanonicalCLSubrootMatch");
2083 
2084             // sources is a subset of source2
2085             ITaskItem[] sources = new TaskItem[] {
2086                                     new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "one.cpp"))),
2087                                     new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "two.cpp"))),
2088                                     new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "three.cpp")))};
2089             ITaskItem[] sources2 = new TaskItem[] {
2090                                     new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "one.cpp"))),
2091                                     new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "two.cpp"))),
2092                                     new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "three.cpp"))),
2093                                     new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "four.cpp"))),
2094                                     new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "five.cpp")))};
2095 
2096             // Prepare files
2097             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
2098             File.WriteAllLines(Path.Combine("TestFiles", "one.tlog"), new string[] {
2099                 "#Command some-command",
2100                 "^" + FileTracker.FormatRootingMarker(sources),
2101                 Path.GetFullPath(Path.Combine("TestFiles", "oNe.obj")),
2102                 Path.GetFullPath(Path.Combine("TestFiles", "two.obj")),
2103                 Path.GetFullPath(Path.Combine("TestFiles", "three.obj")),
2104                 "^" + FileTracker.FormatRootingMarker(sources2),
2105                 Path.GetFullPath(Path.Combine("TestFiles", "fOUr.obj")),
2106                 Path.GetFullPath(Path.Combine("TestFiles", "fIve.obj")),
2107                 Path.GetFullPath(Path.Combine("TestFiles", "sIx.obj")),
2108                 Path.GetFullPath(Path.Combine("TestFiles", "sEvEn.obj")),
2109                 Path.GetFullPath(Path.Combine("TestFiles", "EIght.obj")),
2110             });
2111 
2112             CanonicalTrackedOutputFiles d = new CanonicalTrackedOutputFiles(DependencyTestHelper.MockTask,
2113                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.tlog"))));
2114 
2115             ITaskItem[] outputs = d.OutputsForSource(sources2, /*searchForSubRootsInCompositeRootingMarkers*/ false);
2116 
2117             Assert.True(outputs.Length == 5);
2118             Assert.True(outputs[0].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "fOUr.obj")));
2119             Assert.True(outputs[1].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "fIve.obj")));
2120             Assert.True(outputs[2].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "sIx.obj")));
2121             Assert.True(outputs[3].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "sEvEn.obj")));
2122             Assert.True(outputs[4].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "EIght.obj")));
2123 
2124             ITaskItem[] outputs2 = d.OutputsForSource(sources2, /*searchForSubRootsInCompositeRootingMarkers*/ true);
2125 
2126             Assert.True(outputs2.Length == 8);
2127             Assert.True(outputs2[0].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "oNe.obj")));
2128             Assert.True(outputs2[1].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "two.obj")));
2129             Assert.True(outputs2[2].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "three.obj")));
2130             Assert.True(outputs2[3].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "fOUr.obj")));
2131             Assert.True(outputs2[4].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "fIve.obj")));
2132             Assert.True(outputs2[5].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "sIx.obj")));
2133             Assert.True(outputs2[6].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "sEvEn.obj")));
2134             Assert.True(outputs2[7].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "EIght.obj")));
2135 
2136             // Test if sources can find the superset.
2137             ITaskItem[] outputs3 = d.OutputsForSource(sources, /*searchForSubRootsInCompositeRootingMarkers*/ true);
2138 
2139             Assert.True(outputs3.Length == 8);
2140             Assert.True(outputs3[0].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "oNe.obj")));
2141             Assert.True(outputs3[1].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "two.obj")));
2142             Assert.True(outputs3[2].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "three.obj")));
2143             Assert.True(outputs3[3].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "fOUr.obj")));
2144             Assert.True(outputs3[4].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "fIve.obj")));
2145             Assert.True(outputs3[5].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "sIx.obj")));
2146             Assert.True(outputs3[6].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "sEvEn.obj")));
2147             Assert.True(outputs3[7].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "EIght.obj")));
2148 
2149             ITaskItem[] outputs4 = d.OutputsForSource(sources, /*searchForSubRootsInCompositeRootingMarkers*/ false);
2150 
2151             Assert.True(outputs4.Length == 3);
2152             Assert.True(outputs4[0].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "oNe.obj")));
2153             Assert.True(outputs4[1].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "two.obj")));
2154             Assert.True(outputs4[2].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "three.obj")));
2155         }
2156 
2157         [Fact]
OutputMultipleCanonicalCLSubrootMisMatch()2158         public void OutputMultipleCanonicalCLSubrootMisMatch()
2159         {
2160             Console.WriteLine("Test: OutputMultipleCanonicalCLSubrootMisMatch");
2161 
2162             // sources is NOT a subset of source
2163             ITaskItem[] sources = new TaskItem[] {
2164                                     new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "one.cpp"))),
2165                                     new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "two.cpp"))),
2166                                     new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "three.cpp")))};
2167             ITaskItem[] sources2 = new TaskItem[] {
2168                                     new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "one.cpp"))),
2169                                     new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "two.cpp"))),
2170                                     new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "four.cpp"))),
2171                                     new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "five.cpp")))};
2172             ITaskItem[] sources2Match = new TaskItem[] {
2173                                     new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "one.cpp"))),
2174                                     new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "four.cpp"))),
2175                                     new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "two.cpp"))),
2176                                     new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "five.cpp")))};
2177             ITaskItem[] sourcesPlusOne = new TaskItem[] {
2178                                     new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "one.cpp"))),
2179                                     new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "two.cpp"))),
2180                                     new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "eight.cpp"))),
2181                                     new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "three.cpp")))};
2182 
2183             // Do note sources2Match and source2 is missing three.cpp.  It is to test if the RootContainsAllSubRootComponents can handle the case.
2184 
2185             // Prepare files
2186             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
2187             File.WriteAllLines(Path.Combine("TestFiles", "one.tlog"), new string[] {
2188                 "#Command some-command",
2189                 "^" + FileTracker.FormatRootingMarker(sources),
2190                 Path.GetFullPath(Path.Combine("TestFiles", "oNe.obj")),
2191                 Path.GetFullPath(Path.Combine("TestFiles", "two.obj")),
2192                 Path.GetFullPath(Path.Combine("TestFiles", "three.obj")),
2193                 "^" + FileTracker.FormatRootingMarker(sources2),
2194                 Path.GetFullPath(Path.Combine("TestFiles", "fOUr.obj")),
2195                 Path.GetFullPath(Path.Combine("TestFiles", "fIve.obj")),
2196                 Path.GetFullPath(Path.Combine("TestFiles", "sIx.obj")),
2197                 Path.GetFullPath(Path.Combine("TestFiles", "sEvEn.obj")),
2198                 Path.GetFullPath(Path.Combine("TestFiles", "EIght.obj")),
2199             });
2200 
2201             CanonicalTrackedOutputFiles d = new CanonicalTrackedOutputFiles(DependencyTestHelper.MockTask,
2202                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.tlog"))));
2203 
2204             ITaskItem[] outputs = d.OutputsForSource(sources2Match, /*searchForSubRootsInCompositeRootingMarkers*/ false);
2205 
2206             Assert.True(outputs.Length == 5);
2207             Assert.True(outputs[0].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "fOUr.obj")));
2208             Assert.True(outputs[1].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "fIve.obj")));
2209             Assert.True(outputs[2].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "sIx.obj")));
2210             Assert.True(outputs[3].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "sEvEn.obj")));
2211             Assert.True(outputs[4].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "EIght.obj")));
2212 
2213             ITaskItem[] outputs2 = d.OutputsForSource(sources2Match, /*searchForSubRootsInCompositeRootingMarkers*/ true);
2214 
2215             Assert.True(outputs2.Length == 5);
2216             Assert.True(outputs2[0].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "fOUr.obj")));
2217             Assert.True(outputs2[1].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "fIve.obj")));
2218             Assert.True(outputs2[2].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "sIx.obj")));
2219             Assert.True(outputs2[3].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "sEvEn.obj")));
2220             Assert.True(outputs2[4].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "EIght.obj")));
2221 
2222             ITaskItem[] outputs3 = d.OutputsForSource(sourcesPlusOne, /*searchForSubRootsInCompositeRootingMarkers*/ true);
2223 
2224             Assert.True(outputs3.Length == 3);
2225             Assert.True(outputs3[0].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "oNe.obj")));
2226             Assert.True(outputs3[1].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "two.obj")));
2227             Assert.True(outputs3[2].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "three.obj")));
2228 
2229             ITaskItem[] outputs4 = d.OutputsForSource(sourcesPlusOne, /*searchForSubRootsInCompositeRootingMarkers*/ false);
2230 
2231             Assert.Equal(0, outputs4.Length);
2232         }
2233 
2234         [Fact]
OutputMultipleCanonicalCLLongTempPath()2235         public void OutputMultipleCanonicalCLLongTempPath()
2236         {
2237             Console.WriteLine("Test: OutputMultipleCanonicalCLLongTempPath");
2238 
2239             ITaskItem[] sources = new TaskItem[] {
2240                                     new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "one.cpp"))),
2241                                     new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "two.cpp"))),
2242                                     new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "three.cpp")))};
2243 
2244             string oldTempPath = Environment.GetEnvironmentVariable("TEMP");
2245             string oldTmpPath = Environment.GetEnvironmentVariable("TMP");
2246             string newTempPath = Path.GetFullPath(Path.Combine("TestFiles", "ThisIsAReallyVeryLongTemporaryPlace", "ThatIsLongerThanTheSourcePaths"));
2247 
2248             Directory.CreateDirectory(newTempPath);
2249             Environment.SetEnvironmentVariable("TEMP", newTempPath);
2250             Environment.SetEnvironmentVariable("TMP", newTempPath);
2251 
2252             Console.WriteLine("Test: OutputMultipleCanonicalCL");
2253 
2254             // Prepare files
2255             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
2256             File.WriteAllLines(Path.Combine("TestFiles", "one.tlog"), new string[] {
2257                 "#Command some-command",
2258                 "^" + FileTracker.FormatRootingMarker(sources),
2259                 Path.GetFullPath(Path.Combine("TestFiles", "oNe.obj")),
2260                 Path.GetFullPath(Path.Combine("TestFiles", "two.obj")),
2261                 Path.GetFullPath(Path.Combine("TestFiles", "three.obj")),
2262             });
2263 
2264             CanonicalTrackedOutputFiles d = new CanonicalTrackedOutputFiles(DependencyTestHelper.MockTask,
2265                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.tlog"))));
2266 
2267             ITaskItem[] outputs = d.OutputsForSource(sources);
2268 
2269             Environment.SetEnvironmentVariable("TEMP", oldTempPath);
2270             Environment.SetEnvironmentVariable("TMP", oldTmpPath);
2271 
2272             Assert.True(outputs.Length == 3);
2273             Assert.True(outputs[0].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "oNe.obj")));
2274             Assert.True(outputs[1].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "two.obj")));
2275             Assert.True(outputs[2].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "three.obj")));
2276         }
2277 
2278         [Fact]
OutputMultipleCanonicalCLAcrossTLogs()2279         public void OutputMultipleCanonicalCLAcrossTLogs()
2280         {
2281             Console.WriteLine("Test: OutputMultipleCanonicalCLAcrossTLogs");
2282 
2283             ITaskItem[] sources = new TaskItem[] {
2284                                     new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "one.cpp"))),
2285                                     new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "two.cpp"))),
2286                                     new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "three.cpp")))};
2287 
2288             // Prepare files
2289             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
2290             File.WriteAllLines(Path.Combine("TestFiles", "one.tlog"), new string[] {
2291                 "#Command some-command",
2292                 "^" + FileTracker.FormatRootingMarker(sources),
2293                 Path.GetFullPath(Path.Combine("TestFiles", "oNe.obj")),
2294             });
2295 
2296             File.WriteAllLines(Path.Combine("TestFiles", "two.tlog"), new string[] {
2297                 "#Command some-command",
2298                 "^" + FileTracker.FormatRootingMarker(sources),
2299                 Path.GetFullPath(Path.Combine("TestFiles", "two.obj")),
2300                 Path.GetFullPath(Path.Combine("TestFiles", "three.obj")),
2301             });
2302 
2303             ITaskItem[] tlogs = {
2304                                     new TaskItem(Path.Combine("TestFiles", "one.tlog")),
2305                                     new TaskItem(Path.Combine("TestFiles", "two.tlog"))
2306                                 };
2307 
2308             CanonicalTrackedOutputFiles d = new CanonicalTrackedOutputFiles(DependencyTestHelper.MockTask,
2309                     tlogs);
2310 
2311             ITaskItem[] outputs = d.OutputsForSource(sources);
2312 
2313             Assert.True(outputs.Length == 3);
2314             Assert.True(outputs[0].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "oNe.obj")));
2315             Assert.True(outputs[1].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "two.obj")));
2316             Assert.True(outputs[2].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "three.obj")));
2317         }
2318 
2319         [Fact]
OutputMultipleSingleSubRootCanonicalCL()2320         public void OutputMultipleSingleSubRootCanonicalCL()
2321         {
2322             Console.WriteLine("Test: OutputMultipleSingleSubRootCanonicalCL");
2323 
2324             ITaskItem[] sources = new TaskItem[] {
2325                                     new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "one.cpp"))),
2326                                     new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "two.cpp"))),
2327                                     new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "three.cpp")))};
2328 
2329             // Prepare files
2330             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
2331             File.WriteAllLines(Path.Combine("TestFiles", "one.tlog"), new string[] {
2332                 "#Command some-command",
2333                 "^" + FileTracker.FormatRootingMarker(sources),
2334                 Path.GetFullPath(Path.Combine("TestFiles", "oNe.obj")),
2335                 Path.GetFullPath(Path.Combine("TestFiles", "two.obj")),
2336                 Path.GetFullPath(Path.Combine("TestFiles", "three.obj")),
2337             });
2338 
2339             CanonicalTrackedOutputFiles d = new CanonicalTrackedOutputFiles(DependencyTestHelper.MockTask,
2340                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.tlog"))));
2341 
2342             ITaskItem[] outputs = d.OutputsForSource(new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "two.cpp"))));
2343 
2344             Assert.True(outputs.Length == 3);
2345             Assert.True(outputs[0].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "oNe.obj")));
2346             Assert.True(outputs[1].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "two.obj")));
2347             Assert.True(outputs[2].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "three.obj")));
2348         }
2349 
2350         [Fact]
OutputMultipleUnrecognisedRootCanonicalCL()2351         public void OutputMultipleUnrecognisedRootCanonicalCL()
2352         {
2353             Console.WriteLine("Test: OutputMultipleUnrecognisedRootCanonicalCL");
2354 
2355             // Prepare files
2356             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
2357             File.WriteAllLines(Path.Combine("TestFiles", "one.tlog"), new string[] {
2358                 "#Command some-command",
2359                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")) + "|" + Path.GetFullPath(Path.Combine("TestFiles", "two.cpp")) + "|" + Path.GetFullPath(Path.Combine("TestFiles", "three.cpp")),
2360                 Path.GetFullPath(Path.Combine("TestFiles", "oNe.obj")),
2361                 Path.GetFullPath(Path.Combine("TestFiles", "two.obj")),
2362                 Path.GetFullPath(Path.Combine("TestFiles", "three.obj")),
2363             });
2364 
2365             CanonicalTrackedOutputFiles d = new CanonicalTrackedOutputFiles(DependencyTestHelper.MockTask,
2366                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.tlog"))));
2367 
2368             ITaskItem[] outputs = d.OutputsForSource(new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "four.cpp"))));
2369 
2370             Assert.Equal(0, outputs.Length);
2371         }
2372 
2373         [Fact]
OutputCLMinimalRebuildOptimization()2374         public void OutputCLMinimalRebuildOptimization()
2375         {
2376             Console.WriteLine("Test: OutputCLMinimalRebuildOptimization");
2377 
2378             // Prepare read tlog
2379             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one1.h"), "");
2380             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
2381             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one3.h"), "");
2382             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
2383             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two.cpp"), "");
2384             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "three.cpp"), "");
2385             Thread.Sleep(_sleepTimeMilliseconds);
2386             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
2387             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two.obj"), "");
2388             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "three.obj"), "");
2389             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
2390             File.WriteAllLines(Path.Combine("TestFiles", "one.read.tlog"), new string[] {
2391                 "#Command some-command",
2392                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
2393                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
2394                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")),
2395                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")),
2396                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "two.cpp")),
2397                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
2398                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")),
2399                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")),
2400                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "three.cpp")),
2401                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
2402                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")),
2403                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")),
2404             });
2405 
2406             // Our source files
2407             ITaskItem[] sources = {
2408                                     new TaskItem(Path.Combine("TestFiles", "one.cpp")),
2409                                     new TaskItem(Path.Combine("TestFiles", "two.cpp")),
2410                                     new TaskItem(Path.Combine("TestFiles", "three.cpp")),
2411                                 };
2412 
2413             // Prepare write tlog
2414             // This includes individual output information for each root
2415             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
2416             File.WriteAllLines(Path.Combine("TestFiles", "one.write.tlog"), new string[] {
2417                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
2418                 Path.GetFullPath(Path.Combine("TestFiles", "one.obj")),
2419                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "two.cpp")),
2420                 Path.GetFullPath(Path.Combine("TestFiles", "two.obj")),
2421                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "three.cpp")),
2422                 Path.GetFullPath(Path.Combine("TestFiles", "three.obj")),
2423                 "#Command some-command",
2424                 "^" + FileTracker.FormatRootingMarker(sources),
2425                 Path.GetFullPath(Path.Combine("TestFiles", "one.obj")),
2426                 Path.GetFullPath(Path.Combine("TestFiles", "two.obj")),
2427                 Path.GetFullPath(Path.Combine("TestFiles", "three.obj")),
2428             });
2429 
2430 
2431             // Represent our tracked and computed outputs
2432             CanonicalTrackedOutputFiles outputs = new CanonicalTrackedOutputFiles(DependencyTestHelper.MockTask, DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.write.tlog"))));
2433 
2434             // Represent our tracked and provided inputs
2435             CanonicalTrackedInputFiles d = new CanonicalTrackedInputFiles
2436                 (
2437                     DependencyTestHelper.MockTask,
2438                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.read.tlog"))),
2439                     sources,
2440                     null,
2441                     outputs,
2442                     false, /* no minimal rebuild optimization */
2443                     false /* shred composite rooting markers */
2444                 );
2445 
2446             // First of all, all things should be up to date
2447             ITaskItem[] outofdate = d.ComputeSourcesNeedingCompilation();
2448             Assert.Equal(0, outofdate.Length);
2449 
2450             // Delete one of the outputs in the group
2451             File.Delete(Path.GetFullPath(Path.Combine("TestFiles", "two.obj")));
2452 
2453             // With optimization off, all sources in the group will need compilation
2454             d.SourcesNeedingCompilation = null;
2455             outofdate = d.ComputeSourcesNeedingCompilation();
2456             Assert.Equal(3, outofdate.Length);
2457 
2458             // With optimization on, only the source that matches the output will need compilation
2459             d = new CanonicalTrackedInputFiles
2460                     (
2461                         DependencyTestHelper.MockTask,
2462                         DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.read.tlog"))),
2463                         sources,
2464                         null,
2465                         outputs,
2466                         true, /* enable minimal rebuild optimization */
2467                         false /* shred composite rooting markers */
2468                     );
2469 
2470             outofdate = d.ComputeSourcesNeedingCompilation();
2471             Assert.Equal(1, outofdate.Length);
2472             // And the source is.. two.cpp!
2473             Assert.True(outofdate[0].ItemSpec == Path.Combine("TestFiles", "two.cpp"));
2474         }
2475 
2476         [Fact]
OutputCLMinimalRebuildOptimizationComputed()2477         public void OutputCLMinimalRebuildOptimizationComputed()
2478         {
2479             Console.WriteLine("Test: OutputCLMinimalRebuildOptimizationComputed");
2480 
2481             // Prepare read tlog
2482             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one1.h"), "");
2483             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
2484             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one3.h"), "");
2485             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
2486             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two.cpp"), "");
2487             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "three.cpp"), "");
2488             Thread.Sleep(_sleepTimeMilliseconds);
2489             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
2490             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two.obj"), "");
2491             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "three.obj"), "");
2492             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
2493             File.WriteAllLines(Path.Combine("TestFiles", "one.read.tlog"), new string[] {
2494                 "#Command some-command",
2495                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
2496                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
2497                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")),
2498                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")),
2499                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "two.cpp")),
2500                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
2501                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")),
2502                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")),
2503                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "three.cpp")),
2504                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
2505                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")),
2506                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")),
2507             });
2508 
2509             // Our source files
2510             ITaskItem[] sources = {
2511                                     new TaskItem(Path.Combine("TestFiles", "one.cpp")),
2512                                     new TaskItem(Path.Combine("TestFiles", "two.cpp")),
2513                                     new TaskItem(Path.Combine("TestFiles", "three.cpp")),
2514                                 };
2515 
2516             // Prepare write tlog
2517             File.WriteAllLines(Path.Combine("TestFiles", "one.write.tlog"), new string[] {
2518                 "#Command some-command",
2519                 "^" + FileTracker.FormatRootingMarker(sources),
2520                 Path.GetFullPath(Path.Combine("TestFiles", "one.obj")),
2521                 Path.GetFullPath(Path.Combine("TestFiles", "two.obj")),
2522                 Path.GetFullPath(Path.Combine("TestFiles", "three.obj")),
2523             });
2524 
2525 
2526             // Represent our tracked and computed outputs
2527             CanonicalTrackedOutputFiles outputs = new CanonicalTrackedOutputFiles(DependencyTestHelper.MockTask, DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.write.tlog"))));
2528 
2529             // "Compute" the additional output information for this compilation, rather than them being tracked
2530             outputs.AddComputedOutputForSourceRoot(Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")), Path.GetFullPath(Path.Combine("TestFiles", "one.obj")));
2531             outputs.AddComputedOutputForSourceRoot(Path.GetFullPath(Path.Combine("TestFiles", "two.cpp")), Path.GetFullPath(Path.Combine("TestFiles", "two.obj")));
2532             outputs.AddComputedOutputForSourceRoot(Path.GetFullPath(Path.Combine("TestFiles", "three.cpp")), Path.GetFullPath(Path.Combine("TestFiles", "three.obj")));
2533 
2534             // Represent our tracked and provided inputs
2535             CanonicalTrackedInputFiles d = new CanonicalTrackedInputFiles
2536                 (
2537                     DependencyTestHelper.MockTask,
2538                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.read.tlog"))),
2539                     sources,
2540                     null,
2541                     outputs,
2542                     false, /* no minimal rebuild optimization */
2543                     false /* shred composite rooting markers */
2544                 );
2545 
2546             // First of all, all things should be up to date
2547             ITaskItem[] outofdate = d.ComputeSourcesNeedingCompilation();
2548             Assert.Equal(0, outofdate.Length);
2549 
2550             // Delete one of the outputs in the group
2551             File.Delete(Path.GetFullPath(Path.Combine("TestFiles", "two.obj")));
2552 
2553             // With optimization off, all sources in the group will need compilation
2554             d.SourcesNeedingCompilation = null;
2555             outofdate = d.ComputeSourcesNeedingCompilation();
2556             Assert.Equal(3, outofdate.Length);
2557 
2558             // With optimization on, only the source that matches the output will need compilation
2559             d = new CanonicalTrackedInputFiles
2560                 (
2561                     DependencyTestHelper.MockTask,
2562                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.read.tlog"))),
2563                     sources,
2564                     null,
2565                     outputs,
2566                     true, /* enable minimal rebuild optimization */
2567                     false /* shred composite rooting markers */
2568                 );
2569 
2570             outofdate = d.ComputeSourcesNeedingCompilation();
2571             Assert.Equal(1, outofdate.Length);
2572             // And the source is.. two.cpp!
2573             Assert.True(outofdate[0].ItemSpec == Path.Combine("TestFiles", "two.cpp"));
2574         }
2575 
2576         [Fact]
ReplaceOutputForSource()2577         public void ReplaceOutputForSource()
2578         {
2579             Console.WriteLine("Test: ReplaceOutputForSource");
2580 
2581             if (File.Exists(Path.GetFullPath(Path.Combine("TestFiles", "three.i"))))
2582             {
2583                 File.Delete(Path.GetFullPath(Path.Combine("TestFiles", "three.i")));
2584             }
2585 
2586             // Prepare read tlog
2587             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one1.h"), "");
2588             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
2589             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one3.h"), "");
2590             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
2591             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two.cpp"), "");
2592             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "three.cpp"), "");
2593             Thread.Sleep(_sleepTimeMilliseconds);
2594             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
2595             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two.obj"), "");
2596             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "three.obj"), "");
2597             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
2598             File.WriteAllLines(Path.Combine("TestFiles", "one.read.tlog"), new string[] {
2599                 "#Command some-command",
2600                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
2601                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")).ToUpperInvariant(),
2602                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")),
2603                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")),
2604                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "two.cpp")),
2605                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
2606                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")),
2607                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")),
2608                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "three.cpp")),
2609                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
2610                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")),
2611                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")),
2612             });
2613 
2614             // Our source files
2615             ITaskItem[] sources = {
2616                                     new TaskItem(Path.Combine("TestFiles", "one.cpp")),
2617                                     new TaskItem(Path.Combine("TestFiles", "two.cpp")),
2618                                     new TaskItem(Path.Combine("TestFiles", "three.cpp")),
2619                                 };
2620 
2621             // Prepare write tlog
2622             File.WriteAllLines(Path.Combine("TestFiles", "one.write.tlog"), new string[] {
2623                 "#Command some-command",
2624                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
2625                 Path.GetFullPath(Path.Combine("TestFiles", "one.obj")),
2626                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "two.cpp")),
2627                 Path.GetFullPath(Path.Combine("TestFiles", "two.obj")),
2628                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "three.cpp")),
2629                 Path.GetFullPath(Path.Combine("TestFiles", "three.obj")),
2630             });
2631 
2632             // Represent our tracked and computed outputs
2633             CanonicalTrackedOutputFiles outputs = new CanonicalTrackedOutputFiles(DependencyTestHelper.MockTask, DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.write.tlog"))));
2634 
2635             // Change the output (note that this doesn't affect the timestamp)
2636             File.Move(Path.GetFullPath(Path.Combine("TestFiles", "three.obj")), Path.GetFullPath(Path.Combine("TestFiles", "three.i")));
2637 
2638             string threeRootingMarker = FileTracker.FormatRootingMarker(new TaskItem(Path.Combine("TestFiles", "three.cpp")));
2639             // Remove the fact that three.obj was the tracked output
2640             bool removed = outputs.RemoveOutputForSourceRoot(threeRootingMarker, Path.GetFullPath(Path.Combine("TestFiles", "three.obj")));
2641             Assert.True(removed);
2642             // "Compute" the replacement output information for this compilation, rather than the one originally tracked
2643             outputs.AddComputedOutputForSourceRoot(threeRootingMarker, Path.GetFullPath(Path.Combine("TestFiles", "three.i")));
2644 
2645             // Represent our tracked and provided inputs
2646             CanonicalTrackedInputFiles d = new CanonicalTrackedInputFiles
2647                 (
2648                     DependencyTestHelper.MockTask,
2649                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.read.tlog"))),
2650                     sources,
2651                     null,
2652                     outputs,
2653                     true, /* minimal rebuild optimization */
2654                     false /* shred composite rooting markers */
2655                 );
2656 
2657             // We should have one output for three.cpp
2658             Assert.Equal(1, outputs.DependencyTable[threeRootingMarker].Count);
2659             Assert.Equal(false, outputs.DependencyTable[threeRootingMarker].ContainsKey(Path.GetFullPath(Path.Combine("TestFiles", "three.obj"))));
2660 
2661             // All things should be up to date
2662             ITaskItem[] outofdate = d.ComputeSourcesNeedingCompilation();
2663             Assert.Equal(0, outofdate.Length);
2664 
2665             // Delete the new output
2666             File.Delete(Path.GetFullPath(Path.Combine("TestFiles", "three.i")));
2667 
2668             // This means a recompile would be required for the roots
2669             d.SourcesNeedingCompilation = null;
2670             outofdate = d.ComputeSourcesNeedingCompilation();
2671             Assert.Equal(1, outofdate.Length);
2672         }
2673 
2674         [Fact]
ExcludeSpecificDirectory()2675         public void ExcludeSpecificDirectory()
2676         {
2677             Console.WriteLine("Test: ExcludeSpecificDirectory");
2678 
2679             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one1.h"), "");
2680             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one3.h"), "");
2681             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
2682             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two.cpp"), "");
2683             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "three.cpp"), "");
2684 
2685             Thread.Sleep(_sleepTimeMilliseconds);
2686 
2687             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
2688             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two.obj"), "");
2689             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "three.obj"), "");
2690 
2691             Thread.Sleep(_sleepTimeMilliseconds);
2692 
2693             Directory.CreateDirectory(Path.Combine("TestFiles", "Foo"));
2694             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "Foo", "one2.h"), "");
2695 
2696             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
2697 
2698             File.WriteAllLines(Path.Combine("TestFiles", "one.read.tlog"), new string[] {
2699                 "#Command some-command",
2700                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")).ToUpperInvariant(),
2701                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")).ToUpperInvariant(),
2702                 Path.GetFullPath(Path.Combine("TestFiles", "Foo", "one2.h")).ToUpperInvariant(),
2703                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")).ToUpperInvariant(),
2704                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "two.cpp")).ToUpperInvariant(),
2705                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")).ToUpperInvariant(),
2706                 Path.GetFullPath(Path.Combine("TestFiles", "Foo", "one2.h")).ToUpperInvariant(),
2707                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")).ToUpperInvariant(),
2708                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "three.cpp")).ToUpperInvariant(),
2709                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")).ToUpperInvariant(),
2710                 Path.GetFullPath(Path.Combine("TestFiles", "Foo", "one2.h")).ToUpperInvariant(),
2711                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")).ToUpperInvariant(),
2712             });
2713 
2714             // Our source files
2715             ITaskItem[] sources = {
2716                                     new TaskItem(Path.Combine("TestFiles", "one.cpp")),
2717                                     new TaskItem(Path.Combine("TestFiles", "two.cpp")),
2718                                     new TaskItem(Path.Combine("TestFiles", "three.cpp")),
2719                                 };
2720 
2721             // Prepare write tlog
2722             File.WriteAllLines(Path.Combine("TestFiles", "one.write.tlog"), new string[] {
2723                 "#Command some-command",
2724                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")).ToUpperInvariant(),
2725                 Path.GetFullPath(Path.Combine("TestFiles", "one.obj")).ToUpperInvariant(),
2726                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "two.cpp")).ToUpperInvariant(),
2727                 Path.GetFullPath(Path.Combine("TestFiles", "two.obj")).ToUpperInvariant(),
2728                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "three.cpp")).ToUpperInvariant(),
2729                 Path.GetFullPath(Path.Combine("TestFiles", "three.obj")).ToUpperInvariant(),
2730             });
2731 
2732             // Represent our tracked and computed outputs
2733             CanonicalTrackedOutputFiles outputs = new CanonicalTrackedOutputFiles(DependencyTestHelper.MockTask, DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.write.tlog"))));
2734 
2735             // Represent our tracked and provided inputs
2736             CanonicalTrackedInputFiles d = new CanonicalTrackedInputFiles
2737                 (
2738                     DependencyTestHelper.MockTask,
2739                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.read.tlog"))),
2740                     sources,
2741                     new TaskItem[] { new TaskItem(Path.GetFullPath(Path.Combine("TeSTfiles", "Foo"))) },
2742                     outputs,
2743                     true, /* minimal rebuild optimization */
2744                     false /* shred composite rooting markers */
2745                 );
2746 
2747             // All things should be up to date
2748             ITaskItem[] outofdate = d.ComputeSourcesNeedingCompilation();
2749             Assert.Equal(0, outofdate.Length);
2750         }
2751 
2752         [Fact]
SaveCompactedReadTlog()2753         public void SaveCompactedReadTlog()
2754         {
2755             Console.WriteLine("Test: SaveCompactedReadTlog");
2756             // Prepare files
2757             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one1.h"), "");
2758             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
2759             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one3.h"), "");
2760             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
2761             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
2762             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two1.h"), "");
2763             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two2.h"), "");
2764             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two3.h"), "");
2765             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two.cpp"), "");
2766             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two.obj"), "");
2767 
2768             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
2769             File.WriteAllLines(Path.Combine("TestFiles", "one1.tlog"), new string[] {
2770                 "#Command some-command",
2771                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
2772                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
2773             });
2774 
2775             File.WriteAllLines(Path.Combine("TestFiles", "one2.tlog"), new string[] {
2776                 "#Command some-command1",
2777                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
2778                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")),
2779                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")),
2780             });
2781 
2782             File.WriteAllLines(Path.Combine("TestFiles", "two1.tlog"), new string[] {
2783                 "#Command some-command2",
2784                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "two.cpp")),
2785                 Path.GetFullPath(Path.Combine("TestFiles", "two2.h")),
2786                 Path.GetFullPath(Path.Combine("TestFiles", "two3.h")),
2787             });
2788 
2789             // Touch one
2790             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
2791             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
2792 
2793             ITaskItem[] tlogs = {
2794                                     new TaskItem(Path.Combine("TestFiles", "one1.tlog")),
2795                                     new TaskItem(Path.Combine("TestFiles", "one2.tlog")),
2796                                     new TaskItem(Path.Combine("TestFiles", "two1.tlog"))
2797                                 };
2798 
2799             CanonicalTrackedInputFiles d = new CanonicalTrackedInputFiles
2800                 (
2801                     DependencyTestHelper.MockTask,
2802                     tlogs,
2803                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.cpp"))),
2804                     null,
2805                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.obj"))),
2806                     false, /* no minimal rebuild optimization */
2807                     false /* shred composite rooting markers */
2808                 );
2809 
2810             ITaskItem[] outofdate = d.ComputeSourcesNeedingCompilation();
2811 
2812             Assert.True(outofdate.Length == 1);
2813             Assert.True(outofdate[0].ItemSpec == Path.Combine("TestFiles", "one.cpp"));
2814 
2815             d.RemoveEntriesForSource(d.SourcesNeedingCompilation);
2816             d.SaveTlog();
2817 
2818             // All the tlogs need to still be there even after compaction
2819             // It's OK for them to be empty, but their absence might mean a partial clean
2820             // A missing tlog would mean a clean build
2821             Assert.True(Microsoft.Build.Utilities.TrackedDependencies.ItemsExist(tlogs));
2822 
2823             // There should be no difference in the out of date files after compaction
2824             CanonicalTrackedInputFiles d1 = new CanonicalTrackedInputFiles
2825                 (
2826                     DependencyTestHelper.MockTask,
2827                     tlogs,
2828                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.cpp"))),
2829                     null,
2830                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.obj"))),
2831                     false, /* no minimal rebuild optimization */
2832                     false /* shred composite rooting markers */
2833                 );
2834 
2835             outofdate = d1.ComputeSourcesNeedingCompilation();
2836 
2837             Assert.True(outofdate.Length == 1);
2838             Assert.True(outofdate[0].ItemSpec == Path.Combine("TestFiles", "one.cpp"));
2839 
2840             ITaskItem[] tlogs2 = {
2841                                     tlogs[0]
2842                                  };
2843 
2844             // All log information should now be in the tlog[0]
2845             CanonicalTrackedInputFiles d2 = new CanonicalTrackedInputFiles
2846                 (
2847                     DependencyTestHelper.MockTask,
2848                     tlogs2,
2849                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "two.cpp"))),
2850                     null,
2851                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "two.obj"))),
2852                     false, /* no minimal rebuild optimization */
2853                     false /* shred composite rooting markers */
2854                 );
2855 
2856             outofdate = d2.ComputeSourcesNeedingCompilation();
2857 
2858             Assert.True(outofdate.Length == 0);
2859             Assert.True(d2.DependencyTable.Count == 1);
2860             Assert.False(d2.DependencyTable.ContainsKey(Path.GetFullPath(Path.Combine("TestFiles", "one.cpp"))));
2861 
2862             // There should be no difference even if we send in all the original tlogs
2863             CanonicalTrackedInputFiles d3 = new CanonicalTrackedInputFiles
2864                 (
2865                     DependencyTestHelper.MockTask,
2866                     tlogs,
2867                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "two.cpp"))),
2868                     null,
2869                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "two.obj"))),
2870                     false, /* no minimal rebuild optimization */
2871                     false /* shred composite rooting markers */
2872                 );
2873 
2874             outofdate = d3.ComputeSourcesNeedingCompilation();
2875 
2876             Assert.True(outofdate.Length == 0);
2877             Assert.True(d3.DependencyTable.Count == 1);
2878             Assert.False(d3.DependencyTable.ContainsKey(Path.GetFullPath(Path.Combine("TestFiles", "one.cpp"))));
2879         }
2880 
2881         [Fact]
SaveCompactedWriteTlog()2882         public void SaveCompactedWriteTlog()
2883         {
2884             Console.WriteLine("Test: SaveCompactedWriteTlog");
2885             TaskItem fooItem = new TaskItem("foo");
2886 
2887             ITaskItem[] sources = new TaskItem[] {
2888                                     new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "one.cpp"))),
2889                                     new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "two.cpp"))),
2890                                     new TaskItem(Path.GetFullPath(Path.Combine("TestFiles", "three.cpp")))};
2891 
2892             string rootMarker = FileTracker.FormatRootingMarker(sources);
2893 
2894             // Prepare files
2895             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
2896             File.WriteAllLines(Path.Combine("TestFiles", "one.tlog"), new string[] {
2897                 "#Command some-command",
2898                 "^" + rootMarker,
2899                 Path.GetFullPath(Path.Combine("TestFiles", "oNe.obj")),
2900                 "^" + fooItem.GetMetadata("Fullpath"),
2901                 Path.GetFullPath(Path.Combine("TestFiles", "foo1.bar")),
2902                 Path.GetFullPath(Path.Combine("TestFiles", "bar1.baz")),
2903             });
2904 
2905             File.WriteAllLines(Path.Combine("TestFiles", "two.tlog"), new string[] {
2906                 "#Command some-command",
2907                 "^" + rootMarker,
2908                 Path.GetFullPath(Path.Combine("TestFiles", "two.obj")),
2909                 Path.GetFullPath(Path.Combine("TestFiles", "three.obj")),
2910                 "^" + fooItem.GetMetadata("Fullpath"),
2911                 Path.GetFullPath(Path.Combine("TestFiles", "foo2.bar")),
2912                 Path.GetFullPath(Path.Combine("TestFiles", "bar2.baz")),
2913             });
2914 
2915             ITaskItem[] tlogs = {
2916                                     new TaskItem(Path.Combine("TestFiles", "one.tlog")),
2917                                     new TaskItem(Path.Combine("TestFiles", "two.tlog"))
2918                                 };
2919 
2920             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
2921             CanonicalTrackedOutputFiles d = new CanonicalTrackedOutputFiles(DependencyTestHelper.MockTask,
2922                     tlogs);
2923 
2924             ITaskItem[] outputs = d.OutputsForSource(sources);
2925 
2926             Assert.Equal(3, outputs.Length);
2927             Assert.Equal(outputs[0].ItemSpec, Path.GetFullPath("TestFiles\\oNe.obj"));
2928             Assert.Equal(outputs[1].ItemSpec, Path.GetFullPath("TestFiles\\two.obj"));
2929             Assert.Equal(outputs[2].ItemSpec, Path.GetFullPath("TestFiles\\three.obj"));
2930 
2931             outputs = d.OutputsForSource(fooItem);
2932             Assert.Equal(4, outputs.Length);
2933             Assert.Equal(outputs[0].ItemSpec, Path.GetFullPath("TestFiles\\foo1.bar"));
2934             Assert.Equal(outputs[1].ItemSpec, Path.GetFullPath("TestFiles\\bar1.baz"));
2935             Assert.Equal(outputs[2].ItemSpec, Path.GetFullPath("TestFiles\\foo2.bar"));
2936             Assert.Equal(outputs[3].ItemSpec, Path.GetFullPath("TestFiles\\bar2.baz"));
2937 
2938             // Compact the tlog removing all entries for "foo" leaving the other entries intact
2939             d.RemoveEntriesForSource(fooItem);
2940             d.SaveTlog();
2941 
2942             // All the tlogs need to still be there even after compaction
2943             // It's OK for them to be empty, but their absence might mean a partial clean
2944             // A missing tlog would mean a clean build
2945             Assert.True(Microsoft.Build.Utilities.TrackedDependencies.ItemsExist(tlogs));
2946 
2947             // All log information should now be in the tlog[0]
2948             ITaskItem[] tlogs2 = {
2949                                     tlogs[0]
2950                                  };
2951 
2952             CanonicalTrackedOutputFiles d2 = new CanonicalTrackedOutputFiles(DependencyTestHelper.MockTask,
2953                     tlogs2);
2954 
2955             outputs = d2.OutputsForSource(fooItem);
2956             Assert.Equal(0, outputs.Length);
2957 
2958             outputs = d2.OutputsForSource(sources);
2959             Assert.True(outputs.Length == 3);
2960             Assert.True(outputs[0].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "oNe.obj")));
2961             Assert.True(outputs[1].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "two.obj")));
2962             Assert.True(outputs[2].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "three.obj")));
2963 
2964             // There should be no difference even if we send in all the original tlogs
2965             CanonicalTrackedOutputFiles d3 = new CanonicalTrackedOutputFiles(DependencyTestHelper.MockTask,
2966                     tlogs);
2967 
2968             outputs = d3.OutputsForSource(fooItem);
2969             Assert.Equal(0, outputs.Length);
2970 
2971             outputs = d3.OutputsForSource(sources);
2972             Assert.True(outputs.Length == 3);
2973             Assert.True(outputs[0].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "oNe.obj")));
2974             Assert.True(outputs[1].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "two.obj")));
2975             Assert.True(outputs[2].ItemSpec == Path.GetFullPath(Path.Combine("TestFiles", "three.obj")));
2976         }
2977 
2978         /// <summary>
2979         /// Make sure that the compacted read tlog contains the correct information when the composite rooting
2980         /// markers are kept, as in the case where there is a many-to-one relationship between inputs and
2981         /// outputs (ie. Lib, Link)
2982         /// </summary>
2983         [Fact]
SaveCompactedReadTlog_MaintainCompositeRootingMarkers()2984         public void SaveCompactedReadTlog_MaintainCompositeRootingMarkers()
2985         {
2986             Console.WriteLine("Test: SaveCompactedReadTlog_MaintainCompositeRootingMarkers");
2987             // Prepare files
2988             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one1.h"), "");
2989             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
2990             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one3.h"), "");
2991             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two1.h"), "");
2992             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two2.h"), "");
2993             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two3.h"), "");
2994             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "three1.h"), "");
2995             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "three2.h"), "");
2996 
2997             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
2998             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
2999             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "two.cpp"), "");
3000             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "three.cpp"), "");
3001 
3002             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
3003             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
3004             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "twothree.obj"), "");
3005 
3006             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
3007             File.WriteAllLines(Path.Combine("TestFiles", "one1.read.tlog"), new string[] {
3008                 "#Command some-command",
3009                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
3010                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
3011             });
3012 
3013             File.WriteAllLines(Path.Combine("TestFiles", "one2.read.tlog"), new string[] {
3014                 "#Command some-command1",
3015                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
3016                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")),
3017                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")),
3018             });
3019 
3020             File.WriteAllLines(Path.Combine("TestFiles", "two1.read.tlog"), new string[] {
3021                 "#Command some-command2",
3022                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "two.cpp")),
3023                 Path.GetFullPath(Path.Combine("TestFiles", "two2.h")),
3024                 Path.GetFullPath(Path.Combine("TestFiles", "two3.h")),
3025             });
3026 
3027             File.WriteAllLines(Path.Combine("TestFiles", "three1.read.tlog"), new string[] {
3028                 "#Command some-command2",
3029                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "three.cpp")),
3030                 Path.GetFullPath(Path.Combine("TestFiles", "three1.h"))
3031             });
3032 
3033             File.WriteAllLines(Path.Combine("TestFiles", "twothree.read.tlog"), new string[] {
3034                 "#Command some-command2",
3035                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "three.cpp")) + "|" + Path.GetFullPath(Path.Combine("TestFiles", "two.cpp")),
3036                 Path.GetFullPath(Path.Combine("TestFiles", "two2.h")),
3037                 Path.GetFullPath(Path.Combine("TestFiles", "two3.h")),
3038                 Path.GetFullPath(Path.Combine("TestFiles", "three1.h")),
3039                 Path.GetFullPath(Path.Combine("TestFiles", "three2.h"))
3040             });
3041 
3042             ITaskItem[] tlogs = {
3043                                     new TaskItem(Path.Combine("TestFiles", "one1.read.tlog")),
3044                                     new TaskItem(Path.Combine("TestFiles", "one2.read.tlog")),
3045                                     new TaskItem(Path.Combine("TestFiles", "two1.read.tlog")),
3046                                     new TaskItem(Path.Combine("TestFiles", "three1.read.tlog")),
3047                                     new TaskItem(Path.Combine("TestFiles", "twothree.read.tlog"))
3048                                 };
3049 
3050             ITaskItem[] inputs = {
3051                                      new TaskItem(Path.Combine("TestFiles", "one.cpp")),
3052                                      new TaskItem(Path.Combine("TestFiles", "two.cpp")),
3053                                      new TaskItem(Path.Combine("TestFiles", "three.cpp"))
3054                                  };
3055 
3056             ITaskItem[] outputs = {
3057                                       new TaskItem(Path.Combine("TestFiles", "one.obj")),
3058                                       new TaskItem(Path.Combine("TestFiles", "twothree.obj"))
3059                                   };
3060 
3061             CanonicalTrackedInputFiles d = new CanonicalTrackedInputFiles
3062                 (
3063                     DependencyTestHelper.MockTask,
3064                     tlogs,
3065                     inputs,
3066                     null,
3067                     outputs,
3068                     false, /* no minimal rebuild optimization */
3069                     true /* keep composite rooting markers */
3070                 );
3071 
3072             ITaskItem[] outofdate = d.ComputeSourcesNeedingCompilation();
3073 
3074             // nothing should be out of date
3075             Assert.Equal(0, outofdate.Length);
3076             Assert.Equal(4, d.DependencyTable.Count);
3077 
3078             // dependencies should include the three .h files written into the .tlogs + the rooting marker
3079             Assert.True(d.DependencyTable[Path.GetFullPath(Path.Combine("TestFiles", "three.cpp")) + "|" + Path.GetFullPath(Path.Combine("TestFiles", "two.cpp"))].Values.Count == 4);
3080 
3081             d.SaveTlog();
3082 
3083             CanonicalTrackedInputFiles d2 = new CanonicalTrackedInputFiles
3084                 (
3085                     DependencyTestHelper.MockTask,
3086                     tlogs,
3087                     inputs,
3088                     null,
3089                     outputs,
3090                     false, /* no minimal rebuild optimization */
3091                     true /* keep composite rooting markers */
3092                 );
3093 
3094             ITaskItem[] outofdate2 = d2.ComputeSourcesNeedingCompilation();
3095 
3096             Assert.Equal(0, outofdate.Length);
3097             Assert.Equal(4, d2.DependencyTable.Count);
3098 
3099             // dependencies should include the three .h files written into the .tlogs + the two rooting marker files
3100             Assert.True(d2.DependencyTable[Path.GetFullPath(Path.Combine("TestFiles", "three.cpp")) + "|" + Path.GetFullPath(Path.Combine("TestFiles", "two.cpp"))].Values.Count == 4);
3101         }
3102 
3103         [Fact]
InvalidFlatTrackingTLogName()3104         public void InvalidFlatTrackingTLogName()
3105         {
3106             Console.WriteLine("Test: InvalidFlatTrackingTLogName");
3107 
3108             // Prepare files
3109             DependencyTestHelper.WriteAll("TestFiles\\one.h", "");
3110             DependencyTestHelper.WriteAll("TestFiles\\one.cpp", "");
3111             DependencyTestHelper.WriteAll("TestFiles\\one.obj", "");
3112             DependencyTestHelper.WriteAll("TestFiles\\one.tlog", "");
3113 
3114             MockTask task = DependencyTestHelper.MockTask;
3115             FlatTrackingData data = new FlatTrackingData
3116                 (
3117                     task,
3118                     DependencyTestHelper.ItemArray(new TaskItem("TestFiles\\|one|.write.tlog")),
3119                     false /* don't skip missing files */
3120                 );
3121 
3122             Assert.Equal(1, ((task as ITask).BuildEngine as MockEngine).Warnings); // "Should have a warning."
3123             Assert.Equal(0, data.DependencyTable.Count); // "DependencyTable should be empty."
3124         }
3125 
3126         [Fact]
FlatTrackingTLogWithInitialEmptyLine()3127         public void FlatTrackingTLogWithInitialEmptyLine()
3128         {
3129             Console.WriteLine("Test: FlatTrackingTLogWithInitialEmptyLine");
3130 
3131             File.WriteAllLines(Path.Combine("TestFiles", "one.tlog"), new string[] { "", "^FOO" });
3132 
3133             MockTask task = DependencyTestHelper.MockTask;
3134             FlatTrackingData data = new FlatTrackingData
3135                 (
3136                     task,
3137                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.tlog"))),
3138                     false /* don't skip missing files */
3139                 );
3140 
3141             Assert.Equal(1, ((task as ITask).BuildEngine as MockEngine).Warnings); // "Should have a warning."
3142             Assert.Equal(0, data.DependencyTable.Count); // "DependencyTable should be empty."
3143         }
3144 
3145         [Fact]
FlatTrackingTLogWithEmptyLineImmediatelyAfterRoot()3146         public void FlatTrackingTLogWithEmptyLineImmediatelyAfterRoot()
3147         {
3148             Console.WriteLine("Test: FlatTrackingTLogWithEmptyLineImmediatelyAfterRoot");
3149 
3150             File.WriteAllLines(Path.Combine("TestFiles", "one.tlog"), new string[] { "^FOO", "", "FOO" });
3151 
3152             MockTask task = DependencyTestHelper.MockTask;
3153             FlatTrackingData data = new FlatTrackingData
3154                 (
3155                     task,
3156                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.tlog"))),
3157                     false /* don't skip missing files */
3158                 );
3159 
3160             Assert.Equal(1, ((task as ITask).BuildEngine as MockEngine).Warnings); // "Should have a warning."
3161             Assert.Equal(0, data.DependencyTable.Count); // "DependencyTable should be empty."
3162         }
3163 
3164         [Fact]
FlatTrackingTLogWithEmptyLineBetweenRoots()3165         public void FlatTrackingTLogWithEmptyLineBetweenRoots()
3166         {
3167             Console.WriteLine("Test: FlatTrackingTLogWithEmptyLineBetweenRoots");
3168 
3169             File.WriteAllLines(Path.Combine("TestFiles", "one.tlog"), new string[] { "^FOO", "FOO", "", "^BAR", "BAR" });
3170 
3171             MockTask task = DependencyTestHelper.MockTask;
3172             FlatTrackingData data = new FlatTrackingData
3173                 (
3174                     task,
3175                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.tlog"))),
3176                     false /* don't skip missing files */
3177                 );
3178 
3179             Assert.Equal(1, ((task as ITask).BuildEngine as MockEngine).Warnings); // "Should have a warning."
3180             Assert.Equal(0, data.DependencyTable.Count); // "DependencyTable should be empty."
3181         }
3182 
3183         [Fact]
FlatTrackingTLogWithEmptyRoot()3184         public void FlatTrackingTLogWithEmptyRoot()
3185         {
3186             Console.WriteLine("Test: FlatTrackingTLogWithEmptyRoot");
3187 
3188             File.WriteAllLines(Path.Combine("TestFiles", "one.tlog"), new string[] { "^", "FOO" });
3189 
3190             MockTask task = DependencyTestHelper.MockTask;
3191             FlatTrackingData data = new FlatTrackingData
3192                 (
3193                     task,
3194                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.tlog"))),
3195                     false /* don't skip missing files */
3196                 );
3197 
3198             Assert.Equal(0, ((task as ITask).BuildEngine as MockEngine).Warnings); // "Should not warn -- root markers are ignored by default"
3199             Assert.Equal(1, data.DependencyTable.Count); // "DependencyTable should only contain one entry."
3200             Assert.NotNull(data.DependencyTable["FOO"]); // "FOO should be the only entry."
3201         }
3202 
3203         [Fact]
FlatTrackingDataMissingInputsAndOutputs()3204         public void FlatTrackingDataMissingInputsAndOutputs()
3205         {
3206             Console.WriteLine("Test: FlatTrackingDataMissingInputsAndOutputs");
3207             // Prepare files
3208             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one1.h"), "");
3209             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
3210             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one3.h"), "");
3211             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
3212             Thread.Sleep(_sleepTimeMilliseconds);
3213             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
3214             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
3215             File.WriteAllLines(Path.Combine("TestFiles", "one.read.tlog"), new string[] {
3216                 "#Command some-command",
3217                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
3218                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
3219                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")),
3220                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")),
3221                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "two.cpp")),
3222                 Path.GetFullPath(Path.Combine("TestFiles", "two1.h")),
3223                 Path.GetFullPath(Path.Combine("TestFiles", "two2.h")),
3224             });
3225 
3226             File.WriteAllLines(Path.Combine("TestFiles", "one.write.tlog"), new string[] {
3227                 "#Command some-command",
3228                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
3229                 Path.GetFullPath(Path.Combine("TestFiles", "one.obj")),
3230                 Path.GetFullPath(Path.Combine("TestFiles", "sometempfile.obj")),
3231                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "two.cpp")),
3232                 Path.GetFullPath(Path.Combine("TestFiles", "two.obj")),
3233                 Path.GetFullPath(Path.Combine("TestFiles", "sometempfile2.obj"))
3234             });
3235 
3236             FlatTrackingData outputs = new FlatTrackingData(DependencyTestHelper.MockTask, DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.write.tlog"))), false);
3237             FlatTrackingData inputs = new FlatTrackingData(DependencyTestHelper.MockTask, DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.read.tlog"))), false);
3238 
3239             Assert.Equal(false, FlatTrackingData.IsUpToDate(DependencyTestHelper.MockTask.Log, UpToDateCheckType.InputNewerThanOutput, inputs, outputs));
3240             Assert.Equal(2, inputs.MissingFiles.Count);
3241             Assert.Equal(3, outputs.MissingFiles.Count);
3242         }
3243 
3244         [Fact]
FlatTrackingDataMissingInputs()3245         public void FlatTrackingDataMissingInputs()
3246         {
3247             Console.WriteLine("Test: FlatTrackingDataMissingInputs");
3248             // Prepare files
3249             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one1.h"), "");
3250             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
3251             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one3.h"), "");
3252             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
3253             Thread.Sleep(_sleepTimeMilliseconds);
3254             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
3255             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
3256             File.WriteAllLines(Path.Combine("TestFiles", "one.read.tlog"), new string[] {
3257                 "#Command some-command",
3258                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
3259                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
3260                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")),
3261                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")),
3262                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "two.cpp")),
3263                 Path.GetFullPath(Path.Combine("TestFiles", "two1.h")),
3264                 Path.GetFullPath(Path.Combine("TestFiles", "two2.h")),
3265             });
3266 
3267             File.WriteAllLines(Path.Combine("TestFiles", "one.write.tlog"), new string[] {
3268                 "#Command some-command",
3269                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
3270                 Path.GetFullPath(Path.Combine("TestFiles", "one.obj")),
3271             });
3272 
3273             FlatTrackingData outputs = new FlatTrackingData(DependencyTestHelper.MockTask, DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.write.tlog"))), false);
3274             FlatTrackingData inputs = new FlatTrackingData(DependencyTestHelper.MockTask, DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.read.tlog"))), false);
3275 
3276             // No matter which way you look at it, if we're missing inputs, we're out of date
3277             Assert.Equal(false, FlatTrackingData.IsUpToDate(DependencyTestHelper.MockTask.Log, UpToDateCheckType.InputNewerThanOutput, inputs, outputs));
3278             Assert.Equal(false, FlatTrackingData.IsUpToDate(DependencyTestHelper.MockTask.Log, UpToDateCheckType.InputNewerThanTracking, inputs, outputs));
3279             Assert.Equal(false, FlatTrackingData.IsUpToDate(DependencyTestHelper.MockTask.Log, UpToDateCheckType.InputOrOutputNewerThanTracking, inputs, outputs));
3280             Assert.Equal(2, inputs.MissingFiles.Count);
3281             Assert.Equal(0, outputs.MissingFiles.Count);
3282         }
3283 
3284         [Fact]
FlatTrackingDataMissingOutputs()3285         public void FlatTrackingDataMissingOutputs()
3286         {
3287             Console.WriteLine("Test: FlatTrackingDataMissingOutputs");
3288             // Prepare files
3289             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one1.h"), "");
3290             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
3291             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one3.h"), "");
3292             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
3293             Thread.Sleep(_sleepTimeMilliseconds);
3294             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
3295             Thread.Sleep(_sleepTimeMilliseconds); // need to wait since the timestamp check needs some time to register
3296             File.WriteAllLines(Path.Combine("TestFiles", "one.read.tlog"), new string[] {
3297                 "#Command some-command",
3298                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
3299                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
3300                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")),
3301                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")),
3302                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "two.cpp")),
3303             });
3304 
3305             File.WriteAllLines(Path.Combine("TestFiles", "one.write.tlog"), new string[] {
3306                 "#Command some-command",
3307                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
3308                 Path.GetFullPath(Path.Combine("TestFiles", "one.obj")),
3309                 Path.GetFullPath(Path.Combine("TestFiles", "two.obj")),
3310                 Path.GetFullPath(Path.Combine("TestFiles", "sometempfile2.obj"))
3311             });
3312 
3313             FlatTrackingData outputs = new FlatTrackingData(DependencyTestHelper.MockTask, DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.write.tlog"))), false);
3314             FlatTrackingData inputs = new FlatTrackingData(DependencyTestHelper.MockTask, DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.read.tlog"))), false);
3315 
3316             // No matter which way you look at it, if we're missing outputs, we're out of date
3317             Assert.Equal(false, FlatTrackingData.IsUpToDate(DependencyTestHelper.MockTask.Log, UpToDateCheckType.InputNewerThanOutput, inputs, outputs));
3318             Assert.Equal(false, FlatTrackingData.IsUpToDate(DependencyTestHelper.MockTask.Log, UpToDateCheckType.InputNewerThanTracking, inputs, outputs));
3319             Assert.Equal(false, FlatTrackingData.IsUpToDate(DependencyTestHelper.MockTask.Log, UpToDateCheckType.InputOrOutputNewerThanTracking, inputs, outputs));
3320             Assert.Equal(0, inputs.MissingFiles.Count);
3321             Assert.Equal(2, outputs.MissingFiles.Count);
3322         }
3323 
3324         [Fact]
FlatTrackingDataEmptyInputTLogs()3325         public void FlatTrackingDataEmptyInputTLogs()
3326         {
3327             Console.WriteLine("Test: FlatTrackingDataEmptyInputTLogs");
3328             // Prepare files
3329             File.WriteAllText(Path.Combine("TestFiles", "one.read.tlog"), String.Empty);
3330             File.WriteAllText(Path.Combine("TestFiles", "one.write.tlog"), String.Empty);
3331 
3332             FlatTrackingData outputs = new FlatTrackingData(DependencyTestHelper.MockTask, DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.write.tlog"))), false);
3333             FlatTrackingData inputs = new FlatTrackingData(DependencyTestHelper.MockTask, DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.read.tlog"))), false);
3334 
3335             // No matter which way you look at it, if we're missing inputs, we're out of date
3336             Assert.Equal(false, FlatTrackingData.IsUpToDate(DependencyTestHelper.MockTask.Log, UpToDateCheckType.InputNewerThanOutput, inputs, outputs));
3337             Assert.Equal(false, FlatTrackingData.IsUpToDate(DependencyTestHelper.MockTask.Log, UpToDateCheckType.InputNewerThanTracking, inputs, outputs));
3338             Assert.Equal(false, FlatTrackingData.IsUpToDate(DependencyTestHelper.MockTask.Log, UpToDateCheckType.InputOrOutputNewerThanTracking, inputs, outputs));
3339         }
3340 
3341         [Fact]
FlatTrackingDataEmptyOutputTLogs()3342         public void FlatTrackingDataEmptyOutputTLogs()
3343         {
3344             Console.WriteLine("Test: FlatTrackingDataEmptyOutputTLogs");
3345             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one1.h"), "");
3346             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
3347             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one3.h"), "");
3348             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
3349             Thread.Sleep(_sleepTimeMilliseconds);
3350             // Prepare files
3351             File.WriteAllLines(Path.Combine("TestFiles", "one.read.tlog"), new string[] {
3352                 "#Command some-command",
3353                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
3354                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
3355                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")),
3356                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")),
3357             });
3358 
3359             File.WriteAllText(Path.Combine("TestFiles", "one.write.tlog"), String.Empty);
3360 
3361             FlatTrackingData outputs = new FlatTrackingData(DependencyTestHelper.MockTask, DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.write.tlog"))), false);
3362             FlatTrackingData inputs = new FlatTrackingData(DependencyTestHelper.MockTask, DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.read.tlog"))), false);
3363 
3364             // Inputs newer than outputs - if there are no outputs, then we're out of date
3365             Assert.Equal(false, FlatTrackingData.IsUpToDate(DependencyTestHelper.MockTask.Log, UpToDateCheckType.InputNewerThanOutput, inputs, outputs));
3366             // Inputs newer than tracking - if there are no outputs, then we don't care
3367             Assert.Equal(true, FlatTrackingData.IsUpToDate(DependencyTestHelper.MockTask.Log, UpToDateCheckType.InputNewerThanTracking, inputs, outputs));
3368             // Inputs or Outputs newer than tracking - if there is an output tlog, even if there's no text written to it, we're not out of date
3369             Assert.Equal(true, FlatTrackingData.IsUpToDate(DependencyTestHelper.MockTask.Log, UpToDateCheckType.InputOrOutputNewerThanTracking, inputs, outputs));
3370         }
3371 
3372         [Fact]
FlatTrackingDataInputNewerThanTracking()3373         public void FlatTrackingDataInputNewerThanTracking()
3374         {
3375             Console.WriteLine("Test: FlatTrackingDataInputNewerThanTracking");
3376             File.WriteAllLines(Path.Combine("TestFiles", "one.read.tlog"), new string[] {
3377                 "#Command some-command",
3378                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
3379                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
3380                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")),
3381                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")),
3382                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "two.cpp")),
3383             });
3384 
3385             File.WriteAllLines(Path.Combine("TestFiles", "one.write.tlog"), new string[] {
3386                 "#Command some-command",
3387                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
3388                 Path.GetFullPath(Path.Combine("TestFiles", "one.obj")),
3389             });
3390 
3391             Thread.Sleep(_sleepTimeMilliseconds);
3392             // Prepare files
3393             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one1.h"), "");
3394             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
3395             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one3.h"), "");
3396             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
3397             Thread.Sleep(_sleepTimeMilliseconds);
3398             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
3399 
3400             FlatTrackingData outputs = new FlatTrackingData(DependencyTestHelper.MockTask, DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.write.tlog"))), false);
3401             // Compact the read tlog
3402             FlatTrackingData inputs = new FlatTrackingData(DependencyTestHelper.MockTask, DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.read.tlog"))), false);
3403 
3404             Assert.Equal(false, FlatTrackingData.IsUpToDate(DependencyTestHelper.MockTask.Log, UpToDateCheckType.InputNewerThanTracking, inputs, outputs));
3405 
3406             // Touch the tracking logs so that are more recent that any of the inputs
3407             Thread.Sleep(_sleepTimeMilliseconds);
3408             File.SetLastWriteTime(Path.Combine("TestFiles", "one.read.tlog"), DateTime.Now);
3409             File.SetLastWriteTime(Path.Combine("TestFiles", "one.write.tlog"), DateTime.Now);
3410             Thread.Sleep(_sleepTimeMilliseconds);
3411             // Touch the output so that we would be out of date with respect to the inputs, but up to date with respect to the tracking logs
3412             File.SetLastWriteTime(Path.GetFullPath(Path.Combine("TestFiles", "one.obj")), DateTime.Now - TimeSpan.FromHours(1));
3413 
3414             outputs = new FlatTrackingData(DependencyTestHelper.MockTask, DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.write.tlog"))), false);
3415             inputs = new FlatTrackingData(DependencyTestHelper.MockTask, DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.read.tlog"))), false);
3416 
3417             // We should be out of date with respect to the outputs
3418             Assert.Equal(false, FlatTrackingData.IsUpToDate(DependencyTestHelper.MockTask.Log, UpToDateCheckType.InputNewerThanOutput, inputs, outputs));
3419             // We should be up to date with respect to the tracking data
3420             Assert.Equal(true, FlatTrackingData.IsUpToDate(DependencyTestHelper.MockTask.Log, UpToDateCheckType.InputNewerThanTracking, inputs, outputs));
3421         }
3422 
3423         [Fact]
FlatTrackingDataInputNewerThanTrackingNoOutput()3424         public void FlatTrackingDataInputNewerThanTrackingNoOutput()
3425         {
3426             Console.WriteLine("Test: FlatTrackingDataInputNewerThanTrackingNoOutput");
3427             // Prepare files
3428             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one1.h"), "");
3429             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
3430             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one3.h"), "");
3431             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
3432 
3433             Thread.Sleep(_sleepTimeMilliseconds);
3434 
3435             File.WriteAllLines(Path.Combine("TestFiles", "one.read.tlog"), new string[] {
3436                 "#Command some-command",
3437                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
3438                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
3439                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")),
3440                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")),
3441             });
3442 
3443             FlatTrackingData outputs = new FlatTrackingData(DependencyTestHelper.MockTask, DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "*-one.write.?.tlog"))), false);
3444             // Compact the read tlog
3445             FlatTrackingData inputs = new FlatTrackingData(DependencyTestHelper.MockTask, DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.read.tlog"))), false);
3446 
3447             inputs.SaveTlog();
3448             outputs.SaveTlog();
3449 
3450             outputs = new FlatTrackingData(DependencyTestHelper.MockTask, DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "*-one.write.?.tlog"))), false);
3451             // Compact the read tlog
3452             inputs = new FlatTrackingData(DependencyTestHelper.MockTask, DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.read.tlog"))), false);
3453 
3454             Assert.Equal(true, FlatTrackingData.IsUpToDate(DependencyTestHelper.MockTask.Log, UpToDateCheckType.InputNewerThanTracking, inputs, outputs));
3455         }
3456 
3457         [Fact]
FlatTrackingDataInputNewerThanOutput()3458         public void FlatTrackingDataInputNewerThanOutput()
3459         {
3460             Console.WriteLine("Test: FlatTrackingDataInputOrOutputNewerThanTracking");
3461             File.WriteAllLines(Path.Combine("TestFiles", "one.read.tlog"), new string[] {
3462                 "#Command some-command",
3463                 Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
3464                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
3465                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")),
3466                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")),
3467             });
3468 
3469             File.WriteAllLines(Path.Combine("TestFiles", "one.write.tlog"), new string[] {
3470                 "#Command some-command",
3471                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
3472                 Path.GetFullPath(Path.Combine("TestFiles", "one.obj")),
3473             });
3474             // Wait so that our tlogs are old
3475             Thread.Sleep(_sleepTimeMilliseconds);
3476 
3477             // Prepare the source files (later than tracking logs)
3478             // Therefore newer
3479             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one1.h"), "");
3480             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
3481             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one3.h"), "");
3482             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
3483 
3484             // Prepate the output files (later than tracking logs and source files
3485             // Therefore newer
3486             Thread.Sleep(_sleepTimeMilliseconds);
3487             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
3488 
3489             FlatTrackingData outputs = new FlatTrackingData(DependencyTestHelper.MockTask, DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.write.tlog"))), false);
3490             // Compact the read tlog
3491             FlatTrackingData inputs = new FlatTrackingData(DependencyTestHelper.MockTask, DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.read.tlog"))), false);
3492 
3493             // We should be up to date inputs vs outputs
3494             Assert.True(FlatTrackingData.IsUpToDate(DependencyTestHelper.MockTask.Log, UpToDateCheckType.InputNewerThanOutput, inputs, outputs));
3495 
3496             // We should be out of date inputs & outputs vs tracking (since we wrote the files after the tracking logs)
3497             Assert.False(FlatTrackingData.IsUpToDate(DependencyTestHelper.MockTask.Log, UpToDateCheckType.InputOrOutputNewerThanTracking, inputs, outputs));
3498 
3499             // Touch the input so that we would be out of date with respect to the outputs, and out of date with respect to the tracking logs
3500             Thread.Sleep(_sleepTimeMilliseconds);
3501             File.SetLastWriteTime(Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")), DateTime.Now);
3502 
3503             outputs = new FlatTrackingData(DependencyTestHelper.MockTask, DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.write.tlog"))), false);
3504             inputs = new FlatTrackingData(DependencyTestHelper.MockTask, DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.read.tlog"))), false);
3505 
3506             // We should be out of date with respect to the tracking logs
3507             Assert.False(FlatTrackingData.IsUpToDate(DependencyTestHelper.MockTask.Log, UpToDateCheckType.InputNewerThanTracking, inputs, outputs), "#3");
3508 
3509             // We should be out of date with respect to the outputs
3510             Assert.False(FlatTrackingData.IsUpToDate(DependencyTestHelper.MockTask.Log, UpToDateCheckType.InputNewerThanOutput, inputs, outputs), "#4");
3511         }
3512 
3513         [Fact]
FlatTrackingDataInputOrOutputNewerThanTracking()3514         public void FlatTrackingDataInputOrOutputNewerThanTracking()
3515         {
3516             Console.WriteLine("Test: FlatTrackingDataInputOrOutputNewerThanTracking");
3517             File.WriteAllLines(Path.Combine("TestFiles", "one.read.tlog"), new string[] {
3518                 "#Command some-command",
3519                 Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
3520                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
3521                 Path.GetFullPath(Path.Combine("TestFiles", "one2.h")),
3522                 Path.GetFullPath(Path.Combine("TestFiles", "one3.h")),
3523             });
3524 
3525             File.WriteAllLines(Path.Combine("TestFiles", "one.write.tlog"), new string[] {
3526                 "#Command some-command",
3527                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
3528                 Path.GetFullPath(Path.Combine("TestFiles", "one.obj")),
3529             });
3530 
3531             Thread.Sleep(_sleepTimeMilliseconds);
3532             // Prepare files
3533             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one1.h"), "");
3534             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one2.h"), "");
3535             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one3.h"), "");
3536             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
3537             Thread.Sleep(_sleepTimeMilliseconds);
3538             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.obj"), "");
3539 
3540             FlatTrackingData outputs = new FlatTrackingData(DependencyTestHelper.MockTask, DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.write.tlog"))), false);
3541             // Compact the read tlog
3542             FlatTrackingData inputs = new FlatTrackingData(DependencyTestHelper.MockTask, DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.read.tlog"))), false);
3543             // We should be up to date inputs vs outputs
3544             Assert.Equal(true, FlatTrackingData.IsUpToDate(DependencyTestHelper.MockTask.Log, UpToDateCheckType.InputNewerThanOutput, inputs, outputs));
3545             // We should be out of date inputs & outputs vs tracking (since we wrote the files after the tracking logs)
3546             Assert.Equal(false, FlatTrackingData.IsUpToDate(DependencyTestHelper.MockTask.Log, UpToDateCheckType.InputOrOutputNewerThanTracking, inputs, outputs));
3547 
3548 
3549             // Touch the tracking logs so that are more recent that any of the inputs
3550             Thread.Sleep(_sleepTimeMilliseconds);
3551             File.SetLastWriteTime(Path.Combine("TestFiles", "one.read.tlog"), DateTime.Now);
3552             File.SetLastWriteTime(Path.Combine("TestFiles", "one.write.tlog"), DateTime.Now);
3553             Thread.Sleep(_sleepTimeMilliseconds);
3554 
3555             outputs = new FlatTrackingData(DependencyTestHelper.MockTask, DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.write.tlog"))), false);
3556             inputs = new FlatTrackingData(DependencyTestHelper.MockTask, DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.read.tlog"))), false);
3557 
3558             // We should be up to date with respect to the tracking data
3559             Assert.Equal(true, FlatTrackingData.IsUpToDate(DependencyTestHelper.MockTask.Log, UpToDateCheckType.InputOrOutputNewerThanTracking, inputs, outputs));
3560 
3561             // Touch the input so that we would be out of date with respect to the outputs, but up to date with respect to the tracking logs
3562             File.SetLastWriteTime(Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")), DateTime.Now);
3563 
3564             outputs = new FlatTrackingData(DependencyTestHelper.MockTask, DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.write.tlog"))), false);
3565             inputs = new FlatTrackingData(DependencyTestHelper.MockTask, DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.read.tlog"))), false);
3566 
3567             // We should be out of date with respect to the outputs
3568             Assert.Equal(false, FlatTrackingData.IsUpToDate(DependencyTestHelper.MockTask.Log, UpToDateCheckType.InputNewerThanOutput, inputs, outputs));
3569         }
3570 
3571         [Fact]
FlatTrackingExcludeDirectories()3572         public void FlatTrackingExcludeDirectories()
3573         {
3574             Console.WriteLine("Test: FlatTrackingExcludeDirectories");
3575 
3576             // Prepare files
3577             if (!Directory.Exists(Path.Combine("TestFiles", "ToBeExcluded")))
3578             {
3579                 Directory.CreateDirectory(Path.Combine("TestFiles", "ToBeExcluded"));
3580             }
3581 
3582             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "ToBeExcluded", "two.cpp"), "");
3583             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "ToBeExcluded", "two.h"), "");
3584 
3585             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.h"), "");
3586             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "one.cpp"), "");
3587 
3588             File.WriteAllLines(Path.Combine("TestFiles", "one.tlog"), new string[] {
3589                 "#Command some-command",
3590                 Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
3591                 Path.GetFullPath(Path.Combine("TestFiles", "one1.h")),
3592                 Path.GetFullPath(Path.Combine("TestFiles", "ToBeExcluded", "two.cpp")),
3593                 Path.GetFullPath(Path.Combine("TestFiles", "ToBeExcluded", "two.h")),
3594                 Path.GetFullPath(Path.Combine("TestFiles", "SubdirectoryExcluded", "three.cpp")),
3595                 Path.GetFullPath(Path.Combine("TestFiles", "SubdirectoryExcluded", "three.h")),
3596             });
3597 
3598             // Get the newest time w/o any exclude paths
3599             Dictionary<string, DateTime> sharedLastWriteTimeUtcCache = new Dictionary<string, DateTime>(StringComparer.OrdinalIgnoreCase);
3600             FlatTrackingData data = new FlatTrackingData
3601                 (
3602                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.tlog"))),
3603                     null,
3604                     DateTime.MinValue,
3605                     null,
3606                     sharedLastWriteTimeUtcCache
3607                 );
3608 
3609             DateTime originalNewest = data.NewestFileTimeUtc;
3610 
3611             // Force an update to the files we don't care about
3612             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "ToBeExcluded", "two.cpp"), "");
3613             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "ToBeExcluded", "two.h"), "");
3614             if (!Directory.Exists(Path.Combine("TestFiles", "ToBeExcluded", "SubdirectoryExcluded")))
3615             {
3616                 Directory.CreateDirectory(Path.Combine("TestFiles", "ToBeExcluded", "SubdirectoryExcluded"));
3617             }
3618             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "ToBeExcluded", "SubdirectoryExcluded", "three.cpp"), "");
3619             DependencyTestHelper.WriteAll(Path.Combine("TestFiles", "ToBeExcluded", "SubdirectoryExcluded", "three.h"), "");
3620 
3621             // Now do a flat tracker ignoring the exclude directories and make sure the time didn't change
3622             data = new FlatTrackingData
3623                 (
3624                     DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.tlog"))),
3625                     null,
3626                     DateTime.MinValue,
3627                     new string[] { Path.GetFullPath(Path.Combine("TestFiles", "ToBeExcluded")) },
3628                     sharedLastWriteTimeUtcCache
3629                 );
3630 
3631             Assert.Equal(originalNewest, data.NewestFileTimeUtc); // "Timestamp changed when no tracked files changed."
3632         }
3633 
3634         [Fact]
TrackingDataCacheResetOnTlogChange()3635         public void TrackingDataCacheResetOnTlogChange()
3636         {
3637             Console.WriteLine("Test: FlatTrackingDataCacheResetOnTlogChange");
3638 
3639             File.WriteAllLines(Path.Combine("TestFiles", "one.write.tlog"), new string[] {
3640                 "#Command some-command",
3641                 "^" + Path.GetFullPath(Path.Combine("TestFiles", "one.cpp")),
3642                 Path.GetFullPath(Path.Combine("TestFiles", "one.obj")),
3643             });
3644 
3645             FlatTrackingData outputs = new FlatTrackingData(
3646                 DependencyTestHelper.MockTask,
3647                 DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.write.tlog"))),
3648                 false);
3649             // Sleep once, so that NTFS has enough time to register a file modified time change
3650             Thread.Sleep(_sleepTimeMilliseconds);
3651             File.WriteAllLines(
3652                 Path.Combine("TestFiles", "one.write.tlog"),
3653                 new string[]
3654                     {
3655                         "#Command some-command", "^" + Path.GetFullPath(Path.Combine("TestFiles", "two.cpp")),
3656                         Path.GetFullPath(Path.Combine("TestFiles", "two.obj")),
3657                     });
3658 
3659             FlatTrackingData outputs2 = new FlatTrackingData(
3660                 DependencyTestHelper.MockTask,
3661                 DependencyTestHelper.ItemArray(new TaskItem(Path.Combine("TestFiles", "one.write.tlog"))),
3662                 false);
3663 
3664             // We should not use the cached dependency table, since it has been updated since it was last read from disk
3665             Assert.NotEqual(outputs.DependencyTable, outputs2.DependencyTable);
3666         }
3667 
3668         [Fact]
RootContainsSubRoots()3669         public void RootContainsSubRoots()
3670         {
3671             Console.WriteLine("Test: RootContainsSubRoots");
3672             CanonicalTrackedOutputFiles output = new CanonicalTrackedOutputFiles(DependencyTestHelper.MockTask,
3673                     null);
3674 
3675             Assert.True(output.RootContainsAllSubRootComponents("a|b|c|d|e|f|g", "a|b|C|d|e|F|g"));
3676             Assert.True(output.RootContainsAllSubRootComponents("a|b|c|d|e|f|g", "a"));
3677             Assert.True(output.RootContainsAllSubRootComponents("a|b|c|d|e|f|g", "g"));
3678             Assert.True(output.RootContainsAllSubRootComponents("a|b|c|d|e|f|g", "d"));
3679             Assert.True(output.RootContainsAllSubRootComponents("a|b|c|d|e|f|g", "a|b"));
3680             Assert.True(output.RootContainsAllSubRootComponents("a|b|c|d|e|f|g", "f|g"));
3681             Assert.True(output.RootContainsAllSubRootComponents("a|b|c|d|e|f|g", "b|a"));
3682             Assert.True(output.RootContainsAllSubRootComponents("a|b|c|d|e|f|g", "g|f"));
3683             Assert.True(output.RootContainsAllSubRootComponents("a|b|c|d|e|f|g", "b|e"));
3684         }
3685     }
3686 
3687     internal class MockTask : Task
3688     {
MockTask(ResourceManager resourceManager)3689         public MockTask(ResourceManager resourceManager)
3690             : base(resourceManager)
3691         {
3692         }
3693 
3694         public TaskLoggingHelper LogHelper
3695         {
3696             get { return Log; }
3697         }
3698 
Execute()3699         public override bool Execute()
3700         {
3701             return true;
3702         }
3703     }
3704 
3705     internal class DependencyTestHelper
3706     {
ItemArray(ITaskItem item)3707         public static ITaskItem[] ItemArray(ITaskItem item)
3708         {
3709             List<ITaskItem> itemList = new List<ITaskItem>();
3710             itemList.Add(item);
3711             return itemList.ToArray();
3712         }
3713 
3714         public static TaskLoggingHelper MockTaskLoggingHelper
3715         {
3716             get
3717             {
3718                 MockTask t = new MockTask(Microsoft.Build.Shared.AssemblyResources.PrimaryResources);
3719                 t.BuildEngine = new MockEngine();
3720                 return t.LogHelper;
3721             }
3722         }
3723 
3724         public static MockTask MockTask
3725         {
3726             get
3727             {
3728                 MockTask t = new MockTask(Microsoft.Build.Shared.AssemblyResources.PrimaryResources);
3729                 t.BuildEngine = new MockEngine();
3730                 return t;
3731             }
3732         }
3733 
WriteAll(string filename, string content)3734         public static void WriteAll(string filename, string content)
3735         {
3736             File.WriteAllText(filename, content);
3737         }
3738     }
3739 }
3740 
3741 #endif
3742