Skip to content

Commit

Permalink
Add tests for dealing with multiple projects in workspace
Browse files Browse the repository at this point in the history
  • Loading branch information
desplesda committed Jun 26, 2023
1 parent 2a8061c commit 8a01a4d
Show file tree
Hide file tree
Showing 13 changed files with 265 additions and 72 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -151,4 +151,7 @@ export.log
/_api/

# Test coverage data
YarnSpinner.Tests/coverage/
YarnSpinner.Tests/coverage/

# Folders marked as containing non-public data
*DoNotCommit/
14 changes: 7 additions & 7 deletions YarnSpinner.LanguageServer.Tests/CommandTests.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using FluentAssertions;
using Newtonsoft.Json.Linq;
using OmniSharp.Extensions.LanguageServer.Protocol;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
using OmniSharp.Extensions.LanguageServer.Protocol.Workspace;
using Xunit;
using Xunit.Abstractions;
using YarnLanguageServer;
using System.Linq;

namespace YarnLanguageServer.Tests;

Expand All @@ -24,7 +24,7 @@ public async Task Server_CanListNodesInFile()
{
// Set up the server
var (client, server) = await Initialize(ConfigureClient, ConfigureServer);
var filePath = Path.Combine(PathToTestData, "Test.yarn");
var filePath = Path.Combine(TestUtility.PathToTestWorkspace, "Project1", "Test.yarn");

var result = await client.ExecuteCommand(new ExecuteCommandParams<Container<NodeInfo>>
{
Expand Down Expand Up @@ -70,7 +70,7 @@ public async Task Server_OnAddNodeCommand_ReturnsTextEdit()
{
// Set up the server
var (client, server) = await Initialize(ConfigureClient, ConfigureServer);
var filePath = Path.Combine(PathToTestData, "Test.yarn");
var filePath = Path.Combine(TestUtility.PathToTestWorkspace, "Project1", "Test.yarn");

NodesChangedParams? nodeInfo;

Expand Down Expand Up @@ -111,7 +111,7 @@ public async Task Server_OnRemoveNodeCommand_ReturnsTextEdit()
{
// Set up the server
var (client, server) = await Initialize(ConfigureClient, ConfigureServer);
var filePath = Path.Combine(PathToTestData, "Test.yarn");
var filePath = Path.Combine(TestUtility.PathToTestWorkspace, "Project1", "Test.yarn");

NodesChangedParams? nodeInfo;

Expand Down Expand Up @@ -147,7 +147,7 @@ public async Task Server_OnUpdateHeaderCommand_ReturnsTextEditCreatingHeader()
{
// Set up the server
var (client, server) = await Initialize(ConfigureClient, ConfigureServer);
var filePath = Path.Combine(PathToTestData, "Test.yarn");
var filePath = Path.Combine(TestUtility.PathToTestWorkspace, "Project1", "Test.yarn");

NodesChangedParams? nodeInfo;

Expand Down Expand Up @@ -195,7 +195,7 @@ public async Task Server_OnUpdateHeaderCommand_ReturnsTextEditModifyingHeader()
{
// Set up the server
var (client, server) = await Initialize(ConfigureClient, ConfigureServer);
var filePath = Path.Combine(PathToTestData, "Test.yarn");
var filePath = Path.Combine(TestUtility.PathToTestWorkspace, "Project1", "Test.yarn");

NodesChangedParams? nodeInfo;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
<PackageReference Include="OmniSharp.Extensions.LanguageServer" Version="0.19.5" />
<PackageReference Include="OmniSharp.Extensions.LanguageProtocol.Testing" Version="0.19.5" />

<PackageReference Include="FluentAssertions" Version="6.3.0" />
<PackageReference Include="FluentAssertions" Version="6.11.0" />
</ItemGroup>

</Project>
41 changes: 27 additions & 14 deletions YarnSpinner.LanguageServer.Tests/LanguageServerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public async Task Server_OnEnteringACommand_ShouldReceiveCompletions()
{
// Set up the server
var (client, server) = await Initialize(ConfigureClient, ConfigureServer);
var filePath = Path.Combine(PathToTestData, "Test.yarn");
var filePath = Path.Combine(TestUtility.PathToTestWorkspace, "Project1", "Test.yarn");

// Start typing a command
ChangeTextInDocument(client, filePath, new Position(8, 0), "<<");
Expand Down Expand Up @@ -98,21 +98,28 @@ public async Task Server_OnOpeningDocument_SendsNodesChangedNotification()
[Fact]
public async Task Server_OnChangingDocument_SendsNodesChangedNotification()
{
var nodesChangedOnInitialization = GetNodesChangedNotificationAsync((nodesResult) =>
nodesResult.Uri.AbsolutePath.Contains(Path.Combine("Project1", "Test.yarn"))
);

var (client, server) = await Initialize(ConfigureClient, ConfigureServer);

var filePath = Path.Combine(PathToTestData, "Test.yarn");
var filePath = Path.Combine(TestUtility.PathToTestWorkspace, "Project1", "Test.yarn");

NodesChangedParams? nodeInfo;

nodeInfo = await GetNodesChangedNotificationAsync();

// Await a notification that nodes changed in this file
nodeInfo = await nodesChangedOnInitialization;

nodeInfo.Uri.ToString().Should().Be("file://" + filePath, "because this is the URI of the file we opened");

nodeInfo.Nodes.Should().HaveCount(2, "because there are two nodes in the file before we make changes");

ChangeTextInDocument(client, filePath, new Position(19, 0), "title: Node3\n---\n===\n");
ChangeTextInDocument(client, filePath, new Position(20, 0), "title: Node3\n---\n===\n");

nodeInfo = await GetNodesChangedNotificationAsync();
nodeInfo = await GetNodesChangedNotificationAsync((nodesResult) =>
nodesResult.Uri.AbsolutePath.Contains(Path.Combine("Project1", "Test.yarn"))
);

nodeInfo.Nodes.Should().HaveCount(3, "because we added a new node");
nodeInfo.Nodes.Should().Contain(n => n.Title == "Node3", "because the new node we added has this title");
Expand All @@ -124,26 +131,32 @@ public async Task Server_OnInvalidChanges_ProducesSyntaxErrors()
var (client, server) = await Initialize(ConfigureClient, ConfigureServer);

{
var errors = (await GetDiagnosticsAsync()).Where(d => d.Severity == DiagnosticSeverity.Error);
var errors = (await GetDiagnosticsAsync()).Diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error);

errors.Should().BeNullOrEmpty("because the original document contains no syntax errors");
errors.Should().BeNullOrEmpty("because the original project contains no syntax errors");
}

// Introduce an error
var filePath = Path.Combine(PathToTestData, "Test.yarn");
ChangeTextInDocument(client, filePath, new Position(8, 0), "<<set");
var filePath = Path.Combine(TestUtility.PathToTestWorkspace, "Project1", "Test.yarn");
ChangeTextInDocument(client, filePath, new Position(9, 0), "<<set");

{
var errors = (await GetDiagnosticsAsync()).Where(d => d.Severity == DiagnosticSeverity.Error);
var diagnosticsResult = await GetDiagnosticsAsync(diags => diags.Uri.ToString().Contains("Test.yarn"));

var enumerable = diagnosticsResult.Diagnostics;

var errors = enumerable.Where(d => d.Severity == DiagnosticSeverity.Error);

errors.Should().NotBeNullOrEmpty("because we have introduced a syntax error");
}

// Remove the error
ChangeTextInDocument(client, filePath, new Position(8, 0), new Position(8, 5), "");
ChangeTextInDocument(client, filePath, new Position(9, 0), new Position(9, 5), "");

{
var errors = (await GetDiagnosticsAsync()).Where(d => d.Severity == DiagnosticSeverity.Error);
PublishDiagnosticsParams diagnosticsResult = await GetDiagnosticsAsync(diags => diags.Uri.ToString().Contains("Test.yarn"));

var errors = diagnosticsResult.Diagnostics.Where(d => d.Severity == DiagnosticSeverity.Error);

errors.Should().BeNullOrEmpty("because the syntax error was removed");
}
Expand All @@ -154,7 +167,7 @@ public async Task Server_OnJumpCommand_ShouldReceiveNodeNameCompletions()
{
// Set up the server
var (client, server) = await Initialize(ConfigureClient, ConfigureServer);
var filePath = Path.Combine(PathToTestData, "Test.yarn");
var filePath = Path.Combine(TestUtility.PathToTestWorkspace, "Project1", "Test.yarn");
CompletionList? completions;

// The line in Test.yarn we're inserting the new jump command on.
Expand Down
90 changes: 42 additions & 48 deletions YarnSpinner.LanguageServer.Tests/LanguageServerTestsBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,35 @@
using Range = OmniSharp.Extensions.LanguageServer.Protocol.Models.Range;
using OmniSharp.Extensions.LanguageServer.Protocol.Client;

class NotificationListeners<T> : HashSet<(TaskCompletionSource<T> Task, System.Func<T, bool> Test)> {
public TaskCompletionSource<T> AddListener(Func<T, bool>? test) {
var completionSource = new TaskCompletionSource<T>();

if (test == null) {
// If no test is provided, use a test that always returns true.
test = (item) => true;
}

this.Add((Task: completionSource, Test: test));
return completionSource;
}

public void ApplyResult(T result) {
var completed = this.Where(item => item.Test(result)).ToList();
foreach (var item in completed) {
item.Task.TrySetResult(result);
}
this.ExceptWith(completed);
}
}

#pragma warning disable CS0162

#pragma warning disable VSTHRD200 // async method names should end with "Async"

namespace YarnLanguageServer.Tests
{
public class LanguageServerTestsBase : LanguageProtocolTestBase
public abstract class LanguageServerTestsBase : LanguageProtocolTestBase
{
public LanguageServerTestsBase(ITestOutputHelper outputHelper) : base(
new JsonRpcTestOptions()
Expand All @@ -31,43 +53,11 @@ public LanguageServerTestsBase(ITestOutputHelper outputHelper) : base(
{
}

TaskCompletionSource<List<Diagnostic>> ReceivedDiagnosticsNotification = new();

TaskCompletionSource<NodesChangedParams> NodesChangedNotification = new();

protected static string PathToTestData
{
get
{
var context = AppContext.BaseDirectory;

var directoryContainingProject = GetParentDirectoryContainingFile(new DirectoryInfo(context), "*.csproj");
NotificationListeners<PublishDiagnosticsParams> ReceivedDiagnosticsNotifications = new();

if (directoryContainingProject != null)
{
return Path.Combine(directoryContainingProject.FullName, "TestData");
}
else
{
throw new InvalidOperationException("Failed to find path containing .csproj!");
}
NotificationListeners<NodesChangedParams> NodesChangedNotification = new();

static DirectoryInfo? GetParentDirectoryContainingFile(DirectoryInfo directory, string filePattern)
{
var current = directory;
do
{
if (current.EnumerateFiles(filePattern).Any())
{
return current;
}
current = current.Parent;
} while (current != null);

return null;
}
}
}
protected virtual string RootPath => TestUtility.PathToTestWorkspace;

protected static void ChangeTextInDocument(ILanguageClient client, string fileURI, Position start, string text)
{
Expand Down Expand Up @@ -128,26 +118,30 @@ protected void ConfigureClient(LanguageClientOptions options)

options.OnPublishDiagnostics((diagnosticsParams) =>
{
var diagnostics = diagnosticsParams.Diagnostics.ToList();
ReceivedDiagnosticsNotification.TrySetResult(diagnostics);
ReceivedDiagnosticsNotifications.ApplyResult(diagnosticsParams);
});

void OnNodesChangedNotification(NodesChangedParams nodesChangedParams)
{
NodesChangedNotification.TrySetResult(nodesChangedParams);
NodesChangedNotification.ApplyResult(nodesChangedParams);
}

options.OnNotification(Commands.DidChangeNodesNotification, (Action<NodesChangedParams>)OnNodesChangedNotification);

options.WithRootPath(PathToTestData);
options.ConfigureConfiguration(config =>
{
config.Properties.Add("yarnspinner.CSharpLookup", true);
});

options.WithRootPath(this.RootPath);
}

protected static void ConfigureServer(LanguageServerOptions options)
{
YarnLanguageServer.ConfigureOptions(options);
}

protected async Task<T> GetTaskResultOrTimeoutAsync<T>(TaskCompletionSource<T> task, Action onCompletion, double timeout = 2f) {
protected async Task<T> GetTaskResultOrTimeoutAsync<T>(TaskCompletionSource<T> task, System.Action? onCompletion, double timeout = 2f) {
try
{
// Timeout.
Expand All @@ -165,7 +159,7 @@ protected async Task<T> GetTaskResultOrTimeoutAsync<T>(TaskCompletionSource<T> t
finally
{
// Get ready for the next call
onCompletion();
onCompletion?.Invoke();
}
}

Expand All @@ -178,19 +172,19 @@ protected async Task<T> GetTaskResultOrTimeoutAsync<T>(TaskCompletionSource<T> t
/// <param name="timeout">The amount of time to wait for
/// diagnostics.</param>
/// <returns>A collection of <see cref="Diagnostic"/> objects.</returns>
protected async Task<IEnumerable<Diagnostic>> GetDiagnosticsAsync(double timeout = 2f)
protected async Task<PublishDiagnosticsParams> GetDiagnosticsAsync(Func<PublishDiagnosticsParams, bool>? test = null, double timeout = 2f)
{
return await GetTaskResultOrTimeoutAsync(
ReceivedDiagnosticsNotification,
() => ReceivedDiagnosticsNotification = new(),
ReceivedDiagnosticsNotifications.AddListener(test) ,
null,
timeout
);
}

protected async Task<NodesChangedParams> GetNodesChangedNotificationAsync(double timeout = 2f) {
protected async Task<NodesChangedParams> GetNodesChangedNotificationAsync(Func<NodesChangedParams, bool>? test = null, double timeout = 2f) {
return await GetTaskResultOrTimeoutAsync(
NodesChangedNotification,
() => NodesChangedNotification = new(),
NodesChangedNotification.AddListener(test),
null,
timeout
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
title: NotIncludedInProject
---
// This file is not in a directory that has a .yarnproject file.
===
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"projectFileVersion": 2,
"sourceFiles": ["**/*.yarn"],
"baseLanguage": "en",
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ This is a line.
-> Option 1
-> Option 2

<<command>>
// This command is unknown to the LSP and should produce a warning
<<unknown_command>>

<<jump Node2>>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"Commands": [
{
"YarnName": "custom_command",
"Parameters": [
{
"Name": "target",
"Type": "string"
}
]
}
],
"Functions": [
{
"YarnName": "custom_function",
"ReturnType": "bool",
"Parameters": [
{
"Name": "value",
"Type": "number"
}
]
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"projectFileVersion": 2,
"sourceFiles": ["**/*.yarn"],
"baseLanguage": "en",
"definitions": "Functions.ysls.json"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
title: Start
---
<<custom_command "woo">>
<<if custom_function(2)>>

<<endif>>
<<if unknown_function(3)>>
<<endif>>
===
Loading

0 comments on commit 8a01a4d

Please sign in to comment.