summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJuha Alanen <juha.alanen@mapbox.com>2020-01-14 15:25:36 +0200
committerJuha Alanen <juha.alanen@mapbox.com>2020-01-30 13:52:09 +0200
commitb0e16ec9cb353150355580aa544e4284fe11b8fe (patch)
tree537b687f636842b0c16882cd3bfc75af4322f4b0
parentad18b0d7894add6020c052ae562feefee1e9452f (diff)
downloadqtlocation-mapboxgl-b0e16ec9cb353150355580aa544e4284fe11b8fe.tar.gz
[test] Switch to C++ HTTP server
-rw-r--r--next/test/CMakeLists.txt21
-rw-r--r--test/include/mbgl/test/util.hpp15
-rw-r--r--test/src/mbgl/test/http_server.cpp199
-rw-r--r--test/src/mbgl/test/test.cpp2
-rw-r--r--test/test-files.json1
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",