Skip to content
This repository has been archived by the owner on Nov 20, 2018. It is now read-only.

Commit

Permalink
- E2E test
Browse files Browse the repository at this point in the history
- File watcher that takes the globbing patterns into account
- A big rewrite of the core algorithm
  • Loading branch information
Victor Hurdugaci committed Feb 29, 2016
1 parent b64b30b commit 7262063
Show file tree
Hide file tree
Showing 40 changed files with 1,390 additions and 406 deletions.
40 changes: 40 additions & 0 deletions dotnet-watch.sln
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,20 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
NuGet.Config = NuGet.Config
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{F5B382BC-258F-46E1-AC3D-10E5CCD55134}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "dotnet-watch.FunctionalTests", "test\dotnet-watch.FunctionalTests\dotnet-watch.FunctionalTests.xproj", "{16BADE2F-1184-4518-8A70-B68A19D0805B}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestApps", "TestApps", "{2876B12E-5841-4792-85A8-2929AEE11885}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "NoDepsApp", "test\TestApps\NoDepsApp\NoDepsApp.xproj", "{4F0D8A80-221F-4BCB-822E-44A0655F537E}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "GlobbingApp", "test\TestApps\GlobbingApp\GlobbingApp.xproj", "{2AB1A28B-2022-49EA-AF77-AC8A875915CC}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "AppWithDeps", "test\TestApps\AppWithDeps\AppWithDeps.xproj", "{F7734E61-F510-41E0-AD15-301A64081CD1}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Dependency", "test\TestApps\Dependency\Dependency.xproj", "{2F48041A-F7D1-478F-9C38-D41F0F05E8CA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -29,12 +43,38 @@ Global
{D3DA3BBB-E206-404F-AEE6-17FB9B6F1221}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D3DA3BBB-E206-404F-AEE6-17FB9B6F1221}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D3DA3BBB-E206-404F-AEE6-17FB9B6F1221}.Release|Any CPU.Build.0 = Release|Any CPU
{16BADE2F-1184-4518-8A70-B68A19D0805B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{16BADE2F-1184-4518-8A70-B68A19D0805B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{16BADE2F-1184-4518-8A70-B68A19D0805B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{16BADE2F-1184-4518-8A70-B68A19D0805B}.Release|Any CPU.Build.0 = Release|Any CPU
{4F0D8A80-221F-4BCB-822E-44A0655F537E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4F0D8A80-221F-4BCB-822E-44A0655F537E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4F0D8A80-221F-4BCB-822E-44A0655F537E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4F0D8A80-221F-4BCB-822E-44A0655F537E}.Release|Any CPU.Build.0 = Release|Any CPU
{2AB1A28B-2022-49EA-AF77-AC8A875915CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2AB1A28B-2022-49EA-AF77-AC8A875915CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2AB1A28B-2022-49EA-AF77-AC8A875915CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2AB1A28B-2022-49EA-AF77-AC8A875915CC}.Release|Any CPU.Build.0 = Release|Any CPU
{F7734E61-F510-41E0-AD15-301A64081CD1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F7734E61-F510-41E0-AD15-301A64081CD1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F7734E61-F510-41E0-AD15-301A64081CD1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F7734E61-F510-41E0-AD15-301A64081CD1}.Release|Any CPU.Build.0 = Release|Any CPU
{2F48041A-F7D1-478F-9C38-D41F0F05E8CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2F48041A-F7D1-478F-9C38-D41F0F05E8CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2F48041A-F7D1-478F-9C38-D41F0F05E8CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2F48041A-F7D1-478F-9C38-D41F0F05E8CA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{8A8CEABC-AC47-43FF-A5DF-69224F7E1F46} = {66517987-2A5A-4330-B130-207039378FD4}
{D3DA3BBB-E206-404F-AEE6-17FB9B6F1221} = {66517987-2A5A-4330-B130-207039378FD4}
{16BADE2F-1184-4518-8A70-B68A19D0805B} = {F5B382BC-258F-46E1-AC3D-10E5CCD55134}
{2876B12E-5841-4792-85A8-2929AEE11885} = {F5B382BC-258F-46E1-AC3D-10E5CCD55134}
{4F0D8A80-221F-4BCB-822E-44A0655F537E} = {2876B12E-5841-4792-85A8-2929AEE11885}
{2AB1A28B-2022-49EA-AF77-AC8A875915CC} = {2876B12E-5841-4792-85A8-2929AEE11885}
{F7734E61-F510-41E0-AD15-301A64081CD1} = {2876B12E-5841-4792-85A8-2929AEE11885}
{2F48041A-F7D1-478F-9C38-D41F0F05E8CA} = {2876B12E-5841-4792-85A8-2929AEE11885}
EndGlobalSection
EndGlobal
23 changes: 0 additions & 23 deletions src/Microsoft.DotNet.Watcher.Core/DictionaryExtensions.cs

This file was deleted.

98 changes: 26 additions & 72 deletions src/Microsoft.DotNet.Watcher.Core/DotNetWatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.DotNet.Watcher.Core.Internal;
using Microsoft.Extensions.Logging;

namespace Microsoft.DotNet.Watcher.Core
{
public class DotNetWatcher
{
private readonly Func<string, IFileWatcher> _fileWatcherFactory;
private readonly Func<IFileWatcher> _fileWatcherFactory;
private readonly Func<IProcessWatcher> _processWatcherFactory;
private readonly IProjectProvider _projectProvider;
private readonly ILoggerFactory _loggerFactory;
Expand All @@ -22,7 +22,7 @@ public class DotNetWatcher
public bool ExitOnChange { get; set; }

public DotNetWatcher(
Func<string, IFileWatcher> fileWatcherFactory,
Func<IFileWatcher> fileWatcherFactory,
Func<IProcessWatcher> processWatcherFactory,
IProjectProvider projectProvider,
ILoggerFactory loggerFactory)
Expand All @@ -37,6 +37,13 @@ public class DotNetWatcher

public async Task WatchAsync(string projectFile, string command, string[] dotnetArguments, string workingDir, CancellationToken cancellationToken)
{
//Console.WriteLine(System.Diagnostics.Process.GetCurrentProcess().Id);
//while (!System.Diagnostics.Debugger.IsAttached)
//{
// Thread.Sleep(500);
//}
//System.Diagnostics.Debugger.Break();

if (string.IsNullOrEmpty(projectFile))
{
throw new ArgumentNullException(nameof(projectFile));
Expand Down Expand Up @@ -86,15 +93,15 @@ public async Task WatchAsync(string projectFile, string command, string[] dotnet

while (true)
{
var project = await WaitForValidProjectJsonAsync(projectFile, cancellationToken);
await WaitForValidProjectJsonAsync(projectFile, cancellationToken);
cancellationToken.ThrowIfCancellationRequested();

using (var currentRunCancellationSource = new CancellationTokenSource())
using (var combinedCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(
cancellationToken,
currentRunCancellationSource.Token))
{
var fileWatchingTask = WaitForProjectFileToChangeAsync(project, combinedCancellationSource.Token);
var fileWatchingTask = WaitForProjectFileToChangeAsync(projectFile, combinedCancellationSource.Token);
var dotnetTask = WaitForDotnetToExitAsync(dotnetArgumentsAsString, workingDir, combinedCancellationSource.Token);

var tasksToWait = new Task[] { dotnetTask, fileWatchingTask };
Expand Down Expand Up @@ -129,7 +136,7 @@ public async Task WatchAsync(string projectFile, string command, string[] dotnet

_logger.LogInformation("Waiting for a file to change before restarting dotnet...");
// Now wait for a file to change before restarting dotnet
await WaitForProjectFileToChangeAsync(project, cancellationToken);
await WaitForProjectFileToChangeAsync(projectFile, cancellationToken);
}
else
{
Expand All @@ -146,12 +153,11 @@ public async Task WatchAsync(string projectFile, string command, string[] dotnet
}
}

private async Task<string> WaitForProjectFileToChangeAsync(IProject project, CancellationToken cancellationToken)
private async Task<string> WaitForProjectFileToChangeAsync(string projectFile, CancellationToken cancellationToken)
{
using (var fileWatcher = _fileWatcherFactory(Path.GetDirectoryName(project.ProjectFile)))
using (var projectWatcher = CreateProjectWatcher(projectFile, watchProjectJsonOnly: false))
{
AddProjectAndDependeciesToWatcher(project, fileWatcher);
return await WatchForFileChangeAsync(fileWatcher, cancellationToken);
return await projectWatcher.WaitForChangeAsync(cancellationToken);
}
}

Expand All @@ -161,104 +167,52 @@ private Task<int> WaitForDotnetToExitAsync(string dotnetArguments, string workin

var dotnetWatcher = _processWatcherFactory();
int dotnetProcessId = dotnetWatcher.Start("dotnet", dotnetArguments, workingDir);
_logger.LogInformation($"dotnet run process id: {dotnetProcessId}");
_logger.LogInformation($"dotnet process id: {dotnetProcessId}");

return dotnetWatcher.WaitForExitAsync(cancellationToken);
}

private async Task<IProject> WaitForValidProjectJsonAsync(string projectFile, CancellationToken cancellationToken)
private async Task WaitForValidProjectJsonAsync(string projectFile, CancellationToken cancellationToken)
{
IProject project = null;

while (true)
{
IProject project;
string errors;
if (_projectProvider.TryReadProject(projectFile, out project, out errors))
{
return project;
return;
}

_logger.LogError($"Error(s) reading project file '{projectFile}': ");
_logger.LogError(errors);
_logger.LogInformation("Fix the error to continue.");

using (var fileWatcher = _fileWatcherFactory(Path.GetDirectoryName(projectFile)))
using (var projectWatcher = CreateProjectWatcher(projectFile, watchProjectJsonOnly: true))
{
fileWatcher.WatchFile(projectFile);
fileWatcher.WatchProject(projectFile);

await WatchForFileChangeAsync(fileWatcher, cancellationToken);
await projectWatcher.WaitForChangeAsync(cancellationToken);

if (cancellationToken.IsCancellationRequested)
{
return null;
return;
}

_logger.LogInformation($"File changed: {projectFile}");
}
}
}

private void AddProjectAndDependeciesToWatcher(string projectFile, IFileWatcher fileWatcher)
{
IProject project;
string errors;

if (_projectProvider.TryReadProject(projectFile, out project, out errors))
{
AddProjectAndDependeciesToWatcher(project, fileWatcher);
}
}

private void AddProjectAndDependeciesToWatcher(IProject project, IFileWatcher fileWatcher)
{
foreach (var file in project.Files)
{
if (!string.IsNullOrEmpty(file))
{
fileWatcher.WatchDirectory(
Path.GetDirectoryName(file),
Path.GetExtension(file));
}
}

fileWatcher.WatchProject(project.ProjectFile);

foreach (var projFile in project.ProjectDependencies)
{
AddProjectAndDependeciesToWatcher(projFile, fileWatcher);
}
}

private async Task<string> WatchForFileChangeAsync(IFileWatcher fileWatcher, CancellationToken cancellationToken)
private ProjectWatcher CreateProjectWatcher(string projectFile, bool watchProjectJsonOnly)
{
var tcs = new TaskCompletionSource<string>();

cancellationToken.Register(() => tcs.TrySetResult(null));

Action<string> callback = path =>
{
tcs.TrySetResult(path);
};

fileWatcher.OnChanged += callback;

var changedPath = await tcs.Task;

// Don't need to listen anymore
fileWatcher.OnChanged -= callback;

return changedPath;
return new ProjectWatcher(projectFile, watchProjectJsonOnly, _fileWatcherFactory, _projectProvider);
}

public static DotNetWatcher CreateDefault(ILoggerFactory loggerFactory)
{
return new DotNetWatcher(
fileWatcherFactory: root => new FileWatcher(root),
fileWatcherFactory: () => new FileWatcher(),
processWatcherFactory: () => new ProcessWatcher(),
projectProvider: new ProjectProvider(),
loggerFactory: loggerFactory);
}

}
}

This file was deleted.

Loading

0 comments on commit 7262063

Please sign in to comment.