#include "minunit.h" #include "pmtiles.hpp" #include #include #include #include #include using namespace std; using namespace pmtiles; MU_TEST(test_tileid_to_zxy) { auto result = tileid_to_zxy(0); mu_check(result.z == 0); mu_check(result.x == 0); mu_check(result.y == 0); result = tileid_to_zxy(1); mu_check(result.z == 1); mu_check(result.x == 0); mu_check(result.y == 0); result = tileid_to_zxy(2); mu_check(result.z == 1); mu_check(result.x == 0); mu_check(result.y == 1); result = tileid_to_zxy(3); mu_check(result.z == 1); mu_check(result.x == 1); mu_check(result.y == 1); result = tileid_to_zxy(4); mu_check(result.z == 1); mu_check(result.x == 1); mu_check(result.y == 0); result = tileid_to_zxy(5); mu_check(result.z == 2); mu_check(result.x == 0); mu_check(result.y == 0); } MU_TEST(test_zxy_to_tileid) { mu_check(zxy_to_tileid(0, 0, 0) == 0); mu_check(zxy_to_tileid(1, 0, 0) == 1); mu_check(zxy_to_tileid(1, 0, 1) == 2); mu_check(zxy_to_tileid(1, 1, 1) == 3); mu_check(zxy_to_tileid(1, 1, 0) == 4); mu_check(zxy_to_tileid(2, 0, 0) == 5); } MU_TEST(test_roundtrip) { for (int z = 0; z < 32; z++) { uint64_t dim = (1 << z) - 1; auto tl = tileid_to_zxy(zxy_to_tileid(z, 0, 0)); mu_check(tl.z == z); mu_check(tl.x == 0); mu_check(tl.y == 0); auto tr = tileid_to_zxy(zxy_to_tileid(z, dim, 0)); mu_check(tr.z == z); mu_check(tr.x == dim); mu_check(tr.y == 0); auto bl = tileid_to_zxy(zxy_to_tileid(z, 0, dim)); mu_check(bl.z == z); mu_check(bl.x == 0); mu_check(bl.y == dim); auto br = tileid_to_zxy(zxy_to_tileid(z, dim, dim)); mu_check(br.z == z); mu_check(br.x == dim); mu_check(br.y == dim); } bool caught = false; try { tileid_to_zxy(18446744073709551615ULL); } catch (const std::runtime_error &e) { caught = true; } mu_check(caught); caught = false; try { zxy_to_tileid(32, 0, 0); } catch (const std::runtime_error &e) { caught = true; } mu_check(caught); caught = false; try { zxy_to_tileid(0, 1, 1); } catch (const std::runtime_error &e) { caught = true; } mu_check(caught); caught = false; try { zxy_to_tileid(31, 2147483647ULL + 1, 2147483647ULL + 1); } catch (const std::runtime_error &e) { caught = true; } mu_check(caught); } MU_TEST(test_serialize_directory) { vector entries; entries.push_back(entryv3(0, 0, 0, 0)); entries.push_back(entryv3(1, 1, 1, 1)); entries.push_back(entryv3(2, 2, 2, 2)); auto serialized = serialize_directory(entries); auto result = deserialize_directory(serialized); mu_check(result.size() == 3); mu_check(result[0].tile_id == 0); mu_check(result[0].offset == 0); mu_check(result[0].length == 0); mu_check(result[0].run_length == 0); mu_check(result[1].tile_id == 1); mu_check(result[1].offset == 1); mu_check(result[1].length == 1); mu_check(result[1].run_length == 1); mu_check(result[2].tile_id == 2); mu_check(result[2].offset == 2); mu_check(result[2].length == 2); mu_check(result[2].run_length == 2); } MU_TEST(test_serialize_header) { headerv3 header; auto len = header.serialize().size(); mu_check(len == 127); } MU_TEST(test_deserialize_header) { headerv3 header; header.root_dir_offset = 1; header.root_dir_bytes = 2; header.json_metadata_offset = 3; header.json_metadata_bytes = 4; header.leaf_dirs_offset = 5; header.leaf_dirs_bytes = 6; header.tile_data_offset = 7; header.tile_data_bytes = 8; header.addressed_tiles_count = 9; header.tile_entries_count = 10; header.tile_contents_count = 11; header.clustered = true; header.internal_compression = 0x1; header.tile_compression = 0x2; header.tile_type = 0x3; header.min_zoom = 12; header.max_zoom = 13; header.min_lon_e7 = 14; header.min_lat_e7 = 15; header.max_lon_e7 = 16; header.max_lat_e7 = 17; header.center_zoom = 14; header.center_lon_e7 = 18; header.center_lat_e7 = 19; auto serialized = header.serialize(); auto deserialized = deserialize_header(serialized); mu_check(deserialized.root_dir_offset == 1); mu_check(deserialized.root_dir_bytes == 2); mu_check(deserialized.json_metadata_offset == 3); mu_check(deserialized.json_metadata_bytes == 4); mu_check(deserialized.leaf_dirs_offset == 5); mu_check(deserialized.leaf_dirs_bytes == 6); mu_check(deserialized.tile_data_offset == 7); mu_check(deserialized.tile_data_bytes == 8); mu_check(deserialized.addressed_tiles_count == 9); mu_check(deserialized.tile_entries_count == 10); mu_check(deserialized.tile_contents_count == 11); mu_check(deserialized.clustered == true); mu_check(deserialized.internal_compression == 0x1); mu_check(deserialized.tile_compression == 0x2); mu_check(deserialized.tile_type == 0x3); mu_check(deserialized.min_zoom == 12); mu_check(deserialized.max_zoom == 13); mu_check(deserialized.min_lon_e7 == 14); mu_check(deserialized.min_lat_e7 == 15); mu_check(deserialized.max_lon_e7 == 16); mu_check(deserialized.max_lat_e7 == 17); mu_check(deserialized.center_zoom == 14); mu_check(deserialized.center_lon_e7 == 18); mu_check(deserialized.center_lat_e7 == 19); } string mycompress(const string &input, uint8_t compression) { if (compression != pmtiles::COMPRESSION_NONE) throw runtime_error("Unsupported compression"); return input; } string mydecompress(const string &input, uint8_t compression) { if (compression != pmtiles::COMPRESSION_NONE) throw runtime_error("Unsupported compression"); return input; } MU_TEST(test_build_dirs) { vector entries; for (int i = 0; i < 100000; i += 2) { entries.push_back(entryv3(i, i, 1, 2)); } string root_bytes; string leaves_bytes; int num_leaves; tie(root_bytes, leaves_bytes, num_leaves) = make_root_leaves(&mycompress, pmtiles::COMPRESSION_NONE, entries); pmtiles::headerv3 header; header.clustered = 0x0; header.internal_compression = COMPRESSION_NONE; header.root_dir_offset = 127; header.root_dir_bytes = root_bytes.size(); header.leaf_dirs_offset = 127 + root_bytes.size(); header.leaf_dirs_bytes = leaves_bytes.size(); header.tile_data_offset = header.leaf_dirs_offset + header.leaf_dirs_bytes; ofstream ostream; ostream.open("tmp.pmtiles", ios::out | ios::binary); auto header_str = header.serialize(); ostream.write(header_str.data(), header_str.length()); ostream.write(root_bytes.data(), root_bytes.length()); ostream.write(leaves_bytes.data(), leaves_bytes.length()); ostream.close(); mu_check(root_bytes.size() <= 16384); struct stat st; int fd = open("tmp.pmtiles", O_RDONLY); fstat(fd, &st); char *map = (char *) mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); auto result = entries_tms(&mydecompress, map); mu_check(result.size() == 100000); // 1 4 // 2 3 // in TMS order, 2, 1, 3, 4 mu_check(result[0].offset == header.tile_data_offset); mu_check(result[1].offset == header.tile_data_offset + 2); mu_check(result[2].offset == header.tile_data_offset); mu_check(result[3].offset == header.tile_data_offset + 2); mu_check(result[4].offset == header.tile_data_offset + 4); for (int i = 0; i < 100000; i += 31) { auto zxy = tileid_to_zxy(i); auto tile = get_tile(&mydecompress, map, zxy.z, zxy.x, zxy.y); mu_check(get<0>(tile) == header.tile_data_offset + i - (i % 2)); mu_check(get<1>(tile) == 1); } unlink("tmp.pmtiles"); } MU_TEST_SUITE(test_suite) { MU_RUN_TEST(test_tileid_to_zxy); MU_RUN_TEST(test_zxy_to_tileid); MU_RUN_TEST(test_roundtrip); MU_RUN_TEST(test_serialize_directory); MU_RUN_TEST(test_serialize_header); MU_RUN_TEST(test_deserialize_header); MU_RUN_TEST(test_build_dirs); } int main(int argc, char *argv[]) { MU_RUN_SUITE(test_suite); MU_REPORT(); return MU_EXIT_CODE; }