Skip to content

Commit

Permalink
Yield UI thread between editor commands in tests
Browse files Browse the repository at this point in the history
  • Loading branch information
mhutch committed Sep 26, 2024
1 parent a4e97ec commit 93927f4
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 89 deletions.
2 changes: 1 addition & 1 deletion Editor.Tests/Commands/AutoClosingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public Task TestComment (string sourceText, string expectedText, string typeChar
return this.TestCommands (
sourceText,
expectedText,
(s) => s.Type (typeChars),
EditorAction.Type (typeChars),
caretMarkerChar: '|',
initialize: (ITextView tv) => {
tv.Options.SetOptionValue (XmlOptions.AutoInsertClosingTag, true);
Expand Down
92 changes: 40 additions & 52 deletions Editor.Tests/Completion/CommitTests.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Linq;
using System.Threading.Tasks;

using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Editor.Commanding;

using MonoDevelop.Xml.Editor.Tests.Extensions;

Expand All @@ -28,10 +25,7 @@ public Task SingleClosingTag ()
<bar>
</bar>$
",
(s) => {
s.Type ("</b");
s.Enter ();
}
EditorAction.Type ("</b\n")
);

[Test]
Expand All @@ -43,10 +37,7 @@ public Task SingleClosingTagSameLine ()
@"<foo>
<bar></bar>$
",
(s) => {
s.Type ("</b");
s.Enter ();
}
EditorAction.Type ("</b\n")
);

[Test]
Expand All @@ -60,11 +51,11 @@ public Task SingleClosingTagExplicitInvocation ()
<bar>
</bar>$
",
(s) => {
s.InvokeCompletion ();
s.Type ("</b");
s.Enter ();
}
[
EditorAction.InvokeCompletion,
.. EditorAction.Type ("</b"),
EditorAction.Enter
]
);

[Test]
Expand All @@ -76,23 +67,23 @@ public Task SingleClosingTagSameLineExplicitInvocation ()
@"<foo>
<bar></bar>$
",
(s) => {
s.InvokeCompletion ();
s.Type ("</b");
s.Enter ();
}
[
EditorAction.InvokeCompletion,
.. EditorAction.Type ("</b"),
EditorAction.Enter
]
);

[Test]
public Task ClosingTagSingleLineEof ()
=> this.TestCommands (
@"<foo><bar>$",
@"<foo><bar></bar>$",
(s) => {
s.InvokeCompletion ();
s.Type ("</b");
s.Enter ();
}
[
EditorAction.InvokeCompletion,
.. EditorAction.Type ("</b"),
EditorAction.Enter
]
);

[Test]
Expand All @@ -104,11 +95,11 @@ public Task ClosingTagEof ()
@"<foo>
<bar>
</bar>$",
(s) => {
s.InvokeCompletion ();
s.Type ("</b");
s.Enter ();
}
[
EditorAction.InvokeCompletion,
.. EditorAction.Type ("</b"),
EditorAction.Enter
]
);

[Test]
Expand All @@ -126,10 +117,7 @@ public Task MultipleClosingTags ()
</bar>
</foo>$
",
(s) => {
s.Type ("</f");
s.Enter ();
}
EditorAction.Type ("</f\n")
);

[Test]
Expand All @@ -145,36 +133,36 @@ public Task MultipleClosingTagsSameLine ()
</bar>
</foo>$
",
(s) => {
s.Type ("</f");
s.Enter ();
}
EditorAction.Type ("</f\n")
);

Task TestTypeCommands (string before, string after, string typeChars)
{
// between each separate action, TestCommands will wait for any active completion session to compute its items
var actions = typeChars.Split ('^').Select (t => { Action<IEditorCommandHandlerService> a = (s) => s.Type (t); return a; });
return this.TestCommands (before, after, actions, initialize: (ITextView tv) => {
tv.Options.SetOptionValue ("BraceCompletion/Enabled", true);
return Task.CompletedTask;
});
return this.TestCommands (
before,
after,
EditorAction.Type(typeChars),
initialize: (ITextView tv) => {
tv.Options.SetOptionValue ("BraceCompletion/Enabled", true);
return Task.CompletedTask;
}
);
}

[Test]
[TestCase ("<he\n", "<foo><Hello$")]
[TestCase ("<he^>", "<foo><Hello>$</Hello>")]
[TestCase ("<He^ ", "<foo><Hello $")]
[TestCase ("<He^<", "<foo><He<$")]
[TestCase ("<he>", "<foo><Hello>$</Hello>")]
[TestCase ("<He ", "<foo><Hello $")]
[TestCase ("<He<", "<foo><He<$")]
public Task CommitElement (string typeChars, string after) => TestTypeCommands ("<foo>$", after, typeChars);

[Test]
[TestCase (" T\n", "<Hello There=\"$\"")]
[TestCase (" T\n\"", "<Hello There=\"\"$")]
[TestCase (" T^=", "<Hello There=$")]
[TestCase (" T^=\"", "<Hello There=\"$\"")]
[TestCase (" Th^<", "<Hello Th<$")]
[TestCase (" Th^ ", "<Hello There $")]
[TestCase (" T=", "<Hello There=$")]
[TestCase (" T=\"", "<Hello There=\"$\"")]
[TestCase (" Th<", "<Hello Th<$")]
[TestCase (" Th ", "<Hello There $")]
public Task CommitAttribute (string typeChars, string after) => TestTypeCommands ("<Hello$", after, typeChars);
}
}
39 changes: 3 additions & 36 deletions Editor.Tests/Extensions/CommandServiceExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@

using System;
using System.Threading;

using Microsoft.VisualStudio.Commanding;
using Microsoft.VisualStudio.Language.Intellisense.AsyncCompletion;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Editor.Commanding;
using Microsoft.VisualStudio.Text.Editor.Commanding.Commands;

namespace MonoDevelop.Xml.Editor.Tests.Extensions
{
Expand All @@ -19,39 +19,6 @@ public static class CommandServiceExtensions
/// </summary>
public static bool EnableDebugTrace { get; set; }

public static void Type (this IEditorCommandHandlerService commandService, string text)
{
foreach (var c in text) {
switch (c) {
case '\n':
Enter (commandService);
break;
default:
if (EnableDebugTrace) {
LogTrace ($"Typing '{c}'");
}
commandService.CheckAndExecute ((v, b) => new TypeCharCommandArgs (v, b, c));
break;
}
}
}

public static void Enter (this IEditorCommandHandlerService commandService)
{
if (EnableDebugTrace) {
LogTrace ("Invoking return key");
}
commandService.CheckAndExecute ((v, b) => new ReturnKeyCommandArgs (v, b));
}

public static void InvokeCompletion (this IEditorCommandHandlerService commandService)
{
if (EnableDebugTrace) {
LogTrace ("Invoking completion");
}
commandService.CheckAndExecute ((v, b) => new InvokeCompletionListCommandArgs (v, b));
}

public static void CheckAndExecute<T> (
this IEditorCommandHandlerService commandService,
Func<ITextView, ITextBuffer, T> argsFactory) where T : EditorCommandArgs
Expand Down Expand Up @@ -84,7 +51,7 @@ public static void CheckAndExecute<T> (
// failures on GitHub Actions CI.
//
// Note that polling IAsyncCompletionSessionOperations.IsStarted does not help.
if (IsGithubActions && !session.Properties.TryGetProperty (HasWaitedForCompletionToInitializeKey, out bool hasWaited)) {
if (IsGitHubActions && !session.Properties.TryGetProperty (HasWaitedForCompletionToInitializeKey, out bool hasWaited)) {
session.Properties.AddProperty (HasWaitedForCompletionToInitializeKey, true);
Thread.Sleep (500);
}
Expand All @@ -102,7 +69,7 @@ public static void CheckAndExecute<T> (

static readonly object HasWaitedForCompletionToInitializeKey = new();

static readonly bool IsGithubActions = Environment.GetEnvironmentVariable("GITHUB_ACTIONS") != null;
static readonly bool IsGitHubActions = Environment.GetEnvironmentVariable("GITHUB_ACTIONS") != null;

static void LogTrace(string message) => Console.WriteLine ($"{TraceID}: {message}");

Expand Down
52 changes: 52 additions & 0 deletions Editor.Tests/Extensions/EditorAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Collections.Generic;

using Microsoft.VisualStudio.Text.Editor.Commanding;
using Microsoft.VisualStudio.Text.Editor.Commanding.Commands;

namespace MonoDevelop.Xml.Editor.Tests.Extensions
{
public static class EditorAction
{
public static IEnumerable<Action<IEditorCommandHandlerService>> Type (string text)
{
foreach (var c in text) {
switch (c) {
case '\n':

yield return Enter;
break;
default:
if (EnableDebugTrace) {
LogTrace ($"Typing '{c}'");
}
yield return (commandService) => commandService.CheckAndExecute ((v, b) => new TypeCharCommandArgs (v, b, c));
break;
}
}
}

public static void Enter (IEditorCommandHandlerService commandService)
{
if (EnableDebugTrace) {
LogTrace ("Invoking return key");
}
commandService.CheckAndExecute ((v, b) => new ReturnKeyCommandArgs (v, b));
}

public static void InvokeCompletion (IEditorCommandHandlerService commandService)
{
if (EnableDebugTrace) {
LogTrace ("Invoking completion");
}
commandService.CheckAndExecute ((v, b) => new InvokeCompletionListCommandArgs (v, b));
}

const string TraceID = "EditorAction.Trace";
static bool EnableDebugTrace => CommandServiceExtensions.EnableDebugTrace;
static void LogTrace (string message) => Console.WriteLine ($"{TraceID}: {message}");
}
}
4 changes: 4 additions & 0 deletions Editor.Tests/Extensions/EditorCommandExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ public static async Task TestCommands (

foreach (var c in commands) {
c (commandService);

// yield to let things catch up
// and so we don't block the UI thread between the commands
await Task.Delay (20);
}

Assert.AreEqual (afterDocumentText, textView.TextBuffer.CurrentSnapshot.GetText ());
Expand Down

0 comments on commit 93927f4

Please sign in to comment.