summaryrefslogtreecommitdiff
path: root/test/storage/online_file_source.test.cpp
diff options
context:
space:
mode:
authorKonstantin Käfer <mail@kkaefer.com>2016-09-28 11:45:33 +0200
committerKonstantin Käfer <mail@kkaefer.com>2016-09-28 16:34:22 +0200
commit3f3fc7b7723698e44427e2a14a2f4906832800bf (patch)
tree5acadfa4d77817c41f612c89c93925a149cbcfc0 /test/storage/online_file_source.test.cpp
parenta8b007daa0e85ea4b1a4898fd591d55d0117cc85 (diff)
downloadqtlocation-mapboxgl-3f3fc7b7723698e44427e2a14a2f4906832800bf.tar.gz
[test] add .test.cpp suffix to test case files
Diffstat (limited to 'test/storage/online_file_source.test.cpp')
-rw-r--r--test/storage/online_file_source.test.cpp416
1 files changed, 416 insertions, 0 deletions
diff --git a/test/storage/online_file_source.test.cpp b/test/storage/online_file_source.test.cpp
new file mode 100644
index 0000000000..b832f9c339
--- /dev/null
+++ b/test/storage/online_file_source.test.cpp
@@ -0,0 +1,416 @@
+#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 <mbgl/util/constants.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(util::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(util::now(), res.expires);
+ } break;
+ case 1: {
+ EXPECT_EQ(nullptr, res.error);
+
+ auto now = util::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 = util::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 = util::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();
+}
+
+TEST(OnlineFileSource, TEST_REQUIRES_SERVER(RateLimitStandard)) {
+ util::RunLoop loop;
+ OnlineFileSource fs;
+
+ auto req = fs.request({ Resource::Unknown, "http://127.0.0.1:3000/rate-limit?std=true" }, [&](Response res) {
+ ASSERT_NE(nullptr, res.error);
+ EXPECT_EQ(Response::Error::Reason::RateLimit, res.error->reason);
+ ASSERT_EQ(true, bool(res.error->retryAfter));
+ ASSERT_LT(util::now(), res.error->retryAfter);
+ loop.stop();
+ });
+
+ loop.run();
+}
+
+TEST(OnlineFileSource, TEST_REQUIRES_SERVER(RateLimitMBX)) {
+ util::RunLoop loop;
+ OnlineFileSource fs;
+
+ auto req = fs.request({ Resource::Unknown, "http://127.0.0.1:3000/rate-limit?mbx=true" }, [&](Response res) {
+ ASSERT_NE(nullptr, res.error);
+ EXPECT_EQ(Response::Error::Reason::RateLimit, res.error->reason);
+ ASSERT_EQ(true, bool(res.error->retryAfter));
+ ASSERT_LT(util::now(), res.error->retryAfter);
+ loop.stop();
+ });
+
+ loop.run();
+}
+
+TEST(OnlineFileSource, TEST_REQUIRES_SERVER(RateLimitDefault)) {
+ util::RunLoop loop;
+ OnlineFileSource fs;
+
+ auto req = fs.request({ Resource::Unknown, "http://127.0.0.1:3000/rate-limit" }, [&](Response res) {
+ ASSERT_NE(nullptr, res.error);
+ EXPECT_EQ(Response::Error::Reason::RateLimit, res.error->reason);
+ ASSERT_EQ(false, bool(res.error->retryAfter));
+ loop.stop();
+ });
+
+ loop.run();
+}
+
+TEST(OnlineFileSource, ChangeAPIBaseURL){
+ util::RunLoop loop;
+ OnlineFileSource fs;
+
+ EXPECT_EQ(mbgl::util::API_BASE_URL, fs.getAPIBaseURL());
+ const std::string customURL = "test.domain";
+ fs.setAPIBaseURL(customURL);
+ EXPECT_EQ(customURL, fs.getAPIBaseURL());
+} \ No newline at end of file