1
0
mirror of synced 2024-11-13 18:20:51 +01:00

metadata.preview is now polymorphic

This commit is contained in:
Stepland 2021-10-08 13:21:32 +02:00
parent 9473f3c1ac
commit 6da792fac1
14 changed files with 224 additions and 31 deletions

View File

@ -38,7 +38,6 @@ It's a json object with the following keys :
"BPM": 120.0,
"offset": 0.0,
"preview": {},
"preview path": "",
}
```
Contains information that applies to the whole set of charts
@ -60,11 +59,8 @@ Contains information that applies to the whole set of charts
- number, required
- In seconds, opposite of the time position of the first beat in the music file. For instance, if the first beat occurs at 0.15 seconds in the audio file, `offset̀` should be -0.15
- **preview**
- object, optional
- If present, contains a {ref}`preview object <preview>`
- **preview path**
- string, optional
- Path to a preview file to be played on loop at the music select screen. Alternative to the music sample described by `preview`.
- object or string, optional
- Contains either a {ref}`preview object <preview>` or a path to a bms-style preview file
(preview)=
## Preview

View File

@ -39,24 +39,28 @@
},
"preview": {
"description": "Describes the part of the music file that's to be played on loop when previewing this song",
"type": "object",
"properties": {
"position": {
"description": "In seconds, Time at which preview should start",
"type": "number",
"minimum": 0
"oneOf": [{
"description": "Preview defined as a sample from the music file",
"type": "object",
"properties": {
"position": {
"description": "In seconds, Time at which preview should start",
"type": "number",
"minimum": 0
},
"length": {
"description": "In seconds, for how long should the preview be played past the starting point",
"type": "number",
"minimum": 0
}
},
"required": ["position", "length"]
},
"length": {
"description": "In seconds, for how long should the preview be played past the starting point",
"type": "number",
"minimum": 0
{
"description": "Preview defined as a separate audio file",
"type": "string"
}
},
"required": ["position", "length"]
},
"preview path": {
"description": "Path to the music preview, relative to the memon file",
"type": "string"
]
}
},
"required": ["song title", "artist", "BPM", "offset"]
@ -92,7 +96,7 @@
},
"t": {
"description": "Timing, measured in \"ticks\" as specified with the resolution",
"type":"integer",
"type": "integer",
"minimum": 0
},
"l": {

0
tests/__init__.py Normal file
View File

67
tests/conftest.py Normal file
View File

@ -0,0 +1,67 @@
import json
from functools import partial
from pathlib import Path
from typing import Any
import py.path
import pytest
from jschon import create_catalog, JSON, JSONSchema, Catalog
from .utils import raise_if_invalid, result_of
def pytest_collect_file(path: py.path.local, parent: pytest.Collector):
fspath = Path(path)
if fspath.name == "__init__.py" and fspath.parent.name == "data":
return ExamplesFolder.from_parent(parent, name="data", fspath=path.dirpath())
class ExamplesFolder(pytest.Collector):
def collect(self):
path = Path(self.fspath)
for thing in path.glob("*-*"):
if thing.is_dir():
yield ExampleSubfolder.from_parent(
self,
name=thing.name,
fspath=py.path.local(thing),
)
class ExampleSubfolder(pytest.Collector):
def collect(self):
path = Path(self.fspath)
for file in (path / "pass").glob("*.json"):
yield ValidExample.from_parent(self, name=file.stem, path=file)
for file in (path / "fail").glob("*.json"):
yield InvalidExample.from_parent(self, name=file.stem, path=file)
class ItemWithPath(pytest.Item):
def __init__(self, *a, path: Path, **kw):
super().__init__(*a, **kw)
self.path = path
def reportinfo(self):
return self.path, 0, ""
class ValidExample(ItemWithPath):
def runtest(self):
raise_if_invalid(result_of(self.path))
def repr_failure(self, excinfo):
parent = self.getparent(ExampleSubfolder)
return f"Example {parent.name}::{self.name} did not pass validation"
class InvalidExample(ItemWithPath):
def runtest(self):
with pytest.raises(RuntimeError):
raise_if_invalid(result_of(self.path))
def repr_failure(self, excinfo):
parent = self.getparent(ExampleSubfolder)
return f"Example {parent.name}::{self.name} passed validation but shouldn't"

View File

@ -0,0 +1,10 @@
{
"version": "1.0.0",
"metadata": {
"song title": "",
"artist": "",
"BPM": 1,
"offset": 0
},
"data": {}
}

View File

@ -0,0 +1,11 @@
{
"version": "1.0.0",
"metadata": {
"song title": "",
"artist": "",
"BPM": 1,
"offset": 0,
"preview": []
},
"data": {}
}

View File

@ -0,0 +1,14 @@
{
"version": "1.0.0",
"metadata": {
"song title": "",
"artist": "",
"BPM": 1,
"offset": 0,
"preview": {
"position": 0,
"duration": 0
}
},
"data": {}
}

View File

@ -0,0 +1,11 @@
{
"version": "1.0.0",
"metadata": {
"song title": "",
"artist": "",
"BPM": 1,
"offset": 0,
"preview": null
},
"data": {}
}

View File

@ -0,0 +1,11 @@
{
"version": "1.0.0",
"metadata": {
"song title": "",
"artist": "",
"BPM": 1,
"offset": 0,
"preview": 3
},
"data": {}
}

View File

@ -0,0 +1,14 @@
{
"version": "1.0.0",
"metadata": {
"song title": "",
"artist": "",
"BPM": 1,
"offset": 0,
"preview": {
"position": 0,
"length": 0
}
},
"data": {}
}

View File

@ -0,0 +1,11 @@
{
"version": "1.0.0",
"metadata": {
"song title": "",
"artist": "",
"BPM": 1,
"offset": 0,
"preview": "blablabla"
},
"data": {}
}

2
tests/data/__init__.py Normal file
View File

@ -0,0 +1,2 @@
"""This file is here so the test code can use importlib as a portable way to
open test data in this folder"""

View File

@ -1,10 +1,7 @@
import json
from jschon import create_catalog, JSONSchema
from jschon import JSONSchema
from .utils import raise_if_invalid, SCHEMA
def test_that_the_schema_follows_the_metaschema():
catalog_2019_09 = create_catalog("2019-09", default=True)
with open("schema.json") as file:
raw = json.load(file)
schema = JSONSchema(raw, catalog=catalog_2019_09)
schema.validate()
schema_validity = SCHEMA.validate()
raise_if_invalid(schema_validity)

45
tests/utils.py Normal file
View File

@ -0,0 +1,45 @@
import json
from collections import defaultdict
from pathlib import Path
from typing import Iterator, Dict, Tuple, List
from jschon.jsonschema import Scope
from jschon import create_catalog, JSONSchema, JSON
import pytest
_catalog = create_catalog('2019-09')
SCHEMA = JSONSchema.loadf("schema.json", catalog=_catalog)
def prettify_path(json_path: str) -> str:
"""turns a JSONPath /that/looks/like/this into a.dotted.path.like.this"""
split = json_path.split("/")
return ".".join(split[1:])
def flatten_errors(scope: Scope) -> Dict[str, List[str]]:
"""Returns a (path -> errors) mapping of the leaf nodes
of the detailed error output"""
def iter_errors(d: dict) -> Iterator[Tuple[str, str]]:
if 'error' in d:
yield prettify_path(d['instanceLocation']), d['error']
elif 'errors' in d:
for error in d['errors']:
yield from iter_errors(error)
errors = defaultdict(list)
for path, message in iter_errors(scope.output('detailed')):
errors[path].append(message)
return {
key: value[0] if len(value) == 1 else value
for key, value in errors.items()
}
def raise_if_invalid(scope: Scope):
__tracebackhide__ = True
if not scope.valid:
raise RuntimeError(flatten_errors(scope))
def result_of(path: Path) -> Scope:
raw = json.loads(path.read_text())
instance = JSON(raw)
return SCHEMA.evaluate(instance)