[tests] Rationalize storage tests

Properly divide Storage tests into DefaultFileSource, OnlineFileSource, and HTTPFileSource tests.
This commit is contained in:
John Firebaugh
2016-03-24 15:28:13 -07:00
parent 3af3e72bb3
commit 7de9e196d7
19 changed files with 645 additions and 912 deletions

View File

@@ -1,9 +1,12 @@
#include <mbgl/test.hpp>
#include <mbgl/test/util.hpp>
#include <gtest/gtest.h>
namespace mbgl {
int runTests(int argc, char *argv[]) {
auto server = std::make_unique<test::Server>("test/storage/server.js");
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@@ -1,11 +1,11 @@
#include "storage.hpp"
#include <mbgl/storage/asset_file_source.hpp>
#include <mbgl/platform/platform.hpp>
#include <mbgl/util/chrono.hpp>
#include <mbgl/util/run_loop.hpp>
#include <mbgl/util/thread.hpp>
#include <gtest/gtest.h>
namespace {
std::string getFileSourceRoot() {
@@ -17,45 +17,11 @@ std::string getFileSourceRoot() {
#endif
}
class TestWorker {
public:
TestWorker(mbgl::AssetFileSource* fs_) : fs(fs_) {}
void run(std::function<void()> endCallback) {
const std::string asset("asset://nonempty");
requestCallback = [this, asset, endCallback](mbgl::Response res) {
EXPECT_EQ(nullptr, res.error);
ASSERT_TRUE(res.data.get());
EXPECT_EQ("content is here\n", *res.data);
if (!--numRequests) {
endCallback();
request.reset();
} else {
request = fs->request({ mbgl::Resource::Unknown, asset }, requestCallback);
}
};
request = fs->request({ mbgl::Resource::Unknown, asset }, requestCallback);
}
private:
unsigned numRequests = 1000;
mbgl::AssetFileSource* fs;
std::unique_ptr<mbgl::AsyncRequest> request;
std::function<void(mbgl::Response)> requestCallback;
};
} // namespace
TEST_F(Storage, AssetStress) {
SCOPED_TEST(AssetStress)
using namespace mbgl;
using namespace mbgl;
TEST(AssetFileSource, Stress) {
util::RunLoop loop;
AssetFileSource fs(getFileSourceRoot());
@@ -73,6 +39,38 @@ TEST_F(Storage, AssetStress) {
}
};
class TestWorker {
public:
TestWorker(mbgl::AssetFileSource* fs_) : fs(fs_) {}
void run(std::function<void()> endCallback) {
const std::string asset("asset://nonempty");
requestCallback = [this, asset, endCallback](mbgl::Response res) {
EXPECT_EQ(nullptr, res.error);
ASSERT_TRUE(res.data.get());
EXPECT_EQ("content is here\n", *res.data);
if (!--numRequests) {
endCallback();
request.reset();
} else {
request = fs->request({ mbgl::Resource::Unknown, asset }, requestCallback);
}
};
request = fs->request({ mbgl::Resource::Unknown, asset }, requestCallback);
}
private:
unsigned numRequests = 1000;
mbgl::AssetFileSource* fs;
std::unique_ptr<mbgl::AsyncRequest> request;
std::function<void(mbgl::Response)> requestCallback;
};
std::vector<std::unique_ptr<util::Thread<TestWorker>>> threads;
std::vector<std::unique_ptr<mbgl::AsyncRequest>> requests;
util::ThreadContext context = { "Test", util::ThreadType::Map, util::ThreadPriority::Regular };
@@ -88,15 +86,9 @@ TEST_F(Storage, AssetStress) {
}
loop.run();
AssetStress.finish();
}
TEST_F(Storage, AssetEmptyFile) {
SCOPED_TEST(EmptyFile)
using namespace mbgl;
TEST(AssetFileSource, EmptyFile) {
util::RunLoop loop;
AssetFileSource fs(getFileSourceRoot());
@@ -107,17 +99,12 @@ TEST_F(Storage, AssetEmptyFile) {
ASSERT_TRUE(res.data.get());
EXPECT_EQ("", *res.data);
loop.stop();
EmptyFile.finish();
});
loop.run();
}
TEST_F(Storage, AssetNonEmptyFile) {
SCOPED_TEST(NonEmptyFile)
using namespace mbgl;
TEST(AssetFileSource, NonEmptyFile) {
util::RunLoop loop;
AssetFileSource fs(getFileSourceRoot());
@@ -128,17 +115,12 @@ TEST_F(Storage, AssetNonEmptyFile) {
ASSERT_TRUE(res.data.get());
EXPECT_EQ("content is here\n", *res.data);
loop.stop();
NonEmptyFile.finish();
});
loop.run();
}
TEST_F(Storage, AssetNonExistentFile) {
SCOPED_TEST(NonExistentFile)
using namespace mbgl;
TEST(AssetFileSource, NonExistentFile) {
util::RunLoop loop;
AssetFileSource fs(getFileSourceRoot());
@@ -150,17 +132,12 @@ TEST_F(Storage, AssetNonExistentFile) {
ASSERT_FALSE(res.data.get());
// Do not assert on platform-specific error message.
loop.stop();
NonExistentFile.finish();
});
loop.run();
}
TEST_F(Storage, AssetReadDirectory) {
SCOPED_TEST(ReadDirectory)
using namespace mbgl;
TEST(AssetFileSource, ReadDirectory) {
util::RunLoop loop;
AssetFileSource fs(getFileSourceRoot());
@@ -172,17 +149,12 @@ TEST_F(Storage, AssetReadDirectory) {
ASSERT_FALSE(res.data.get());
// Do not assert on platform-specific error message.
loop.stop();
ReadDirectory.finish();
});
loop.run();
}
TEST_F(Storage, AssetURLEncoding) {
SCOPED_TEST(NonEmptyFile)
using namespace mbgl;
TEST(AssetFileSource, URLEncoding) {
util::RunLoop loop;
AssetFileSource fs(getFileSourceRoot());
@@ -193,7 +165,6 @@ TEST_F(Storage, AssetURLEncoding) {
ASSERT_TRUE(res.data.get());
EXPECT_EQ("content is here\n", *res.data);
loop.stop();
NonEmptyFile.finish();
});
loop.run();

View File

@@ -1,15 +1,10 @@
#include "storage.hpp"
#include <mbgl/test/util.hpp>
#include <mbgl/storage/default_file_source.hpp>
#include <mbgl/util/run_loop.hpp>
class DefaultFileSourceTest : public Storage {};
TEST_F(DefaultFileSourceTest, TEST_REQUIRES_SERVER(CacheResponse)) {
SCOPED_TEST(CacheResponse);
using namespace mbgl;
using namespace mbgl;
TEST(DefaultFileSource, TEST_REQUIRES_SERVER(CacheResponse)) {
util::RunLoop loop;
DefaultFileSource fs(":memory:", ".");
@@ -41,18 +36,13 @@ TEST_F(DefaultFileSourceTest, TEST_REQUIRES_SERVER(CacheResponse)) {
EXPECT_EQ(response.etag, res2.etag);
loop.stop();
CacheResponse.finish();
});
});
loop.run();
}
TEST_F(DefaultFileSourceTest, TEST_REQUIRES_SERVER(CacheRevalidateSame)) {
SCOPED_TEST(CacheRevalidateSame)
using namespace mbgl;
TEST(DefaultFileSource, TEST_REQUIRES_SERVER(CacheRevalidateSame)) {
util::RunLoop loop;
DefaultFileSource fs(":memory:", ".");
@@ -89,7 +79,6 @@ TEST_F(DefaultFileSourceTest, TEST_REQUIRES_SERVER(CacheRevalidateSame)) {
EXPECT_EQ("snowfall", *res2.etag);
loop.stop();
CacheRevalidateSame.finish();
}
});
});
@@ -97,11 +86,7 @@ TEST_F(DefaultFileSourceTest, TEST_REQUIRES_SERVER(CacheRevalidateSame)) {
loop.run();
}
TEST_F(DefaultFileSourceTest, TEST_REQUIRES_SERVER(CacheRevalidateModified)) {
SCOPED_TEST(CacheRevalidateModified)
using namespace mbgl;
TEST(DefaultFileSource, TEST_REQUIRES_SERVER(CacheRevalidateModified)) {
util::RunLoop loop;
DefaultFileSource fs(":memory:", ".");
@@ -138,7 +123,6 @@ TEST_F(DefaultFileSourceTest, TEST_REQUIRES_SERVER(CacheRevalidateModified)) {
EXPECT_FALSE(res2.etag);
loop.stop();
CacheRevalidateModified.finish();
}
});
});
@@ -146,11 +130,7 @@ TEST_F(DefaultFileSourceTest, TEST_REQUIRES_SERVER(CacheRevalidateModified)) {
loop.run();
}
TEST_F(DefaultFileSourceTest, TEST_REQUIRES_SERVER(CacheRevalidateEtag)) {
SCOPED_TEST(CacheRevalidateEtag)
using namespace mbgl;
TEST(DefaultFileSource, TEST_REQUIRES_SERVER(CacheRevalidateEtag)) {
util::RunLoop loop;
DefaultFileSource fs(":memory:", ".");
@@ -187,10 +167,44 @@ TEST_F(DefaultFileSourceTest, TEST_REQUIRES_SERVER(CacheRevalidateEtag)) {
EXPECT_EQ("response-2", *res2.etag);
loop.stop();
CacheRevalidateEtag.finish();
}
});
});
loop.run();
}
// Test for https://github.com/mapbox/mapbox-gl-native/issue/1369
//
// A request for http://example.com is made. This triggers a cache get. While the cache get is
// pending, the request is canceled. This removes it from pending. Then, still while the cache get
// is pending, a second request is made for the same resource. This adds an entry back to pending
// and queues another cache request, even though the first one is still pending. Now both cache
// requests resolve to misses, resulting in two HTTP requests for the same resource. The first one
// will notify as expected, the second one will have bound a DefaultFileRequest* in the lambda that
// gets invalidated by the first notify's pending.erase, and when it gets notified, the crash
// occurs.
TEST(DefaultFileSource, TEST_REQUIRES_SERVER(HTTPIssue1369)) {
util::RunLoop loop;
DefaultFileSource fs(":memory:", ".");
const Resource resource { Resource::Unknown, "http://127.0.0.1:3000/test" };
auto req = fs.request(resource, [&](Response) {
ADD_FAILURE() << "Callback should not be called";
});
req.reset();
req = fs.request(resource, [&](Response res) {
req.reset();
EXPECT_EQ(nullptr, res.error);
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Hello World!", *res.data);
EXPECT_FALSE(bool(res.expires));
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
});
loop.run();
}

View File

@@ -1,9 +1,7 @@
#include "storage.hpp"
#include <mbgl/test/util.hpp>
#include <mbgl/util/http_header.hpp>
TEST_F(Storage, HTTPHeaderParsing) {
TEST(HTTPHeader, Parsing) {
using namespace mbgl;
http::CacheControl cc;

View File

@@ -1,55 +0,0 @@
#include "storage.hpp"
#include <mbgl/storage/online_file_source.hpp>
#include <mbgl/storage/network_status.hpp>
#include <mbgl/util/chrono.hpp>
#include <mbgl/util/run_loop.hpp>
#include <cmath>
TEST_F(Storage, TEST_REQUIRES_SERVER(HTTPCancel)) {
SCOPED_TEST(HTTPCancel)
using namespace mbgl;
util::RunLoop loop;
OnlineFileSource fs;
auto req =
fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" },
[&](Response) { ADD_FAILURE() << "Callback should not be called"; });
req.reset();
HTTPCancel.finish();
loop.runOnce();
}
TEST_F(Storage, TEST_REQUIRES_SERVER(HTTPCancelMultiple)) {
SCOPED_TEST(HTTPCancelMultiple)
using namespace mbgl;
util::RunLoop loop;
OnlineFileSource fs;
const Resource resource { Resource::Unknown, "http://127.0.0.1:3000/test" };
std::unique_ptr<AsyncRequest> req2 = fs.request(resource, [&](Response) {
ADD_FAILURE() << "Callback should not be called";
});
std::unique_ptr<AsyncRequest> req = fs.request(resource, [&](Response res) {
req.reset();
EXPECT_EQ(nullptr, res.error);
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Hello World!", *res.data);
EXPECT_FALSE(bool(res.expires));
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
HTTPCancelMultiple.finish();
});
req2.reset();
loop.run();
}

View File

@@ -1,87 +0,0 @@
#include "storage.hpp"
#include <mbgl/storage/online_file_source.hpp>
#include <mbgl/storage/network_status.hpp>
#include <mbgl/util/chrono.hpp>
#include <mbgl/util/run_loop.hpp>
#include <cmath>
TEST_F(Storage, TEST_REQUIRES_SERVER(HTTPTemporaryError)) {
SCOPED_TEST(HTTPTemporaryError)
using namespace mbgl;
util::RunLoop loop;
OnlineFileSource fs;
const auto start = Clock::now();
std::unique_ptr<AsyncRequest> req1 = fs.request({ Resource::Unknown, "http://127.0.0.1:3000/temporary-error" }, [&](Response res) {
static int counter = 0;
switch (counter++) {
case 0: {
const auto duration = std::chrono::duration<const double>(Clock::now() - start).count();
EXPECT_GT(0.2, duration) << "Initial error request took too long";
ASSERT_NE(nullptr, res.error);
EXPECT_EQ(Response::Error::Reason::Server, res.error->reason);
EXPECT_EQ("HTTP status code 500", res.error->message);
ASSERT_FALSE(bool(res.data));
EXPECT_FALSE(bool(res.expires));
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
} break;
case 1: {
req1.reset();
const auto duration = std::chrono::duration<const double>(Clock::now() - start).count();
EXPECT_LT(0.99, duration) << "Backoff timer didn't wait 1 second";
EXPECT_GT(1.2, duration) << "Backoff timer fired too late";
EXPECT_EQ(nullptr, res.error);
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Hello World!", *res.data);
EXPECT_FALSE(bool(res.expires));
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
HTTPTemporaryError.finish();
} break;
}
});
loop.run();
}
TEST_F(Storage, TEST_REQUIRES_SERVER(HTTPConnectionError)) {
SCOPED_TEST(HTTPConnectionError)
using namespace mbgl;
util::RunLoop loop;
OnlineFileSource fs;
const auto start = Clock::now();
std::unique_ptr<AsyncRequest> req2 = fs.request({ Resource::Unknown, "http://127.0.0.1:3001/" }, [&](Response res) {
static int counter = 0;
static int wait = 0;
const auto duration = std::chrono::duration<const double>(Clock::now() - start).count();
EXPECT_LT(wait - 0.01, duration) << "Backoff timer didn't wait 1 second";
EXPECT_GT(wait + 0.2, duration) << "Backoff timer fired too late";
ASSERT_NE(nullptr, res.error);
EXPECT_EQ(Response::Error::Reason::Connection, res.error->reason);
ASSERT_FALSE(res.data.get());
EXPECT_FALSE(bool(res.expires));
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
if (counter == 2) {
req2.reset();
loop.stop();
HTTPConnectionError.finish();
}
wait += (1 << counter);
counter++;
});
loop.run();
}

View File

@@ -1,105 +0,0 @@
#include "storage.hpp"
#include <mbgl/storage/default_file_source.hpp>
#include <mbgl/storage/online_file_source.hpp>
#include <mbgl/util/chrono.hpp>
#include <mbgl/util/run_loop.hpp>
#include <mbgl/util/timer.hpp>
TEST_F(Storage, TEST_REQUIRES_SERVER(HTTPRetryDelayOnExpiredTile)) {
SCOPED_TEST(HTTPRetryDelayOnExpiredTile)
using namespace mbgl;
util::RunLoop loop;
OnlineFileSource fs;
int counter = 0;
const Resource resource { Resource::Unknown, "http://127.0.0.1:3000/test?expires=10000" };
std::unique_ptr<AsyncRequest> req = fs.request(resource, [&](Response res) {
counter++;
EXPECT_EQ(nullptr, res.error);
EXPECT_GT(SystemClock::now(), res.expires);
});
util::Timer timer;
timer.start(Milliseconds(500), Duration::zero(), [&] () {
loop.stop();
});
loop.run();
EXPECT_EQ(1, counter);
HTTPRetryDelayOnExpiredTile.finish();
}
TEST_F(Storage, TEST_REQUIRES_SERVER(HTTPRetryOnClockSkew)) {
SCOPED_TEST(HTTPRetryOnClockSkew)
using namespace mbgl;
util::RunLoop loop;
DefaultFileSource fs(":memory:", ".");
int counter = 0;
const Resource resource { Resource::Unknown, "http://127.0.0.1:3000/clockskew" };
std::unique_ptr<AsyncRequest> req1 = fs.request(resource, [&](Response res) {
switch (counter++) {
case 0: {
EXPECT_EQ(nullptr, res.error);
EXPECT_GT(SystemClock::now(), res.expires);
} break;
case 1: {
EXPECT_EQ(nullptr, res.error);
auto now = SystemClock::now();
EXPECT_LT(now + Seconds(40), res.expires) << "Expiration not interpolated to 60s";
EXPECT_GT(now + Seconds(80), res.expires) << "Expiration not interpolated to 60s";
loop.stop();
} break;
}
});
loop.run();
HTTPRetryOnClockSkew.finish();
}
TEST_F(Storage, TEST_REQUIRES_SERVER(HTTPRespectPriorExpires)) {
SCOPED_TEST(HTTPRespectPriorExpires)
using namespace mbgl;
util::RunLoop loop;
DefaultFileSource fs(":memory:", ".");
// Very long expiration time, should never arrive.
Resource resource1{ Resource::Unknown, "http://127.0.0.1:3000/test" };
resource1.priorExpires = SystemClock::now() + Seconds(100000);
std::unique_ptr<AsyncRequest> req1 = fs.request(resource1, [&](Response) {
FAIL() << "Should never be called";
});
// No expiration time, should be requested immediately.
Resource resource2{ Resource::Unknown, "http://127.0.0.1:3000/test" };
std::unique_ptr<AsyncRequest> req2 = fs.request(resource2, [&](Response) {
HTTPRespectPriorExpires.finish();
loop.stop();
});
// Very long expiration time, should never arrive.
Resource resource3{ Resource::Unknown, "http://127.0.0.1:3000/test" };
resource3.priorExpires = SystemClock::now() + Seconds(100000);
std::unique_ptr<AsyncRequest> req3 = fs.request(resource3, [&](Response) {
FAIL() << "Should never be called";
});
loop.run();
}

View File

@@ -0,0 +1,195 @@
#include <mbgl/test/util.hpp>
#include <mbgl/storage/http_file_source.hpp>
#include <mbgl/util/exception.hpp>
#include <mbgl/util/chrono.hpp>
#include <mbgl/util/run_loop.hpp>
using namespace mbgl;
TEST(HTTPFileSource, TEST_REQUIRES_SERVER(Cancel)) {
util::RunLoop loop;
HTTPFileSource fs;
fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, [&](Response) {
ADD_FAILURE() << "Callback should not be called";
});
loop.runOnce();
}
TEST(HTTPFileSource, TEST_REQUIRES_SERVER(HTTP200)) {
util::RunLoop loop;
HTTPFileSource fs;
auto req = fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, [&](Response res) {
EXPECT_EQ(nullptr, res.error);
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Hello World!", *res.data);
EXPECT_FALSE(bool(res.expires));
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
});
loop.run();
}
TEST(HTTPFileSource, TEST_REQUIRES_SERVER(HTTP404)) {
util::RunLoop loop;
HTTPFileSource fs;
auto req = fs.request({ Resource::Unknown, "http://127.0.0.1:3000/doesnotexist" }, [&](Response res) {
ASSERT_NE(nullptr, res.error);
EXPECT_EQ(Response::Error::Reason::NotFound, res.error->reason);
EXPECT_EQ("HTTP status code 404", res.error->message);
EXPECT_FALSE(bool(res.data));
EXPECT_FALSE(bool(res.expires));
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
});
loop.run();
}
TEST(HTTPFileSource, TEST_REQUIRES_SERVER(HTTPTile404)) {
util::RunLoop loop;
HTTPFileSource fs;
auto req = fs.request({ Resource::Tile, "http://127.0.0.1:3000/doesnotexist" }, [&](Response res) {
EXPECT_TRUE(res.noContent);
EXPECT_FALSE(bool(res.error));
EXPECT_FALSE(bool(res.data));
EXPECT_FALSE(bool(res.expires));
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
});
loop.run();
}
TEST(HTTPFileSource, TEST_REQUIRES_SERVER(HTTP200EmptyData)) {
util::RunLoop loop;
HTTPFileSource fs;
auto req = fs.request({ Resource::Unknown, "http://127.0.0.1:3000/empty-data" }, [&](Response res) {
EXPECT_FALSE(res.noContent);
EXPECT_FALSE(bool(res.error));
EXPECT_EQ(*res.data, std::string());
EXPECT_FALSE(bool(res.expires));
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
});
loop.run();
}
TEST(HTTPFileSource, TEST_REQUIRES_SERVER(HTTP204)) {
util::RunLoop loop;
HTTPFileSource fs;
auto req = fs.request({ Resource::Unknown, "http://127.0.0.1:3000/no-content" }, [&](Response res) {
EXPECT_TRUE(res.noContent);
EXPECT_FALSE(bool(res.error));
EXPECT_FALSE(bool(res.data));
EXPECT_FALSE(bool(res.expires));
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
});
loop.run();
}
TEST(HTTPFileSource, TEST_REQUIRES_SERVER(HTTP500)) {
util::RunLoop loop;
HTTPFileSource fs;
auto req = fs.request({ Resource::Unknown, "http://127.0.0.1:3000/permanent-error" }, [&](Response res) {
ASSERT_NE(nullptr, res.error);
EXPECT_EQ(Response::Error::Reason::Server, res.error->reason);
EXPECT_EQ("HTTP status code 500", res.error->message);
EXPECT_FALSE(bool(res.data));
EXPECT_FALSE(bool(res.expires));
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
});
loop.run();
}
TEST(HTTPFileSource, TEST_REQUIRES_SERVER(ExpiresParsing)) {
util::RunLoop loop;
HTTPFileSource fs;
auto req = fs.request({ Resource::Unknown,
"http://127.0.0.1:3000/test?modified=1420794326&expires=1420797926&etag=foo" }, [&](Response res) {
EXPECT_EQ(nullptr, res.error);
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Hello World!", *res.data);
EXPECT_EQ(SystemClock::from_time_t(1420797926), res.expires);
EXPECT_EQ(SystemClock::from_time_t(1420794326), res.modified);
EXPECT_EQ("foo", *res.etag);
loop.stop();
});
loop.run();
}
TEST(HTTPFileSource, TEST_REQUIRES_SERVER(CacheControlParsing)) {
util::RunLoop loop;
HTTPFileSource fs;
auto req = fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test?cachecontrol=max-age=120" }, [&](Response res) {
EXPECT_EQ(nullptr, res.error);
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Hello World!", *res.data);
EXPECT_GT(Seconds(2), util::abs(*res.expires - SystemClock::now() - Seconds(120))) << "Expiration date isn't about 120 seconds in the future";
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
});
loop.run();
}
TEST(HTTPFileSource, TEST_REQUIRES_SERVER(Load)) {
util::RunLoop loop;
HTTPFileSource fs;
const int concurrency = 50;
const int max = 10000;
int number = 1;
std::unique_ptr<AsyncRequest> reqs[concurrency];
std::function<void(int)> req = [&](int i) {
const auto current = number++;
reqs[i] = fs.request({ Resource::Unknown,
std::string("http://127.0.0.1:3000/load/") + std::to_string(current) },
[&, i, current](Response res) {
reqs[i].reset();
EXPECT_EQ(nullptr, res.error);
ASSERT_TRUE(res.data.get());
EXPECT_EQ(std::string("Request ") + std::to_string(current), *res.data);
EXPECT_FALSE(bool(res.expires));
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
if (number <= max) {
req(i);
} else if (current == max) {
loop.stop();
}
});
};
for (int i = 0; i < concurrency; i++) {
req(i);
}
loop.run();
}

View File

@@ -1,56 +0,0 @@
#include "storage.hpp"
#include <mbgl/storage/online_file_source.hpp>
#include <mbgl/util/chrono.hpp>
#include <mbgl/util/run_loop.hpp>
#include <cmath>
TEST_F(Storage, TEST_REQUIRES_SERVER(HTTPExpiresParsing)) {
SCOPED_TEST(HTTPExpiresTest)
using namespace mbgl;
util::RunLoop loop;
OnlineFileSource fs;
std::unique_ptr<AsyncRequest> req1 = fs.request({ Resource::Unknown,
"http://127.0.0.1:3000/test?modified=1420794326&expires=1420797926&etag=foo" },
[&](Response res) {
req1.reset();
EXPECT_EQ(nullptr, res.error);
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Hello World!", *res.data);
EXPECT_EQ(SystemClock::from_time_t(1420797926), res.expires);
EXPECT_EQ(SystemClock::from_time_t(1420794326), res.modified);
EXPECT_EQ("foo", *res.etag);
loop.stop();
HTTPExpiresTest.finish();
});
loop.run();
}
TEST_F(Storage, TEST_REQUIRES_SERVER(HTTPCacheControlParsing)) {
SCOPED_TEST(HTTPCacheControlTest)
using namespace mbgl;
util::RunLoop loop;
OnlineFileSource fs;
std::unique_ptr<AsyncRequest> req2 = fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test?cachecontrol=max-age=120" },
[&](Response res) {
req2.reset();
EXPECT_EQ(nullptr, res.error);
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Hello World!", *res.data);
EXPECT_GT(Seconds(2), util::abs(*res.expires - SystemClock::now() - Seconds(120))) << "Expiration date isn't about 120 seconds in the future";
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
HTTPCacheControlTest.finish();
});
loop.run();
}

View File

@@ -1,44 +0,0 @@
#include "storage.hpp"
#include <mbgl/storage/default_file_source.hpp>
#include <mbgl/util/run_loop.hpp>
// Test for https://github.com/mapbox/mapbox-gl-native/issue/1369
//
// A request for http://example.com is made. This triggers a cache get. While the cache get is
// pending, the request is canceled. This removes it from pending. Then, still while the cache get
// is pending, a second request is made for the same resource. This adds an entry back to pending
// and queues another cache request, even though the first one is still pending. Now both cache
// requests resolve to misses, resulting in two HTTP requests for the same resource. The first one
// will notify as expected, the second one will have bound a DefaultFileRequest* in the lambda that
// gets invalidated by the first notify's pending.erase, and when it gets notified, the crash
// occurs.
TEST_F(Storage, TEST_REQUIRES_SERVER(HTTPIssue1369)) {
SCOPED_TEST(HTTPIssue1369)
using namespace mbgl;
util::RunLoop loop;
DefaultFileSource fs(":memory:", ".");
const Resource resource { Resource::Unknown, "http://127.0.0.1:3000/test" };
auto req = fs.request(resource, [&](Response) {
ADD_FAILURE() << "Callback should not be called";
});
req.reset();
req = fs.request(resource, [&](Response res) {
req.reset();
EXPECT_EQ(nullptr, res.error);
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Hello World!", *res.data);
EXPECT_FALSE(bool(res.expires));
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
HTTPIssue1369.finish();
});
loop.run();
}

View File

@@ -1,49 +0,0 @@
#include "storage.hpp"
#include <mbgl/storage/online_file_source.hpp>
#include <mbgl/util/chrono.hpp>
#include <mbgl/util/run_loop.hpp>
TEST_F(Storage, TEST_REQUIRES_SERVER(HTTPLoad)) {
SCOPED_TEST(HTTPLoad)
using namespace mbgl;
util::RunLoop loop;
OnlineFileSource fs;
const int concurrency = 50;
const int max = 10000;
int number = 1;
std::unique_ptr<AsyncRequest> reqs[concurrency];
std::function<void(int)> req = [&](int i) {
const auto current = number++;
reqs[i] = fs.request({ Resource::Unknown,
std::string("http://127.0.0.1:3000/load/") + std::to_string(current) },
[&, i, current](Response res) {
reqs[i].reset();
EXPECT_EQ(nullptr, res.error);
ASSERT_TRUE(res.data.get());
EXPECT_EQ(std::string("Request ") + std::to_string(current), *res.data);
EXPECT_FALSE(bool(res.expires));
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
if (number <= max) {
req(i);
} else if (current == max) {
loop.stop();
HTTPLoad.finish();
}
});
};
for (int i = 0; i < concurrency; i++) {
req(i);
}
loop.run();
}

View File

@@ -1,30 +0,0 @@
#include "storage.hpp"
#include <mbgl/storage/online_file_source.hpp>
#include <mbgl/util/chrono.hpp>
#include <mbgl/util/run_loop.hpp>
TEST_F(Storage, TEST_REQUIRES_SERVER(HTTPOtherLoop)) {
SCOPED_TEST(HTTPOtherLoop)
using namespace mbgl;
// This file source launches a separate thread to do the processing.
util::RunLoop loop;
OnlineFileSource fs;
std::unique_ptr<AsyncRequest> req = fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" },
[&](Response res) {
req.reset();
EXPECT_EQ(nullptr, res.error);
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Hello World!", *res.data);
EXPECT_FALSE(bool(res.expires));
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
HTTPOtherLoop.finish();
});
loop.run();
}

View File

@@ -1,181 +0,0 @@
#include "storage.hpp"
#include <mbgl/storage/online_file_source.hpp>
#include <mbgl/util/exception.hpp>
#include <mbgl/util/chrono.hpp>
#include <mbgl/util/run_loop.hpp>
#include <mbgl/util/thread_context.hpp>
#include <future>
TEST_F(Storage, TEST_REQUIRES_SERVER(HTTPTest)) {
SCOPED_TEST(HTTPTest)
using namespace mbgl;
util::RunLoop loop;
OnlineFileSource fs;
std::unique_ptr<AsyncRequest> req1 = fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" },
[&](Response res) {
req1.reset();
EXPECT_TRUE(util::ThreadContext::currentlyOn(util::ThreadType::Main));
EXPECT_EQ(nullptr, res.error);
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Hello World!", *res.data);
EXPECT_FALSE(bool(res.expires));
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
HTTPTest.finish();
});
loop.run();
}
TEST_F(Storage, TEST_REQUIRES_SERVER(HTTP404)) {
SCOPED_TEST(HTTP404)
using namespace mbgl;
util::RunLoop loop;
OnlineFileSource fs;
std::unique_ptr<AsyncRequest> req2 = fs.request({ Resource::Unknown, "http://127.0.0.1:3000/doesnotexist" },
[&](Response res) {
req2.reset();
EXPECT_TRUE(util::ThreadContext::currentlyOn(util::ThreadType::Main));
ASSERT_NE(nullptr, res.error);
EXPECT_EQ(Response::Error::Reason::NotFound, res.error->reason);
EXPECT_EQ("HTTP status code 404", res.error->message);
EXPECT_FALSE(bool(res.data));
EXPECT_FALSE(bool(res.expires));
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
HTTP404.finish();
});
loop.run();
}
TEST_F(Storage, TEST_REQUIRES_SERVER(HTTPTile404)) {
SCOPED_TEST(HTTPTile404)
using namespace mbgl;
util::RunLoop loop;
OnlineFileSource fs;
std::unique_ptr<AsyncRequest> req2 = fs.request({ Resource::Tile, "http://127.0.0.1:3000/doesnotexist" },
[&](Response res) {
req2.reset();
EXPECT_TRUE(util::ThreadContext::currentlyOn(util::ThreadType::Main));
EXPECT_TRUE(res.noContent);
EXPECT_FALSE(bool(res.error));
EXPECT_FALSE(bool(res.data));
EXPECT_FALSE(bool(res.expires));
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
HTTPTile404.finish();
});
loop.run();
}
TEST_F(Storage, TEST_REQUIRES_SERVER(HTTP200EmptyData)) {
SCOPED_TEST(HTTP200EmptyData)
using namespace mbgl;
util::RunLoop loop;
OnlineFileSource fs;
std::unique_ptr<AsyncRequest> req = fs.request({ Resource::Unknown, "http://127.0.0.1:3000/empty-data" },
[&](Response res) {
req.reset();
EXPECT_TRUE(util::ThreadContext::currentlyOn(util::ThreadType::Main));
EXPECT_FALSE(res.noContent);
EXPECT_FALSE(bool(res.error));
EXPECT_EQ(*res.data, std::string());
EXPECT_FALSE(bool(res.expires));
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
HTTP200EmptyData.finish();
});
loop.run();
}
TEST_F(Storage, TEST_REQUIRES_SERVER(HTTP204)) {
SCOPED_TEST(HTTP204)
using namespace mbgl;
util::RunLoop loop;
OnlineFileSource fs;
std::unique_ptr<AsyncRequest> req2 = fs.request({ Resource::Unknown, "http://127.0.0.1:3000/no-content" },
[&](Response res) {
req2.reset();
EXPECT_TRUE(util::ThreadContext::currentlyOn(util::ThreadType::Main));
EXPECT_TRUE(res.noContent);
EXPECT_FALSE(bool(res.error));
EXPECT_FALSE(bool(res.data));
EXPECT_FALSE(bool(res.expires));
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
HTTP204.finish();
});
loop.run();
}
TEST_F(Storage, TEST_REQUIRES_SERVER(HTTP500)) {
SCOPED_TEST(HTTP500)
using namespace mbgl;
util::RunLoop loop;
OnlineFileSource fs;
std::unique_ptr<AsyncRequest> req3 = fs.request({ Resource::Unknown, "http://127.0.0.1:3000/permanent-error" },
[&](Response res) {
req3.reset();
EXPECT_TRUE(util::ThreadContext::currentlyOn(util::ThreadType::Main));
ASSERT_NE(nullptr, res.error);
EXPECT_EQ(Response::Error::Reason::Server, res.error->reason);
EXPECT_EQ("HTTP status code 500", res.error->message);
EXPECT_FALSE(bool(res.data));
EXPECT_FALSE(bool(res.expires));
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
HTTP500.finish();
});
loop.run();
}
TEST_F(Storage, TEST_REQUIRES_SERVER(HTTPNoCallback)) {
SCOPED_TEST(HTTPNoCallback)
using namespace mbgl;
util::RunLoop loop;
OnlineFileSource fs;
try {
fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" },
nullptr);
} catch (const util::MisuseException& ex) {
EXPECT_EQ(std::string(ex.what()), "FileSource callback can't be empty");
} catch (const std::exception&) {
EXPECT_TRUE(false) << "Unhandled exception.";
}
HTTPNoCallback.finish();
}

View File

@@ -1,125 +0,0 @@
#include "storage.hpp"
#include <mbgl/storage/online_file_source.hpp>
#include <mbgl/storage/network_status.hpp>
#include <mbgl/util/chrono.hpp>
#include <mbgl/util/run_loop.hpp>
#include <mbgl/util/timer.hpp>
// Test for https://github.com/mapbox/mapbox-gl-native/issues/2123
//
// A request is made. While the request is in progress, the network status changes. This should
// trigger an immediate retry of all requests that are not in progress. This test makes sure that
// we don't accidentally double-trigger the request.
TEST_F(Storage, TEST_REQUIRES_SERVER(HTTPNetworkStatusChange)) {
SCOPED_TEST(HTTPNetworkStatusChange)
using namespace mbgl;
util::RunLoop loop;
OnlineFileSource fs;
const Resource resource { Resource::Unknown, "http://127.0.0.1:3000/delayed" };
// This request takes 200 milliseconds to answer.
std::unique_ptr<AsyncRequest> req = fs.request(resource, [&](Response res) {
req.reset();
EXPECT_EQ(nullptr, res.error);
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Response", *res.data);
EXPECT_FALSE(bool(res.expires));
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
HTTPNetworkStatusChange.finish();
});
// After 50 milliseconds, we're going to trigger a NetworkStatus change.
util::Timer reachableTimer;
reachableTimer.start(Milliseconds(50), Duration::zero(), [] () {
mbgl::NetworkStatus::Reachable();
});
loop.run();
}
// Tests that a change in network status preempts requests that failed due to connection or
// reachability issues.
TEST_F(Storage, TEST_REQUIRES_SERVER(HTTPNetworkStatusChangePreempt)) {
SCOPED_TEST(HTTPNetworkStatusChangePreempt)
using namespace mbgl;
util::RunLoop loop;
OnlineFileSource fs;
const auto start = Clock::now();
const Resource resource{ Resource::Unknown, "http://127.0.0.1:3001/test" };
std::unique_ptr<AsyncRequest> req = fs.request(resource, [&](Response res) {
static int counter = 0;
const auto duration = std::chrono::duration<const double>(Clock::now() - start).count();
if (counter == 0) {
EXPECT_GT(0.2, duration) << "Response came in too late";
} else if (counter == 1) {
EXPECT_LT(0.39, duration) << "Preempted retry triggered too early";
EXPECT_GT(0.6, duration) << "Preempted retry triggered too late";
} else if (counter > 1) {
FAIL() << "Retried too often";
}
ASSERT_NE(nullptr, res.error);
EXPECT_EQ(Response::Error::Reason::Connection, res.error->reason);
ASSERT_FALSE(res.data.get());
EXPECT_FALSE(bool(res.expires));
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
if (counter++ == 1) {
req.reset();
loop.stop();
HTTPNetworkStatusChangePreempt.finish();
}
});
// After 400 milliseconds, we're going to trigger a NetworkStatus change.
util::Timer reachableTimer;
reachableTimer.start(Milliseconds(400), Duration::zero(), [] () {
mbgl::NetworkStatus::Reachable();
});
loop.run();
}
TEST_F(Storage, TEST_REQUIRES_SERVER(HTTPNetworkStatusOnlineOffline)) {
SCOPED_TEST(HTTPNetworkStatusOnlineOffline)
using namespace mbgl;
util::RunLoop loop;
OnlineFileSource fs;
const Resource resource { Resource::Unknown, "http://127.0.0.1:3000/test" };
EXPECT_EQ(NetworkStatus::Get(), NetworkStatus::Status::Online) << "Default status should be Online";
NetworkStatus::Set(NetworkStatus::Status::Offline);
util::Timer onlineTimer;
onlineTimer.start(Milliseconds(100), Duration::zero(), [&] () {
NetworkStatus::Set(NetworkStatus::Status::Online);
});
std::unique_ptr<AsyncRequest> req = fs.request(resource, [&](Response res) {
req.reset();
EXPECT_EQ(nullptr, res.error);
ASSERT_TRUE(res.data.get());
EXPECT_EQ(NetworkStatus::Get(), NetworkStatus::Status::Online) << "Triggered before set back to Online";
loop.stop();
HTTPNetworkStatusOnlineOffline.finish();
});
loop.run();
}

View File

@@ -1,37 +0,0 @@
#include "storage.hpp"
#include <mbgl/storage/online_file_source.hpp>
#include <mbgl/storage/network_status.hpp>
#include <mbgl/util/chrono.hpp>
#include <mbgl/util/run_loop.hpp>
TEST_F(Storage, TEST_REQUIRES_SERVER(HTTPTimeout)) {
SCOPED_TEST(HTTPTimeout)
using namespace mbgl;
util::RunLoop loop;
OnlineFileSource fs;
int counter = 0;
const Resource resource { Resource::Unknown, "http://127.0.0.1:3000/test?cachecontrol=max-age=1" };
std::unique_ptr<AsyncRequest> req = fs.request(resource, [&](Response res) {
counter++;
EXPECT_EQ(nullptr, res.error);
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Hello World!", *res.data);
EXPECT_TRUE(bool(res.expires));
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
if (counter == 4) {
req.reset();
loop.stop();
HTTPTimeout.finish();
}
});
loop.run();
EXPECT_EQ(4, counter);
}

View File

@@ -0,0 +1,361 @@
#include <mbgl/test/util.hpp>
#include <mbgl/storage/online_file_source.hpp>
#include <mbgl/storage/network_status.hpp>
#include <mbgl/util/chrono.hpp>
#include <mbgl/util/run_loop.hpp>
#include <mbgl/util/timer.hpp>
#include <gtest/gtest.h>
using namespace mbgl;
TEST(OnlineFileSource, Cancel) {
util::RunLoop loop;
OnlineFileSource fs;
fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, [&](Response) {
ADD_FAILURE() << "Callback should not be called";
});
loop.runOnce();
}
TEST(OnlineFileSource, TEST_REQUIRES_SERVER(CancelMultiple)) {
util::RunLoop loop;
OnlineFileSource fs;
const Resource resource { Resource::Unknown, "http://127.0.0.1:3000/test" };
std::unique_ptr<AsyncRequest> req2 = fs.request(resource, [&](Response) {
ADD_FAILURE() << "Callback should not be called";
});
std::unique_ptr<AsyncRequest> req = fs.request(resource, [&](Response res) {
req.reset();
EXPECT_EQ(nullptr, res.error);
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Hello World!", *res.data);
EXPECT_FALSE(bool(res.expires));
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
});
req2.reset();
loop.run();
}
TEST(OnlineFileSource, TEST_REQUIRES_SERVER(TemporaryError)) {
util::RunLoop loop;
OnlineFileSource fs;
const auto start = Clock::now();
auto req = fs.request({ Resource::Unknown, "http://127.0.0.1:3000/temporary-error" }, [&](Response res) {
static int counter = 0;
switch (counter++) {
case 0: {
const auto duration = std::chrono::duration<const double>(Clock::now() - start).count();
EXPECT_GT(0.2, duration) << "Initial error request took too long";
ASSERT_NE(nullptr, res.error);
EXPECT_EQ(Response::Error::Reason::Server, res.error->reason);
EXPECT_EQ("HTTP status code 500", res.error->message);
ASSERT_FALSE(bool(res.data));
EXPECT_FALSE(bool(res.expires));
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
} break;
case 1: {
const auto duration = std::chrono::duration<const double>(Clock::now() - start).count();
EXPECT_LT(0.99, duration) << "Backoff timer didn't wait 1 second";
EXPECT_GT(1.2, duration) << "Backoff timer fired too late";
EXPECT_EQ(nullptr, res.error);
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Hello World!", *res.data);
EXPECT_FALSE(bool(res.expires));
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
} break;
}
});
loop.run();
}
TEST(OnlineFileSource, TEST_REQUIRES_SERVER(ConnectionError)) {
util::RunLoop loop;
OnlineFileSource fs;
const auto start = Clock::now();
std::unique_ptr<AsyncRequest> req = fs.request({ Resource::Unknown, "http://127.0.0.1:3001/" }, [&](Response res) {
static int counter = 0;
static int wait = 0;
const auto duration = std::chrono::duration<const double>(Clock::now() - start).count();
EXPECT_LT(wait - 0.01, duration) << "Backoff timer didn't wait 1 second";
EXPECT_GT(wait + 0.2, duration) << "Backoff timer fired too late";
ASSERT_NE(nullptr, res.error);
EXPECT_EQ(Response::Error::Reason::Connection, res.error->reason);
ASSERT_FALSE(res.data.get());
EXPECT_FALSE(bool(res.expires));
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
if (counter == 2) {
req.reset();
loop.stop();
}
wait += (1 << counter);
counter++;
});
loop.run();
}
TEST(OnlineFileSource, TEST_REQUIRES_SERVER(Timeout)) {
util::RunLoop loop;
OnlineFileSource fs;
int counter = 0;
const Resource resource { Resource::Unknown, "http://127.0.0.1:3000/test?cachecontrol=max-age=1" };
std::unique_ptr<AsyncRequest> req = fs.request(resource, [&](Response res) {
counter++;
EXPECT_EQ(nullptr, res.error);
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Hello World!", *res.data);
EXPECT_TRUE(bool(res.expires));
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
if (counter == 4) {
req.reset();
loop.stop();
}
});
loop.run();
EXPECT_EQ(4, counter);
}
TEST(OnlineFileSource, TEST_REQUIRES_SERVER(RetryDelayOnExpiredTile)) {
util::RunLoop loop;
OnlineFileSource fs;
int counter = 0;
const Resource resource { Resource::Unknown, "http://127.0.0.1:3000/test?expires=10000" };
std::unique_ptr<AsyncRequest> req = fs.request(resource, [&](Response res) {
counter++;
EXPECT_EQ(nullptr, res.error);
EXPECT_GT(SystemClock::now(), res.expires);
});
util::Timer timer;
timer.start(Milliseconds(500), Duration::zero(), [&] () {
loop.stop();
});
loop.run();
EXPECT_EQ(1, counter);
}
TEST(OnlineFileSource, TEST_REQUIRES_SERVER(RetryOnClockSkew)) {
util::RunLoop loop;
OnlineFileSource fs;
int counter = 0;
const Resource resource { Resource::Unknown, "http://127.0.0.1:3000/clockskew" };
std::unique_ptr<AsyncRequest> req1 = fs.request(resource, [&](Response res) {
switch (counter++) {
case 0: {
EXPECT_EQ(nullptr, res.error);
EXPECT_GT(SystemClock::now(), res.expires);
} break;
case 1: {
EXPECT_EQ(nullptr, res.error);
auto now = SystemClock::now();
EXPECT_LT(now + Seconds(40), res.expires) << "Expiration not interpolated to 60s";
EXPECT_GT(now + Seconds(80), res.expires) << "Expiration not interpolated to 60s";
loop.stop();
} break;
}
});
loop.run();
}
TEST(OnlineFileSource, TEST_REQUIRES_SERVER(RespectPriorExpires)) {
util::RunLoop loop;
OnlineFileSource fs;
// Very long expiration time, should never arrive.
Resource resource1{ Resource::Unknown, "http://127.0.0.1:3000/test" };
resource1.priorExpires = SystemClock::now() + Seconds(100000);
std::unique_ptr<AsyncRequest> req1 = fs.request(resource1, [&](Response) {
FAIL() << "Should never be called";
});
// No expiration time, should be requested immediately.
Resource resource2{ Resource::Unknown, "http://127.0.0.1:3000/test" };
std::unique_ptr<AsyncRequest> req2 = fs.request(resource2, [&](Response) {
loop.stop();
});
// Very long expiration time, should never arrive.
Resource resource3{ Resource::Unknown, "http://127.0.0.1:3000/test" };
resource3.priorExpires = SystemClock::now() + Seconds(100000);
std::unique_ptr<AsyncRequest> req3 = fs.request(resource3, [&](Response) {
FAIL() << "Should never be called";
});
loop.run();
}
TEST(OnlineFileSource, TEST_REQUIRES_SERVER(Load)) {
util::RunLoop loop;
OnlineFileSource fs;
const int concurrency = 50;
const int max = 10000;
int number = 1;
std::unique_ptr<AsyncRequest> reqs[concurrency];
std::function<void(int)> req = [&](int i) {
const auto current = number++;
reqs[i] = fs.request({ Resource::Unknown,
std::string("http://127.0.0.1:3000/load/") + std::to_string(current) },
[&, i, current](Response res) {
reqs[i].reset();
EXPECT_EQ(nullptr, res.error);
ASSERT_TRUE(res.data.get());
EXPECT_EQ(std::string("Request ") + std::to_string(current), *res.data);
EXPECT_FALSE(bool(res.expires));
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
if (number <= max) {
req(i);
} else if (current == max) {
loop.stop();
}
});
};
for (int i = 0; i < concurrency; i++) {
req(i);
}
loop.run();
}
// Test for https://github.com/mapbox/mapbox-gl-native/issues/2123
//
// A request is made. While the request is in progress, the network status changes. This should
// trigger an immediate retry of all requests that are not in progress. This test makes sure that
// we don't accidentally double-trigger the request.
TEST(OnlineFileSource, TEST_REQUIRES_SERVER(NetworkStatusChange)) {
util::RunLoop loop;
OnlineFileSource fs;
const Resource resource { Resource::Unknown, "http://127.0.0.1:3000/delayed" };
// This request takes 200 milliseconds to answer.
std::unique_ptr<AsyncRequest> req = fs.request(resource, [&](Response res) {
req.reset();
EXPECT_EQ(nullptr, res.error);
ASSERT_TRUE(res.data.get());
EXPECT_EQ("Response", *res.data);
EXPECT_FALSE(bool(res.expires));
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
loop.stop();
});
// After 50 milliseconds, we're going to trigger a NetworkStatus change.
util::Timer reachableTimer;
reachableTimer.start(Milliseconds(50), Duration::zero(), [] () {
mbgl::NetworkStatus::Reachable();
});
loop.run();
}
// Tests that a change in network status preempts requests that failed due to connection or
// reachability issues.
TEST(OnlineFileSource, TEST_REQUIRES_SERVER(NetworkStatusChangePreempt)) {
util::RunLoop loop;
OnlineFileSource fs;
const auto start = Clock::now();
const Resource resource{ Resource::Unknown, "http://127.0.0.1:3001/test" };
std::unique_ptr<AsyncRequest> req = fs.request(resource, [&](Response res) {
static int counter = 0;
const auto duration = std::chrono::duration<const double>(Clock::now() - start).count();
if (counter == 0) {
EXPECT_GT(0.2, duration) << "Response came in too late";
} else if (counter == 1) {
EXPECT_LT(0.39, duration) << "Preempted retry triggered too early";
EXPECT_GT(0.6, duration) << "Preempted retry triggered too late";
} else if (counter > 1) {
FAIL() << "Retried too often";
}
ASSERT_NE(nullptr, res.error);
EXPECT_EQ(Response::Error::Reason::Connection, res.error->reason);
ASSERT_FALSE(res.data.get());
EXPECT_FALSE(bool(res.expires));
EXPECT_FALSE(bool(res.modified));
EXPECT_FALSE(bool(res.etag));
if (counter++ == 1) {
req.reset();
loop.stop();
}
});
// After 400 milliseconds, we're going to trigger a NetworkStatus change.
util::Timer reachableTimer;
reachableTimer.start(Milliseconds(400), Duration::zero(), [] () {
mbgl::NetworkStatus::Reachable();
});
loop.run();
}
TEST(OnlineFileSource, TEST_REQUIRES_SERVER(NetworkStatusOnlineOffline)) {
util::RunLoop loop;
OnlineFileSource fs;
const Resource resource { Resource::Unknown, "http://127.0.0.1:3000/test" };
EXPECT_EQ(NetworkStatus::Get(), NetworkStatus::Status::Online) << "Default status should be Online";
NetworkStatus::Set(NetworkStatus::Status::Offline);
util::Timer onlineTimer;
onlineTimer.start(Milliseconds(100), Duration::zero(), [&] () {
NetworkStatus::Set(NetworkStatus::Status::Online);
});
std::unique_ptr<AsyncRequest> req = fs.request(resource, [&](Response res) {
req.reset();
EXPECT_EQ(nullptr, res.error);
ASSERT_TRUE(res.data.get());
EXPECT_EQ(NetworkStatus::Get(), NetworkStatus::Status::Online) << "Triggered before set back to Online";
loop.stop();
});
loop.run();
}

View File

@@ -1,13 +0,0 @@
#include "storage.hpp"
std::unique_ptr<mbgl::test::Server> Storage::server;
void Storage::SetUpTestCase() {
#if TEST_HAS_SERVER
server = std::make_unique<mbgl::test::Server>("test/storage/server.js");
#endif
}
void Storage::TearDownTestCase() {
server.reset();
}

View File

@@ -1,17 +0,0 @@
#ifndef MBGL_TEST_STORAGE_STORAGE
#define MBGL_TEST_STORAGE_STORAGE
#include <mbgl/test/util.hpp>
#include <mbgl/storage/response.hpp>
#include <memory>
class Storage : public testing::Test {
public:
static void SetUpTestCase();
static void TearDownTestCase();
protected:
static std::unique_ptr<mbgl::test::Server> server;
};
#endif

View File

@@ -45,24 +45,14 @@
'map/tile.cpp',
'map/transform.cpp',
'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/default_file_source.cpp',
'storage/http_file_source.cpp',
'storage/online_file_source.cpp',
'storage/headers.cpp',
'storage/http_cancel.cpp',
'storage/http_error.cpp',
'storage/http_expires.cpp',
'storage/http_header_parsing.cpp',
'storage/http_issue_1369.cpp',
'storage/http_load.cpp',
'storage/http_other_loop.cpp',
'storage/http_retry_network_status.cpp',
'storage/http_reading.cpp',
'storage/http_timeout.cpp',
'storage/resource.cpp',
'style/glyph_store.cpp',