Initial commit: Client Doc docs Server Tools
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using Xunit;
|
||||
using XWorld.PublishTool;
|
||||
|
||||
namespace XWorld.PublishTool.Tests
|
||||
{
|
||||
public class PublishLayoutTests
|
||||
{
|
||||
private static GameSpec MakeSpec() => new GameSpec
|
||||
{
|
||||
GameId = "rps", Version = 1, MinFrameworkVersion = 1, PlayerCount = 2, TickRateHz = 10,
|
||||
ServerAssembly = "RPS.Server.dll", ServerEntryType = "RPS.Server.RpsServerRoom",
|
||||
CoreDll = "RPS.Core.dll", ClientDll = "RPS.Client.dll", ClientEntryType = "RPS.Client.RpsGameClient",
|
||||
CodeAb = "code.unity3d",
|
||||
Assets = new System.Collections.Generic.List<string> { "res.unity3d" },
|
||||
};
|
||||
|
||||
private static string TempDir() =>
|
||||
Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), "pub_" + System.Guid.NewGuid().ToString("N"))).FullName;
|
||||
|
||||
[Fact]
|
||||
public void PublishServer_ProducesDlls_GameJson_Manifest_Signature()
|
||||
{
|
||||
string src = TempDir(); string outDir = Path.Combine(TempDir(), "s");
|
||||
File.WriteAllBytes(Path.Combine(src, "RPS.Core.dll"), new byte[] { 1, 2, 3 });
|
||||
File.WriteAllBytes(Path.Combine(src, "RPS.Server.dll"), new byte[] { 4, 5 });
|
||||
using var rsa = RSA.Create(2048);
|
||||
|
||||
new PublishLayout().PublishServer(src, outDir, MakeSpec(), rsa.ExportRSAPrivateKeyPem());
|
||||
|
||||
Assert.True(File.Exists(Path.Combine(outDir, "RPS.Server.dll")));
|
||||
Assert.True(File.Exists(Path.Combine(outDir, "RPS.Core.dll")));
|
||||
Assert.True(File.Exists(Path.Combine(outDir, "game.json")));
|
||||
var fm = FilesManifest.Parse(File.ReadAllText(Path.Combine(outDir, "files.txt")));
|
||||
Assert.True(fm.Entries.ContainsKey("RPS.Server.dll"));
|
||||
Assert.True(fm.Entries.ContainsKey("RPS.Core.dll"));
|
||||
Assert.True(fm.Entries.ContainsKey("game.json"));
|
||||
byte[] sig = File.ReadAllBytes(Path.Combine(outDir, "files.txt.sig"));
|
||||
Assert.True(ManifestSigner.Verify(fm.Digest(), sig, rsa.ExportRSAPublicKeyPem()));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PublishClient_Md5NamedAbs_CodeAbInManifest_NoLooseBytes()
|
||||
{
|
||||
string src = TempDir(); string outDir = Path.Combine(TempDir(), "c");
|
||||
File.WriteAllBytes(Path.Combine(src, "code.unity3d"), new byte[] { 6, 7, 8 });
|
||||
File.WriteAllBytes(Path.Combine(src, "res.unity3d"), new byte[] { 9 });
|
||||
using var rsa = RSA.Create(2048);
|
||||
|
||||
new PublishLayout().PublishClient(src, outDir, MakeSpec(), rsa.ExportRSAPrivateKeyPem());
|
||||
|
||||
var fm = FilesManifest.Parse(File.ReadAllText(Path.Combine(outDir, "files.txt")));
|
||||
// files.txt 登记逻辑名;落盘 md5 命名(与 MiniGameDownloader.Md5Name 一致)
|
||||
Assert.True(fm.Entries.ContainsKey("code.unity3d"));
|
||||
Assert.True(fm.Entries.ContainsKey("res.unity3d"));
|
||||
string codeMd5 = Md5Util.OfFile(Path.Combine(src, "code.unity3d"));
|
||||
string resMd5 = Md5Util.OfFile(Path.Combine(src, "res.unity3d"));
|
||||
Assert.True(File.Exists(Path.Combine(outDir, $"code_{codeMd5}.unity3d")));
|
||||
Assert.True(File.Exists(Path.Combine(outDir, $"res_{resMd5}.unity3d")));
|
||||
// 统一 AB:不再有散 .bytes 文件
|
||||
Assert.Empty(Directory.GetFiles(outDir, "*.bytes"));
|
||||
// game.json 带 codeAb
|
||||
Assert.Contains("\"codeAb\": \"code.unity3d\"", File.ReadAllText(Path.Combine(outDir, "game.json")));
|
||||
byte[] sig = File.ReadAllBytes(Path.Combine(outDir, "files.txt.sig"));
|
||||
Assert.True(ManifestSigner.Verify(fm.Digest(), sig, rsa.ExportRSAPublicKeyPem()));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NullKey_WritesEmptySig_BothTrees()
|
||||
{
|
||||
string src = TempDir();
|
||||
string outS = Path.Combine(TempDir(), "s"); string outC = Path.Combine(TempDir(), "c");
|
||||
File.WriteAllBytes(Path.Combine(src, "RPS.Core.dll"), new byte[] { 1 });
|
||||
File.WriteAllBytes(Path.Combine(src, "RPS.Server.dll"), new byte[] { 2 });
|
||||
File.WriteAllBytes(Path.Combine(src, "code.unity3d"), new byte[] { 3 });
|
||||
File.WriteAllBytes(Path.Combine(src, "res.unity3d"), new byte[] { 4 });
|
||||
|
||||
var layout = new PublishLayout();
|
||||
layout.PublishServer(src, outS, MakeSpec(), null);
|
||||
layout.PublishClient(src, outC, MakeSpec(), null);
|
||||
|
||||
Assert.Equal(0, new FileInfo(Path.Combine(outS, "files.txt.sig")).Length);
|
||||
Assert.Equal(0, new FileInfo(Path.Combine(outC, "files.txt.sig")).Length);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PublishClient_EmptyCodeAb_Throws()
|
||||
{
|
||||
var spec = MakeSpec(); spec.CodeAb = null;
|
||||
Assert.Throws<System.InvalidOperationException>(
|
||||
() => new PublishLayout().PublishClient(TempDir(), Path.Combine(TempDir(), "c"), spec, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user