Files
Gateway-keepaliveForDotnet/tests/GatewayKeepalive.Tests/Program.cs
T
chuzhongzai 7be117057a 重做设置界面并修复登录自启:页签单选、滚轮归属、任务缺失判定、发布去掉自带运行时
设置窗口
- 改为独立非模态窗口(进任务栏、可最小化),不再被托盘浮窗挡住导致点不到
- 位置改为按屏幕可用区域居中裁剪,不再默认落在右下角
- 三个页签重构为卡片式布局:统一卡片内边距、页签行独立成行、正文左右留白、
  帮助文字按实际换行测量高度、标题栏与底栏加分隔线
- 直连 DNS 复选框、下拉框、滚动条改为自绘控件,配色与整体统一

页签
- PanelChip 的选中/强调改为值变化即重绘,修掉“切换页签后旧页签仍显示为激活、
  鼠标移上去才更新”的陈旧绘制

滚轮
- 输入框滚不动自己时把滚轮交给外层页面,单行输入框不再吞掉滚轮事件

登录自启
- 改用任务 XML + schtasks 写入,COM 失败时用 schtasks 复核;写入后读回校验
- 任务不存在按 HRESULT 判定(0x80070002 / 0x80070003 / 0x8004130F),修掉
  “关闭登录自启必定报错并回滚”(晚绑定 COM 抛的是 FileNotFoundException)
- 状态读取 COM 失败时回退到 schtasks,失败按异常类型 + HResult 去重记录

诊断
- 界面报错统一写日志;控制器记录自启读写结果;启动自检记录管理员/自启/进程/参数

发布
- 去掉 -SelfContained 分支,只保留单文件运行时依赖包,脚本与文档同步
- 新增 tools/unblock-publish.ps1,清除发布产物的网络来源标记

测试
- 新增登录自启任务 XML 用例与“任务缺失按 HRESULT 判定”回归测试(46 → 47),探测 16/16
2026-10-24 01:32:03 +08:00

80 lines
7.4 KiB
C#

namespace GatewayKeepalive.Tests;
internal static class Program
{
private static async Task<int> Main()
{
var tests = new (string Name, Func<Task> Run)[]
{
("SwitchPolicy: success threshold, cap, and recovery cooldown", SwitchPolicyTests.SuccessThresholdAndCooldownAsync),
("SwitchPolicy: failure threshold and failure cooldown", SwitchPolicyTests.FailureThresholdAndCooldownAsync),
("SwitchPolicy: failure retry has a 30 second floor", SwitchPolicyTests.FailureRetryUsesThirtySecondFloorAsync),
("SwitchPolicy: paused, fixed, and observe-only modes reset counts", SwitchPolicyTests.NonAutoModesResetCountsAsync),
("SwitchPolicy: switch, failure, and explicit reset clear counters", SwitchPolicyTests.CountsResetAfterEventsAsync),
("SwitchService: DNS failure after route write rolls back", SwitchServiceTests.DnsFailureAfterRouteWriteRollsBackAsync),
("SwitchService: egress verification failure rolls back", SwitchServiceTests.EgressFailureRollsBackAsync),
("SwitchService: rollback failure retains journal and raises RecoveryRequired", SwitchServiceTests.RollbackFailureRetainsJournalAsync),
("SwitchService: external modification is never overwritten", SwitchServiceTests.ExternalModificationIsNotOverwrittenAsync),
("SwitchService: cross-network rollback is rejected", SwitchServiceTests.CrossNetworkRollbackIsRejectedAsync),
("SwitchService: profile DNS is rejected", SwitchServiceTests.ProfileDnsIsRejectedAsync),
("SwitchService: destination gateway must be on the adapter subnet", SwitchServiceTests.GatewayMustBeOnAdapterSubnetAsync),
("SwitchService: failed baseline restore compensates original configuration", SwitchServiceTests.BaselineRestoreFailureCompensatesAsync),
("SwitchService: plan preserves an already preferred gateway metric", SwitchServiceTests.PlanPreservesPreferredMetricAsync),
("SwitchService: identical configuration skips route and DNS writes", SwitchServiceTests.SwitchSameConfigurationSkipsWritesAsync),
("SwitchService: only changed DNS is written", SwitchServiceTests.SwitchWritesOnlyChangedDnsAsync),
("SwitchService: only changed routes are written", SwitchServiceTests.SwitchWritesOnlyChangedRoutesAsync),
("SwitchService: unchanged rollback skips route and DNS writes", SwitchServiceTests.RollbackSameConfigurationSkipsWritesAsync),
("SwitchService: unchanged baseline restore skips route and DNS writes", SwitchServiceTests.BaselineRestoreSameConfigurationSkipsWritesAsync),
("GatewayController: unconfigured refresh does no probe or write", GatewayControllerTests.InitialUnconfiguredDoesNotProbeOrWriteAsync),
("GatewayController: first paused save preserves real direct DNS", GatewayControllerTests.ExplicitPausedSavePreservesRealDirectDnsAsync),
("GatewayController: external configuration is not overwritten", GatewayControllerTests.ExternalConfigurationChangeIsNotOverwrittenAsync),
("GatewayController: legacy empty session fingerprint is not writable", GatewayControllerTests.LegacySessionWithoutFingerprintIsNotWritableAsync),
("GatewayController: failed save does not become writable", GatewayControllerTests.SaveFailureAfterSessionWriteDoesNotBecomeWritableAsync),
("GatewayController: unfinished journal blocks automatic control", GatewayControllerTests.UnfinishedJournalBlocksAutomaticControlAsync),
("GatewayController: fixed rollback failure keeps fixed policy", GatewayControllerTests.FixedFailureKeepsFixedPolicyAfterRollbackAsync),
("GatewayController: concurrent refreshes are coalesced", GatewayControllerTests.ConcurrentRefreshIsCoalescedAsync),
("GatewayController: save and refresh writes are serialized", GatewayControllerTests.SaveAndRefreshAreSerializedAsync),
("GatewayController: stop restores when RestoreOnExit is enabled", GatewayControllerTests.StopRestoresWhenRequestedAsync),
("GatewayController: stop restores persisted session before first refresh", GatewayControllerTests.StopRestoresPersistedSessionBeforeFirstRefreshAsync),
("GatewayController: stop leaves network when restore is disabled", GatewayControllerTests.StopLeavesNetworkWhenRestoreIsDisabledAsync),
("GatewayController: failed stop restore leaves controller available", GatewayControllerTests.StopRestoreFailureLeavesControllerAvailableAsync),
("Recovery: failed durable Pause prevents all restore writes", RecoveryRegressionTests.RestorePauseWriteFailurePreventsNetworkWritesAsync),
("Recovery: Pause precedes network restore and survives restart", RecoveryRegressionTests.RestorePersistsPauseBeforeWritesAndRestartAsync),
("Recovery: baseline metadata failure retains journal and durable Pause", RecoveryRegressionTests.RestoreSessionFailureRetainsBaselineJournalAsync),
("Recovery: explicit rollback metadata failure retains journal and durable Pause", RecoveryRegressionTests.RestoreSessionFailureRetainsRollbackJournalAsync),
("Recovery: Stop resolves pending journal then restores original baseline", RecoveryRegressionTests.StopPendingJournalContinuesToOriginalBaselineAsync),
("Guarded switch: foreign route after preflight is preserved", RecoveryRegressionTests.ForeignRouteAfterPreflightIsProtectedAsync),
("Guarded switch: foreign DNS after preflight is preserved", RecoveryRegressionTests.ForeignDnsAfterPreflightIsProtectedAsync),
("Guarded switch: foreign route after routes and before DNS is preserved", RecoveryRegressionTests.ForeignRouteBeforeDnsIsProtectedAsync),
("Guarded switch: foreign DNS after routes and before DNS is preserved", RecoveryRegressionTests.ForeignDnsBeforeDnsIsProtectedAsync),
("NetworkComparison: every owned route, DNS, and network field participates", NetworkComparisonTests.EachOwnedFieldParticipatesAsync),
("NetworkComparison: transaction recovery accepts only exact before/intended rows", NetworkComparisonTests.RecoveryRejectsForeignRowsAsync),
("JsonStore: round trip, backup, and failed serialization preserve the last complete file", JsonStoreTests.AtomicReplacementAsync),
("Windows NativeLayout: x64 structure sizes and metric offsets", NativeLayoutTests.X64LayoutAsync),
("LoginStartup: logon task XML starts minimized with highest privileges", LoginStartupTests.LogonTaskXmlAsync),
("LoginStartup: missing-task detection follows HRESULT, not exception type", LoginStartupTests.TaskMissingDetectionAsync),
};
var failures = 0;
var started = DateTime.UtcNow;
Console.WriteLine($"Running {tests.Length} offline GatewayKeepalive tests.");
foreach (var test in tests)
{
try
{
await test.Run();
Console.WriteLine($"PASS {test.Name}");
}
catch (Exception exception)
{
failures++;
Console.WriteLine($"FAIL {test.Name}");
Console.WriteLine(exception);
}
}
Console.WriteLine($"Completed in {(DateTime.UtcNow - started).TotalSeconds:F2}s: {tests.Length - failures} passed, {failures} failed.");
return failures == 0 ? 0 : 1;
}
}