mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-04 18:26:39 +02:00
Functionality to detect Win32 apps which are installed, deleted or renamed while PowerToys is running (#4960)
* Added file system wrapper and interface * added win32program repository which would load store app and also handle new apps being added/deleted * Added event handlers to win32 program repo * added paths to monitor and setting FSWs for each location * Events firing as expected * filter extensions added, events fire as expected * override gethashcode so that duplicates don't show up. OnCreated and OnDeleted events trigger as expected * implemented setter for filters in FileSystemWatcher * Rename adds item but does not seem to delete the previous app * catching an exception when a duplicate item is inserted * Removed notify filter for directory because we only need to monitor files * Added exe programs to be indexed in the desktop and startmenu * created a new class to init FileSystemHelpers instead of main * Added fix for shortcut applications to work as expected while renaming and deleting them * Added wrappers for file system operations * Added some tests * Added all tests for appref-ms and added a condition to search in sub directories * Added tests for Exe applications * Added lnk app tests * Added tests for shortcut applications * removed unnecessary wrappers * override Equals for win32 * removed debug statements * Fixed exe issue * Fixed internet shortcut exception * fixed renaming shortcut apps * Added a retry block for ReadAllLines * capitalized method name - RetrieveTargetPath * made naming consistent, helper variables start with underscore * Added the exception condition back * renamed Win32ProgramRepositoryHelper to Win32ProgramFileSystemWatchers * made win32Program repository variable static * changed list to ilist * disposed file system watchers * make retrieveTargetPath upper case in tests
This commit is contained in:
@@ -0,0 +1,363 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using System.Linq;
|
||||
using Wox.Infrastructure.Storage;
|
||||
using Wox.Plugin;
|
||||
using Microsoft.Plugin.Program.Programs;
|
||||
using Microsoft.Plugin.Program.Storage;
|
||||
using System.IO;
|
||||
using Wox.Infrastructure.FileSystemHelper;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Microsoft.Plugin.Program.UnitTests.Storage
|
||||
{
|
||||
using Win32 = Program.Programs.Win32;
|
||||
|
||||
[TestFixture]
|
||||
class Win32ProgramRepositoryTest
|
||||
{
|
||||
List<IFileSystemWatcherWrapper> _fileSystemWatchers;
|
||||
Settings _settings = new Settings();
|
||||
string[] _pathsToWatch = new string[] { "location1", "location2" };
|
||||
List<Mock<IFileSystemWatcherWrapper>> _fileSystemMocks;
|
||||
|
||||
[SetUp]
|
||||
public void SetFileSystemWatchers()
|
||||
{
|
||||
_fileSystemWatchers = new List<IFileSystemWatcherWrapper>();
|
||||
_fileSystemMocks = new List<Mock<IFileSystemWatcherWrapper>>();
|
||||
for (int index = 0; index < _pathsToWatch.Length; index++)
|
||||
{
|
||||
var mockFileWatcher = new Mock<IFileSystemWatcherWrapper>();
|
||||
_fileSystemMocks.Add(mockFileWatcher);
|
||||
_fileSystemWatchers.Add(mockFileWatcher.Object);
|
||||
}
|
||||
}
|
||||
|
||||
[TestCase("Name", "ExecutableName", "FullPath", "description1", "description2")]
|
||||
public void Win32Repository_MustNotStoreDuplicates_WhileAddingItemsWithSameHashCode(string name, string exename, string fullPath, string description1, string description2)
|
||||
{
|
||||
// Arrange
|
||||
Win32ProgramRepository _win32ProgramRepository = new Win32ProgramRepository(_fileSystemWatchers, new BinaryStorage<IList<Win32>>("Win32"), _settings, _pathsToWatch);
|
||||
|
||||
Win32 item1 = new Win32
|
||||
{
|
||||
Name = name,
|
||||
ExecutableName = exename,
|
||||
FullPath = fullPath,
|
||||
Description = description1
|
||||
};
|
||||
|
||||
Win32 item2 = new Win32
|
||||
{
|
||||
Name = name,
|
||||
ExecutableName = exename,
|
||||
FullPath = fullPath,
|
||||
Description = description2
|
||||
};
|
||||
|
||||
// Act
|
||||
_win32ProgramRepository.Add(item1);
|
||||
|
||||
Assert.AreEqual(_win32ProgramRepository.Count(), 1);
|
||||
|
||||
// To add an item with the same hashCode, ie, same name, exename and fullPath
|
||||
_win32ProgramRepository.Add(item2);
|
||||
|
||||
// Assert, count still remains 1 because they are duplicate items
|
||||
Assert.AreEqual(_win32ProgramRepository.Count(), 1);
|
||||
}
|
||||
|
||||
[TestCase("path.appref-ms")]
|
||||
public void Win32ProgramRepository_MustCallOnAppCreatedForApprefApps_WhenCreatedEventIsRaised(string path)
|
||||
{
|
||||
// Arrange
|
||||
Win32ProgramRepository _win32ProgramRepository = new Win32ProgramRepository(_fileSystemWatchers, new BinaryStorage<IList<Win32>>("Win32"), _settings, _pathsToWatch);
|
||||
FileSystemEventArgs e = new FileSystemEventArgs(WatcherChangeTypes.Created, "directory", path);
|
||||
|
||||
// Act
|
||||
_fileSystemMocks[0].Raise(m => m.Created += null, e);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(_win32ProgramRepository.Count(), 1);
|
||||
Assert.AreEqual(_win32ProgramRepository.ElementAt(0).AppType, 2);
|
||||
}
|
||||
|
||||
[TestCase("directory", "path.appref-ms")]
|
||||
public void Win32ProgramRepository_MustCallOnAppDeletedForApprefApps_WhenDeletedEventIsRaised(string directory, string path)
|
||||
{
|
||||
// Arrange
|
||||
Win32ProgramRepository _win32ProgramRepository = new Win32ProgramRepository(_fileSystemWatchers, new BinaryStorage<IList<Win32>>("Win32"), _settings, _pathsToWatch);
|
||||
FileSystemEventArgs e = new FileSystemEventArgs(WatcherChangeTypes.Deleted, directory, path);
|
||||
|
||||
string fullPath = directory + "\\" + path;
|
||||
Win32 item = Win32.GetAppFromPath(fullPath);
|
||||
_win32ProgramRepository.Add(item);
|
||||
|
||||
// Act
|
||||
_fileSystemMocks[0].Raise(m => m.Deleted += null, e);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(_win32ProgramRepository.Count(), 0);
|
||||
}
|
||||
|
||||
[TestCase("directory", "oldpath.appref-ms", "newpath.appref-ms")]
|
||||
public void Win32ProgramRepository_MustCallOnAppRenamedForApprefApps_WhenRenamedEventIsRaised(string directory, string oldpath, string newpath)
|
||||
{
|
||||
// Arrange
|
||||
Win32ProgramRepository _win32ProgramRepository = new Win32ProgramRepository(_fileSystemWatchers, new BinaryStorage<IList<Win32>>("Win32"), _settings, _pathsToWatch);
|
||||
RenamedEventArgs e = new RenamedEventArgs(WatcherChangeTypes.Renamed, directory , newpath, oldpath);
|
||||
|
||||
string oldFullPath = directory + "\\" + oldpath;
|
||||
string newFullPath = directory + "\\" + newpath;
|
||||
|
||||
Win32 olditem = Win32.GetAppFromPath(oldFullPath);
|
||||
Win32 newitem = Win32.GetAppFromPath(newFullPath);
|
||||
_win32ProgramRepository.Add(olditem);
|
||||
|
||||
// Act
|
||||
_fileSystemMocks[0].Raise(m => m.Renamed += null, e);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(_win32ProgramRepository.Count(), 1);
|
||||
Assert.IsTrue(_win32ProgramRepository.Contains(newitem));
|
||||
Assert.IsFalse(_win32ProgramRepository.Contains(olditem));
|
||||
}
|
||||
|
||||
[TestCase("path.exe")]
|
||||
public void Win32ProgramRepository_MustCallOnAppCreatedForExeApps_WhenCreatedEventIsRaised(string path)
|
||||
{
|
||||
// Arrange
|
||||
Win32ProgramRepository _win32ProgramRepository = new Win32ProgramRepository(_fileSystemWatchers, new BinaryStorage<IList<Win32>>("Win32"), _settings, _pathsToWatch);
|
||||
FileSystemEventArgs e = new FileSystemEventArgs(WatcherChangeTypes.Created, "directory", path);
|
||||
|
||||
// FileVersionInfo must be mocked for exe applications
|
||||
var mockFileVersionInfo = new Mock<IFileVersionInfoWrapper>();
|
||||
mockFileVersionInfo.Setup(m => m.GetVersionInfo(It.IsAny<string>())).Returns((FileVersionInfo)null);
|
||||
Win32._fileVersionInfoWrapper = mockFileVersionInfo.Object;
|
||||
|
||||
// Act
|
||||
_fileSystemMocks[0].Raise(m => m.Created += null, e);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(_win32ProgramRepository.Count(), 1);
|
||||
Assert.AreEqual(_win32ProgramRepository.ElementAt(0).AppType, 2);
|
||||
}
|
||||
|
||||
[TestCase("directory", "path.exe")]
|
||||
public void Win32ProgramRepository_MustCallOnAppDeletedForExeApps_WhenDeletedEventIsRaised(string directory, string path)
|
||||
{
|
||||
// Arrange
|
||||
Win32ProgramRepository _win32ProgramRepository = new Win32ProgramRepository(_fileSystemWatchers, new BinaryStorage<IList<Win32>>("Win32"), _settings, _pathsToWatch);
|
||||
FileSystemEventArgs e = new FileSystemEventArgs(WatcherChangeTypes.Deleted, directory, path);
|
||||
|
||||
// FileVersionInfo must be mocked for exe applications
|
||||
var mockFileVersionInfo = new Mock<IFileVersionInfoWrapper>();
|
||||
mockFileVersionInfo.Setup(m => m.GetVersionInfo(It.IsAny<string>())).Returns((FileVersionInfo)null);
|
||||
Win32._fileVersionInfoWrapper = mockFileVersionInfo.Object;
|
||||
|
||||
string fullPath = directory + "\\" + path;
|
||||
Win32 item = Win32.GetAppFromPath(fullPath);
|
||||
_win32ProgramRepository.Add(item);
|
||||
|
||||
// Act
|
||||
_fileSystemMocks[0].Raise(m => m.Deleted += null, e);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(_win32ProgramRepository.Count(), 0);
|
||||
}
|
||||
|
||||
[TestCase("directory", "oldpath.appref-ms", "newpath.appref-ms")]
|
||||
public void Win32ProgramRepository_MustCallOnAppRenamedForExeApps_WhenRenamedEventIsRaised(string directory, string oldpath, string newpath)
|
||||
{
|
||||
// Arrange
|
||||
Win32ProgramRepository _win32ProgramRepository = new Win32ProgramRepository(_fileSystemWatchers, new BinaryStorage<IList<Win32>>("Win32"), _settings, _pathsToWatch);
|
||||
RenamedEventArgs e = new RenamedEventArgs(WatcherChangeTypes.Renamed, directory, newpath, oldpath);
|
||||
|
||||
string oldFullPath = directory + "\\" + oldpath;
|
||||
string newFullPath = directory + "\\" + newpath;
|
||||
|
||||
// FileVersionInfo must be mocked for exe applications
|
||||
var mockFileVersionInfo = new Mock<IFileVersionInfoWrapper>();
|
||||
mockFileVersionInfo.Setup(m => m.GetVersionInfo(It.IsAny<string>())).Returns((FileVersionInfo)null);
|
||||
Win32._fileVersionInfoWrapper = mockFileVersionInfo.Object;
|
||||
|
||||
Win32 olditem = Win32.GetAppFromPath(oldFullPath);
|
||||
Win32 newitem = Win32.GetAppFromPath(newFullPath);
|
||||
_win32ProgramRepository.Add(olditem);
|
||||
|
||||
// Act
|
||||
_fileSystemMocks[0].Raise(m => m.Renamed += null, e);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(_win32ProgramRepository.Count(), 1);
|
||||
Assert.IsTrue(_win32ProgramRepository.Contains(newitem));
|
||||
Assert.IsFalse(_win32ProgramRepository.Contains(olditem));
|
||||
}
|
||||
|
||||
[TestCase("path.url")]
|
||||
public void Win32ProgramRepository_MustCallOnAppCreatedForUrlApps_WhenCreatedEventIsRaised(string path)
|
||||
{
|
||||
// Arrange
|
||||
Win32ProgramRepository _win32ProgramRepository = new Win32ProgramRepository(_fileSystemWatchers, new BinaryStorage<IList<Win32>>("Win32"), _settings, _pathsToWatch);
|
||||
FileSystemEventArgs e = new FileSystemEventArgs(WatcherChangeTypes.Created, "directory", path);
|
||||
|
||||
// File.ReadAllLines must be mocked for url applications
|
||||
var mockFile = new Mock<IFileWrapper>();
|
||||
mockFile.Setup(m => m.ReadAllLines(It.IsAny<string>())).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
|
||||
}
|
||||
|
||||
[TestCase("directory", "path.url")]
|
||||
public void Win32ProgramRepository_MustCallOnAppDeletedForUrlApps_WhenDeletedEventIsRaised(string directory, string path)
|
||||
{
|
||||
// Arrange
|
||||
Win32ProgramRepository _win32ProgramRepository = new Win32ProgramRepository(_fileSystemWatchers, new BinaryStorage<IList<Win32>>("Win32"), _settings, _pathsToWatch);
|
||||
FileSystemEventArgs e = new FileSystemEventArgs(WatcherChangeTypes.Deleted, directory, path);
|
||||
|
||||
// File.ReadAllLines must be mocked for url applications
|
||||
var mockFile = new Mock<IFileWrapper>();
|
||||
mockFile.Setup(m => m.ReadAllLines(It.IsAny<string>())).Returns(new string[] { "URL=steam://rungameid/1258080", "IconFile=iconFile" });
|
||||
Win32._fileWrapper = mockFile.Object;
|
||||
|
||||
string fullPath = directory + "\\" + path;
|
||||
Win32 item = Win32.GetAppFromPath(fullPath);
|
||||
_win32ProgramRepository.Add(item);
|
||||
|
||||
// Act
|
||||
_fileSystemMocks[0].Raise(m => m.Deleted += null, e);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(_win32ProgramRepository.Count(), 0);
|
||||
}
|
||||
|
||||
[TestCase("directory", "oldpath.url", "newpath.url")]
|
||||
public void Win32ProgramRepository_MustCallOnAppRenamedForUrlApps_WhenRenamedEventIsRaised(string directory, string oldpath, string newpath)
|
||||
{
|
||||
// Arrange
|
||||
Win32ProgramRepository _win32ProgramRepository = new Win32ProgramRepository(_fileSystemWatchers, new BinaryStorage<IList<Win32>>("Win32"), _settings, _pathsToWatch);
|
||||
RenamedEventArgs e = new RenamedEventArgs(WatcherChangeTypes.Renamed, directory, newpath, oldpath);
|
||||
|
||||
// File.ReadAllLines must be mocked for url applications
|
||||
var mockFile = new Mock<IFileWrapper>();
|
||||
mockFile.Setup(m => m.ReadAllLines(It.IsAny<string>())).Returns(new string[] { "URL=steam://rungameid/1258080", "IconFile=iconFile" });
|
||||
Win32._fileWrapper = mockFile.Object;
|
||||
|
||||
string oldFullPath = directory + "\\" + oldpath;
|
||||
string newFullPath = directory + "\\" + newpath;
|
||||
|
||||
Win32 olditem = Win32.GetAppFromPath(oldFullPath);
|
||||
Win32 newitem = Win32.GetAppFromPath(newFullPath);
|
||||
_win32ProgramRepository.Add(olditem);
|
||||
|
||||
// Act
|
||||
_fileSystemMocks[0].Raise(m => m.Renamed += null, e);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(_win32ProgramRepository.Count(), 1);
|
||||
Assert.IsTrue(_win32ProgramRepository.Contains(newitem));
|
||||
Assert.IsFalse(_win32ProgramRepository.Contains(olditem));
|
||||
}
|
||||
|
||||
|
||||
[TestCase("path.lnk")]
|
||||
public void Win32ProgramRepository_MustCallOnAppCreatedForLnkApps_WhenCreatedEventIsRaised(string path)
|
||||
{
|
||||
// Arrange
|
||||
Win32ProgramRepository _win32ProgramRepository = new Win32ProgramRepository(_fileSystemWatchers, new BinaryStorage<IList<Win32>>("Win32"), _settings, _pathsToWatch);
|
||||
FileSystemEventArgs e = new FileSystemEventArgs(WatcherChangeTypes.Created, "directory", path);
|
||||
|
||||
// ShellLinkHelper must be mocked for lnk applications
|
||||
var mockShellLink = new Mock<IShellLinkHelper>();
|
||||
mockShellLink.Setup(m => m.RetrieveTargetPath(It.IsAny<string>())).Returns(String.Empty);
|
||||
Win32._helper = mockShellLink.Object;
|
||||
|
||||
// Act
|
||||
_fileSystemMocks[0].Raise(m => m.Created += null, e);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(_win32ProgramRepository.Count(), 1);
|
||||
Assert.AreEqual(_win32ProgramRepository.ElementAt(0).AppType, 2);
|
||||
}
|
||||
|
||||
[TestCase("directory", "path.lnk")]
|
||||
public void Win32ProgramRepository_MustCallOnAppDeletedForLnkApps_WhenDeletedEventIsRaised(string directory, string path)
|
||||
{
|
||||
// Arrange
|
||||
Win32ProgramRepository _win32ProgramRepository = new Win32ProgramRepository(_fileSystemWatchers, new BinaryStorage<IList<Win32>>("Win32"), _settings, _pathsToWatch);
|
||||
FileSystemEventArgs e = new FileSystemEventArgs(WatcherChangeTypes.Deleted, directory, path);
|
||||
|
||||
// ShellLinkHelper must be mocked for lnk applications
|
||||
var mockShellLink = new Mock<IShellLinkHelper>();
|
||||
mockShellLink.Setup(m => m.RetrieveTargetPath(It.IsAny<string>())).Returns(String.Empty);
|
||||
Win32._helper = mockShellLink.Object;
|
||||
|
||||
string fullPath = directory + "\\" + path;
|
||||
Win32 item = new Win32
|
||||
{
|
||||
Name = "path",
|
||||
ExecutableName = "path.exe",
|
||||
ParentDirectory = "directory",
|
||||
FullPath = "directory\\path.exe",
|
||||
LnkResolvedPath = "directory\\path.lnk" // This must be equal for lnk applications
|
||||
};
|
||||
_win32ProgramRepository.Add(item);
|
||||
|
||||
// Act
|
||||
_fileSystemMocks[0].Raise(m => m.Deleted += null, e);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(_win32ProgramRepository.Count(), 0);
|
||||
}
|
||||
|
||||
[TestCase("directory", "oldpath.lnk", "path.lnk")]
|
||||
public void Win32ProgramRepository_MustCallOnAppRenamedForLnkApps_WhenRenamedEventIsRaised(string directory, string oldpath, string path)
|
||||
{
|
||||
// Arrange
|
||||
Win32ProgramRepository _win32ProgramRepository = new Win32ProgramRepository(_fileSystemWatchers, new BinaryStorage<IList<Win32>>("Win32"), _settings, _pathsToWatch);
|
||||
RenamedEventArgs e = new RenamedEventArgs(WatcherChangeTypes.Renamed, directory, path, oldpath);
|
||||
|
||||
string oldFullPath = directory + "\\" + oldpath;
|
||||
string FullPath = directory + "\\" + path;
|
||||
|
||||
// ShellLinkHelper must be mocked for lnk applications
|
||||
var mockShellLink = new Mock<IShellLinkHelper>();
|
||||
mockShellLink.Setup(m => m.RetrieveTargetPath(It.IsAny<string>())).Returns(String.Empty);
|
||||
Win32._helper = mockShellLink.Object;
|
||||
|
||||
// old item and new item are the actual items when they are in existence
|
||||
Win32 olditem = new Win32
|
||||
{
|
||||
Name = "oldpath",
|
||||
ExecutableName = path,
|
||||
FullPath = FullPath,
|
||||
};
|
||||
|
||||
Win32 newitem = new Win32
|
||||
{
|
||||
Name = "path",
|
||||
ExecutableName = path,
|
||||
FullPath = FullPath,
|
||||
};
|
||||
|
||||
_win32ProgramRepository.Add(olditem);
|
||||
|
||||
// Act
|
||||
_fileSystemMocks[0].Raise(m => m.Renamed += null, e);
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(_win32ProgramRepository.Count(), 1);
|
||||
Assert.IsTrue(_win32ProgramRepository.Contains(newitem));
|
||||
Assert.IsFalse(_win32ProgramRepository.Contains(olditem));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user