[Settings] KBM Migrated KBM Settings Tests (#5821)

* added MSTest project

* enabled settings tests run in the build pipeline

* migrated KBM settings

* fixed typo

* moved the callback function initialization to the top

* fixed build
This commit is contained in:
Nkateko
2020-08-17 15:00:19 -07:00
committed by GitHub
parent 2ff439dc47
commit c6c9839208
5 changed files with 222 additions and 63 deletions

View File

@@ -9,15 +9,11 @@ using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Input; using System.Windows.Input;
using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Lib.Helpers;
using Microsoft.PowerToys.Settings.UI.Lib;
using Microsoft.PowerToys.Settings.UI.Lib.Utilities; using Microsoft.PowerToys.Settings.UI.Lib.Utilities;
using Microsoft.PowerToys.Settings.UI.Views; using Microsoft.PowerToys.Settings.UI.Lib.ViewModels.Commands;
using Windows.System;
using Windows.UI.Core;
using Windows.UI.Xaml;
namespace Microsoft.PowerToys.Settings.UI.ViewModels namespace Microsoft.PowerToys.Settings.UI.Lib.ViewModels
{ {
public class KeyboardManagerViewModel : Observable public class KeyboardManagerViewModel : Observable
{ {
@@ -30,18 +26,24 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
private const string ProfileFileMutexName = "PowerToys.KeyboardManager.ConfigMutex"; private const string ProfileFileMutexName = "PowerToys.KeyboardManager.ConfigMutex";
private const int ProfileFileMutexWaitTimeoutMilliseconds = 1000; private const int ProfileFileMutexWaitTimeoutMilliseconds = 1000;
private readonly CoreDispatcher dispatcher;
private readonly FileSystemWatcher watcher; private readonly FileSystemWatcher watcher;
private ICommand remapKeyboardCommand; private ICommand remapKeyboardCommand;
private ICommand editShortcutCommand; private ICommand editShortcutCommand;
private KeyboardManagerSettings settings; public KeyboardManagerSettings settings;
private KeyboardManagerProfile profile; private KeyboardManagerProfile profile;
private GeneralSettings generalSettings; private GeneralSettings generalSettings;
public KeyboardManagerViewModel() private Func<string, int> SendConfigMSG { get; }
private Func<List<KeysDataModel>, int> FilterRemapKeysList { get; }
public KeyboardManagerViewModel(Func<string, int> ipcMSGCallBackFunc, Func<List<KeysDataModel>, int> filterRemapKeysList)
{ {
dispatcher = Window.Current.Dispatcher; // set the callback functions value to hangle outgoing IPC message.
SendConfigMSG = ipcMSGCallBackFunc;
FilterRemapKeysList = filterRemapKeysList;
if (SettingsUtils.SettingsExists(PowerToyName)) if (SettingsUtils.SettingsExists(PowerToyName))
{ {
// Todo: Be more resilient while reading and saving settings. // Todo: Be more resilient while reading and saving settings.
@@ -68,11 +70,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
generalSettings = new GeneralSettings(); generalSettings = new GeneralSettings();
SettingsUtils.SaveSettings(generalSettings.ToJsonString(), string.Empty); SettingsUtils.SaveSettings(generalSettings.ToJsonString(), string.Empty);
} }
watcher = Helper.GetFileWatcher(
PowerToyName,
settings.Properties.ActiveConfiguration.Value + JsonFileType,
OnConfigFileUpdate);
} }
public bool Enabled public bool Enabled
@@ -90,7 +87,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
OnPropertyChanged(nameof(Enabled)); OnPropertyChanged(nameof(Enabled));
OutGoingGeneralSettings outgoing = new OutGoingGeneralSettings(generalSettings); OutGoingGeneralSettings outgoing = new OutGoingGeneralSettings(generalSettings);
ShellPage.DefaultSndMSGCallback(outgoing.ToString()); SendConfigMSG(outgoing.ToString());
} }
} }
} }
@@ -148,32 +145,24 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
private async Task OnRemapKeyboardBackground() private async Task OnRemapKeyboardBackground()
{ {
Helper.AllowRunnerToForeground(); Helper.AllowRunnerToForeground();
ShellPage.DefaultSndMSGCallback(Helper.GetSerializedCustomAction(PowerToyName, RemapKeyboardActionName, RemapKeyboardActionValue)); SendConfigMSG(Helper.GetSerializedCustomAction(PowerToyName, RemapKeyboardActionName, RemapKeyboardActionValue));
await Task.CompletedTask; await Task.CompletedTask;
} }
private async Task OnEditShortcutBackground() private async Task OnEditShortcutBackground()
{ {
Helper.AllowRunnerToForeground(); Helper.AllowRunnerToForeground();
ShellPage.DefaultSndMSGCallback(Helper.GetSerializedCustomAction(PowerToyName, EditShortcutActionName, EditShortcutActionValue)); SendConfigMSG(Helper.GetSerializedCustomAction(PowerToyName, EditShortcutActionName, EditShortcutActionValue));
await Task.CompletedTask; await Task.CompletedTask;
} }
private async void OnConfigFileUpdate() public void NotifyFileChanged()
{ {
// Note: FileSystemWatcher raise notification multiple times for single update operation. OnPropertyChanged(nameof(RemapKeys));
// Todo: Handle duplicate events either by somehow suppress them or re-read the configuration everytime since we will be updating the UI only if something is changed. OnPropertyChanged(nameof(RemapShortcuts));
if (LoadProfile())
{
await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
OnPropertyChanged(nameof(RemapKeys));
OnPropertyChanged(nameof(RemapShortcuts));
});
}
} }
private bool LoadProfile() public bool LoadProfile()
{ {
var success = true; var success = true;
@@ -210,32 +199,5 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
return success; return success;
} }
private void FilterRemapKeysList(List<KeysDataModel> remapKeysList)
{
CombineRemappings(remapKeysList, (uint)VirtualKey.LeftControl, (uint)VirtualKey.RightControl, (uint)VirtualKey.Control);
CombineRemappings(remapKeysList, (uint)VirtualKey.LeftMenu, (uint)VirtualKey.RightMenu, (uint)VirtualKey.Menu);
CombineRemappings(remapKeysList, (uint)VirtualKey.LeftShift, (uint)VirtualKey.RightShift, (uint)VirtualKey.Shift);
CombineRemappings(remapKeysList, (uint)VirtualKey.LeftWindows, (uint)VirtualKey.RightWindows, Helper.VirtualKeyWindows);
}
private void CombineRemappings(List<KeysDataModel> remapKeysList, uint leftKey, uint rightKey, uint combinedKey)
{
KeysDataModel firstRemap = remapKeysList.Find(x => uint.Parse(x.OriginalKeys) == leftKey);
KeysDataModel secondRemap = remapKeysList.Find(x => uint.Parse(x.OriginalKeys) == rightKey);
if (firstRemap != null && secondRemap != null)
{
if (firstRemap.NewRemapKeys == secondRemap.NewRemapKeys)
{
KeysDataModel combinedRemap = new KeysDataModel
{
OriginalKeys = combinedKey.ToString(),
NewRemapKeys = firstRemap.NewRemapKeys,
};
remapKeysList.Insert(remapKeysList.IndexOf(firstRemap), combinedRemap);
remapKeysList.Remove(firstRemap);
remapKeysList.Remove(secondRemap);
}
}
}
} }
} }

View File

@@ -0,0 +1,134 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.Linq;
using Microsoft.PowerToys.Settings.UI.Lib;
using Microsoft.PowerToys.Settings.UI.Lib.ViewModels;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace ViewModelTests
{
[TestClass]
public class KeyboardManager
{
public const string Module = "Keyboard Manager";
[TestInitialize]
public void Setup()
{
}
[TestCleanup]
public void CleanUp()
{
}
[TestMethod]
public void CombineShortcutLists_ShouldReturnEmptyList_WhenBothArgumentsAreEmptyLists()
{
// arrange
var firstList = new List<KeysDataModel>();
var secondList = new List<AppSpecificKeysDataModel>();
// act
var result = KeyboardManagerViewModel.CombineShortcutLists(firstList, secondList);
// Assert
var expectedResult = new List<AppSpecificKeysDataModel>();
Assert.AreEqual(expectedResult.Count(), result.Count());
}
[TestMethod]
public void CombineShortcutLists_ShouldReturnListWithOneAllAppsEntry_WhenFirstArgumentHasOneEntryAndSecondArgumentIsEmpty()
{
// arrange
var firstList = new List<KeysDataModel>();
var entry = new KeysDataModel();
entry.OriginalKeys = "17;65";
entry.NewRemapKeys = "17;86";
firstList.Add(entry);
var secondList = new List<AppSpecificKeysDataModel>();
// act
var result = KeyboardManagerViewModel.CombineShortcutLists(firstList, secondList);
// Assert
var expectedResult = new List<AppSpecificKeysDataModel>();
var expectedEntry = new AppSpecificKeysDataModel();
expectedEntry.OriginalKeys = entry.OriginalKeys;
expectedEntry.NewRemapKeys = entry.NewRemapKeys;
expectedEntry.TargetApp = "All Apps";
expectedResult.Add(expectedEntry);
var x = expectedResult[0].Equals(result[0]);
Assert.AreEqual(expectedResult.Count(), result.Count());
Assert.IsTrue(expectedResult[0].Compare(result[0]));
}
[TestMethod]
public void CombineShortcutLists_ShouldReturnListWithOneAppSpecificEntry_WhenFirstArgumentIsEmptyAndSecondArgumentHasOneEntry()
{
// arrange
var firstList = new List<KeysDataModel>();
var secondList = new List<AppSpecificKeysDataModel>();
var entry = new AppSpecificKeysDataModel();
entry.OriginalKeys = "17;65";
entry.NewRemapKeys = "17;86";
entry.TargetApp = "msedge";
secondList.Add(entry);
// act
var result = KeyboardManagerViewModel.CombineShortcutLists(firstList, secondList);
// Assert
var expectedResult = new List<AppSpecificKeysDataModel>();
var expectedEntry = new AppSpecificKeysDataModel();
expectedEntry.OriginalKeys = entry.OriginalKeys;
expectedEntry.NewRemapKeys = entry.NewRemapKeys;
expectedEntry.TargetApp = entry.TargetApp;
expectedResult.Add(expectedEntry);
Assert.AreEqual(expectedResult.Count(), result.Count());
Assert.IsTrue(expectedResult[0].Compare(result[0]));
}
[TestMethod]
public void CombineShortcutLists_ShouldReturnListWithOneAllAppsEntryAndOneAppSpecificEntry_WhenFirstArgumentHasOneEntryAndSecondArgumentHasOneEntry()
{
// arrange
var firstList = new List<KeysDataModel>();
var firstListEntry = new KeysDataModel();
firstListEntry.OriginalKeys = "17;65";
firstListEntry.NewRemapKeys = "17;86";
firstList.Add(firstListEntry);
var secondList = new List<AppSpecificKeysDataModel>();
var secondListEntry = new AppSpecificKeysDataModel();
secondListEntry.OriginalKeys = "17;66";
secondListEntry.NewRemapKeys = "17;87";
secondListEntry.TargetApp = "msedge";
secondList.Add(secondListEntry);
// act
var result = KeyboardManagerViewModel.CombineShortcutLists(firstList, secondList);
// Assert
var expectedResult = new List<AppSpecificKeysDataModel>();
var expectedFirstEntry = new AppSpecificKeysDataModel();
expectedFirstEntry.OriginalKeys = firstListEntry.OriginalKeys;
expectedFirstEntry.NewRemapKeys = firstListEntry.NewRemapKeys;
expectedFirstEntry.TargetApp = "All Apps";
expectedResult.Add(expectedFirstEntry);
var expectedSecondEntry = new AppSpecificKeysDataModel();
expectedSecondEntry.OriginalKeys = secondListEntry.OriginalKeys;
expectedSecondEntry.NewRemapKeys = secondListEntry.NewRemapKeys;
expectedSecondEntry.TargetApp = secondListEntry.TargetApp;
expectedResult.Add(expectedSecondEntry);
Assert.AreEqual(expectedResult.Count(), result.Count());
Assert.IsTrue(expectedResult[0].Compare(result[0]));
Assert.IsTrue(expectedResult[1].Compare(result[1]));
}
}
}

View File

@@ -111,7 +111,6 @@
<Compile Include="ViewModels\Commands\ButtonClickCommand.cs" /> <Compile Include="ViewModels\Commands\ButtonClickCommand.cs" />
<Compile Include="ViewModels\FancyZonesViewModel.cs" /> <Compile Include="ViewModels\FancyZonesViewModel.cs" />
<Compile Include="ViewModels\ImageResizerViewModel.cs" /> <Compile Include="ViewModels\ImageResizerViewModel.cs" />
<Compile Include="ViewModels\KeyboardManagerViewModel.cs" />
<Compile Include="ViewModels\PowerLauncherViewModel.cs" /> <Compile Include="ViewModels\PowerLauncherViewModel.cs" />
<Compile Include="ViewModels\ShellViewModel.cs" /> <Compile Include="ViewModels\ShellViewModel.cs" />
<Compile Include="Views\ColorPickerPage.xaml.cs"> <Compile Include="Views\ColorPickerPage.xaml.cs">

View File

@@ -5,7 +5,7 @@
xmlns:local="using:Microsoft.PowerToys.Settings.UI.Views" xmlns:local="using:Microsoft.PowerToys.Settings.UI.Views"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModel="using:Microsoft.PowerToys.Settings.UI.ViewModels" xmlns:viewModel="using:Microsoft.PowerToys.Settings.UI.Lib.ViewModels"
xmlns:extensions="using:Microsoft.Toolkit.Uwp.UI.Extensions" xmlns:extensions="using:Microsoft.Toolkit.Uwp.UI.Extensions"
xmlns:CustomControls="using:Microsoft.PowerToys.Settings.UI.Controls" xmlns:CustomControls="using:Microsoft.PowerToys.Settings.UI.Controls"
xmlns:Lib="using:Microsoft.PowerToys.Settings.UI.Lib" xmlns:Lib="using:Microsoft.PowerToys.Settings.UI.Lib"
@@ -13,7 +13,6 @@
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Page.Resources> <Page.Resources>
<viewModel:KeyboardManagerViewModel x:Key="eventViewModel"/>
<local:VisibleIfNotEmpty x:Key="visibleIfNotEmptyConverter" /> <local:VisibleIfNotEmpty x:Key="visibleIfNotEmptyConverter" />
<DataTemplate x:Name="KeysListViewTemplate" x:DataType="Lib:KeysDataModel"> <DataTemplate x:Name="KeysListViewTemplate" x:DataType="Lib:KeysDataModel">
<StackPanel <StackPanel

View File

@@ -2,7 +2,15 @@
// The Microsoft Corporation licenses this file to you under the MIT license. // The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information. // See the LICENSE file in the project root for more information.
using Microsoft.PowerToys.Settings.UI.ViewModels; using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.PowerToys.Settings.UI.Lib;
using Microsoft.PowerToys.Settings.UI.Lib.Utilities;
using Microsoft.PowerToys.Settings.UI.Lib.ViewModels;
using Windows.System;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
namespace Microsoft.PowerToys.Settings.UI.Views namespace Microsoft.PowerToys.Settings.UI.Views
@@ -12,12 +20,69 @@ namespace Microsoft.PowerToys.Settings.UI.Views
/// </summary> /// </summary>
public sealed partial class KeyboardManagerPage : Page public sealed partial class KeyboardManagerPage : Page
{ {
public KeyboardManagerViewModel ViewModel { get; } = new KeyboardManagerViewModel(); private const string PowerToyName = "Keyboard Manager";
private readonly CoreDispatcher dispatcher;
private readonly FileSystemWatcher watcher;
public KeyboardManagerViewModel ViewModel { get; }
public KeyboardManagerPage() public KeyboardManagerPage()
{ {
dispatcher = Window.Current.Dispatcher;
ViewModel = new KeyboardManagerViewModel(ShellPage.SendDefaultIPCMessage, FilterRemapKeysList);
watcher = Helper.GetFileWatcher(
PowerToyName,
ViewModel.settings.Properties.ActiveConfiguration.Value + ".json",
OnConfigFileUpdate);
InitializeComponent(); InitializeComponent();
DataContext = ViewModel; DataContext = ViewModel;
} }
private async void OnConfigFileUpdate()
{
// Note: FileSystemWatcher raise notification multiple times for single update operation.
// Todo: Handle duplicate events either by somehow suppress them or re-read the configuration everytime since we will be updating the UI only if something is changed.
if (ViewModel.LoadProfile())
{
await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
ViewModel.NotifyFileChanged();
});
}
}
private void CombineRemappings(List<KeysDataModel> remapKeysList, uint leftKey, uint rightKey, uint combinedKey)
{
KeysDataModel firstRemap = remapKeysList.Find(x => uint.Parse(x.OriginalKeys) == leftKey);
KeysDataModel secondRemap = remapKeysList.Find(x => uint.Parse(x.OriginalKeys) == rightKey);
if (firstRemap != null && secondRemap != null)
{
if (firstRemap.NewRemapKeys == secondRemap.NewRemapKeys)
{
KeysDataModel combinedRemap = new KeysDataModel
{
OriginalKeys = combinedKey.ToString(),
NewRemapKeys = firstRemap.NewRemapKeys,
};
remapKeysList.Insert(remapKeysList.IndexOf(firstRemap), combinedRemap);
remapKeysList.Remove(firstRemap);
remapKeysList.Remove(secondRemap);
}
}
}
private int FilterRemapKeysList(List<KeysDataModel> remapKeysList)
{
CombineRemappings(remapKeysList, (uint)VirtualKey.LeftControl, (uint)VirtualKey.RightControl, (uint)VirtualKey.Control);
CombineRemappings(remapKeysList, (uint)VirtualKey.LeftMenu, (uint)VirtualKey.RightMenu, (uint)VirtualKey.Menu);
CombineRemappings(remapKeysList, (uint)VirtualKey.LeftShift, (uint)VirtualKey.RightShift, (uint)VirtualKey.Shift);
CombineRemappings(remapKeysList, (uint)VirtualKey.LeftWindows, (uint)VirtualKey.RightWindows, Helper.VirtualKeyWindows);
return 0;
}
} }
} }