Skip to content

Commit 4708827

Browse files
authored
test: add unit tests for Hooks, IPC, Backup/Restore, EventListener (#351)
- HooksTests: NoOpUpdateHooks defaults, UpdateContext/DownloadContext record equality - ProcessInfoProviderTests: EncryptedFile send/receive roundtrip, no-file returns null - BackupRestoreTests: backup+restore roundtrip, CleanBackup keeps N versions, ListBackups metadata - EventListenerTests: add/remove/dispatch, multiple listeners, progress args, event args constructors - Removed IsTrimmable from Core.csproj (not supported on netstandard2.0) Test results: CoreTest 54/55 (1 pre-existing failure), ClientCoreTest 20/20, DifferentialTest 62/62, BowlTest 50/50 Closes #350
1 parent a6d04b6 commit 4708827

5 files changed

Lines changed: 333 additions & 3 deletions

File tree

src/c#/GeneralUpdate.Core/GeneralUpdate.Core.csproj

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@
1212
<PackageTags>upgrade,update,client</PackageTags>
1313
<Version>10.0.0-preview</Version>
1414
<TargetFramework>netstandard2.0</TargetFramework>
15-
<!-- Trim readiness within netstandard2.0 constraints -->
16-
<IsTrimmable>true</IsTrimmable>
17-
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
1815
</PropertyGroup>
1916

2017
<ItemGroup>
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
using System;
2+
using GeneralUpdate.Core.Download;
3+
using GeneralUpdate.Core.Download.Models;
4+
using GeneralUpdate.Core.Event;
5+
using Xunit;
6+
7+
namespace CoreTest.Event;
8+
9+
public class EventListenerTests
10+
{
11+
private class TestListener : IUpdateEventListener
12+
{
13+
public int AllDownloadCompletedCalls;
14+
public int DownloadCompletedCalls;
15+
public int DownloadErrorCalls;
16+
public int DownloadStatisticsCalls;
17+
public int UpdateInfoCalls;
18+
public int ExceptionCalls;
19+
public int ProgressCalls;
20+
21+
public void OnAllDownloadCompleted(MultiAllDownloadCompletedEventArgs args)
22+
=> AllDownloadCompletedCalls++;
23+
24+
public void OnDownloadCompleted(MultiDownloadCompletedEventArgs args)
25+
=> DownloadCompletedCalls++;
26+
27+
public void OnDownloadError(MultiDownloadErrorEventArgs args)
28+
=> DownloadErrorCalls++;
29+
30+
public void OnDownloadStatistics(MultiDownloadStatisticsEventArgs args)
31+
=> DownloadStatisticsCalls++;
32+
33+
public void OnUpdateInfo(UpdateInfoEventArgs args)
34+
=> UpdateInfoCalls++;
35+
36+
public void OnException(ExceptionEventArgs args)
37+
=> ExceptionCalls++;
38+
39+
public void OnProgress(DownloadProgress progress)
40+
=> ProgressCalls++;
41+
}
42+
43+
[Fact]
44+
public void EventManager_AddListener_And_Dispatch()
45+
{
46+
var listener = new TestListener();
47+
EventManager.Instance.AddListener<MultiAllDownloadCompletedEventArgs>((s, e) => listener.OnAllDownloadCompleted(e));
48+
49+
var args = new MultiAllDownloadCompletedEventArgs(true, Array.Empty<(object, string)>());
50+
EventManager.Instance.Dispatch(this, args);
51+
52+
Assert.Equal(1, listener.AllDownloadCompletedCalls);
53+
}
54+
55+
[Fact]
56+
public void EventManager_RemoveListener_StopsDispatch()
57+
{
58+
var listener = new TestListener();
59+
Action<object, MultiDownloadCompletedEventArgs> handler = (s, e) => listener.OnDownloadCompleted(e);
60+
61+
EventManager.Instance.AddListener(handler);
62+
EventManager.Instance.RemoveListener(handler);
63+
64+
var args = new MultiDownloadCompletedEventArgs(new object(), true);
65+
EventManager.Instance.Dispatch(this, args);
66+
67+
Assert.Equal(0, listener.DownloadCompletedCalls);
68+
}
69+
70+
[Fact]
71+
public void EventManager_MultipleListeners_AllCalled()
72+
{
73+
var listener1 = new TestListener();
74+
var listener2 = new TestListener();
75+
76+
EventManager.Instance.AddListener<ExceptionEventArgs>((s, e) => listener1.OnException(e));
77+
EventManager.Instance.AddListener<ExceptionEventArgs>((s, e) => listener2.OnException(e));
78+
79+
var args = new ExceptionEventArgs(new Exception("test"), "test message");
80+
EventManager.Instance.Dispatch(this, args);
81+
82+
Assert.Equal(1, listener1.ExceptionCalls);
83+
Assert.Equal(1, listener2.ExceptionCalls);
84+
}
85+
86+
[Fact]
87+
public void ProgressEventArgs_Constructor()
88+
{
89+
var progress = new DownloadProgress("asset.zip", 500, 1000, 50.0, DownloadStatus.Downloading);
90+
var args = new ProgressEventArgs(progress);
91+
92+
Assert.Same(progress, args.Progress);
93+
Assert.Equal("asset.zip", args.Progress.AssetName);
94+
Assert.Equal(500, args.Progress.BytesDownloaded);
95+
Assert.Equal(1000, args.Progress.TotalBytes);
96+
Assert.Equal(50.0, args.Progress.Percentage);
97+
Assert.Equal(DownloadStatus.Downloading, args.Progress.Status);
98+
}
99+
100+
[Fact]
101+
public void EventArgs_Types_Constructed()
102+
{
103+
var allDone = new MultiAllDownloadCompletedEventArgs(true, Array.Empty<(object, string)>());
104+
Assert.True(allDone.IsAllDownloadCompleted);
105+
106+
var downloadDone = new MultiDownloadCompletedEventArgs(new object(), true);
107+
Assert.NotNull(downloadDone.Version);
108+
Assert.True(downloadDone.IsComplated);
109+
110+
var ex = new Exception("boom");
111+
var err = new MultiDownloadErrorEventArgs(ex, new object());
112+
Assert.Same(ex, err.Exception);
113+
114+
var stats = new MultiDownloadStatisticsEventArgs(new object(), TimeSpan.FromSeconds(1), "1MB/s", 1000, 500, 50.0);
115+
Assert.Equal("1MB/s", stats.Speed);
116+
Assert.Equal(1000, stats.TotalBytesToReceive);
117+
}
118+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
using System;
2+
using System.IO;
3+
using System.Linq;
4+
using GeneralUpdate.Core.FileSystem;
5+
using Xunit;
6+
7+
namespace CoreTest.Backup;
8+
9+
public class BackupRestoreTests
10+
{
11+
[Fact]
12+
public void Backup_And_Restore_Roundtrip()
13+
{
14+
var tmpRoot = Path.Combine(Path.GetTempPath(), "CoreTest.Backup." + Guid.NewGuid().ToString("N"));
15+
var sourceDir = Path.Combine(tmpRoot, "source");
16+
var backupDir = Path.Combine(tmpRoot, "backup");
17+
var restoreDir = Path.Combine(tmpRoot, "restored");
18+
19+
try
20+
{
21+
Directory.CreateDirectory(sourceDir);
22+
File.WriteAllText(Path.Combine(sourceDir, "app.exe"), "v1.0");
23+
File.WriteAllText(Path.Combine(sourceDir, "config.json"), "{}");
24+
var subDir = Path.Combine(sourceDir, "data");
25+
Directory.CreateDirectory(subDir);
26+
File.WriteAllText(Path.Combine(subDir, "data.db"), "mydata");
27+
28+
StorageManager.Backup(sourceDir, backupDir, Array.Empty<string>());
29+
Assert.True(Directory.Exists(backupDir));
30+
Assert.True(File.Exists(Path.Combine(backupDir, "app.exe")));
31+
Assert.True(File.Exists(Path.Combine(backupDir, "config.json")));
32+
33+
StorageManager.Restore(backupDir, restoreDir);
34+
Assert.True(Directory.Exists(restoreDir));
35+
Assert.Equal("v1.0", File.ReadAllText(Path.Combine(restoreDir, "app.exe")));
36+
Assert.Equal("{}", File.ReadAllText(Path.Combine(restoreDir, "config.json")));
37+
}
38+
finally
39+
{
40+
if (Directory.Exists(tmpRoot)) Directory.Delete(tmpRoot, true);
41+
}
42+
}
43+
44+
[Fact]
45+
public void CleanBackup_KeepsOnlyRecentVersions()
46+
{
47+
var installPath = Path.Combine(Path.GetTempPath(), "CoreTest.CleanBackup." + Guid.NewGuid().ToString("N"));
48+
var backupRoot = Path.Combine(installPath, "__backups");
49+
try
50+
{
51+
for (int i = 1; i <= 5; i++)
52+
{
53+
var verDir = Path.Combine(backupRoot, $"{i}.0.0");
54+
Directory.CreateDirectory(verDir);
55+
File.WriteAllText(Path.Combine(verDir, "app.exe"), $"v{i}");
56+
}
57+
58+
StorageManager.CleanBackup(installPath, keepVersions: 3);
59+
60+
var remaining = Directory.GetDirectories(backupRoot);
61+
Assert.Equal(3, remaining.Length);
62+
var names = remaining.Select(Path.GetFileName).OrderBy(n => new Version(n!)).ToList();
63+
Assert.Equal("3.0.0", names[0]);
64+
Assert.Equal("4.0.0", names[1]);
65+
Assert.Equal("5.0.0", names[2]);
66+
}
67+
finally
68+
{
69+
if (Directory.Exists(installPath)) Directory.Delete(installPath, true);
70+
}
71+
}
72+
73+
[Fact]
74+
public void ListBackups_ReturnsMetadata()
75+
{
76+
var installPath = Path.Combine(Path.GetTempPath(), "CoreTest.ListBackups." + Guid.NewGuid().ToString("N"));
77+
var backupRoot = Path.Combine(installPath, "__backups");
78+
try
79+
{
80+
var verDir = Path.Combine(backupRoot, "1.0.0");
81+
Directory.CreateDirectory(verDir);
82+
File.WriteAllText(Path.Combine(verDir, "app.exe"), "v1");
83+
84+
var backups = StorageManager.ListBackups(installPath);
85+
Assert.Single(backups);
86+
Assert.Equal("1.0.0", backups[0].Version);
87+
Assert.Contains("__backups", backups[0].Path);
88+
}
89+
finally
90+
{
91+
if (Directory.Exists(installPath)) Directory.Delete(installPath, true);
92+
}
93+
}
94+
}

tests/CoreTest/Hooks/HooksTests.cs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using System;
2+
using System.Threading.Tasks;
3+
using GeneralUpdate.Core.Hooks;
4+
using Xunit;
5+
6+
namespace CoreTest.Hooks;
7+
8+
public class HooksTests
9+
{
10+
[Fact]
11+
public async Task NoOpUpdateHooks_AllMethods_ReturnDefaults()
12+
{
13+
var hooks = new NoOpUpdateHooks();
14+
var ctx = new UpdateContext("test", "/tmp", "1.0", "2.0", 1);
15+
16+
Assert.True(await hooks.OnBeforeUpdateAsync(ctx));
17+
await hooks.OnDownloadCompletedAsync(new("a", "1.0", 100, TimeSpan.Zero, null, true));
18+
await hooks.OnAfterUpdateAsync(ctx);
19+
await hooks.OnUpdateErrorAsync(ctx, new Exception("test"));
20+
await hooks.OnBeforeStartAppAsync(ctx);
21+
}
22+
23+
[Fact]
24+
public void UpdateContext_RecordEquality_Works()
25+
{
26+
var a = new UpdateContext("app", "/path", "1.0", "2.0", 1);
27+
var b = new UpdateContext("app", "/path", "1.0", "2.0", 1);
28+
var c = new UpdateContext("app2", "/path", "1.0", "2.0", 1);
29+
30+
Assert.Equal(a, b);
31+
Assert.NotEqual(a, c);
32+
}
33+
34+
[Fact]
35+
public void DownloadContext_RecordEquality_Works()
36+
{
37+
var a = new DownloadContext("asset", "1.0", 100, TimeSpan.FromSeconds(1), "/tmp/a.zip", true);
38+
var b = new DownloadContext("asset", "1.0", 100, TimeSpan.FromSeconds(1), "/tmp/a.zip", true);
39+
var c = a with { AssetName = "other" };
40+
41+
Assert.Equal(a, b);
42+
Assert.NotEqual(a, c);
43+
}
44+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Threading.Tasks;
5+
using GeneralUpdate.Core.Configuration;
6+
using GeneralUpdate.Core.Ipc;
7+
using Xunit;
8+
9+
namespace CoreTest.Ipc;
10+
11+
public class ProcessInfoProviderTests
12+
{
13+
[Fact]
14+
public async Task EncryptedFileProvider_SendReceive_RoundTrips()
15+
{
16+
var tmpDir = Path.Combine(Path.GetTempPath(), "CoreTest.Ipc", Guid.NewGuid().ToString("N"));
17+
Directory.CreateDirectory(tmpDir);
18+
try
19+
{
20+
var provider = new EncryptedFileProcessInfoProvider(tmpDir);
21+
var info = new ProcessInfo(
22+
appName: "test-app",
23+
installPath: Path.GetTempPath(),
24+
currentVersion: "1.0.0",
25+
lastVersion: "2.0.0",
26+
updateLogUrl: "https://example.com/log",
27+
compressEncoding: System.Text.Encoding.UTF8,
28+
compressFormat: "ZIP",
29+
downloadTimeOut: 30,
30+
appSecretKey: "secret",
31+
updateVersions: new List<VersionInfo> { new() { Version = "1.0.0", Name = "test" } },
32+
reportUrl: "https://example.com/report",
33+
backupDirectory: "/tmp/backup",
34+
bowl: "",
35+
scheme: "",
36+
token: "",
37+
script: "",
38+
driverDirectory: "",
39+
blackFileFormats: new List<string> { ".pdb" },
40+
blackFiles: new List<string> { "test.dll" },
41+
skipDirectories: new List<string> { "logs" }
42+
);
43+
44+
await provider.SendAsync(info);
45+
var result = await provider.ReceiveAsync();
46+
47+
Assert.NotNull(result);
48+
Assert.Equal("test-app", result!.AppName);
49+
Assert.Equal("1.0.0", result.CurrentVersion);
50+
Assert.NotEmpty(result.BlackFileFormats);
51+
52+
Assert.False(Directory.GetFiles(tmpDir, "*.enc").Length > 0,
53+
"Encrypted file should be deleted after ReceiveAsync");
54+
}
55+
finally
56+
{
57+
if (Directory.Exists(tmpDir)) Directory.Delete(tmpDir, true);
58+
}
59+
}
60+
61+
[Fact]
62+
public async Task EncryptedFileProvider_NoFile_ReturnsNull()
63+
{
64+
var tmpDir = Path.Combine(Path.GetTempPath(), "CoreTest.Ipc.Empty." + Guid.NewGuid().ToString("N"));
65+
Directory.CreateDirectory(tmpDir);
66+
try
67+
{
68+
var provider = new EncryptedFileProcessInfoProvider(tmpDir);
69+
var result = await provider.ReceiveAsync();
70+
Assert.Null(result);
71+
}
72+
finally
73+
{
74+
if (Directory.Exists(tmpDir)) Directory.Delete(tmpDir, true);
75+
}
76+
}
77+
}

0 commit comments

Comments
 (0)