Compare commits

...

9 Commits
lei ... v0.23.2

Author SHA1 Message Date
Enrico Giordani
a27c013196 Fix for FZ losing layout settings 2020-10-07 17:08:36 +02:00
Enrico Giordani
e9a7b5d08e Update version to 0.23.2 2020-10-06 19:39:03 +02:00
Enrico Giordani
2b0896dded [FancyZones] Clone parent data only for new VD 2020-10-06 18:40:01 +02:00
Divyansh Srivastava
d246bdd32f Add exception handling in win32 program (#6958)
* Add exception handling to prevent program from failing due to error in one program

* Error handling for program path function

* Fix incorrect log value in ProgramLogger
2020-10-05 18:55:14 +02:00
Divyansh Srivastava
7116d69002 Fix autocomplete due to error in returned autocomplete text (#6988) 2020-10-03 10:41:53 +02:00
Arjun Balgovind
d8f0ebe105 [KBM] Moved unregistering of key delays to always run on the dispatcher thread to avoid mutex re-entrancy (#6959)
* Moved unregistering of key delays to always run on the dispatcher thread

* Updated comments
2020-10-02 09:38:21 +02:00
stefansjfw
2946b08823 [FancyZones] Fix stuck chrome tab when merging into existing window (#6931)
* Fix stuck Chrome tab when merging with existing window
2020-10-02 09:33:53 +02:00
Arjun Balgovind
7bcbda39ef [KBM] Avoid checking reserved/unassigned/oem-specific/undefined key codes during shortcut remaps (#6952)
* Updated list of key codes to be ignored

* Added comments
2020-10-02 09:20:03 +02:00
Enrico Giordani
b90363a041 Update version to 0.23.0 2020-09-28 18:49:36 +02:00
13 changed files with 213 additions and 179 deletions

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Version>0.21.2</Version>
<Version>0.23.2</Version>
</PropertyGroup>
</Project>

View File

@@ -171,7 +171,7 @@ std::optional<FancyZonesDataTypes::CustomZoneSetData> FancyZonesData::FindCustom
return it != end(customZoneSetsMap) ? std::optional{ it->second } : std::nullopt;
}
void FancyZonesData::AddDevice(const std::wstring& deviceId)
bool FancyZonesData::AddDevice(const std::wstring& deviceId)
{
using namespace FancyZonesDataTypes;
@@ -192,7 +192,11 @@ void FancyZonesData::AddDevice(const std::wstring& deviceId)
{
deviceInfoMap[deviceId] = DeviceInfoData{ ZoneSetData{ NonLocalizable::NullStr, ZoneSetLayoutType::Blank } };
}
return true;
}
return false;
}
void FancyZonesData::CloneDeviceInfo(const std::wstring& source, const std::wstring& destination)
@@ -209,7 +213,6 @@ void FancyZonesData::CloneDeviceInfo(const std::wstring& source, const std::wstr
return;
}
// Clone information from source device if destination device is uninitialized (Blank).
deviceInfoMap[destination] = deviceInfoMap[source];
}

View File

@@ -58,7 +58,7 @@ public:
return appZoneHistoryMap;
}
void AddDevice(const std::wstring& deviceId);
bool AddDevice(const std::wstring& deviceId);
void CloneDeviceInfo(const std::wstring& source, const std::wstring& destination);
void UpdatePrimaryDesktopData(const std::wstring& desktopId);
void RemoveDeletedDesktops(const std::vector<std::wstring>& activeDesktops);

View File

@@ -86,14 +86,7 @@ namespace VirtualDesktopUtils
{
return true;
}
// First fallback scenario is to try obtaining virtual desktop id through IVirtualDesktopManager
// interface. Use foreground window (the window with which the user is currently working) to determine
// current virtual desktop.
else if (GetWindowDesktopId(GetForegroundWindow(), desktopId))
{
return true;
}
// Second fallback scenario is to get array of virtual desktops stored in registry, but not kept per
// First fallback scenario is to get array of virtual desktops stored in registry, but not kept per
// session. Note that we are taking first element from virtual desktop array, which is primary desktop.
// If user has more than one virtual desktop, one of previous functions should return correct value,
// as desktop switch occured in current session.

View File

@@ -335,10 +335,10 @@ void WindowMoveHandlerPrivate::MoveSizeEnd(HWND window, POINT const& ptScreen, c
auto zoneWindow = std::move(m_zoneWindowMoveSize);
ResetWindowTransparency();
bool hasNoVisibleOwnoer = FancyZonesUtils::HasNoVisibleOwner(window);
bool hasNoVisibleOwner = FancyZonesUtils::HasNoVisibleOwner(window);
bool isStandardWindow = FancyZonesUtils::IsStandardWindow(window);
if ((isStandardWindow == false && hasNoVisibleOwnoer == false &&
if ((isStandardWindow == false && hasNoVisibleOwner == true &&
m_moveSizeWindowInfo.standardWindow == true && m_moveSizeWindowInfo.noVisibleOwner == true) ||
FancyZonesUtils::IsWindowMaximized(window))
{

View File

@@ -443,9 +443,9 @@ ZoneWindow::ClearSelectedZones() noexcept
void ZoneWindow::InitializeZoneSets(const std::wstring& parentUniqueId) noexcept
{
// If there is not defined zone layout for this work area, created default entry.
FancyZonesDataInstance().AddDevice(m_uniqueId);
if (!parentUniqueId.empty())
bool deviceAdded = FancyZonesDataInstance().AddDevice(m_uniqueId);
// If the device has been added, check if it should inherit the parent's layout
if (deviceAdded && !parentUniqueId.empty())
{
FancyZonesDataInstance().CloneDeviceInfo(parentUniqueId, m_uniqueId);
}

View File

@@ -1,6 +1,7 @@
#include "pch.h"
#include "KeyDelay.h"
// NOTE: The destructor should never be called on the DelayThread, i.e. from any of shortPress, longPress or longPressReleased, as it will re-enter the mutex. Even if the mutex is removed it will deadlock because of the join statement
KeyDelay::~KeyDelay()
{
std::unique_lock<std::mutex> l(_queueMutex);

View File

@@ -613,25 +613,53 @@ bool Shortcut::CheckModifiersKeyboardState(InputInterface& ii) const
return true;
}
// Helper method for checking if a key is in a range for cleaner code
bool in_range(DWORD key, DWORD a, DWORD b)
{
return (key >= a && key <= b);
}
// Helper method for checking if a key is equal to a value for cleaner code
bool equals(DWORD key, DWORD a)
{
return (key == a);
}
// Function to check if the key code is to be ignored
bool IgnoreKeyCode(DWORD key)
{
// Ignore mouse buttons. Keeping this could cause a remapping to fail if a mouse button is also pressed at the same time
switch (key)
{
// Ignore mouse buttons. Keeping this could cause a remapping to fail if a mouse button is also pressed at the same time
case VK_LBUTTON:
case VK_RBUTTON:
case VK_MBUTTON:
case VK_XBUTTON1:
case VK_XBUTTON2:
// Ignore these key codes as they are reserved. Used by IME keyboards. More information at https://github.com/microsoft/PowerToys/issues/5225
case 0xF0:
case 0xF1:
case 0xF2:
return true;
}
return false;
// As per docs: https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
// Undefined keys
bool isUndefined = equals(key, 0x07) || in_range(key, 0x0E, 0x0F) || in_range(key, 0x3A, 0x40);
// Reserved keys
bool isReserved = in_range(key, 0x0A, 0x0B) || equals(key, 0x5E) || in_range(key, 0xB8, 0xB9) || in_range(key, 0xC1, 0xD7) || equals(key, 0xE0) || equals(key, VK_NONAME);
// Unassigned keys
bool isUnassigned = in_range(key, 0x88, 0x8F) || in_range(key, 0x97, 0x9F) || in_range(key, 0xD8, 0xDA) || equals(key, 0xE8);
//OEM Specific keys. Ignore these key codes as some of them are used by IME keyboards. More information at https://github.com/microsoft/PowerToys/issues/5225
bool isOEMSpecific = in_range(key, 0x92, 0x96) || equals(key, 0xE1) || in_range(key, 0xE3, 0xE4) || equals(key, 0xE6) || in_range(key, 0xE9, 0xF5);
if (isUndefined || isReserved || isUnassigned || isOEMSpecific)
{
return true;
}
else
{
return false;
}
}
// Function to check if any keys are pressed down except those in the shortcut

View File

@@ -355,6 +355,7 @@ void ShortcutControl::createDetectShortcutWindow(winrt::Windows::Foundation::IIn
onAccept();
});
// NOTE: UnregisterKeys should never be called on the DelayThread, as it will re-enter the mutex. To avoid this it is run on the dispatcher thread
keyboardManagerState.RegisterKeyDelay(
VK_RETURN,
selectDetectedShortcutAndResetKeys,
@@ -367,19 +368,24 @@ void ShortcutControl::createDetectShortcutWindow(winrt::Windows::Foundation::IIn
onPressEnter();
});
},
[onReleaseEnter](DWORD) {
onReleaseEnter();
[onReleaseEnter, detectShortcutBox](DWORD) {
detectShortcutBox.Dispatcher().RunAsync(
Windows::UI::Core::CoreDispatcherPriority::Normal,
[onReleaseEnter]() {
onReleaseEnter();
});
});
TextBlock cancelButtonText;
cancelButtonText.Text(GET_RESOURCE_STRING(IDS_CANCEL_BUTTON));
Button cancelButton;
cancelButton.HorizontalAlignment(HorizontalAlignment::Stretch);
cancelButton.Margin({ 2, 2, 2, 2 });
cancelButton.Content(cancelButtonText);
// Cancel button
cancelButton.Click([detectShortcutBox, unregisterKeys, &keyboardManagerState, isSingleKeyWindow, parentWindow](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
auto onCancel = [&keyboardManagerState,
detectShortcutBox,
unregisterKeys,
isSingleKeyWindow,
parentWindow] {
detectShortcutBox.Hide();
// Reset the keyboard manager UI state
keyboardManagerState.ResetUIState();
if (isSingleKeyWindow)
@@ -393,31 +399,27 @@ void ShortcutControl::createDetectShortcutWindow(winrt::Windows::Foundation::IIn
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditShortcutsWindowActivated, parentWindow);
}
unregisterKeys();
detectShortcutBox.Hide();
};
Button cancelButton;
cancelButton.HorizontalAlignment(HorizontalAlignment::Stretch);
cancelButton.Margin({ 2, 2, 2, 2 });
cancelButton.Content(cancelButtonText);
// Cancel button
cancelButton.Click([onCancel](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
onCancel();
});
// NOTE: UnregisterKeys should never be called on the DelayThread, as it will re-enter the mutex. To avoid this it is run on the dispatcher thread
keyboardManagerState.RegisterKeyDelay(
VK_ESCAPE,
selectDetectedShortcutAndResetKeys,
[&keyboardManagerState, detectShortcutBox, unregisterKeys, isSingleKeyWindow, parentWindow](DWORD) {
[onCancel, detectShortcutBox](DWORD) {
detectShortcutBox.Dispatcher().RunAsync(
Windows::UI::Core::CoreDispatcherPriority::Normal,
[detectShortcutBox] {
detectShortcutBox.Hide();
[onCancel] {
onCancel();
});
keyboardManagerState.ResetUIState();
if (isSingleKeyWindow)
{
// Revert UI state back to Edit Keyboard window
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, parentWindow);
}
else
{
// Revert UI state back to Edit Shortcut window
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditShortcutsWindowActivated, parentWindow);
}
unregisterKeys();
},
nullptr);

View File

@@ -269,6 +269,7 @@ void SingleKeyRemapControl::createDetectKeyWindow(winrt::Windows::Foundation::II
onAccept();
});
// NOTE: UnregisterKeys should never be called on the DelayThread, as it will re-enter the mutex. To avoid this it is run on the dispatcher thread
keyboardManagerState.RegisterKeyDelay(
VK_RETURN,
std::bind(&KeyboardManagerState::SelectDetectedRemapKey, &keyboardManagerState, std::placeholders::_1),
@@ -281,41 +282,48 @@ void SingleKeyRemapControl::createDetectKeyWindow(winrt::Windows::Foundation::II
onPressEnter();
});
},
[onReleaseEnter](DWORD) {
onReleaseEnter();
[onReleaseEnter, detectRemapKeyBox](DWORD) {
detectRemapKeyBox.Dispatcher().RunAsync(
Windows::UI::Core::CoreDispatcherPriority::Normal,
[onReleaseEnter]() {
onReleaseEnter();
});
});
TextBlock cancelButtonText;
cancelButtonText.Text(GET_RESOURCE_STRING(IDS_CANCEL_BUTTON));
auto onCancel = [&keyboardManagerState,
detectRemapKeyBox,
unregisterKeys] {
detectRemapKeyBox.Hide();
// Reset the keyboard manager UI state
keyboardManagerState.ResetUIState();
// Revert UI state back to Edit Keyboard window
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, EditKeyboardWindowHandle);
unregisterKeys();
};
Button cancelButton;
cancelButton.HorizontalAlignment(HorizontalAlignment::Stretch);
cancelButton.Margin({ 2, 2, 2, 2 });
cancelButton.Content(cancelButtonText);
// Cancel button
cancelButton.Click([detectRemapKeyBox, unregisterKeys, &keyboardManagerState](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
// Reset the keyboard manager UI state
keyboardManagerState.ResetUIState();
// Revert UI state back to Edit Keyboard window
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, EditKeyboardWindowHandle);
unregisterKeys();
detectRemapKeyBox.Hide();
cancelButton.Click([onCancel](winrt::Windows::Foundation::IInspectable const& sender, RoutedEventArgs const&) {
onCancel();
});
// NOTE: UnregisterKeys should never be called on the DelayThread, as it will re-enter the mutex. To avoid this it is run on the dispatcher thread
keyboardManagerState.RegisterKeyDelay(
VK_ESCAPE,
std::bind(&KeyboardManagerState::SelectDetectedRemapKey, &keyboardManagerState, std::placeholders::_1),
[&keyboardManagerState, detectRemapKeyBox, unregisterKeys](DWORD) {
[onCancel, detectRemapKeyBox](DWORD) {
detectRemapKeyBox.Dispatcher().RunAsync(
Windows::UI::Core::CoreDispatcherPriority::Normal,
[detectRemapKeyBox] {
detectRemapKeyBox.Hide();
[onCancel] {
onCancel();
});
keyboardManagerState.ResetUIState();
// Revert UI state back to Edit Keyboard window
keyboardManagerState.SetUIState(KeyboardManagerUIState::EditKeyboardWindowActivated, EditKeyboardWindowHandle);
unregisterKeys();
},
nullptr);

View File

@@ -330,6 +330,7 @@ namespace Microsoft.Plugin.Program.Programs
return ExecutableName;
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Any error in CreateWin32Program should not prevent other programs from loading.")]
private static Win32Program CreateWin32Program(string path)
{
try
@@ -353,78 +354,94 @@ namespace Microsoft.Plugin.Program.Programs
{
ProgramLogger.Exception($"|Permission denied when trying to load the program from {path}", e, MethodBase.GetCurrentMethod().DeclaringType, path);
return new Win32Program() { Valid = false, Enabled = false };
}
catch (Exception e)
{
ProgramLogger.Exception($"|An unexpected error occurred in the calling method CreateWin32Program at {path}", e, MethodBase.GetCurrentMethod().DeclaringType, path);
return new Win32Program() { Valid = false, Enabled = false };
}
}
// This function filters Internet Shortcut programs
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Any error in InternetShortcutProgram should not prevent other programs from loading.")]
private static Win32Program InternetShortcutProgram(string path)
{
string[] lines = FileWrapper.ReadAllLines(path);
string iconPath = string.Empty;
string urlPath = string.Empty;
bool validApp = false;
Regex internetShortcutURLPrefixes = new Regex(@"^steam:\/\/(rungameid|run)\/|^com\.epicgames\.launcher:\/\/apps\/");
const string urlPrefix = "URL=";
const string iconFilePrefix = "IconFile=";
foreach (string line in lines)
{
if (line.StartsWith(urlPrefix, StringComparison.OrdinalIgnoreCase))
{
urlPath = line.Substring(urlPrefix.Length);
try
{
Uri uri = new Uri(urlPath);
}
catch (UriFormatException e)
{
// To catch the exception if the uri cannot be parsed.
// Link to watson crash: https://watsonportal.microsoft.com/Failure?FailureSearchText=5f871ea7-e886-911f-1b31-131f63f6655b
ProgramLogger.Exception($"url could not be parsed", e, MethodBase.GetCurrentMethod().DeclaringType, urlPath);
return new Win32Program() { Valid = false, Enabled = false };
}
// To filter out only those steam shortcuts which have 'run' or 'rungameid' as the hostname
if (internetShortcutURLPrefixes.Match(urlPath).Success)
{
validApp = true;
}
}
if (line.StartsWith(iconFilePrefix, StringComparison.OrdinalIgnoreCase))
{
iconPath = line.Substring(iconFilePrefix.Length);
}
}
if (!validApp)
{
return new Win32Program() { Valid = false, Enabled = false };
}
try
{
var p = new Win32Program
string[] lines = FileWrapper.ReadAllLines(path);
string iconPath = string.Empty;
string urlPath = string.Empty;
bool validApp = false;
Regex internetShortcutURLPrefixes = new Regex(@"^steam:\/\/(rungameid|run)\/|^com\.epicgames\.launcher:\/\/apps\/");
const string urlPrefix = "URL=";
const string iconFilePrefix = "IconFile=";
foreach (string line in lines)
{
Name = Path.GetFileNameWithoutExtension(path),
ExecutableName = Path.GetFileName(path),
IcoPath = iconPath,
FullPath = urlPath,
UniqueIdentifier = path,
ParentDirectory = Directory.GetParent(path).FullName,
Valid = true,
Enabled = true,
AppType = ApplicationType.InternetShortcutApplication,
};
return p;
if (line.StartsWith(urlPrefix, StringComparison.OrdinalIgnoreCase))
{
urlPath = line.Substring(urlPrefix.Length);
try
{
Uri uri = new Uri(urlPath);
}
catch (UriFormatException e)
{
// To catch the exception if the uri cannot be parsed.
// Link to watson crash: https://watsonportal.microsoft.com/Failure?FailureSearchText=5f871ea7-e886-911f-1b31-131f63f6655b
ProgramLogger.Exception($"url could not be parsed", e, MethodBase.GetCurrentMethod().DeclaringType, urlPath);
return new Win32Program() { Valid = false, Enabled = false };
}
// To filter out only those steam shortcuts which have 'run' or 'rungameid' as the hostname
if (internetShortcutURLPrefixes.Match(urlPath).Success)
{
validApp = true;
}
}
if (line.StartsWith(iconFilePrefix, StringComparison.OrdinalIgnoreCase))
{
iconPath = line.Substring(iconFilePrefix.Length);
}
}
if (!validApp)
{
return new Win32Program() { Valid = false, Enabled = false };
}
try
{
var p = new Win32Program
{
Name = Path.GetFileNameWithoutExtension(path),
ExecutableName = Path.GetFileName(path),
IcoPath = iconPath,
FullPath = urlPath,
UniqueIdentifier = path,
ParentDirectory = Directory.GetParent(path).FullName,
Valid = true,
Enabled = true,
AppType = ApplicationType.InternetShortcutApplication,
};
return p;
}
catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException)
{
ProgramLogger.Exception($"|Permission denied when trying to load the program from {path}", e, MethodBase.GetCurrentMethod().DeclaringType, path);
return new Win32Program() { Valid = false, Enabled = false };
}
}
catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException)
catch (Exception e)
{
ProgramLogger.Exception($"|Permission denied when trying to load the program from {path}", e, MethodBase.GetCurrentMethod().DeclaringType, path);
ProgramLogger.Exception($"|An unexpected error occurred in the calling method InternetShortcutProgram at {path}", e, MethodBase.GetCurrentMethod().DeclaringType, path);
return new Win32Program() { Valid = false, Enabled = false };
}
@@ -433,9 +450,9 @@ namespace Microsoft.Plugin.Program.Programs
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Unsure of what exceptions are caught here while enabling static analysis")]
private static Win32Program LnkProgram(string path)
{
var program = CreateWin32Program(path);
try
{
var program = CreateWin32Program(path);
const int MAX_PATH = 260;
StringBuilder buffer = new StringBuilder(MAX_PATH);
@@ -472,13 +489,13 @@ namespace Microsoft.Plugin.Program.Programs
// Error caused likely due to trying to get the description of the program
catch (Exception e)
{
ProgramLogger.Exception("An unexpected error occurred in the calling method LnkProgram", e, MethodBase.GetCurrentMethod().DeclaringType, path);
ProgramLogger.Exception($"|An unexpected error occurred in the calling method LnkProgram at {path}", e, MethodBase.GetCurrentMethod().DeclaringType, path);
program.Valid = false;
return program;
return new Win32Program() { Valid = false, Enabled = false };
}
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Any error in ExeProgram should not prevent other programs from loading.")]
private static Win32Program ExeProgram(string path)
{
try
@@ -503,6 +520,12 @@ namespace Microsoft.Plugin.Program.Programs
{
ProgramLogger.Exception($"|Unable to locate exe file at {path}", e, MethodBase.GetCurrentMethod().DeclaringType, path);
return new Win32Program() { Valid = false, Enabled = false };
}
catch (Exception e)
{
ProgramLogger.Exception($"|An unexpected error occurred in the calling method ExeProgram at {path}", e, MethodBase.GetCurrentMethod().DeclaringType, path);
return new Win32Program() { Valid = false, Enabled = false };
}
}
@@ -585,6 +608,7 @@ namespace Microsoft.Plugin.Program.Programs
}
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "Minimise the effect of error on other programs")]
private static IEnumerable<string> ProgramPaths(string directory, IList<string> suffixes, bool recursiveSearch = true)
{
if (!Directory.Exists(directory))
@@ -617,6 +641,10 @@ namespace Microsoft.Plugin.Program.Programs
{
ProgramLogger.Exception($"|Permission denied when trying to load programs from {currentDirectory}", e, MethodBase.GetCurrentMethod().DeclaringType, currentDirectory);
}
catch (Exception e)
{
ProgramLogger.Exception($"|An unexpected error occurred in the calling method ProgramPaths at {currentDirectory}", e, MethodBase.GetCurrentMethod().DeclaringType, currentDirectory);
}
try
{
@@ -635,6 +663,10 @@ namespace Microsoft.Plugin.Program.Programs
{
ProgramLogger.Exception($"|Permission denied when trying to load programs from {currentDirectory}", e, MethodBase.GetCurrentMethod().DeclaringType, currentDirectory);
}
catch (Exception e)
{
ProgramLogger.Exception($"|An unexpected error occurred in the calling method ProgramPaths at {currentDirectory}", e, MethodBase.GetCurrentMethod().DeclaringType, currentDirectory);
}
}
while (folderQueue.Any());

View File

@@ -194,14 +194,15 @@ namespace Wox.Core.Plugin
{
foreach (Result result in results)
{
if (!string.IsNullOrEmpty(result.QueryTextDisplay))
if (string.IsNullOrEmpty(result.QueryTextDisplay))
{
result.QueryTextDisplay = result.Title;
}
if (!string.IsNullOrEmpty(query.ActionKeyword))
{
result.QueryTextDisplay = string.Format("{0} {1}", query.ActionKeyword, result.QueryTextDisplay);
}
else
{
result.QueryTextDisplay = string.Format("{0} {1}", query.ActionKeyword, result.Title);
}
}
return results;

View File

@@ -15,13 +15,18 @@ namespace Wox.Test
[TestFixture]
public class PluginManagerTest
{
[Test]
public void QueryForPlugin_SetsActionKeyword_WhenQueryTextDisplayIsSet()
[TestCase(">", "dummyQueryText", "dummyTitle", "> dummyQueryText")]
[TestCase(">", null, "dummyTitle", "> dummyTitle")]
[TestCase(">", "", "dummyTitle", "> dummyTitle")]
[TestCase("", "dummyQueryText", "dummyTitle", "dummyQueryText")]
[TestCase("", null, "dummyTitle", "dummyTitle")]
[TestCase("", "", "dummyTitle", "dummyTitle")]
[TestCase(null, "dummyQueryText", "dummyTitle", "dummyQueryText")]
[TestCase(null, null, "dummyTitle", "dummyTitle")]
[TestCase(null, "", "dummyTitle", "dummyTitle")]
public void QueryForPlugin_SetsActionKeyword_WhenQueryTextDisplayIsEmpty(string actionKeyword, string queryTextDisplay, string title, string expectedResult)
{
// Arrange
var actionKeyword = ">";
var title = "dummyTitle";
var queryTextDisplay = "dummyQueryTextDisplay";
var query = new Query
{
ActionKeyword = actionKeyword,
@@ -51,46 +56,7 @@ namespace Wox.Test
var queryOutput = PluginManager.QueryForPlugin(pluginPair, query);
// Assert
Assert.AreEqual(string.Format("{0} {1}", ">", queryTextDisplay), queryOutput[0].QueryTextDisplay);
}
[TestCase("")]
[TestCase(null)]
public void QueryForPlugin_SetsActionKeyword_WhenQueryTextDisplayIsEmpty(string queryTextDisplay)
{
// Arrange
var actionKeyword = ">";
var title = "dummyTitle";
var query = new Query
{
ActionKeyword = actionKeyword,
};
var metadata = new PluginMetadata
{
ID = "dummyName",
IcoPath = "dummyIcoPath",
ExecuteFileName = "dummyExecuteFileName",
PluginDirectory = "dummyPluginDirectory",
};
var result = new Result()
{
QueryTextDisplay = queryTextDisplay,
Title = title,
};
var results = new List<Result>() { result };
var pluginMock = new Mock<IPlugin>();
pluginMock.Setup(r => r.Query(query)).Returns(results);
var pluginPair = new PluginPair
{
Plugin = pluginMock.Object,
Metadata = metadata,
};
// Act
var queryOutput = PluginManager.QueryForPlugin(pluginPair, query);
// Assert
Assert.AreEqual(string.Format("{0} {1}", ">", title), queryOutput[0].QueryTextDisplay);
Assert.AreEqual(expectedResult, queryOutput[0].QueryTextDisplay);
}
}
}