Files
lmxopcua/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/ScriptAnalysis/AnalysisCompilationReuseTests.cs
T
Joseph Doherty b211911f98
v2-ci / unit-tests (push) Failing after 13m22s
test: migrate AdminUI/ControlPlane/Runtime/Host tests to xunit.v3 (#756)
Akka.TestKit.Xunit2 -> Akka.TestKit.Xunit 1.5.62 (xunit.v3 TestKit; needs xunit.v3 3.2.2, set via
VersionOverride on the four projects). Removes the xunit 2.9.3 pin. IAsyncLifetime returns ValueTask,
SqlServerFactAttribute gains the source-info ctor, and xUnit1051 call sites pass
TestContext.Current.CancellationToken. Test counts unchanged.

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-10 08:57:33 -05:00

59 lines
2.8 KiB
C#

using Microsoft.CodeAnalysis;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.AdminUI.ScriptAnalysis;
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.ScriptAnalysis;
/// <summary>
/// R04:P2 — the editor used to build a brand-new <c>CSharpCompilation</c> (and re-bind the whole sandbox
/// reference set) on every completion / hover / signature-help / diagnostics request. Each analysis now
/// derives from ONE process-wide base compilation via <c>ReplaceSyntaxTree</c>, so the referenced-assembly
/// binding is done once and shared.
/// </summary>
public sealed class AnalysisCompilationReuseTests
{
[Fact]
public void Two_services_share_the_bound_reference_set()
{
// Two separate scopes (two HTTP requests) analysing different sources.
var c1 = new ScriptAnalysisService().CompilationFor("return 1;");
var c2 = new ScriptAnalysisService().CompilationFor("return 2;");
// Positive control: the two compilations really carry different user sources.
c1.SyntaxTrees.Single().ToString().ShouldContain("return 1;");
c2.SyntaxTrees.Single().ToString().ShouldContain("return 2;");
c1.ShouldNotBeSameAs(c2);
// Derived from one bound base: the corlib symbol is the SAME instance, not a re-bound copy.
c1.GetSpecialType(SpecialType.System_Object)
.ShouldBeSameAs(c2.GetSpecialType(SpecialType.System_Object));
c1.GetSpecialType(SpecialType.System_Object)
.ShouldBeSameAs(ScriptAnalysisService.BaseCompilation.GetSpecialType(SpecialType.System_Object));
}
[Fact]
public void Repeated_analysis_in_one_scope_replaces_the_tree_rather_than_adding_one()
{
var svc = new ScriptAnalysisService();
svc.CompilationFor("return 1;");
var second = svc.CompilationFor("return 2;");
second.SyntaxTrees.Count().ShouldBe(1);
second.SyntaxTrees.Single().ToString().ShouldContain("return 2;");
second.SyntaxTrees.Single().ToString().ShouldNotContain("return 1;");
}
[Fact]
public void Diagnostics_are_unchanged_by_reuse()
{
// A clean script stays clean and an error stays an error across interleaved analyses on one service
// — a stale tree leaking from the previous call would surface here.
var svc = new ScriptAnalysisService();
svc.Diagnose(new DiagnoseRequest("return ctx.Nope();"), TestContext.Current.CancellationToken).Markers.ShouldContain(m => m.Severity == 8);
svc.Diagnose(new DiagnoseRequest("""return (double)ctx.GetTag("A").Value * 2.0;"""), TestContext.Current.CancellationToken).Markers.ShouldBeEmpty();
svc.Diagnose(new DiagnoseRequest("var a = 1;\nreturn ctx.Nope();"), TestContext.Current.CancellationToken).Markers
.ShouldContain(m => m.Severity == 8 && m.StartLineNumber == 2);
}
}