Skip to content

Commit a6077ee

Browse files
committed
refactor:use-embedded-resources-for-test-data
1 parent c70440e commit a6077ee

3 files changed

Lines changed: 85 additions & 41 deletions

File tree

DotSchema.Tests/Analyzers/SchemaAnalyzerTests.cs

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using System.Reflection;
2+
13
using DotSchema.Analyzers;
24

35
using Microsoft.Extensions.Logging.Abstractions;
@@ -8,18 +10,28 @@ public class SchemaAnalyzerTests
810
{
911
private readonly SchemaAnalyzer _analyzer = new(NullLogger.Instance);
1012

11-
private static string GetTestDataPath(string filename)
13+
private static SchemaInput LoadEmbeddedSchema(string filename)
1214
{
13-
// TestData files are copied to output directory
14-
var baseDir = AppContext.BaseDirectory;
15+
var assembly = Assembly.GetExecutingAssembly();
16+
var resourceName = $"DotSchema.Tests.TestData.{filename}";
17+
18+
using var stream = assembly.GetManifestResourceStream(resourceName)
19+
?? throw new InvalidOperationException($"Embedded resource not found: {resourceName}");
20+
using var reader = new StreamReader(stream);
1521

16-
return Path.Combine(baseDir, "TestData", filename);
22+
return SchemaInput.FromContent(filename, reader.ReadToEnd());
1723
}
1824

25+
private static List<SchemaInput> GetTestSchemas() =>
26+
[
27+
LoadEmbeddedSchema("windows.schema.json"),
28+
LoadEmbeddedSchema("linux.schema.json")
29+
];
30+
1931
[Fact]
2032
public async Task AnalyzeAsync_DetectsSharedTypes()
2133
{
22-
var schemas = new List<string> { GetTestDataPath("windows.schema.json"), GetTestDataPath("linux.schema.json") };
34+
var schemas = GetTestSchemas();
2335

2436
var result = await _analyzer.AnalyzeAsync(schemas, "Windows");
2537

@@ -30,7 +42,7 @@ public async Task AnalyzeAsync_DetectsSharedTypes()
3042
[Fact]
3143
public async Task AnalyzeAsync_DetectsConflictingTypes()
3244
{
33-
var schemas = new List<string> { GetTestDataPath("windows.schema.json"), GetTestDataPath("linux.schema.json") };
45+
var schemas = GetTestSchemas();
3446

3547
var result = await _analyzer.AnalyzeAsync(schemas, "Windows");
3648

@@ -41,7 +53,7 @@ public async Task AnalyzeAsync_DetectsConflictingTypes()
4153
[Fact]
4254
public async Task AnalyzeAsync_DetectsVariantSpecificTypes()
4355
{
44-
var schemas = new List<string> { GetTestDataPath("windows.schema.json"), GetTestDataPath("linux.schema.json") };
56+
var schemas = GetTestSchemas();
4557

4658
var result = await _analyzer.AnalyzeAsync(schemas, "Windows");
4759

@@ -52,7 +64,7 @@ public async Task AnalyzeAsync_DetectsVariantSpecificTypes()
5264
[Fact]
5365
public async Task AnalyzeAsync_ExtractsRootTypeName()
5466
{
55-
var schemas = new List<string> { GetTestDataPath("windows.schema.json"), GetTestDataPath("linux.schema.json") };
67+
var schemas = GetTestSchemas();
5668

5769
var result = await _analyzer.AnalyzeAsync(schemas, "Windows");
5870

@@ -62,7 +74,7 @@ public async Task AnalyzeAsync_ExtractsRootTypeName()
6274
[Fact]
6375
public async Task AnalyzeAsync_DeterminesPrimarySchemaPath()
6476
{
65-
var schemas = new List<string> { GetTestDataPath("windows.schema.json"), GetTestDataPath("linux.schema.json") };
77+
var schemas = GetTestSchemas();
6678

6779
var result = await _analyzer.AnalyzeAsync(schemas, "Linux");
6880

@@ -72,7 +84,7 @@ public async Task AnalyzeAsync_DeterminesPrimarySchemaPath()
7284
[Fact]
7385
public async Task AnalyzeAsync_SingleSchema_ReturnsEmptySets()
7486
{
75-
var schemas = new List<string> { GetTestDataPath("windows.schema.json") };
87+
var schemas = new List<SchemaInput> { LoadEmbeddedSchema("windows.schema.json") };
7688

7789
var result = await _analyzer.AnalyzeAsync(schemas, "Windows");
7890

@@ -84,7 +96,7 @@ public async Task AnalyzeAsync_SingleSchema_ReturnsEmptySets()
8496
[Fact]
8597
public async Task AnalyzeAsync_ExcludesRootTypeFromSharedAndConflicting()
8698
{
87-
var schemas = new List<string> { GetTestDataPath("windows.schema.json"), GetTestDataPath("linux.schema.json") };
99+
var schemas = GetTestSchemas();
88100

89101
var result = await _analyzer.AnalyzeAsync(schemas, "Windows");
90102

DotSchema.Tests/DotSchema.Tests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
</ItemGroup>
2424

2525
<ItemGroup>
26-
<None Include="TestData\**\*" CopyToOutputDirectory="PreserveNewest" />
26+
<EmbeddedResource Include="TestData\**\*" />
2727
</ItemGroup>
2828

2929
</Project>

DotSchema/Analyzers/SchemaAnalyzer.cs

Lines changed: 61 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,25 @@ public sealed record SchemaAnalysisResult(
2626
JsonSchema PrimarySchema,
2727
string RootTypeName);
2828

29+
/// <summary>
30+
/// Represents a schema input that can be either a file path or raw content.
31+
/// </summary>
32+
public sealed record SchemaInput
33+
{
34+
public required string Name { get; init; }
35+
public string? FilePath { get; init; }
36+
public string? Content { get; init; }
37+
38+
public static SchemaInput FromFile(string filePath) =>
39+
new() { Name = Path.GetFileName(filePath), FilePath = filePath };
40+
41+
public static SchemaInput FromContent(string name, string content) =>
42+
new() { Name = name, Content = content };
43+
44+
public async Task<string> ReadContentAsync(CancellationToken cancellationToken = default) =>
45+
Content ?? await File.ReadAllTextAsync(FilePath!, cancellationToken);
46+
}
47+
2948
/// <summary>
3049
/// Analyzes JSON schemas to determine shared vs variant-specific types.
3150
/// Types are considered shared only if they have the same name AND same content hash.
@@ -44,83 +63,97 @@ public SchemaAnalyzer(ILogger logger)
4463
/// Analyzes multiple schemas to determine which types are shared (exist in all with identical content)
4564
/// vs variant-specific (exist in only some schemas or have different content).
4665
/// </summary>
47-
public async Task<SchemaAnalysisResult> AnalyzeAsync(
66+
public Task<SchemaAnalysisResult> AnalyzeAsync(
4867
List<string> schemaPaths,
4968
string currentVariant,
5069
CancellationToken cancellationToken = default)
5170
{
52-
// Determine primary schema path
53-
var primarySchemaPath = DeterminePrimarySchemaPath(schemaPaths, currentVariant);
71+
var inputs = schemaPaths.Select(SchemaInput.FromFile).ToList();
72+
return AnalyzeAsync(inputs, currentVariant, cancellationToken);
73+
}
74+
75+
/// <summary>
76+
/// Analyzes multiple schemas to determine which types are shared (exist in all with identical content)
77+
/// vs variant-specific (exist in only some schemas or have different content).
78+
/// </summary>
79+
public async Task<SchemaAnalysisResult> AnalyzeAsync(
80+
List<SchemaInput> schemaInputs,
81+
string currentVariant,
82+
CancellationToken cancellationToken = default)
83+
{
84+
var schemaNames = schemaInputs.Select(s => s.Name).ToList();
85+
86+
// Determine primary schema name
87+
var primarySchemaName = DeterminePrimarySchemaName(schemaNames, currentVariant);
5488

5589
// Parse all schemas and extract type hashes
56-
var (parsedSchemas, allSchemaTypes) = await ParseSchemasAsync(schemaPaths, cancellationToken);
90+
var (parsedSchemas, allSchemaTypes) = await ParseSchemasAsync(schemaInputs, cancellationToken);
5791

5892
// Extract root type name from the first schema's title
5993
var rootTypeName = parsedSchemas.Values.FirstOrDefault()?.Title ?? Constants.DefaultRootTypeName;
6094

6195
// Get the primary schema (already parsed)
62-
var primarySchema = parsedSchemas[primarySchemaPath];
96+
var primarySchema = parsedSchemas[primarySchemaName];
6397

6498
// With only one schema, we can't determine what's shared
65-
if (schemaPaths.Count < 2)
99+
if (schemaInputs.Count < 2)
66100
{
67-
return new SchemaAnalysisResult([], [], [], primarySchemaPath, primarySchema, rootTypeName);
101+
return new SchemaAnalysisResult([], [], [], primarySchemaName, primarySchema, rootTypeName);
68102
}
69103

70104
// Categorize types as shared or conflicting
71105
var (sharedTypes, conflictingTypes) = CategorizeTypes(allSchemaTypes, rootTypeName);
72106

73107
// Variant-specific types = types in current variant schema that aren't shared or conflicting
74108
var variantTypes = DetermineVariantTypes(
75-
schemaPaths,
109+
schemaNames,
76110
currentVariant,
77111
parsedSchemas,
78112
sharedTypes,
79113
conflictingTypes,
80114
rootTypeName);
81115

82-
return new SchemaAnalysisResult(sharedTypes, variantTypes, conflictingTypes, primarySchemaPath, primarySchema, rootTypeName);
116+
return new SchemaAnalysisResult(sharedTypes, variantTypes, conflictingTypes, primarySchemaName, primarySchema, rootTypeName);
83117
}
84118

85119
/// <summary>
86-
/// Determines the primary schema path based on the current variant.
120+
/// Determines the primary schema name based on the current variant.
87121
/// </summary>
88-
private static string DeterminePrimarySchemaPath(List<string> schemaPaths, string currentVariant)
122+
private static string DeterminePrimarySchemaName(List<string> schemaNames, string currentVariant)
89123
{
90124
if (string.IsNullOrEmpty(currentVariant))
91125
{
92-
return schemaPaths[0];
126+
return schemaNames[0];
93127
}
94128

95-
var variantSchema = schemaPaths.FirstOrDefault(p => Path.GetFileName(p)
96-
.Contains(
97-
currentVariant,
98-
StringComparison.OrdinalIgnoreCase));
129+
var variantSchema = schemaNames.FirstOrDefault(name => name.Contains(
130+
currentVariant,
131+
StringComparison.OrdinalIgnoreCase));
99132

100-
return variantSchema ?? schemaPaths[0];
133+
return variantSchema ?? schemaNames[0];
101134
}
102135

103136
/// <summary>
104137
/// Parses all schemas and extracts type hashes.
105138
/// </summary>
106139
private async Task<(Dictionary<string, JsonSchema> parsedSchemas, List<Dictionary<string, string>> allSchemaTypes)>
107-
ParseSchemasAsync(List<string> schemaPaths, CancellationToken cancellationToken)
140+
ParseSchemasAsync(List<SchemaInput> schemaInputs, CancellationToken cancellationToken)
108141
{
109142
var parsedSchemas = new Dictionary<string, JsonSchema>();
110143
var allSchemaTypes = new List<Dictionary<string, string>>();
111144

112-
foreach (var schemaPath in schemaPaths)
145+
foreach (var input in schemaInputs)
113146
{
114147
cancellationToken.ThrowIfCancellationRequested();
115148

116-
var schemaJson = await File.ReadAllTextAsync(schemaPath, cancellationToken);
149+
var schemaJson = await input.ReadContentAsync(cancellationToken);
117150
var schema = await JsonSchema.FromJsonAsync(schemaJson, cancellationToken);
118-
parsedSchemas[schemaPath] = schema;
151+
parsedSchemas[input.Name] = schema;
119152

120153
var types = ExtractTypeHashes(schema);
121154
allSchemaTypes.Add(types);
122155

123-
_logger.LogDebug(" {SchemaFile}: {TypeCount} types", Path.GetFileName(schemaPath), types.Count);
156+
_logger.LogDebug(" {SchemaFile}: {TypeCount} types", input.Name, types.Count);
124157
}
125158

126159
return (parsedSchemas, allSchemaTypes);
@@ -175,20 +208,19 @@ private static (HashSet<string> sharedTypes, HashSet<string> conflictingTypes) C
175208
/// Determines variant-specific types (types not shared or conflicting).
176209
/// </summary>
177210
private HashSet<string> DetermineVariantTypes(
178-
List<string> schemaPaths,
211+
List<string> schemaNames,
179212
string currentVariant,
180213
Dictionary<string, JsonSchema> parsedSchemas,
181214
HashSet<string> sharedTypes,
182215
HashSet<string> conflictingTypes,
183216
string rootTypeName)
184217
{
185-
var currentVariantSchemaPath = schemaPaths.FirstOrDefault(p => Path.GetFileName(p)
186-
.Contains(
187-
currentVariant,
188-
StringComparison.OrdinalIgnoreCase))
189-
?? schemaPaths[0];
218+
var currentVariantSchemaName = schemaNames.FirstOrDefault(name => name.Contains(
219+
currentVariant,
220+
StringComparison.OrdinalIgnoreCase))
221+
?? schemaNames[0];
190222

191-
var currentTypes = ExtractTypeHashes(parsedSchemas[currentVariantSchemaPath]);
223+
var currentTypes = ExtractTypeHashes(parsedSchemas[currentVariantSchemaName]);
192224

193225
var variantTypes = new HashSet<string>(currentTypes.Keys);
194226
variantTypes.ExceptWith(sharedTypes);

0 commit comments

Comments
 (0)