release orchestrator v1 draft and build fixes
This commit is contained in:
@@ -16,9 +16,7 @@
|
||||
<PackageReference Include="xunit.runner.visualstudio">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" />
|
||||
<PackageReference Include="Moq" />
|
||||
</PackageReference> <PackageReference Include="Moq" />
|
||||
<PackageReference Include="FluentAssertions" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -0,0 +1,297 @@
|
||||
// Copyright (c) StellaOps. All rights reserved.
|
||||
// Licensed under AGPL-3.0-or-later. See LICENSE in the project root.
|
||||
|
||||
using System.Collections.Immutable;
|
||||
using FluentAssertions;
|
||||
|
||||
namespace StellaOps.BinaryIndex.DeltaSig.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for DeltaSignatureMatcher.CompareSignaturesAsync via SymbolChangeTracer.
|
||||
/// Note: CompareSignaturesAsync only requires ISymbolChangeTracer, not disassembly services.
|
||||
/// </summary>
|
||||
[Trait("Category", "Unit")]
|
||||
public sealed class ExtendedMatcherTests
|
||||
{
|
||||
private readonly SymbolChangeTracer _changeTracer;
|
||||
|
||||
public ExtendedMatcherTests()
|
||||
{
|
||||
_changeTracer = new SymbolChangeTracer();
|
||||
}
|
||||
|
||||
// Helper to directly test the comparison logic that CompareSignaturesAsync uses
|
||||
private DeltaComparisonResult CompareSignatures(DeltaSignature from, DeltaSignature to)
|
||||
{
|
||||
var symbolResults = _changeTracer.CompareAllSymbols(from, to);
|
||||
|
||||
var summary = new DeltaComparisonSummary
|
||||
{
|
||||
TotalSymbols = symbolResults.Count,
|
||||
UnchangedSymbols = symbolResults.Count(r => r.ChangeType == SymbolChangeType.Unchanged),
|
||||
AddedSymbols = symbolResults.Count(r => r.ChangeType == SymbolChangeType.Added),
|
||||
RemovedSymbols = symbolResults.Count(r => r.ChangeType == SymbolChangeType.Removed),
|
||||
ModifiedSymbols = symbolResults.Count(r => r.ChangeType == SymbolChangeType.Modified),
|
||||
PatchedSymbols = symbolResults.Count(r => r.ChangeType == SymbolChangeType.Patched),
|
||||
AverageConfidence = symbolResults.Count > 0
|
||||
? symbolResults.Average(r => r.Confidence)
|
||||
: 0.0,
|
||||
TotalSizeDelta = symbolResults.Sum(r => r.SizeDelta)
|
||||
};
|
||||
|
||||
return new DeltaComparisonResult
|
||||
{
|
||||
FromSignatureId = from.SignatureId,
|
||||
ToSignatureId = to.SignatureId,
|
||||
SymbolResults = [.. symbolResults],
|
||||
Summary = summary
|
||||
};
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CompareSignaturesAsync_IdenticalSignatures_ReturnsAllUnchanged()
|
||||
{
|
||||
// Arrange
|
||||
var symbols = new[]
|
||||
{
|
||||
CreateSymbol("func1", "sha256:abc", 100),
|
||||
CreateSymbol("func2", "sha256:def", 200)
|
||||
};
|
||||
|
||||
var fromSig = CreateSignature("from-1", symbols);
|
||||
var toSig = CreateSignature("to-1", symbols);
|
||||
|
||||
// Act
|
||||
var result = CompareSignatures(fromSig, toSig);
|
||||
|
||||
// Assert
|
||||
result.FromSignatureId.Should().Be("from-1");
|
||||
result.ToSignatureId.Should().Be("to-1");
|
||||
result.Summary.TotalSymbols.Should().Be(2);
|
||||
result.Summary.UnchangedSymbols.Should().Be(2);
|
||||
result.Summary.AddedSymbols.Should().Be(0);
|
||||
result.Summary.RemovedSymbols.Should().Be(0);
|
||||
result.Summary.ModifiedSymbols.Should().Be(0);
|
||||
result.Summary.AverageConfidence.Should().Be(1.0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CompareSignaturesAsync_AddedSymbols_CountsCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var fromSig = CreateSignature("from", [CreateSymbol("existing", "sha256:a", 100)]);
|
||||
var toSig = CreateSignature("to",
|
||||
[
|
||||
CreateSymbol("existing", "sha256:a", 100),
|
||||
CreateSymbol("new1", "sha256:b", 200),
|
||||
CreateSymbol("new2", "sha256:c", 300)
|
||||
]);
|
||||
|
||||
// Act
|
||||
var result = CompareSignatures(fromSig, toSig);
|
||||
|
||||
// Assert
|
||||
result.Summary.AddedSymbols.Should().Be(2);
|
||||
result.Summary.UnchangedSymbols.Should().Be(1);
|
||||
result.Summary.TotalSymbols.Should().Be(3);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CompareSignaturesAsync_RemovedSymbols_CountsCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var fromSig = CreateSignature("from",
|
||||
[
|
||||
CreateSymbol("staying", "sha256:a", 100),
|
||||
CreateSymbol("removed1", "sha256:b", 200),
|
||||
CreateSymbol("removed2", "sha256:c", 300)
|
||||
]);
|
||||
var toSig = CreateSignature("to", [CreateSymbol("staying", "sha256:a", 100)]);
|
||||
|
||||
// Act
|
||||
var result = CompareSignatures(fromSig, toSig);
|
||||
|
||||
// Assert
|
||||
result.Summary.RemovedSymbols.Should().Be(2);
|
||||
result.Summary.UnchangedSymbols.Should().Be(1);
|
||||
result.Summary.TotalSymbols.Should().Be(3);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CompareSignaturesAsync_ModifiedSymbols_CountsCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var fromSig = CreateSignature("from",
|
||||
[
|
||||
CreateSymbol("unchanged", "sha256:same", 100),
|
||||
CreateSymbol("modified", "sha256:old", 200)
|
||||
]);
|
||||
var toSig = CreateSignature("to",
|
||||
[
|
||||
CreateSymbol("unchanged", "sha256:same", 100),
|
||||
CreateSymbol("modified", "sha256:new", 220)
|
||||
]);
|
||||
|
||||
// Act
|
||||
var result = CompareSignatures(fromSig, toSig);
|
||||
|
||||
// Assert
|
||||
result.Summary.ModifiedSymbols.Should().Be(1);
|
||||
result.Summary.UnchangedSymbols.Should().Be(1);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CompareSignaturesAsync_CalculatesTotalSizeDelta()
|
||||
{
|
||||
// Arrange
|
||||
var fromSig = CreateSignature("from",
|
||||
[
|
||||
CreateSymbol("func1", "sha256:a", 100),
|
||||
CreateSymbol("func2", "sha256:b", 200)
|
||||
]);
|
||||
var toSig = CreateSignature("to",
|
||||
[
|
||||
CreateSymbol("func1", "sha256:c", 150), // +50
|
||||
CreateSymbol("func2", "sha256:d", 180) // -20
|
||||
]);
|
||||
|
||||
// Act
|
||||
var result = CompareSignatures(fromSig, toSig);
|
||||
|
||||
// Assert - Total delta should be +50 - 20 = +30
|
||||
result.Summary.TotalSizeDelta.Should().Be(30);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CompareSignaturesAsync_MixedChanges_SummaryIsComplete()
|
||||
{
|
||||
// Arrange
|
||||
var fromSig = CreateSignature("from",
|
||||
[
|
||||
CreateSymbol("unchanged", "sha256:same", 100),
|
||||
CreateSymbol("modified", "sha256:old", 200),
|
||||
CreateSymbol("removed", "sha256:gone", 150)
|
||||
]);
|
||||
var toSig = CreateSignature("to",
|
||||
[
|
||||
CreateSymbol("unchanged", "sha256:same", 100),
|
||||
CreateSymbol("modified", "sha256:new", 220),
|
||||
CreateSymbol("added", "sha256:brand-new", 300)
|
||||
]);
|
||||
|
||||
// Act
|
||||
var result = CompareSignatures(fromSig, toSig);
|
||||
|
||||
// Assert
|
||||
result.Summary.TotalSymbols.Should().Be(4);
|
||||
result.Summary.UnchangedSymbols.Should().Be(1);
|
||||
result.Summary.ModifiedSymbols.Should().Be(1);
|
||||
result.Summary.AddedSymbols.Should().Be(1);
|
||||
result.Summary.RemovedSymbols.Should().Be(1);
|
||||
result.Summary.PatchedSymbols.Should().Be(0);
|
||||
result.SymbolResults.Should().HaveCount(4);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CompareSignaturesAsync_PatchedSymbols_CountsCorrectly()
|
||||
{
|
||||
// Arrange - High chunk similarity (>= 85%) with CFG change -> 6/7 = 86%
|
||||
var fromSig = CreateSignature("from",
|
||||
[
|
||||
CreateSymbolWithChunks("patched", "sha256:before", 350, 10,
|
||||
[("sha256:1", 0, 50), ("sha256:2", 50, 50), ("sha256:3", 100, 50), ("sha256:4", 150, 50),
|
||||
("sha256:5", 200, 50), ("sha256:6", 250, 50), ("sha256:7", 300, 50)])
|
||||
]);
|
||||
var toSig = CreateSignature("to",
|
||||
[
|
||||
CreateSymbolWithChunks("patched", "sha256:after", 360, 12,
|
||||
[("sha256:1", 0, 50), ("sha256:2", 50, 50), ("sha256:3", 100, 50), ("sha256:4", 150, 50),
|
||||
("sha256:5", 200, 50), ("sha256:6", 250, 50), ("sha256:new", 300, 60)])
|
||||
]);
|
||||
|
||||
// Act
|
||||
var result = CompareSignatures(fromSig, toSig);
|
||||
|
||||
// Assert
|
||||
result.Summary.PatchedSymbols.Should().Be(1);
|
||||
result.Summary.ModifiedSymbols.Should().Be(0); // Patched is separate from Modified
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CompareSignaturesAsync_EmptySignatures_ReturnsEmptyResult()
|
||||
{
|
||||
// Arrange
|
||||
var fromSig = CreateSignature("from", []);
|
||||
var toSig = CreateSignature("to", []);
|
||||
|
||||
// Act
|
||||
var result = CompareSignatures(fromSig, toSig);
|
||||
|
||||
// Assert
|
||||
result.Summary.TotalSymbols.Should().Be(0);
|
||||
result.SymbolResults.Should().BeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CompareSignaturesAsync_SymbolResultsContainDetailedInfo()
|
||||
{
|
||||
// Arrange
|
||||
var fromSig = CreateSignature("from", [CreateSymbol("func", "sha256:old", 100)]);
|
||||
var toSig = CreateSignature("to", [CreateSymbol("func", "sha256:new", 120)]);
|
||||
|
||||
// Act
|
||||
var result = CompareSignatures(fromSig, toSig);
|
||||
|
||||
// Assert
|
||||
result.SymbolResults.Should().HaveCount(1);
|
||||
var symbolResult = result.SymbolResults[0];
|
||||
symbolResult.SymbolName.Should().Be("func");
|
||||
symbolResult.FromHash.Should().Be("sha256:old");
|
||||
symbolResult.ToHash.Should().Be("sha256:new");
|
||||
symbolResult.SizeDelta.Should().Be(20);
|
||||
symbolResult.ChangeExplanation.Should().NotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
// Helper methods
|
||||
|
||||
private static SymbolSignature CreateSymbol(string name, string hash, int size, int? cfgCount = null)
|
||||
{
|
||||
return new SymbolSignature
|
||||
{
|
||||
Name = name,
|
||||
HashAlg = "sha256",
|
||||
HashHex = hash,
|
||||
SizeBytes = size,
|
||||
CfgBbCount = cfgCount
|
||||
};
|
||||
}
|
||||
|
||||
private static SymbolSignature CreateSymbolWithChunks(
|
||||
string name, string hash, int size, int? cfgCount,
|
||||
(string hash, int offset, int size)[] chunks)
|
||||
{
|
||||
return new SymbolSignature
|
||||
{
|
||||
Name = name,
|
||||
HashAlg = "sha256",
|
||||
HashHex = hash,
|
||||
SizeBytes = size,
|
||||
CfgBbCount = cfgCount,
|
||||
Chunks = chunks.Select(c => new ChunkHash(c.offset, c.size, c.hash)).ToImmutableArray()
|
||||
};
|
||||
}
|
||||
|
||||
private static DeltaSignature CreateSignature(string id, SymbolSignature[] symbols)
|
||||
{
|
||||
return new DeltaSignature
|
||||
{
|
||||
SignatureId = id,
|
||||
Cve = "CVE-2026-0001",
|
||||
Package = new PackageRef("testpkg", null),
|
||||
Target = new TargetRef("x86_64", "gnu"),
|
||||
Normalization = new NormalizationRef("test", "1.0", []),
|
||||
SignatureState = "vulnerable",
|
||||
Symbols = symbols.ToImmutableArray()
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,8 @@
|
||||
<PackageReference Include="FluentAssertions" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
|
||||
<PackageReference Include="Moq" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -0,0 +1,273 @@
|
||||
// Copyright (c) StellaOps. All rights reserved.
|
||||
// Licensed under AGPL-3.0-or-later. See LICENSE in the project root.
|
||||
|
||||
using System.Collections.Immutable;
|
||||
using FluentAssertions;
|
||||
|
||||
namespace StellaOps.BinaryIndex.DeltaSig.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for SymbolChangeTracer.
|
||||
/// </summary>
|
||||
[Trait("Category", "Unit")]
|
||||
public sealed class SymbolChangeTracerTests
|
||||
{
|
||||
private readonly SymbolChangeTracer _tracer;
|
||||
|
||||
public SymbolChangeTracerTests()
|
||||
{
|
||||
_tracer = new SymbolChangeTracer();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CompareSymbols_AddedSymbol_ReturnsAddedChangeType()
|
||||
{
|
||||
// Arrange
|
||||
var toSymbol = CreateSymbol("new_function", "sha256:abc123", 512);
|
||||
|
||||
// Act
|
||||
var result = _tracer.CompareSymbols(null, toSymbol);
|
||||
|
||||
// Assert
|
||||
result.ChangeType.Should().Be(SymbolChangeType.Added);
|
||||
result.SymbolName.Should().Be("new_function");
|
||||
result.SizeDelta.Should().Be(512);
|
||||
result.ToHash.Should().Be("sha256:abc123");
|
||||
result.FromHash.Should().BeNull();
|
||||
result.Confidence.Should().Be(1.0);
|
||||
result.ChangeExplanation.Should().Contain("added");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CompareSymbols_RemovedSymbol_ReturnsRemovedChangeType()
|
||||
{
|
||||
// Arrange
|
||||
var fromSymbol = CreateSymbol("old_function", "sha256:def456", 256);
|
||||
|
||||
// Act
|
||||
var result = _tracer.CompareSymbols(fromSymbol, null);
|
||||
|
||||
// Assert
|
||||
result.ChangeType.Should().Be(SymbolChangeType.Removed);
|
||||
result.SymbolName.Should().Be("old_function");
|
||||
result.SizeDelta.Should().Be(-256);
|
||||
result.FromHash.Should().Be("sha256:def456");
|
||||
result.ToHash.Should().BeNull();
|
||||
result.Confidence.Should().Be(1.0);
|
||||
result.ChangeExplanation.Should().Contain("removed");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CompareSymbols_UnchangedSymbol_ReturnsUnchangedChangeType()
|
||||
{
|
||||
// Arrange
|
||||
var fromSymbol = CreateSymbol("stable_function", "sha256:same", 128);
|
||||
var toSymbol = CreateSymbol("stable_function", "sha256:same", 128);
|
||||
|
||||
// Act
|
||||
var result = _tracer.CompareSymbols(fromSymbol, toSymbol);
|
||||
|
||||
// Assert
|
||||
result.ChangeType.Should().Be(SymbolChangeType.Unchanged);
|
||||
result.ExactMatch.Should().BeTrue();
|
||||
result.SizeDelta.Should().Be(0);
|
||||
result.Confidence.Should().Be(1.0);
|
||||
result.MatchMethod.Should().Be("ExactHash");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CompareSymbols_ModifiedSymbol_ReturnsModifiedChangeType()
|
||||
{
|
||||
// Arrange
|
||||
var fromSymbol = CreateSymbol("func", "sha256:before", 100);
|
||||
var toSymbol = CreateSymbol("func", "sha256:after", 120);
|
||||
|
||||
// Act
|
||||
var result = _tracer.CompareSymbols(fromSymbol, toSymbol);
|
||||
|
||||
// Assert
|
||||
result.ChangeType.Should().Be(SymbolChangeType.Modified);
|
||||
result.ExactMatch.Should().BeFalse();
|
||||
result.SizeDelta.Should().Be(20);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CompareSymbols_HighChunkSimilarityWithCfgChange_ReturnsPatchedChangeType()
|
||||
{
|
||||
// Arrange - >= 85% chunk similarity (6/7 = 86%) with small CFG change indicates patch
|
||||
var fromSymbol = CreateSymbolWithChunks("patched_func", "sha256:before", 350, 10,
|
||||
[("sha256:chunk1", 0, 50), ("sha256:chunk2", 50, 50), ("sha256:chunk3", 100, 50),
|
||||
("sha256:chunk4", 150, 50), ("sha256:chunk5", 200, 50), ("sha256:chunk6", 250, 50), ("sha256:chunk7", 300, 50)]);
|
||||
var toSymbol = CreateSymbolWithChunks("patched_func", "sha256:after", 360, 12,
|
||||
[("sha256:chunk1", 0, 50), ("sha256:chunk2", 50, 50), ("sha256:chunk3", 100, 50),
|
||||
("sha256:chunk4", 150, 50), ("sha256:chunk5", 200, 50), ("sha256:chunk6", 250, 50), ("sha256:newchunk", 300, 60)]);
|
||||
|
||||
// Act
|
||||
var result = _tracer.CompareSymbols(fromSymbol, toSymbol);
|
||||
|
||||
// Assert
|
||||
result.ChangeType.Should().Be(SymbolChangeType.Patched);
|
||||
result.CfgBlockDelta.Should().Be(2);
|
||||
result.ChangeExplanation.Should().Contain("patched");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CompareSymbols_HighChunkSimilarity_ReturnsModifiedWithHighConfidence()
|
||||
{
|
||||
// Arrange - 75% chunk similarity
|
||||
var fromSymbol = CreateSymbolWithChunks("func", "sha256:before", 200, null,
|
||||
[("sha256:chunk1", 0, 50), ("sha256:chunk2", 50, 50), ("sha256:chunk3", 100, 50), ("sha256:chunk4", 150, 50)]);
|
||||
var toSymbol = CreateSymbolWithChunks("func", "sha256:after", 200, null,
|
||||
[("sha256:chunk1", 0, 50), ("sha256:chunk2", 50, 50), ("sha256:chunk3", 100, 50), ("sha256:new", 150, 50)]);
|
||||
|
||||
// Act
|
||||
var result = _tracer.CompareSymbols(fromSymbol, toSymbol);
|
||||
|
||||
// Assert
|
||||
result.ChangeType.Should().Be(SymbolChangeType.Modified);
|
||||
result.ChunksMatched.Should().Be(3);
|
||||
result.ChunksTotal.Should().Be(4);
|
||||
result.Confidence.Should().BeGreaterThanOrEqualTo(0.7);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CompareSymbols_SemanticMatch_ReturnsModifiedWithSemanticMethod()
|
||||
{
|
||||
// Arrange - Different hash but same semantic fingerprint
|
||||
var fromSymbol = CreateSymbolWithSemantic("func", "sha256:before", 200, "sha256:semantic1");
|
||||
var toSymbol = CreateSymbolWithSemantic("func", "sha256:after", 200, "sha256:semantic1");
|
||||
|
||||
// Act
|
||||
var result = _tracer.CompareSymbols(fromSymbol, toSymbol);
|
||||
|
||||
// Assert
|
||||
result.ChangeType.Should().Be(SymbolChangeType.Modified);
|
||||
result.MatchMethod.Should().Be("SemanticHash");
|
||||
result.Confidence.Should().BeGreaterThanOrEqualTo(0.8);
|
||||
result.ChangeExplanation.Should().Contain("semantically equivalent");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CompareSymbols_BothNull_ThrowsArgumentException()
|
||||
{
|
||||
// Act & Assert
|
||||
var action = () => _tracer.CompareSymbols(null, null);
|
||||
action.Should().Throw<ArgumentException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CompareAllSymbols_MixedChanges_ReturnsCorrectResults()
|
||||
{
|
||||
// Arrange
|
||||
var fromSignature = CreateSignature("sig-from",
|
||||
[
|
||||
CreateSymbol("unchanged", "sha256:same", 100),
|
||||
CreateSymbol("modified", "sha256:old", 200),
|
||||
CreateSymbol("removed", "sha256:gone", 150)
|
||||
]);
|
||||
|
||||
var toSignature = CreateSignature("sig-to",
|
||||
[
|
||||
CreateSymbol("unchanged", "sha256:same", 100),
|
||||
CreateSymbol("modified", "sha256:new", 220),
|
||||
CreateSymbol("added", "sha256:new", 300)
|
||||
]);
|
||||
|
||||
// Act
|
||||
var results = _tracer.CompareAllSymbols(fromSignature, toSignature);
|
||||
|
||||
// Assert
|
||||
results.Should().HaveCount(4);
|
||||
results.Should().ContainSingle(r => r.ChangeType == SymbolChangeType.Unchanged);
|
||||
results.Should().ContainSingle(r => r.ChangeType == SymbolChangeType.Modified);
|
||||
results.Should().ContainSingle(r => r.ChangeType == SymbolChangeType.Added);
|
||||
results.Should().ContainSingle(r => r.ChangeType == SymbolChangeType.Removed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CompareAllSymbols_ResultsAreSortedByName()
|
||||
{
|
||||
// Arrange
|
||||
var fromSignature = CreateSignature("from", [CreateSymbol("z_func", "sha256:a", 100)]);
|
||||
var toSignature = CreateSignature("to", [CreateSymbol("a_func", "sha256:b", 100)]);
|
||||
|
||||
// Act
|
||||
var results = _tracer.CompareAllSymbols(fromSignature, toSignature);
|
||||
|
||||
// Assert
|
||||
results.Should().HaveCount(2);
|
||||
results[0].SymbolName.Should().Be("a_func");
|
||||
results[1].SymbolName.Should().Be("z_func");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CompareSymbols_RecordsMatchedChunkIndices()
|
||||
{
|
||||
// Arrange
|
||||
var fromSymbol = CreateSymbolWithChunks("func", "sha256:before", 200, null,
|
||||
[("sha256:a", 0, 50), ("sha256:b", 50, 50), ("sha256:c", 100, 50), ("sha256:d", 150, 50)]);
|
||||
var toSymbol = CreateSymbolWithChunks("func", "sha256:after", 200, null,
|
||||
[("sha256:a", 0, 50), ("sha256:x", 50, 50), ("sha256:c", 100, 50), ("sha256:y", 150, 50)]);
|
||||
|
||||
// Act
|
||||
var result = _tracer.CompareSymbols(fromSymbol, toSymbol);
|
||||
|
||||
// Assert
|
||||
result.MatchedChunkIndices.Should().BeEquivalentTo([0, 2]);
|
||||
}
|
||||
|
||||
// Helper methods
|
||||
|
||||
private static SymbolSignature CreateSymbol(string name, string hash, int size, int? cfgCount = null)
|
||||
{
|
||||
return new SymbolSignature
|
||||
{
|
||||
Name = name,
|
||||
HashAlg = "sha256",
|
||||
HashHex = hash,
|
||||
SizeBytes = size,
|
||||
CfgBbCount = cfgCount
|
||||
};
|
||||
}
|
||||
|
||||
private static SymbolSignature CreateSymbolWithChunks(
|
||||
string name, string hash, int size, int? cfgCount,
|
||||
(string hash, int offset, int size)[] chunks)
|
||||
{
|
||||
return new SymbolSignature
|
||||
{
|
||||
Name = name,
|
||||
HashAlg = "sha256",
|
||||
HashHex = hash,
|
||||
SizeBytes = size,
|
||||
CfgBbCount = cfgCount,
|
||||
Chunks = chunks.Select(c => new ChunkHash(c.offset, c.size, c.hash)).ToImmutableArray()
|
||||
};
|
||||
}
|
||||
|
||||
private static SymbolSignature CreateSymbolWithSemantic(string name, string hash, int size, string semanticHash)
|
||||
{
|
||||
return new SymbolSignature
|
||||
{
|
||||
Name = name,
|
||||
HashAlg = "sha256",
|
||||
HashHex = hash,
|
||||
SizeBytes = size,
|
||||
SemanticHashHex = semanticHash
|
||||
};
|
||||
}
|
||||
|
||||
private static DeltaSignature CreateSignature(string id, SymbolSignature[] symbols)
|
||||
{
|
||||
return new DeltaSignature
|
||||
{
|
||||
SignatureId = id,
|
||||
Cve = "CVE-2026-0001",
|
||||
Package = new PackageRef("testpkg", null),
|
||||
Target = new TargetRef("x86_64", "gnu"),
|
||||
Normalization = new NormalizationRef("test", "1.0", []),
|
||||
SignatureState = "vulnerable",
|
||||
Symbols = symbols.ToImmutableArray()
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user