feat(cordis): 完成阶段5配置与调试工具

This commit is contained in:
2026-08-17 01:58:46 +08:00
parent 8738e633ee
commit 5765f411e9
29 changed files with 1322 additions and 18 deletions
@@ -0,0 +1,113 @@
#nullable enable
using System;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using NUnit.Framework;
using ShrinkApp;
using UnityEngine;
using Object = UnityEngine.Object;
namespace ShrinkContext.AppAdapter.Tests
{
public class CompositionProfileTests
{
[Test]
public void JsonRoundTrip_PreservesEntryIsolationAndInterceptMetadata()
{
var document = new ShrinkAppCompositionDocument(false, new[]
{
new ShrinkAppCompositionEntry(
"app.a",
enabled: false,
isolate: new[] { new ShrinkAppCompositionIsolate("service", "community") },
intercept: new[]
{
new ShrinkAppCompositionIntercept("service", new[]
{
new ShrinkAppCompositionMetadata("access", "read-only")
})
})
});
var restored = ShrinkAppCompositionDocument.FromJson(document.ToJson());
Assert.IsFalse(restored.includeUnlistedEntries);
Assert.AreEqual(1, restored.entries.Count);
Assert.IsFalse(restored.entries[0].enabled);
Assert.AreEqual("community", restored.entries[0].isolate[0].realm);
Assert.AreEqual("read-only", restored.entries[0].intercept[0].metadata[0].value);
}
[Test]
public void ApplyComposition_RestrictsEntriesAndKeepsSettingsDisabledModules()
{
var settings = ScriptableObject.CreateInstance<ShrinkAppSettings>();
settings.disabledModuleIds = new[] { "app.a" };
var host = new ShrinkAppLoaderHost(settings, new IShrinkAppModuleInstaller[]
{
new ProfileInstaller("app.a"),
new ProfileInstaller("app.b")
});
try
{
host.ApplyComposition(new ShrinkAppCompositionDocument(false, new[]
{
new ShrinkAppCompositionEntry("app.a", enabled: true)
}));
TestAwait.Run(host.StartAsync());
Assert.IsTrue(host.IsModuleDisabled("app.a"),
"profile enabled 不应覆盖 ShrinkAppSettings 的显式禁用");
Assert.IsTrue(host.IsModuleDisabled("app.b"), "未列出的条目应被显式组合排除");
Assert.IsFalse(host.TryGetModuleFiber("app.a", out _));
Assert.IsFalse(host.TryGetModuleFiber("app.b", out _));
Assert.Throws<InvalidOperationException>(() =>
TestAwait.Run(host.SetModuleDisabledAsync("app.b", false)));
}
finally
{
if (host.IsRunning)
TestAwait.Run(host.ShutdownAsync());
Object.DestroyImmediate(settings);
}
}
[Test]
public void ApplyComposition_RejectsUnknownFactoryBeforeStarting()
{
var settings = ScriptableObject.CreateInstance<ShrinkAppSettings>();
try
{
var host = new ShrinkAppLoaderHost(settings, new[] { new ProfileInstaller("app.a") });
var error = Assert.Throws<ShrinkLoaderException>(() => host.ApplyComposition(
new ShrinkAppCompositionDocument(false, new[]
{
new ShrinkAppCompositionEntry("app.unknown")
})));
StringAssert.Contains("no registered module/component factory", error!.Message);
}
finally
{
Object.DestroyImmediate(settings);
}
}
private sealed class ProfileInstaller : IShrinkAppModuleInstaller
{
public ProfileInstaller(string moduleId)
{
ModuleId = moduleId;
}
public string ModuleId { get; }
public int Order => 0;
public IReadOnlyList<string> DependsOn => Array.Empty<string>();
public void RegisterServices(ShrinkAppContext context)
{
}
public UniTask InitializeAsync(ShrinkAppContext context) => UniTask.CompletedTask;
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ef8ea53988c89c848b9bd7e147ee34bf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,27 @@
#nullable enable
using NUnit.Framework;
using ShrinkContext.AppAdapter.Editor;
namespace ShrinkContext.AppAdapter.Tests
{
public class ContextBenchmarkTests
{
[Test]
public void Benchmark_UsesIndexedCandidatesAndRestoresEveryFailedReplacement()
{
var report = TestAwait.Run(ShrinkContextBenchmarkRunner.RunAsync(
unrelatedFiberCount: 10,
reloadIterations: 3,
failureIterations: 2));
Assert.AreEqual(3, report.ConsumerApplyCount);
Assert.AreEqual(12L, report.NotificationDispatchCount);
Assert.AreEqual(12L, report.NotificationCandidateVisits,
"每次 provider 生命周期有四次键通知,每次只访问目标 consumer");
Assert.Less(report.NotificationCandidateVisits, report.NaiveFiberVisits);
Assert.AreEqual(2, report.RestoredFailureCount);
Assert.GreaterOrEqual(report.NotifyMilliseconds, 0d);
Assert.GreaterOrEqual(report.RestoreMilliseconds, 0d);
}
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f3f54c253e16c954097f47fdd34e5f28
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -28,6 +28,7 @@ namespace ShrinkContext.AppAdapter.Tests
{
host = new ShrinkAppLoaderHost(settings);
ShrinkAppBasicContextComposition.Configure(host);
host.ApplyComposition(ShrinkAppBasicContextComposition.CreateDefaultDocument());
TestAwait.Run(host.StartAsync());
@@ -170,5 +170,10 @@ namespace ShrinkContext.AppAdapter.Tests
{
task.GetAwaiter().GetResult();
}
public static T Run<T>(UniTask<T> task)
{
return task.GetAwaiter().GetResult();
}
}
}
@@ -3,6 +3,7 @@
"rootNamespace": "ShrinkContext.AppAdapter.Tests",
"references": [
"ShrinkContext.AppAdapter.Runtime",
"ShrinkContext.AppAdapter.Editor",
"ShrinkContext.Core.Runtime",
"ShrinkApp.Core.Runtime",
"ShrinkApp.Starter.Basic.Runtime",