85 lines
4.6 KiB
C#
85 lines
4.6 KiB
C#
using Microsoft.CodeAnalysis;
|
|
using Microsoft.CodeAnalysis.CSharp;
|
|
using NUnit.Framework;
|
|
using ShrinkSDK.Inspect;
|
|
using Index = ShrinkSDK.Inspect.Index;
|
|
|
|
public class NavigationTests
|
|
{
|
|
private string _root = null!;
|
|
[SetUp] public void SetUp() { _root = Path.Combine(Path.GetTempPath(), "ShrinkInspectTests", Guid.NewGuid().ToString("N")); Directory.CreateDirectory(_root); }
|
|
[TearDown] public void TearDown() => Directory.Delete(_root, true);
|
|
|
|
[Test] public void ExactOverloadsAndVirtualCandidatesStayDistinct()
|
|
{
|
|
var index = Analyze("public class A { public void Start() { Call(1); } public void Call(int x) {} public void Call(string x) {} public virtual void Tick() {} public void Run() { Tick(); } }");
|
|
Assert.That(index.Relations.Any(r => r.From == "M:A.Start" && r.To == "M:A.Call(System.Int32)" && r.Evidence == "static"));
|
|
Assert.That(index.Relations.Any(r => r.To == "M:A.Call(System.String)"), Is.False);
|
|
Assert.That(index.Relations.Any(r => r.To == "M:A.Tick" && r.Evidence == "candidate"));
|
|
}
|
|
|
|
[Test] public void UnresolvedCallIsNotInventedAsStatic()
|
|
{
|
|
var index = Analyze("class A { void Run() { Missing.Go(); } }");
|
|
Assert.That(index.Relations.Any(r => r.To == "Missing.Go" && r.Evidence == "unresolved"));
|
|
Assert.That(index.Coverage, Is.Not.Empty);
|
|
}
|
|
|
|
[Test] public void FrameworkEventEdgesBridgePublisherAndSubscriber()
|
|
{
|
|
var index = Analyze("""
|
|
using System;
|
|
namespace ShrinkEventBus { public class ShrinkSubscribeAttribute : Attribute {} public static class EventBus { public static void Post<T>(T value) {} } }
|
|
public struct Killed {}
|
|
public class Combat { public void Kill() { ShrinkEventBus.EventBus.Post(new Killed()); } }
|
|
public class Rage { [ShrinkEventBus.ShrinkSubscribe] public void Gain(Killed value) {} }
|
|
""");
|
|
var edges = InspectCommand.Walk(index, "M:Combat.Kill", 2, null).ToArray();
|
|
Assert.That(edges.Any(e => e.Kind == "publishes" && e.To == "type:Killed"));
|
|
Assert.That(edges.Any(e => e.Kind == "subscribes" && e.To == "M:Rage.Gain(Killed)" && e.Evidence == "candidate"));
|
|
}
|
|
|
|
[Test] public void OpcodeAndRouteUniquenessAreIndependent()
|
|
{
|
|
var index = Analyze("""
|
|
using System;
|
|
namespace ShrinkNetwork { public class ShrinkNetworkMessageAttribute : Attribute { public ShrinkNetworkMessageAttribute(int id, string route) {} } }
|
|
[ShrinkNetwork.ShrinkNetworkMessage(1,"a")] class A {}
|
|
[ShrinkNetwork.ShrinkNetworkMessage(1,"b")] class B {}
|
|
[ShrinkNetwork.ShrinkNetworkMessage(2,"a")] class C {}
|
|
""");
|
|
SemanticIndex.CheckRelations(index);
|
|
Assert.That(index.Findings.Any(f => f.Message.Contains("opcode:1")));
|
|
Assert.That(index.Findings.Any(f => f.Message.Contains("route:a")));
|
|
}
|
|
|
|
[Test] public void CapabilityMatchesDoNotImplyMissingAbilitiesAreAbsent()
|
|
{
|
|
Directory.CreateDirectory(Path.Combine(_root, "Inventory"));
|
|
File.WriteAllText(Path.Combine(_root, "Inventory", "inventory.asmdef"), "{\"name\":\"Inventory\"}");
|
|
File.WriteAllText(Path.Combine(_root, "Inventory", "README.md"), """
|
|
<!-- shrink:capabilities -->
|
|
Capability: 配置物品容量
|
|
Aliases: 背包 inventory 容量
|
|
Limits: 不负责装备属性
|
|
Extension: SlotOptions
|
|
Evidence: Inventory.cs
|
|
<!-- /shrink:capabilities -->
|
|
""");
|
|
Directory.CreateDirectory(Path.Combine(_root, "Combat"));
|
|
File.WriteAllText(Path.Combine(_root, "Combat", "combat.asmdef"), "{\"name\":\"Combat\"}");
|
|
var index = new Index { Root = _root }; ModuleCatalog.Load(index);
|
|
Assert.That(ModuleCatalog.Search(index, "背包", null).Single().Limits, Is.EqualTo("不负责装备属性"));
|
|
Assert.That(index.Modules.Single(m => m.Name == "Combat").SemanticDescriptionComplete, Is.False);
|
|
Assert.That(ModuleCatalog.Search(index, "怒气", null), Is.Empty);
|
|
}
|
|
|
|
private Index Analyze(string code)
|
|
{
|
|
var references = ((string)AppContext.GetData("TRUSTED_PLATFORM_ASSEMBLIES")!).Split(Path.PathSeparator).Select(p => MetadataReference.CreateFromFile(p));
|
|
var compilation = CSharpCompilation.Create("Fixture", [CSharpSyntaxTree.ParseText(code, path: Path.Combine(_root, "Fixture.cs"))], references,
|
|
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
|
|
var index = new Index { Root = _root }; SemanticIndex.Analyze(index, compilation); return index;
|
|
}
|
|
}
|