Initial commit

This commit is contained in:
2026-07-03 16:31:37 +07:00
commit 899c7c637d
1939 changed files with 641750 additions and 0 deletions

View File

@@ -0,0 +1,275 @@
using FluentAssertions;
using Moq;
using Xunit;
using RobotNet10.NavigationTune.Execution;
using RobotNet10.NavigationTune.Shared.Interfaces;
using RobotNet10.NavigationTune.Shared.Models;
using RobotNet10.NavigationTune.Test.Helpers;
using RobotNet10.NavigationTune.Scenarios;
namespace RobotNet10.NavigationTune.Test.Execution;
public class TestExecutorTests
{
private Mock<ILocalizationProvider> CreateMockLocalization(double x = 0.0, double y = 0.0, double theta = 0.0)
{
var mock = new Mock<ILocalizationProvider>();
mock.Setup(l => l.X).Returns(x);
mock.Setup(l => l.Y).Returns(y);
mock.Setup(l => l.Theta).Returns(theta);
return mock;
}
private Mock<IVelocityProvider> CreateMockVelocityProvider(
double linearVel = 0.0,
double angularVel = 0.0,
double confidence = 1.0)
{
var mock = new Mock<IVelocityProvider>();
mock.Setup(v => v.GetActualVelocity()).Returns((linearVel, angularVel));
mock.Setup(v => v.GetModelConfidence()).Returns(confidence);
return mock;
}
[Fact]
public void Constructor_WithProviders_ShouldInitialize()
{
// Arrange
var localization = CreateMockLocalization();
var velocityProvider = CreateMockVelocityProvider();
// Act
var executor = new TestExecutor(
localization.Object,
velocityProvider.Object);
// Assert
executor.Should().NotBeNull();
}
[Fact]
public async Task ExecuteAsync_WithStraightLineScenario_ShouldComplete()
{
// Arrange
var localization = CreateMockLocalization();
var velocityProvider = CreateMockVelocityProvider();
var executor = new TestExecutor(localization.Object, velocityProvider.Object);
var scenario = TestHelpers.CreateStraightLineScenario(5.0);
var parameters = TestHelpers.CreateDefaultParameterSet();
var telemetryUpdates = new List<TelemetryData>();
// Act
var result = await executor.ExecuteAsync(
scenario,
parameters,
telemetry => telemetryUpdates.Add(telemetry),
new CancellationTokenSource(TimeSpan.FromSeconds(1)).Token);
// Assert
result.Should().NotBeNull();
result.Status.Should().BeOneOf(TestStatus.Completed, TestStatus.Aborted);
telemetryUpdates.Should().NotBeEmpty();
}
[Fact]
public async Task ExecuteAsync_WhenCancelled_ShouldAbort()
{
// Arrange
var localization = CreateMockLocalization();
var velocityProvider = CreateMockVelocityProvider();
var executor = new TestExecutor(localization.Object, velocityProvider.Object);
var scenario = TestHelpers.CreateStraightLineScenario(10.0);
var parameters = TestHelpers.CreateDefaultParameterSet();
var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(100));
// Act
var result = await executor.ExecuteAsync(scenario, parameters, null, cts.Token);
// Assert
result.Status.Should().BeOneOf(TestStatus.Aborted, TestStatus.Error);
}
[Fact]
public void GetProgress_BeforeExecution_ShouldReturnZero()
{
// Arrange
var localization = CreateMockLocalization();
var velocityProvider = CreateMockVelocityProvider();
var executor = new TestExecutor(localization.Object, velocityProvider.Object);
// Act
var progress = executor.GetProgress();
// Assert
progress.ProgressPercent.Should().Be(0.0);
progress.DistanceTraveled.Should().Be(0.0);
}
[Fact]
public async Task Pause_WhenRunning_ShouldChangeStatus()
{
// Arrange
var localization = CreateMockLocalization();
var velocityProvider = CreateMockVelocityProvider();
var executor = new TestExecutor(localization.Object, velocityProvider.Object);
var scenario = TestHelpers.CreateStraightLineScenario(1.0);
var parameters = TestHelpers.CreateDefaultParameterSet();
// Act - Start test in background
var task = Task.Run(async () =>
{
await executor.ExecuteAsync(scenario, parameters, null,
new CancellationTokenSource(TimeSpan.FromSeconds(2)).Token);
});
// Wait a bit then pause
await Task.Delay(100);
executor.Pause();
// Assert
var progress = executor.GetProgress();
progress.Should().NotBeNull();
// Cleanup
executor.Stop();
await task;
}
[Fact]
public async Task Resume_AfterPause_ShouldContinue()
{
// Arrange
var localization = CreateMockLocalization();
var velocityProvider = CreateMockVelocityProvider();
var executor = new TestExecutor(localization.Object, velocityProvider.Object);
var scenario = TestHelpers.CreateStraightLineScenario(1.0);
var parameters = TestHelpers.CreateDefaultParameterSet();
// Act
var task = Task.Run(async () =>
{
await executor.ExecuteAsync(scenario, parameters, null,
new CancellationTokenSource(TimeSpan.FromSeconds(2)).Token);
});
await Task.Delay(100);
executor.Pause();
await Task.Delay(50);
executor.Resume();
// Assert
var progress = executor.GetProgress();
progress.Should().NotBeNull();
// Cleanup
executor.Stop();
await task;
}
[Fact]
public async Task Stop_WhenRunning_ShouldAbort()
{
// Arrange
var localization = CreateMockLocalization();
var velocityProvider = CreateMockVelocityProvider();
var executor = new TestExecutor(localization.Object, velocityProvider.Object);
var scenario = TestHelpers.CreateStraightLineScenario(10.0);
var parameters = TestHelpers.CreateDefaultParameterSet();
// Act
var task = Task.Run(async () =>
{
await executor.ExecuteAsync(scenario, parameters, null,
new CancellationTokenSource(TimeSpan.FromSeconds(5)).Token);
});
await Task.Delay(200);
executor.Stop();
// Assert
await task;
var progress = executor.GetProgress();
progress.Should().NotBeNull();
}
[Fact]
public async Task EmergencyStop_WhenRunning_ShouldImmediatelyStop()
{
// Arrange
var localization = CreateMockLocalization();
var velocityProvider = CreateMockVelocityProvider();
var executor = new TestExecutor(localization.Object, velocityProvider.Object);
var scenario = TestHelpers.CreateStraightLineScenario(10.0);
var parameters = TestHelpers.CreateDefaultParameterSet();
// Act
var task = Task.Run(async () =>
{
await executor.ExecuteAsync(scenario, parameters, null,
new CancellationTokenSource(TimeSpan.FromSeconds(5)).Token);
});
await Task.Delay(200);
executor.EmergencyStop();
// Assert
await task;
var progress = executor.GetProgress();
progress.Should().NotBeNull();
}
[Fact]
public async Task ExecuteAsync_WithCircleScenario_ShouldComplete()
{
// Arrange
var localization = CreateMockLocalization();
var velocityProvider = CreateMockVelocityProvider();
var executor = new TestExecutor(localization.Object, velocityProvider.Object);
var scenario = TestHelpers.CreateCircleScenario(2.0);
var parameters = TestHelpers.CreateDefaultParameterSet();
// Act
var result = await executor.ExecuteAsync(
scenario,
parameters,
null,
new CancellationTokenSource(TimeSpan.FromSeconds(1)).Token);
// Assert
result.Should().NotBeNull();
result.Status.Should().BeOneOf(TestStatus.Completed, TestStatus.Aborted);
}
[Fact]
public async Task ExecuteAsync_ShouldCollectTelemetry()
{
// Arrange
var localization = CreateMockLocalization();
var velocityProvider = CreateMockVelocityProvider();
var executor = new TestExecutor(localization.Object, velocityProvider.Object);
var scenario = TestHelpers.CreateStraightLineScenario(2.0);
var parameters = TestHelpers.CreateDefaultParameterSet();
var telemetryList = new List<TelemetryData>();
// Act
var result = await executor.ExecuteAsync(
scenario,
parameters,
telemetry => telemetryList.Add(telemetry),
new CancellationTokenSource(TimeSpan.FromSeconds(1)).Token);
// Assert
telemetryList.Should().NotBeEmpty();
result.TelemetryData.Should().NotBeEmpty();
}
}

View File

@@ -0,0 +1,270 @@
using FluentAssertions;
using Moq;
using Xunit;
using RobotNet10.NavigationTune.Execution;
using RobotNet10.NavigationTune.Shared.Interfaces;
using RobotNet10.NavigationTune.Shared.Models;
using RobotNet10.NavigationTune.Test.Helpers;
using RobotNet10.NavigationTune.Services;
namespace RobotNet10.NavigationTune.Test.Execution;
public class TuningNavigationTests
{
private Mock<ITestExecutor> CreateMockTestExecutor()
{
var mock = new Mock<ITestExecutor>();
return mock;
}
private Mock<IMetricsCalculator> CreateMockMetricsCalculator()
{
var mock = new Mock<IMetricsCalculator>();
mock.Setup(m => m.CalculateMetrics(
It.IsAny<List<TelemetryData>>(),
It.IsAny<ReferencePath>()))
.Returns(new TestMetrics
{
OverallScore = 0.85f,
CrossTrackErrorRMS = 0.05f,
HeadingErrorRMS = 0.02f
});
return mock;
}
[Fact]
public void Constructor_WithDependencies_ShouldInitialize()
{
// Arrange
var testExecutor = CreateMockTestExecutor();
var metricsCalculator = CreateMockMetricsCalculator();
// Act
var tuningNav = new TuningNavigation(
testExecutor.Object,
metricsCalculator.Object);
// Assert
tuningNav.Should().NotBeNull();
tuningNav.IsTestRunning.Should().BeFalse();
}
[Fact]
public async Task ExecuteTestAsync_WithValidInputs_ShouldReturnResult()
{
// Arrange
var testExecutor = CreateMockTestExecutor();
var metricsCalculator = CreateMockMetricsCalculator();
testExecutor.Setup(e => e.ExecuteAsync(
It.IsAny<TestScenario>(),
It.IsAny<NavigationParameterSet>(),
It.IsAny<Action<TelemetryData>>(),
It.IsAny<CancellationToken>()))
.ReturnsAsync(new TestExecutionResult
{
TestRunId = Guid.NewGuid(),
Status = TestStatus.Completed,
TelemetryData = new List<TelemetryData>(),
StartTime = DateTime.UtcNow
});
var tuningNav = new TuningNavigation(
testExecutor.Object,
metricsCalculator.Object);
var scenario = TestHelpers.CreateStraightLineScenario();
var parameters = TestHelpers.CreateDefaultParameterSet();
// Act
var result = await tuningNav.ExecuteTestAsync(scenario, parameters);
// Assert
result.Should().NotBeNull();
result.Status.Should().Be(TestStatus.Completed);
testExecutor.Verify(e => e.ExecuteAsync(
It.IsAny<TestScenario>(),
It.IsAny<NavigationParameterSet>(),
It.IsAny<Action<TelemetryData>>(),
It.IsAny<CancellationToken>()), Times.Once);
}
[Fact]
public async Task ExecuteTestAsync_WhenAlreadyRunning_ShouldThrow()
{
// Arrange
var testExecutor = CreateMockTestExecutor();
var metricsCalculator = CreateMockMetricsCalculator();
// Simulate long-running test
testExecutor.Setup(e => e.ExecuteAsync(
It.IsAny<TestScenario>(),
It.IsAny<NavigationParameterSet>(),
It.IsAny<Action<TelemetryData>>(),
It.IsAny<CancellationToken>()))
.Returns(async () =>
{
await Task.Delay(1000);
return new TestExecutionResult { Status = TestStatus.Completed };
});
var tuningNav = new TuningNavigation(
testExecutor.Object,
metricsCalculator.Object);
var scenario = TestHelpers.CreateStraightLineScenario();
var parameters = TestHelpers.CreateDefaultParameterSet();
// Act
var task1 = tuningNav.ExecuteTestAsync(scenario, parameters);
await Task.Delay(50); // Let first test start
// Assert
await Assert.ThrowsAsync<InvalidOperationException>(async () =>
await tuningNav.ExecuteTestAsync(scenario, parameters));
// Cleanup
tuningNav.Stop();
await task1;
}
[Fact]
public void GetProgress_ShouldDelegateToTestExecutor()
{
// Arrange
var testExecutor = CreateMockTestExecutor();
var metricsCalculator = CreateMockMetricsCalculator();
testExecutor.Setup(e => e.GetProgress())
.Returns(new TestProgress
{
ProgressPercent = 0.5,
DistanceTraveled = 5.0
});
var tuningNav = new TuningNavigation(
testExecutor.Object,
metricsCalculator.Object);
// Act
var progress = tuningNav.GetProgress();
// Assert
progress.Should().NotBeNull();
progress.ProgressPercent.Should().Be(0.5);
testExecutor.Verify(e => e.GetProgress(), Times.Once);
}
[Fact]
public void Pause_ShouldDelegateToTestExecutor()
{
// Arrange
var testExecutor = CreateMockTestExecutor();
var metricsCalculator = CreateMockMetricsCalculator();
var tuningNav = new TuningNavigation(
testExecutor.Object,
metricsCalculator.Object);
// Act
tuningNav.Pause();
// Assert
testExecutor.Verify(e => e.Pause(), Times.Once);
}
[Fact]
public void Resume_ShouldDelegateToTestExecutor()
{
// Arrange
var testExecutor = CreateMockTestExecutor();
var metricsCalculator = CreateMockMetricsCalculator();
var tuningNav = new TuningNavigation(
testExecutor.Object,
metricsCalculator.Object);
// Act
tuningNav.Resume();
// Assert
testExecutor.Verify(e => e.Resume(), Times.Once);
}
[Fact]
public void Stop_ShouldDelegateToTestExecutor()
{
// Arrange
var testExecutor = CreateMockTestExecutor();
var metricsCalculator = CreateMockMetricsCalculator();
var tuningNav = new TuningNavigation(
testExecutor.Object,
metricsCalculator.Object);
// Act
tuningNav.Stop();
// Assert
testExecutor.Verify(e => e.Stop(), Times.Once);
}
[Fact]
public void EmergencyStop_ShouldDelegateToTestExecutor()
{
// Arrange
var testExecutor = CreateMockTestExecutor();
var metricsCalculator = CreateMockMetricsCalculator();
var tuningNav = new TuningNavigation(
testExecutor.Object,
metricsCalculator.Object);
// Act
tuningNav.EmergencyStop();
// Assert
testExecutor.Verify(e => e.EmergencyStop(), Times.Once);
}
[Fact]
public async Task ExecuteTestAsync_ShouldInvokeTelemetryUpdateEvent()
{
// Arrange
var testExecutor = CreateMockTestExecutor();
var metricsCalculator = CreateMockMetricsCalculator();
var telemetryReceived = false;
var tuningNav = new TuningNavigation(
testExecutor.Object,
metricsCalculator.Object);
tuningNav.OnTelemetryUpdate += (telemetry) => telemetryReceived = true;
testExecutor.Setup(e => e.ExecuteAsync(
It.IsAny<TestScenario>(),
It.IsAny<NavigationParameterSet>(),
It.IsAny<Action<TelemetryData>>(),
It.IsAny<CancellationToken>()))
.Callback<TestScenario, NavigationParameterSet, Action<TelemetryData>?, CancellationToken>(
(s, p, callback, ct) =>
{
callback?.Invoke(TestHelpers.CreateTelemetryData());
})
.ReturnsAsync(new TestExecutionResult
{
Status = TestStatus.Completed,
TelemetryData = new List<TelemetryData>()
});
var scenario = TestHelpers.CreateStraightLineScenario();
var parameters = TestHelpers.CreateDefaultParameterSet();
// Act
await tuningNav.ExecuteTestAsync(scenario, parameters);
// Assert
telemetryReceived.Should().BeTrue();
}
}