Files
git.stella-ops.org/src/Scanner/__Tests/StellaOps.Scanner.Analyzers.Lang.Java.Tests/Java/JavaServiceProviderScannerTests.cs

148 lines
6.2 KiB
C#

using System.Collections.Generic;
using System.IO.Compression;
using System.Linq;
using System.Text;
using System.Threading;
using StellaOps.Scanner.Analyzers.Lang.Java.Internal;
using StellaOps.Scanner.Analyzers.Lang.Java.Internal.ClassPath;
using StellaOps.Scanner.Analyzers.Lang.Java.Internal.ServiceProviders;
using StellaOps.Scanner.Analyzers.Lang.Tests.TestUtilities;
using Xunit;
namespace StellaOps.Scanner.Analyzers.Lang.Java.Tests;
public sealed class JavaServiceProviderScannerTests
{
[Fact]
public void Scan_SelectsFirstProviderByClasspathOrder()
{
var root = TestPaths.CreateTemporaryDirectory();
try
{
var servicesA = new Dictionary<string, string[]>
{
["java.sql.Driver"] = new[] { "com.example.ADriver" },
};
var servicesB = new Dictionary<string, string[]>
{
["java.sql.Driver"] = new[] { "com.example.BDriver" },
};
CreateJarWithClasses(root, "libs/a.jar", new[] { "com.example.ADriver" }, servicesA);
CreateJarWithClasses(root, "libs/b.jar", new[] { "com.example.BDriver" }, servicesB);
var cancellationToken = CancellationToken.None;
var context = new LanguageAnalyzerContext(root, TimeProvider.System);
var workspace = JavaWorkspaceNormalizer.Normalize(context, cancellationToken);
var classPath = JavaClassPathBuilder.Build(workspace, cancellationToken);
var analysis = JavaServiceProviderScanner.Scan(classPath, JavaSpiCatalog.Default, cancellationToken);
var service = Assert.Single(analysis.Services, record => record.ServiceId == "java.sql.Driver");
Assert.Equal("jdk", service.Category);
var selected = Assert.Single(service.Candidates.Where(candidate => candidate.IsSelected));
Assert.Equal("com.example.ADriver", selected.ProviderClass);
Assert.Empty(service.Warnings);
}
finally
{
TestPaths.SafeDelete(root);
}
}
[Fact]
public void Scan_FlagsDuplicateProviders()
{
var root = TestPaths.CreateTemporaryDirectory();
try
{
var services = new Dictionary<string, string[]>
{
["java.sql.Driver"] = new[] { "com.example.DuplicateDriver" },
};
CreateJarWithClasses(root, "libs/a.jar", new[] { "com.example.DuplicateDriver" }, services);
CreateJarWithClasses(root, "libs/b.jar", new[] { "com.example.Other" }, services);
var cancellationToken = CancellationToken.None;
var context = new LanguageAnalyzerContext(root, TimeProvider.System);
var workspace = JavaWorkspaceNormalizer.Normalize(context, cancellationToken);
var classPath = JavaClassPathBuilder.Build(workspace, cancellationToken);
var analysis = JavaServiceProviderScanner.Scan(classPath, JavaSpiCatalog.Default, cancellationToken);
var service = Assert.Single(analysis.Services, record => record.ServiceId == "java.sql.Driver");
Assert.NotEmpty(service.Warnings);
Assert.Contains(service.Warnings, warning => warning.Contains("duplicate-provider", StringComparison.OrdinalIgnoreCase));
}
finally
{
TestPaths.SafeDelete(root);
}
}
[Fact]
public void Scan_RespectsBootFatJarOrdering()
{
var root = TestPaths.CreateTemporaryDirectory();
try
{
JavaFixtureBuilder.CreateSpringBootFatJar(root, "apps/app-fat.jar");
var cancellationToken = CancellationToken.None;
var context = new LanguageAnalyzerContext(root, TimeProvider.System);
var workspace = JavaWorkspaceNormalizer.Normalize(context, cancellationToken);
var classPath = JavaClassPathBuilder.Build(workspace, cancellationToken);
var analysis = JavaServiceProviderScanner.Scan(classPath, JavaSpiCatalog.Default, cancellationToken);
var service = Assert.Single(analysis.Services, record => record.ServiceId == "java.sql.Driver");
var selected = Assert.Single(service.Candidates.Where(candidate => candidate.IsSelected));
Assert.Equal("com.example.AppDriver", selected.ProviderClass);
Assert.Contains(service.Candidates.Select(candidate => candidate.ProviderClass), provider => provider == "com.example.LibDriver");
}
finally
{
TestPaths.SafeDelete(root);
}
}
private static void CreateJarWithClasses(
string rootDirectory,
string relativePath,
IEnumerable<string> classNames,
IDictionary<string, string[]> serviceDefinitions)
{
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);
var timestamp = new DateTimeOffset(2024, 01, 01, 0, 0, 0, TimeSpan.Zero);
foreach (var className in classNames)
{
var entryPath = className.Replace('.', '/') + ".class";
var entry = archive.CreateEntry(entryPath, CompressionLevel.NoCompression);
entry.LastWriteTime = timestamp;
using var writer = new BinaryWriter(entry.Open(), Encoding.UTF8, leaveOpen: false);
writer.Write(new byte[] { 0xCA, 0xFE, 0xBA, 0xBE });
}
foreach (var pair in serviceDefinitions)
{
var entryPath = "META-INF/services/" + pair.Key;
var entry = archive.CreateEntry(entryPath, CompressionLevel.NoCompression);
entry.LastWriteTime = timestamp;
using var writer = new StreamWriter(entry.Open(), Encoding.UTF8, leaveOpen: false);
foreach (var provider in pair.Value)
{
writer.WriteLine(provider);
}
}
}
}