독립 검증 도구 추가와 앱 상태 책임 분리

This commit is contained in:
2026-09-06 18:09:11 +09:00
parent 0f0431de5a
commit 002a922e1b
62 changed files with 3454 additions and 2008 deletions
@@ -0,0 +1,31 @@
using NUnit.Framework;
using ProjectQuint.Probes;
namespace ProjectQuint.Tests
{
public sealed class CoreBenchmarkTests
{
[Test]
public void Benchmark_CheckpointsAreIncompleteUntilEverySampleFinishes()
{
var checkpoints = 0;
var report = CoreBenchmark.Run(100, 42, 60, 3, checkpoint =>
{
++checkpoints;
Assert.That(checkpoint.Complete, Is.False);
Assert.That(checkpoint.Samples.Length, Is.EqualTo(checkpoints));
Assert.That(checkpoint.Samples[0].Judgement.Finished, Is.True);
});
Assert.That(report.Complete, Is.True);
Assert.That(checkpoints, Is.EqualTo(3));
Assert.That(report.Samples.Length, Is.EqualTo(3));
Assert.That(report.WarmupIterations, Is.EqualTo(3));
}
[TestCase(0)]
[TestCase(101)]
public void Benchmark_RejectsInvalidIterationCount(int iterations) =>
Assert.Throws<System.ArgumentOutOfRangeException>(() =>
CoreBenchmark.Run(100, 42, 60, iterations, _ => { }));
}
}
@@ -0,0 +1,44 @@
using System.Linq;
using NUnit.Framework;
using ProjectQuint.Probes;
namespace ProjectQuint.Tests
{
public sealed class CoreReplayTests
{
[Test]
public void SyntheticWorkload_IsReproducibleWithoutMedia()
{
var first = SyntheticWorkload.Create(500, 42);
var second = SyntheticWorkload.Create(500, 42);
Assert.That(first.Events, Is.EqualTo(second.Events));
Assert.That(first.Notes.Select(n => (n.Id, n.Lane, n.StartTime, n.EndTime)),
Is.EqualTo(second.Notes.Select(n => (n.Id, n.Lane, n.StartTime, n.EndTime))));
}
[TestCase(30)]
[TestCase(60)]
[TestCase(144)]
[TestCase(240)]
public void Replay_KeepsTheSameJudgementsAcrossFrameSchedules(int frameRate)
{
var workload = SyntheticWorkload.Create(1000, 42);
var expected = ReplayRunner.Run(workload, 1000).Judgement;
var actual = ReplayRunner.Run(workload, frameRate).Judgement;
Assert.That(actual, Is.EqualTo(expected));
Assert.That(actual.Finished, Is.True);
Assert.That(actual.MissCount, Is.GreaterThan(0));
Assert.That(actual.JudgedUnits, Is.GreaterThan(workload.Notes.Length));
}
[TestCase(0)]
[TestCase(-1)]
public void InvalidWorkloadSize_IsRejected(int notes) =>
Assert.Throws<System.ArgumentOutOfRangeException>(() => SyntheticWorkload.Create(notes, 42));
[Test]
public void InvalidFrameRate_IsRejected() =>
Assert.Throws<System.ArgumentOutOfRangeException>(() =>
ReplayRunner.Run(SyntheticWorkload.Create(10, 42), 0));
}
}
@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<LangVersion>9.0</LangVersion>
<IsTestProject>true</IsTestProject>
<IsPackable>false</IsPackable>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../ProjectQuint.Core/ProjectQuint.Core.csproj" />
<ProjectReference Include="../ProjectQuint.Probes/ProjectQuint.Probes.csproj" />
<!-- NUnit 3 matches the assertions supported by the Unity test runner. -->
<PackageReference Include="NUnit" Version="3.14.0" />
<PackageReference Include="NUnit3TestAdapter" Version="6.3.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.9.0" />
<Compile Include="../../Assets/ProjectQuint/Tests/Core/**/*.cs" />
<Compile Include="../../Assets/ProjectQuint/Runtime/Unity/Features/Flow/AppFlowController.cs" />
<Compile Include="../../Assets/ProjectQuint/Tests/EditMode/HomeNavigationTests.cs" />
<Compile Include="../../Assets/ProjectQuint/Runtime/Unity/Features/Updates/*.cs" />
<Compile Include="../../Assets/ProjectQuint/Tests/EditMode/UpdateSessionControllerTests.cs" />
<Compile Include="../../Assets/ProjectQuint/Runtime/Unity/Features/Profile/ProfileSessionController.cs" />
<Compile Include="../../Assets/ProjectQuint/Tests/EditMode/ProfileSessionControllerTests.cs" />
<Compile Include="CoreReplayTests.cs" />
<Compile Include="CoreBenchmarkTests.cs" />
</ItemGroup>
</Project>
@@ -0,0 +1,128 @@
{
"version": 1,
"dependencies": {
"net10.0": {
"Microsoft.NET.Test.Sdk": {
"type": "Direct",
"requested": "[18.9.0, )",
"resolved": "18.9.0",
"contentHash": "xIzVXa/VpKXqDWzyC/5Hw8JbFY5u9k3Jc53CpcjBiOXvkQGio484LD9FNwOiGUSMDcYL3Rcw9sNgGi5WrswlPQ==",
"dependencies": {
"Microsoft.CodeCoverage": "18.9.0",
"Microsoft.TestPlatform.TestHost": "18.9.0"
}
},
"NUnit": {
"type": "Direct",
"requested": "[3.14.0, )",
"resolved": "3.14.0",
"contentHash": "R7iPwD7kbOaP3o2zldWJbWeMQAvDKD0uld27QvA3PAALl1unl7x0v2J7eGiJOYjimV/BuGT4VJmr45RjS7z4LA==",
"dependencies": {
"NETStandard.Library": "2.0.0"
}
},
"NUnit3TestAdapter": {
"type": "Direct",
"requested": "[6.3.0, )",
"resolved": "6.3.0",
"contentHash": "8ZD+0dIfDHsL4w4Gl/LPxWH0O4ky3hFXqTZjY+ry0ThI3cMVErr9Tjxal5O5wJxGXh50a9qkDEAWVca8KQYCEw==",
"dependencies": {
"Microsoft.Extensions.DependencyModel": "8.0.2",
"Microsoft.Testing.Extensions.VSTestBridge": "2.3.3",
"Microsoft.Testing.Platform.MSBuild": "2.3.3"
}
},
"Microsoft.ApplicationInsights": {
"type": "Transitive",
"resolved": "2.23.0",
"contentHash": "nWArUZTdU7iqZLycLKWe0TDms48KKGE6pONH2terYNa8REXiqixrMOkf1sk5DHGMaUTqONU2YkS4SAXBhLStgw=="
},
"Microsoft.CodeCoverage": {
"type": "Transitive",
"resolved": "18.9.0",
"contentHash": "MtegCIKGuG0r/LCdIjoR1HgzCGIUBhR3aNdbtQpts5vMXQuqBDZ2Jsd2uFKoHFM2XrQV6ZrDf3F5oLi3SLTp8w=="
},
"Microsoft.Extensions.DependencyModel": {
"type": "Transitive",
"resolved": "8.0.2",
"contentHash": "mUBDZZRgZrSyFOsJ2qJJ9fXfqd/kXJwf3AiDoqLD9m6TjY5OO/vLNOb9fb4juC0487eq4hcGN/M2Rh/CKS7QYw=="
},
"Microsoft.NETCore.Platforms": {
"type": "Transitive",
"resolved": "1.1.0",
"contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A=="
},
"Microsoft.Testing.Extensions.Telemetry": {
"type": "Transitive",
"resolved": "2.3.3",
"contentHash": "nY8ceQyPWB9TRE1WE5Oe/sks2e10SxgPv61vBHsFYpgCeDtNwhpMZwmL29GklO3/etTdOsLdrCmNr4zJWaR2fg==",
"dependencies": {
"Microsoft.ApplicationInsights": "2.23.0",
"Microsoft.Testing.Platform": "2.3.3"
}
},
"Microsoft.Testing.Extensions.TrxReport.Abstractions": {
"type": "Transitive",
"resolved": "2.3.3",
"contentHash": "dceVNxnfTEjKnrIreLcMfkgrzzBY8kgCCJX5NAv7Lq/6vPlTP4VB5zxKeuYy0yfCoBtWuPkLjUG6qtOBMfpypA==",
"dependencies": {
"Microsoft.Testing.Platform": "2.3.3"
}
},
"Microsoft.Testing.Extensions.VSTestBridge": {
"type": "Transitive",
"resolved": "2.3.3",
"contentHash": "JLovZjH6K10YiHO/BcidsaSHo/M2DfOe9FDjxjFdV6aoHO6VYTTYlLBNcyLJSo20q5gDTmNvmxYVH39A942aJA==",
"dependencies": {
"Microsoft.TestPlatform.ObjectModel": "18.4.0",
"Microsoft.Testing.Extensions.Telemetry": "2.3.3",
"Microsoft.Testing.Extensions.TrxReport.Abstractions": "2.3.3",
"Microsoft.Testing.Platform": "2.3.3"
}
},
"Microsoft.Testing.Platform": {
"type": "Transitive",
"resolved": "2.3.3",
"contentHash": "ENbH4BQh9riXtOKc25KKITfiGGWhWMBJA7pZuNaF9zxzzSaVkp5AMeZxGQ6sXYaR6xb724NLz985Is6XIYqLSg=="
},
"Microsoft.Testing.Platform.MSBuild": {
"type": "Transitive",
"resolved": "2.3.3",
"contentHash": "iVAvNbZ5JPDZSrTcIrCDoCsAkzjDxwDEt2mDuMd8ng1P6e2P2NCd0g0BsoBVG+nJLSmK//V+yeEvjCa3E2fxHw==",
"dependencies": {
"Microsoft.Testing.Platform": "2.3.3"
}
},
"Microsoft.TestPlatform.ObjectModel": {
"type": "Transitive",
"resolved": "18.9.0",
"contentHash": "Fz/qXC52VXXopzHj7v2reCKcCqSxkTDCkEDfkNAH0vni/7qK/KLMiEYtRmnUanxO2F2g2zZZ60qCpxF0AF7URA=="
},
"Microsoft.TestPlatform.TestHost": {
"type": "Transitive",
"resolved": "18.9.0",
"contentHash": "Oq+Pma/J86aZL72t71bcESvDszDsTjUfl6PIsuDdPoKTHZfNMhKamCfLD5fKx51W/u0KozXtJo2/3fnt0sgPxg==",
"dependencies": {
"Microsoft.TestPlatform.ObjectModel": "18.9.0"
}
},
"NETStandard.Library": {
"type": "Transitive",
"resolved": "2.0.0",
"contentHash": "7jnbRU+L08FXKMxqUflxEXtVymWvNOrS8yHgu9s6EM8Anr6T/wIX4nZ08j/u3Asz+tCufp3YVwFSEvFTPYmBPA==",
"dependencies": {
"Microsoft.NETCore.Platforms": "1.1.0"
}
},
"projectquint.core": {
"type": "Project"
},
"projectquint.probes": {
"type": "Project",
"dependencies": {
"ProjectQuint.Core": "[1.0.0, )"
}
}
}
}
}