From 7de9e196d776e5be972d2e75fc5b290b5e01b33c Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Thu, 24 Mar 2016 15:28:13 -0700 Subject: [tests] Rationalize storage tests Properly divide Storage tests into DefaultFileSource, OnlineFileSource, and HTTPFileSource tests. --- test/storage/asset_file_source.cpp | 111 ++++----- test/storage/default_file_source.cpp | 68 +++--- test/storage/headers.cpp | 4 +- test/storage/http_cancel.cpp | 55 ----- test/storage/http_error.cpp | 87 ------- test/storage/http_expires.cpp | 105 --------- test/storage/http_file_source.cpp | 195 ++++++++++++++++ test/storage/http_header_parsing.cpp | 56 ----- test/storage/http_issue_1369.cpp | 44 ---- test/storage/http_load.cpp | 49 ---- test/storage/http_other_loop.cpp | 30 --- test/storage/http_reading.cpp | 181 --------------- test/storage/http_retry_network_status.cpp | 125 ---------- test/storage/http_timeout.cpp | 37 --- test/storage/online_file_source.cpp | 361 +++++++++++++++++++++++++++++ test/storage/storage.cpp | 13 -- test/storage/storage.hpp | 17 -- 17 files changed, 639 insertions(+), 899 deletions(-) delete mode 100644 test/storage/http_cancel.cpp delete mode 100644 test/storage/http_error.cpp delete mode 100644 test/storage/http_expires.cpp create mode 100644 test/storage/http_file_source.cpp delete mode 100644 test/storage/http_header_parsing.cpp delete mode 100644 test/storage/http_issue_1369.cpp delete mode 100644 test/storage/http_load.cpp delete mode 100644 test/storage/http_other_loop.cpp delete mode 100644 test/storage/http_reading.cpp delete mode 100644 test/storage/http_retry_network_status.cpp delete mode 100644 test/storage/http_timeout.cpp create mode 100644 test/storage/online_file_source.cpp delete mode 100644 test/storage/storage.cpp delete mode 100644 test/storage/storage.hpp (limited to 'test/storage') diff --git a/test/storage/asset_file_source.cpp b/test/storage/asset_file_source.cpp index c6a25f83dd..4a9866601a 100644 --- a/test/storage/asset_file_source.cpp +++ b/test/storage/asset_file_source.cpp @@ -1,11 +1,11 @@ -#include "storage.hpp" - #include #include #include #include #include +#include + 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 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 request; - - std::function 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 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 request; + + std::function requestCallback; + }; + std::vector>> threads; std::vector> 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(); diff --git a/test/storage/default_file_source.cpp b/test/storage/default_file_source.cpp index a3453a83bc..8061470fca 100644 --- a/test/storage/default_file_source.cpp +++ b/test/storage/default_file_source.cpp @@ -1,15 +1,10 @@ -#include "storage.hpp" - +#include #include #include -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(); +} diff --git a/test/storage/headers.cpp b/test/storage/headers.cpp index 3ef4fe25e5..b879c43b12 100644 --- a/test/storage/headers.cpp +++ b/test/storage/headers.cpp @@ -1,9 +1,7 @@ -#include "storage.hpp" #include - #include -TEST_F(Storage, HTTPHeaderParsing) { +TEST(HTTPHeader, Parsing) { using namespace mbgl; http::CacheControl cc; diff --git a/test/storage/http_cancel.cpp b/test/storage/http_cancel.cpp deleted file mode 100644 index bda274c547..0000000000 --- a/test/storage/http_cancel.cpp +++ /dev/null @@ -1,55 +0,0 @@ -#include "storage.hpp" - -#include -#include -#include -#include - -#include - -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 req2 = fs.request(resource, [&](Response) { - ADD_FAILURE() << "Callback should not be called"; - }); - std::unique_ptr 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(); -} diff --git a/test/storage/http_error.cpp b/test/storage/http_error.cpp deleted file mode 100644 index dfe4a55973..0000000000 --- a/test/storage/http_error.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include "storage.hpp" - -#include -#include -#include -#include - -#include - -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 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(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(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 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(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(); -} diff --git a/test/storage/http_expires.cpp b/test/storage/http_expires.cpp deleted file mode 100644 index 787d3e540b..0000000000 --- a/test/storage/http_expires.cpp +++ /dev/null @@ -1,105 +0,0 @@ -#include "storage.hpp" - -#include -#include -#include -#include -#include - -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 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 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 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 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 req3 = fs.request(resource3, [&](Response) { - FAIL() << "Should never be called"; - }); - - loop.run(); -} diff --git a/test/storage/http_file_source.cpp b/test/storage/http_file_source.cpp new file mode 100644 index 0000000000..53d9a248c2 --- /dev/null +++ b/test/storage/http_file_source.cpp @@ -0,0 +1,195 @@ +#include +#include +#include +#include +#include + +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 reqs[concurrency]; + + std::function 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(); +} diff --git a/test/storage/http_header_parsing.cpp b/test/storage/http_header_parsing.cpp deleted file mode 100644 index cdebe8db5d..0000000000 --- a/test/storage/http_header_parsing.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include "storage.hpp" - -#include -#include -#include - -#include - -TEST_F(Storage, TEST_REQUIRES_SERVER(HTTPExpiresParsing)) { - SCOPED_TEST(HTTPExpiresTest) - - using namespace mbgl; - - util::RunLoop loop; - OnlineFileSource fs; - - std::unique_ptr 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 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(); -} diff --git a/test/storage/http_issue_1369.cpp b/test/storage/http_issue_1369.cpp deleted file mode 100644 index d6890953fa..0000000000 --- a/test/storage/http_issue_1369.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "storage.hpp" - -#include -#include - -// 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(); -} diff --git a/test/storage/http_load.cpp b/test/storage/http_load.cpp deleted file mode 100644 index 4feb790cbc..0000000000 --- a/test/storage/http_load.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include "storage.hpp" - -#include -#include -#include - -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 reqs[concurrency]; - - std::function 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(); -} diff --git a/test/storage/http_other_loop.cpp b/test/storage/http_other_loop.cpp deleted file mode 100644 index 20e7e2356a..0000000000 --- a/test/storage/http_other_loop.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include "storage.hpp" - -#include -#include -#include - -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 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(); -} diff --git a/test/storage/http_reading.cpp b/test/storage/http_reading.cpp deleted file mode 100644 index 9682ec93cf..0000000000 --- a/test/storage/http_reading.cpp +++ /dev/null @@ -1,181 +0,0 @@ -#include "storage.hpp" - -#include -#include -#include -#include -#include - -#include - -TEST_F(Storage, TEST_REQUIRES_SERVER(HTTPTest)) { - SCOPED_TEST(HTTPTest) - - using namespace mbgl; - - util::RunLoop loop; - OnlineFileSource fs; - - std::unique_ptr 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 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 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 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 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 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(); -} diff --git a/test/storage/http_retry_network_status.cpp b/test/storage/http_retry_network_status.cpp deleted file mode 100644 index 94a2bcdc4d..0000000000 --- a/test/storage/http_retry_network_status.cpp +++ /dev/null @@ -1,125 +0,0 @@ -#include "storage.hpp" - -#include -#include -#include -#include -#include - -// 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 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 req = fs.request(resource, [&](Response res) { - static int counter = 0; - const auto duration = std::chrono::duration(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 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(); -} diff --git a/test/storage/http_timeout.cpp b/test/storage/http_timeout.cpp deleted file mode 100644 index 42c7f39f56..0000000000 --- a/test/storage/http_timeout.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#include "storage.hpp" - -#include -#include -#include -#include - -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 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); -} diff --git a/test/storage/online_file_source.cpp b/test/storage/online_file_source.cpp new file mode 100644 index 0000000000..dced95c196 --- /dev/null +++ b/test/storage/online_file_source.cpp @@ -0,0 +1,361 @@ +#include +#include +#include +#include +#include +#include + +#include + +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 req2 = fs.request(resource, [&](Response) { + ADD_FAILURE() << "Callback should not be called"; + }); + std::unique_ptr 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(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(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 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(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 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 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 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 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 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 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 reqs[concurrency]; + + std::function 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 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 req = fs.request(resource, [&](Response res) { + static int counter = 0; + const auto duration = std::chrono::duration(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 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(); +} diff --git a/test/storage/storage.cpp b/test/storage/storage.cpp deleted file mode 100644 index d9a414beb7..0000000000 --- a/test/storage/storage.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "storage.hpp" - -std::unique_ptr Storage::server; - -void Storage::SetUpTestCase() { -#if TEST_HAS_SERVER - server = std::make_unique("test/storage/server.js"); -#endif -} - -void Storage::TearDownTestCase() { - server.reset(); -} diff --git a/test/storage/storage.hpp b/test/storage/storage.hpp deleted file mode 100644 index e7cd960546..0000000000 --- a/test/storage/storage.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef MBGL_TEST_STORAGE_STORAGE -#define MBGL_TEST_STORAGE_STORAGE - -#include -#include -#include - -class Storage : public testing::Test { -public: - static void SetUpTestCase(); - static void TearDownTestCase(); - -protected: - static std::unique_ptr server; -}; - -#endif -- cgit v1.2.1