run all tests v1

This commit is contained in:
Matt Parker
2025-11-04 20:55:51 +10:00
parent dfd9b3dd96
commit fcf5ccf357
5 changed files with 85 additions and 23 deletions

View File

@@ -30,35 +30,30 @@ public class TestRunnerService
return allDiscoveredTestNodes;
}
public async Task RunTestsAsync(SharpIdeSolutionModel solutionModel, Func<TestNodeUpdate[], Task> func)
{
await Task.WhenAll(solutionModel.AllProjects.Select(s => s.MsBuildEvaluationProjectTask));
var testProjects = solutionModel.AllProjects.Where(p => p.IsMtpTestProject).ToList();
foreach (var testProject in testProjects)
{
await RunTestsAsync(testProject, func);
}
}
// Assumes it has already been built
public async Task RunTestsAsync(SharpIdeProjectModel project)
public async Task RunTestsAsync(SharpIdeProjectModel project, Func<TestNodeUpdate[], Task> func)
{
using var client = await GetInitialisedClientAsync(project);
List<TestNodeUpdate> testNodeUpdates = [];
var discoveryResponse = await client.DiscoverTestsAsync(Guid.NewGuid(), node =>
var discoveryResponse = await client.DiscoverTestsAsync(Guid.NewGuid(), async nodeUpdates =>
{
testNodeUpdates.AddRange(node);
return Task.CompletedTask;
testNodeUpdates.AddRange(nodeUpdates);
await func(nodeUpdates);
});
await discoveryResponse.WaitCompletionAsync();
Console.WriteLine($"Discovery finished: {testNodeUpdates.Count} tests discovered");
Console.WriteLine(string.Join(Environment.NewLine, testNodeUpdates.Select(n => n.Node.DisplayName)));
List <TestNodeUpdate> runResults = [];
ResponseListener runRequest = await client.RunTestsAsync(Guid.NewGuid(), testNodeUpdates.Select(x => x.Node).ToArray(), node =>
{
runResults.AddRange(node);
return Task.CompletedTask;
});
ResponseListener runRequest = await client.RunTestsAsync(Guid.NewGuid(), testNodeUpdates.Select(x => x.Node).ToArray(), func);
await runRequest.WaitCompletionAsync();
var passedCount = runResults.Where(tn => tn.Node.ExecutionState == ExecutionStates.Passed).Count();
var failedCount = runResults.Where(tn => tn.Node.ExecutionState == ExecutionStates.Failed).Count();
var skippedCount = runResults.Where(tn => tn.Node.ExecutionState == ExecutionStates.Skipped).Count();
Console.WriteLine($"Passed: {passedCount}; Skipped: {skippedCount}; Failed: {failedCount};");
await client.ExitAsync();
}

View File

@@ -1,6 +1,7 @@
using Godot;
using SharpIDE.Application.Features.Build;
using SharpIDE.Application.Features.Testing;
using SharpIDE.Application.Features.Testing.Client.Dtos;
namespace SharpIDE.Godot.Features.TestExplorer;
@@ -14,13 +15,16 @@ public partial class TestExplorerPanel : Control
private Button _refreshButton = null!;
private VBoxContainer _testNodesVBoxContainer = null!;
private Button _runAllTestsButton = null!;
public override void _Ready()
{
_refreshButton = GetNode<Button>("%RefreshButton");
_testNodesVBoxContainer = GetNode<VBoxContainer>("%TestNodesVBoxContainer");
_runAllTestsButton = GetNode<Button>("%RunAllTestsButton");
_ = Task.GodotRun(AsyncReady);
_refreshButton.Pressed += OnRefreshButtonPressed;
_runAllTestsButton.Pressed += OnRunAllTestsButtonPressed;
}
private async Task AsyncReady()
@@ -58,4 +62,41 @@ public partial class TestExplorerPanel : Control
}
});
}
private readonly Dictionary<string, TestNodeEntry> _testNodeEntryNodes = [];
private void OnRunAllTestsButtonPressed()
{
_ = Task.GodotRun(async () =>
{
await _solutionAccessor.SolutionReadyTcs.Task;
var solution = _solutionAccessor.SolutionModel!;
await _buildService.MsBuildAsync(solution.FilePath);
await this.InvokeAsync(() => _testNodesVBoxContainer.QueueFreeChildren());
_testNodeEntryNodes.Clear();
await _testRunnerService.RunTestsAsync(solution, Func);
});
}
private async Task Func(TestNodeUpdate[] nodeUpdates)
{
// Receive node updates - could be discovery, running, success, failed, skipped, etc
await this.InvokeAsync(() =>
{
foreach (var update in nodeUpdates)
{
if (_testNodeEntryNodes.TryGetValue(update.Node.Uid, out var entry))
{
entry.TestNode = update.Node;
entry.SetValues();
}
else
{
var newEntry = _testNodeEntryScene.Instantiate<TestNodeEntry>();
newEntry.TestNode = update.Node;
_testNodeEntryNodes[update.Node.Uid] = newEntry;
_testNodesVBoxContainer.AddChild(newEntry);
}
}
});
}
}

View File

@@ -1,7 +1,9 @@
[gd_scene load_steps=3 format=3 uid="uid://hwdok1kch3b3"]
[gd_scene load_steps=5 format=3 uid="uid://hwdok1kch3b3"]
[ext_resource type="Script" uid="uid://8o78ti2hb0pu" path="res://Features/TestExplorer/TestExplorerPanel.cs" id="1_b5miy"]
[ext_resource type="Texture2D" uid="uid://cmwkpdv6pxaai" path="res://Resources/refresh.svg" id="2_4bhqg"]
[ext_resource type="Texture2D" uid="uid://bkty6563cthj8" path="res://Features/Run/Resources/Run.svg" id="3_b7h3i"]
[ext_resource type="PackedScene" uid="uid://dt50f2of66dlt" path="res://Features/TestExplorer/TestNodeEntry.tscn" id="3_vj6ii"]
[node name="TestExplorerPanel" type="Control"]
layout_mode = 3
@@ -67,15 +69,30 @@ layout_mode = 2
size_flags_horizontal = 3
placeholder_text = "Search for a test"
[node name="ScrollContainer" type="ScrollContainer" parent="VBoxContainer/MarginContainer2/HSplitContainer/VBoxContainer"]
[node name="HBoxContainer2" type="HBoxContainer" parent="VBoxContainer/MarginContainer2/HSplitContainer/VBoxContainer"]
layout_mode = 2
size_flags_vertical = 3
[node name="TestNodesVBoxContainer" type="VBoxContainer" parent="VBoxContainer/MarginContainer2/HSplitContainer/VBoxContainer/ScrollContainer"]
[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/MarginContainer2/HSplitContainer/VBoxContainer/HBoxContainer2"]
layout_mode = 2
[node name="RunAllTestsButton" type="Button" parent="VBoxContainer/MarginContainer2/HSplitContainer/VBoxContainer/HBoxContainer2/VBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
icon = ExtResource("3_b7h3i")
[node name="ScrollContainer" type="ScrollContainer" parent="VBoxContainer/MarginContainer2/HSplitContainer/VBoxContainer/HBoxContainer2"]
layout_mode = 2
size_flags_horizontal = 3
[node name="TestNodesVBoxContainer" type="VBoxContainer" parent="VBoxContainer/MarginContainer2/HSplitContainer/VBoxContainer/HBoxContainer2/ScrollContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="TestNodeEntry" parent="VBoxContainer/MarginContainer2/HSplitContainer/VBoxContainer/HBoxContainer2/ScrollContainer/TestNodesVBoxContainer" instance=ExtResource("3_vj6ii")]
layout_mode = 2
[node name="VBoxContainer2" type="VBoxContainer" parent="VBoxContainer/MarginContainer2/HSplitContainer"]
layout_mode = 2

View File

@@ -6,13 +6,16 @@ namespace SharpIDE.Godot.Features.TestExplorer;
public partial class TestNodeEntry : MarginContainer
{
private Label _testNameLabel = null!;
private Label _testNodeStatusLabel = null!;
public TestNode TestNode { get; set; } = null!;
public override void _Ready()
{
_testNameLabel = GetNode<Label>("%TestNameLabel");
_testNodeStatusLabel = GetNode<Label>("%TestNodeStatusLabel");
_testNameLabel.Text = string.Empty;
_testNodeStatusLabel.Text = string.Empty;
SetValues();
}
@@ -20,5 +23,6 @@ public partial class TestNodeEntry : MarginContainer
{
if (TestNode == null) return;
_testNameLabel.Text = TestNode.DisplayName;
_testNodeStatusLabel.Text = TestNode.ExecutionState;
}
}

View File

@@ -20,3 +20,8 @@ layout_mode = 2
unique_name_in_owner = true
layout_mode = 2
text = "Test Name"
[node name="TestNodeStatusLabel" type="Label" parent="HBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
text = "Node Status"