fix: restore existing WeChat instance and add M6 validation tools
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
using WxAgent.Core;
|
||||
using Xunit;
|
||||
|
||||
namespace WxAgent.Core.Tests;
|
||||
|
||||
public sealed class UiDiagnosticsTests
|
||||
{
|
||||
[Fact]
|
||||
public void TraversalGuard_StopsCyclesBeforeRecursing()
|
||||
{
|
||||
var guard = new UiTraversalGuard();
|
||||
Assert.True(guard.TryVisit("root", 0));
|
||||
Assert.True(guard.TryVisit("child", 1));
|
||||
Assert.False(guard.TryVisit("root", 2));
|
||||
Assert.True(guard.CycleDetected);
|
||||
Assert.Equal(2, guard.NodeCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TraversalGuard_BoundsDepthAndNodesEvenWithMissingRuntimeIds()
|
||||
{
|
||||
var depth = new UiTraversalGuard(maxDepth: 2);
|
||||
Assert.True(depth.TryVisit("", 0));
|
||||
Assert.False(depth.TryVisit("", 3));
|
||||
Assert.True(depth.Truncated);
|
||||
var count = new UiTraversalGuard(maxNodes: 2);
|
||||
Assert.True(count.TryVisit("", 0));
|
||||
Assert.True(count.TryVisit("", 1));
|
||||
Assert.False(count.TryVisit("", 2));
|
||||
Assert.Equal(2, count.NodeCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SnapshotComparison_DetectsRequiredControlRegressionWithoutLeakingDynamicIds()
|
||||
{
|
||||
var baseline = Root(WechatLocators.RequiredAutomationIds);
|
||||
var missing = WechatLocators.MessageList;
|
||||
var current = Root(WechatLocators.RequiredAutomationIds.Where(id => id != missing).Append("[id:private-hash]").Append("new_static_control"));
|
||||
var report = UiSnapshotComparison.Compare(baseline, current);
|
||||
Assert.Equal([missing], report.MissingRequiredControls);
|
||||
Assert.Equal([missing], report.RemovedAutomationIds);
|
||||
Assert.Equal(["new_static_control"], report.AddedAutomationIds);
|
||||
}
|
||||
|
||||
private static UiNodeSnapshot Root(IEnumerable<string> ids) =>
|
||||
new("Window", "", "", true, "0,0,800,600", ids.Select(id => new UiNodeSnapshot("Pane", "", id, true, "", [])).ToArray());
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
using WxAgent.Core;
|
||||
using Xunit;
|
||||
|
||||
namespace WxAgent.Core.Tests;
|
||||
|
||||
public sealed class WechatStabilityTests
|
||||
{
|
||||
[Fact]
|
||||
public void Deduper_HandlesTenThousandMessagesAndEvictsOldEntries()
|
||||
{
|
||||
var deduper = new BoundedMessageDeduper(2048);
|
||||
for (var index = 0; index < 10_000; index++)
|
||||
Assert.True(deduper.TryAdd($"message-{index}"));
|
||||
|
||||
Assert.False(deduper.TryAdd("message-9999"));
|
||||
Assert.True(deduper.TryAdd("message-0"));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Qt51514QWindowIcon", "微信", true)]
|
||||
[InlineData("Qt6QWindowIcon", "微信", true)]
|
||||
[InlineData("Qt51514QWindowIcon", "文件传输助手", false)]
|
||||
[InlineData("Qt51514WxTrayIconMessageWindowClass", "微信", false)]
|
||||
[InlineData("Notepad", "微信", false)]
|
||||
public void NativeWindowMatching_RejectsSubwindowsAndOtherApplications(string className, string title, bool expected)
|
||||
{
|
||||
Assert.Equal(expected, WechatLocators.IsNativeMainWindow(className, title));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("微信", true)]
|
||||
[InlineData("微信\n收到新消息", true)]
|
||||
[InlineData("WeChat", true)]
|
||||
[InlineData("企业微信", false)]
|
||||
[InlineData("微信测试脚本", false)]
|
||||
public void TrayMatching_IsExplicit(string name, bool expected) =>
|
||||
Assert.Equal(expected, WechatLocators.IsWechatTrayName(name));
|
||||
|
||||
[Fact]
|
||||
public void StabilityAnalyzer_DetectsAgentLeakAndFinalError()
|
||||
{
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
var first = Sample(now, 100, 10, 1);
|
||||
var last = first with { Timestamp = now.AddHours(1), AgentHandleCount = 101, Errors = [WxAgentErrorCode.SessionLocked] };
|
||||
var report = WechatStabilityAnalyzer.Analyze(now, last.Timestamp, [first, last], 0, 0);
|
||||
Assert.False(report.Healthy);
|
||||
Assert.Contains("agent-handle-growth-over-100", report.Findings);
|
||||
Assert.Contains("final-error:SessionLocked", report.Findings);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Deduper_RejectsInvalidCapacity()
|
||||
{
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => new BoundedMessageDeduper(0));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StabilityAnalyzer_ReportsHealthyBoundedGrowth()
|
||||
{
|
||||
var started = DateTimeOffset.UtcNow;
|
||||
var samples = new[]
|
||||
{
|
||||
Sample(started, 100_000_000, 100, 20),
|
||||
Sample(started.AddMinutes(1), 110_000_000, 120, 22)
|
||||
};
|
||||
|
||||
var report = WechatStabilityAnalyzer.Analyze(started, started.AddMinutes(1), samples, 5, 1);
|
||||
|
||||
Assert.True(report.Healthy);
|
||||
Assert.Equal(10_000_000, report.WorkingSetGrowthBytes);
|
||||
Assert.Empty(report.Findings);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StabilityAnalyzer_AllowsTransientWindowLossAfterReconnect()
|
||||
{
|
||||
var started = DateTimeOffset.UtcNow;
|
||||
var samples = new[]
|
||||
{
|
||||
Sample(started, 100_000_000, 100, 20),
|
||||
Sample(started.AddMinutes(1), 0, 0, 0, windowFound: false),
|
||||
Sample(started.AddMinutes(2), 105_000_000, 110, 21)
|
||||
};
|
||||
|
||||
var report = WechatStabilityAnalyzer.Analyze(started, started.AddMinutes(2), samples, 0, 1);
|
||||
|
||||
Assert.True(report.Healthy);
|
||||
Assert.DoesNotContain("wechat-window-unavailable", report.Findings);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StabilityAnalyzer_FlagsWindowLossAndResourceGrowth()
|
||||
{
|
||||
var started = DateTimeOffset.UtcNow;
|
||||
var samples = new[]
|
||||
{
|
||||
Sample(started, 100_000_000, 100, 20),
|
||||
Sample(started.AddHours(1), 400_000_000, 700, 150, windowFound: false)
|
||||
};
|
||||
|
||||
var report = WechatStabilityAnalyzer.Analyze(started, started.AddHours(1), samples, 0, 0);
|
||||
|
||||
Assert.False(report.Healthy);
|
||||
Assert.Contains("wechat-window-unavailable", report.Findings);
|
||||
Assert.Contains("working-set-growth-over-256mb", report.Findings);
|
||||
Assert.Contains("handle-growth-over-500", report.Findings);
|
||||
Assert.Contains("thread-growth-over-100", report.Findings);
|
||||
}
|
||||
|
||||
private static WechatStabilitySample Sample(
|
||||
DateTimeOffset timestamp,
|
||||
long memory,
|
||||
int handles,
|
||||
int threads,
|
||||
bool windowFound = true) =>
|
||||
new(timestamp, 1, memory, handles, threads, windowFound, [], 0, 0);
|
||||
}
|
||||
Reference in New Issue
Block a user