1 module tests.data;
2 
3 import std.conv;
4 import std.range;
5 import std.algorithm;
6 import std.path : buildPath, setExtension;
7 import std.exception : assertThrown;
8 import dtiled.data;
9 
10 enum testPath(string name) = "tests".buildPath("resources", name).setExtension("json");
11 
12 // expected gids for the test terrain layer
13 enum terrainGids = [1, 2, 1, 2, 3, 1, 3, 1, 2, 2, 3, 3, 4, 4, 4, 1];
14 enum flippedTerrainGids = [1, 2, 2, 1, 3, 1, 1, 3, 4, 4, 1, 4, 2, 2, 3, 3];
15 
16 /// Load a map containing a single tile layer
17 unittest {
18   // load map
19   auto map = MapData.load(testPath!"tiles");
20 
21   // general fields
22   assert(map.numRows         == 4);
23   assert(map.numCols         == 4);
24   assert(map.tileWidth       == 32);
25   assert(map.tileHeight      == 32);
26   assert(map.renderOrder     == MapData.RenderOrder.rightDown);
27   assert(map.orientation     == MapData.Orientation.orthogonal);
28   assert(map.backgroundColor == "#656667");
29 
30   // user defined properties
31   assert(map.properties["mapProperty1"] == "one");
32   assert(map.properties["mapProperty2"] == "two");
33 
34   // this map should have a single tile layer
35   assert(map.layers.length == 1);
36 
37   auto tiles = map.layers[0];
38   assert(tiles.name    == "terrain");
39   assert(tiles.data    == terrainGids);
40   assert(tiles.numRows == 4);
41   assert(tiles.numCols == 4);
42   assert(tiles.opacity == 1f);
43   assert(tiles.type    == LayerData.Type.tilelayer);
44   assert(tiles.visible);
45   assert(tiles.x == 0);
46   assert(tiles.y == 0);
47 
48   // getLayer should return layers[0]
49   assert(map.getLayer("terrain") == tiles);
50 
51   // this map should have a single tile set
52   assert(map.tilesets.length == 1); auto tileset = map.tilesets[0];
53   // fields
54   assert(tileset.name        == "terrain");
55   assert(tileset.firstGid    == 1);
56   assert(tileset.imageHeight == 64);
57   assert(tileset.imageWidth  == 64);
58   assert(tileset.margin      == 0);
59   assert(tileset.tileHeight  == 32);
60   assert(tileset.tileWidth   == 32);
61   assert(tileset.spacing     == 0);
62   // properties
63   assert(tileset.numRows == 2);
64   assert(tileset.numCols == 2);
65   assert(tileset.numTiles == 4);
66 
67   // getTileset should return tilesets[0]
68   assert(map.getTileset("terrain") == tileset);
69 }
70 
71 /// Load a map containing an object layer
72 unittest {
73   import std.string : format;
74 
75   // load map
76   auto map = MapData.load(testPath!"objects");
77 
78   // Layer 1 is an object layer in the test map
79   auto layer = map.layers[1];
80   assert(layer.name == "things");
81   assert(layer.type == LayerData.Type.objectgroup);
82   assert(layer.drawOrder == "topdown");
83 
84   // Tileset 1 is the tileset used for the objects
85   auto tileset = map.tilesets[1];
86   assert(tileset.name == "numbers");
87   auto objects = layer.objects;
88 
89   // helper to check an object in the test data
90   void checkObject(int num) {
91     string name = "number%d".format(num);
92     auto found = objects.find!(x => x.name == name);
93     assert(!found.empty, "no object with name " ~ name);
94     auto obj = found.front;
95 
96     assert(obj.gid == tileset.firstGid + num - 1); // number1 is the zeroth tile, ect.
97     assert(obj.type == (num % 2 == 0 ? "even" : "odd")); // just an arbitrarily picked type
98     //assert(obj.properties["half"].to!int == num / 2 ));
99     assert(obj.rotation == 0);
100     assert(obj.visible);
101   }
102 
103   checkObject(1);
104   checkObject(2);
105   checkObject(3);
106   checkObject(4);
107 }
108 
109 /// Load a map containing flipped (mirrored) tiles.
110 unittest {
111   import std.algorithm : map, equal;
112 
113   // load map
114   auto tileMap = MapData.load(testPath!"flipped_tiles");
115 
116   // this map should have a single tile layer
117   assert(tileMap.layers.length == 1);
118   auto layer = tileMap.layers[0];
119 
120   // clear special bits to get actual gid
121   auto gids = layer.data.map!(gid => gid & ~TiledFlag.all);
122   // with the special bits cleared, the gids should be the same as in the original map
123   assert(gids.equal(flippedTerrainGids));
124 
125   // isolate special bits to get flipped state
126   auto flags = layer.data.map!(gid => gid & TiledFlag.all);
127 
128   with(TiledFlag) {
129     enum N = none;
130     enum H = flipHorizontal;
131     enum V = flipVertical;
132     enum D = H | V;
133 
134     enum flippedState = [
135       N, N, H, H,
136       N, N, H, H,
137       V, V, D, D,
138       V, V, D, D,
139     ];
140 
141     assert(flags.equal(flippedState));
142   }
143 }
144 
145 /// Load a map containing flipped (mirrored) objects.
146 unittest {
147   import std.conv;
148   import std.string : format;
149 
150   // load map
151   auto map = MapData.load(testPath!"flipped_objects");
152 
153   // Layer 1 is an object layer in the test map
154   auto layer = map.layers[1];
155 
156   // Tileset 1 is the tileset used for the objects
157   auto tileset = map.tilesets[1];
158   assert(tileset.name == "numbers");
159   auto objects = layer.objects;
160 
161   // helper to check an object in the test data
162   void checkObject(int num, TiledFlag expectedFlags) {
163     string name = "number%d".format(num);
164     auto found = objects.find!(x => x.name == name);
165     assert(!found.empty, "no object with name " ~ name);
166     auto obj = found.front;
167 
168     auto gid = obj.gid & ~TiledFlag.all;
169     auto flags = obj.gid & TiledFlag.all;
170     assert(gid == tileset.firstGid + num - 1); // number1 is the zeroth tile, ect.
171     assert(flags == expectedFlags,
172         "tile %d: expected flag %s, got %s".format(num, expectedFlags, cast(TiledFlag) flags));
173   }
174 
175   checkObject(1, TiledFlag.none);
176   checkObject(2, TiledFlag.flipVertical);
177   checkObject(3, TiledFlag.flipHorizontal);
178   checkObject(4, TiledFlag.flipHorizontal | TiledFlag.flipVertical);
179 }