Add tests for handling corrupted data
The MockFileSource will insert garbage on the reply and report as a request done successfully. The network stack cannot detect this kind of errors but the consumer should report it like we do for network problems.
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
#include "../fixtures/util.hpp"
|
||||
#include "mock_file_source.hpp"
|
||||
|
||||
#include <mbgl/storage/request.hpp>
|
||||
@@ -8,30 +9,64 @@ namespace mbgl {
|
||||
|
||||
class MockFileSource::Impl {
|
||||
public:
|
||||
Impl(uv_loop_t*, const std::string& matchFail) : matchFail_(matchFail) {}
|
||||
Impl(uv_loop_t*, Type type, const std::string& match) : type_(type), match_(match) {}
|
||||
|
||||
void handleRequest(Request* req) const;
|
||||
|
||||
private:
|
||||
std::string matchFail_;
|
||||
void replyWithFailure(Response* res) const;
|
||||
void replyWithCorruptedData(Response* res, const std::string& url) const;
|
||||
void replyWithSuccess(Response* res, const std::string& url) const;
|
||||
|
||||
Type type_;
|
||||
std::string match_;
|
||||
};
|
||||
|
||||
void MockFileSource::Impl::handleRequest(Request* req) const {
|
||||
const Resource& resource = req->resource;
|
||||
void MockFileSource::Impl::replyWithFailure(Response* res) const {
|
||||
res->status = Response::Status::Error;
|
||||
res->message = "Failed by the test case";
|
||||
}
|
||||
|
||||
void MockFileSource::Impl::replyWithCorruptedData(Response* res, const std::string& url) const {
|
||||
res->status = Response::Status::Successful;
|
||||
res->data = util::read_file(url);
|
||||
res->data.insert(0, "CORRUPTED");
|
||||
}
|
||||
|
||||
void MockFileSource::Impl::replyWithSuccess(Response* res, const std::string& url) const {
|
||||
res->status = Response::Status::Successful;
|
||||
res->data = util::read_file(url);
|
||||
}
|
||||
|
||||
void MockFileSource::Impl::handleRequest(Request* req) const {
|
||||
const std::string& url = req->resource.url;
|
||||
std::shared_ptr<Response> response = std::make_shared<Response>();
|
||||
if (matchFail_.empty() || resource.url.find(matchFail_) == std::string::npos) {
|
||||
response->status = Response::Status::Successful;
|
||||
response->data = util::read_file(resource.url.c_str());
|
||||
} else {
|
||||
response->message = "Failed by the test case";
|
||||
|
||||
if (url.find(match_) == std::string::npos) {
|
||||
replyWithSuccess(response.get(), url);
|
||||
req->notify(response);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (type_) {
|
||||
case Type::Success:
|
||||
replyWithSuccess(response.get(), url);
|
||||
break;
|
||||
case Type::RequestFail:
|
||||
replyWithFailure(response.get());
|
||||
break;
|
||||
case Type::RequestWithCorruptedData:
|
||||
replyWithCorruptedData(response.get(), url);
|
||||
break;
|
||||
default:
|
||||
EXPECT_TRUE(false) << "Should never be reached.";
|
||||
}
|
||||
|
||||
req->notify(response);
|
||||
}
|
||||
|
||||
MockFileSource::MockFileSource(const std::string& matchFail)
|
||||
: thread_(std::make_unique<util::Thread<Impl>>("FileSource", util::ThreadPriority::Low, matchFail)) {
|
||||
MockFileSource::MockFileSource(Type type, const std::string& match)
|
||||
: thread_(std::make_unique<util::Thread<Impl>>("FileSource", util::ThreadPriority::Low, type, match)) {
|
||||
}
|
||||
|
||||
Request* MockFileSource::request(const Resource& resource, uv_loop_t* loop, Callback callback) {
|
||||
|
||||
@@ -13,12 +13,18 @@ template <typename T> class Thread;
|
||||
}
|
||||
|
||||
// This mock FileSource will read data from the disk and will fail
|
||||
// the request if the URL matches the 'matchFail' string.
|
||||
// the request if the URL matches a string.
|
||||
class MockFileSource : public FileSource {
|
||||
public:
|
||||
enum Type {
|
||||
Success,
|
||||
RequestFail,
|
||||
RequestWithCorruptedData
|
||||
};
|
||||
|
||||
class Impl;
|
||||
|
||||
MockFileSource(const std::string& matchFail);
|
||||
MockFileSource(Type type, const std::string& match);
|
||||
~MockFileSource() override = default;
|
||||
|
||||
// FileSource implementation.
|
||||
|
||||
@@ -107,18 +107,13 @@ private:
|
||||
std::function<void(std::exception_ptr error)> callback_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
class ResourceLoaderTest : public ::testing::TestWithParam<std::string> {
|
||||
};
|
||||
|
||||
TEST_P(ResourceLoaderTest, RequestFailure) {
|
||||
void runTestCase(MockFileSource::Type type,
|
||||
const std::string& param,
|
||||
const std::string& message) {
|
||||
util::RunLoop loop(uv_default_loop());
|
||||
|
||||
const std::string param(GetParam());
|
||||
|
||||
MockView view;
|
||||
MockFileSource fileSource(param);
|
||||
MockFileSource fileSource(type, param);
|
||||
|
||||
FixtureLogObserver* log = new FixtureLogObserver();
|
||||
Log::setObserver(std::unique_ptr<Log::Observer>(log));
|
||||
@@ -127,9 +122,6 @@ TEST_P(ResourceLoaderTest, RequestFailure) {
|
||||
try {
|
||||
if (error) {
|
||||
std::rethrow_exception(error);
|
||||
} else {
|
||||
// Control test, should succeed.
|
||||
EXPECT_TRUE(param.empty());
|
||||
}
|
||||
} catch (const util::GlyphRangeLoadingException&) {
|
||||
EXPECT_EQ(param, "glyphs.pbf");
|
||||
@@ -156,24 +148,57 @@ TEST_P(ResourceLoaderTest, RequestFailure) {
|
||||
// join and cease logging after this point.
|
||||
context.reset();
|
||||
|
||||
std::stringstream message;
|
||||
message << "Failed to load [test/fixtures/resources/" << param << "]: Failed by the test case";
|
||||
|
||||
const FixtureLogObserver::LogMessage logMessage {
|
||||
EventSeverity::Error,
|
||||
Event::ResourceLoader,
|
||||
int64_t(-1),
|
||||
message.str(),
|
||||
message,
|
||||
};
|
||||
|
||||
if (param.empty()) {
|
||||
if (type == MockFileSource::Success) {
|
||||
EXPECT_EQ(log->count(logMessage), 0u);
|
||||
} else {
|
||||
EXPECT_GT(log->count(logMessage), 0u);
|
||||
}
|
||||
|
||||
EXPECT_EQ(log->unchecked().size(), 0u);
|
||||
// Clear the remaining error messages
|
||||
log->unchecked().size();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class ResourceLoaderTest : public ::testing::TestWithParam<std::string> {
|
||||
};
|
||||
|
||||
TEST_P(ResourceLoaderTest, Success) {
|
||||
runTestCase(MockFileSource::Success, GetParam(), std::string());
|
||||
}
|
||||
|
||||
TEST_P(ResourceLoaderTest, RequestFail) {
|
||||
std::stringstream message;
|
||||
message << "Failed to load [test/fixtures/resources/" << GetParam() << "]: Failed by the test case";
|
||||
|
||||
runTestCase(MockFileSource::RequestFail, GetParam(), message.str());
|
||||
}
|
||||
|
||||
TEST_P(ResourceLoaderTest, RequestWithCorruptedData) {
|
||||
const std::string param(GetParam());
|
||||
|
||||
std::stringstream message;
|
||||
message << "Failed to parse ";
|
||||
|
||||
if (param == "vector.pbf") {
|
||||
message << "[15/16384/16384]: pbf unknown field type exception";
|
||||
} else {
|
||||
message << "[test/fixtures/resources/" << param << "]";
|
||||
}
|
||||
|
||||
if (param.find("json") != std::string::npos) {
|
||||
message << ": 0 - Expect either an object or array at root";
|
||||
}
|
||||
|
||||
runTestCase(MockFileSource::RequestWithCorruptedData, GetParam(), message.str());
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(ResourceLoader, ResourceLoaderTest,
|
||||
::testing::Values("", "source.json", "sprite.json", "sprite.png", "vector.pbf", "glyphs.pbf"));
|
||||
::testing::Values("source.json", "sprite.json", "sprite.png", "vector.pbf", "glyphs.pbf"));
|
||||
|
||||
Reference in New Issue
Block a user