summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorJohn Firebaugh <john.firebaugh@gmail.com>2016-02-05 17:10:13 -0800
committerJohn Firebaugh <john.firebaugh@gmail.com>2016-02-10 15:40:20 -0800
commitc3c4c7b9a695ad1dbebe57242ba071103fe9a567 (patch)
treee205ecdc6a2f6318c6ba6308b5aa8baacc42f481 /test
parente9302c797f68c7e48b908b87b126045c8c5e5209 (diff)
downloadqtlocation-mapboxgl-c3c4c7b9a695ad1dbebe57242ba071103fe9a567.tar.gz
[core] Interface and implementation for offline
Diffstat (limited to 'test')
-rw-r--r--test/fixtures/offline/empty.style.json5
-rw-r--r--test/fixtures/offline/geojson.json4
-rw-r--r--test/fixtures/offline/geojson_source.style.json10
-rw-r--r--test/fixtures/offline/inline_source.style.json17
-rw-r--r--test/storage/offline.cpp73
-rw-r--r--test/storage/offline_database.cpp76
-rw-r--r--test/storage/offline_download.cpp302
-rw-r--r--test/test.gypi2
8 files changed, 489 insertions, 0 deletions
diff --git a/test/fixtures/offline/empty.style.json b/test/fixtures/offline/empty.style.json
new file mode 100644
index 0000000000..61a8fadcdb
--- /dev/null
+++ b/test/fixtures/offline/empty.style.json
@@ -0,0 +1,5 @@
+{
+ "version": 8,
+ "sources": {},
+ "layers": []
+}
diff --git a/test/fixtures/offline/geojson.json b/test/fixtures/offline/geojson.json
new file mode 100644
index 0000000000..8b3698faf7
--- /dev/null
+++ b/test/fixtures/offline/geojson.json
@@ -0,0 +1,4 @@
+{
+ "type": "FeatureCollection",
+ "features": []
+}
diff --git a/test/fixtures/offline/geojson_source.style.json b/test/fixtures/offline/geojson_source.style.json
new file mode 100644
index 0000000000..511fca9fd0
--- /dev/null
+++ b/test/fixtures/offline/geojson_source.style.json
@@ -0,0 +1,10 @@
+{
+ "version": 8,
+ "sources": {
+ "geojson": {
+ "type": "geojson",
+ "data": "http://127.0.0.1:3000/offline/geojson.json"
+ }
+ },
+ "layers": []
+}
diff --git a/test/fixtures/offline/inline_source.style.json b/test/fixtures/offline/inline_source.style.json
new file mode 100644
index 0000000000..87155d07d8
--- /dev/null
+++ b/test/fixtures/offline/inline_source.style.json
@@ -0,0 +1,17 @@
+{
+ "version": 8,
+ "sources": {
+ "inline": {
+ "type": "vector",
+ "maxzoom": 15,
+ "minzoom": 0,
+ "tiles": [ "http://127.0.0.1:3000/offline/{z}-{x}-{y}.vector.pbf" ]
+ }
+ },
+ "layers": [{
+ "id": "fill",
+ "type": "fill",
+ "source": "inline",
+ "source-layer": "water"
+ }]
+}
diff --git a/test/storage/offline.cpp b/test/storage/offline.cpp
new file mode 100644
index 0000000000..b34aa02c22
--- /dev/null
+++ b/test/storage/offline.cpp
@@ -0,0 +1,73 @@
+#include <mbgl/storage/offline.hpp>
+#include <mbgl/map/source_info.hpp>
+#include <mbgl/map/tile_id.hpp>
+
+#include <gtest/gtest.h>
+
+using namespace mbgl;
+
+static const LatLngBounds sanFrancisco = LatLngBounds::hull(
+ { 37.6609, -122.5744 },
+ { 37.8271, -122.3204 });
+
+static const LatLngBounds sanFranciscoWrapped = LatLngBounds::hull(
+ { 37.6609, 238.5744 },
+ { 37.8271, 238.3204 });
+
+TEST(OfflineTilePyramidRegionDefinition, TileCoverEmpty) {
+ OfflineTilePyramidRegionDefinition region("", LatLngBounds::empty(), 0, 20, 1.0);
+ SourceInfo info;
+
+ auto result = region.tileCover(SourceType::Vector, 512, info);
+ ASSERT_TRUE(result.empty());
+}
+
+TEST(OfflineTilePyramidRegionDefinition, TileCoverZoomIntersection) {
+ OfflineTilePyramidRegionDefinition region("", sanFrancisco, 2, 2, 1.0);
+ SourceInfo info;
+
+ info.minZoom = 0;
+ auto resultIntersection = region.tileCover(SourceType::Vector, 512, info);
+ ASSERT_EQ(1, resultIntersection.size());
+
+ info.minZoom = 3;
+ auto resultNoIntersection = region.tileCover(SourceType::Vector, 512, info);
+ ASSERT_TRUE(resultNoIntersection.empty());
+}
+
+TEST(OfflineTilePyramidRegionDefinition, TileCoverTileSize) {
+ OfflineTilePyramidRegionDefinition region("", LatLngBounds::world(), 0, 0, 1.0);
+ SourceInfo info;
+
+ auto result512 = region.tileCover(SourceType::Vector, 512, info);
+ ASSERT_EQ(1, result512.size());
+ ASSERT_EQ(0, result512[0].z);
+
+ auto result256 = region.tileCover(SourceType::Vector, 256, info);
+ ASSERT_EQ(4, result256.size());
+ ASSERT_EQ(1, result256[0].z);
+}
+
+TEST(OfflineTilePyramidRegionDefinition, TileCoverZoomRounding) {
+ OfflineTilePyramidRegionDefinition region("", sanFrancisco, 0.6, 0.7, 1.0);
+ SourceInfo info;
+
+ auto resultVector = region.tileCover(SourceType::Vector, 512, info);
+ ASSERT_EQ(1, resultVector.size());
+ ASSERT_EQ(0, resultVector[0].z);
+
+ auto resultRaster = region.tileCover(SourceType::Raster, 512, info);
+ ASSERT_EQ(1, resultRaster.size());
+ ASSERT_EQ(1, resultRaster[0].z);
+}
+
+TEST(OfflineTilePyramidRegionDefinition, TileCoverWrapped) {
+ OfflineTilePyramidRegionDefinition region("", sanFranciscoWrapped, 0, 0, 1.0);
+ SourceInfo info;
+
+ auto result = region.tileCover(SourceType::Vector, 512, info);
+ ASSERT_EQ(1, result.size());
+ ASSERT_EQ(0, result[0].z);
+ ASSERT_EQ(0, result[0].x);
+ ASSERT_EQ(0, result[0].y);
+}
diff --git a/test/storage/offline_database.cpp b/test/storage/offline_database.cpp
index 1c43506066..2018f6a40b 100644
--- a/test/storage/offline_database.cpp
+++ b/test/storage/offline_database.cpp
@@ -372,3 +372,79 @@ TEST(OfflineDatabase, PutTileNotFound) {
EXPECT_TRUE(res->noContent);
EXPECT_FALSE(res->data.get());
}
+
+TEST(OfflineDatabase, CreateRegion) {
+ using namespace mbgl;
+
+ OfflineDatabase db(":memory:");
+ OfflineRegionDefinition definition { "http://example.com/style", LatLngBounds::hull({1, 2}, {3, 4}), 5, 6, 2.0 };
+ OfflineRegionMetadata metadata {{ 1, 2, 3 }};
+ OfflineRegion region = db.createRegion(definition, metadata);
+
+ EXPECT_EQ(definition.styleURL, region.getDefinition().styleURL);
+ EXPECT_EQ(definition.bounds, region.getDefinition().bounds);
+ EXPECT_EQ(definition.minZoom, region.getDefinition().minZoom);
+ EXPECT_EQ(definition.maxZoom, region.getDefinition().maxZoom);
+ EXPECT_EQ(definition.pixelRatio, region.getDefinition().pixelRatio);
+ EXPECT_EQ(metadata, region.getMetadata());
+}
+
+TEST(OfflineDatabase, ListRegions) {
+ using namespace mbgl;
+
+ OfflineDatabase db(":memory:");
+ OfflineRegionDefinition definition { "http://example.com/style", LatLngBounds::hull({1, 2}, {3, 4}), 5, 6, 2.0 };
+ OfflineRegionMetadata metadata {{ 1, 2, 3 }};
+
+ OfflineRegion region = db.createRegion(definition, metadata);
+ std::vector<OfflineRegion> regions = db.listRegions();
+
+ ASSERT_EQ(1, regions.size());
+ EXPECT_EQ(region.getID(), regions.at(0).getID());
+ EXPECT_EQ(definition.styleURL, regions.at(0).getDefinition().styleURL);
+ EXPECT_EQ(definition.bounds, regions.at(0).getDefinition().bounds);
+ EXPECT_EQ(definition.minZoom, regions.at(0).getDefinition().minZoom);
+ EXPECT_EQ(definition.maxZoom, regions.at(0).getDefinition().maxZoom);
+ EXPECT_EQ(definition.pixelRatio, regions.at(0).getDefinition().pixelRatio);
+ EXPECT_EQ(metadata, regions.at(0).getMetadata());
+}
+
+TEST(OfflineDatabase, GetRegionDefinition) {
+ using namespace mbgl;
+
+ OfflineDatabase db(":memory:");
+ OfflineRegionDefinition definition { "http://example.com/style", LatLngBounds::hull({1, 2}, {3, 4}), 5, 6, 2.0 };
+ OfflineRegionMetadata metadata {{ 1, 2, 3 }};
+
+ OfflineRegion region = db.createRegion(definition, metadata);
+ OfflineRegionDefinition result = db.getRegionDefinition(region.getID());
+
+ EXPECT_EQ(definition.styleURL, result.styleURL);
+ EXPECT_EQ(definition.bounds, result.bounds);
+ EXPECT_EQ(definition.minZoom, result.minZoom);
+ EXPECT_EQ(definition.maxZoom, result.maxZoom);
+ EXPECT_EQ(definition.pixelRatio, result.pixelRatio);
+}
+
+TEST(OfflineDatabase, DeleteRegion) {
+ using namespace mbgl;
+
+ OfflineDatabase db(":memory:");
+ OfflineRegionDefinition definition { "http://example.com/style", LatLngBounds::hull({1, 2}, {3, 4}), 5, 6, 2.0 };
+ OfflineRegionMetadata metadata {{ 1, 2, 3 }};
+ db.deleteRegion(db.createRegion(definition, metadata));
+
+ ASSERT_EQ(0, db.listRegions().size());
+}
+
+TEST(OfflineDatabase, CreateRegionInfiniteMaxZoom) {
+ using namespace mbgl;
+
+ OfflineDatabase db(":memory:");
+ OfflineRegionDefinition definition { "", LatLngBounds::world(), 0, INFINITY, 1.0 };
+ OfflineRegionMetadata metadata;
+ OfflineRegion region = db.createRegion(definition, metadata);
+
+ EXPECT_EQ(0, region.getDefinition().minZoom);
+ EXPECT_EQ(INFINITY, region.getDefinition().maxZoom);
+}
diff --git a/test/storage/offline_download.cpp b/test/storage/offline_download.cpp
new file mode 100644
index 0000000000..d89d4a035c
--- /dev/null
+++ b/test/storage/offline_download.cpp
@@ -0,0 +1,302 @@
+#include "../fixtures/stub_file_source.hpp"
+
+#include <mbgl/storage/offline.hpp>
+#include <mbgl/storage/offline_database.hpp>
+#include <mbgl/storage/offline_download.hpp>
+#include <mbgl/util/run_loop.hpp>
+#include <mbgl/util/io.hpp>
+#include <mbgl/util/compression.hpp>
+#include <mbgl/util/string.hpp>
+
+#include <gtest/gtest.h>
+#include <iostream>
+
+using namespace mbgl;
+using namespace std::literals::string_literals;
+
+class MockObserver : public OfflineRegionObserver {
+public:
+ void statusChanged(OfflineRegionStatus status) override {
+ if (statusChangedFn) statusChangedFn(status);
+ }
+
+ void responseError(Response::Error error) override {
+ if (responseErrorFn) responseErrorFn(error);
+ }
+
+ std::function<void (OfflineRegionStatus)> statusChangedFn;
+ std::function<void (Response::Error)> responseErrorFn;
+};
+
+class OfflineTest {
+public:
+ util::RunLoop loop;
+ StubFileSource fileSource;
+ OfflineDatabase db { ":memory:" };
+ std::size_t size = 0;
+
+ Response response(const std::string& path) {
+ Response result;
+ result.data = std::make_shared<std::string>(util::read_file("test/fixtures/"s + path));
+ size_t uncompressed = result.data->size();
+ size_t compressed = util::compress(*result.data).size();
+ size += std::min(uncompressed, compressed);
+ return result;
+ }
+};
+
+TEST(OfflineDownload, NoSubresources) {
+ OfflineTest test;
+ OfflineDownload download(
+ 1,
+ OfflineTilePyramidRegionDefinition("http://127.0.0.1:3000/offline/style.json", LatLngBounds::world(), 0.0, 0.0, 1.0),
+ test.db, test.fileSource);
+
+ test.fileSource.styleResponse = [&] (const Resource& resource) {
+ EXPECT_EQ("http://127.0.0.1:3000/offline/style.json", resource.url);
+ return test.response("offline/empty.style.json");
+ };
+
+ auto observer = std::make_unique<MockObserver>();
+
+ observer->statusChangedFn = [&] (OfflineRegionStatus status) {
+ if (status.complete()) {
+ EXPECT_EQ(1, status.completedResourceCount);
+ EXPECT_EQ(test.size, status.completedResourceSize);
+ EXPECT_FALSE(status.requiredResourceCountIsIndeterminate);
+ test.loop.stop();
+ }
+ };
+
+ download.setObserver(std::move(observer));
+ download.setState(OfflineRegionDownloadState::Active);
+
+ test.loop.run();
+}
+
+TEST(OfflineDownload, InlineSource) {
+ OfflineTest test;
+ OfflineDownload download(
+ 1,
+ OfflineTilePyramidRegionDefinition("http://127.0.0.1:3000/offline/style.json", LatLngBounds::world(), 0.0, 0.0, 1.0),
+ test.db, test.fileSource);
+
+ test.fileSource.styleResponse = [&] (const Resource& resource) {
+ EXPECT_EQ("http://127.0.0.1:3000/offline/style.json", resource.url);
+ return test.response("offline/inline_source.style.json");
+ };
+
+ test.fileSource.tileResponse = [&] (const Resource& resource) {
+ const Resource::TileData& tile = *resource.tileData;
+ EXPECT_EQ("http://127.0.0.1:3000/offline/{z}-{x}-{y}.vector.pbf", tile.urlTemplate);
+ EXPECT_EQ(1, tile.pixelRatio);
+ EXPECT_EQ(0, tile.x);
+ EXPECT_EQ(0, tile.y);
+ EXPECT_EQ(0, tile.z);
+ return test.response("offline/0-0-0.vector.pbf");
+ };
+
+ auto observer = std::make_unique<MockObserver>();
+
+ observer->statusChangedFn = [&] (OfflineRegionStatus status) {
+ if (status.complete()) {
+ EXPECT_EQ(2, status.completedResourceCount);
+ EXPECT_EQ(test.size, status.completedResourceSize);
+ EXPECT_FALSE(status.requiredResourceCountIsIndeterminate);
+ test.loop.stop();
+ }
+ };
+
+ download.setObserver(std::move(observer));
+ download.setState(OfflineRegionDownloadState::Active);
+
+ test.loop.run();
+}
+
+TEST(OfflineDownload, GeoJSONSource) {
+ OfflineTest test;
+ OfflineDownload download(
+ 1,
+ OfflineTilePyramidRegionDefinition("http://127.0.0.1:3000/offline/style.json", LatLngBounds::world(), 0.0, 0.0, 1.0),
+ test.db, test.fileSource);
+
+ test.fileSource.styleResponse = [&] (const Resource& resource) {
+ EXPECT_EQ("http://127.0.0.1:3000/offline/style.json", resource.url);
+ return test.response("offline/geojson_source.style.json");
+ };
+
+ test.fileSource.sourceResponse = [&] (const Resource& resource) {
+ EXPECT_EQ("http://127.0.0.1:3000/offline/geojson.json", resource.url);
+ return test.response("offline/geojson.json");
+ };
+
+ auto observer = std::make_unique<MockObserver>();
+
+ observer->statusChangedFn = [&] (OfflineRegionStatus status) {
+ if (status.complete()) {
+ EXPECT_EQ(2, status.completedResourceCount);
+ EXPECT_EQ(test.size, status.completedResourceSize);
+ EXPECT_FALSE(status.requiredResourceCountIsIndeterminate);
+ test.loop.stop();
+ }
+ };
+
+ download.setObserver(std::move(observer));
+ download.setState(OfflineRegionDownloadState::Active);
+
+ test.loop.run();
+}
+
+TEST(OfflineDownload, Activate) {
+ OfflineTest test;
+ OfflineDownload download(
+ 1,
+ OfflineTilePyramidRegionDefinition("http://127.0.0.1:3000/offline/style.json", LatLngBounds::world(), 0.0, 0.0, 1.0),
+ test.db, test.fileSource);
+
+ test.fileSource.styleResponse = [&] (const Resource& resource) {
+ EXPECT_EQ("http://127.0.0.1:3000/offline/style.json", resource.url);
+ return test.response("offline/style.json");
+ };
+
+ test.fileSource.spriteImageResponse = [&] (const Resource& resource) {
+ EXPECT_EQ("http://127.0.0.1:3000/offline/sprite.png", resource.url);
+ return test.response("offline/sprite.png");
+ };
+
+ test.fileSource.spriteJSONResponse = [&] (const Resource& resource) {
+ EXPECT_EQ("http://127.0.0.1:3000/offline/sprite.json", resource.url);
+ return test.response("offline/sprite.json");
+ };
+
+ test.fileSource.glyphsResponse = [&] (const Resource&) {
+ return test.response("offline/glyph.pbf");
+ };
+
+ test.fileSource.sourceResponse = [&] (const Resource& resource) {
+ EXPECT_EQ("http://127.0.0.1:3000/offline/streets.json", resource.url);
+ return test.response("offline/streets.json");
+ };
+
+ test.fileSource.tileResponse = [&] (const Resource& resource) {
+ const Resource::TileData& tile = *resource.tileData;
+ EXPECT_EQ("http://127.0.0.1:3000/offline/{z}-{x}-{y}.vector.pbf", tile.urlTemplate);
+ EXPECT_EQ(1, tile.pixelRatio);
+ EXPECT_EQ(0, tile.x);
+ EXPECT_EQ(0, tile.y);
+ EXPECT_EQ(0, tile.z);
+ return test.response("offline/0-0-0.vector.pbf");
+ };
+
+ auto observer = std::make_unique<MockObserver>();
+
+ observer->statusChangedFn = [&] (OfflineRegionStatus status) {
+ if (status.complete()) {
+ EXPECT_EQ(261, status.completedResourceCount); // 256 glyphs, 1 tile, 1 style, source, sprite image, and sprite json
+ EXPECT_EQ(test.size, status.completedResourceSize);
+
+ download.setState(OfflineRegionDownloadState::Inactive);
+ OfflineRegionStatus computedStatus = download.getStatus();
+ EXPECT_EQ(status.requiredResourceCount, computedStatus.requiredResourceCount);
+ EXPECT_EQ(status.completedResourceCount, computedStatus.completedResourceCount);
+ EXPECT_EQ(status.completedResourceSize, computedStatus.completedResourceSize);
+ EXPECT_FALSE(status.requiredResourceCountIsIndeterminate);
+
+ test.loop.stop();
+ }
+ };
+
+ download.setObserver(std::move(observer));
+ download.setState(OfflineRegionDownloadState::Active);
+
+ test.loop.run();
+}
+
+TEST(OfflineDownload, GetStatusNoResources) {
+ OfflineTest test;
+ OfflineDownload download(
+ 1,
+ OfflineTilePyramidRegionDefinition("http://127.0.0.1:3000/offline/style.json", LatLngBounds::world(), 0.0, 0.0, 1.0),
+ test.db, test.fileSource);
+ OfflineRegionStatus status = download.getStatus();
+
+ EXPECT_EQ(OfflineRegionDownloadState::Inactive, status.downloadState);
+ EXPECT_EQ(0, status.completedResourceCount);
+ EXPECT_EQ(0, status.completedResourceSize);
+ EXPECT_EQ(1, status.requiredResourceCount);
+ EXPECT_TRUE(status.requiredResourceCountIsIndeterminate);
+ EXPECT_FALSE(status.complete());
+}
+
+TEST(OfflineDownload, GetStatusStyleComplete) {
+ OfflineTest test;
+ OfflineDownload download(
+ 1,
+ OfflineTilePyramidRegionDefinition("http://127.0.0.1:3000/offline/style.json", LatLngBounds::world(), 0.0, 0.0, 1.0),
+ test.db, test.fileSource);
+
+ test.db.putRegionResource(1,
+ Resource::style("http://127.0.0.1:3000/offline/style.json"),
+ test.response("offline/style.json"));
+
+ OfflineRegionStatus status = download.getStatus();
+
+ EXPECT_EQ(OfflineRegionDownloadState::Inactive, status.downloadState);
+ EXPECT_EQ(1, status.completedResourceCount);
+ EXPECT_EQ(test.size, status.completedResourceSize);
+ EXPECT_EQ(260, status.requiredResourceCount);
+ EXPECT_TRUE(status.requiredResourceCountIsIndeterminate);
+ EXPECT_FALSE(status.complete());
+}
+
+TEST(OfflineDownload, GetStatusStyleAndSourceComplete) {
+ OfflineTest test;
+ OfflineDownload download(
+ 1,
+ OfflineTilePyramidRegionDefinition("http://127.0.0.1:3000/offline/style.json", LatLngBounds::world(), 0.0, 0.0, 1.0),
+ test.db, test.fileSource);
+
+ test.db.putRegionResource(1,
+ Resource::style("http://127.0.0.1:3000/offline/style.json"),
+ test.response("offline/style.json"));
+
+ test.db.putRegionResource(1,
+ Resource::source("http://127.0.0.1:3000/offline/streets.json"),
+ test.response("offline/streets.json"));
+
+ OfflineRegionStatus status = download.getStatus();
+
+ EXPECT_EQ(OfflineRegionDownloadState::Inactive, status.downloadState);
+ EXPECT_EQ(2, status.completedResourceCount);
+ EXPECT_EQ(test.size, status.completedResourceSize);
+ EXPECT_EQ(261, status.requiredResourceCount);
+ EXPECT_FALSE(status.requiredResourceCountIsIndeterminate);
+ EXPECT_FALSE(status.complete());
+}
+
+TEST(OfflineDownload, RequestError) {
+ OfflineTest test;
+ OfflineDownload download(
+ 1,
+ OfflineTilePyramidRegionDefinition("http://127.0.0.1:3000/offline/style.json", LatLngBounds::world(), 0.0, 0.0, 1.0),
+ test.db, test.fileSource);
+
+ test.fileSource.styleResponse = [&] (const Resource&) {
+ Response response;
+ response.error = std::make_unique<Response::Error>(Response::Error::Reason::Connection, "connection error");
+ return response;
+ };
+
+ auto observer = std::make_unique<MockObserver>();
+
+ observer->responseErrorFn = [&] (Response::Error error) {
+ EXPECT_EQ(Response::Error::Reason::Connection, error.reason);
+ EXPECT_EQ("connection error", error.message);
+ test.loop.stop();
+ };
+
+ download.setObserver(std::move(observer));
+ download.setState(OfflineRegionDownloadState::Active);
+
+ test.loop.run();
+}
diff --git a/test/test.gypi b/test/test.gypi
index 883b954a35..9b5bceea72 100644
--- a/test/test.gypi
+++ b/test/test.gypi
@@ -70,7 +70,9 @@
'storage/storage.hpp',
'storage/storage.cpp',
'storage/default_file_source.cpp',
+ 'storage/offline.cpp',
'storage/offline_database.cpp',
+ 'storage/offline_download.cpp',
'storage/asset_file_source.cpp',
'storage/headers.cpp',
'storage/http_cancel.cpp',