CmdPal: A couple more run commands bugs (#42174)

Caching bugs are hard.

This fixes like, three different run commands bugs:
* typing `c:\windows\p`, then backspacing to `c:\windows` would populate
the cache for `c:\` with the files in `c:\` that matched `windows*`.
* Now when the dir chenges, we correctly fill the cache with everything
in that dir, then filter it.
* that also caused a similar edge case for `c:\windows\` -> `c:\windows`
(the first should show results under c:\windows\` the second should only
show things in `c:\` matching `windows`
* As of my last PR, we support commandlines with spaces. We however
forgot to handle _paths_ with spaces. We'll now correctly show path
results for something like `c:\program files\`
This commit is contained in:
Mike Griese
2025-10-06 12:33:38 -05:00
committed by GitHub
parent 233ca4c05b
commit ccc31c13ae
6 changed files with 199 additions and 79 deletions

View File

@@ -3,11 +3,13 @@
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.CmdPal.Core.Common.Services;
using Microsoft.CmdPal.Ext.Shell.Pages;
using Microsoft.CmdPal.Ext.UnitTestBase;
using Microsoft.CommandPalette.Extensions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
@@ -153,4 +155,131 @@ public class QueryTests : CommandPaletteUnitTestBase
// Should find at least the ping command from history
Assert.IsTrue(commandList.Length > 1);
}
[TestMethod]
public async Task TestCacheBackToSameDirectory()
{
// Setup
var settings = Settings.CreateDefaultSettings();
var mockHistoryService = CreateMockHistoryService();
var page = new ShellListPage(settings, mockHistoryService.Object, telemetryService: null);
// Load up everything in c:\, for the sake of comparing:
var filesInC = Directory.EnumerateFileSystemEntries("C:\\");
await UpdatePageAndWaitForItems(page, () => { page.SearchText = "c:\\"; });
var commandList = page.GetItems();
// Should find only items for what's in c:\
Assert.IsTrue(commandList.Length == filesInC.Count());
await UpdatePageAndWaitForItems(page, () => { page.SearchText = "c:\\Win"; });
await UpdatePageAndWaitForItems(page, () => { page.SearchText = "c:\\Windows"; });
await UpdatePageAndWaitForItems(page, () => { page.SearchText = "c:\\"; });
commandList = page.GetItems();
// Should still find everything
Assert.IsTrue(commandList.Length == filesInC.Count());
await TypeStringIntoPage(page, "c:\\Windows\\Pro");
await BackspaceSearchText(page, "c:\\Windows\\Pro", 3); // 3 characters for c:\
commandList = page.GetItems();
// Should still find everything
Assert.IsTrue(commandList.Length == filesInC.Count());
}
private async Task TypeStringIntoPage(IDynamicListPage page, string searchText)
{
// type the string one character at a time
for (var i = 0; i < searchText.Length; i++)
{
var substr = searchText[..i];
await UpdatePageAndWaitForItems(page, () => { page.SearchText = substr; });
}
}
private async Task BackspaceSearchText(IDynamicListPage page, string originalSearchText, int finalStringLength)
{
var originalLength = originalSearchText.Length;
for (var i = originalLength; i >= finalStringLength; i--)
{
var substr = originalSearchText[..i];
await UpdatePageAndWaitForItems(page, () => { page.SearchText = substr; });
}
}
[TestMethod]
public async Task TestCacheSameDirectorySlashy()
{
// Setup
var settings = Settings.CreateDefaultSettings();
var mockHistoryService = CreateMockHistoryService();
var page = new ShellListPage(settings, mockHistoryService.Object, telemetryService: null);
// Load up everything in c:\, for the sake of comparing:
var filesInC = Directory.EnumerateFileSystemEntries("C:\\");
var filesInWindows = Directory.EnumerateFileSystemEntries("C:\\Windows");
await UpdatePageAndWaitForItems(page, () => { page.SearchText = "c:\\"; });
var commandList = page.GetItems();
Assert.IsTrue(commandList.Length == filesInC.Count());
// First navigate to c:\Windows. This should match everything that matches "windows" inside of C:\
await UpdatePageAndWaitForItems(page, () => { page.SearchText = "c:\\Windows"; });
var cWindowsCommandsPre = page.GetItems();
// Then go into c:\windows\. This will only have the results in c:\windows\
await UpdatePageAndWaitForItems(page, () => { page.SearchText = "c:\\Windows\\"; });
var windowsCommands = page.GetItems();
Assert.IsTrue(windowsCommands.Length != cWindowsCommandsPre.Length);
// now go back to c:\windows. This should match the results from the last time we entered this string
await UpdatePageAndWaitForItems(page, () => { page.SearchText = "c:\\Windows"; });
var cWindowsCommandsPost = page.GetItems();
Assert.IsTrue(cWindowsCommandsPre.Length == cWindowsCommandsPost.Length);
}
[TestMethod]
public async Task TestPathWithSpaces()
{
// Setup
var settings = Settings.CreateDefaultSettings();
var mockHistoryService = CreateMockHistoryService();
var page = new ShellListPage(settings, mockHistoryService.Object, telemetryService: null);
// Load up everything in c:\, for the sake of comparing:
var filesInC = Directory.EnumerateFileSystemEntries("C:\\");
var filesInProgramFiles = Directory.EnumerateFileSystemEntries("C:\\Program Files");
await UpdatePageAndWaitForItems(page, () => { page.SearchText = "c:\\Program Files\\"; });
var commandList = page.GetItems();
Assert.IsTrue(commandList.Length == filesInProgramFiles.Count());
}
[TestMethod]
public async Task TestNoWrapSuggestionsWithSpaces()
{
// Setup
var settings = Settings.CreateDefaultSettings();
var mockHistoryService = CreateMockHistoryService();
var page = new ShellListPage(settings, mockHistoryService.Object, telemetryService: null);
await UpdatePageAndWaitForItems(page, () => { page.SearchText = "c:\\Program Files\\"; });
var commandList = page.GetItems();
foreach (var item in commandList)
{
Assert.IsTrue(!string.IsNullOrEmpty(item.TextToSuggest));
Assert.IsFalse(item.TextToSuggest.StartsWith('"'));
}
}
}

View File

@@ -7,6 +7,7 @@ using System.Linq;
using System.Threading.Tasks;
using Microsoft.CommandPalette.Extensions;
using Microsoft.CommandPalette.Extensions.Toolkit;
using Windows.Foundation;
namespace Microsoft.CmdPal.Ext.UnitTestBase;
@@ -32,9 +33,14 @@ public class CommandPaletteUnitTestBase
// and wait for the event to be raised.
var tcs = new TaskCompletionSource<object>();
page.ItemsChanged += (sender, args) => tcs.SetResult(null);
TypedEventHandler<object, IItemsChangedEventArgs> handleItemsChanged = (object s, IItemsChangedEventArgs e) =>
{
tcs.TrySetResult(e);
};
page.ItemsChanged += handleItemsChanged;
modification();
await tcs.Task;
}
}