diff --git a/pytiled_parser/parsers/json/tiled_map.py b/pytiled_parser/parsers/json/tiled_map.py index 82f6c04..c57a47e 100644 --- a/pytiled_parser/parsers/json/tiled_map.py +++ b/pytiled_parser/parsers/json/tiled_map.py @@ -75,13 +75,7 @@ def parse(file: Path) -> TiledMap: tileset_path = Path(parent_dir / raw_tileset["source"]) parser = check_format(tileset_path) with open(tileset_path) as raw_tileset_file: - if parser == "json": - tilesets[raw_tileset["firstgid"]] = parse_json_tileset( - json.load(raw_tileset_file), - raw_tileset["firstgid"], - external_path=tileset_path.parent, - ) - elif parser == "tmx": + if parser == "tmx": raw_tileset_external = etree.parse(raw_tileset_file).getroot() tilesets[raw_tileset["firstgid"]] = parse_tmx_tileset( raw_tileset_external, @@ -89,10 +83,17 @@ def parse(file: Path) -> TiledMap: external_path=tileset_path.parent, ) else: - raise UnknownFormat( - "Unkown Tileset format, please use either the TSX or JSON format." - ) - + try: + tilesets[raw_tileset["firstgid"]] = parse_json_tileset( + json.load(raw_tileset_file), + raw_tileset["firstgid"], + external_path=tileset_path.parent, + ) + except ValueError: + raise UnknownFormat( + "Unknown Tileset Format, please use either the TSX or JSON format. " + "This message could also mean your tileset file is invalid or corrupted." + ) else: # Is an embedded Tileset raw_tileset = cast(RawTileSet, raw_tileset) @@ -100,7 +101,7 @@ def parse(file: Path) -> TiledMap: raw_tileset, raw_tileset["firstgid"] ) - if isinstance(raw_tiled_map["version"], float): + if isinstance(raw_tiled_map["version"], float): # pragma: no cover version = str(raw_tiled_map["version"]) else: version = raw_tiled_map["version"] diff --git a/pytiled_parser/parsers/json/tileset.py b/pytiled_parser/parsers/json/tileset.py index d86c841..208d787 100644 --- a/pytiled_parser/parsers/json/tileset.py +++ b/pytiled_parser/parsers/json/tileset.py @@ -178,10 +178,14 @@ def _parse_tile(raw_tile: RawTile, external_path: Optional[Path] = None) -> Tile else: tile.image = Path(raw_tile["image"]) - if raw_tile.get("imagewidth") is not None: + # These are ignored from coverage because there does not exist a scenario where + # image is set, but these aren't, so the branches will never fully be hit. + # However, leaving these checks in place is nice to prevent fatal errors on + # a manually edited map that has an "incorrect" but not "unusable" structure + if raw_tile.get("imagewidth") is not None: # pragma: no cover tile.image_width = raw_tile["imagewidth"] - if raw_tile.get("imageheight") is not None: + if raw_tile.get("imageheight") is not None: # pragma: no cover tile.image_height = raw_tile["imageheight"] if raw_tile.get("type") is not None: @@ -218,7 +222,11 @@ def parse( ) if raw_tileset.get("version") is not None: - if isinstance(raw_tileset["version"], float): + # This is here to support old versions of Tiled Maps. It's a pain + # to keep old versions in the test data and not update them with the + # rest so I'm excluding this from coverage. In reality it's probably + # not needed. Tiled hasn't been using floats for the version for a long time + if isinstance(raw_tileset["version"], float): # pragma: no cover tileset.version = str(raw_tileset["version"]) else: tileset.version = raw_tileset["version"] @@ -234,10 +242,12 @@ def parse( else: tileset.image = Path(raw_tileset["image"]) - if raw_tileset.get("imagewidth") is not None: + # See above note about imagewidth and imageheight on parse_tile function + # for an explanation on why these are ignored + if raw_tileset.get("imagewidth") is not None: # pragma: no cover tileset.image_width = raw_tileset["imagewidth"] - if raw_tileset.get("imageheight") is not None: + if raw_tileset.get("imageheight") is not None: # pragma: no cover tileset.image_height = raw_tileset["imageheight"] if raw_tileset.get("objectalignment") is not None: diff --git a/tests/test_data/map_tests/cross_format_tileset/expected.py b/tests/test_data/map_tests/cross_format_tileset/expected.py new file mode 100644 index 0000000..61c9622 --- /dev/null +++ b/tests/test_data/map_tests/cross_format_tileset/expected.py @@ -0,0 +1,45 @@ +from pathlib import Path + +from pytiled_parser import common_types, tiled_map, tileset + +EXPECTED = tiled_map.TiledMap( + infinite=False, + layers=[], + map_size=common_types.Size(8, 6), + next_layer_id=2, + next_object_id=1, + orientation="orthogonal", + render_order="right-down", + tiled_version="1.6.0", + tile_size=common_types.Size(32, 32), + version="1.6", + background_color=common_types.Color(255, 0, 4, 255), + tilesets={ + 1: tileset.Tileset( + columns=8, + image=Path(Path(__file__).parent / "../../images/tmw_desert_spacing.png") + .absolute() + .resolve(), + image_width=265, + image_height=199, + firstgid=1, + margin=1, + spacing=1, + name="tile_set_image", + tile_count=48, + tiled_version="1.6.0", + tile_height=32, + tile_width=32, + version="1.6", + type="tileset", + ) + }, + properties={ + "bool property - true": True, + "color property": common_types.Color(73, 252, 255, 255), + "file property": Path("../../../../../../var/log/syslog"), + "float property": 1.23456789, + "int property": 13, + "string property": "Hello, World!!", + }, +) diff --git a/tests/test_data/map_tests/cross_format_tileset/map.json b/tests/test_data/map_tests/cross_format_tileset/map.json new file mode 100644 index 0000000..262aeef --- /dev/null +++ b/tests/test_data/map_tests/cross_format_tileset/map.json @@ -0,0 +1,52 @@ +{ "backgroundcolor":"#ff0004", + "compressionlevel":0, + "height":6, + "infinite":false, + "layers":[], + "nextlayerid":2, + "nextobjectid":1, + "orientation":"orthogonal", + "properties":[ + { + "name":"bool property - true", + "type":"bool", + "value":true + }, + { + "name":"color property", + "type":"color", + "value":"#ff49fcff" + }, + { + "name":"file property", + "type":"file", + "value":"..\/..\/..\/..\/..\/..\/var\/log\/syslog" + }, + { + "name":"float property", + "type":"float", + "value":1.23456789 + }, + { + "name":"int property", + "type":"int", + "value":13 + }, + { + "name":"string property", + "type":"string", + "value":"Hello, World!!" + }], + "renderorder":"right-down", + "tiledversion":"1.6.0", + "tileheight":32, + "tilesets":[ + { + "firstgid":1, + "source":"tileset.tsx" + }], + "tilewidth":32, + "type":"map", + "version":"1.6", + "width":8 +} \ No newline at end of file diff --git a/tests/test_data/map_tests/cross_format_tileset/map.tmx b/tests/test_data/map_tests/cross_format_tileset/map.tmx new file mode 100644 index 0000000..b7ad3fe --- /dev/null +++ b/tests/test_data/map_tests/cross_format_tileset/map.tmx @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/tests/test_data/map_tests/cross_format_tileset/tileset.json b/tests/test_data/map_tests/cross_format_tileset/tileset.json new file mode 100644 index 0000000..1a07451 --- /dev/null +++ b/tests/test_data/map_tests/cross_format_tileset/tileset.json @@ -0,0 +1,14 @@ +{ "columns":8, + "image":"..\/..\/images\/tmw_desert_spacing.png", + "imageheight":199, + "imagewidth":265, + "margin":1, + "name":"tile_set_image", + "spacing":1, + "tilecount":48, + "tiledversion":"1.6.0", + "tileheight":32, + "tilewidth":32, + "type":"tileset", + "version":"1.6" +} \ No newline at end of file diff --git a/tests/test_data/map_tests/cross_format_tileset/tileset.tsx b/tests/test_data/map_tests/cross_format_tileset/tileset.tsx new file mode 100644 index 0000000..8b1cf24 --- /dev/null +++ b/tests/test_data/map_tests/cross_format_tileset/tileset.tsx @@ -0,0 +1,4 @@ + + + + diff --git a/tests/test_data/map_tests/json_invalid_tileset/map.json b/tests/test_data/map_tests/json_invalid_tileset/map.json new file mode 100644 index 0000000..74476ea --- /dev/null +++ b/tests/test_data/map_tests/json_invalid_tileset/map.json @@ -0,0 +1,52 @@ +{ "backgroundcolor":"#ff0004", + "compressionlevel":0, + "height":6, + "infinite":false, + "layers":[], + "nextlayerid":2, + "nextobjectid":1, + "orientation":"orthogonal", + "properties":[ + { + "name":"bool property - true", + "type":"bool", + "value":true + }, + { + "name":"color property", + "type":"color", + "value":"#ff49fcff" + }, + { + "name":"file property", + "type":"file", + "value":"..\/..\/..\/..\/..\/..\/var\/log\/syslog" + }, + { + "name":"float property", + "type":"float", + "value":1.23456789 + }, + { + "name":"int property", + "type":"int", + "value":13 + }, + { + "name":"string property", + "type":"string", + "value":"Hello, World!!" + }], + "renderorder":"right-down", + "tiledversion":"1.6.0", + "tileheight":32, + "tilesets":[ + { + "firstgid":1, + "source":"tileset.garbage" + }], + "tilewidth":32, + "type":"map", + "version":"1.6", + "width":8 +} \ No newline at end of file diff --git a/tests/test_data/map_tests/json_invalid_tileset/tileset.garbage b/tests/test_data/map_tests/json_invalid_tileset/tileset.garbage new file mode 100644 index 0000000..1792273 --- /dev/null +++ b/tests/test_data/map_tests/json_invalid_tileset/tileset.garbage @@ -0,0 +1 @@ +|||thisisgarbage|||||itwillbreak|||butthatswhatwewant|| \ No newline at end of file diff --git a/tests/test_data/tilesets/image/expected.py b/tests/test_data/tilesets/image/expected.py index b1df6c6..9c5634e 100644 --- a/tests/test_data/tilesets/image/expected.py +++ b/tests/test_data/tilesets/image/expected.py @@ -12,9 +12,10 @@ EXPECTED = tileset.Tileset( spacing=1, name="tile_set_image", tile_count=48, - tiled_version="1.6.0", + tiled_version="1.8.5", tile_height=32, tile_width=32, - version="1.6", + version="1.8", type="tileset", + alignment="topleft" ) diff --git a/tests/test_data/tilesets/image/tileset.json b/tests/test_data/tilesets/image/tileset.json index 1a07451..fe47118 100644 --- a/tests/test_data/tilesets/image/tileset.json +++ b/tests/test_data/tilesets/image/tileset.json @@ -4,11 +4,12 @@ "imagewidth":265, "margin":1, "name":"tile_set_image", + "objectalignment":"topleft", "spacing":1, "tilecount":48, - "tiledversion":"1.6.0", + "tiledversion":"1.8.5", "tileheight":32, "tilewidth":32, "type":"tileset", - "version":"1.6" + "version":"1.8" } \ No newline at end of file diff --git a/tests/test_data/tilesets/image/tileset.tsx b/tests/test_data/tilesets/image/tileset.tsx index 8aee17a..ccff966 100644 --- a/tests/test_data/tilesets/image/tileset.tsx +++ b/tests/test_data/tilesets/image/tileset.tsx @@ -1,4 +1,4 @@ - + diff --git a/tests/test_map.py b/tests/test_map.py index 424e802..b1730df 100644 --- a/tests/test_map.py +++ b/tests/test_map.py @@ -5,7 +5,7 @@ from pathlib import Path import pytest -from pytiled_parser import parse_map +from pytiled_parser import UnknownFormat, parse_map from pytiled_parser.common_types import OrderedPair, Size TESTS_DIR = Path(os.path.dirname(os.path.abspath(__file__))) @@ -19,8 +19,10 @@ ALL_MAP_TESTS = [ MAP_TESTS / "hexagonal", MAP_TESTS / "embedded_tileset", MAP_TESTS / "template", + MAP_TESTS / "cross_format_tileset", ] +JSON_INVALID_TILESET = MAP_TESTS / "json_invalid_tileset" def fix_object(my_object): my_object.coordinates = OrderedPair( @@ -83,3 +85,9 @@ def test_map_integration(parser_type, map_test): fix_map(expected.EXPECTED) fix_map(casted_map) assert casted_map == expected.EXPECTED + +def test_json_invalid_tileset(): + raw_map_path = JSON_INVALID_TILESET / "map.json" + + with pytest.raises(UnknownFormat): + parse_map(raw_map_path)