Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 0 additions & 18 deletions src/c#/GeneralUpdate.Core/Configuration/VersionEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -164,24 +164,6 @@ public class VersionEntry : VersionIdentity
[JsonPropertyName("urlExpireTimeUtc")]
public DateTime? UrlExpireTimeUtc { get; set; }

/// <summary>
/// The upgrade mode: 1 = VersionChain (sequential version upgrades), 2 = CrossVersion (cross-version upgrade).
/// </summary>
/// <remarks>
/// <para>
/// <list type="bullet">
/// <item>
/// <description><c>1 (VersionChain)</c>: Upgrades through versions sequentially, without skipping intermediate versions.</description>
/// </item>
/// <item>
/// <description><c>2 (CrossVersion)</c>: Directly upgrades from an old version to any newer version.</description>
/// </item>
/// </list>
/// </para>
/// </remarks>
[JsonPropertyName("upgradeMode")]
public int? UpgradeMode { get; set; }

/// <summary>
/// Whether this is a cross-version upgrade package.
Comment on lines 164 to 168
/// <c>true</c> indicates this package is used to upgrade directly from an old version to a new version,
Expand Down
1 change: 0 additions & 1 deletion src/c#/GeneralUpdate.Core/Download/Models/DownloadAsset.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ public record DownloadAsset(
bool IsForcibly = false,
bool IsFreeze = false,
int RecordId = 0,
int? UpgradeMode = null,
int? AppType = null,
string? AuthScheme = null,
Comment on lines 18 to 22
string? AuthToken = null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,6 @@ private static DownloadAsset MapVersionEntry(VersionEntry v)
IsForcibly: v.IsForcibly == true,
IsFreeze: v.IsFreeze == true,
RecordId: v.RecordId,
UpgradeMode: v.UpgradeMode,
AppType: v.AppType,
IsCrossVersion: v.IsCrossVersion == true,
FromVersion: v.FromVersion,
Expand Down
1 change: 0 additions & 1 deletion src/c#/GeneralUpdate.Core/Strategy/ClientStrategy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,6 @@ private async Task ExecuteStandardWorkflowAsync()
IsForcibly = a.IsForcibly,
IsFreeze = a.IsFreeze,
AppType = a.AppType,
UpgradeMode = a.UpgradeMode,
IsCrossVersion = a.IsCrossVersion,
FromVersion = a.FromVersion
}).ToList();
Expand Down
2 changes: 1 addition & 1 deletion tests/ClientTest/ClientTest.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
<UpgradeTestDir>..\UpgradeTest\bin\$(Configuration)\net10.0</UpgradeTestDir>
</PropertyGroup>
<ItemGroup>
<UpgradeTestFiles Include="$(UpgradeTestDir)\**\*" Exclude="$(UpgradeTestDir)\**\*.pdb;$(UpgradeTestDir)\**\*.json" />
<UpgradeTestFiles Include="$(UpgradeTestDir)\**\*" Exclude="$(UpgradeTestDir)\**\*.pdb" />
</ItemGroup>
<Copy SourceFiles="@(UpgradeTestFiles)"
DestinationFolder="$(OutputPath)"
Expand Down
156 changes: 53 additions & 103 deletions tests/ClientTest/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,7 @@

try
{
await RunOssClientAsync();
/*var isOssMode = args.Length > 0 && args[0] == "--oss";

if (isOssMode)
{
await RunOssClientAsync();
}
else
{
await RunStandardClientAsync();
}*/
await RunUpdateTestAsync();
}
Comment on lines 8 to 11
catch (Exception ex)
{
Expand All @@ -27,67 +17,58 @@
Environment.Exit(1);
}

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

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

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

Console.WriteLine($"UpdateUrl: {updateUrl}");
Console.WriteLine();

await new GeneralUpdateBootstrap()
.SetSource(updateUrl, appSecretKey)
.SetOption(Option.AppType, AppType.OssClient)
.Hooks<ClientTestHooks>()
.AddListenerMultiDownloadStatistics(OnDownloadStatistics)
.AddListenerMultiDownloadCompleted(OnDownloadCompleted)
.AddListenerMultiAllDownloadCompleted(OnAllDownloadCompleted)
.AddListenerMultiDownloadError(OnDownloadError)
.AddListenerException(OnException)
.AddListenerUpdateInfo(OnUpdateInfo)
.LaunchAsync();

Console.WriteLine("OSS Client test completed.");
}

// ═══════════════════════════════════════════════════════════════════
// Standard Client mode — silent poll with IPC handoff to Upgrade
// ═══════════════════════════════════════════════════════════════════
static async Task RunStandardClientAsync()
{
Console.WriteLine("=== GeneralUpdate Client Test (Silent Mode) ===");
Console.WriteLine($"Started at {DateTime.Now}");
Console.WriteLine($"Running from: {AppDomain.CurrentDomain.BaseDirectory}");

// Secrets come from code — never from files.
var updateUrl = "http://localhost:5000/Upgrade/Verification";
var reportUrl = "http://localhost:5000/Upgrade/Report";
var appSecretKey = Environment.GetEnvironmentVariable("APP_SECRET_KEY") ?? "dfeb5833-975e-4afb-88f1-6278ee9aeff6";

Console.WriteLine($"UpdateUrl: {updateUrl}");
Console.WriteLine($"Silent mode: ENABLED (poll every 1 minute)");
Console.WriteLine("NOTE: Configure the GeneralSpacestation server with");
Console.WriteLine(" the desired test data before running.");
Console.WriteLine(" - Chain data: TbPackets (IsCrossVersion=false)");
Console.WriteLine(" - Cross-version: additionally TbVersionArchives +");
Console.WriteLine(" TbPacket (IsCrossVersion=true,");
Console.WriteLine(" FromVersion=currentVersion)");
Console.WriteLine();

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

var orchestrator = bootstrap.SilentOrchestrator;

Console.WriteLine();
Console.WriteLine("╔════════════════════════════════════════════╗");
Console.WriteLine("║ Silent poll running in background. ║");
Console.WriteLine("║ Press Ctrl+C or Enter to exit. ║");
Console.WriteLine("║ On exit, Upgrade process will be launched ║");
Console.WriteLine("║ if an update has been prepared. ║");
Console.WriteLine("╚════════════════════════════════════════════╝");
Console.WriteLine();

// Keep the process alive so the background poll loop can work.
var cts = new CancellationTokenSource();
Console.CancelKeyPress += (_, e) =>
{
Console.WriteLine();
Console.WriteLine("[Shutdown] Ctrl+C pressed. Exiting...");
e.Cancel = true;
cts.Cancel();
};

try
{
await Task.Delay(Timeout.Infinite, cts.Token);
}
catch (OperationCanceledException)
{
// Expected on Ctrl+C — graceful shutdown
}

Console.WriteLine("[Shutdown] Launching upgrade process...");
if (orchestrator != null && orchestrator.HasPreparedUpdate)
{
var launched = orchestrator.TryLaunchUpgrade();
Console.WriteLine(launched
? "[Shutdown] Upgrade process launched successfully."
: "[Shutdown] No update prepared or upgrade already launched.");
}
else
{
Console.WriteLine("[Shutdown] No orchestrator or no update prepared.");
}

Console.WriteLine("[Shutdown] Client test exiting gracefully.");
Console.WriteLine("Update test completed.");
}

// ═══════════════════════════════════════════════════════════════════
// Event handlers (shared across both modes)
// Event handlers
// ═══════════════════════════════════════════════════════════════════

static void OnDownloadStatistics(object sender, MultiDownloadStatisticsEventArgs e)
Expand Down Expand Up @@ -183,7 +121,19 @@ static void OnUpdateInfo(object sender, UpdateInfoEventArgs e)
if (e.Info?.Body is { Count: > 0 })
{
foreach (var vi in e.Info.Body)
Console.WriteLine($" - {vi.Version} ({vi.Name}) [{vi.Size} bytes] {(vi.IsForcibly == true ? "(forced)" : "")}");
{
var mode = vi.IsCrossVersion == true ? "CVP" : "Chain";
var appType = vi.AppType switch
{
1 => "Client",
2 => "Upgrade",
_ => $"Unknown({vi.AppType})"
};
Console.WriteLine($" - [{mode}] {vi.Version} ({vi.Name}) [{vi.Size} bytes] " +
$"AppType={appType} " +
$"{(vi.IsForcibly == true ? "(forced)" : "")}" +
$"{(!string.IsNullOrEmpty(vi.FromVersion) ? $" from={vi.FromVersion}" : "")}");
}
}
else
{
Expand All @@ -192,7 +142,7 @@ static void OnUpdateInfo(object sender, UpdateInfoEventArgs e)
}

// ═══════════════════════════════════════════════════════════════════
// Hooks (shared across both modes)
// Hooks
// ═══════════════════════════════════════════════════════════════════

sealed class ClientTestHooks : IUpdateHooks
Expand Down
79 changes: 22 additions & 57 deletions tests/UpgradeTest/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,64 +6,16 @@

try
{
await RunOssUpgradeAsync();
/*var isOssMode = args.Length > 0 && args[0] == "--oss";

if (isOssMode)
{
await RunOssUpgradeAsync();
}
else
{
await RunStandardUpgradeAsync();
}*/
await RunStandardUpgradeAsync();
}
catch (Exception ex)
{
Console.WriteLine($"FATAL: {ex}");
Environment.Exit(1);
}

// ═══════════════════════════════════════════════════════════════════
// OSS Upgrade mode — read versions.json → download packages → decompress → launch main app
// ═══════════════════════════════════════════════════════════════════
static async Task RunOssUpgradeAsync()
{
Console.WriteLine("=== GeneralUpdate OSS Upgrade Test ===");
Console.WriteLine($"Started at {DateTime.Now}");
Console.WriteLine($"Running from: {AppDomain.CurrentDomain.BaseDirectory}");

// OssUpgrade runs from a subdirectory (e.g. update/), but the manifest
// and versions.json are in the parent directory (same as OssClient).
// InstallPath resolves to the parent so OssStrategy reads the correct
// manifest and versions.json.
var baseDir = AppDomain.CurrentDomain.BaseDirectory;
var installPath = Path.GetFullPath(Path.Combine(baseDir, ".."));

Console.WriteLine($"BaseDir={baseDir}");
Console.WriteLine($"InstallPath={installPath} (parent, where manifest + versions.json live)");
Console.WriteLine();

// Only secrets and InstallPath are supplied in code. Identity fields
// (MainAppName, ClientVersion, UpdateAppName) are read from
// generalupdate.manifest.json by OssStrategy via
// AppMetadataDiscoverer.Discover() — same as the standard OSS client flow.
await new GeneralUpdateBootstrap()
.SetSource(
"http://localhost:5000/packages/versions.json",
"dfeb5833-975e-4afb-88f1-6278ee9aeff6",
installPath: installPath)
.SetOption(Option.AppType, AppType.OssUpgrade)
.Hooks<UpgradeTestHooks>()
.AddListenerMultiDownloadStatistics(OnDownloadStatistics)
.AddListenerMultiDownloadCompleted(OnDownloadCompleted)
.AddListenerMultiAllDownloadCompleted(OnAllDownloadCompleted)
.AddListenerMultiDownloadError(OnDownloadError)
.AddListenerException(OnException)
.LaunchAsync();

Console.WriteLine("OSS Upgrade test completed.");
}
Console.WriteLine("Press Enter to exit...");
Console.ReadLine();

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

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

await new GeneralUpdateBootstrap()
.SetOption(Option.AppType, AppType.Upgrade)
Expand All @@ -88,11 +49,15 @@ static async Task RunStandardUpgradeAsync()
.AddListenerException(OnException)
.LaunchAsync();

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

// ═══════════════════════════════════════════════════════════════════
// Event handlers (shared across both modes)
// Event handlers
// ═══════════════════════════════════════════════════════════════════

static void OnDownloadStatistics(object sender, MultiDownloadStatisticsEventArgs e)
Expand Down Expand Up @@ -126,7 +91,7 @@ static void OnException(object sender, ExceptionEventArgs e)
}

// ═══════════════════════════════════════════════════════════════════
// Hooks (shared across both modes)
// Hooks
// ═══════════════════════════════════════════════════════════════════

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

Expand Down
Loading