feat: 公开宿主组合加载 API、Forge 风格依赖版本区间与加载顺序、程序集解析策略
- ShrinkModComponentDiscovery.Discover 公开:只发现、不执行生命周期 - ShrinkModLoader.LoadComposed / ShrinkModCordisRuntime.ApplyComposedAsync:宿主提供元数据组合加载,支持纯数据包与 IShrinkModWrapper 公开真实实例 - 公开 TryAttachEventBusInstance,随生命周期逆操作释放 - 依赖新增 VersionRange(Maven/Forge 区间语法)与 Ordering(Before/After) - AssemblyResolvePolicy 可限制跨模组程序集解析
This commit is contained in:
1 parent
5398036d3b
commit
d15bf00512
29 files changed
+917
-132
No files matched your search
@@ -0,0 +1,9 @@
|
||||
# 独立生命周期回归
|
||||
|
||||
从包根运行:
|
||||
|
||||
```powershell
|
||||
dotnet test Tools~/HostTests/Tests/Tests.csproj --configuration Release
|
||||
```
|
||||
|
||||
此 harness 编译包内真实 Unity Loader、ContextHost、Registry 和测试源码,使用已发布的 Context.Core 0.3.1、EventBus 2.3.1 与 UniTask。测试替身只覆盖 Unity 对象创建、日志、时钟和编辑器包路径查询;不模拟 Unity PlayerLoop、程序集导入或 IL 编织。生成订阅测试直接实现生成合同。原生 Unity EditMode 测试仍使用 `Tests` asmdef。
|
||||
@@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework><AssemblyName>ShrinkModFramework.Runtime</AssemblyName>
|
||||
<Nullable>disable</Nullable><ShrinkCodeGenEnabled>false</ShrinkCodeGenEnabled>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="../../../Runtime/**/*.cs" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="ShrinkSDK.Context.Core" Version="0.3.1" />
|
||||
<PackageReference Include="ShrinkSDK.EventBus" Version="2.3.1" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
// Minimal engine boundary for .NET lifecycle tests; not a Unity runtime replacement.
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
|
||||
namespace UnityEngine
|
||||
{
|
||||
public class Object
|
||||
{
|
||||
public static implicit operator bool(Object value) => value != null;
|
||||
public static void DestroyImmediate(Object value)
|
||||
{
|
||||
if (value is GameObject go) foreach (var component in go.Components)
|
||||
component.GetType().GetMethod("OnDestroy", BindingFlags.NonPublic | BindingFlags.Instance)?.Invoke(component, null);
|
||||
}
|
||||
public static void Destroy(Object value) => DestroyImmediate(value);
|
||||
public static void DontDestroyOnLoad(Object value) { }
|
||||
}
|
||||
public class ScriptableObject : Object { public static T CreateInstance<T>() where T : ScriptableObject, new() => new T(); }
|
||||
public class MonoBehaviour : Object { public GameObject gameObject; }
|
||||
public class GameObject : Object
|
||||
{
|
||||
internal readonly List<MonoBehaviour> Components = new();
|
||||
public GameObject(string name) { }
|
||||
public T AddComponent<T>() where T : MonoBehaviour, new() { var c = new T { gameObject = this }; Components.Add(c); return c; }
|
||||
}
|
||||
public static class Resources { public static T Load<T>(string path) where T : Object => null; }
|
||||
public static class Application { public static string persistentDataPath => System.IO.Path.GetTempPath(); public static bool isPlaying => false; }
|
||||
public static class Time { private static readonly Stopwatch Clock = Stopwatch.StartNew(); public static float realtimeSinceStartup => (float)Clock.Elapsed.TotalSeconds; }
|
||||
public static class Mathf { public static float Max(float a, float b) => Math.Max(a, b); }
|
||||
public enum LogType { Error, Assert, Warning, Log, Exception }
|
||||
public static class Debug
|
||||
{
|
||||
public static event Action<LogType, string> Logged;
|
||||
public static void Log(object value) => Logged?.Invoke(LogType.Log, value?.ToString());
|
||||
public static void LogWarning(object value) => Logged?.Invoke(LogType.Warning, value?.ToString());
|
||||
public static void LogError(object value) => Logged?.Invoke(LogType.Error, value?.ToString());
|
||||
public static void LogException(Exception value) => Logged?.Invoke(LogType.Exception, value.ToString());
|
||||
}
|
||||
public class CreateAssetMenuAttribute : Attribute { public string fileName, menuName; }
|
||||
public class HeaderAttribute : Attribute { public HeaderAttribute(string text) { } }
|
||||
public class TooltipAttribute : Attribute { public TooltipAttribute(string text) { } }
|
||||
public class MinAttribute : Attribute { public MinAttribute(float value) { } }
|
||||
public class SerializeField : Attribute { }
|
||||
public class DefaultExecutionOrder : Attribute { public DefaultExecutionOrder(int value) { } }
|
||||
public enum RuntimeInitializeLoadType { SubsystemRegistration, AfterAssembliesLoaded }
|
||||
public class RuntimeInitializeOnLoadMethodAttribute : Attribute { public RuntimeInitializeOnLoadMethodAttribute(RuntimeInitializeLoadType type) { } }
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using NUnit.Framework;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityEditor.PackageManager
|
||||
{
|
||||
public class PackageInfo
|
||||
{
|
||||
public string resolvedPath;
|
||||
public static PackageInfo FindForAssembly(System.Reflection.Assembly assembly)
|
||||
{
|
||||
var directory = new DirectoryInfo(AppContext.BaseDirectory);
|
||||
while (directory != null && !File.Exists(Path.Combine(directory.FullName, "package.json"))) directory = directory.Parent;
|
||||
return new PackageInfo { resolvedPath = directory?.FullName ?? throw new DirectoryNotFoundException("Package root") };
|
||||
}
|
||||
}
|
||||
}
|
||||
namespace UnityEditor
|
||||
{
|
||||
public static class AssetDatabase
|
||||
{
|
||||
public static string[] FindAssets(string query) => Array.Empty<string>();
|
||||
public static string GUIDToAssetPath(string guid) => guid;
|
||||
}
|
||||
}
|
||||
namespace UnityEngine.TestTools
|
||||
{
|
||||
public static class LogAssert
|
||||
{
|
||||
internal static readonly Queue<(LogType type, Regex text)> Expected = new();
|
||||
public static void Expect(LogType type, Regex text) => Expected.Enqueue((type, text));
|
||||
public static void Expect(LogType type, string text) => Expect(type, new Regex(Regex.Escape(text)));
|
||||
}
|
||||
}
|
||||
[SetUpFixture]
|
||||
public class LogVerification
|
||||
{
|
||||
private readonly List<string> _unexpected = new();
|
||||
[OneTimeSetUp] public void Start() => Debug.Logged += Observe;
|
||||
private void Observe(LogType type, string text)
|
||||
{
|
||||
var expected = UnityEngine.TestTools.LogAssert.Expected;
|
||||
if (expected.Count > 0 && expected.Peek().type == type && expected.Peek().text.IsMatch(text)) expected.Dequeue();
|
||||
else if (type == LogType.Error || type == LogType.Exception) _unexpected.Add(text);
|
||||
}
|
||||
[OneTimeTearDown] public void Finish()
|
||||
{
|
||||
Debug.Logged -= Observe;
|
||||
Assert.IsEmpty(_unexpected, "Unexpected Unity-style errors");
|
||||
Assert.IsEmpty(UnityEngine.TestTools.LogAssert.Expected, "Expected log was not emitted");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework><AssemblyName>ShrinkModFramework.Tests</AssemblyName>
|
||||
<Nullable>disable</Nullable><IsPackable>false</IsPackable><ShrinkCodeGenEnabled>false</ShrinkCodeGenEnabled>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../Runtime/Runtime.csproj" />
|
||||
<Compile Include="../../../Tests/*.cs" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
|
||||
<PackageReference Include="NUnit" Version="3.14.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
Reference in new issue
Block a user