diff --git a/include/llmr/util/error.hpp b/include/llmr/util/error.hpp new file mode 100644 index 000000000..95106ff2c --- /dev/null +++ b/include/llmr/util/error.hpp @@ -0,0 +1,20 @@ +#ifndef LLMR_UTIL_ERROR +#define LLMR_UTIL_ERROR + +#include +#include + +namespace llmr { +namespace error { + +struct style_parse : std::exception { + inline style_parse(size_t offset, const char *msg) : offset(offset), msg(msg) {} + inline const char* what() const noexcept { return msg.c_str(); } + const size_t offset; + const std::string msg; +}; +} + +} + +#endif \ No newline at end of file diff --git a/include/llmr/util/io.hpp b/include/llmr/util/io.hpp index f1e560ffe..cdad97913 100644 --- a/include/llmr/util/io.hpp +++ b/include/llmr/util/io.hpp @@ -7,6 +7,7 @@ namespace llmr { namespace util { void write_file(const std::string &filename, const std::string &data); +std::string read_file(const std::string &filename); } } diff --git a/src/style/style.cpp b/src/style/style.cpp index fc2364da8..006376172 100644 --- a/src/style/style.cpp +++ b/src/style/style.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -102,6 +103,9 @@ void Style::loadJSON(const uint8_t *const data) { rapidjson::Document doc; doc.Parse<0>((const char *const)data); + if (doc.HasParseError()) { + throw error::style_parse(doc.GetErrorOffset(), doc.GetParseError()); + } StyleParser parser; parser.parse(const_cast(doc)); diff --git a/src/util/io.cpp b/src/util/io.cpp index bf519b057..fb36998e0 100644 --- a/src/util/io.cpp +++ b/src/util/io.cpp @@ -1,8 +1,14 @@ #include #include +#include +#include +#include -void llmr::util::write_file(const std::string &filename, const std::string &data) { +namespace llmr { +namespace util { + +void write_file(const std::string &filename, const std::string &data) { FILE *fd = fopen(filename.c_str(), "wb"); if (fd) { fwrite(data.data(), sizeof(std::string::value_type), data.size(), fd); @@ -11,3 +17,17 @@ void llmr::util::write_file(const std::string &filename, const std::string &data fprintf(stderr, "Failed to open file\n"); } } + +std::string read_file(const std::string &filename) { + std::ifstream file(filename); + if (file.good()) { + std::stringstream data; + data << file.rdbuf(); + return data.str(); + } else { + throw std::runtime_error("Cannot read file " + filename); + } +} + +} +} \ No newline at end of file diff --git a/test/fixtures/https/mapbox.s3.amazonaws.com/gl-glyphs-256/Open Sans Semibold, Arial Unicode MS Bold/0-255.pbf b/test/fixtures/glyphs/Open Sans Semibold, Arial Unicode MS Bold/0-255.pbf similarity index 100% rename from test/fixtures/https/mapbox.s3.amazonaws.com/gl-glyphs-256/Open Sans Semibold, Arial Unicode MS Bold/0-255.pbf rename to test/fixtures/glyphs/Open Sans Semibold, Arial Unicode MS Bold/0-255.pbf diff --git a/test/fixtures/result/out.png b/test/fixtures/result/out.png deleted file mode 100644 index aff3dccbb..000000000 Binary files a/test/fixtures/result/out.png and /dev/null differ diff --git a/test/fixtures/https/www.mapbox.com/mapbox-gl-styles/sprites/outdoors.json b/test/fixtures/sprites/outdoors.json similarity index 100% rename from test/fixtures/https/www.mapbox.com/mapbox-gl-styles/sprites/outdoors.json rename to test/fixtures/sprites/outdoors.json diff --git a/test/fixtures/https/www.mapbox.com/mapbox-gl-styles/sprites/outdoors.png b/test/fixtures/sprites/outdoors.png similarity index 100% rename from test/fixtures/https/www.mapbox.com/mapbox-gl-styles/sprites/outdoors.png rename to test/fixtures/sprites/outdoors.png diff --git a/test/fixtures/styles/0.actual.png b/test/fixtures/styles/0.actual.png new file mode 100644 index 000000000..34e984cce Binary files /dev/null and b/test/fixtures/styles/0.actual.png differ diff --git a/test/fixtures/styles/0.expected.png b/test/fixtures/styles/0.expected.png new file mode 100644 index 000000000..34e984cce Binary files /dev/null and b/test/fixtures/styles/0.expected.png differ diff --git a/test/fixtures/styles/0.info.json b/test/fixtures/styles/0.info.json new file mode 100644 index 000000000..3501d30ed --- /dev/null +++ b/test/fixtures/styles/0.info.json @@ -0,0 +1,8 @@ +{ + "zoom": 0, + "latitude": 0, + "longitude": 0, + "angle": 0, + "width": 512, + "height": 512 +} diff --git a/test/fixtures/styles/0.style.json b/test/fixtures/styles/0.style.json new file mode 100644 index 000000000..06d7a1db1 --- /dev/null +++ b/test/fixtures/styles/0.style.json @@ -0,0 +1,34 @@ +{ + "version": 3, + "sources": { + "mapbox": { + "type": "vector", + "url": "tiles/%d-%d-%d.vector.pbf", + "tileSize": 512, + "maxZoom": 14 + } + }, + "layers": [ + { + "id": "background", + "render": { + "type": "background" + }, + "style": { + "background-color": "red" + } + }, + { + "id": "water", + "source": "mapbox", + "source-layer": "water", + "render": { + "type": "fill" + }, + "style": { + "fill-color": "blue", + "fill-antialias": false + } + } + ] +} diff --git a/test/fixtures/http/api.tiles.mapbox.com/v3/mapbox.mapbox-terrain-v1,mapbox.mapbox-streets-v5/0/0/0.vector.pbf b/test/fixtures/tiles/0-0-0.vector.pbf similarity index 100% rename from test/fixtures/http/api.tiles.mapbox.com/v3/mapbox.mapbox-terrain-v1,mapbox.mapbox-streets-v5/0/0/0.vector.pbf rename to test/fixtures/tiles/0-0-0.vector.pbf diff --git a/test/fixtures/http/api.tiles.mapbox.com/v3/mapbox.mapbox-terrain-v1,mapbox.mapbox-streets-v5/2/1/1.vector.pbf b/test/fixtures/tiles/2-1-1.vector.pbf similarity index 100% rename from test/fixtures/http/api.tiles.mapbox.com/v3/mapbox.mapbox-terrain-v1,mapbox.mapbox-streets-v5/2/1/1.vector.pbf rename to test/fixtures/tiles/2-1-1.vector.pbf diff --git a/test/fixtures/http/api.tiles.mapbox.com/v3/mapbox.mapbox-terrain-v1,mapbox.mapbox-streets-v5/2/1/2.vector.pbf b/test/fixtures/tiles/2-1-2.vector.pbf similarity index 100% rename from test/fixtures/http/api.tiles.mapbox.com/v3/mapbox.mapbox-terrain-v1,mapbox.mapbox-streets-v5/2/1/2.vector.pbf rename to test/fixtures/tiles/2-1-2.vector.pbf diff --git a/test/fixtures/http/api.tiles.mapbox.com/v3/mapbox.mapbox-terrain-v1,mapbox.mapbox-streets-v5/2/2/1.vector.pbf b/test/fixtures/tiles/2-2-1.vector.pbf similarity index 100% rename from test/fixtures/http/api.tiles.mapbox.com/v3/mapbox.mapbox-terrain-v1,mapbox.mapbox-streets-v5/2/2/1.vector.pbf rename to test/fixtures/tiles/2-2-1.vector.pbf diff --git a/test/fixtures/http/api.tiles.mapbox.com/v3/mapbox.mapbox-terrain-v1,mapbox.mapbox-streets-v5/2/2/2.vector.pbf b/test/fixtures/tiles/2-2-2.vector.pbf similarity index 100% rename from test/fixtures/http/api.tiles.mapbox.com/v3/mapbox.mapbox-terrain-v1,mapbox.mapbox-streets-v5/2/2/2.vector.pbf rename to test/fixtures/tiles/2-2-2.vector.pbf diff --git a/test/headless.cpp b/test/headless.cpp index 88169d8da..108d7627b 100644 --- a/test/headless.cpp +++ b/test/headless.cpp @@ -3,12 +3,10 @@ #include #include #include -#include +#include #include "../common/headless_view.hpp" - -#include -#include +#include const std::string base_directory = []{ std::string fn = __FILE__; @@ -16,11 +14,27 @@ const std::string base_directory = []{ return fn; }(); -TEST(Headless, initialize) { - const unsigned int width = 1024; - const unsigned int height = 768; +class HeadlessTest : public ::testing::TestWithParam {}; - llmr::util::timer timer; +TEST_P(HeadlessTest, render) { + const char *base = GetParam(); + + const std::string style = llmr::util::read_file(base_directory + "/fixtures/styles/" + base + ".style.json"); + const std::string info = llmr::util::read_file(base_directory + "/fixtures/styles/" + base + ".info.json"); + const std::string expected_image = base_directory + "/fixtures/styles/" + base + ".expected.png"; + const std::string actual_image = base_directory + "/fixtures/styles/" + base + ".actual.png"; + + // Parse settings. + rapidjson::Document doc; + doc.Parse<0>((const char *const)info.c_str()); + ASSERT_EQ(false, doc.HasParseError()); + + const double zoom = doc.HasMember("zoom") ? doc["zoom"].GetDouble() : 0; + const double angle = doc.HasMember("angle") ? doc["angle"].GetDouble() : 0; + const double longitude = doc.HasMember("longitude") ? doc["longitude"].GetDouble() : 0; + const double latitude = doc.HasMember("latitude") ? doc["latitude"].GetDouble() : 0; + const unsigned int width = doc.HasMember("width") ? doc["width"].GetUint() : 0; + const unsigned int height = doc.HasMember("height") ? doc["height"].GetUint() : 0; // Setup OpenGL llmr::HeadlessView view; @@ -29,42 +43,27 @@ TEST(Headless, initialize) { view.resize(width, height); map.resize(width, height); - timer.report("map setup"); + map.setStyleJSON(style); - std::ifstream stylefile("./style.min.js"); - ASSERT_TRUE(stylefile.good()); - std::stringstream stylejson; - stylejson << stylefile.rdbuf(); - - map.setStyleJSON(stylejson.str()); - - timer.report("map style"); - - map.setLonLatZoom(0, 0, 2); - map.setAngle(0); + map.setLonLatZoom(longitude, latitude, zoom); + map.setAngle(angle); map.setDebug(false); // Run the loop. It will terminate when we don't have any further listeners. map.run(); - timer.report("map loop"); + const std::unique_ptr pixels(new uint32_t[width * height]); + glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.get()); - uint32_t *pixels = new uint32_t[width * height]; + const std::string image = llmr::util::compress_png(width, height, pixels.get(), true); + llmr::util::write_file(actual_image, image); - glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); - - std::string result = llmr::util::compress_png(width, height, pixels, true); - llmr::util::write_file("out.png", result); - - std::ifstream imagefile(base_directory + "/fixtures/result/out.png"); - ASSERT_TRUE(imagefile.good()); - std::stringstream image; - image << imagefile.rdbuf(); - llmr::util::Image reference(image.str(), true); - - ASSERT_EQ(reference.getWidth(), width); - ASSERT_EQ(reference.getHeight(), height); - ASSERT_EQ(0, std::memcmp(pixels, reference.getData(), width * height * 4)); - - delete[] pixels; + const std::string expected_image_data(llmr::util::read_file(expected_image)); + const llmr::util::Image expected(expected_image_data, true); + ASSERT_EQ(width, expected.getWidth()); + ASSERT_EQ(height, expected.getHeight()); + ASSERT_EQ(0, std::memcmp(pixels.get(), expected.getData(), width * height * 4)); } + +INSTANTIATE_TEST_CASE_P(Headless, HeadlessTest, + ::testing::Values("0"));