diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Storage/Win32ProgramRepositoryTest.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Storage/Win32ProgramRepositoryTest.cs index 7421bd6f1c..252310fece 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Storage/Win32ProgramRepositoryTest.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program.UnitTests/Storage/Win32ProgramRepositoryTest.cs @@ -198,23 +198,72 @@ namespace Microsoft.Plugin.Program.UnitTests.Storage } [TestCase("path.url")] - public void Win32ProgramRepository_MustCallOnAppCreatedForUrlApps_WhenCreatedEventIsRaised(string path) + public void Win32ProgramRepository_MustCallOnAppChangedForUrlApps_WhenChangedEventIsRaised(string path) { + // Arrange + Win32ProgramRepository _win32ProgramRepository = new Win32ProgramRepository(_fileSystemWatchers, new BinaryStorage>("Win32"), _settings, _pathsToWatch); + FileSystemEventArgs e = new FileSystemEventArgs(WatcherChangeTypes.Changed, "directory", path); + + // File.ReadAllLines must be mocked for url applications + var mockFile = new Mock(); + mockFile.Setup(m => m.ReadAllLines(It.IsAny())).Returns(new string[] { "URL=steam://rungameid/1258080", "IconFile=iconFile" }); + Win32._fileWrapper = mockFile.Object; + + // Act + _fileSystemMocks[0].Raise(m => m.Changed += null, e); + + // Assert + Assert.AreEqual(_win32ProgramRepository.Count(), 1); + Assert.AreEqual(_win32ProgramRepository.ElementAt(0).AppType, 1); // Internet Shortcut Application + } + + [TestCase("path.url")] + public void Win32ProgramRepository_MustNotCreateUrlApp_WhenCreatedEventIsRaised(string path) + { + // We are handing internet shortcut apps using the Changed event instead + // Arrange Win32ProgramRepository _win32ProgramRepository = new Win32ProgramRepository(_fileSystemWatchers, new BinaryStorage>("Win32"), _settings, _pathsToWatch); FileSystemEventArgs e = new FileSystemEventArgs(WatcherChangeTypes.Created, "directory", path); // File.ReadAllLines must be mocked for url applications var mockFile = new Mock(); - mockFile.Setup(m => m.ReadAllLines(It.IsAny())).Returns(new string[] { "URL=steam://rungameid/1258080" , "IconFile=iconFile"}); + mockFile.Setup(m => m.ReadAllLines(It.IsAny())).Returns(new string[] { "URL=steam://rungameid/1258080", "IconFile=iconFile" }); Win32._fileWrapper = mockFile.Object; // Act _fileSystemMocks[0].Raise(m => m.Created += null, e); // Assert - Assert.AreEqual(_win32ProgramRepository.Count(), 1); - Assert.AreEqual(_win32ProgramRepository.ElementAt(0).AppType, 1); // Internet Shortcut Application + Assert.AreEqual(_win32ProgramRepository.Count(), 0); + } + + [TestCase("path.exe")] + [TestCase("path.lnk")] + [TestCase("path.appref-ms")] + public void Win32ProgramRepository_MustNotCreateAnyAppOtherThanUrlApp_WhenChangedEventIsRaised(string path) + { + // We are handing internet shortcut apps using the Changed event instead + + // Arrange + Win32ProgramRepository _win32ProgramRepository = new Win32ProgramRepository(_fileSystemWatchers, new BinaryStorage>("Win32"), _settings, _pathsToWatch); + FileSystemEventArgs e = new FileSystemEventArgs(WatcherChangeTypes.Changed, "directory", path); + + // FileVersionInfo must be mocked for exe applications + var mockFileVersionInfo = new Mock(); + mockFileVersionInfo.Setup(m => m.GetVersionInfo(It.IsAny())).Returns((FileVersionInfo)null); + Win32._fileVersionInfoWrapper = mockFileVersionInfo.Object; + + // ShellLinkHelper must be mocked for lnk applications + var mockShellLink = new Mock(); + mockShellLink.Setup(m => m.RetrieveTargetPath(It.IsAny())).Returns(String.Empty); + Win32._helper = mockShellLink.Object; + + // Act + _fileSystemMocks[0].Raise(m => m.Changed += null, e); + + // Assert + Assert.AreEqual(_win32ProgramRepository.Count(), 0); } [TestCase("directory", "path.url")] diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Storage/Win32ProgramRepository.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Storage/Win32ProgramRepository.cs index f1147d6ee9..735db5d115 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Storage/Win32ProgramRepository.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Storage/Win32ProgramRepository.cs @@ -39,7 +39,7 @@ namespace Microsoft.Plugin.Program.Storage _fileSystemWatcherHelpers[index].Path = _pathsToWatch[index]; // to be notified when there is a change to a file - _fileSystemWatcherHelpers[index].NotifyFilter = NotifyFilters.FileName; + _fileSystemWatcherHelpers[index].NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite; // filtering the app types that we want to monitor _fileSystemWatcherHelpers[index].Filters = extensionsToWatch; @@ -48,6 +48,7 @@ namespace Microsoft.Plugin.Program.Storage _fileSystemWatcherHelpers[index].Created += OnAppCreated; _fileSystemWatcherHelpers[index].Deleted += OnAppDeleted; _fileSystemWatcherHelpers[index].Renamed += OnAppRenamed; + _fileSystemWatcherHelpers[index].Changed += OnAppChanged; // Enable the file system watcher _fileSystemWatcherHelpers[index].EnableRaisingEvents = true; @@ -166,10 +167,26 @@ namespace Microsoft.Plugin.Program.Storage private void OnAppCreated(object sender, FileSystemEventArgs e) { string path = e.FullPath; - Programs.Win32 app = Programs.Win32.GetAppFromPath(path); - if (app != null) + if(!Path.GetExtension(path).Equals(urlExtension)) { - Add(app); + Programs.Win32 app = Programs.Win32.GetAppFromPath(path); + if (app != null) + { + Add(app); + } + } + } + + private void OnAppChanged(object sender, FileSystemEventArgs e) + { + string path = e.FullPath; + if (Path.GetExtension(path).Equals(urlExtension)) + { + Programs.Win32 app = Programs.Win32.GetAppFromPath(path); + if (app != null) + { + Add(app); + } } } diff --git a/src/modules/launcher/Wox.Infrastructure/FileSystemHelper/FileWrapper.cs b/src/modules/launcher/Wox.Infrastructure/FileSystemHelper/FileWrapper.cs index 485928e38f..eb1a9b41f2 100644 --- a/src/modules/launcher/Wox.Infrastructure/FileSystemHelper/FileWrapper.cs +++ b/src/modules/launcher/Wox.Infrastructure/FileSystemHelper/FileWrapper.cs @@ -13,29 +13,15 @@ namespace Wox.Infrastructure.FileSystemHelper public string[] ReadAllLines(string path) { - int attempt = 0; - int maxRetries = 5; - - // Sometimes when files are being installed, url applications are written to line by line. - // During this process their contents cannot be read as they are being accessed by an other process. - // This ensures that if we do face this scenario, we retry after some time. - while(attempt < maxRetries) + try { - try - { - return File.ReadAllLines(path); - } - catch (IOException ex) - { - attempt++; - Thread.Sleep(500); - Log.Info($"File {path} is being accessed by another process| {ex.Message}"); - - } + return File.ReadAllLines(path); + } + catch (IOException ex) + { + Log.Info($"File {path} is being accessed by another process| {ex.Message}"); + return new string[] { String.Empty }; } - - return new string[] { String.Empty }; } - } } diff --git a/src/modules/launcher/Wox.Infrastructure/Storage/IFileSystemWatcherWrapper.cs b/src/modules/launcher/Wox.Infrastructure/Storage/IFileSystemWatcherWrapper.cs index 0ddb16fe6c..acb43c0847 100644 --- a/src/modules/launcher/Wox.Infrastructure/Storage/IFileSystemWatcherWrapper.cs +++ b/src/modules/launcher/Wox.Infrastructure/Storage/IFileSystemWatcherWrapper.cs @@ -8,6 +8,7 @@ namespace Wox.Infrastructure.Storage // Events to watch out for event FileSystemEventHandler Created; event FileSystemEventHandler Deleted; + event FileSystemEventHandler Changed; event RenamedEventHandler Renamed; // Properties of File System watcher