Skip to content

Commit 39f7f47

Browse files
enricosadalatkin
authored andcommitted
fix app.config up-to-date status
fixes #11 closes #374 commit f677819e9f4258eeee5577c23ad107132a901b07 Author: enricosada <[email protected]> Date: Wed Feb 25 00:02:18 2015 +0100 fix app.config up-to-date status commit bc419357eccfd52c6e46103409521052f5321071 Author: enricosada <[email protected]> Date: Wed May 6 17:17:25 2015 +0200 fix test build up-to-date should fail if the .exe.config doesn't exists commit 8219016c1f3009218a26545c1c96b6dddc26d515 Author: enricosada <[email protected]> Date: Wed May 6 16:47:31 2015 +0200 add assert AssertBuildSuccessful to check if a build is successful
1 parent 1f195e4 commit 39f7f47

File tree

3 files changed

+132
-20
lines changed

3 files changed

+132
-20
lines changed

vsintegration/src/unittests/TestLib.Utils.fs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ module Asserts =
4747
| Some(msg) -> Assert.Fail(msg)
4848
| None -> ()
4949

50+
let AssertBuildSuccessful (result: Microsoft.VisualStudio.FSharp.ProjectSystem.BuildResult) =
51+
Assert.IsTrue(result.IsSuccessful, "Expected build to succeed")
52+
5053
module UIStuff =
5154
let SetupSynchronizationContext() =
5255
Microsoft.VisualStudio.FSharp.LanguageService.UIThread.InitUnitTestingMode()

vsintegration/src/unittests/Tests.ProjectSystem.UpToDate.fs

Lines changed: 69 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ type UpToDate() =
5959
File.AppendAllText(embedPath, "some embedded resource")
6060

6161
Assert.IsFalse(config.IsUpToDate(logger, true))
62-
project.Build(configNameDebug, output, "Build") |> ignore
62+
project.Build(configNameDebug, output, "Build") |> AssertBuildSuccessful
6363
Assert.IsTrue(config.IsUpToDate(logger, true))
6464

6565
// None items should not affect up-to-date (unless captured by well-known items, e.g. App.config)
@@ -111,7 +111,7 @@ type UpToDate() =
111111

112112
project.SetConfiguration(config.ConfigCanonicalName);
113113
Assert.IsFalse(config.IsUpToDate(logger, true))
114-
project.Build(configNameDebug, output, "Build") |> ignore
114+
project.Build(configNameDebug, output, "Build") |> AssertBuildSuccessful
115115
Assert.IsTrue(config.IsUpToDate(logger, true))
116116

117117
for path in [verPath; keyPath] do
@@ -146,7 +146,7 @@ type UpToDate() =
146146
File.AppendAllText(absFilePath, "printfn \"hello\"")
147147

148148
Assert.IsFalse(config.IsUpToDate(logger, true))
149-
project.Build(configNameDebug, output, "Build") |> ignore
149+
project.Build(configNameDebug, output, "Build") |> AssertBuildSuccessful
150150
Assert.IsTrue(config.IsUpToDate(logger, true))
151151

152152
// touch proj file
@@ -177,7 +177,7 @@ type UpToDate() =
177177
let config1 = project1.ConfigProvider.GetProjectConfiguration(configNameDebug)
178178

179179
Assert.IsFalse(config1.IsUpToDate(logger, true))
180-
project1.Build(configNameDebug, output, "Build") |> ignore
180+
project1.Build(configNameDebug, output, "Build") |> AssertBuildSuccessful
181181
Assert.IsTrue(config1.IsUpToDate(logger, true))
182182

183183
let output1 = Path.Combine(project1.ProjectFolder, "bin\\debug", project1.OutputFileName)
@@ -196,7 +196,7 @@ type UpToDate() =
196196
let startTime = DateTime.Now
197197

198198
Assert.IsFalse(config2.IsUpToDate(logger, true))
199-
project2.Build(configNameDebug, output, "Build") |> ignore
199+
project2.Build(configNameDebug, output, "Build") |> AssertBuildSuccessful
200200
Assert.IsTrue(config2.IsUpToDate(logger, true))
201201

202202
// reference is updated
@@ -214,6 +214,9 @@ type UpToDate() =
214214
[<Test>]
215215
member public this.OutputFiles () =
216216
this.MakeProjectAndDo(["file1.fs"], [], @"
217+
<ItemGroup>
218+
<None Include=""App.config"" />
219+
</ItemGroup>
217220
<PropertyGroup>
218221
<DocumentationFile>bin\Debug\Test.XML</DocumentationFile>
219222
<DebugSymbols>true</DebugSymbols>
@@ -224,22 +227,25 @@ type UpToDate() =
224227
let output = VsMocks.vsOutputWindowPane(ref [])
225228
let logger = OutputWindowLogger.CreateUpToDateCheckLogger(output)
226229
let sourcePath = Path.Combine(project.ProjectFolder, "file1.fs")
230+
let appConfigPath = Path.Combine(project.ProjectFolder, "App.config")
227231

228232
let exeObjPath = Path.Combine(project.ProjectFolder, "obj\\x86\\debug", project.OutputFileName)
229233
let exeBinpath = Path.Combine(project.ProjectFolder, "bin\\debug\\", project.OutputFileName)
230234
let pdbObjPath = Regex.Replace(exeObjPath, "exe$", "pdb")
231235
let pdbBinPath = Regex.Replace(exeBinpath, "exe$", "pdb")
232236
let xmlDocPath = Regex.Replace(exeBinpath, "exe$", "xml")
237+
let exeConfigPath = Regex.Replace(exeBinpath, "exe$", "exe.config")
233238

234239
File.AppendAllText(sourcePath, "printfn \"hello\"")
240+
File.AppendAllText(appConfigPath, """<?xml version="1.0" encoding="utf-8" ?><configuration></configuration>""")
235241

236242
Assert.IsFalse(config.IsUpToDate(logger, true))
237-
project.Build(configNameDebug, output, "Build") |> ignore
243+
project.Build(configNameDebug, output, "Build") |> AssertBuildSuccessful
238244
Assert.IsTrue(config.IsUpToDate(logger, true))
239245

240246
let startTime = DateTime.Now
241247

242-
for path in [exeObjPath; exeBinpath; pdbObjPath; pdbBinPath; xmlDocPath] do
248+
for path in [exeObjPath; exeBinpath; pdbObjPath; pdbBinPath; xmlDocPath; exeConfigPath] do
243249
printfn "Testing output %s" path
244250

245251
// touch file
@@ -283,25 +289,25 @@ type UpToDate() =
283289
Assert.IsFalse(debugConfigAnyCPU.IsUpToDate(logger, true))
284290
Assert.IsFalse(releaseConfigAnyCPU.IsUpToDate(logger, true))
285291

286-
project.Build(configNameDebugx86, output, "Build") |> ignore
292+
project.Build(configNameDebugx86, output, "Build") |> AssertBuildSuccessful
287293
Assert.IsTrue(debugConfigx86.IsUpToDate(logger, true))
288294
Assert.IsFalse(releaseConfigx86.IsUpToDate(logger, true))
289295
Assert.IsFalse(debugConfigAnyCPU.IsUpToDate(logger, true))
290296
Assert.IsFalse(releaseConfigAnyCPU.IsUpToDate(logger, true))
291297

292-
project.Build(configNameReleasex86, output, "Build") |> ignore
298+
project.Build(configNameReleasex86, output, "Build") |> AssertBuildSuccessful
293299
Assert.IsTrue(debugConfigx86.IsUpToDate(logger, true))
294300
Assert.IsTrue(releaseConfigx86.IsUpToDate(logger, true))
295301
Assert.IsFalse(debugConfigAnyCPU.IsUpToDate(logger, true))
296302
Assert.IsFalse(releaseConfigAnyCPU.IsUpToDate(logger, true))
297303

298-
project.Build(configNameDebugAnyCPU, output, "Build") |> ignore
304+
project.Build(configNameDebugAnyCPU, output, "Build") |> AssertBuildSuccessful
299305
Assert.IsTrue(debugConfigx86.IsUpToDate(logger, true))
300306
Assert.IsTrue(releaseConfigx86.IsUpToDate(logger, true))
301307
Assert.IsTrue(debugConfigAnyCPU.IsUpToDate(logger, true))
302308
Assert.IsFalse(releaseConfigAnyCPU.IsUpToDate(logger, true))
303309

304-
project.Build(configNameReleaseAnyCPU, output, "Build") |> ignore
310+
project.Build(configNameReleaseAnyCPU, output, "Build") |> AssertBuildSuccessful
305311
Assert.IsTrue(debugConfigx86.IsUpToDate(logger, true))
306312
Assert.IsTrue(releaseConfigx86.IsUpToDate(logger, true))
307313
Assert.IsTrue(debugConfigAnyCPU.IsUpToDate(logger, true))
@@ -320,3 +326,55 @@ type UpToDate() =
320326

321327
Assert.IsFalse(config.IsFastUpToDateCheckEnabled())
322328
))
329+
330+
[<TestFixture>]
331+
type ``UpToDate PreserveNewest`` () =
332+
333+
[<Test>]
334+
member public this.IsUpToDatePreserveNewest () =
335+
336+
let test (input, inputTimestamp) (output, outputTimestamp) =
337+
let logs = ref []
338+
let outputPanel = VsMocks.vsOutputWindowPane(logs)
339+
let logger = OutputWindowLogger.CreateUpToDateCheckLogger(outputPanel)
340+
341+
let tryTimestamp (path: string) (_l: OutputWindowLogger) =
342+
let toN = function Some d -> Nullable<_>(d) | None -> Nullable<_>()
343+
match path with
344+
| x when x = input -> toN inputTimestamp
345+
| x when x = output -> toN outputTimestamp
346+
| _ -> failwithf "unexpected %s" path
347+
348+
let u = ProjectConfig.IsUpToDatePreserveNewest(logger, (Func<_,_,_>(tryTimestamp)), input, output)
349+
u, !logs
350+
351+
let now = System.DateTime.Now
352+
let before = now.AddHours(-1.0)
353+
354+
let ``no input -> not up-to-date and log`` =
355+
let u, logs = test ("readme.md", None) ("leggimi.md", None)
356+
Assert.IsFalse(u)
357+
logs
358+
|> List.exists (fun s -> s.Contains("readme.md") && s.Contains("can't find expected input"))
359+
|> Assert.IsTrue
360+
361+
let ``no output -> not up-to-date and log`` =
362+
let u, logs = test ("from.txt", Some now) ("to.txt", None)
363+
Assert.IsFalse(u)
364+
logs
365+
|> List.exists (fun s -> s.Contains("to.txt") && s.Contains("can't find expected output"))
366+
|> Assert.IsTrue
367+
368+
let ``a newer version of output file is ok`` =
369+
let u, logs = test ("before.doc", Some before) ("after.doc", Some now)
370+
Assert.True(u)
371+
logs |> AssertEqual []
372+
373+
let ``stale output file -> not up-to-date and log`` =
374+
let u, logs = test ("logo.png", Some now) ("animatedlogo.gif", Some before)
375+
Assert.IsFalse(u)
376+
logs
377+
|> List.exists (fun s -> s.Contains("animatedlogo.gif") && s.Contains("stale"))
378+
|> Assert.IsTrue
379+
380+
()

vsintegration/src/vs/FsPkgs/FSharp.Project/Common.Source.CSharp/Project/ProjectConfig.cs

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1197,7 +1197,7 @@ public virtual int QueryDebugTargets(uint grfLaunch, uint cTargets, VsDebugTarge
11971197
public virtual int OpenOutputGroup(string szCanonicalName, out IVsOutputGroup ppIVsOutputGroup)
11981198
{
11991199
ppIVsOutputGroup = null;
1200-
// Search through our list of groups to find the one they are looking forgroupName
1200+
// Search through our list of groups to find the one they are looking for groupName
12011201
foreach (OutputGroup group in OutputGroups)
12021202
{
12031203
string groupName;
@@ -1458,7 +1458,7 @@ private static bool IsPossibleOutputGroup(string groupName)
14581458
return null;
14591459
}
14601460

1461-
internal bool GetUTDCheckInputs(ref HashSet<string> inputs)
1461+
internal bool GetUTDCheckInputs(HashSet<string> inputs)
14621462
{
14631463
// the project file itself
14641464
inputs.Add(Utilities.CanonicalizeFileNameNoThrow(this.project.BuildProject.FullPath));
@@ -1527,8 +1527,10 @@ internal bool GetUTDCheckInputs(ref HashSet<string> inputs)
15271527
return true;
15281528
}
15291529

1530-
internal bool GetUTDCheckOutputs(ref HashSet<string> outputs, HashSet<string> inputs)
1530+
internal void GetUTDCheckOutputs(HashSet<string> inputs, HashSet<string> outputs, out List<Tuple<string, string>> preserveNewestOutputs)
15311531
{
1532+
preserveNewestOutputs = new List<Tuple<string, string>>();
1533+
15321534
// Output groups give us the paths to the following outputs
15331535
// result EXE or DLL in "obj" dir
15341536
// PDB file in "obj" dir (if project is configured to create this)
@@ -1546,15 +1548,29 @@ internal bool GetUTDCheckOutputs(ref HashSet<string> outputs, HashSet<string> in
15461548
var outputAssembly = this.project.GetOutputAssembly(this.ConfigCanonicalName);
15471549
outputs.Add(Utilities.CanonicalizeFileNameNoThrow(outputAssembly));
15481550

1551+
bool isExe = outputAssembly.EndsWith(".exe", StringComparison.OrdinalIgnoreCase);
1552+
15491553
// final PDB path
15501554
if (this.DebugSymbols &&
1551-
(outputAssembly.EndsWith(".exe", StringComparison.OrdinalIgnoreCase) || outputAssembly.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)))
1555+
(isExe || outputAssembly.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)))
15521556
{
15531557
var pdbPath = outputAssembly.Remove(outputAssembly.Length - 4) + ".pdb";
15541558
outputs.Add(Utilities.CanonicalizeFileNameNoThrow(pdbPath));
15551559
}
15561560

1557-
return true;
1561+
if (isExe)
1562+
{
1563+
var appConfig = inputs.FirstOrDefault(x => String.Compare(Path.GetFileName(x), "app.config", StringComparison.OrdinalIgnoreCase) == 0);
1564+
if (appConfig != null)
1565+
{
1566+
// the app.config is not removed from the inputs to maintain
1567+
// the same behavior of a C# project:
1568+
// When a app.config is changed, after the build, the project
1569+
// is not up-to-date until a rebuild
1570+
var exeConfig = Utilities.CanonicalizeFileNameNoThrow(outputAssembly + ".config");
1571+
preserveNewestOutputs.Add(Tuple.Create(appConfig, exeConfig));
1572+
}
1573+
}
15581574
}
15591575

15601576
// there is a well-known property users can specify that signals for UTD check to be disabled
@@ -1584,12 +1600,12 @@ internal bool IsUpToDate(OutputWindowLogger logger, bool testing)
15841600
}
15851601

15861602
var inputs = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
1587-
if (!GetUTDCheckInputs(ref inputs))
1603+
if (!GetUTDCheckInputs(inputs))
15881604
return false;
15891605

15901606
var outputs = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
1591-
if (!GetUTDCheckOutputs(ref outputs, inputs))
1592-
return false;
1607+
List<Tuple<string, string>> preserveNewestOutputs;
1608+
GetUTDCheckOutputs(inputs, outputs, out preserveNewestOutputs);
15931609

15941610
// determine the oldest output timestamp
15951611
DateTime stalestOutputTime = DateTime.MaxValue.ToUniversalTime();
@@ -1625,13 +1641,48 @@ internal bool IsUpToDate(OutputWindowLogger logger, bool testing)
16251641
freshestInputTime = timeStamp.Value;
16261642
}
16271643

1644+
// check 1-1 Preserve Newest mappings
1645+
foreach (var kv in preserveNewestOutputs)
1646+
{
1647+
if (!IsUpToDatePreserveNewest(logger, TryGetLastWriteTimeUtc, kv.Item1, kv.Item2))
1648+
return false;
1649+
}
1650+
16281651
logger.WriteLine("Freshest input: {0}", freshestInputTime.ToLocalTime());
16291652
logger.WriteLine("Stalest output: {0}", stalestOutputTime.ToLocalTime());
16301653
logger.WriteLine("Up to date: {0}", freshestInputTime <= stalestOutputTime);
16311654

1632-
// if all outputs are younger than all inuts, we are up to date
1655+
// if all outputs are younger than all inputs, we are up to date
16331656
return freshestInputTime <= stalestOutputTime;
16341657
}
1658+
1659+
public static bool IsUpToDatePreserveNewest(OutputWindowLogger logger, Func<string, OutputWindowLogger, DateTime?> tryGetLastWriteTimeUtc, string input, string output)
1660+
{
1661+
var inputTime = tryGetLastWriteTimeUtc(input, logger);
1662+
if (!inputTime.HasValue)
1663+
{
1664+
logger.WriteLine("Declaring project NOT up to date, can't find expected input {0}", input);
1665+
return false;
1666+
}
1667+
1668+
var outputTime = tryGetLastWriteTimeUtc(output, logger);
1669+
if (!outputTime.HasValue)
1670+
{
1671+
logger.WriteLine("Declaring project NOT up to date, can't find expected output {0}", output);
1672+
return false;
1673+
}
1674+
1675+
var inputTimeValue = inputTime.Value;
1676+
var outputTimeValue = outputTime.Value;
1677+
1678+
if (outputTimeValue < inputTimeValue)
1679+
{
1680+
logger.WriteLine("Declaring project NOT up to date, ouput {0} is stale", output);
1681+
return false;
1682+
}
1683+
1684+
return true;
1685+
}
16351686
}
16361687

16371688
internal class ClassLibraryCannotBeStartedDirectlyException : COMException

0 commit comments

Comments
 (0)