85 lines
3.9 KiB
Python
85 lines
3.9 KiB
Python
|
|
import json
|
||
|
|
from pathlib import Path
|
||
|
|
import shutil
|
||
|
|
from typer.testing import CliRunner
|
||
|
|
|
||
|
|
from spriteforge.backend_registry import register_backend_contract
|
||
|
|
from spriteforge.backends.base import Backend,BackendContext,RawAsset
|
||
|
|
from spriteforge.backends.registry import register_backend
|
||
|
|
from spriteforge.cache import input_hash
|
||
|
|
from spriteforge.cli import app
|
||
|
|
from spriteforge.manifest import AssetEntry,AssetSpec
|
||
|
|
|
||
|
|
runner=CliRunner()
|
||
|
|
|
||
|
|
def manifest_file(tmp_path):
|
||
|
|
path=tmp_path/"assets.yaml"
|
||
|
|
path.write_text("""\
|
||
|
|
a_floor:
|
||
|
|
backend: procedural
|
||
|
|
generator: floor_tile
|
||
|
|
params: {width: 24, height: 12, color: '#605040'}
|
||
|
|
b_decal:
|
||
|
|
backend: procedural
|
||
|
|
generator: decal
|
||
|
|
params: {width: 20, height: 10, color: '#904030'}
|
||
|
|
""")
|
||
|
|
return path
|
||
|
|
|
||
|
|
def build(path,output,cache,*extra):
|
||
|
|
return runner.invoke(app,["build",str(path),"--output",str(output),"--cache-dir",str(cache),*extra])
|
||
|
|
|
||
|
|
def test_second_build_hits_artifact_cache(tmp_path):
|
||
|
|
path=manifest_file(tmp_path);output=tmp_path/"out";cache=tmp_path/"cache"
|
||
|
|
first=build(path,output,cache,"--jobs","2")
|
||
|
|
assert first.exit_code==0 and "generated=2" in first.stdout
|
||
|
|
second=build(path,output,cache,"--jobs","2")
|
||
|
|
assert second.exit_code==0 and "artifact=2" in second.stdout and "generated=0" in second.stdout
|
||
|
|
|
||
|
|
def test_raw_cache_survives_artifact_eviction(tmp_path):
|
||
|
|
path=manifest_file(tmp_path);output=tmp_path/"out";cache=tmp_path/"cache"
|
||
|
|
assert build(path,output,cache).exit_code==0
|
||
|
|
shutil.rmtree(cache/"artifacts")
|
||
|
|
repeated=build(path,output,cache)
|
||
|
|
assert repeated.exit_code==0 and "raw=2" in repeated.stdout and "artifact=0" in repeated.stdout
|
||
|
|
|
||
|
|
def test_metadata_tags_do_not_invalidate_render_or_artifact(tmp_path):
|
||
|
|
path=manifest_file(tmp_path);output=tmp_path/"out";cache=tmp_path/"cache"
|
||
|
|
assert build(path,output,cache).exit_code==0
|
||
|
|
path.write_text(path.read_text().replace("a_floor:\n", "a_floor:\n tags: [environment]\n"))
|
||
|
|
repeated=build(path,output,cache)
|
||
|
|
assert repeated.exit_code==0 and "generated=0" in repeated.stdout and "artifact=2" in repeated.stdout
|
||
|
|
index=json.loads((output/"index.json").read_text())
|
||
|
|
assert next(item for item in index["assets"] if item["id"]=="a_floor")["tags"]==["environment"]
|
||
|
|
|
||
|
|
def test_one_asset_change_preserves_other_and_palette(tmp_path):
|
||
|
|
path=manifest_file(tmp_path);output=tmp_path/"out";cache=tmp_path/"cache"
|
||
|
|
assert build(path,output,cache).exit_code==0
|
||
|
|
old=json.loads((output/"index.json").read_text());old_items={x["id"]:x for x in old["assets"]};palette=old["palette"]["hash"]
|
||
|
|
path.write_text(path.read_text().replace("#605040","#405870"))
|
||
|
|
changed=build(path,output,cache,"--asset","a_floor")
|
||
|
|
assert changed.exit_code==0 and "generated=1" in changed.stdout
|
||
|
|
new=json.loads((output/"index.json").read_text());new_items={x["id"]:x for x in new["assets"]}
|
||
|
|
assert new_items["b_decal"]==old_items["b_decal"]
|
||
|
|
assert new_items["a_floor"]["hash"]!=old_items["a_floor"]["hash"]
|
||
|
|
assert new["palette"]["hash"]==palette
|
||
|
|
|
||
|
|
class DependencyBackend(Backend):
|
||
|
|
name="dependency_test";version="1"
|
||
|
|
def dependencies(self,spec,context):return (Path(spec.params["path"]),)
|
||
|
|
def generate(self,spec,context)->RawAsset:raise AssertionError("not used")
|
||
|
|
|
||
|
|
def test_dependency_bytes_invalidate_input_hash(tmp_path):
|
||
|
|
register_backend_contract("dependency_test",lambda spec:None,replace=True)
|
||
|
|
register_backend("dependency_test",DependencyBackend,replace=True)
|
||
|
|
source=tmp_path/"source.bin";source.write_bytes(b"one")
|
||
|
|
spec=AssetSpec(backend="dependency_test",params={"path":str(source)})
|
||
|
|
entry=AssetEntry("thing",spec,tmp_path/"assets.yaml",1)
|
||
|
|
before=input_hash(entry)[0];source.write_bytes(b"two");after=input_hash(entry)[0]
|
||
|
|
assert before!=after
|
||
|
|
|
||
|
|
def test_filtered_palette_rebuild_is_rejected(tmp_path):
|
||
|
|
path=manifest_file(tmp_path)
|
||
|
|
result=build(path,tmp_path/"out",tmp_path/"cache","--asset","a_floor","--rebuild-palette")
|
||
|
|
assert result.exit_code==2 and "unfiltered full build" in result.stderr
|