diff options
author | Juha Alanen <juha.alanen@mapbox.com> | 2020-01-14 15:25:36 +0200 |
---|---|---|
committer | Juha Alanen <juha.alanen@mapbox.com> | 2020-01-30 13:52:09 +0200 |
commit | b0e16ec9cb353150355580aa544e4284fe11b8fe (patch) | |
tree | 537b687f636842b0c16882cd3bfc75af4322f4b0 | |
parent | ad18b0d7894add6020c052ae562feefee1e9452f (diff) | |
download | qtlocation-mapboxgl-b0e16ec9cb353150355580aa544e4284fe11b8fe.tar.gz |
[test] Switch to C++ HTTP server
-rw-r--r-- | next/test/CMakeLists.txt | 21 | ||||
-rw-r--r-- | test/include/mbgl/test/util.hpp | 15 | ||||
-rw-r--r-- | test/src/mbgl/test/http_server.cpp | 199 | ||||
-rw-r--r-- | test/src/mbgl/test/test.cpp | 2 | ||||
-rw-r--r-- | test/test-files.json | 1 |
5 files changed, 232 insertions, 6 deletions
diff --git a/next/test/CMakeLists.txt b/next/test/CMakeLists.txt index cb0519ade9..dc6ea3987c 100644 --- a/next/test/CMakeLists.txt +++ b/next/test/CMakeLists.txt @@ -110,15 +110,26 @@ add_library( ${MBGL_ROOT}/test/util/url.test.cpp ) -find_program(MBGL_TEST_NODEJS NAMES nodejs node) -find_program(MBGL_TEST_NPM NAMES npm) - -if(WIN32 OR CMAKE_SYSTEM_NAME STREQUAL Android OR NOT MBGL_TEST_NODEJS OR NOT MBGL_TEST_NPM) +if(WIN32 OR CMAKE_SYSTEM_NAME STREQUAL Android) message("Target platform does not support HTTP tests or dependencies not found.") set(MBGL_TEST_HAS_TEST_SERVER 0) else() set(MBGL_TEST_HAS_TEST_SERVER 1) + target_sources( + mbgl-test + PRIVATE ${MBGL_ROOT}/test/src/mbgl/test/http_server.cpp + ) + set_source_files_properties( + ${MBGL_ROOT}/test/src/mbgl/test/http_server.cpp + PROPERTIES + COMPILE_FLAGS + -Wno-shadow + ) + target_include_directories( + mbgl-test + PRIVATE ${MBGL_ROOT}/vendor/cpp-httplib + ) endif() if(NOT DEFINED ENV{CI}) @@ -129,7 +140,7 @@ endif() target_compile_definitions( mbgl-test - PRIVATE NODE_EXECUTABLE=${MBGL_TEST_NODEJS} TEST_HAS_SERVER=${MBGL_TEST_HAS_TEST_SERVER} CI_BUILD=${MBGL_TEST_BUILD_ON_CI} + PRIVATE TEST_HAS_SERVER=${MBGL_TEST_HAS_TEST_SERVER} CI_BUILD=${MBGL_TEST_BUILD_ON_CI} ) target_include_directories( diff --git a/test/include/mbgl/test/util.hpp b/test/include/mbgl/test/util.hpp index 9f56841dcc..b499b5750c 100644 --- a/test/include/mbgl/test/util.hpp +++ b/test/include/mbgl/test/util.hpp @@ -54,9 +54,14 @@ #include <cstdint> #include <memory> +#include <thread> #include <gtest/gtest.h> +namespace httplib { +class Server; +} + namespace mbgl { namespace test { @@ -69,6 +74,16 @@ private: int fd = -1; }; +class HttpServer { +public: + HttpServer(); + ~HttpServer(); + +private: + std::unique_ptr<httplib::Server> server; + std::thread serverThread; +}; + void checkImage(const std::string& base, const PremultipliedImage& actual, double imageThreshold = 0, diff --git a/test/src/mbgl/test/http_server.cpp b/test/src/mbgl/test/http_server.cpp new file mode 100644 index 0000000000..2b72bb2c94 --- /dev/null +++ b/test/src/mbgl/test/http_server.cpp @@ -0,0 +1,199 @@ +#include <mbgl/test/util.hpp> + +#include <mbgl/util/chrono.hpp> +#include <mbgl/util/logging.hpp> + +// Limit to 4 threads (default is the nbr of CPU cores) +#define CPPHTTPLIB_THREAD_POOL_COUNT 4 +#include <httplib.h> + +#include <atomic> + +namespace mbgl { +namespace test { + +void runServer(std::unique_ptr<httplib::Server>& server) { + using namespace httplib; + + server->Get("/test", [](const Request& req, Response& res) { + if (req.has_param("modified")) { + std::string str = util::rfc1123(util::parseTimestamp(std::stoi(req.get_param_value("modified")))); + res.set_header("Last-Modified", str); + } + if (req.has_param("expires")) { + std::string str = util::rfc1123(util::parseTimestamp(std::stoi(req.get_param_value("expires")))); + res.set_header("Expires", str); + } + if (req.has_param("etag")) { + res.set_header("ETag", req.get_param_value("etag")); + } + if (req.has_param("cachecontrol")) { + res.set_header("Cache-Control", "max-age=" + req.get_param_value("cachecontrol")); + } + res.set_content("Hello World!", "text/plain"); + }); + + server->Get("/stale", [](const Request&, Response&) { + // Never respond. + }); + + std::atomic_int cacheCounter(0); + server->Get("/cache", [&](const Request&, Response& res) { + res.set_header("Cache-Control", "max-age=30"); // Allow caching for 30 seconds + res.set_content("Response " + std::to_string(++cacheCounter), "text/plain"); + }); + + server->Get("/revalidate-same", [](const Request& req, Response& res) { + if (req.get_header_value("if-none-match") == "snowfall") { + // Second request can be cached for 1 second. + res.set_header("Cache-Control", "max-age=1, must-revalidate"); + res.status = 304; + } else { + // First request must always be revalidated. + res.set_header("ETag", "snowfall"); + res.set_header("Cache-Control", "must-revalidate"); + res.status = 200; + res.set_content("Response", "text/plain"); + } + }); + + std::atomic_bool expiredOnce(true); + server->Get("/clockskew", [&](const Request&, Response& res) { + std::string dateStr; + if (expiredOnce) { + dateStr = "jan 1 10:00:00 2010 utc"; + expiredOnce = false; + } else { + dateStr = "jan 1 10:01:00 2010 utc"; + } + res.set_header("Expires", util::rfc1123(util::parseTimestamp(dateStr.c_str()))); + res.status = 200; + res.set_content("Response", "text/plain"); + }); + + server->Get("/revalidate-modified", [&](const Request& req, Response& res) { + auto jan1 = util::parseTimestamp("jan 1 2015 utc"); + + if (req.has_header("if-modified-since")) { + auto modified_since = util::parseTimestamp(req.get_header_value("if-modified-since").c_str()); + if (modified_since >= jan1) { + res.set_header("Cache-Control", "max-age=1, must-revalidate"); + res.status = 304; + return; + } + } + + // First request must always be revalidated. + res.set_header("Last-Modified", util::rfc1123(jan1)); + res.set_header("Cache-Control", "must-revalidate"); + res.status = 200; + res.set_content("Response", "text/plain"); + }); + + std::atomic_int revalidateEtagCounter(1); + server->Get("/revalidate-etag", [&](const Request&, Response& res) { + res.set_header("ETag", "response-" + std::to_string(revalidateEtagCounter)); + res.set_header("Cache-Control", "must-revalidate"); + + res.status = 200; + res.set_content("Response " + std::to_string(revalidateEtagCounter), "text/plain"); + revalidateEtagCounter++; + }); + + server->Get("/empty-data", [](const Request&, Response& res) { res.status = 200; }); + + server->Get("/no-content", [](const Request&, Response& res) { res.status = 204; }); + + server->Get("/not-found", [](const Request&, Response& res) { + res.status = 404; + res.set_content("Not Found!", "text/plain"); + }); + + server->Get("/permanent-error", [](const Request&, Response& res) { + res.status = 500; + res.set_content("Server Error!", "text/plain"); + }); + + std::atomic_int temporaryErrorCounter(0); + server->Get("/temporary-error", [&](const Request&, Response& res) { + if (temporaryErrorCounter == 0) { + res.status = 500; + } else { + res.status = 200; + res.set_content("Hello World!", "text/plain"); + } + temporaryErrorCounter++; + }); + + server->Get("/rate-limit", [](const Request& req, Response& res) { + if (req.has_param("std")) { + res.set_header("Retry-After", "1"); + } else if (req.has_param("mbx")) { + res.set_header("x-rate-limit-reset", std::to_string(util::now().time_since_epoch().count() + 1)); + } + res.status = 429; + }); + + std::atomic_bool styleFailOnce500(true); + server->Get("/style-fail-once-500", [&](const Request&, Response& res) { + if (styleFailOnce500) { + res.status = 500; + res.set_content("Not found!", "text/plain"); + styleFailOnce500 = false; + } else { + res.status = 200; + res.set_content(R"({ "version": 8, "name": "Test Style" })", "text/plain"); + } + }); + + std::atomic_bool styleFailOnce404(true); + server->Get("/style-fail-once-404", [&](const Request&, Response& res) { + if (styleFailOnce404) { + res.status = 404; + res.set_content("Not found!", "text/plain"); + styleFailOnce404 = false; + } else { + res.status = 200; + res.set_content(R"({ "version": 8, "name": "Test Style" })", "text/plain"); + } + }); + + std::atomic_bool styleFailOnce404Cache(true); + server->Get("/style-fail-once-404-cache", [&](const Request&, Response& res) { + if (styleFailOnce404Cache) { + res.set_header("Cache-Control", "max-age=30"); + res.status = 404; + res.set_content("Not found!", "text/plain"); + styleFailOnce404Cache = false; + } else { + res.status = 200; + res.set_content(R"({ "version": 8, "name": "Teste Style" })", "text/plain"); + } + }); + + server->Get("/delayed", [](const Request&, Response& res) { + usleep(200000); + res.status = 200; + res.set_content("Response", "text/plain"); + }); + + server->Get(R"(/load/(\d+))", [](const Request req, Response& res) { + auto numbers = req.matches[1]; + res.set_content("Request " + std::string(numbers), "text/plain"); + }); + + server->listen("127.0.0.1", 3000); +} + +HttpServer::HttpServer() { + server = std::make_unique<httplib::Server>(); + serverThread = std::thread(runServer, std::ref(server)); +} + +HttpServer::~HttpServer() { + server->stop(); + serverThread.join(); +} + +} // namespace test +} // namespace mbgl diff --git a/test/src/mbgl/test/test.cpp b/test/src/mbgl/test/test.cpp index 12a3dff720..fbc67f680c 100644 --- a/test/src/mbgl/test/test.cpp +++ b/test/src/mbgl/test/test.cpp @@ -8,7 +8,7 @@ namespace mbgl { int runTests(int argc, char *argv[]) { #if TEST_HAS_SERVER - auto server = std::make_unique<test::Server>("test/storage/server.js"); + auto server = std::make_unique<test::HttpServer>(); #endif testing::InitGoogleTest(&argc, argv); diff --git a/test/test-files.json b/test/test-files.json index 3a4ad95722..dbf81f031b 100644 --- a/test/test-files.json +++ b/test/test-files.json @@ -31,6 +31,7 @@ "test/sprite/sprite_parser.test.cpp", "test/src/mbgl/test/fixture_log_observer.cpp", "test/src/mbgl/test/getrss.cpp", + "test/src/mbgl/test/http_server.cpp", "test/src/mbgl/test/sqlite3_test_fs.cpp", "test/src/mbgl/test/stub_file_source.cpp", "test/src/mbgl/test/test.cpp", |