mirror of
https://github.com/AtomUI/AtomUI.git
synced 2026-10-25 02:30:32 +08:00
117 lines
5.4 KiB
C#
117 lines
5.4 KiB
C#
using System.Reflection;
|
|
using System.Security.Cryptography;
|
|
using System.Text.Json;
|
|
using Shouldly;
|
|
using Xunit;
|
|
|
|
namespace AtomUI.Build.Tasks.Tests;
|
|
|
|
public class TypeMapReceiptIntegrityTests
|
|
{
|
|
[Theory]
|
|
[InlineData(0)]
|
|
[InlineData(17)]
|
|
[InlineData(4 * 1024 * 1024)]
|
|
public void File_hash_matches_SHA256_oracle(int size)
|
|
{
|
|
var path = Path.Combine(Path.GetTempPath(), "atomui-hash-" + Guid.NewGuid().ToString("N"));
|
|
var bytes = new byte[size];
|
|
new Random(42).NextBytes(bytes);
|
|
File.WriteAllBytes(path, bytes);
|
|
try
|
|
{
|
|
TypeMapBuildContract.Hash(path).ShouldBe(Convert.ToHexString(SHA256.HashData(bytes)));
|
|
}
|
|
finally
|
|
{
|
|
File.Delete(path);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Valid_zero_slot_receipt_is_bound_and_reusable()
|
|
{
|
|
using var fixture = new Fixture(false);
|
|
fixture.Verify(true).ShouldBeTrue();
|
|
fixture.Verify(false).ShouldBeTrue();
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("input")]
|
|
[InlineData("output")]
|
|
[InlineData("symbols")]
|
|
[InlineData("tool")]
|
|
[InlineData("binding")]
|
|
public void Changed_evidence_is_rejected_before_consumption(string change)
|
|
{
|
|
using var fixture = new Fixture(true);
|
|
fixture.Verify(true).ShouldBeTrue();
|
|
var path = change switch { "input" => fixture.Input, "output" => fixture.Output, "symbols" => fixture.Symbols,
|
|
"tool" => fixture.Tool, _ => fixture.Receipt + ".binding" };
|
|
File.AppendAllText(path, "changed");
|
|
fixture.Verify(false).ShouldBeFalse();
|
|
fixture.Engine.Errors.ShouldContain(e => e.Code == "ATOMUIREG007");
|
|
}
|
|
|
|
[Fact]
|
|
public void Consumption_gate_rejects_an_empty_actual_input_set()
|
|
{
|
|
using var fixture = new Fixture(true);
|
|
fixture.Verify(true).ShouldBeTrue();
|
|
var task = fixture.Task(false);
|
|
task.RequireConsumedAssemblies = true;
|
|
task.Execute().ShouldBeFalse();
|
|
fixture.Engine.Errors.ShouldContain(e => e.Code == "ATOMUIREG007");
|
|
}
|
|
|
|
[Fact]
|
|
public void Replaced_actual_aot_input_is_rejected_even_when_linked_directory_is_valid()
|
|
{
|
|
using var fixture = new Fixture(true);
|
|
fixture.Verify(true).ShouldBeTrue();
|
|
var task = fixture.Task(false);
|
|
var consumed = Path.Combine(fixture.Directory, "consumed", Path.GetFileName(fixture.Output));
|
|
System.IO.Directory.CreateDirectory(Path.GetDirectoryName(consumed)!);
|
|
File.Copy(fixture.Output, consumed); File.AppendAllText(consumed, "changed");
|
|
task.ConsumedAssemblies = [new TestTaskItem(consumed)];
|
|
task.Execute().ShouldBeFalse();
|
|
fixture.Engine.Errors.ShouldContain(e => e.Code == "ATOMUIREG007");
|
|
}
|
|
|
|
private sealed class Fixture : IDisposable
|
|
{
|
|
internal readonly string Directory = Path.Combine(Path.GetTempPath(), "atomui-receipt-" + Guid.NewGuid().ToString("N"));
|
|
internal readonly RecordingBuildEngine Engine = new();
|
|
internal string Input => Path.Combine(Directory, "input.dll");
|
|
internal string Output => Path.Combine(Directory, "linked", "input.dll");
|
|
internal string Symbols => Path.ChangeExtension(Output, ".pdb");
|
|
internal string Tool => Path.Combine(Directory, "tool.dll");
|
|
internal string Receipt => Path.Combine(Directory, "receipt.json");
|
|
private string Signature => Path.Combine(Directory, "inputs.json");
|
|
internal Fixture(bool withAssembly)
|
|
{
|
|
System.IO.Directory.CreateDirectory(Path.Combine(Directory, "linked"));
|
|
var assembly = typeof(TypeMapReceiptIntegrityTests).Assembly.Location;
|
|
File.Copy(assembly, Input); File.Copy(assembly, Output); File.Copy(assembly, Tool);
|
|
File.WriteAllText(Symbols, "verified symbol bytes");
|
|
var identity = AssemblyName.GetAssemblyName(Input).FullName!;
|
|
var hash = TypeMapBuildContract.Hash(Input);
|
|
var signature = new TypeMapLinkSignature(TypeMapBuildContract.Capability, Tool, identity, hash,
|
|
Tool, TypeMapBuildContract.LinkerIdentity, TypeMapBuildContract.LinkerVersion, Receipt, Path.GetDirectoryName(Output)!, "fixture",
|
|
[new TypeMapFile(Tool, hash)], withAssembly ? [new TypeMapLinkInput(Input, identity, hash, "link:true")] : []);
|
|
File.WriteAllText(Signature, JsonSerializer.Serialize(signature));
|
|
File.WriteAllText(Receipt, JsonSerializer.Serialize(new
|
|
{
|
|
status = "output-verified", abi = 1, capability = TypeMapBuildContract.Capability, toolIdentity = identity, toolSha256 = hash,
|
|
linkerIdentity = TypeMapBuildContract.LinkerIdentity, linkerVersion = TypeMapBuildContract.LinkerVersion,
|
|
assemblies = withAssembly ? new[] { new { identity, inputSha256 = hash, outputSha256 = hash, outputSymbolsSha256 = TypeMapBuildContract.Hash(Symbols) } } : [],
|
|
accessors = withAssembly ? new[] { new { packageId = "fixture", assembly = identity, group = "fixture.Group", method = "GetMap", bodySha256 = new string('A', 64) } } : []
|
|
}));
|
|
}
|
|
internal VerifyTypeMapReceiptTask Task(bool bind) => new() { BuildEngine = Engine, ReceiptPath = Receipt, SignaturePath = Signature, BindReceipt = bind };
|
|
internal bool Verify(bool bind) => Task(bind).Execute();
|
|
public void Dispose() => System.IO.Directory.Delete(Directory, true);
|
|
}
|
|
|
|
}
|