summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Firebaugh <john.firebaugh@gmail.com>2015-12-22 15:10:24 -0800
committerJohn Firebaugh <john.firebaugh@gmail.com>2015-12-23 12:50:42 -0800
commit16de579d7cfc2960793cbcb5e95741f22ab73768 (patch)
treeb4c3b7651f605e3d3dd61b469f61036bd2c4dcc3
parent7bd4745cf10c504a4899a37016e87bce45e51472 (diff)
downloadqtlocation-mapboxgl-16de579d7cfc2960793cbcb5e95741f22ab73768.tar.gz
[core] Rationalize error handling for resource loading
* Standardize on std::exception_ptr as the error representation (fixes #2854). * Don't format textual strings at the error source; pass on the constituent data via observer method parameters instead. * Use the null object pattern to simplify observer notification code. * Further refactoring for ResourceLoading tests.
-rw-r--r--include/mbgl/util/exception.hpp20
-rw-r--r--platform/node/test/js/map.test.js2
-rw-r--r--src/mbgl/map/map_context.cpp9
-rw-r--r--src/mbgl/map/map_context.hpp8
-rw-r--r--src/mbgl/map/raster_tile_data.cpp10
-rw-r--r--src/mbgl/map/source.cpp60
-rw-r--r--src/mbgl/map/source.hpp19
-rw-r--r--src/mbgl/map/tile_data.cpp6
-rw-r--r--src/mbgl/map/tile_data.hpp4
-rw-r--r--src/mbgl/map/tile_worker.hpp5
-rw-r--r--src/mbgl/map/vector_tile.cpp5
-rw-r--r--src/mbgl/map/vector_tile_data.cpp22
-rw-r--r--src/mbgl/sprite/sprite_parser.cpp6
-rw-r--r--src/mbgl/sprite/sprite_parser.hpp4
-rw-r--r--src/mbgl/sprite/sprite_store.cpp30
-rw-r--r--src/mbgl/sprite/sprite_store.hpp9
-rw-r--r--src/mbgl/style/style.cpp78
-rw-r--r--src/mbgl/style/style.hpp36
-rw-r--r--src/mbgl/text/glyph_pbf.cpp46
-rw-r--r--src/mbgl/text/glyph_pbf.hpp26
-rw-r--r--src/mbgl/text/glyph_store.cpp21
-rw-r--r--src/mbgl/text/glyph_store.hpp22
-rw-r--r--src/mbgl/util/worker.cpp20
-rw-r--r--src/mbgl/util/worker.hpp2
-rw-r--r--test/sprite/sprite_parser.cpp5
-rw-r--r--test/sprite/sprite_store.cpp2
-rw-r--r--test/style/glyph_store.cpp4
-rw-r--r--test/style/resource_loading.cpp390
28 files changed, 402 insertions, 469 deletions
diff --git a/include/mbgl/util/exception.hpp b/include/mbgl/util/exception.hpp
index 06098840a8..b2214b36b8 100644
--- a/include/mbgl/util/exception.hpp
+++ b/include/mbgl/util/exception.hpp
@@ -16,11 +16,6 @@ struct SpriteImageException : Exception {
inline SpriteImageException(const std::string &msg) : Exception(msg) {}
};
-struct GlyphRangeLoadingException : Exception {
- inline GlyphRangeLoadingException(const char *msg) : Exception(msg) {}
- inline GlyphRangeLoadingException(const std::string &msg) : Exception(msg) {}
-};
-
struct MisuseException : Exception {
inline MisuseException(const char *msg) : Exception(msg) {}
inline MisuseException(const std::string &msg) : Exception(msg) {}
@@ -31,21 +26,6 @@ struct ShaderException : Exception {
inline ShaderException(const std::string &msg) : Exception(msg) {}
};
-struct SourceLoadingException : Exception {
- inline SourceLoadingException(const char *msg) : Exception(msg) {}
- inline SourceLoadingException(const std::string &msg) : Exception(msg) {}
-};
-
-struct SpriteLoadingException : Exception {
- inline SpriteLoadingException(const char *msg) : Exception(msg) {}
- inline SpriteLoadingException(const std::string &msg) : Exception(msg) {}
-};
-
-struct TileLoadingException : Exception {
- inline TileLoadingException(const char *msg) : Exception(msg) {}
- inline TileLoadingException(const std::string &msg) : Exception(msg) {}
-};
-
} // namespace util
} // namespace mbgl
diff --git a/platform/node/test/js/map.test.js b/platform/node/test/js/map.test.js
index 9c983828ff..df7b5f8706 100644
--- a/platform/node/test/js/map.test.js
+++ b/platform/node/test/js/map.test.js
@@ -224,8 +224,6 @@ test('Map', function(t) {
map.release();
t.ok(err, 'returns error');
- t.ok(err.message.match(/Failed to load/), 'error text matches');
-
t.end();
});
});
diff --git a/src/mbgl/map/map_context.cpp b/src/mbgl/map/map_context.cpp
index 75ca188fbb..f2c17824c3 100644
--- a/src/mbgl/map/map_context.cpp
+++ b/src/mbgl/map/map_context.cpp
@@ -311,16 +311,13 @@ void MapContext::onLowMemory() {
style->onLowMemory();
asyncInvalidate.send();
}
-
-void MapContext::onTileDataChanged() {
- assert(util::ThreadContext::currentlyOn(util::ThreadType::Map));
+
+void MapContext::onResourceLoaded() {
updateFlags |= Update::Repaint;
asyncUpdate.send();
}
-void MapContext::onResourceLoadingFailed(std::exception_ptr error) {
- assert(util::ThreadContext::currentlyOn(util::ThreadType::Map));
-
+void MapContext::onResourceError(std::exception_ptr error) {
if (data.mode == MapMode::Still && callback) {
callback(error, {});
callback = nullptr;
diff --git a/src/mbgl/map/map_context.hpp b/src/mbgl/map/map_context.hpp
index 24459f270a..012da785c9 100644
--- a/src/mbgl/map/map_context.hpp
+++ b/src/mbgl/map/map_context.hpp
@@ -65,14 +65,12 @@ public:
void onLowMemory();
void cleanup();
-
- // Style::Observer implementation.
- void onTileDataChanged() override;
- void onResourceLoadingFailed(std::exception_ptr error) override;
-
void dumpDebugLogs() const;
private:
+ void onResourceLoaded() override;
+ void onResourceError(std::exception_ptr) override;
+
// Update the state indicated by the accumulated Update flags, then render.
void update();
diff --git a/src/mbgl/map/raster_tile_data.cpp b/src/mbgl/map/raster_tile_data.cpp
index 7ef13301e8..5c85cbd034 100644
--- a/src/mbgl/map/raster_tile_data.cpp
+++ b/src/mbgl/map/raster_tile_data.cpp
@@ -6,8 +6,6 @@
#include <mbgl/util/worker.hpp>
#include <mbgl/util/work_request.hpp>
-#include <sstream>
-
using namespace mbgl;
RasterTileData::RasterTileData(const TileID& id_,
@@ -41,9 +39,7 @@ void RasterTileData::request(float pixelRatio,
if (res.error->reason == Response::Error::Reason::NotFound) {
state = State::parsed;
} else {
- std::stringstream message;
- message << "Failed to load [" << url << "]: " << res.error->message;
- error = message.str();
+ error = std::make_exception_ptr(std::runtime_error(res.error->message));
state = State::obsolete;
}
callback();
@@ -68,9 +64,7 @@ void RasterTileData::request(float pixelRatio,
state = State::parsed;
bucket = std::move(result.get<std::unique_ptr<Bucket>>());
} else {
- std::stringstream message;
- message << "Failed to parse [" << std::string(id) << "]: " << result.get<std::string>();
- error = message.str();
+ error = result.get<std::exception_ptr>();
state = State::obsolete;
}
diff --git a/src/mbgl/map/source.cpp b/src/mbgl/map/source.cpp
index f09d6db85a..a814e484ff 100644
--- a/src/mbgl/map/source.cpp
+++ b/src/mbgl/map/source.cpp
@@ -31,6 +31,7 @@
#include <rapidjson/error/en.h>
#include <algorithm>
+#include <sstream>
namespace mbgl {
@@ -75,9 +76,7 @@ void Source::load() {
req = nullptr;
if (res.error) {
- std::stringstream message;
- message << "Failed to load [" << info.url << "]: " << res.error->message;
- emitSourceLoadingFailed(message.str());
+ observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error(res.error->message)));
return;
}
@@ -86,8 +85,8 @@ void Source::load() {
if (d.HasParseError()) {
std::stringstream message;
- message << "Failed to parse [" << info.url << "]: " << d.GetErrorOffset() << " - " << rapidjson::GetParseError_En(d.GetParseError());
- emitSourceLoadingFailed(message.str());
+ message << d.GetErrorOffset() << " - " << rapidjson::GetParseError_En(d.GetParseError());
+ observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error(message.str())));
return;
}
@@ -98,8 +97,7 @@ void Source::load() {
}
loaded = true;
-
- emitSourceLoaded();
+ observer->onSourceLoaded(*this);
});
}
@@ -166,8 +164,8 @@ bool Source::handlePartialTile(const TileID& id, Worker&) {
return true;
}
- return tileData->parsePending([this]() {
- emitTileLoaded(false);
+ return tileData->parsePending([this, id]() {
+ observer->onTileLoaded(*this, id, false);
});
}
@@ -464,12 +462,12 @@ void Source::onLowMemory() {
cache.clear();
}
-void Source::setObserver(Observer* observer) {
- observer_ = observer;
+void Source::setObserver(Observer* observer_) {
+ observer = observer_;
}
-void Source::tileLoadingCompleteCallback(const TileID& normalized_id, const TransformState& transformState, bool collisionDebug) {
- auto it = tileDataMap.find(normalized_id);
+void Source::tileLoadingCompleteCallback(const TileID& id, const TransformState& transformState, bool collisionDebug) {
+ auto it = tileDataMap.find(id);
if (it == tileDataMap.end()) {
return;
}
@@ -479,43 +477,13 @@ void Source::tileLoadingCompleteCallback(const TileID& normalized_id, const Tran
return;
}
- if (tileData->getState() == TileData::State::obsolete && !tileData->getError().empty()) {
- emitTileLoadingFailed(tileData->getError());
+ if (tileData->getState() == TileData::State::obsolete && tileData->getError()) {
+ observer->onTileError(*this, id, tileData->getError());
return;
}
tileData->redoPlacement({ transformState.getAngle(), transformState.getPitch(), collisionDebug });
- emitTileLoaded(true);
-}
-
-void Source::emitSourceLoaded() {
- if (observer_) {
- observer_->onSourceLoaded();
- }
-}
-
-void Source::emitSourceLoadingFailed(const std::string& message) {
- if (!observer_) {
- return;
- }
-
- auto error = std::make_exception_ptr(util::SourceLoadingException(message));
- observer_->onSourceLoadingFailed(error);
-}
-
-void Source::emitTileLoaded(bool isNewTile) {
- if (observer_) {
- observer_->onTileLoaded(isNewTile);
- }
-}
-
-void Source::emitTileLoadingFailed(const std::string& message) {
- if (!observer_) {
- return;
- }
-
- auto error = std::make_exception_ptr(util::TileLoadingException(message));
- observer_->onTileLoadingFailed(error);
+ observer->onTileLoaded(*this, id, true);
}
void Source::dumpDebugLogs() const {
diff --git a/src/mbgl/map/source.hpp b/src/mbgl/map/source.hpp
index dc939113b2..36613c2052 100644
--- a/src/mbgl/map/source.hpp
+++ b/src/mbgl/map/source.hpp
@@ -25,11 +25,11 @@ public:
public:
virtual ~Observer() = default;
- virtual void onSourceLoaded() = 0;
- virtual void onSourceLoadingFailed(std::exception_ptr error) = 0;
+ virtual void onSourceLoaded(Source&) {};
+ virtual void onSourceError(Source&, std::exception_ptr) {};
- virtual void onTileLoaded(bool isNewTile) = 0;
- virtual void onTileLoadingFailed(std::exception_ptr error) = 0;
+ virtual void onTileLoaded(Source&, const TileID&, bool /* isNewTile */) {};
+ virtual void onTileError(Source&, const TileID&, std::exception_ptr) {};
};
Source();
@@ -64,12 +64,6 @@ public:
private:
void tileLoadingCompleteCallback(const TileID&, const TransformState&, bool collisionDebug);
-
- void emitSourceLoaded();
- void emitSourceLoadingFailed(const std::string& message);
- void emitTileLoaded(bool isNewTile);
- void emitTileLoadingFailed(const std::string& message);
-
bool handlePartialTile(const TileID &id, Worker &worker);
bool findLoadedChildren(const TileID& id, int32_t maxCoveringZoom, std::forward_list<TileID>& retain);
void findLoadedParent(const TileID& id, int32_t minCoveringZoom, std::forward_list<TileID>& retain);
@@ -82,7 +76,6 @@ private:
double getZoom(const TransformState &state) const;
-
// Stores the time when this source was most recently updated.
TimePoint updated = TimePoint::min();
@@ -92,7 +85,9 @@ private:
TileCache cache;
std::unique_ptr<FileRequest> req;
- Observer* observer_ = nullptr;
+
+ Observer nullObserver;
+ Observer* observer = &nullObserver;
};
} // namespace mbgl
diff --git a/src/mbgl/map/tile_data.cpp b/src/mbgl/map/tile_data.cpp
index bba7d7fe22..d9fd3f0047 100644
--- a/src/mbgl/map/tile_data.cpp
+++ b/src/mbgl/map/tile_data.cpp
@@ -1,5 +1,6 @@
#include <mbgl/map/tile_data.hpp>
#include <mbgl/renderer/debug_bucket.hpp>
+#include <mbgl/util/string.hpp>
namespace mbgl {
@@ -26,7 +27,10 @@ const char* TileData::StateToString(const State state) {
void TileData::dumpDebugLogs() const {
Log::Info(Event::General, "TileData::id: %s", std::string(id).c_str());
Log::Info(Event::General, "TileData::state: %s", TileData::StateToString(state));
- Log::Info(Event::General, "TileData::error: %s", error.c_str());
+
+ if (error) {
+ Log::Info(Event::General, "TileData::error: %s", util::toString(error).c_str());
+ }
}
} // namespace mbgl
diff --git a/src/mbgl/map/tile_data.hpp b/src/mbgl/map/tile_data.hpp
index ed3426cf80..2bb11054a6 100644
--- a/src/mbgl/map/tile_data.hpp
+++ b/src/mbgl/map/tile_data.hpp
@@ -88,7 +88,7 @@ public:
return state;
}
- std::string getError() const {
+ std::exception_ptr getError() const {
return error;
}
@@ -103,7 +103,7 @@ public:
protected:
std::atomic<State> state;
- std::string error;
+ std::exception_ptr error;
};
} // namespace mbgl
diff --git a/src/mbgl/map/tile_worker.hpp b/src/mbgl/map/tile_worker.hpp
index 14f996c1b0..d25b6e50f2 100644
--- a/src/mbgl/map/tile_worker.hpp
+++ b/src/mbgl/map/tile_worker.hpp
@@ -33,8 +33,9 @@ public:
std::unordered_map<std::string, std::unique_ptr<Bucket>> buckets;
};
-using TileParseResult = mapbox::util::variant<TileParseResultBuckets, // success
- std::string>; // error
+using TileParseResult = mapbox::util::variant<
+ TileParseResultBuckets, // success
+ std::exception_ptr>; // error
class TileWorker : public util::noncopyable {
public:
diff --git a/src/mbgl/map/vector_tile.cpp b/src/mbgl/map/vector_tile.cpp
index 00307f04a9..39e31da0d4 100644
--- a/src/mbgl/map/vector_tile.cpp
+++ b/src/mbgl/map/vector_tile.cpp
@@ -5,7 +5,6 @@
#include <mbgl/storage/file_source.hpp>
#include <mbgl/util/thread_context.hpp>
-#include <sstream>
#include <utility>
namespace mbgl {
@@ -194,9 +193,7 @@ std::unique_ptr<FileRequest> VectorTileMonitor::monitorTile(const GeometryTileMo
callback(nullptr, nullptr, res.modified, res.expires);
return;
} else {
- std::stringstream message;
- message << "Failed to load [" << url << "]: " << res.error->message;
- callback(std::make_exception_ptr(std::runtime_error(message.str())), nullptr, res.modified, res.expires);
+ callback(std::make_exception_ptr(std::runtime_error(res.error->message)), nullptr, res.modified, res.expires);
return;
}
}
diff --git a/src/mbgl/map/vector_tile_data.cpp b/src/mbgl/map/vector_tile_data.cpp
index 708ded02cc..3fe423b502 100644
--- a/src/mbgl/map/vector_tile_data.cpp
+++ b/src/mbgl/map/vector_tile_data.cpp
@@ -6,8 +6,6 @@
#include <mbgl/style/style.hpp>
#include <mbgl/storage/file_source.hpp>
-#include <sstream>
-
namespace mbgl {
VectorTileData::VectorTileData(const TileID& id_,
@@ -32,14 +30,10 @@ VectorTileData::VectorTileData(const TileID& id_,
Seconds modified_,
Seconds expires_) {
if (err) {
- try {
- std::rethrow_exception(err);
- } catch (const std::exception& e) {
- error = e.what();
- state = State::obsolete;
- callback();
- return;
- }
+ error = err;
+ state = State::obsolete;
+ callback();
+ return;
}
if (!tile) {
@@ -86,9 +80,7 @@ VectorTileData::VectorTileData(const TileID& id_,
redoPlacement();
}
} else {
- std::stringstream message;
- message << "Failed to parse [" << std::string(id) << "]: " << result.get<std::string>();
- error = message.str();
+ error = result.get<std::exception_ptr>();
state = State::obsolete;
}
@@ -130,9 +122,7 @@ bool VectorTileData::parsePending(std::function<void()> callback) {
redoPlacement();
}
} else {
- std::stringstream message;
- message << "Failed to parse [" << std::string(id) << "]: " << result.get<std::string>();
- error = message.str();
+ error = result.get<std::exception_ptr>();
state = State::obsolete;
}
diff --git a/src/mbgl/sprite/sprite_parser.cpp b/src/mbgl/sprite/sprite_parser.cpp
index 59dce93241..1aa95310c5 100644
--- a/src/mbgl/sprite/sprite_parser.cpp
+++ b/src/mbgl/sprite/sprite_parser.cpp
@@ -106,7 +106,7 @@ SpriteParseResult parseSprite(const std::string& image, const std::string& json)
try {
raster = decodeImage(image);
} catch (...) {
- return std::string("Could not parse sprite image");
+ return std::current_exception();
}
JSDocument doc;
@@ -115,9 +115,9 @@ SpriteParseResult parseSprite(const std::string& image, const std::string& json)
if (doc.HasParseError()) {
std::stringstream message;
message << "Failed to parse JSON: " << rapidjson::GetParseError_En(doc.GetParseError()) << " at offset " << doc.GetErrorOffset();
- return message.str();
+ return std::make_exception_ptr(std::runtime_error(message.str()));
} else if (!doc.IsObject()) {
- return std::string("Sprite JSON root must be an object");
+ return std::make_exception_ptr(std::runtime_error("Sprite JSON root must be an object"));
} else {
for (JSValue::ConstMemberIterator itr = doc.MemberBegin(); itr != doc.MemberEnd(); ++itr) {
const std::string name = { itr->name.GetString(), itr->name.GetStringLength() };
diff --git a/src/mbgl/sprite/sprite_parser.hpp b/src/mbgl/sprite/sprite_parser.hpp
index 468ba13c53..36e8947992 100644
--- a/src/mbgl/sprite/sprite_parser.hpp
+++ b/src/mbgl/sprite/sprite_parser.hpp
@@ -30,8 +30,8 @@ using Sprites = std::map<std::string, SpriteImagePtr>;
using SpriteParseResult = mapbox::util::variant<
- Sprites, // success
- std::string>; // error
+ Sprites, // success
+ std::exception_ptr>; // error
// Parses an image and an associated JSON file and returns the sprite objects.
SpriteParseResult parseSprite(const std::string& image, const std::string& json);
diff --git a/src/mbgl/sprite/sprite_store.cpp b/src/mbgl/sprite/sprite_store.cpp
index f54f3f18fd..0c3a7d3ba0 100644
--- a/src/mbgl/sprite/sprite_store.cpp
+++ b/src/mbgl/sprite/sprite_store.cpp
@@ -9,7 +9,6 @@
#include <cassert>
#include <string>
-#include <sstream>
namespace mbgl {
@@ -48,14 +47,11 @@ void SpriteStore::setURL(const std::string& url) {
loader->jsonRequest = nullptr;
if (res.error) {
- std::stringstream message;
- message << "Failed to load [" << jsonURL << "]: " << res.error->message;
- emitSpriteLoadingFailed(message.str());
- return;
+ observer->onSpriteError(std::make_exception_ptr(std::runtime_error(res.error->message)));
} else {
loader->json = res.data;
+ emitSpriteLoadedIfComplete();
}
- emitSpriteLoadedIfComplete();
});
loader->spriteRequest =
@@ -68,14 +64,11 @@ void SpriteStore::setURL(const std::string& url) {
loader->spriteRequest = nullptr;
if (res.error) {
- std::stringstream message;
- message << "Failed to load [" << spriteURL << "]: " << res.error->message;
- emitSpriteLoadingFailed(message.str());
- return;
+ observer->onSpriteError(std::make_exception_ptr(std::runtime_error(res.error->message)));
} else {
loader->image = res.data;
+ emitSpriteLoadedIfComplete();
}
- emitSpriteLoadedIfComplete();
});
}
@@ -91,23 +84,12 @@ void SpriteStore::emitSpriteLoadedIfComplete() {
if (result.is<Sprites>()) {
loaded = true;
setSprites(result.get<Sprites>());
- if (observer) {
- observer->onSpriteLoaded();
- }
+ observer->onSpriteLoaded();
} else {
- emitSpriteLoadingFailed(result.get<std::string>());
+ observer->onSpriteError(result.get<std::exception_ptr>());
}
}
-void SpriteStore::emitSpriteLoadingFailed(const std::string& message) {
- if (!observer) {
- return;
- }
-
- auto error = std::make_exception_ptr(util::SpriteLoadingException(message));
- observer->onSpriteLoadingFailed(error);
-}
-
void SpriteStore::setObserver(Observer* observer_) {
observer = observer_;
}
diff --git a/src/mbgl/sprite/sprite_store.hpp b/src/mbgl/sprite/sprite_store.hpp
index 98b43ac8e4..7c5fe71802 100644
--- a/src/mbgl/sprite/sprite_store.hpp
+++ b/src/mbgl/sprite/sprite_store.hpp
@@ -18,8 +18,8 @@ public:
public:
virtual ~Observer() = default;
- virtual void onSpriteLoaded() = 0;
- virtual void onSpriteLoadingFailed(std::exception_ptr) = 0;
+ virtual void onSpriteLoaded() {};
+ virtual void onSpriteError(std::exception_ptr) {};
};
SpriteStore(float pixelRatio);
@@ -54,16 +54,15 @@ public:
private:
void _setSprite(const std::string&, const std::shared_ptr<const SpriteImage>& = nullptr);
-
void emitSpriteLoadedIfComplete();
- void emitSpriteLoadingFailed(const std::string& message);
struct Loader;
std::unique_ptr<Loader> loader;
bool loaded = false;
- Observer* observer = nullptr;
+ Observer nullObserver;
+ Observer* observer = &nullObserver;
// Lock for sprites and dirty maps.
std::mutex mutex;
diff --git a/src/mbgl/style/style.cpp b/src/mbgl/style/style.cpp
index 77a544714e..b1c867a692 100644
--- a/src/mbgl/style/style.cpp
+++ b/src/mbgl/style/style.cpp
@@ -17,6 +17,7 @@
#include <mbgl/geometry/glyph_atlas.hpp>
#include <mbgl/geometry/line_atlas.hpp>
#include <mbgl/util/constants.hpp>
+#include <mbgl/util/string.hpp>
#include <mbgl/platform/log.hpp>
#include <mbgl/layer/background_layer.hpp>
@@ -304,73 +305,64 @@ void Style::onLowMemory() {
void Style::setObserver(Observer* observer_) {
assert(util::ThreadContext::currentlyOn(util::ThreadType::Map));
- assert(!observer);
-
observer = observer_;
}
-void Style::onGlyphRangeLoaded() {
+void Style::onGlyphsLoaded(const std::string& fontStack, const GlyphRange& glyphRange) {
shouldReparsePartialTiles = true;
-
- emitTileDataChanged();
+ observer->onGlyphsLoaded(fontStack, glyphRange);
+ observer->onResourceLoaded();
}
-void Style::onGlyphRangeLoadingFailed(std::exception_ptr error) {
- emitResourceLoadingFailed(error);
+void Style::onGlyphsError(const std::string& fontStack, const GlyphRange& glyphRange, std::exception_ptr error) {
+ lastError = error;
+ Log::Error(Event::Style, "Failed to load glyph range %d-%d for font stack %s: %s",
+ glyphRange.first, glyphRange.second, fontStack.c_str(), util::toString(error).c_str());
+ observer->onGlyphsError(fontStack, glyphRange, error);
+ observer->onResourceError(error);
}
-void Style::onSourceLoaded() {
- emitTileDataChanged();
+void Style::onSourceLoaded(Source& source) {
+ observer->onSourceLoaded(source);
+ observer->onResourceLoaded();
}
-void Style::onSourceLoadingFailed(std::exception_ptr error) {
- emitResourceLoadingFailed(error);
+void Style::onSourceError(Source& source, std::exception_ptr error) {
+ lastError = error;
+ Log::Error(Event::Style, "Failed to load source %s: %s",
+ source.info.source_id.c_str(), util::toString(error).c_str());
+ observer->onSourceError(source, error);
+ observer->onResourceError(error);
}
-void Style::onTileLoaded(bool isNewTile) {
+void Style::onTileLoaded(Source& source, const TileID& tileID, bool isNewTile) {
if (isNewTile) {
shouldReparsePartialTiles = true;
}
- emitTileDataChanged();
+ observer->onTileLoaded(source, tileID, isNewTile);
+ observer->onResourceLoaded();
}
-void Style::onTileLoadingFailed(std::exception_ptr error) {
- emitResourceLoadingFailed(error);
+void Style::onTileError(Source& source, const TileID& tileID, std::exception_ptr error) {
+ lastError = error;
+ Log::Error(Event::Style, "Failed to load tile %s for source %s: %s",
+ std::string(tileID).c_str(), source.info.source_id.c_str(), util::toString(error).c_str());
+ observer->onTileError(source, tileID, error);
+ observer->onResourceError(error);
}
void Style::onSpriteLoaded() {
shouldReparsePartialTiles = true;
- emitTileDataChanged();
-}
-
-void Style::onSpriteLoadingFailed(std::exception_ptr error) {
- emitResourceLoadingFailed(error);
+ observer->onSpriteLoaded();
+ observer->onResourceLoaded();
}
-void Style::emitTileDataChanged() {
- assert(util::ThreadContext::currentlyOn(util::ThreadType::Map));
-
- if (observer) {
- observer->onTileDataChanged();
- }
-}
-
-void Style::emitResourceLoadingFailed(std::exception_ptr error) {
- assert(util::ThreadContext::currentlyOn(util::ThreadType::Map));
-
- try {
- if (error) {
- lastError = error;
- std::rethrow_exception(error);
- }
- } catch(const std::exception& e) {
- Log::Error(Event::Style, "%s", e.what());
- }
-
- if (observer) {
- observer->onResourceLoadingFailed(error);
- }
+void Style::onSpriteError(std::exception_ptr error) {
+ lastError = error;
+ Log::Error(Event::Style, "Failed to load sprite: %s", util::toString(error).c_str());
+ observer->onSpriteError(error);
+ observer->onResourceError(error);
}
void Style::dumpDebugLogs() const {
diff --git a/src/mbgl/style/style.hpp b/src/mbgl/style/style.hpp
index ed5df04256..ba5da7435a 100644
--- a/src/mbgl/style/style.hpp
+++ b/src/mbgl/style/style.hpp
@@ -58,12 +58,18 @@ public:
Style(MapData&);
~Style();
- class Observer {
+ class Observer : public GlyphStore::Observer,
+ public SpriteStore::Observer,
+ public Source::Observer {
public:
- virtual ~Observer() = default;
-
- virtual void onTileDataChanged() = 0;
- virtual void onResourceLoadingFailed(std::exception_ptr error) = 0;
+ /**
+ * In addition to the individual glyph, sprite, and source events, the
+ * following "rollup" events are provided for convenience. They are
+ * strictly additive; e.g. when a source is loaded, both `onSourceLoaded`
+ * and `onResourceLoaded` will be called.
+ */
+ virtual void onResourceLoaded() {};
+ virtual void onResourceError(std::exception_ptr) {};
};
void setJSON(const std::string& data, const std::string& base);
@@ -115,25 +121,23 @@ private:
std::vector<std::unique_ptr<StyleLayer>>::const_iterator findLayer(const std::string& layerID) const;
// GlyphStore::Observer implementation.
- void onGlyphRangeLoaded() override;
- void onGlyphRangeLoadingFailed(std::exception_ptr error) override;
+ void onGlyphsLoaded(const std::string& fontStack, const GlyphRange&) override;
+ void onGlyphsError(const std::string& fontStack, const GlyphRange&, std::exception_ptr) override;
// SpriteStore::Observer implementation.
void onSpriteLoaded() override;
- void onSpriteLoadingFailed(std::exception_ptr error) override;
+ void onSpriteError(std::exception_ptr) override;
// Source::Observer implementation.
- void onSourceLoaded() override;
- void onSourceLoadingFailed(std::exception_ptr error) override;
- void onTileLoaded(bool isNewTile) override;
- void onTileLoadingFailed(std::exception_ptr error) override;
-
- void emitTileDataChanged();
- void emitResourceLoadingFailed(std::exception_ptr error);
+ void onSourceLoaded(Source&) override;
+ void onSourceError(Source&, std::exception_ptr) override;
+ void onTileLoaded(Source&, const TileID&, bool isNewTile) override;
+ void onTileError(Source&, const TileID&, std::exception_ptr) override;
bool shouldReparsePartialTiles = false;
- Observer* observer = nullptr;
+ Observer nullObserver;
+ Observer* observer = &nullObserver;
std::exception_ptr lastError;
diff --git a/src/mbgl/text/glyph_pbf.cpp b/src/mbgl/text/glyph_pbf.cpp
index c14f52de7a..0c0626c0de 100644
--- a/src/mbgl/text/glyph_pbf.cpp
+++ b/src/mbgl/text/glyph_pbf.cpp
@@ -12,8 +12,6 @@
#include <mbgl/util/token.hpp>
#include <mbgl/util/url.hpp>
-#include <sstream>
-
namespace {
void parseGlyphPBF(mbgl::FontStack& stack, const std::string& data) {
@@ -65,8 +63,10 @@ namespace mbgl {
GlyphPBF::GlyphPBF(GlyphStore* store,
const std::string& fontStack,
- const GlyphRange& glyphRange)
- : parsed(false) {
+ const GlyphRange& glyphRange,
+ GlyphStore::Observer* observer_)
+ : parsed(false),
+ observer(observer_) {
// Load the glyph set URL
std::string url = util::replaceTokens(store->getURL(), [&](const std::string &name) -> std::string {
if (name == "fontstack") return util::percentEncode(fontStack);
@@ -74,7 +74,7 @@ GlyphPBF::GlyphPBF(GlyphStore* store,
return "";
});
- auto requestCallback = [this, store, fontStack, url](Response res) {
+ auto requestCallback = [this, store, fontStack, glyphRange](Response res) {
if (res.stale) {
// Only handle fresh responses.
return;
@@ -82,12 +82,10 @@ GlyphPBF::GlyphPBF(GlyphStore* store,
req = nullptr;
if (res.error) {
- std::stringstream message;
- message << "Failed to load [" << url << "]: " << res.error->message;
- emitGlyphPBFLoadingFailed(message.str());
+ observer->onGlyphsError(fontStack, glyphRange, std::make_exception_ptr(std::runtime_error(res.error->message)));
} else {
data = res.data;
- parse(store, fontStack, url);
+ parse(store, fontStack, glyphRange);
}
};
@@ -97,7 +95,7 @@ GlyphPBF::GlyphPBF(GlyphStore* store,
GlyphPBF::~GlyphPBF() = default;
-void GlyphPBF::parse(GlyphStore* store, const std::string& fontStack, const std::string& url) {
+void GlyphPBF::parse(GlyphStore* store, const std::string& fontStack, const GlyphRange& glyphRange) {
assert(data);
if (data->empty()) {
// If there is no data, this means we either haven't
@@ -107,35 +105,13 @@ void GlyphPBF::parse(GlyphStore* store, const std::string& fontStack, const std:
try {
parseGlyphPBF(**store->getFontStack(fontStack), *data);
- } catch (const std::exception& ex) {
- std::stringstream message;
- message << "Failed to parse [" << url << "]: " << ex.what();
- emitGlyphPBFLoadingFailed(message.str());
+ } catch (...) {
+ observer->onGlyphsError(fontStack, glyphRange, std::current_exception());
return;
}
parsed = true;
-
- emitGlyphPBFLoaded();
-}
-
-void GlyphPBF::setObserver(Observer* observer_) {
- observer = observer_;
-}
-
-void GlyphPBF::emitGlyphPBFLoaded() {
- if (observer) {
- observer->onGlyphPBFLoaded();
- }
-}
-
-void GlyphPBF::emitGlyphPBFLoadingFailed(const std::string& message) {
- if (!observer) {
- return;
- }
-
- auto error = std::make_exception_ptr(util::GlyphRangeLoadingException(message));
- observer->onGlyphPBFLoadingFailed(error);
+ observer->onGlyphsLoaded(fontStack, glyphRange);
}
} // namespace mbgl
diff --git a/src/mbgl/text/glyph_pbf.hpp b/src/mbgl/text/glyph_pbf.hpp
index d595298516..e0f65e7789 100644
--- a/src/mbgl/text/glyph_pbf.hpp
+++ b/src/mbgl/text/glyph_pbf.hpp
@@ -2,6 +2,7 @@
#define MBGL_TEXT_GLYPH_PBF
#include <mbgl/text/glyph.hpp>
+#include <mbgl/text/glyph_store.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <atomic>
@@ -11,43 +12,30 @@
namespace mbgl {
-class GlyphStore;
class FontStack;
class FileRequest;
class GlyphPBF : private util::noncopyable {
public:
- class Observer {
- public:
- virtual ~Observer() = default;
-
- virtual void onGlyphPBFLoaded() = 0;
- virtual void onGlyphPBFLoadingFailed(std::exception_ptr error) = 0;
- };
-
GlyphPBF(GlyphStore* store,
const std::string& fontStack,
- const GlyphRange& glyphRange);
- virtual ~GlyphPBF();
+ const GlyphRange&,
+ GlyphStore::Observer*);
+ ~GlyphPBF();
bool isParsed() const {
return parsed;
- };
-
- void setObserver(Observer* observer);
+ }
private:
- void emitGlyphPBFLoaded();
- void emitGlyphPBFLoadingFailed(const std::string& message);
-
- void parse(GlyphStore* store, const std::string& fontStack, const std::string& url);
+ void parse(GlyphStore*, const std::string& fontStack, const GlyphRange&);
std::shared_ptr<const std::string> data;
std::atomic<bool> parsed;
std::unique_ptr<FileRequest> req;
- Observer* observer = nullptr;
+ GlyphStore::Observer* observer = nullptr;
};
} // namespace mbgl
diff --git a/src/mbgl/text/glyph_store.cpp b/src/mbgl/text/glyph_store.cpp
index 047db17b7d..a3d1530e3d 100644
--- a/src/mbgl/text/glyph_store.cpp
+++ b/src/mbgl/text/glyph_store.cpp
@@ -7,6 +7,9 @@
namespace mbgl {
+GlyphStore::GlyphStore() = default;
+GlyphStore::~GlyphStore() = default;
+
void GlyphStore::requestGlyphRange(const std::string& fontStackName, const GlyphRange& range) {
assert(util::ThreadContext::currentlyOn(util::ThreadType::Map));
@@ -18,10 +21,8 @@ void GlyphStore::requestGlyphRange(const std::string& fontStackName, const Glyph
return;
}
- auto glyphPBF = std::make_unique<GlyphPBF>(this, fontStackName, range);
- glyphPBF->setObserver(this);
-
- rangeSets.emplace(range, std::move(glyphPBF));
+ rangeSets.emplace(range,
+ std::make_unique<GlyphPBF>(this, fontStackName, range, observer));
}
@@ -66,18 +67,6 @@ util::exclusive<FontStack> GlyphStore::getFontStack(const std::string& fontStack
return { it->second.get(), std::move(lock) };
}
-void GlyphStore::onGlyphPBFLoaded() {
- if (observer) {
- observer->onGlyphRangeLoaded();
- }
-}
-
-void GlyphStore::onGlyphPBFLoadingFailed(std::exception_ptr error) {
- if (observer) {
- observer->onGlyphRangeLoadingFailed(error);
- }
-}
-
void GlyphStore::setObserver(Observer* observer_) {
observer = observer_;
}
diff --git a/src/mbgl/text/glyph_store.hpp b/src/mbgl/text/glyph_store.hpp
index 4e665087ea..2236bce38c 100644
--- a/src/mbgl/text/glyph_store.hpp
+++ b/src/mbgl/text/glyph_store.hpp
@@ -3,7 +3,6 @@
#include <mbgl/text/font_stack.hpp>
#include <mbgl/text/glyph.hpp>
-#include <mbgl/text/glyph_pbf.hpp>
#include <mbgl/util/exclusive.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/work_queue.hpp>
@@ -15,21 +14,23 @@
namespace mbgl {
+class GlyphPBF;
+
// The GlyphStore manages the loading and storage of Glyphs
// and creation of FontStack objects. The GlyphStore lives
// on the MapThread but can be queried from any thread.
-class GlyphStore : public GlyphPBF::Observer, private util::noncopyable {
+class GlyphStore : private util::noncopyable {
public:
class Observer {
public:
virtual ~Observer() = default;
- virtual void onGlyphRangeLoaded() = 0;
- virtual void onGlyphRangeLoadingFailed(std::exception_ptr error) = 0;
+ virtual void onGlyphsLoaded(const std::string& /* fontStack */, const GlyphRange&) {};
+ virtual void onGlyphsError(const std::string& /* fontStack */, const GlyphRange&, std::exception_ptr) {};
};
- GlyphStore() = default;
- virtual ~GlyphStore() = default;
+ GlyphStore();
+ ~GlyphStore();
util::exclusive<FontStack> getFontStack(const std::string& fontStack);
@@ -38,7 +39,7 @@ public:
// made and when the glyph if finally parsed, it gets added to the respective
// FontStack and a signal is emitted to notify the observers. This method
// can be called from any thread.
- bool hasGlyphRanges(const std::string& fontStackName, const std::set<GlyphRange>& glyphRanges);
+ bool hasGlyphRanges(const std::string& fontStack, const std::set<GlyphRange>& glyphRanges);
void setURL(const std::string &url) {
glyphURL = url;
@@ -48,10 +49,6 @@ public:
return glyphURL;
}
- // GlyphPBF::Observer implementation.
- void onGlyphPBFLoaded() override;
- void onGlyphPBFLoadingFailed(std::exception_ptr error) override;
-
void setObserver(Observer* observer);
private:
@@ -67,7 +64,8 @@ private:
util::WorkQueue workQueue;
- Observer* observer = nullptr;
+ Observer nullObserver;
+ Observer* observer = &nullObserver;
};
} // namespace mbgl
diff --git a/src/mbgl/util/worker.cpp b/src/mbgl/util/worker.cpp
index d23831b440..49d1c2bc5b 100644
--- a/src/mbgl/util/worker.cpp
+++ b/src/mbgl/util/worker.cpp
@@ -18,18 +18,12 @@ public:
void parseRasterTile(std::unique_ptr<RasterBucket> bucket,
const std::shared_ptr<const std::string> data,
std::function<void(RasterTileParseResult)> callback) {
- PremultipliedImage image;
-
try {
- image = decodeImage(*data);
+ bucket->setImage(decodeImage(*data));
+ callback(RasterTileParseResult(std::move(bucket)));
} catch (...) {
- callback(RasterTileParseResult("error parsing raster image"));
- return;
+ callback(std::current_exception());
}
-
- bucket->setImage(std::move(image));
-
- callback(RasterTileParseResult(std::move(bucket)));
}
void parseGeometryTile(TileWorker* worker,
@@ -39,8 +33,8 @@ public:
std::function<void(TileParseResult)> callback) {
try {
callback(worker->parseAllLayers(std::move(layers), *tile, config));
- } catch (const std::exception& ex) {
- callback(TileParseResult(ex.what()));
+ } catch (...) {
+ callback(std::current_exception());
}
}
@@ -48,8 +42,8 @@ public:
std::function<void(TileParseResult)> callback) {
try {
callback(worker->parsePendingLayers());
- } catch (const std::exception& ex) {
- callback(TileParseResult(ex.what()));
+ } catch (...) {
+ callback(std::current_exception());
}
}
diff --git a/src/mbgl/util/worker.hpp b/src/mbgl/util/worker.hpp
index 5ba477491d..d4660d9e7c 100644
--- a/src/mbgl/util/worker.hpp
+++ b/src/mbgl/util/worker.hpp
@@ -16,7 +16,7 @@ class GeometryTileLoader;
using RasterTileParseResult = mapbox::util::variant<
std::unique_ptr<Bucket>, // success
- std::string>; // error
+ std::exception_ptr>; // error
class Worker : public mbgl::util::noncopyable {
public:
diff --git a/test/sprite/sprite_parser.cpp b/test/sprite/sprite_parser.cpp
index dfa3a1055b..0eb298b23e 100644
--- a/test/sprite/sprite_parser.cpp
+++ b/test/sprite/sprite_parser.cpp
@@ -5,6 +5,7 @@
#include <mbgl/sprite/sprite_image.hpp>
#include <mbgl/util/image.hpp>
#include <mbgl/util/io.hpp>
+#include <mbgl/util/string.hpp>
#include <algorithm>
@@ -211,9 +212,9 @@ TEST(Sprite, SpriteParsingInvalidJSON) {
const auto image_1x = util::read_file("test/fixtures/annotations/emerald.png");
const auto json_1x = R"JSON({ "image": " })JSON";
- const auto error = parseSprite(image_1x, json_1x).get<std::string>();
+ const auto error = parseSprite(image_1x, json_1x).get<std::exception_ptr>();
- EXPECT_EQ(error,
+ EXPECT_EQ(util::toString(error),
std::string("Failed to parse JSON: Missing a closing quotation mark in string. at offset 13"));
}
diff --git a/test/sprite/sprite_store.cpp b/test/sprite/sprite_store.cpp
index 574af51b81..6209bde2a3 100644
--- a/test/sprite/sprite_store.cpp
+++ b/test/sprite/sprite_store.cpp
@@ -176,7 +176,7 @@ public:
callback_(spriteStore_.get(), nullptr);
}
- void onSpriteLoadingFailed(std::exception_ptr error) override {
+ void onSpriteError(std::exception_ptr error) override {
callback_(spriteStore_.get(), error);
}
diff --git a/test/style/glyph_store.cpp b/test/style/glyph_store.cpp
index 4524716212..8e7518c33f 100644
--- a/test/style/glyph_store.cpp
+++ b/test/style/glyph_store.cpp
@@ -39,11 +39,11 @@ public:
glyphStore_.reset();
}
- void onGlyphRangeLoaded() override {
+ void onGlyphsLoaded(const std::string&, const GlyphRange&) override {
callback_(glyphStore_.get(), nullptr);
}
- void onGlyphRangeLoadingFailed(std::exception_ptr error) override {
+ void onGlyphsError(const std::string&, const GlyphRange&, std::exception_ptr error) override {
callback_(glyphStore_.get(), error);
}
diff --git a/test/style/resource_loading.cpp b/test/style/resource_loading.cpp
index 9e0185286e..0476c53aa1 100644
--- a/test/style/resource_loading.cpp
+++ b/test/style/resource_loading.cpp
@@ -17,234 +17,322 @@ using namespace mbgl;
class StyleObserver : public Style::Observer {
public:
- void onTileDataChanged() override {
- if (tileDataChanged) tileDataChanged();
+ void onGlyphsLoaded(const std::string& fontStack, const GlyphRange& glyphRange) override {
+ if (glyphsLoaded) glyphsLoaded(fontStack, glyphRange);
}
- void onResourceLoadingFailed(std::exception_ptr error) override {
- if (resourceLoadingFailed) resourceLoadingFailed(error);
+ void onGlyphsError(const std::string& fontStack, const GlyphRange& glyphRange, std::exception_ptr error) override {
+ if (glyphsError) glyphsError(fontStack, glyphRange, error);
}
- std::function<void ()> tileDataChanged = nullptr;
- std::function<void (std::exception_ptr)> resourceLoadingFailed = nullptr;
-};
+ void onSpriteLoaded() override {
+ if (spriteLoaded) spriteLoaded();
+ }
-struct StyleTestData {
- MockFileSource::Type type;
- std::string style;
- std::string resource;
- std::string expectedError;
- std::string styleClass;
- std::function<void (const Style&)> callback = nullptr;
-};
+ void onSpriteError(std::exception_ptr error) override {
+ if (spriteError) spriteError(error);
+ }
-void runTest(StyleTestData& testData) {
- Log::setObserver(std::make_unique<Log::NullObserver>()); // Squelch logging.
+ void onSourceLoaded(Source& source) override {
+ if (sourceLoaded) sourceLoaded(source);
+ }
- util::RunLoop loop;
+ void onSourceError(Source& source, std::exception_ptr error) override {
+ if (sourceError) sourceError(source, error);
+ }
- util::ThreadContext context("Map", util::ThreadType::Map, util::ThreadPriority::Regular);
- util::ThreadContext::Set(&context);
+ void onTileLoaded(Source& source, const TileID& tileID, bool isNewTile) override {
+ if (tileLoaded) tileLoaded(source, tileID, isNewTile);
+ }
- MockFileSource fileSource(testData.type, testData.resource);
- util::ThreadContext::setFileSource(&fileSource);
+ void onTileError(Source& source, const TileID& tileID, std::exception_ptr error) override {
+ if (tileError) tileError(source, tileID, error);
+ }
- MapData data(MapMode::Still, GLContextMode::Unique, 1.0);
- MockView view;
- Transform transform(view, ConstrainMode::HeightOnly);
- TexturePool texturePool;
- Style style(data);
+ void onResourceLoaded() override {
+ if (resourceLoaded) resourceLoaded();
+ };
+
+ void onResourceError(std::exception_ptr error) override {
+ if (resourceError) resourceError(error);
+ };
+
+ std::function<void (const std::string& fontStack, const GlyphRange&)> glyphsLoaded;
+ std::function<void (const std::string& fontStack, const GlyphRange&, std::exception_ptr)> glyphsError;
+ std::function<void ()> spriteLoaded;
+ std::function<void (std::exception_ptr)> spriteError;
+ std::function<void (Source&)> sourceLoaded;
+ std::function<void (Source&, std::exception_ptr)> sourceError;
+ std::function<void (Source&, const TileID&, bool isNewTile)> tileLoaded;
+ std::function<void (Source&, const TileID&, std::exception_ptr)> tileError;
+ std::function<void ()> resourceLoaded;
+ std::function<void (std::exception_ptr)> resourceError;
+
+ std::function<void ()> fullyLoaded;
+};
+class ResourceLoadingTest {
+public:
+ ResourceLoadingTest(MockFileSource::Type type, const std::string& resource)
+ : fileSource(type, resource) {}
+
+ util::ThreadContext context { "Map", util::ThreadType::Map, util::ThreadPriority::Regular };
+
+ MockFileSource fileSource;
StyleObserver observer;
+ util::RunLoop loop;
- observer.tileDataChanged = [&] () {
- // Prompt tile loading after sources load.
- style.update(transform.getState(), texturePool);
+ MapData data { MapMode::Still, GLContextMode::Unique, 1.0 };
+ MockView view;
+ Transform transform { view, ConstrainMode::HeightOnly };
+ TexturePool texturePool;
+ Style style { data };
- if (style.isLoaded()) {
- loop.stop();
- if (testData.callback) testData.callback(style);
- }
- };
+ void run(const std::string& stylePath) {
+ // Squelch logging.
+ Log::setObserver(std::make_unique<Log::NullObserver>());
- observer.resourceLoadingFailed = [&] (std::exception_ptr error) {
- loop.stop();
- testData.expectedError = util::toString(error);
- };
+ util::ThreadContext::Set(&context);
+ util::ThreadContext::setFileSource(&fileSource);
- transform.resize({{ 512, 512 }});
- transform.setLatLngZoom({0, 0}, 0);
+ observer.resourceLoaded = [&] () {
+ style.update(transform.getState(), texturePool);
+ if (style.isLoaded() && observer.fullyLoaded) {
+ observer.fullyLoaded();
+ }
+ };
- style.setObserver(&observer);
- style.setJSON(util::read_file(testData.style), "");
+ transform.resize({{ 512, 512 }});
+ transform.setLatLngZoom({0, 0}, 0);
- if (!testData.styleClass.empty()) data.addClass(testData.styleClass);
+ style.setObserver(&observer);
+ style.setJSON(util::read_file(stylePath), "");
+ style.cascade();
+ style.recalculate(0);
- style.cascade();
- style.recalculate(16);
+ loop.run();
+ }
- loop.run();
-}
+ void end() {
+ loop.stop();
+ }
+};
TEST(ResourceLoading, Success) {
- StyleTestData testData;
- testData.type = MockFileSource::Success;
- testData.style = "test/fixtures/resources/style.json";
- runTest(testData);
- EXPECT_EQ(testData.expectedError, "");
+ ResourceLoadingTest test(MockFileSource::Success, "");
+
+ test.observer.resourceError = [&] (std::exception_ptr error) {
+ FAIL() << util::toString(error);
+ };
+
+ test.observer.fullyLoaded = [&] () {
+ SUCCEED();
+ test.end();
+ };
+
+ test.run("test/fixtures/resources/style.json");
}
TEST(ResourceLoading, RasterSourceFail) {
- StyleTestData testData;
- testData.type = MockFileSource::RequestFail;
- testData.style = "test/fixtures/resources/style.json";
- testData.resource = "source_raster.json";
- runTest(testData);
- EXPECT_EQ(testData.expectedError, "Failed to load [test/fixtures/resources/source_raster.json]: Failed by the test case");
+ ResourceLoadingTest test(MockFileSource::RequestFail, "source_raster.json");
+
+ test.observer.sourceError = [&] (Source& source, std::exception_ptr error) {
+ EXPECT_EQ(source.info.source_id, "rastersource");
+ EXPECT_EQ(util::toString(error), "Failed by the test case");
+ test.end();
+ };
+
+ test.run("test/fixtures/resources/style.json");
}
TEST(ResourceLoading, VectorSourceFail) {
- StyleTestData testData;
- testData.type = MockFileSource::RequestFail;
- testData.style = "test/fixtures/resources/style.json";
- testData.resource = "source_vector.json";
- runTest(testData);
- EXPECT_EQ(testData.expectedError, "Failed to load [test/fixtures/resources/source_vector.json]: Failed by the test case");
+ ResourceLoadingTest test(MockFileSource::RequestFail, "source_vector.json");
+
+ test.observer.sourceError = [&] (Source& source, std::exception_ptr error) {
+ EXPECT_EQ(source.info.source_id, "vectorsource");
+ EXPECT_EQ(util::toString(error), "Failed by the test case");
+ test.end();
+ };
+
+ test.run("test/fixtures/resources/style.json");
}
TEST(ResourceLoading, SpriteJSONFail) {
- StyleTestData testData;
- testData.type = MockFileSource::RequestFail;
- testData.style = "test/fixtures/resources/style.json";
- testData.resource = "sprite.json";
- runTest(testData);
- EXPECT_EQ(testData.expectedError, "Failed to load [test/fixtures/resources/sprite.json]: Failed by the test case");
+ ResourceLoadingTest test(MockFileSource::RequestFail, "sprite.json");
+
+ test.observer.spriteError = [&] (std::exception_ptr error) {
+ EXPECT_EQ(util::toString(error), "Failed by the test case");
+ test.end();
+ };
+
+ test.run("test/fixtures/resources/style.json");
}
TEST(ResourceLoading, SpriteImageFail) {
- StyleTestData testData;
- testData.type = MockFileSource::RequestFail;
- testData.style = "test/fixtures/resources/style.json";
- testData.resource = "sprite.png";
- runTest(testData);
- EXPECT_EQ(testData.expectedError, "Failed to load [test/fixtures/resources/sprite.png]: Failed by the test case");
+ ResourceLoadingTest test(MockFileSource::RequestFail, "sprite.png");
+
+ test.observer.spriteError = [&] (std::exception_ptr error) {
+ EXPECT_EQ(util::toString(error), "Failed by the test case");
+ test.end();
+ };
+
+ test.run("test/fixtures/resources/style.json");
}
TEST(ResourceLoading, RasterTileFail) {
- StyleTestData testData;
- testData.type = MockFileSource::RequestFail;
- testData.style = "test/fixtures/resources/style.json";
- testData.resource = "raster.png";
- runTest(testData);
- EXPECT_EQ(testData.expectedError, "Failed to load [test/fixtures/resources/raster.png]: Failed by the test case");
+ ResourceLoadingTest test(MockFileSource::RequestFail, "raster.png");
+
+ test.observer.tileError = [&] (Source& source, const TileID& tileID, std::exception_ptr error) {
+ EXPECT_EQ(source.info.source_id, "rastersource");
+ EXPECT_EQ(std::string(tileID), "0/0/0");
+ EXPECT_EQ(util::toString(error), "Failed by the test case");
+ test.end();
+ };
+
+ test.run("test/fixtures/resources/style.json");
}
TEST(ResourceLoading, VectorTileFail) {
- StyleTestData testData;
- testData.type = MockFileSource::RequestFail;
- testData.style = "test/fixtures/resources/style.json";
- testData.resource = "vector.pbf";
- runTest(testData);
- EXPECT_EQ(testData.expectedError, "Failed to load [test/fixtures/resources/vector.pbf]: Failed by the test case");
+ ResourceLoadingTest test(MockFileSource::RequestFail, "vector.pbf");
+
+ test.observer.tileError = [&] (Source& source, const TileID& tileID, std::exception_ptr error) {
+ EXPECT_EQ(source.info.source_id, "vectorsource");
+ EXPECT_EQ(std::string(tileID), "0/0/0");
+ EXPECT_EQ(util::toString(error), "Failed by the test case");
+ test.end();
+ };
+
+ test.run("test/fixtures/resources/style.json");
}
TEST(ResourceLoading, GlyphsFail) {
- StyleTestData testData;
- testData.type = MockFileSource::RequestFail;
- testData.style = "test/fixtures/resources/style.json";
- testData.resource = "glyphs.pbf";
- runTest(testData);
- EXPECT_EQ(testData.expectedError, "Failed to load [test/fixtures/resources/glyphs.pbf]: Failed by the test case");
+ ResourceLoadingTest test(MockFileSource::RequestFail, "glyphs.pbf");
+
+ test.observer.glyphsError = [&] (const std::string& fontStack, const GlyphRange&, std::exception_ptr error) {
+ EXPECT_EQ(fontStack, "Open Sans Regular,Arial Unicode MS Regular");
+ EXPECT_EQ(util::toString(error), "Failed by the test case");
+ test.end();
+ };
+
+ test.run("test/fixtures/resources/style.json");
}
TEST(ResourceLoading, RasterSourceCorrupt) {
- StyleTestData testData;
- testData.type = MockFileSource::RequestWithCorruptedData;
- testData.style = "test/fixtures/resources/style.json";
- testData.resource = "source_raster.json";
- runTest(testData);
- EXPECT_EQ(testData.expectedError, "Failed to parse [test/fixtures/resources/source_raster.json]: 0 - Invalid value.");
+ ResourceLoadingTest test(MockFileSource::RequestWithCorruptedData, "source_raster.json");
+
+ test.observer.sourceError = [&] (Source& source, std::exception_ptr error) {
+ EXPECT_EQ(source.info.source_id, "rastersource");
+ EXPECT_EQ(util::toString(error), "0 - Invalid value.");
+ test.end();
+ };
+
+ test.run("test/fixtures/resources/style.json");
}
TEST(ResourceLoading, VectorSourceCorrupt) {
- StyleTestData testData;
- testData.type = MockFileSource::RequestWithCorruptedData;
- testData.style = "test/fixtures/resources/style.json";
- testData.resource = "source_vector.json";
- runTest(testData);
- EXPECT_EQ(testData.expectedError, "Failed to parse [test/fixtures/resources/source_vector.json]: 0 - Invalid value.");
+ ResourceLoadingTest test(MockFileSource::RequestWithCorruptedData, "source_vector.json");
+
+ test.observer.sourceError = [&] (Source& source, std::exception_ptr error) {
+ EXPECT_EQ(source.info.source_id, "vectorsource");
+ EXPECT_EQ(util::toString(error), "0 - Invalid value.");
+ test.end();
+ };
+
+ test.run("test/fixtures/resources/style.json");
}
TEST(ResourceLoading, SpriteJSONCorrupt) {
- StyleTestData testData;
- testData.type = MockFileSource::RequestWithCorruptedData;
- testData.style = "test/fixtures/resources/style.json";
- testData.resource = "sprite.json";
- runTest(testData);
- EXPECT_EQ(testData.expectedError, "Failed to parse JSON: Invalid value. at offset 0");
+ ResourceLoadingTest test(MockFileSource::RequestWithCorruptedData, "sprite.json");
+
+ test.observer.spriteError = [&] (std::exception_ptr error) {
+ EXPECT_EQ(util::toString(error), "Failed to parse JSON: Invalid value. at offset 0");
+ test.end();
+ };
+
+ test.run("test/fixtures/resources/style.json");
}
TEST(ResourceLoading, SpriteImageCorrupt) {
- StyleTestData testData;
- testData.type = MockFileSource::RequestWithCorruptedData;
- testData.style = "test/fixtures/resources/style.json";
- testData.resource = "sprite.png";
- runTest(testData);
- EXPECT_EQ(testData.expectedError, "Could not parse sprite image");
+ ResourceLoadingTest test(MockFileSource::RequestWithCorruptedData, "sprite.png");
+
+ test.observer.spriteError = [&] (std::exception_ptr error) {
+ EXPECT_TRUE(bool(error));
+ // Not asserting on platform-specific error text.
+ test.end();
+ };
+
+ test.run("test/fixtures/resources/style.json");
}
TEST(ResourceLoading, RasterTileCorrupt) {
- StyleTestData testData;
- testData.type = MockFileSource::RequestWithCorruptedData;
- testData.style = "test/fixtures/resources/style.json";
- testData.resource = "raster.png";
- runTest(testData);
- EXPECT_EQ(testData.expectedError, "Failed to parse [0/0/0]: error parsing raster image");
+ ResourceLoadingTest test(MockFileSource::RequestWithCorruptedData, "raster.png");
+
+ test.observer.tileError = [&] (Source& source, const TileID& tileID, std::exception_ptr error) {
+ EXPECT_EQ(source.info.source_id, "rastersource");
+ EXPECT_EQ(std::string(tileID), "0/0/0");
+ EXPECT_TRUE(bool(error));
+ // Not asserting on platform-specific error text.
+ test.end();
+ };
+
+ test.run("test/fixtures/resources/style.json");
}
TEST(ResourceLoading, VectorTileCorrupt) {
- StyleTestData testData;
- testData.type = MockFileSource::RequestWithCorruptedData;
- testData.style = "test/fixtures/resources/style.json";
- testData.resource = "vector.pbf";
- runTest(testData);
- EXPECT_EQ(testData.expectedError, "Failed to parse [0/0/0]: pbf unknown field type exception");
+ ResourceLoadingTest test(MockFileSource::RequestWithCorruptedData, "vector.pbf");
+
+ test.observer.tileError = [&] (Source& source, const TileID& tileID, std::exception_ptr error) {
+ EXPECT_EQ(source.info.source_id, "vectorsource");
+ EXPECT_EQ(std::string(tileID), "0/0/0");
+ EXPECT_EQ(util::toString(error), "pbf unknown field type exception");
+ test.end();
+ };
+
+ test.run("test/fixtures/resources/style.json");
}
TEST(ResourceLoading, GlyphsCorrupt) {
- StyleTestData testData;
- testData.type = MockFileSource::RequestWithCorruptedData;
- testData.style = "test/fixtures/resources/style.json";
- testData.resource = "glyphs.pbf";
- runTest(testData);
- EXPECT_EQ(testData.expectedError, "Failed to parse [test/fixtures/resources/glyphs.pbf]: pbf unknown field type exception");
+ ResourceLoadingTest test(MockFileSource::RequestWithCorruptedData, "glyphs.pbf");
+
+ test.observer.glyphsError = [&] (const std::string& fontStack, const GlyphRange&, std::exception_ptr error) {
+ EXPECT_EQ(fontStack, "Open Sans Regular,Arial Unicode MS Regular");
+ EXPECT_EQ(util::toString(error), "pbf unknown field type exception");
+ test.end();
+ };
+
+ test.run("test/fixtures/resources/style.json");
}
TEST(ResourceLoading, UnusedSource) {
- StyleTestData testData;
- testData.type = MockFileSource::Success;
- testData.style = "test/fixtures/resources/style-unused-sources.json";
- testData.callback = [] (const Style& style) {
- Source *usedSource = style.getSource("usedsource");
+ ResourceLoadingTest test(MockFileSource::Success, "");
+
+ test.observer.fullyLoaded = [&] () {
+ Source *usedSource = test.style.getSource("usedsource");
EXPECT_TRUE(usedSource);
EXPECT_TRUE(usedSource->isLoaded());
- Source *unusedSource = style.getSource("unusedsource");
+ Source *unusedSource = test.style.getSource("unusedsource");
EXPECT_TRUE(unusedSource);
EXPECT_FALSE(unusedSource->isLoaded());
+
+ test.end();
};
- runTest(testData);
+
+ test.run("test/fixtures/resources/style-unused-sources.json");
}
TEST(ResourceLoading, UnusedSourceActiveViaClassUpdate) {
- StyleTestData testData;
- testData.type = MockFileSource::Success;
- testData.style = "test/fixtures/resources/style-unused-sources.json";
- testData.styleClass = "visible";
- testData.callback = [] (const Style& style) {
- Source *unusedSource = style.getSource("unusedsource");
+ ResourceLoadingTest test(MockFileSource::Success, "");
+
+ test.data.addClass("visible");
+
+ test.observer.fullyLoaded = [&] () {
+ Source *unusedSource = test.style.getSource("unusedsource");
EXPECT_TRUE(unusedSource);
EXPECT_TRUE(unusedSource->isLoaded());
+
+ test.end();
};
- runTest(testData);
+
+ test.run("test/fixtures/resources/style-unused-sources.json");
}