[core] LocalFileSource implementation and tests

This commit is contained in:
Ivo van Dongen
2016-09-28 16:01:06 +02:00
committed by Ivo van Dongen
parent eca126d804
commit 1bf12e20a6
6 changed files with 222 additions and 1 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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);
}

View 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

View 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

View 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();
}