Skip to content

Commit ae8eb95

Browse files
JusterZhuclaude
andauthored
feat: simplify ClientTest/UpgradeTest for chain and cross-version integration testing (#507)
* feat: simplify ClientTest/UpgradeTest for chain and cross-version integration testing - Replace OSS/deprecated silent mode with a single mode-agnostic ClientTest entry point - Remove CLI argument branching (chain/cross-version); the SDK's DownloadPlanBuilder selects CVP vs chain automatically based on server response (IsCrossVersion + FromVersion) - Fix MSBuild CopyUpgradeTest target: stop excluding *.json so UpgradeTest.runtimeconfig.json is bundled as Update.exe, fixing 'hostpolicy.dll not found' launch error - Improve events logging (IsCrossVersion/AppType/FromVersion display in OnUpdateInfo) - Update UpgradeTest with clearer IPC flow documentation Co-Authored-By: Claude <noreply@anthropic.com> * fix: address Copilot review feedback - Replace hard-coded '%TEMP%/...' IPC path with Path.GetTempPath() at runtime - Use OS-agnostic wording for strategy comments (WindowsStrategy → OS strategy) - Use switch expression for AppType display to handle null/unknown values --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent dbf4ef1 commit ae8eb95

3 files changed

Lines changed: 76 additions & 161 deletions

File tree

tests/ClientTest/ClientTest.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
<UpgradeTestDir>..\UpgradeTest\bin\$(Configuration)\net10.0</UpgradeTestDir>
2424
</PropertyGroup>
2525
<ItemGroup>
26-
<UpgradeTestFiles Include="$(UpgradeTestDir)\**\*" Exclude="$(UpgradeTestDir)\**\*.pdb;$(UpgradeTestDir)\**\*.json" />
26+
<UpgradeTestFiles Include="$(UpgradeTestDir)\**\*" Exclude="$(UpgradeTestDir)\**\*.pdb" />
2727
</ItemGroup>
2828
<Copy SourceFiles="@(UpgradeTestFiles)"
2929
DestinationFolder="$(OutputPath)"

tests/ClientTest/Program.cs

Lines changed: 53 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,7 @@
77

88
try
99
{
10-
await RunOssClientAsync();
11-
/*var isOssMode = args.Length > 0 && args[0] == "--oss";
12-
13-
if (isOssMode)
14-
{
15-
await RunOssClientAsync();
16-
}
17-
else
18-
{
19-
await RunStandardClientAsync();
20-
}*/
10+
await RunUpdateTestAsync();
2111
}
2212
catch (Exception ex)
2313
{
@@ -27,67 +17,58 @@
2717
Environment.Exit(1);
2818
}
2919

20+
// NOTE: In the success path where a MainApp update is applied and the Upgrade
21+
// process is launched, the Client process exits inside the bootstrap —
22+
// the OS strategy's StartAppAsync() calls GracefulExit.CurrentProcessAsync().
23+
// The code below is only reached when no update is needed, or when only
24+
// Upgrade packages were applied (no MainApp IPC/launch).
3025
Console.WriteLine("Press Enter to exit...");
3126
Console.ReadLine();
3227

3328
// ═══════════════════════════════════════════════════════════════════
34-
// OSS Client mode — version JSON download → version compare → launch upgrade
29+
// Core update test — runs the full non-silent immediate update flow.
30+
//
31+
// The update mode (chain vs. cross-version) is determined entirely by
32+
// GeneralUpdate.Core's DownloadPlanBuilder, which inspects the server
33+
// response and prefers CVP when its FromVersion matches the local
34+
// client version. The test itself is mode-agnostic — configure the
35+
// GeneralSpacestation server's data to exercise either path.
3536
// ═══════════════════════════════════════════════════════════════════
36-
static async Task RunOssClientAsync()
37+
static async Task RunUpdateTestAsync()
3738
{
38-
Console.WriteLine("=== GeneralUpdate OSS Client Test ===");
39+
Console.WriteLine("=== GeneralUpdate Client Test ===");
3940
Console.WriteLine($"Started at {DateTime.Now}");
4041
Console.WriteLine($"Running from: {AppDomain.CurrentDomain.BaseDirectory}");
4142

42-
// Only secrets are supplied in code. Identity fields (MainAppName,
43-
// ClientVersion, UpdateAppName, UpdatePath) are read from
44-
// generalupdate.manifest.json by OssStrategy via
45-
// AppMetadataDiscoverer.Discover() — same as the standard flow.
46-
var updateUrl = "http://localhost:5000/packages/versions.json";
47-
var appSecretKey = "dfeb5833-975e-4afb-88f1-6278ee9aeff6";
43+
var updateUrl = "http://localhost:7391/Upgrade/Verification";
44+
var reportUrl = "http://localhost:7391/Upgrade/Report";
45+
var appSecretKey =
46+
Environment.GetEnvironmentVariable("APP_SECRET_KEY")
47+
?? "dfeb5833-975e-4afb-88f1-6278ee9aeff6";
4848

4949
Console.WriteLine($"UpdateUrl: {updateUrl}");
5050
Console.WriteLine();
51-
52-
await new GeneralUpdateBootstrap()
53-
.SetSource(updateUrl, appSecretKey)
54-
.SetOption(Option.AppType, AppType.OssClient)
55-
.Hooks<ClientTestHooks>()
56-
.AddListenerMultiDownloadStatistics(OnDownloadStatistics)
57-
.AddListenerMultiDownloadCompleted(OnDownloadCompleted)
58-
.AddListenerMultiAllDownloadCompleted(OnAllDownloadCompleted)
59-
.AddListenerMultiDownloadError(OnDownloadError)
60-
.AddListenerException(OnException)
61-
.AddListenerUpdateInfo(OnUpdateInfo)
62-
.LaunchAsync();
63-
64-
Console.WriteLine("OSS Client test completed.");
65-
}
66-
67-
// ═══════════════════════════════════════════════════════════════════
68-
// Standard Client mode — silent poll with IPC handoff to Upgrade
69-
// ═══════════════════════════════════════════════════════════════════
70-
static async Task RunStandardClientAsync()
71-
{
72-
Console.WriteLine("=== GeneralUpdate Client Test (Silent Mode) ===");
73-
Console.WriteLine($"Started at {DateTime.Now}");
74-
Console.WriteLine($"Running from: {AppDomain.CurrentDomain.BaseDirectory}");
75-
76-
// Secrets come from code — never from files.
77-
var updateUrl = "http://localhost:5000/Upgrade/Verification";
78-
var reportUrl = "http://localhost:5000/Upgrade/Report";
79-
var appSecretKey = Environment.GetEnvironmentVariable("APP_SECRET_KEY") ?? "dfeb5833-975e-4afb-88f1-6278ee9aeff6";
80-
81-
Console.WriteLine($"UpdateUrl: {updateUrl}");
82-
Console.WriteLine($"Silent mode: ENABLED (poll every 1 minute)");
51+
Console.WriteLine("NOTE: Configure the GeneralSpacestation server with");
52+
Console.WriteLine(" the desired test data before running.");
53+
Console.WriteLine(" - Chain data: TbPackets (IsCrossVersion=false)");
54+
Console.WriteLine(" - Cross-version: additionally TbVersionArchives +");
55+
Console.WriteLine(" TbPacket (IsCrossVersion=true,");
56+
Console.WriteLine(" FromVersion=currentVersion)");
8357
Console.WriteLine();
8458

85-
// Silent mode: polls server in background, prepares update, launches Upgrade on exit.
86-
var bootstrap = await new GeneralUpdateBootstrap()
59+
// Non-silent immediate update flow:
60+
// 1. Version validation against server (HttpDownloadSource.ListAsync)
61+
// 2. Event dispatch (UpdateInfoEventArgs — shows available versions)
62+
// 3. Pre-check, hooks, backup
63+
// 4. Download all packages via DefaultDownloadOrchestrator
64+
// 5. Scenario dispatch:
65+
// - UpgradeOnly: apply upgrade packages in-place, client continues
66+
// - MainOnly: send MainApp versions via IPC → launch Upgrade process → exit
67+
// - Both: apply upgrade packages → IPC → launch Upgrade process → exit
68+
// - None: no-op
69+
await new GeneralUpdateBootstrap()
8770
.SetSource(updateUrl, appSecretKey, reportUrl)
8871
.SetOption(Option.AppType, AppType.Client)
89-
.SetOption(Option.Silent, true)
90-
.SetOption(Option.SilentPollIntervalMinutes, 1)
9172
.Hooks<ClientTestHooks>()
9273
.AddListenerMultiDownloadStatistics(OnDownloadStatistics)
9374
.AddListenerMultiDownloadCompleted(OnDownloadCompleted)
@@ -97,54 +78,11 @@ static async Task RunStandardClientAsync()
9778
.AddListenerUpdateInfo(OnUpdateInfo)
9879
.LaunchAsync();
9980

100-
var orchestrator = bootstrap.SilentOrchestrator;
101-
102-
Console.WriteLine();
103-
Console.WriteLine("╔════════════════════════════════════════════╗");
104-
Console.WriteLine("║ Silent poll running in background. ║");
105-
Console.WriteLine("║ Press Ctrl+C or Enter to exit. ║");
106-
Console.WriteLine("║ On exit, Upgrade process will be launched ║");
107-
Console.WriteLine("║ if an update has been prepared. ║");
108-
Console.WriteLine("╚════════════════════════════════════════════╝");
109-
Console.WriteLine();
110-
111-
// Keep the process alive so the background poll loop can work.
112-
var cts = new CancellationTokenSource();
113-
Console.CancelKeyPress += (_, e) =>
114-
{
115-
Console.WriteLine();
116-
Console.WriteLine("[Shutdown] Ctrl+C pressed. Exiting...");
117-
e.Cancel = true;
118-
cts.Cancel();
119-
};
120-
121-
try
122-
{
123-
await Task.Delay(Timeout.Infinite, cts.Token);
124-
}
125-
catch (OperationCanceledException)
126-
{
127-
// Expected on Ctrl+C — graceful shutdown
128-
}
129-
130-
Console.WriteLine("[Shutdown] Launching upgrade process...");
131-
if (orchestrator != null && orchestrator.HasPreparedUpdate)
132-
{
133-
var launched = orchestrator.TryLaunchUpgrade();
134-
Console.WriteLine(launched
135-
? "[Shutdown] Upgrade process launched successfully."
136-
: "[Shutdown] No update prepared or upgrade already launched.");
137-
}
138-
else
139-
{
140-
Console.WriteLine("[Shutdown] No orchestrator or no update prepared.");
141-
}
142-
143-
Console.WriteLine("[Shutdown] Client test exiting gracefully.");
81+
Console.WriteLine("Update test completed.");
14482
}
14583

14684
// ═══════════════════════════════════════════════════════════════════
147-
// Event handlers (shared across both modes)
85+
// Event handlers
14886
// ═══════════════════════════════════════════════════════════════════
14987

15088
static void OnDownloadStatistics(object sender, MultiDownloadStatisticsEventArgs e)
@@ -183,7 +121,19 @@ static void OnUpdateInfo(object sender, UpdateInfoEventArgs e)
183121
if (e.Info?.Body is { Count: > 0 })
184122
{
185123
foreach (var vi in e.Info.Body)
186-
Console.WriteLine($" - {vi.Version} ({vi.Name}) [{vi.Size} bytes] {(vi.IsForcibly == true ? "(forced)" : "")}");
124+
{
125+
var mode = vi.IsCrossVersion == true ? "CVP" : "Chain";
126+
var appType = vi.AppType switch
127+
{
128+
1 => "Client",
129+
2 => "Upgrade",
130+
_ => $"Unknown({vi.AppType})"
131+
};
132+
Console.WriteLine($" - [{mode}] {vi.Version} ({vi.Name}) [{vi.Size} bytes] " +
133+
$"AppType={appType} " +
134+
$"{(vi.IsForcibly == true ? "(forced)" : "")}" +
135+
$"{(!string.IsNullOrEmpty(vi.FromVersion) ? $" from={vi.FromVersion}" : "")}");
136+
}
187137
}
188138
else
189139
{
@@ -192,7 +142,7 @@ static void OnUpdateInfo(object sender, UpdateInfoEventArgs e)
192142
}
193143

194144
// ═══════════════════════════════════════════════════════════════════
195-
// Hooks (shared across both modes)
145+
// Hooks
196146
// ═══════════════════════════════════════════════════════════════════
197147

198148
sealed class ClientTestHooks : IUpdateHooks

tests/UpgradeTest/Program.cs

Lines changed: 22 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -6,64 +6,16 @@
66

77
try
88
{
9-
await RunOssUpgradeAsync();
10-
/*var isOssMode = args.Length > 0 && args[0] == "--oss";
11-
12-
if (isOssMode)
13-
{
14-
await RunOssUpgradeAsync();
15-
}
16-
else
17-
{
18-
await RunStandardUpgradeAsync();
19-
}*/
9+
await RunStandardUpgradeAsync();
2010
}
2111
catch (Exception ex)
2212
{
2313
Console.WriteLine($"FATAL: {ex}");
2414
Environment.Exit(1);
2515
}
2616

27-
// ═══════════════════════════════════════════════════════════════════
28-
// OSS Upgrade mode — read versions.json → download packages → decompress → launch main app
29-
// ═══════════════════════════════════════════════════════════════════
30-
static async Task RunOssUpgradeAsync()
31-
{
32-
Console.WriteLine("=== GeneralUpdate OSS Upgrade Test ===");
33-
Console.WriteLine($"Started at {DateTime.Now}");
34-
Console.WriteLine($"Running from: {AppDomain.CurrentDomain.BaseDirectory}");
35-
36-
// OssUpgrade runs from a subdirectory (e.g. update/), but the manifest
37-
// and versions.json are in the parent directory (same as OssClient).
38-
// InstallPath resolves to the parent so OssStrategy reads the correct
39-
// manifest and versions.json.
40-
var baseDir = AppDomain.CurrentDomain.BaseDirectory;
41-
var installPath = Path.GetFullPath(Path.Combine(baseDir, ".."));
42-
43-
Console.WriteLine($"BaseDir={baseDir}");
44-
Console.WriteLine($"InstallPath={installPath} (parent, where manifest + versions.json live)");
45-
Console.WriteLine();
46-
47-
// Only secrets and InstallPath are supplied in code. Identity fields
48-
// (MainAppName, ClientVersion, UpdateAppName) are read from
49-
// generalupdate.manifest.json by OssStrategy via
50-
// AppMetadataDiscoverer.Discover() — same as the standard OSS client flow.
51-
await new GeneralUpdateBootstrap()
52-
.SetSource(
53-
"http://localhost:5000/packages/versions.json",
54-
"dfeb5833-975e-4afb-88f1-6278ee9aeff6",
55-
installPath: installPath)
56-
.SetOption(Option.AppType, AppType.OssUpgrade)
57-
.Hooks<UpgradeTestHooks>()
58-
.AddListenerMultiDownloadStatistics(OnDownloadStatistics)
59-
.AddListenerMultiDownloadCompleted(OnDownloadCompleted)
60-
.AddListenerMultiAllDownloadCompleted(OnAllDownloadCompleted)
61-
.AddListenerMultiDownloadError(OnDownloadError)
62-
.AddListenerException(OnException)
63-
.LaunchAsync();
64-
65-
Console.WriteLine("OSS Upgrade test completed.");
66-
}
17+
Console.WriteLine("Press Enter to exit...");
18+
Console.ReadLine();
6719

6820
// ═══════════════════════════════════════════════════════════════════
6921
// Standard Upgrade mode — read IPC config → apply patches → launch main app
@@ -75,8 +27,17 @@ static async Task RunStandardUpgradeAsync()
7527
Console.WriteLine($"Running from: {AppDomain.CurrentDomain.BaseDirectory}");
7628

7729
// Config comes from the encrypted IPC file written by the Client process.
78-
// The Client's generalupdate.manifest.json + SetSource flows through IPC,
79-
// so the Upgrade never needs to load a manifest directly.
30+
// The generalupdate.manifest.json lives in the Client's directory; the Upgrade
31+
// process receives all necessary identity fields and update versions through
32+
// the ProcessContract via EncryptedFileProcessContractProvider.Receive().
33+
//
34+
// When no IPC file exists (first run, no update pending), the bootstrap
35+
// treats it as a no-op and returns gracefully — no error, no crash.
36+
var ipcPath = System.IO.Path.Combine(
37+
System.IO.Path.GetTempPath(), "GeneralUpdate", "ipc", "process_info.enc");
38+
Console.WriteLine("Reading IPC process contract (written by Client process)...");
39+
Console.WriteLine($"IPC file: {ipcPath}");
40+
Console.WriteLine();
8041

8142
await new GeneralUpdateBootstrap()
8243
.SetOption(Option.AppType, AppType.Upgrade)
@@ -88,11 +49,15 @@ static async Task RunStandardUpgradeAsync()
8849
.AddListenerException(OnException)
8950
.LaunchAsync();
9051

52+
// NOTE: After the update pipeline completes and the main app is launched,
53+
// the OS strategy's StartAppAsync() calls GracefulExit.CurrentProcessAsync(),
54+
// which terminates the Upgrade process. This line is only reached when no
55+
// updates were found (no IPC data) or when update application fails.
9156
Console.WriteLine("Upgrade test completed.");
9257
}
9358

9459
// ═══════════════════════════════════════════════════════════════════
95-
// Event handlers (shared across both modes)
60+
// Event handlers
9661
// ═══════════════════════════════════════════════════════════════════
9762

9863
static void OnDownloadStatistics(object sender, MultiDownloadStatisticsEventArgs e)
@@ -126,7 +91,7 @@ static void OnException(object sender, ExceptionEventArgs e)
12691
}
12792

12893
// ═══════════════════════════════════════════════════════════════════
129-
// Hooks (shared across both modes)
94+
// Hooks
13095
// ═══════════════════════════════════════════════════════════════════
13196

13297
sealed class UpgradeTestHooks : IUpdateHooks
@@ -146,8 +111,8 @@ public async Task OnDownloadCompletedAsync(DownloadContext ctx)
146111
public async Task OnAfterUpdateAsync(HookContext ctx)
147112
{
148113
Console.WriteLine($"[Hook] OnAfterUpdate: {ctx.CurrentVersion} -> {ctx.TargetVersion}");
149-
// Manifest ClientVersion is updated by OssStrategy itself via
150-
// ManifestInfo.TryUpdateVersion() after decompression.
114+
// Manifest ClientVersion is updated by UpdateStrategy itself via
115+
// ManifestInfo.TryUpdateVersion() after successful apply.
151116
await Task.CompletedTask;
152117
}
153118

0 commit comments

Comments
 (0)