Restructure solution layout by module

This commit is contained in:
master
2025-10-28 15:10:40 +02:00
parent 95daa159c4
commit d870da18ce
4103 changed files with 192899 additions and 187024 deletions

View File

@@ -0,0 +1,88 @@
using StellaOps.Scanner.Analyzers.Lang.Tests.TestUtilities;
namespace StellaOps.Scanner.Analyzers.Lang.Tests.Core;
public sealed class LanguageAnalyzerResultTests
{
[Fact]
public async Task MergesDuplicateComponentsDeterministicallyAsync()
{
var analyzer = new DuplicateComponentAnalyzer();
var engine = new LanguageAnalyzerEngine(new[] { analyzer });
var root = TestPaths.CreateTemporaryDirectory();
try
{
var context = new LanguageAnalyzerContext(root, TimeProvider.System);
var result = await engine.AnalyzeAsync(context, CancellationToken.None);
var component = Assert.Single(result.Components);
Assert.Equal("purl::pkg:example/acme@2.0.0", component.ComponentKey);
Assert.Equal("pkg:example/acme@2.0.0", component.Purl);
Assert.True(component.UsedByEntrypoint);
Assert.Equal(2, component.Evidence.Count);
Assert.Equal(3, component.Metadata.Count);
// Metadata retains stable ordering (sorted by key)
var keys = component.Metadata.Keys.ToArray();
Assert.Equal(new[] { "artifactId", "groupId", "path" }, keys);
// Evidence de-duplicates via comparison key
Assert.Equal(2, component.Evidence.Count);
}
finally
{
TestPaths.SafeDelete(root);
}
}
private sealed class DuplicateComponentAnalyzer : ILanguageAnalyzer
{
public string Id => "duplicate";
public string DisplayName => "Duplicate Analyzer";
public async ValueTask AnalyzeAsync(LanguageAnalyzerContext context, LanguageComponentWriter writer, CancellationToken cancellationToken)
{
await Task.Yield();
var metadataA = new[]
{
new KeyValuePair<string, string?>("groupId", "example"),
new KeyValuePair<string, string?>("artifactId", "acme")
};
var metadataB = new[]
{
new KeyValuePair<string, string?>("artifactId", "acme"),
new KeyValuePair<string, string?>("path", ".")
};
var evidence = new[]
{
new LanguageComponentEvidence(LanguageEvidenceKind.File, "manifest", "META-INF/MANIFEST.MF", null, null),
new LanguageComponentEvidence(LanguageEvidenceKind.Metadata, "pom", "pom.xml", "groupId=example", null)
};
writer.AddFromPurl(
analyzerId: Id,
purl: "pkg:example/acme@2.0.0",
name: "acme",
version: "2.0.0",
type: "example",
metadata: metadataA,
evidence: evidence,
usedByEntrypoint: true);
// duplicate insert with different metadata ordering
writer.AddFromPurl(
analyzerId: Id,
purl: "pkg:example/acme@2.0.0",
name: "acme",
version: "2.0.0",
type: "example",
metadata: metadataB,
evidence: evidence,
usedByEntrypoint: false);
}
}
}

View File

@@ -0,0 +1,70 @@
using StellaOps.Scanner.Core.Contracts;
namespace StellaOps.Scanner.Analyzers.Lang.Tests.Core;
public sealed class LanguageComponentMapperTests
{
[Fact]
public void ToComponentRecordsProjectsDeterministicComponents()
{
// Arrange
var analyzerId = "node";
var records = new[]
{
LanguageComponentRecord.FromPurl(
analyzerId: analyzerId,
purl: "pkg:npm/example@1.0.0",
name: "example",
version: "1.0.0",
type: "npm",
metadata: new Dictionary<string, string?>()
{
["path"] = "packages/app",
["license"] = "MIT"
},
evidence: new[]
{
new LanguageComponentEvidence(LanguageEvidenceKind.File, "package.json", "packages/app/package.json", null, "abc123")
},
usedByEntrypoint: true),
LanguageComponentRecord.FromExplicitKey(
analyzerId: analyzerId,
componentKey: "bin::sha256:deadbeef",
purl: null,
name: "app-binary",
version: null,
type: "binary",
metadata: new Dictionary<string, string?>()
{
["description"] = "Utility binary"
},
evidence: new[]
{
new LanguageComponentEvidence(LanguageEvidenceKind.Derived, "entrypoint", "/usr/local/bin/app", "ENTRYPOINT", null)
})
};
// Act
var layerDigest = LanguageComponentMapper.ComputeLayerDigest(analyzerId);
var results = LanguageComponentMapper.ToComponentRecords(analyzerId, records, layerDigest);
// Assert
Assert.Equal(2, results.Length);
Assert.All(results, component => Assert.Equal(layerDigest, component.LayerDigest));
var first = results[0];
Assert.Equal("bin::sha256:deadbeef", first.Identity.Key);
Assert.Equal("Utility binary", first.Metadata!.Properties!["stellaops.lang.meta.description"]);
Assert.Equal("derived", first.Evidence.Single().Kind);
var second = results[1];
Assert.Equal("pkg:npm/example@1.0.0", second.Identity.Key); // prefix removed
Assert.True(second.Usage.UsedByEntrypoint);
Assert.Contains("MIT", second.Metadata!.Licenses!);
Assert.Equal("packages/app", second.Metadata.Properties!["stellaops.lang.meta.path"]);
Assert.Equal("abc123", second.Metadata.Properties!["stellaops.lang.evidence.0.sha256"]);
Assert.Equal("file", second.Evidence.Single().Kind);
Assert.Equal("packages/app/package.json", second.Evidence.Single().Value);
Assert.Equal("package.json", second.Evidence.Single().Source);
}
}

View File

@@ -0,0 +1,102 @@
using StellaOps.Scanner.Analyzers.Lang;
using StellaOps.Scanner.Analyzers.Lang.Tests.Harness;
using StellaOps.Scanner.Analyzers.Lang.Tests.TestUtilities;
namespace StellaOps.Scanner.Analyzers.Lang.Tests.Determinism;
public sealed class LanguageAnalyzerHarnessTests
{
[Fact]
public async Task HarnessProducesDeterministicOutputAsync()
{
var fixturePath = TestPaths.ResolveFixture("determinism", "basic", "input");
var goldenPath = TestPaths.ResolveFixture("determinism", "basic", "expected.json");
var cancellationToken = TestContext.Current.CancellationToken;
var analyzers = new ILanguageAnalyzer[]
{
new FakeLanguageAnalyzer(
"fake-java",
LanguageComponentRecord.FromPurl(
analyzerId: "fake-java",
purl: "pkg:maven/org.example/example-lib@1.2.3",
name: "example-lib",
version: "1.2.3",
type: "maven",
metadata: new Dictionary<string, string?>
{
["groupId"] = "org.example",
["artifactId"] = "example-lib",
},
evidence: new []
{
new LanguageComponentEvidence(LanguageEvidenceKind.File, "pom.properties", "META-INF/maven/org.example/example-lib/pom.properties", null, "abc123"),
}),
LanguageComponentRecord.FromExplicitKey(
analyzerId: "fake-java",
componentKey: "bin::sha256:deadbeef",
purl: null,
name: "example-cli",
version: null,
type: "bin",
metadata: new Dictionary<string, string?>
{
["sha256"] = "deadbeef",
},
evidence: new []
{
new LanguageComponentEvidence(LanguageEvidenceKind.File, "binary", "usr/local/bin/example", null, "deadbeef"),
})),
new FakeLanguageAnalyzer(
"fake-node",
LanguageComponentRecord.FromPurl(
analyzerId: "fake-node",
purl: "pkg:npm/example-package@4.5.6",
name: "example-package",
version: "4.5.6",
type: "npm",
metadata: new Dictionary<string, string?>
{
["workspace"] = "packages/example",
},
evidence: new []
{
new LanguageComponentEvidence(LanguageEvidenceKind.File, "package.json", "packages/example/package.json", null, null),
},
usedByEntrypoint: true)),
};
await LanguageAnalyzerTestHarness.AssertDeterministicAsync(fixturePath, goldenPath, analyzers, cancellationToken);
var first = await LanguageAnalyzerTestHarness.RunToJsonAsync(fixturePath, analyzers, cancellationToken);
var second = await LanguageAnalyzerTestHarness.RunToJsonAsync(fixturePath, analyzers, cancellationToken);
Assert.Equal(first, second);
}
private sealed class FakeLanguageAnalyzer : ILanguageAnalyzer
{
private readonly IReadOnlyList<LanguageComponentRecord> _components;
public FakeLanguageAnalyzer(string id, params LanguageComponentRecord[] components)
{
Id = id;
DisplayName = id;
_components = components ?? Array.Empty<LanguageComponentRecord>();
}
public string Id { get; }
public string DisplayName { get; }
public async ValueTask AnalyzeAsync(LanguageAnalyzerContext context, LanguageComponentWriter writer, CancellationToken cancellationToken)
{
await Task.Delay(5, cancellationToken).ConfigureAwait(false); // ensure asynchrony is handled
// Intentionally add in reverse order to prove determinism.
foreach (var component in _components.Reverse())
{
writer.Add(component);
}
}
}
}

View File

@@ -0,0 +1,179 @@
using System;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using StellaOps.Scanner.Analyzers.Lang.DotNet;
using StellaOps.Scanner.Analyzers.Lang.Tests.Harness;
using StellaOps.Scanner.Analyzers.Lang.Tests.TestUtilities;
namespace StellaOps.Scanner.Analyzers.Lang.Tests.DotNet;
public sealed class DotNetLanguageAnalyzerTests
{
[Fact]
public async Task SimpleFixtureProducesDeterministicOutputAsync()
{
var cancellationToken = TestContext.Current.CancellationToken;
var fixturePath = TestPaths.ResolveFixture("lang", "dotnet", "simple");
var goldenPath = Path.Combine(fixturePath, "expected.json");
var analyzers = new ILanguageAnalyzer[]
{
new DotNetLanguageAnalyzer()
};
await LanguageAnalyzerTestHarness.AssertDeterministicAsync(
fixturePath,
goldenPath,
analyzers,
cancellationToken);
}
[Fact]
public async Task SignedFixtureCapturesAssemblyMetadataAsync()
{
var cancellationToken = TestContext.Current.CancellationToken;
var fixturePath = TestPaths.ResolveFixture("lang", "dotnet", "signed");
var goldenPath = Path.Combine(fixturePath, "expected.json");
var analyzers = new ILanguageAnalyzer[]
{
new DotNetLanguageAnalyzer()
};
var inspector = new StubAuthenticodeInspector();
var services = new SingleServiceProvider(inspector);
await LanguageAnalyzerTestHarness.AssertDeterministicAsync(
fixturePath,
goldenPath,
analyzers,
cancellationToken,
usageHints: null,
services: services);
}
[Fact]
public async Task SelfContainedFixtureHandlesNativeAssetsAndUsageAsync()
{
var cancellationToken = TestContext.Current.CancellationToken;
var fixturePath = TestPaths.ResolveFixture("lang", "dotnet", "selfcontained");
var goldenPath = Path.Combine(fixturePath, "expected.json");
var usageHints = new LanguageUsageHints(new[]
{
Path.Combine(fixturePath, "lib", "net10.0", "StellaOps.Toolkit.dll"),
Path.Combine(fixturePath, "runtimes", "linux-x64", "native", "libstellaopsnative.so")
});
var analyzers = new ILanguageAnalyzer[]
{
new DotNetLanguageAnalyzer()
};
await LanguageAnalyzerTestHarness.AssertDeterministicAsync(
fixturePath,
goldenPath,
analyzers,
cancellationToken,
usageHints);
}
[Fact]
public async Task AnalyzerIsThreadSafeUnderConcurrencyAsync()
{
var cancellationToken = TestContext.Current.CancellationToken;
var fixturePath = TestPaths.ResolveFixture("lang", "dotnet", "selfcontained");
var analyzers = new ILanguageAnalyzer[]
{
new DotNetLanguageAnalyzer()
};
var workers = Math.Max(Environment.ProcessorCount, 4);
var tasks = Enumerable.Range(0, workers)
.Select(_ => LanguageAnalyzerTestHarness.RunToJsonAsync(fixturePath, analyzers, cancellationToken));
var results = await Task.WhenAll(tasks);
var first = results[0];
foreach (var result in results)
{
Assert.Equal(first, result);
}
}
[Fact]
public async Task MultiFixtureMergesRuntimeMetadataAsync()
{
var cancellationToken = TestContext.Current.CancellationToken;
var fixturePath = TestPaths.ResolveFixture("lang", "dotnet", "multi");
var goldenPath = Path.Combine(fixturePath, "expected.json");
var analyzers = new ILanguageAnalyzer[]
{
new DotNetLanguageAnalyzer()
};
await LanguageAnalyzerTestHarness.AssertDeterministicAsync(
fixturePath,
goldenPath,
analyzers,
cancellationToken);
var json = await LanguageAnalyzerTestHarness.RunToJsonAsync(
fixturePath,
analyzers,
cancellationToken);
using var document = JsonDocument.Parse(json);
var root = document.RootElement;
Assert.True(root.ValueKind == JsonValueKind.Array, "Result root should be an array.");
Assert.Equal(2, root.GetArrayLength());
var loggingComponent = root.EnumerateArray()
.First(element => element.GetProperty("name").GetString() == "StellaOps.Logging");
var metadata = loggingComponent.GetProperty("metadata");
Assert.Equal("StellaOps.Logging", loggingComponent.GetProperty("name").GetString());
Assert.Equal("2.5.1", loggingComponent.GetProperty("version").GetString());
Assert.Equal("pkg:nuget/stellaops.logging@2.5.1", loggingComponent.GetProperty("purl").GetString());
var ridValues = metadata.EnumerateObject()
.Where(property => property.Name.Contains(".rid", StringComparison.Ordinal))
.Select(property => property.Value.GetString())
.Where(value => !string.IsNullOrEmpty(value))
.Select(value => value!)
.ToHashSet(StringComparer.OrdinalIgnoreCase);
Assert.Contains("linux-x64", ridValues);
Assert.Contains("osx-arm64", ridValues);
Assert.Contains("win-arm64", ridValues);
}
private sealed class StubAuthenticodeInspector : IDotNetAuthenticodeInspector
{
public DotNetAuthenticodeMetadata? TryInspect(string assemblyPath, CancellationToken cancellationToken)
=> new DotNetAuthenticodeMetadata(
Subject: "CN=StellaOps Test Signing",
Issuer: "CN=StellaOps Root",
NotBefore: new DateTimeOffset(2025, 1, 1, 0, 0, 0, TimeSpan.Zero),
NotAfter: new DateTimeOffset(2026, 1, 1, 0, 0, 0, TimeSpan.Zero),
Thumbprint: "AA11BB22CC33DD44EE55FF66GG77HH88II99JJ00",
SerialNumber: "0123456789ABCDEF");
}
private sealed class SingleServiceProvider : IServiceProvider
{
private readonly object _service;
public SingleServiceProvider(object service)
{
_service = service;
}
public object? GetService(Type serviceType)
=> serviceType == typeof(IDotNetAuthenticodeInspector) ? _service : null;
}
}

View File

@@ -0,0 +1,60 @@
[
{
"analyzerId": "fake-java",
"componentKey": "bin::sha256:deadbeef",
"name": "example-cli",
"type": "bin",
"usedByEntrypoint": false,
"metadata": {
"sha256": "deadbeef"
},
"evidence": [
{
"kind": "file",
"source": "binary",
"locator": "usr/local/bin/example",
"sha256": "deadbeef"
}
]
},
{
"analyzerId": "fake-java",
"componentKey": "purl::pkg:maven/org.example/example-lib@1.2.3",
"purl": "pkg:maven/org.example/example-lib@1.2.3",
"name": "example-lib",
"version": "1.2.3",
"type": "maven",
"usedByEntrypoint": false,
"metadata": {
"artifactId": "example-lib",
"groupId": "org.example"
},
"evidence": [
{
"kind": "file",
"source": "pom.properties",
"locator": "META-INF/maven/org.example/example-lib/pom.properties",
"sha256": "abc123"
}
]
},
{
"analyzerId": "fake-node",
"componentKey": "purl::pkg:npm/example-package@4.5.6",
"purl": "pkg:npm/example-package@4.5.6",
"name": "example-package",
"version": "4.5.6",
"type": "npm",
"usedByEntrypoint": true,
"metadata": {
"workspace": "packages/example"
},
"evidence": [
{
"kind": "file",
"source": "package.json",
"locator": "packages/example/package.json"
}
]
}
]

View File

@@ -0,0 +1,84 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v10.0/osx-arm64"
},
"targets": {
".NETCoreApp,Version=v10.0": {
"AppA/2.0.0": {
"dependencies": {
"StellaOps.Toolkit": "1.2.3",
"StellaOps.Logging": "2.5.1"
}
},
"StellaOps.Toolkit/1.2.3": {
"dependencies": {
"StellaOps.Logging": "2.5.1"
},
"runtime": {
"lib/net10.0/StellaOps.Toolkit.dll": {
"assemblyVersion": "1.2.3.0",
"fileVersion": "1.2.3.0"
}
}
},
"StellaOps.Logging/2.5.1": {
"runtime": {
"lib/net10.0/StellaOps.Logging.dll": {
"assemblyVersion": "2.5.1.0",
"fileVersion": "2.5.1.12345"
}
}
}
},
".NETCoreApp,Version=v10.0/linux-x64": {
"StellaOps.Toolkit/1.2.3": {
"runtimeTargets": {
"runtimes/linux-x64/native/libstellaops.toolkit.so": {
"rid": "linux-x64",
"assetType": "native"
}
}
},
"StellaOps.Logging/2.5.1": {
"runtime": {
"runtimes/linux-x64/lib/net10.0/StellaOps.Logging.dll": {}
}
}
},
".NETCoreApp,Version=v10.0/osx-arm64": {
"StellaOps.Toolkit/1.2.3": {
"runtimeTargets": {
"runtimes/osx-arm64/native/libstellaops.toolkit.dylib": {
"rid": "osx-arm64",
"assetType": "native"
}
}
},
"StellaOps.Logging/2.5.1": {
"runtime": {
"runtimes/osx-arm64/lib/net10.0/StellaOps.Logging.dll": {}
}
}
}
},
"libraries": {
"AppA/2.0.0": {
"type": "project",
"serviceable": false
},
"StellaOps.Toolkit/1.2.3": {
"type": "package",
"serviceable": true,
"sha512": "sha512-FAKE_TOOLKIT_SHA==",
"path": "stellaops.toolkit/1.2.3",
"hashPath": "stellaops.toolkit.1.2.3.nupkg.sha512"
},
"StellaOps.Logging/2.5.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-FAKE_LOGGING_SHA==",
"path": "stellaops.logging/2.5.1",
"hashPath": "stellaops.logging.2.5.1.nupkg.sha512"
}
}
}

View File

@@ -0,0 +1,39 @@
{
"runtimeOptions": {
"tfm": "net10.0",
"framework": {
"name": "Microsoft.NETCore.App",
"version": "10.0.1"
},
"frameworks": [
{
"name": "Microsoft.NETCore.App",
"version": "10.0.1"
},
{
"name": "Microsoft.AspNetCore.App",
"version": "10.0.0"
},
{
"name": "StellaOps.Hosting",
"version": "2.0.0"
}
],
"runtimeGraph": {
"runtimes": {
"osx-arm64": {
"fallbacks": [
"osx",
"unix"
]
},
"linux-x64": {
"fallbacks": [
"linux",
"unix"
]
}
}
}
}
}

View File

@@ -0,0 +1,76 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v10.0/win-arm64"
},
"targets": {
".NETCoreApp,Version=v10.0": {
"AppB/3.1.0": {
"dependencies": {
"StellaOps.Toolkit": "1.2.3",
"StellaOps.Logging": "2.5.1"
}
},
"StellaOps.Toolkit/1.2.3": {
"runtime": {
"lib/net10.0/StellaOps.Toolkit.dll": {
"assemblyVersion": "1.2.3.0",
"fileVersion": "1.2.3.0"
}
}
},
"StellaOps.Logging/2.5.1": {
"runtime": {
"lib/net10.0/StellaOps.Logging.dll": {
"assemblyVersion": "2.5.1.0",
"fileVersion": "2.5.1.12345"
}
}
}
},
".NETCoreApp,Version=v10.0/win-arm64": {
"StellaOps.Toolkit/1.2.3": {
"runtimeTargets": {
"runtimes/win-arm64/native/stellaops.toolkit.dll": {
"rid": "win-arm64",
"assetType": "native"
}
}
},
"StellaOps.Logging/2.5.1": {
"runtimeTargets": {
"runtimes/win-arm64/native/stellaops.logging.dll": {
"rid": "win-arm64",
"assetType": "native"
}
}
}
},
".NETCoreApp,Version=v10.0/linux-arm64": {
"StellaOps.Logging/2.5.1": {
"runtime": {
"runtimes/linux-arm64/lib/net10.0/StellaOps.Logging.dll": {}
}
}
}
},
"libraries": {
"AppB/3.1.0": {
"type": "project",
"serviceable": false
},
"StellaOps.Toolkit/1.2.3": {
"type": "package",
"serviceable": true,
"sha512": "sha512-FAKE_TOOLKIT_SHA==",
"path": "stellaops.toolkit/1.2.3",
"hashPath": "stellaops.toolkit.1.2.3.nupkg.sha512"
},
"StellaOps.Logging/2.5.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-FAKE_LOGGING_SHA==",
"path": "stellaops.logging/2.5.1",
"hashPath": "stellaops.logging.2.5.1.nupkg.sha512"
}
}
}

View File

@@ -0,0 +1,38 @@
{
"runtimeOptions": {
"tfm": "net10.0",
"framework": {
"name": "Microsoft.NETCore.App",
"version": "10.0.0"
},
"frameworks": [
{
"name": "Microsoft.NETCore.App",
"version": "10.0.0"
},
{
"name": "Microsoft.WindowsDesktop.App",
"version": "10.0.0"
}
],
"additionalProbingPaths": [
"C:/Users/runner/.nuget/packages"
],
"runtimeGraph": {
"runtimes": {
"win-arm64": {
"fallbacks": [
"win",
"any"
]
},
"linux-arm64": {
"fallbacks": [
"linux",
"unix"
]
}
}
}
}
}

View File

@@ -0,0 +1,120 @@
[
{
"analyzerId": "dotnet",
"componentKey": "purl::pkg:nuget/stellaops.logging@2.5.1",
"purl": "pkg:nuget/stellaops.logging@2.5.1",
"name": "StellaOps.Logging",
"version": "2.5.1",
"type": "nuget",
"usedByEntrypoint": false,
"metadata": {
"assembly[0].assetPath": "lib/net10.0/StellaOps.Logging.dll",
"assembly[0].fileVersion": "2.5.1.12345",
"assembly[0].tfm[0]": ".NETCoreApp,Version=v10.0",
"assembly[0].version": "2.5.1.0",
"assembly[1].assetPath": "runtimes/linux-arm64/lib/net10.0/StellaOps.Logging.dll",
"assembly[1].rid[0]": "linux-arm64",
"assembly[1].tfm[0]": ".NETCoreApp,Version=v10.0",
"assembly[2].assetPath": "runtimes/linux-x64/lib/net10.0/StellaOps.Logging.dll",
"assembly[2].rid[0]": "linux-x64",
"assembly[2].tfm[0]": ".NETCoreApp,Version=v10.0",
"assembly[3].assetPath": "runtimes/osx-arm64/lib/net10.0/StellaOps.Logging.dll",
"assembly[3].rid[0]": "osx-arm64",
"assembly[3].tfm[0]": ".NETCoreApp,Version=v10.0",
"deps.path[0]": "AppA.deps.json",
"deps.path[1]": "AppB.deps.json",
"deps.rid[0]": "linux-arm64",
"deps.rid[1]": "linux-x64",
"deps.rid[2]": "osx-arm64",
"deps.rid[3]": "win-arm64",
"deps.tfm[0]": ".NETCoreApp,Version=v10.0",
"license.expression[0]": "Apache-2.0",
"native[0].assetPath": "runtimes/win-arm64/native/stellaops.logging.dll",
"native[0].rid[0]": "win-arm64",
"native[0].tfm[0]": ".NETCoreApp,Version=v10.0",
"package.hashPath[0]": "stellaops.logging.2.5.1.nupkg.sha512",
"package.id": "StellaOps.Logging",
"package.id.normalized": "stellaops.logging",
"package.path[0]": "stellaops.logging/2.5.1",
"package.serviceable": "true",
"package.sha512[0]": "sha512-FAKE_LOGGING_SHA==",
"package.version": "2.5.1",
"provenance": "manifest"
},
"evidence": [
{
"kind": "file",
"source": "deps.json",
"locator": "AppA.deps.json",
"value": "StellaOps.Logging/2.5.1"
},
{
"kind": "file",
"source": "deps.json",
"locator": "AppB.deps.json",
"value": "StellaOps.Logging/2.5.1"
}
]
},
{
"analyzerId": "dotnet",
"componentKey": "purl::pkg:nuget/stellaops.toolkit@1.2.3",
"purl": "pkg:nuget/stellaops.toolkit@1.2.3",
"name": "StellaOps.Toolkit",
"version": "1.2.3",
"type": "nuget",
"usedByEntrypoint": false,
"metadata": {
"assembly[0].assetPath": "lib/net10.0/StellaOps.Toolkit.dll",
"assembly[0].fileVersion": "1.2.3.0",
"assembly[0].tfm[0]": ".NETCoreApp,Version=v10.0",
"assembly[0].version": "1.2.3.0",
"deps.dependency[0]": "stellaops.logging",
"deps.path[0]": "AppA.deps.json",
"deps.path[1]": "AppB.deps.json",
"deps.rid[0]": "linux-x64",
"deps.rid[1]": "osx-arm64",
"deps.rid[2]": "win-arm64",
"deps.tfm[0]": ".NETCoreApp,Version=v10.0",
"license.file.sha256[0]": "604e182900b0ecb1ffb911c817bcbd148a31b8f55ad392a3b770be8005048c5c",
"license.file[0]": "packages/stellaops.toolkit/1.2.3/LICENSE.txt",
"native[0].assetPath": "runtimes/linux-x64/native/libstellaops.toolkit.so",
"native[0].rid[0]": "linux-x64",
"native[0].tfm[0]": ".NETCoreApp,Version=v10.0",
"native[1].assetPath": "runtimes/osx-arm64/native/libstellaops.toolkit.dylib",
"native[1].rid[0]": "osx-arm64",
"native[1].tfm[0]": ".NETCoreApp,Version=v10.0",
"native[2].assetPath": "runtimes/win-arm64/native/stellaops.toolkit.dll",
"native[2].rid[0]": "win-arm64",
"native[2].tfm[0]": ".NETCoreApp,Version=v10.0",
"package.hashPath[0]": "stellaops.toolkit.1.2.3.nupkg.sha512",
"package.id": "StellaOps.Toolkit",
"package.id.normalized": "stellaops.toolkit",
"package.path[0]": "stellaops.toolkit/1.2.3",
"package.serviceable": "true",
"package.sha512[0]": "sha512-FAKE_TOOLKIT_SHA==",
"package.version": "1.2.3",
"provenance": "manifest"
},
"evidence": [
{
"kind": "file",
"source": "deps.json",
"locator": "AppA.deps.json",
"value": "StellaOps.Toolkit/1.2.3"
},
{
"kind": "file",
"source": "deps.json",
"locator": "AppB.deps.json",
"value": "StellaOps.Toolkit/1.2.3"
},
{
"kind": "file",
"source": "license",
"locator": "packages/stellaops.toolkit/1.2.3/LICENSE.txt",
"sha256": "604e182900b0ecb1ffb911c817bcbd148a31b8f55ad392a3b770be8005048c5c"
}
]
}
]

View File

@@ -0,0 +1,15 @@
StellaOps Logging
Copyright (c) 2025 StellaOps.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
<metadata>
<id>StellaOps.Logging</id>
<version>2.5.1</version>
<authors>StellaOps</authors>
<description>Logging sample package for analyzer fixtures.</description>
<license type="expression">Apache-2.0</license>
<licenseUrl>https://stella-ops.example/licenses/logging</licenseUrl>
<projectUrl>https://stella-ops.example/projects/logging</projectUrl>
</metadata>
</package>

View File

@@ -0,0 +1,7 @@
StellaOps Toolkit License
=========================
This sample license is provided for test fixtures only.
Permission is granted to use, copy, modify, and distribute this fixture
for the purpose of automated testing.

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
<metadata>
<id>StellaOps.Toolkit</id>
<version>1.2.3</version>
<authors>StellaOps</authors>
<description>Toolkit sample package for analyzer fixtures.</description>
<license type="file">LICENSE.txt</license>
<licenseUrl>https://stella-ops.example/licenses/toolkit</licenseUrl>
</metadata>
</package>

View File

@@ -0,0 +1,85 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v10.0",
"signature": null
},
"targets": {
".NETCoreApp,Version=v10.0/linux-x64": {
"MyApp/1.0.0": {
"dependencies": {
"StellaOps.Toolkit": "1.2.3",
"StellaOps.Runtime.SelfContained": "2.1.0"
},
"runtime": {
"MyApp.dll": {}
}
},
"StellaOps.Toolkit/1.2.3": {
"runtime": {
"lib/net10.0/StellaOps.Toolkit.dll": {
"assemblyVersion": "1.2.3.0",
"fileVersion": "1.2.3.0"
}
}
},
"StellaOps.Runtime.SelfContained/2.1.0": {
"runtimeTargets": {
"runtimes/linux-x64/native/libstellaopsnative.so": {
"rid": "linux-x64",
"assetType": "native"
}
}
}
},
".NETCoreApp,Version=v10.0/win-x64": {
"MyApp/1.0.0": {
"dependencies": {
"StellaOps.Toolkit": "1.2.3",
"StellaOps.Runtime.SelfContained": "2.1.0"
},
"runtime": {
"MyApp.dll": {}
}
},
"StellaOps.Toolkit/1.2.3": {
"runtime": {
"lib/net10.0/StellaOps.Toolkit.dll": {
"assemblyVersion": "1.2.3.0",
"fileVersion": "1.2.3.0"
}
}
},
"StellaOps.Runtime.SelfContained/2.1.0": {
"runtimeTargets": {
"runtimes/win-x64/native/stellaopsnative.dll": {
"rid": "win-x64",
"assetType": "native"
}
}
}
}
},
"libraries": {
"MyApp/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": "",
"path": null,
"hashPath": null
},
"StellaOps.Toolkit/1.2.3": {
"type": "package",
"serviceable": true,
"sha512": "sha512-FAKE_TOOLKIT_SHA==",
"path": "stellaops.toolkit/1.2.3",
"hashPath": "stellaops.toolkit.1.2.3.nupkg.sha512"
},
"StellaOps.Runtime.SelfContained/2.1.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-FAKE_RUNTIME_SHA==",
"path": "stellaops.runtime.selfcontained/2.1.0",
"hashPath": "stellaops.runtime.selfcontained.2.1.0.nupkg.sha512"
}
}
}

View File

@@ -0,0 +1,15 @@
{
"runtimeOptions": {
"tfm": "net10.0",
"framework": {
"name": "Microsoft.NETCore.App",
"version": "10.0.0"
},
"includedFrameworks": [
{
"name": "Microsoft.NETCore.DotNetAppHost",
"version": "10.0.0"
}
]
}
}

View File

@@ -0,0 +1,94 @@
[
{
"analyzerId": "dotnet",
"componentKey": "purl::pkg:nuget/stellaops.runtime.selfcontained@2.1.0",
"purl": "pkg:nuget/stellaops.runtime.selfcontained@2.1.0",
"name": "StellaOps.Runtime.SelfContained",
"version": "2.1.0",
"type": "nuget",
"usedByEntrypoint": true,
"metadata": {
"deps.path[0]": "MyApp.deps.json",
"deps.rid[0]": "linux-x64",
"deps.rid[1]": "win-x64",
"deps.tfm[0]": ".NETCoreApp,Version=v10.0",
"license.expression[0]": "Apache-2.0",
"native[0].assetPath": "runtimes/linux-x64/native/libstellaopsnative.so",
"native[0].path": "runtimes/linux-x64/native/libstellaopsnative.so",
"native[0].rid[0]": "linux-x64",
"native[0].sha256": "6cf3d2a487d6a42fc7c3e2edbc452224e99a3656287a534f1164ee6ec9daadf0",
"native[0].tfm[0]": ".NETCoreApp,Version=v10.0",
"native[1].assetPath": "runtimes/win-x64/native/stellaopsnative.dll",
"native[1].rid[0]": "win-x64",
"native[1].tfm[0]": ".NETCoreApp,Version=v10.0",
"package.hashPath[0]": "stellaops.runtime.selfcontained.2.1.0.nupkg.sha512",
"package.id": "StellaOps.Runtime.SelfContained",
"package.id.normalized": "stellaops.runtime.selfcontained",
"package.path[0]": "stellaops.runtime.selfcontained/2.1.0",
"package.serviceable": "true",
"package.sha512[0]": "sha512-FAKE_RUNTIME_SHA==",
"package.version": "2.1.0",
"provenance": "manifest"
},
"evidence": [
{
"kind": "file",
"source": "deps.json",
"locator": "MyApp.deps.json",
"value": "StellaOps.Runtime.SelfContained/2.1.0"
},
{
"kind": "file",
"source": "native",
"locator": "runtimes/linux-x64/native/libstellaopsnative.so",
"value": "runtimes/linux-x64/native/libstellaopsnative.so",
"sha256": "6cf3d2a487d6a42fc7c3e2edbc452224e99a3656287a534f1164ee6ec9daadf0"
}
]
},
{
"analyzerId": "dotnet",
"componentKey": "purl::pkg:nuget/stellaops.toolkit@1.2.3",
"purl": "pkg:nuget/stellaops.toolkit@1.2.3",
"name": "StellaOps.Toolkit",
"version": "1.2.3",
"type": "nuget",
"usedByEntrypoint": false,
"metadata": {
"assembly[0].assetPath": "lib/net10.0/StellaOps.Toolkit.dll",
"assembly[0].fileVersion": "1.2.3.0",
"assembly[0].rid[0]": "linux-x64",
"assembly[0].rid[1]": "win-x64",
"assembly[0].tfm[0]": ".NETCoreApp,Version=v10.0",
"assembly[0].version": "1.2.3.0",
"deps.path[0]": "MyApp.deps.json",
"deps.rid[0]": "linux-x64",
"deps.rid[1]": "win-x64",
"deps.tfm[0]": ".NETCoreApp,Version=v10.0",
"license.file.sha256[0]": "f94d89a576c63e8ba6ee01760c52fa7861ba609491d7c6e6c01ead5ca66b6048",
"license.file[0]": "packages/stellaops.toolkit/1.2.3/LICENSE.txt",
"package.hashPath[0]": "stellaops.toolkit.1.2.3.nupkg.sha512",
"package.id": "StellaOps.Toolkit",
"package.id.normalized": "stellaops.toolkit",
"package.path[0]": "stellaops.toolkit/1.2.3",
"package.serviceable": "true",
"package.sha512[0]": "sha512-FAKE_TOOLKIT_SHA==",
"package.version": "1.2.3",
"provenance": "manifest"
},
"evidence": [
{
"kind": "file",
"source": "deps.json",
"locator": "MyApp.deps.json",
"value": "StellaOps.Toolkit/1.2.3"
},
{
"kind": "file",
"source": "license",
"locator": "packages/stellaops.toolkit/1.2.3/LICENSE.txt",
"sha256": "f94d89a576c63e8ba6ee01760c52fa7861ba609491d7c6e6c01ead5ca66b6048"
}
]
}
]

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
<metadata>
<id>StellaOps.Runtime.SelfContained</id>
<version>2.1.0</version>
<authors>StellaOps</authors>
<description>Runtime bundle used for self-contained analyzer fixtures.</description>
<license type="expression">Apache-2.0</license>
<licenseUrl>https://stella-ops.example/licenses/runtime</licenseUrl>
</metadata>
</package>

View File

@@ -0,0 +1,6 @@
StellaOps Toolkit License
=========================
Reusable toolkit licensing terms for analyzer fixtures.
This document is intentionally short for deterministic hashing tests.

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
<metadata>
<id>StellaOps.Toolkit</id>
<version>1.2.3</version>
<authors>StellaOps</authors>
<description>Toolkit package for self-contained analyzer fixtures.</description>
<license type="file">LICENSE.txt</license>
<licenseUrl>https://stella-ops.example/licenses/toolkit</licenseUrl>
</metadata>
</package>

View File

@@ -0,0 +1,42 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v10.0/linux-x64"
},
"targets": {
".NETCoreApp,Version=v10.0": {
"Signed.App/1.0.0": {
"dependencies": {
"Microsoft.Extensions.Logging": "9.0.0"
}
},
"Microsoft.Extensions.Logging/9.0.0": {
"runtime": {
"lib/net9.0/Microsoft.Extensions.Logging.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.24.52809"
}
}
}
},
".NETCoreApp,Version=v10.0/linux-x64": {
"Microsoft.Extensions.Logging/9.0.0": {
"runtime": {
"runtimes/linux-x64/lib/net9.0/Microsoft.Extensions.Logging.dll": {}
}
}
}
},
"libraries": {
"Signed.App/1.0.0": {
"type": "project",
"serviceable": false
},
"Microsoft.Extensions.Logging/9.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-FAKE_LOGGING_SHA==",
"path": "microsoft.extensions.logging/9.0.0",
"hashPath": "microsoft.extensions.logging.9.0.0.nupkg.sha512"
}
}
}

View File

@@ -0,0 +1,9 @@
{
"runtimeOptions": {
"tfm": "net10.0",
"framework": {
"name": "Microsoft.NETCore.App",
"version": "10.0.0"
}
}
}

View File

@@ -0,0 +1,40 @@
[
{
"analyzerId": "dotnet",
"componentKey": "purl::pkg:nuget/microsoft.extensions.logging@9.0.0",
"purl": "pkg:nuget/microsoft.extensions.logging@9.0.0",
"name": "Microsoft.Extensions.Logging",
"version": "9.0.0",
"type": "nuget",
"usedByEntrypoint": false,
"metadata": {
"assembly[0].assetPath": "lib/net9.0/Microsoft.Extensions.Logging.dll",
"assembly[0].fileVersion": "9.0.24.52809",
"assembly[0].tfm[0]": ".NETCoreApp,Version=v10.0",
"assembly[0].version": "9.0.0.0",
"assembly[1].assetPath": "runtimes/linux-x64/lib/net9.0/Microsoft.Extensions.Logging.dll",
"assembly[1].rid[0]": "linux-x64",
"assembly[1].tfm[0]": ".NETCoreApp,Version=v10.0",
"deps.path[0]": "Signed.App.deps.json",
"deps.rid[0]": "linux-x64",
"deps.tfm[0]": ".NETCoreApp,Version=v10.0",
"license.expression[0]": "MIT",
"package.hashPath[0]": "microsoft.extensions.logging.9.0.0.nupkg.sha512",
"package.id": "Microsoft.Extensions.Logging",
"package.id.normalized": "microsoft.extensions.logging",
"package.path[0]": "microsoft.extensions.logging/9.0.0",
"package.serviceable": "true",
"package.sha512[0]": "sha512-FAKE_LOGGING_SHA==",
"package.version": "9.0.0",
"provenance": "manifest"
},
"evidence": [
{
"kind": "file",
"source": "deps.json",
"locator": "Signed.App.deps.json",
"value": "Microsoft.Extensions.Logging/9.0.0"
}
]
}
]

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
<metadata>
<id>Microsoft.Extensions.Logging</id>
<version>9.0.0</version>
<authors>Microsoft</authors>
<description>Signed logging package fixture.</description>
<license type="expression">MIT</license>
<licenseUrl>https://licenses.nuget.org/MIT</licenseUrl>
</metadata>
</package>

View File

@@ -0,0 +1,73 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v10.0/linux-x64"
},
"targets": {
".NETCoreApp,Version=v10.0": {
"Sample.App/1.0.0": {
"dependencies": {
"StellaOps.Toolkit": "1.2.3",
"Microsoft.Extensions.Logging": "9.0.0"
}
},
"StellaOps.Toolkit/1.2.3": {
"dependencies": {
"Microsoft.Extensions.Logging": "9.0.0"
},
"runtime": {
"lib/net10.0/StellaOps.Toolkit.dll": {
"assemblyVersion": "1.2.3.0",
"fileVersion": "1.2.3.0"
}
}
},
"Microsoft.Extensions.Logging/9.0.0": {
"runtime": {
"lib/net9.0/Microsoft.Extensions.Logging.dll": {
"assemblyVersion": "9.0.0.0",
"fileVersion": "9.0.24.52809"
}
}
}
},
".NETCoreApp,Version=v10.0/linux-x64": {
"StellaOps.Toolkit/1.2.3": {
"runtime": {
"runtimes/linux-x64/native/libstellaops.toolkit.so": {}
}
},
"Microsoft.Extensions.Logging/9.0.0": {
"runtime": {
"runtimes/linux-x64/lib/net9.0/Microsoft.Extensions.Logging.dll": {}
}
}
},
".NETCoreApp,Version=v10.0/win-x86": {
"Microsoft.Extensions.Logging/9.0.0": {
"runtime": {
"runtimes/win-x86/lib/net9.0/Microsoft.Extensions.Logging.dll": {}
}
}
}
},
"libraries": {
"Sample.App/1.0.0": {
"type": "project",
"serviceable": false
},
"StellaOps.Toolkit/1.2.3": {
"type": "package",
"serviceable": true,
"sha512": "sha512-FAKE_TOOLKIT_SHA==",
"path": "stellaops.toolkit/1.2.3",
"hashPath": "stellaops.toolkit.1.2.3.nupkg.sha512"
},
"Microsoft.Extensions.Logging/9.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-FAKE_LOGGING_SHA==",
"path": "microsoft.extensions.logging/9.0.0",
"hashPath": "microsoft.extensions.logging.9.0.0.nupkg.sha512"
}
}
}

View File

@@ -0,0 +1,35 @@
{
"runtimeOptions": {
"tfm": "net10.0",
"framework": {
"name": "Microsoft.NETCore.App",
"version": "10.0.0"
},
"frameworks": [
{
"name": "Microsoft.NETCore.App",
"version": "10.0.0"
},
{
"name": "Microsoft.AspNetCore.App",
"version": "10.0.0"
}
],
"runtimeGraph": {
"runtimes": {
"linux-x64": {
"fallbacks": [
"linux",
"unix"
]
},
"win-x86": {
"fallbacks": [
"win",
"any"
]
}
}
}
}
}

View File

@@ -0,0 +1,87 @@
[
{
"analyzerId": "dotnet",
"componentKey": "purl::pkg:nuget/microsoft.extensions.logging@9.0.0",
"purl": "pkg:nuget/microsoft.extensions.logging@9.0.0",
"name": "Microsoft.Extensions.Logging",
"version": "9.0.0",
"type": "nuget",
"usedByEntrypoint": false,
"metadata": {
"assembly[0].assetPath": "lib/net9.0/Microsoft.Extensions.Logging.dll",
"assembly[0].fileVersion": "9.0.24.52809",
"assembly[0].tfm[0]": ".NETCoreApp,Version=v10.0",
"assembly[0].version": "9.0.0.0",
"assembly[1].assetPath": "runtimes/linux-x64/lib/net9.0/Microsoft.Extensions.Logging.dll",
"assembly[1].rid[0]": "linux-x64",
"assembly[1].tfm[0]": ".NETCoreApp,Version=v10.0",
"assembly[2].assetPath": "runtimes/win-x86/lib/net9.0/Microsoft.Extensions.Logging.dll",
"assembly[2].rid[0]": "win-x86",
"assembly[2].tfm[0]": ".NETCoreApp,Version=v10.0",
"deps.path[0]": "Sample.App.deps.json",
"deps.rid[0]": "linux-x64",
"deps.rid[1]": "win-x86",
"deps.tfm[0]": ".NETCoreApp,Version=v10.0",
"license.expression[0]": "MIT",
"package.hashPath[0]": "microsoft.extensions.logging.9.0.0.nupkg.sha512",
"package.id": "Microsoft.Extensions.Logging",
"package.id.normalized": "microsoft.extensions.logging",
"package.path[0]": "microsoft.extensions.logging/9.0.0",
"package.serviceable": "true",
"package.sha512[0]": "sha512-FAKE_LOGGING_SHA==",
"package.version": "9.0.0",
"provenance": "manifest"
},
"evidence": [
{
"kind": "file",
"source": "deps.json",
"locator": "Sample.App.deps.json",
"value": "Microsoft.Extensions.Logging/9.0.0"
}
]
},
{
"analyzerId": "dotnet",
"componentKey": "purl::pkg:nuget/stellaops.toolkit@1.2.3",
"purl": "pkg:nuget/stellaops.toolkit@1.2.3",
"name": "StellaOps.Toolkit",
"version": "1.2.3",
"type": "nuget",
"usedByEntrypoint": false,
"metadata": {
"assembly[0].assetPath": "lib/net10.0/StellaOps.Toolkit.dll",
"assembly[0].fileVersion": "1.2.3.0",
"assembly[0].tfm[0]": ".NETCoreApp,Version=v10.0",
"assembly[0].version": "1.2.3.0",
"deps.dependency[0]": "microsoft.extensions.logging",
"deps.path[0]": "Sample.App.deps.json",
"deps.rid[0]": "linux-x64",
"deps.tfm[0]": ".NETCoreApp,Version=v10.0",
"license.file.sha256[0]": "604e182900b0ecb1ffb911c817bcbd148a31b8f55ad392a3b770be8005048c5c",
"license.file[0]": "packages/stellaops.toolkit/1.2.3/LICENSE.txt",
"package.hashPath[0]": "stellaops.toolkit.1.2.3.nupkg.sha512",
"package.id": "StellaOps.Toolkit",
"package.id.normalized": "stellaops.toolkit",
"package.path[0]": "stellaops.toolkit/1.2.3",
"package.serviceable": "true",
"package.sha512[0]": "sha512-FAKE_TOOLKIT_SHA==",
"package.version": "1.2.3",
"provenance": "manifest"
},
"evidence": [
{
"kind": "file",
"source": "deps.json",
"locator": "Sample.App.deps.json",
"value": "StellaOps.Toolkit/1.2.3"
},
{
"kind": "file",
"source": "license",
"locator": "packages/stellaops.toolkit/1.2.3/LICENSE.txt",
"sha256": "604e182900b0ecb1ffb911c817bcbd148a31b8f55ad392a3b770be8005048c5c"
}
]
}
]

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
<metadata>
<id>Microsoft.Extensions.Logging</id>
<version>9.0.0</version>
<authors>Microsoft</authors>
<description>Logging abstractions for StellaOps test fixture.</description>
<license type="expression">MIT</license>
<licenseUrl>https://licenses.nuget.org/MIT</licenseUrl>
</metadata>
</package>

View File

@@ -0,0 +1,7 @@
StellaOps Toolkit License
=========================
This sample license is provided for test fixtures only.
Permission is granted to use, copy, modify, and distribute this fixture
for the purpose of automated testing.

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
<metadata>
<id>StellaOps.Toolkit</id>
<version>1.2.3</version>
<authors>StellaOps</authors>
<description>Toolkit sample package for analyzer fixtures.</description>
<license type="file">LICENSE.txt</license>
<licenseUrl>https://stella-ops.example/licenses/toolkit</licenseUrl>
</metadata>
</package>

View File

@@ -0,0 +1,12 @@
[[package]]
name = "my_app"
version = "0.1.0"
dependencies = [
"serde",
]
[[package]]
name = "serde"
version = "1.0.188"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abc123"

View File

@@ -0,0 +1,62 @@
[
{
"analyzerId": "rust",
"componentKey": "purl::pkg:cargo/my_app@0.1.0",
"purl": "pkg:cargo/my_app@0.1.0",
"name": "my_app",
"version": "0.1.0",
"type": "cargo",
"usedByEntrypoint": false,
"metadata": {
"cargo.lock.path": "Cargo.lock",
"fingerprint.profile": "debug",
"fingerprint.targetKind": "bin",
"source": "registry\u002Bhttps://github.com/rust-lang/crates.io-index"
},
"evidence": [
{
"kind": "file",
"source": "cargo.fingerprint",
"locator": "target/debug/.fingerprint/my_app-1234567890abcdef/bin-my_app-1234567890abcdef.json",
"value": "bin"
},
{
"kind": "file",
"source": "cargo.lock",
"locator": "Cargo.lock",
"value": "my_app 0.1.0"
}
]
},
{
"analyzerId": "rust",
"componentKey": "purl::pkg:cargo/serde@1.0.188",
"purl": "pkg:cargo/serde@1.0.188",
"name": "serde",
"version": "1.0.188",
"type": "cargo",
"usedByEntrypoint": false,
"metadata": {
"cargo.lock.path": "Cargo.lock",
"checksum": "abc123",
"fingerprint.profile": "release",
"fingerprint.targetKind": "lib",
"source": "registry\u002Bhttps://github.com/rust-lang/crates.io-index"
},
"evidence": [
{
"kind": "file",
"source": "cargo.fingerprint",
"locator": "target/debug/.fingerprint/serde-abcdef1234567890/libserde-abcdef1234567890.json",
"value": "lib"
},
{
"kind": "file",
"source": "cargo.lock",
"locator": "Cargo.lock",
"value": "serde 1.0.188",
"sha256": "abc123"
}
]
}
]

View File

@@ -0,0 +1,5 @@
{
"pkgid": "my_app 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"profile": "debug",
"target_kind": ["bin"]
}

View File

@@ -0,0 +1,5 @@
{
"pkgid": "serde 1.0.188 (registry+https://github.com/rust-lang/crates.io-index)",
"profile": "release",
"target_kind": ["lib"]
}

View File

@@ -0,0 +1,46 @@
using StellaOps.Scanner.Analyzers.Lang;
namespace StellaOps.Scanner.Analyzers.Lang.Tests.Harness;
public static class LanguageAnalyzerTestHarness
{
public static async Task<string> RunToJsonAsync(string fixturePath, IEnumerable<ILanguageAnalyzer> analyzers, CancellationToken cancellationToken = default, LanguageUsageHints? usageHints = null, IServiceProvider? services = null)
{
if (string.IsNullOrWhiteSpace(fixturePath))
{
throw new ArgumentException("Fixture path is required", nameof(fixturePath));
}
var engine = new LanguageAnalyzerEngine(analyzers ?? Array.Empty<ILanguageAnalyzer>());
var context = new LanguageAnalyzerContext(fixturePath, TimeProvider.System, usageHints, services);
var result = await engine.AnalyzeAsync(context, cancellationToken).ConfigureAwait(false);
return result.ToJson(indent: true);
}
public static async Task AssertDeterministicAsync(string fixturePath, string goldenPath, IEnumerable<ILanguageAnalyzer> analyzers, CancellationToken cancellationToken = default, LanguageUsageHints? usageHints = null, IServiceProvider? services = null)
{
var actual = await RunToJsonAsync(fixturePath, analyzers, cancellationToken, usageHints, services).ConfigureAwait(false);
var expected = await File.ReadAllTextAsync(goldenPath, cancellationToken).ConfigureAwait(false);
// Normalize newlines for portability.
actual = NormalizeLineEndings(actual).TrimEnd();
expected = NormalizeLineEndings(expected).TrimEnd();
if (!string.Equals(expected, actual, StringComparison.Ordinal))
{
var actualPath = goldenPath + ".actual";
var directory = Path.GetDirectoryName(actualPath);
if (!string.IsNullOrEmpty(directory))
{
Directory.CreateDirectory(directory);
}
await File.WriteAllTextAsync(actualPath, actual, cancellationToken).ConfigureAwait(false);
}
Assert.Equal(expected, actual);
}
private static string NormalizeLineEndings(string value)
=> value.Replace("\r\n", "\n", StringComparison.Ordinal);
}

View File

@@ -0,0 +1,34 @@
using System.IO;
using StellaOps.Scanner.Analyzers.Lang.Rust;
using StellaOps.Scanner.Analyzers.Lang.Tests.Harness;
using StellaOps.Scanner.Analyzers.Lang.Tests.TestUtilities;
namespace StellaOps.Scanner.Analyzers.Lang.Tests.Rust;
public sealed class RustLanguageAnalyzerTests
{
[Fact]
public async Task SimpleFixtureProducesDeterministicOutputAsync()
{
var cancellationToken = TestContext.Current.CancellationToken;
var fixturePath = TestPaths.ResolveFixture("lang", "rust", "simple");
var goldenPath = Path.Combine(fixturePath, "expected.json");
var usageHints = new LanguageUsageHints(new[]
{
Path.Combine(fixturePath, "usr/local/bin/my_app")
});
var analyzers = new ILanguageAnalyzer[]
{
new RustLanguageAnalyzer()
};
await LanguageAnalyzerTestHarness.AssertDeterministicAsync(
fixturePath,
goldenPath,
analyzers,
cancellationToken,
usageHints);
}
}

View File

@@ -0,0 +1,47 @@
<?xml version='1.0' encoding='utf-8'?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<LangVersion>preview</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<IsPackable>false</IsPackable>
<OutputType>Exe</OutputType>
</PropertyGroup>
<ItemGroup>
<PackageReference Remove="Microsoft.NET.Test.Sdk" />
<PackageReference Remove="xunit" />
<PackageReference Remove="xunit.runner.visualstudio" />
<PackageReference Remove="Microsoft.AspNetCore.Mvc.Testing" />
<PackageReference Remove="Mongo2Go" />
<PackageReference Remove="coverlet.collector" />
<PackageReference Remove="Microsoft.Extensions.TimeProvider.Testing" />
<ProjectReference Remove="..\StellaOps.Concelier.Testing\StellaOps.Concelier.Testing.csproj" />
<Compile Remove="$(MSBuildThisFileDirectory)..\StellaOps.Concelier.Tests.Shared\AssemblyInfo.cs" />
<Compile Remove="$(MSBuildThisFileDirectory)..\StellaOps.Concelier.Tests.Shared\MongoFixtureCollection.cs" />
<Using Remove="StellaOps.Concelier.Testing" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="xunit.v3" Version="3.0.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../../__Libraries/StellaOps.Scanner.Analyzers.Lang/StellaOps.Scanner.Analyzers.Lang.csproj" />
<ProjectReference Include="../../__Libraries/StellaOps.Scanner.Analyzers.Lang.DotNet/StellaOps.Scanner.Analyzers.Lang.DotNet.csproj" />
<ProjectReference Include="../../__Libraries/StellaOps.Scanner.Analyzers.Lang.Rust/StellaOps.Scanner.Analyzers.Lang.Rust.csproj" />
<ProjectReference Include="../../__Libraries/StellaOps.Scanner.Core/StellaOps.Scanner.Core.csproj" />
</ItemGroup>
<ItemGroup>
<Using Include="Xunit" />
</ItemGroup>
<ItemGroup>
<None Include="Fixtures\**\*" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,202 @@
using System.Buffers.Binary;
using System.Text;
namespace StellaOps.Scanner.Analyzers.Lang.Tests.TestUtilities;
public static class JavaClassFileFactory
{
public static byte[] CreateClassForNameInvoker(string internalClassName, string targetClassName)
{
using var buffer = new MemoryStream();
using var writer = new BigEndianWriter(buffer);
WriteClassFileHeader(writer, constantPoolCount: 16);
writer.WriteByte((byte)ConstantTag.Utf8); writer.WriteUtf8(internalClassName); // #1
writer.WriteByte((byte)ConstantTag.Class); writer.WriteUInt16(1); // #2
writer.WriteByte((byte)ConstantTag.Utf8); writer.WriteUtf8("java/lang/Object"); // #3
writer.WriteByte((byte)ConstantTag.Class); writer.WriteUInt16(3); // #4
writer.WriteByte((byte)ConstantTag.Utf8); writer.WriteUtf8("invoke"); // #5
writer.WriteByte((byte)ConstantTag.Utf8); writer.WriteUtf8("()V"); // #6
writer.WriteByte((byte)ConstantTag.Utf8); writer.WriteUtf8("Code"); // #7
writer.WriteByte((byte)ConstantTag.Utf8); writer.WriteUtf8(targetClassName); // #8
writer.WriteByte((byte)ConstantTag.String); writer.WriteUInt16(8); // #9
writer.WriteByte((byte)ConstantTag.Utf8); writer.WriteUtf8("java/lang/Class"); // #10
writer.WriteByte((byte)ConstantTag.Class); writer.WriteUInt16(10); // #11
writer.WriteByte((byte)ConstantTag.Utf8); writer.WriteUtf8("forName"); // #12
writer.WriteByte((byte)ConstantTag.Utf8); writer.WriteUtf8("(Ljava/lang/String;)Ljava/lang/Class;"); // #13
writer.WriteByte((byte)ConstantTag.NameAndType); writer.WriteUInt16(12); writer.WriteUInt16(13); // #14
writer.WriteByte((byte)ConstantTag.Methodref); writer.WriteUInt16(11); writer.WriteUInt16(14); // #15
writer.WriteUInt16(0x0001); // public
writer.WriteUInt16(2); // this class
writer.WriteUInt16(4); // super class
writer.WriteUInt16(0); // interfaces
writer.WriteUInt16(0); // fields
writer.WriteUInt16(1); // methods
WriteInvokeMethod(writer, methodNameIndex: 5, descriptorIndex: 6, ldcIndex: 9, methodRefIndex: 15);
writer.WriteUInt16(0); // class attributes
return buffer.ToArray();
}
public static byte[] CreateTcclChecker(string internalClassName)
{
using var buffer = new MemoryStream();
using var writer = new BigEndianWriter(buffer);
WriteClassFileHeader(writer, constantPoolCount: 18);
writer.WriteByte((byte)ConstantTag.Utf8); writer.WriteUtf8(internalClassName); // #1
writer.WriteByte((byte)ConstantTag.Class); writer.WriteUInt16(1); // #2
writer.WriteByte((byte)ConstantTag.Utf8); writer.WriteUtf8("java/lang/Object"); // #3
writer.WriteByte((byte)ConstantTag.Class); writer.WriteUInt16(3); // #4
writer.WriteByte((byte)ConstantTag.Utf8); writer.WriteUtf8("check"); // #5
writer.WriteByte((byte)ConstantTag.Utf8); writer.WriteUtf8("()V"); // #6
writer.WriteByte((byte)ConstantTag.Utf8); writer.WriteUtf8("Code"); // #7
writer.WriteByte((byte)ConstantTag.Utf8); writer.WriteUtf8("java/lang/Thread"); // #8
writer.WriteByte((byte)ConstantTag.Class); writer.WriteUInt16(8); // #9
writer.WriteByte((byte)ConstantTag.Utf8); writer.WriteUtf8("currentThread"); // #10
writer.WriteByte((byte)ConstantTag.Utf8); writer.WriteUtf8("()Ljava/lang/Thread;"); // #11
writer.WriteByte((byte)ConstantTag.NameAndType); writer.WriteUInt16(10); writer.WriteUInt16(11); // #12
writer.WriteByte((byte)ConstantTag.Methodref); writer.WriteUInt16(9); writer.WriteUInt16(12); // #13
writer.WriteByte((byte)ConstantTag.Utf8); writer.WriteUtf8("getContextClassLoader"); // #14
writer.WriteByte((byte)ConstantTag.Utf8); writer.WriteUtf8("()Ljava/lang/ClassLoader;"); // #15
writer.WriteByte((byte)ConstantTag.NameAndType); writer.WriteUInt16(14); writer.WriteUInt16(15); // #16
writer.WriteByte((byte)ConstantTag.Methodref); writer.WriteUInt16(9); writer.WriteUInt16(16); // #17
writer.WriteUInt16(0x0001); // public
writer.WriteUInt16(2); // this
writer.WriteUInt16(4); // super
writer.WriteUInt16(0); // interfaces
writer.WriteUInt16(0); // fields
writer.WriteUInt16(1); // methods
WriteTcclMethod(writer, methodNameIndex: 5, descriptorIndex: 6, currentThreadMethodRefIndex: 13, getContextMethodRefIndex: 17);
writer.WriteUInt16(0); // class attributes
return buffer.ToArray();
}
private static void WriteClassFileHeader(BigEndianWriter writer, ushort constantPoolCount)
{
writer.WriteUInt32(0xCAFEBABE);
writer.WriteUInt16(0);
writer.WriteUInt16(52);
writer.WriteUInt16(constantPoolCount);
}
private static void WriteInvokeMethod(BigEndianWriter writer, ushort methodNameIndex, ushort descriptorIndex, ushort ldcIndex, ushort methodRefIndex)
{
writer.WriteUInt16(0x0009); // public static
writer.WriteUInt16(methodNameIndex);
writer.WriteUInt16(descriptorIndex);
writer.WriteUInt16(1); // attributes_count
writer.WriteUInt16(7); // "Code"
using var codeBuffer = new MemoryStream();
using (var codeWriter = new BigEndianWriter(codeBuffer))
{
codeWriter.WriteUInt16(1); // max_stack
codeWriter.WriteUInt16(0); // max_locals
codeWriter.WriteUInt32(6); // code_length
codeWriter.WriteByte(0x12);
codeWriter.WriteByte((byte)ldcIndex);
codeWriter.WriteByte(0xB8);
codeWriter.WriteUInt16(methodRefIndex);
codeWriter.WriteByte(0xB1);
codeWriter.WriteUInt16(0); // exception table length
codeWriter.WriteUInt16(0); // code attributes
}
var codeBytes = codeBuffer.ToArray();
writer.WriteUInt32((uint)codeBytes.Length);
writer.WriteBytes(codeBytes);
}
private static void WriteTcclMethod(BigEndianWriter writer, ushort methodNameIndex, ushort descriptorIndex, ushort currentThreadMethodRefIndex, ushort getContextMethodRefIndex)
{
writer.WriteUInt16(0x0009);
writer.WriteUInt16(methodNameIndex);
writer.WriteUInt16(descriptorIndex);
writer.WriteUInt16(1);
writer.WriteUInt16(7);
using var codeBuffer = new MemoryStream();
using (var codeWriter = new BigEndianWriter(codeBuffer))
{
codeWriter.WriteUInt16(2);
codeWriter.WriteUInt16(0);
codeWriter.WriteUInt32(8);
codeWriter.WriteByte(0xB8);
codeWriter.WriteUInt16(currentThreadMethodRefIndex);
codeWriter.WriteByte(0xB6);
codeWriter.WriteUInt16(getContextMethodRefIndex);
codeWriter.WriteByte(0x57);
codeWriter.WriteByte(0xB1);
codeWriter.WriteUInt16(0);
codeWriter.WriteUInt16(0);
}
var codeBytes = codeBuffer.ToArray();
writer.WriteUInt32((uint)codeBytes.Length);
writer.WriteBytes(codeBytes);
}
private sealed class BigEndianWriter : IDisposable
{
private readonly BinaryWriter _writer;
public BigEndianWriter(Stream stream)
{
_writer = new BinaryWriter(stream, Encoding.UTF8, leaveOpen: true);
}
public void WriteByte(byte value) => _writer.Write(value);
public void WriteBytes(byte[] data) => _writer.Write(data);
public void WriteUInt16(ushort value)
{
Span<byte> buffer = stackalloc byte[2];
BinaryPrimitives.WriteUInt16BigEndian(buffer, value);
_writer.Write(buffer);
}
public void WriteUInt32(uint value)
{
Span<byte> buffer = stackalloc byte[4];
BinaryPrimitives.WriteUInt32BigEndian(buffer, value);
_writer.Write(buffer);
}
public void WriteUtf8(string value)
{
var bytes = Encoding.UTF8.GetBytes(value);
WriteUInt16((ushort)bytes.Length);
_writer.Write(bytes);
}
public void Dispose() => _writer.Dispose();
}
private enum ConstantTag : byte
{
Utf8 = 1,
Integer = 3,
Float = 4,
Long = 5,
Double = 6,
Class = 7,
String = 8,
Fieldref = 9,
Methodref = 10,
InterfaceMethodref = 11,
NameAndType = 12,
}
}

View File

@@ -0,0 +1,199 @@
using System.IO.Compression;
using System.Text;
namespace StellaOps.Scanner.Analyzers.Lang.Tests.TestUtilities;
public static class JavaFixtureBuilder
{
private static readonly DateTimeOffset DefaultTimestamp = new(2024, 01, 01, 0, 0, 0, TimeSpan.Zero);
public static string CreateSampleJar(string rootDirectory, string relativePath = "libs/demo.jar")
=> CreateJar(rootDirectory, relativePath, static archive =>
{
var pomEntry = archive.CreateEntry("META-INF/maven/com.example/demo/pom.properties", CompressionLevel.NoCompression);
pomEntry.LastWriteTime = DefaultTimestamp;
using (var writer = new StreamWriter(pomEntry.Open(), Encoding.UTF8, leaveOpen: false))
{
writer.WriteLine("# Test pom.properties");
writer.WriteLine("groupId=com.example");
writer.WriteLine("artifactId=demo");
writer.WriteLine("version=1.0.0");
writer.WriteLine("name=Demo Library");
writer.WriteLine("packaging=jar");
}
var manifestEntry = archive.CreateEntry("META-INF/MANIFEST.MF", CompressionLevel.NoCompression);
manifestEntry.LastWriteTime = DefaultTimestamp;
using (var manifestWriter = new StreamWriter(manifestEntry.Open(), Encoding.UTF8, leaveOpen: false))
{
manifestWriter.WriteLine("Manifest-Version: 1.0");
manifestWriter.WriteLine("Implementation-Title: Demo");
manifestWriter.WriteLine("Implementation-Version: 1.0.0");
manifestWriter.WriteLine("Implementation-Vendor: Example Corp");
manifestWriter.WriteLine();
}
var classEntry = archive.CreateEntry("com/example/Demo.class", CompressionLevel.NoCompression);
classEntry.LastWriteTime = DefaultTimestamp;
using (var classWriter = new BinaryWriter(classEntry.Open(), Encoding.UTF8, leaveOpen: false))
{
classWriter.Write(new byte[] { 0xCA, 0xFE, 0xBA, 0xBE });
}
});
public static string CreateSpringBootFatJar(string rootDirectory, string relativePath = "libs/app-fat.jar")
=> CreateJar(rootDirectory, relativePath, static archive =>
{
var manifestEntry = archive.CreateEntry("META-INF/MANIFEST.MF", CompressionLevel.NoCompression);
manifestEntry.LastWriteTime = DefaultTimestamp;
using (var writer = new StreamWriter(manifestEntry.Open(), Encoding.UTF8, leaveOpen: false))
{
writer.WriteLine("Manifest-Version: 1.0");
writer.WriteLine("Main-Class: org.springframework.boot.loader.JarLauncher");
writer.WriteLine();
}
var bootClasses = archive.CreateEntry("BOOT-INF/classes/com/example/App.class", CompressionLevel.NoCompression);
bootClasses.LastWriteTime = DefaultTimestamp;
var appClassBytes = JavaClassFileFactory.CreateClassForNameInvoker("com/example/App", "com.example.boot.Plugin");
using (var bootStream = bootClasses.Open())
{
bootStream.Write(appClassBytes);
}
var bootService = archive.CreateEntry("BOOT-INF/classes/META-INF/services/java.sql.Driver", CompressionLevel.NoCompression);
bootService.LastWriteTime = DefaultTimestamp;
using (var serviceWriter = new StreamWriter(bootService.Open(), Encoding.UTF8, leaveOpen: false))
{
serviceWriter.WriteLine("com.example.AppDriver");
}
using var libBuffer = new MemoryStream();
using (var nested = new ZipArchive(libBuffer, ZipArchiveMode.Create, leaveOpen: true))
{
var libClass = nested.CreateEntry("com/example/Lib.class", CompressionLevel.NoCompression);
libClass.LastWriteTime = DefaultTimestamp;
var libClassBytes = JavaClassFileFactory.CreateClassForNameInvoker("com/example/Lib", "com.example.boot.Library");
using (var libStream = libClass.Open())
{
libStream.Write(libClassBytes);
}
var libService = nested.CreateEntry("META-INF/services/java.sql.Driver", CompressionLevel.NoCompression);
libService.LastWriteTime = DefaultTimestamp;
using (var libServiceWriter = new StreamWriter(libService.Open(), Encoding.UTF8, leaveOpen: false))
{
libServiceWriter.WriteLine("com.example.LibDriver");
}
}
libBuffer.Position = 0;
var libEntry = archive.CreateEntry("BOOT-INF/lib/library.jar", CompressionLevel.NoCompression);
libEntry.LastWriteTime = DefaultTimestamp;
using var libEntryStream = libEntry.Open();
libBuffer.CopyTo(libEntryStream);
});
public static string CreateWarArchive(string rootDirectory, string relativePath = "apps/sample.war")
=> CreateJar(rootDirectory, relativePath, static archive =>
{
var manifestEntry = archive.CreateEntry("META-INF/MANIFEST.MF", CompressionLevel.NoCompression);
manifestEntry.LastWriteTime = DefaultTimestamp;
using (var manifestWriter = new StreamWriter(manifestEntry.Open(), Encoding.UTF8, leaveOpen: false))
{
manifestWriter.WriteLine("Manifest-Version: 1.0");
manifestWriter.WriteLine();
}
var webXml = archive.CreateEntry("WEB-INF/web.xml", CompressionLevel.NoCompression);
webXml.LastWriteTime = DefaultTimestamp;
using (var webXmlWriter = new StreamWriter(webXml.Open(), Encoding.UTF8, leaveOpen: false))
{
webXmlWriter.WriteLine("<web-app/>");
}
var webClass = archive.CreateEntry("WEB-INF/classes/com/example/Servlet.class", CompressionLevel.NoCompression);
webClass.LastWriteTime = DefaultTimestamp;
using (var classWriter = new BinaryWriter(webClass.Open(), Encoding.UTF8, leaveOpen: false))
{
classWriter.Write(new byte[] { 0xCA, 0xFE, 0xBA, 0xBE });
}
using var libBuffer = new MemoryStream();
using (var nested = new ZipArchive(libBuffer, ZipArchiveMode.Create, leaveOpen: true))
{
var libClass = nested.CreateEntry("com/example/WebLib.class", CompressionLevel.NoCompression);
libClass.LastWriteTime = DefaultTimestamp;
using var libWriter = new BinaryWriter(libClass.Open(), Encoding.UTF8, leaveOpen: false);
libWriter.Write(new byte[] { 0xCA, 0xFE, 0xBA, 0xBE });
}
libBuffer.Position = 0;
var libEntry = archive.CreateEntry("WEB-INF/lib/web-lib.jar", CompressionLevel.NoCompression);
libEntry.LastWriteTime = DefaultTimestamp;
using var libStream = libEntry.Open();
libBuffer.CopyTo(libStream);
});
public static string CreateMultiReleaseJar(string rootDirectory, string relativePath = "libs/mr.jar")
=> CreateJar(rootDirectory, relativePath, static archive =>
{
var manifestEntry = archive.CreateEntry("META-INF/MANIFEST.MF", CompressionLevel.NoCompression);
manifestEntry.LastWriteTime = DefaultTimestamp;
using (var writer = new StreamWriter(manifestEntry.Open(), Encoding.UTF8, leaveOpen: false))
{
writer.WriteLine("Manifest-Version: 1.0");
writer.WriteLine("Multi-Release: true");
writer.WriteLine();
}
var baseClass = archive.CreateEntry("com/example/App.class", CompressionLevel.NoCompression);
baseClass.LastWriteTime = DefaultTimestamp;
using (var baseWriter = new BinaryWriter(baseClass.Open(), Encoding.UTF8, leaveOpen: false))
{
baseWriter.Write(new byte[] { 0xCA, 0xFE, 0xBA, 0xBE });
}
var overlayClass = archive.CreateEntry("META-INF/versions/11/com/example/App.class", CompressionLevel.NoCompression);
overlayClass.LastWriteTime = DefaultTimestamp.AddMinutes(1);
using (var overlayWriter = new BinaryWriter(overlayClass.Open(), Encoding.UTF8, leaveOpen: false))
{
overlayWriter.Write(new byte[] { 0xFE, 0xED, 0xFA, 0xCE });
}
});
public static string CreateRuntimeImage(string rootDirectory, string relativePath = "runtime/jre")
{
ArgumentNullException.ThrowIfNull(rootDirectory);
ArgumentException.ThrowIfNullOrEmpty(relativePath);
var runtimeRoot = Path.Combine(rootDirectory, relativePath.Replace('/', Path.DirectorySeparatorChar));
Directory.CreateDirectory(runtimeRoot);
Directory.CreateDirectory(Path.Combine(runtimeRoot, "bin"));
var javaBinary = OperatingSystem.IsWindows() ? "java.exe" : "java";
File.WriteAllText(Path.Combine(runtimeRoot, "bin", javaBinary), string.Empty);
File.WriteAllText(
Path.Combine(runtimeRoot, "release"),
"JAVA_VERSION=\"17.0.8\"\nIMPLEMENTOR=\"Eclipse Adoptium\"\n");
return runtimeRoot;
}
private static string CreateJar(string rootDirectory, string relativePath, Action<ZipArchive> configure)
{
ArgumentNullException.ThrowIfNull(rootDirectory);
ArgumentException.ThrowIfNullOrEmpty(relativePath);
var jarPath = Path.Combine(rootDirectory, relativePath.Replace('/', Path.DirectorySeparatorChar));
Directory.CreateDirectory(Path.GetDirectoryName(jarPath)!);
using var fileStream = new FileStream(jarPath, FileMode.Create, FileAccess.ReadWrite, FileShare.None);
using var archive = new ZipArchive(fileStream, ZipArchiveMode.Create, leaveOpen: false);
configure(archive);
return jarPath;
}
}

View File

@@ -0,0 +1,54 @@
namespace StellaOps.Scanner.Analyzers.Lang.Tests.TestUtilities;
public static class TestPaths
{
public static string ResolveFixture(params string[] segments)
{
var baseDirectory = AppContext.BaseDirectory;
var parts = new List<string> { baseDirectory };
parts.AddRange(new[] { "Fixtures" });
parts.AddRange(segments);
return Path.GetFullPath(Path.Combine(parts.ToArray()));
}
public static string CreateTemporaryDirectory()
{
var root = Path.Combine(AppContext.BaseDirectory, "tmp", Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(root);
return root;
}
public static void SafeDelete(string directory)
{
if (string.IsNullOrWhiteSpace(directory) || !Directory.Exists(directory))
{
return;
}
try
{
Directory.Delete(directory, recursive: true);
}
catch
{
// Swallow cleanup exceptions to avoid masking test failures.
}
}
public static string ResolveProjectRoot()
{
var directory = AppContext.BaseDirectory;
while (!string.IsNullOrEmpty(directory))
{
var matches = Directory.EnumerateFiles(directory, "StellaOps.Scanner.Analyzers.Lang*.Tests.csproj", SearchOption.TopDirectoryOnly);
if (matches.Any())
{
return directory;
}
directory = Path.GetDirectoryName(directory) ?? string.Empty;
}
throw new InvalidOperationException("Unable to locate project root.");
}
}

View File

@@ -0,0 +1,3 @@
{
"$schema": "https://xunit.net/schema/current/xunit.runner.schema.json"
}