[core] LocalFileSource implementation and tests
This commit is contained in:
committed by
Ivo van Dongen
parent
eca126d804
commit
1bf12e20a6
@@ -60,6 +60,7 @@ set(MBGL_TEST_FILES
|
||||
# storage
|
||||
test/storage/asset_file_source.test.cpp
|
||||
test/storage/default_file_source.test.cpp
|
||||
test/storage/local_file_source.test.cpp
|
||||
test/storage/headers.test.cpp
|
||||
test/storage/http_file_source.test.cpp
|
||||
test/storage/offline.test.cpp
|
||||
|
||||
@@ -121,6 +121,7 @@ public:
|
||||
private:
|
||||
const std::unique_ptr<util::Thread<Impl>> thread;
|
||||
const std::unique_ptr<FileSource> assetFileSource;
|
||||
const std::unique_ptr<FileSource> localFileSource;
|
||||
};
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <mbgl/storage/default_file_source.hpp>
|
||||
#include <mbgl/storage/asset_file_source.hpp>
|
||||
#include <mbgl/storage/local_file_source.hpp>
|
||||
#include <mbgl/storage/online_file_source.hpp>
|
||||
#include <mbgl/storage/offline_database.hpp>
|
||||
#include <mbgl/storage/offline_download.hpp>
|
||||
@@ -164,7 +165,8 @@ DefaultFileSource::DefaultFileSource(const std::string& cachePath,
|
||||
uint64_t maximumCacheSize)
|
||||
: thread(std::make_unique<util::Thread<Impl>>(util::ThreadContext{"DefaultFileSource", util::ThreadPriority::Low},
|
||||
cachePath, maximumCacheSize)),
|
||||
assetFileSource(std::make_unique<AssetFileSource>(assetRoot)) {
|
||||
assetFileSource(std::make_unique<AssetFileSource>(assetRoot)),
|
||||
localFileSource(std::make_unique<LocalFileSource>()) {
|
||||
}
|
||||
|
||||
DefaultFileSource::~DefaultFileSource() = default;
|
||||
@@ -203,6 +205,8 @@ std::unique_ptr<AsyncRequest> DefaultFileSource::request(const Resource& resourc
|
||||
|
||||
if (isAssetURL(resource.url)) {
|
||||
return assetFileSource->request(resource, callback);
|
||||
} else if (LocalFileSource::acceptsURL(resource.url)) {
|
||||
return localFileSource->request(resource, callback);
|
||||
} else {
|
||||
return std::make_unique<DefaultFileRequest>(resource, callback, *thread);
|
||||
}
|
||||
|
||||
66
platform/default/local_file_source.cpp
Normal file
66
platform/default/local_file_source.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
#include <mbgl/storage/local_file_source.hpp>
|
||||
#include <mbgl/storage/response.hpp>
|
||||
#include <mbgl/util/string.hpp>
|
||||
#include <mbgl/util/thread.hpp>
|
||||
#include <mbgl/util/url.hpp>
|
||||
#include <mbgl/util/util.hpp>
|
||||
#include <mbgl/util/io.hpp>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace {
|
||||
|
||||
const char* protocol = "file://";
|
||||
const std::size_t protocolLength = 7;
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
class LocalFileSource::Impl {
|
||||
public:
|
||||
void request(const std::string& url, FileSource::Callback callback) {
|
||||
//Cut off the protocol
|
||||
std::string path = mbgl::util::percentDecode(url.substr(protocolLength));
|
||||
|
||||
Response response;
|
||||
|
||||
struct stat buf;
|
||||
int result = stat(path.c_str(), &buf);
|
||||
|
||||
if (result == 0 && S_ISDIR(buf.st_mode)) {
|
||||
response.error = std::make_unique<Response::Error>(Response::Error::Reason::NotFound);
|
||||
} else if (result == -1 && errno == ENOENT) {
|
||||
response.error = std::make_unique<Response::Error>(Response::Error::Reason::NotFound);
|
||||
} else {
|
||||
try {
|
||||
response.data = std::make_shared<std::string>(util::read_file(path));
|
||||
} catch (...) {
|
||||
response.error = std::make_unique<Response::Error>(
|
||||
Response::Error::Reason::Other,
|
||||
util::toString(std::current_exception()));
|
||||
}
|
||||
}
|
||||
|
||||
callback(response);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
LocalFileSource::LocalFileSource()
|
||||
: thread(std::make_unique<util::Thread<Impl>>(util::ThreadContext{"LocalFileSource", util::ThreadPriority::Low})) {
|
||||
}
|
||||
|
||||
LocalFileSource::~LocalFileSource() = default;
|
||||
|
||||
std::unique_ptr<AsyncRequest> LocalFileSource::request(const Resource& resource, Callback callback) {
|
||||
return thread->invokeWithCallback(&Impl::request, resource.url, callback);
|
||||
}
|
||||
|
||||
bool LocalFileSource::acceptsURL(const std::string& url) {
|
||||
return url.compare(0, protocolLength, protocol) == 0;
|
||||
}
|
||||
|
||||
} // namespace mbgl
|
||||
25
src/mbgl/storage/local_file_source.hpp
Normal file
25
src/mbgl/storage/local_file_source.hpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/storage/file_source.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
namespace util {
|
||||
template <typename T> class Thread;
|
||||
} // namespace util
|
||||
|
||||
class LocalFileSource : public FileSource {
|
||||
public:
|
||||
LocalFileSource();
|
||||
~LocalFileSource() override;
|
||||
|
||||
std::unique_ptr<AsyncRequest> request(const Resource&, Callback) override;
|
||||
|
||||
static bool acceptsURL(const std::string& url);
|
||||
|
||||
private:
|
||||
class Impl;
|
||||
std::unique_ptr<util::Thread<Impl>> thread;
|
||||
};
|
||||
|
||||
} // namespace mbgl
|
||||
124
test/storage/local_file_source.test.cpp
Normal file
124
test/storage/local_file_source.test.cpp
Normal file
@@ -0,0 +1,124 @@
|
||||
#include <mbgl/storage/local_file_source.hpp>
|
||||
#include <mbgl/platform/platform.hpp>
|
||||
#include <mbgl/util/run_loop.hpp>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
namespace {
|
||||
|
||||
std::string toAbsoluteURL(const std::string& fileName) {
|
||||
char buff[PATH_MAX + 1];
|
||||
char* cwd = getcwd( buff, PATH_MAX + 1 );
|
||||
std::string url = { "file://" + std::string(cwd) + "/test/fixtures/storage/assets/" + fileName };
|
||||
assert(url.size() <= PATH_MAX);
|
||||
return url;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
using namespace mbgl;
|
||||
|
||||
TEST(LocalFileSource, EmptyFile) {
|
||||
util::RunLoop loop;
|
||||
|
||||
LocalFileSource fs;
|
||||
|
||||
std::unique_ptr<AsyncRequest> req = fs.request({ Resource::Unknown, toAbsoluteURL("empty") }, [&](Response res) {
|
||||
req.reset();
|
||||
EXPECT_EQ(nullptr, res.error);
|
||||
ASSERT_TRUE(res.data.get());
|
||||
EXPECT_EQ("", *res.data);
|
||||
loop.stop();
|
||||
});
|
||||
|
||||
loop.run();
|
||||
}
|
||||
|
||||
TEST(LocalFileSource, NonEmptyFile) {
|
||||
util::RunLoop loop;
|
||||
|
||||
LocalFileSource fs;
|
||||
|
||||
std::unique_ptr<AsyncRequest> req = fs.request({ Resource::Unknown, toAbsoluteURL("nonempty") }, [&](Response res) {
|
||||
req.reset();
|
||||
EXPECT_EQ(nullptr, res.error);
|
||||
ASSERT_TRUE(res.data.get());
|
||||
EXPECT_EQ("content is here\n", *res.data);
|
||||
loop.stop();
|
||||
});
|
||||
|
||||
loop.run();
|
||||
}
|
||||
|
||||
TEST(LocalFileSource, NonExistentFile) {
|
||||
util::RunLoop loop;
|
||||
|
||||
LocalFileSource fs;
|
||||
|
||||
std::unique_ptr<AsyncRequest> req = fs.request({ Resource::Unknown, toAbsoluteURL("does_not_exist") }, [&](Response res) {
|
||||
req.reset();
|
||||
ASSERT_NE(nullptr, res.error);
|
||||
EXPECT_EQ(Response::Error::Reason::NotFound, res.error->reason);
|
||||
ASSERT_FALSE(res.data.get());
|
||||
// Do not assert on platform-specific error message.
|
||||
loop.stop();
|
||||
});
|
||||
|
||||
loop.run();
|
||||
}
|
||||
|
||||
TEST(LocalFileSource, ReadDirectory) {
|
||||
util::RunLoop loop;
|
||||
|
||||
LocalFileSource fs;
|
||||
|
||||
std::unique_ptr<AsyncRequest> req = fs.request({ Resource::Unknown, toAbsoluteURL("directory") }, [&](Response res) {
|
||||
req.reset();
|
||||
ASSERT_NE(nullptr, res.error);
|
||||
EXPECT_EQ(Response::Error::Reason::NotFound, res.error->reason);
|
||||
ASSERT_FALSE(res.data.get());
|
||||
// Do not assert on platform-specific error message.
|
||||
loop.stop();
|
||||
});
|
||||
|
||||
loop.run();
|
||||
}
|
||||
|
||||
TEST(LocalFileSource, URLEncoding) {
|
||||
util::RunLoop loop;
|
||||
|
||||
LocalFileSource fs;
|
||||
|
||||
std::unique_ptr<AsyncRequest> req = fs.request({ Resource::Unknown, toAbsoluteURL("%6eonempty") }, [&](Response res) {
|
||||
req.reset();
|
||||
EXPECT_EQ(nullptr, res.error);
|
||||
ASSERT_TRUE(res.data.get());
|
||||
EXPECT_EQ("content is here\n", *res.data);
|
||||
loop.stop();
|
||||
});
|
||||
|
||||
loop.run();
|
||||
}
|
||||
|
||||
TEST(LocalFileSource, URLLimit) {
|
||||
util::RunLoop loop;
|
||||
|
||||
size_t length = PATH_MAX - toAbsoluteURL("").size();
|
||||
LocalFileSource fs;
|
||||
char filename[length];
|
||||
memset(filename, 'x', length);
|
||||
|
||||
std::string url(filename, length);
|
||||
|
||||
std::unique_ptr<AsyncRequest> req = fs.request({ Resource::Unknown, toAbsoluteURL(url) }, [&](Response res) {
|
||||
req.reset();
|
||||
ASSERT_NE(nullptr, res.error);
|
||||
EXPECT_EQ(Response::Error::Reason::Other, res.error->reason);
|
||||
ASSERT_FALSE(res.data.get());
|
||||
loop.stop();
|
||||
});
|
||||
|
||||
loop.run();
|
||||
}
|
||||
Reference in New Issue
Block a user