289 lines
11 KiB
Java

package p.packer.services;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import p.packer.messages.assets.AssetFamilyCatalog;
import p.packer.messages.assets.OutputCodecCatalog;
import p.packer.messages.diagnostics.PackerDiagnosticCategory;
import p.packer.testing.PackerFixtureLocator;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
final class PackerAssetDeclarationParserTest {
private final PackerAssetDeclarationParser parser = new PackerAssetDeclarationParser(new ObjectMapper());
@TempDir
Path tempDir;
@Test
void parsesValidDeclarationFixture() {
final var result = parser.parse(PackerFixtureLocator.fixtureRoot("workspaces/managed-basic/assets/ui/atlas/asset.json"));
assertTrue(result.valid());
assertNotNull(result.declaration());
assertEquals(1, result.declaration().schemaVersion());
assertEquals("fixture-uuid-1", result.declaration().assetUuid());
assertEquals("ui_atlas", result.declaration().name());
assertEquals(AssetFamilyCatalog.GLYPH_BANK, result.declaration().assetFamily());
assertEquals("GLYPH/indexed_v1", result.declaration().outputFormat().displayName());
assertEquals(OutputCodecCatalog.NONE, result.declaration().outputCodec());
assertEquals(Map.of(), result.declaration().outputPipelineMetadata());
assertTrue(result.declaration().preloadEnabled());
}
@Test
void parsesPipelineMetadataAsDedicatedOutputObject() throws Exception {
final Path manifest = tempDir.resolve("asset.json");
Files.writeString(manifest, """
{
"schema_version": 1,
"asset_uuid": "uuid-pipeline",
"name": "pipeline_asset",
"type": "sound_bank",
"output": {
"format": "SOUND/v1",
"codec": "NONE",
"pipeline": {
"samples": {
"1": { "offset": 0, "length": 128 }
},
"normalized": true
}
},
"preload": { "enabled": true }
}
""");
final var result = parser.parse(manifest);
assertTrue(result.valid());
assertEquals(2, result.declaration().outputPipelineMetadata().size());
assertTrue(result.declaration().outputPipelineMetadata().get("normalized").asBoolean());
assertEquals(128, result.declaration().outputPipelineMetadata().get("samples").path("1").path("length").asInt());
}
@Test
void parsesSceneBankDeclaration() throws Exception {
final Path manifest = tempDir.resolve("scene-asset.json");
Files.writeString(manifest, """
{
"schema_version": 1,
"asset_uuid": "uuid-scene",
"name": "overworld_scene",
"type": "scene_bank",
"output": {
"format": "SCENE/tilemap_v1",
"codec": "NONE"
},
"preload": { "enabled": false }
}
""");
final var result = parser.parse(manifest);
assertTrue(result.valid());
assertEquals(AssetFamilyCatalog.SCENE_BANK, result.declaration().assetFamily());
assertEquals("SCENE/tilemap_v1", result.declaration().outputFormat().displayName());
assertEquals(OutputCodecCatalog.NONE, result.declaration().outputCodec());
}
@Test
void parsesLegacySceneBankDeclarationFormatAsTilemapContract() throws Exception {
final Path manifest = tempDir.resolve("legacy-scene-asset.json");
Files.writeString(manifest, """
{
"schema_version": 1,
"asset_uuid": "uuid-scene-legacy",
"name": "overworld_scene",
"type": "scene_bank",
"output": {
"format": "SCENE/tiled_v1",
"codec": "NONE"
},
"preload": { "enabled": false }
}
""");
final var result = parser.parse(manifest);
assertTrue(result.valid());
assertEquals(AssetFamilyCatalog.SCENE_BANK, result.declaration().assetFamily());
assertEquals("SCENE/tilemap_v1", result.declaration().outputFormat().displayName());
assertEquals(OutputCodecCatalog.NONE, result.declaration().outputCodec());
}
@Test
void rejectsNonObjectPipelineMetadata() throws Exception {
final Path manifest = tempDir.resolve("asset.json");
Files.writeString(manifest, """
{
"schema_version": 1,
"asset_uuid": "uuid-pipeline",
"name": "pipeline_asset",
"type": "glyph_bank",
"output": {
"format": "GLYPH/indexed_v1",
"codec": "NONE",
"pipeline": []
},
"preload": { "enabled": true }
}
""");
final var result = parser.parse(manifest);
assertFalse(result.valid());
assertTrue(result.diagnostics().stream().anyMatch(diagnostic -> diagnostic.message().contains("output.pipeline")));
}
@Test
void parsesPaletteDeclarationsUsingExplicitIndexContract() throws Exception {
final Path manifest = tempDir.resolve("asset.json");
Files.writeString(manifest, """
{
"schema_version": 1,
"asset_uuid": "uuid-palette-contract",
"name": "palette_asset",
"type": "glyph_bank",
"output": {
"format": "GLYPH/indexed_v1",
"codec": "NONE",
"pipeline": {
"palettes": [
{
"index": 1,
"palette": {
"originalArgb8888": [4294901760],
"convertedRgb565": [63488]
}
}
]
}
},
"preload": { "enabled": true }
}
""");
final var result = parser.parse(manifest);
assertTrue(result.valid());
assertTrue(result.declaration().outputPipelineMetadata().get("palettes").isArray());
assertEquals(1, result.declaration().outputPipelineMetadata().get("palettes").get(0).path("index").asInt());
assertEquals(63488, result.declaration().outputPipelineMetadata().get("palettes").get(0).path("palette").path("convertedRgb565").get(0).asInt());
}
@Test
void rejectsPaletteDeclarationsWithDuplicateIndices() throws Exception {
final Path manifest = tempDir.resolve("asset.json");
Files.writeString(manifest, """
{
"schema_version": 1,
"asset_uuid": "uuid-palette-duplicate",
"name": "palette_asset",
"type": "glyph_bank",
"output": {
"format": "GLYPH/indexed_v1",
"codec": "NONE",
"pipeline": {
"palettes": [
{
"index": 0,
"palette": {
"originalArgb8888": [4294901760],
"convertedRgb565": [63488]
}
},
{
"index": 0,
"palette": {
"originalArgb8888": [4278255360],
"convertedRgb565": [2016]
}
}
]
}
},
"preload": { "enabled": true }
}
""");
final var result = parser.parse(manifest);
assertFalse(result.valid());
assertTrue(result.diagnostics().stream().anyMatch(diagnostic -> diagnostic.message().contains("duplicate palette index 0")));
}
@Test
void rejectsMalformedJsonWithStructuralDiagnostic() {
final var result = parser.parse(PackerFixtureLocator.fixtureRoot("workspaces/invalid-malformed/assets/bad/asset.json"));
assertFalse(result.valid());
assertNull(result.declaration());
assertTrue(result.diagnostics().stream().anyMatch(diagnostic -> diagnostic.category() == PackerDiagnosticCategory.STRUCTURAL));
}
@Test
void rejectsMissingRequiredFields() {
final var result = parser.parse(PackerFixtureLocator.fixtureRoot("workspaces/invalid-missing-fields/assets/bad/asset.json"));
assertFalse(result.valid());
assertTrue(result.diagnostics().stream().anyMatch(diagnostic -> diagnostic.message().contains("name")));
assertTrue(result.diagnostics().stream().anyMatch(diagnostic -> diagnostic.message().contains("asset_uuid")));
assertTrue(result.diagnostics().stream().anyMatch(diagnostic -> diagnostic.message().contains("format")));
}
@Test
void rejectsUnsupportedSchemaVersion() {
final var result = parser.parse(PackerFixtureLocator.fixtureRoot("workspaces/invalid-version/assets/bad/asset.json"));
assertFalse(result.valid());
assertTrue(result.diagnostics().stream().anyMatch(diagnostic -> diagnostic.category() == PackerDiagnosticCategory.VERSIONING));
}
@Test
void rejectsLegacyInputsField() throws Exception {
final Path manifest = tempDir.resolve("asset.json");
Files.writeString(manifest, """
{
"schema_version": 1,
"asset_uuid": "uuid-outside",
"name": "bad_asset",
"type": "glyph_bank",
"inputs": { "sprites": ["../outside.png"] },
"output": { "format": "GLYPH/indexed_v1", "codec": "NONE" },
"preload": { "enabled": true }
}
""");
final var result = parser.parse(manifest);
assertFalse(result.valid());
assertTrue(result.diagnostics().stream().anyMatch(diagnostic -> diagnostic.message().contains("no longer supported")));
}
@Test
void rejectsUnknownAssetFamily() throws Exception {
final Path manifest = tempDir.resolve("asset.json");
Files.writeString(manifest, """
{
"schema_version": 1,
"asset_uuid": "uuid-video",
"name": "bad_asset",
"type": "video_bank",
"output": { "format": "GLYPH/indexed_v1", "codec": "NONE" },
"preload": { "enabled": true }
}
""");
final var result = parser.parse(manifest);
assertFalse(result.valid());
assertTrue(result.diagnostics().stream().anyMatch(diagnostic -> diagnostic.message().contains("Field 'type' must be one of")));
}
}