summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/comparisons.cpp2
-rw-r--r--test/fixtures/fixture_log.cpp47
-rw-r--r--test/fixtures/fixture_log.hpp59
-rw-r--r--test/fixtures/server_environment.hpp51
-rw-r--r--test/fixtures/storage/empty0
-rw-r--r--test/headless/headless.cpp (renamed from test/headless.cpp)114
-rw-r--r--test/headless/headless.gypi17
-rwxr-xr-xtest/headless/server.js19
-rw-r--r--test/main.cpp6
-rw-r--r--test/miscellaneous/clip_ids.cpp (renamed from test/clip_ids.cpp)2
-rw-r--r--test/miscellaneous/comparisons.cpp103
-rw-r--r--test/miscellaneous/enums.cpp (renamed from test/enums.cpp)2
-rw-r--r--test/miscellaneous/functions.cpp (renamed from test/functions.cpp)2
-rw-r--r--test/miscellaneous/merge_lines.cpp (renamed from test/merge_lines.cpp)20
-rw-r--r--test/miscellaneous/miscellaneous.gypi26
-rw-r--r--test/miscellaneous/rotation_range.cpp (renamed from test/rotation_range.cpp)2
-rw-r--r--test/miscellaneous/style_parser.cpp (renamed from test/style_parser.cpp)33
-rw-r--r--test/miscellaneous/text_conversions.cpp (renamed from test/text_conversions.cpp)2
-rw-r--r--test/miscellaneous/tile.cpp (renamed from test/tile.cpp)2
-rw-r--r--test/miscellaneous/variant.cpp (renamed from test/variant.cpp)2
-rw-r--r--test/storage/cache_response.cpp39
-rw-r--r--test/storage/cache_revalidate.cpp85
-rw-r--r--test/storage/directory_reading.cpp25
-rw-r--r--test/storage/file_reading.cpp45
-rw-r--r--test/storage/http_cancel.cpp50
-rw-r--r--test/storage/http_coalescing.cpp48
-rw-r--r--test/storage/http_error.cpp63
-rw-r--r--test/storage/http_header_parsing.cpp42
-rw-r--r--test/storage/http_load.cpp44
-rw-r--r--test/storage/http_noloop.cpp37
-rw-r--r--test/storage/http_other_loop.cpp26
-rw-r--r--test/storage/http_reading.cpp39
-rwxr-xr-xtest/storage/server.js101
-rw-r--r--test/storage/storage.gypi28
-rw-r--r--test/test.gyp308
-rw-r--r--test/util.hpp14
36 files changed, 1069 insertions, 436 deletions
diff --git a/test/comparisons.cpp b/test/comparisons.cpp
index 9b74d6e36f..0daa78ab77 100644
--- a/test/comparisons.cpp
+++ b/test/comparisons.cpp
@@ -1,5 +1,5 @@
#include <iostream>
-#include "gtest/gtest.h"
+#include "../util.hpp"
#include <mbgl/map/vector_tile.hpp>
#include <mbgl/style/filter_expression.hpp>
diff --git a/test/fixtures/fixture_log.cpp b/test/fixtures/fixture_log.cpp
deleted file mode 100644
index 1b1646e665..0000000000
--- a/test/fixtures/fixture_log.cpp
+++ /dev/null
@@ -1,47 +0,0 @@
-#include "fixture_log.hpp"
-
-#include <iostream>
-
-namespace mbgl {
-
-FixtureLogBackend::~FixtureLogBackend() {
- std::cerr << unchecked();
-}
-
-size_t FixtureLogBackend::count(const LogMessage &message) const {
- size_t message_count = 0;
- for (const LogMessage &msg : messages) {
- if (msg == message) {
- message_count++;
- msg.checked = true;
- }
- }
- return message_count;
-}
-
-std::vector<FixtureLogBackend::LogMessage> FixtureLogBackend::unchecked() const {
- std::vector<LogMessage> unchecked_messages;
- for (const LogMessage &msg : messages) {
- if (!msg.checked) {
- unchecked_messages.push_back(msg);
- msg.checked = true;
- }
- }
- return unchecked_messages;
-}
-
-::std::ostream& operator<<(::std::ostream& os, const std::vector<FixtureLogBackend::LogMessage>& messages) {
- for (const FixtureLogBackend::LogMessage &message : messages) {
- os << "- " << message;
- }
- return os;
-}
-
-::std::ostream& operator<<(::std::ostream& os, const FixtureLogBackend::LogMessage& message) {
- os << "[\"" << message.severity.get() << "\", \"" << message.event.get() << "\"";
- if (message.code) os << ", " << message.code.get();
- if (message.msg) os << ", \"" << message.msg.get() << "\"";
- return os << "]" << std::endl;
-}
-
-}
diff --git a/test/fixtures/fixture_log.hpp b/test/fixtures/fixture_log.hpp
index 99097a2c47..8df16e3086 100644
--- a/test/fixtures/fixture_log.hpp
+++ b/test/fixtures/fixture_log.hpp
@@ -7,6 +7,8 @@
#include <vector>
#include <cstdarg>
+#include <iostream>
+
namespace mbgl {
@@ -37,13 +39,13 @@ public:
mutable bool checked = false;
};
- ~FixtureLogBackend();
+ inline ~FixtureLogBackend();
- void record(EventSeverity severity, Event event, const std::string &msg) {
+ inline void record(EventSeverity severity, Event event, const std::string &msg) {
messages.emplace_back(severity, event, msg);
}
- void record(EventSeverity severity, Event event, const char* format, ...) {
+ inline void record(EventSeverity severity, Event event, const char* format, ...) {
va_list args;
va_start(args, format);
const size_t len = vsnprintf(NULL, 0, format, args);
@@ -55,24 +57,63 @@ public:
messages.emplace_back(severity, event, std::string { buffer.get(), len });
}
- void record(EventSeverity severity, Event event, int64_t code) {
+ inline void record(EventSeverity severity, Event event, int64_t code) {
messages.emplace_back(severity, event, code);
}
- void record(EventSeverity severity, Event event, int64_t code, const std::string &msg) {
+ inline void record(EventSeverity severity, Event event, int64_t code, const std::string &msg) {
messages.emplace_back(severity, event, code, msg);
}
- size_t count(const LogMessage &message) const;
- std::vector<LogMessage> unchecked() const;
+ inline size_t count(const LogMessage &message) const;
+ inline std::vector<LogMessage> unchecked() const;
public:
std::vector<LogMessage> messages;
};
-::std::ostream& operator<<(::std::ostream& os, const std::vector<FixtureLogBackend::LogMessage>& messages);
-::std::ostream& operator<<(::std::ostream& os, const FixtureLogBackend::LogMessage& message);
+inline ::std::ostream& operator<<(::std::ostream& os, const std::vector<FixtureLogBackend::LogMessage>& messages);
+inline ::std::ostream& operator<<(::std::ostream& os, const FixtureLogBackend::LogMessage& message);
+
+FixtureLogBackend::~FixtureLogBackend() {
+ std::cerr << unchecked();
+}
+
+size_t FixtureLogBackend::count(const LogMessage &message) const {
+ size_t message_count = 0;
+ for (const LogMessage &msg : messages) {
+ if (msg == message) {
+ message_count++;
+ msg.checked = true;
+ }
+ }
+ return message_count;
+}
+std::vector<FixtureLogBackend::LogMessage> FixtureLogBackend::unchecked() const {
+ std::vector<LogMessage> unchecked_messages;
+ for (const LogMessage &msg : messages) {
+ if (!msg.checked) {
+ unchecked_messages.push_back(msg);
+ msg.checked = true;
+ }
+ }
+ return unchecked_messages;
+}
+
+::std::ostream& operator<<(::std::ostream& os, const std::vector<FixtureLogBackend::LogMessage>& messages) {
+ for (const FixtureLogBackend::LogMessage &message : messages) {
+ os << "- " << message;
+ }
+ return os;
+}
+
+::std::ostream& operator<<(::std::ostream& os, const FixtureLogBackend::LogMessage& message) {
+ os << "[\"" << message.severity.get() << "\", \"" << message.event.get() << "\"";
+ if (message.code) os << ", " << message.code.get();
+ if (message.msg) os << ", \"" << message.msg.get() << "\"";
+ return os << "]" << std::endl;
+}
}
diff --git a/test/fixtures/server_environment.hpp b/test/fixtures/server_environment.hpp
new file mode 100644
index 0000000000..54dbb24bdd
--- /dev/null
+++ b/test/fixtures/server_environment.hpp
@@ -0,0 +1,51 @@
+#ifndef MBGL_TEST_FIXTURES_SERVER_ENVIRONMENT
+#define MBGL_TEST_FIXTURES_SERVER_ENVIRONMENT
+
+#include <gtest/gtest.h>
+
+#include <dirent.h>
+#include <signal.h>
+#include <libgen.h>
+
+class ServerEnvironment : public ::testing::Environment {
+public:
+ inline ServerEnvironment(const std::string &executable);
+ inline virtual void SetUp();
+ inline virtual void TearDown();
+
+private:
+ const std::string executable;
+ const std::string parent_pid = std::to_string(getpid());
+ pid_t pid = 0;
+};
+
+ServerEnvironment::ServerEnvironment(const std::string &executable_) : executable(executable_) {}
+
+void ServerEnvironment::SetUp() {
+ pid = fork();
+ if (pid < 0) {
+ throw std::runtime_error("Cannot create server process");
+ } else if (pid == 0) {
+ char *arg[] = {
+ const_cast<char *>(executable.c_str()),
+ const_cast<char *>(parent_pid.c_str()),
+ nullptr
+ };
+ int ret = execv(executable.c_str(), arg);
+ // This call should not return. In case execve failed, we exit anyway.
+ if (ret < 0) {
+ fprintf(stderr, "Failed to start server: %s\n", strerror(errno));
+ }
+ exit(0);
+ } else {
+ // Wait until the server process sends SIGCONT.
+ raise(SIGSTOP);
+ }
+}
+
+void ServerEnvironment::TearDown() {
+ ASSERT_TRUE(pid);
+ kill(pid, SIGHUP);
+}
+
+#endif \ No newline at end of file
diff --git a/test/fixtures/storage/empty b/test/fixtures/storage/empty
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/fixtures/storage/empty
diff --git a/test/headless.cpp b/test/headless/headless.cpp
index c39e5b74a0..d131254322 100644
--- a/test/headless.cpp
+++ b/test/headless/headless.cpp
@@ -1,4 +1,5 @@
-#include "gtest/gtest.h"
+#include "../util.hpp"
+#include "../fixtures/server_environment.hpp"
#include <mbgl/map/map.hpp>
#include <mbgl/util/image.hpp>
@@ -13,81 +14,9 @@
#include <mbgl/platform/default/headless_display.hpp>
#include <mbgl/storage/default/default_file_source.hpp>
-#include "./fixtures/fixture_log.hpp"
+#include "../fixtures/fixture_log.hpp"
-#include <dirent.h>
-#include <signal.h>
-#include <libgen.h>
-#ifdef __linux__
-#include <sys/prctl.h>
-#endif
-
-std::string base_directory;
-
-namespace mbgl {
-namespace platform {
-
-std::string defaultCacheDatabase() {
- // Disables the cache.
- return "";
-}
-}
-}
-
-class ServerEnvironment : public ::testing::Environment {
-public:
- virtual void SetUp() {
- pid = fork();
- if (pid < 0) {
- throw std::runtime_error("Cannot create web server");
- } else if (pid == 0) {
-#ifdef __linux__
- prctl(PR_SET_PDEATHSIG, SIGHUP);
-#endif
- const auto executable = base_directory + "bin/server.py";
- const char *port = "2900";
- char *arg[] = { const_cast<char *>(executable.c_str()), const_cast<char *>(port), nullptr };
- int ret = execv(executable.c_str(), arg);
- // This call should not return. In case execve failed, we exit anyway.
- if (ret < 0) {
- fprintf(stderr, "Failed to start server: %s\n", strerror(errno));
- }
- exit(0);
- } else {
- display = std::make_shared<mbgl::HeadlessDisplay>();
- }
- }
- virtual void TearDown() {
- ASSERT_TRUE(pid);
- kill(pid, SIGHUP);
- }
-
- std::shared_ptr<mbgl::HeadlessDisplay> display;
-
-private:
- pid_t pid = 0;
-};
-
-
-ServerEnvironment* env = nullptr;
-
-
-GTEST_API_ int main(int argc, char *argv[]) {
- // Note: glibc's dirname() **modifies** the argument and can't handle static strings.
- std::string file { __FILE__ }; file = dirname(const_cast<char *>(file.c_str()));
- if (file[0] == '/') {
- // If __FILE__ is an absolute path, we don't have to guess from the argv 0.
- base_directory = file + "/suite/";
- } else {
- std::string argv0 { argv[0] }; argv0 = dirname(const_cast<char *>(argv0.c_str()));
- base_directory = argv0 + "/" + file + "/suite/";
- }
-
- testing::InitGoogleTest(&argc, argv);
- env = new ServerEnvironment();
- ::testing::AddGlobalTestEnvironment(env);
- return RUN_ALL_TESTS();
-}
+std::shared_ptr<mbgl::HeadlessDisplay> display;
void rewriteLocalScheme(rapidjson::Value &value, rapidjson::Document::AllocatorType &allocator) {
ASSERT_TRUE(value.IsString());
@@ -105,8 +34,8 @@ TEST_P(HeadlessTest, render) {
const std::string& base = GetParam();
- std::string style = util::read_file(base_directory + "tests/" + base + "/style.json");
- std::string info = util::read_file(base_directory + "tests/" + base + "/info.json");
+ std::string style = util::read_file("test/suite/tests/" + base + "/style.json");
+ std::string info = util::read_file("test/suite/tests/" + base + "/info.json");
// Parse style.
rapidjson::Document styleDoc;
@@ -169,7 +98,7 @@ TEST_P(HeadlessTest, render) {
if (value.HasMember("center")) ASSERT_TRUE(value["center"].IsArray());
- const std::string actual_image = base_directory + "tests/" + base + "/" + name + "/actual.png";
+ const std::string actual_image = "test/suite/tests/" + base + "/" + name + "/actual.png";
const double zoom = value.HasMember("zoom") ? value["zoom"].GetDouble() : 0;
const double bearing = value.HasMember("bearing") ? value["bearing"].GetDouble() : 0;
@@ -190,19 +119,22 @@ TEST_P(HeadlessTest, render) {
}
}
- HeadlessView view(env->display);
+ if (!display) {
+ display = std::make_shared<mbgl::HeadlessDisplay>();
+ }
+
+ HeadlessView view(display);
mbgl::DefaultFileSource fileSource(nullptr);
Map map(view, fileSource);
map.setClasses(classes);
- map.setStyleJSON(style, base_directory);
+ map.setStyleJSON(style, "test/suite");
view.resize(width, height, pixelRatio);
map.resize(width, height, pixelRatio);
map.setLonLatZoom(longitude, latitude, zoom);
map.setBearing(bearing);
-
// Run the loop. It will terminate when we don't have any further listeners.
map.run();
@@ -219,19 +151,17 @@ TEST_P(HeadlessTest, render) {
INSTANTIATE_TEST_CASE_P(Headless, HeadlessTest, ::testing::ValuesIn([] {
std::vector<std::string> names;
- DIR *dir = opendir((base_directory + "tests").c_str());
- if (dir == nullptr) {
- return names;
- }
-
- for (dirent *dp = nullptr; (dp = readdir(dir)) != nullptr;) {
- const std::string name = dp->d_name;
- if (name != "index.html" && !(name.size() >= 1 && name[0] == '.')) {
- names.push_back(name);
+ DIR *dir = opendir("test/suite/tests");
+ if (dir != nullptr) {
+ for (dirent *dp = nullptr; (dp = readdir(dir)) != nullptr;) {
+ const std::string name = dp->d_name;
+ if (name != "index.html" && !(name.size() >= 1 && name[0] == '.')) {
+ names.push_back(name);
+ }
}
+ closedir(dir);
}
- closedir(dir);
-
+ EXPECT_GT(names.size(), 0ul);
return names;
}()));
diff --git a/test/headless/headless.gypi b/test/headless/headless.gypi
new file mode 100644
index 0000000000..86b40fd543
--- /dev/null
+++ b/test/headless/headless.gypi
@@ -0,0 +1,17 @@
+{
+ 'targets': [
+ { 'target_name': 'test_headless',
+ 'type': 'executable',
+ 'dependencies': [
+ 'test_base',
+ '../mapboxgl.gyp:mbgl-core',
+ '../mapboxgl.gyp:mbgl-<(platform)',
+ '../mapboxgl.gyp:mbgl-headless',
+ '../deps/gtest/gtest.gyp:gtest',
+ ],
+ 'sources': [
+ 'headless.cpp',
+ ],
+ },
+ ],
+}
diff --git a/test/headless/server.js b/test/headless/server.js
new file mode 100755
index 0000000000..bb2c451f84
--- /dev/null
+++ b/test/headless/server.js
@@ -0,0 +1,19 @@
+#!/usr/bin/env node
+/* jshint node: true */
+'use strict';
+
+var express = require('express');
+var app = express();
+
+app.use(express.static('test/suite'));
+
+var server = app.listen(2900, function () {
+ var host = server.address().address;
+ var port = server.address().port;
+ console.log('Test server listening at http://%s:%s', host, port);
+
+ if (process.argv[2]) {
+ // Allow the test to continue running.
+ process.kill(+process.argv[2], 'SIGCONT');
+ }
+});
diff --git a/test/main.cpp b/test/main.cpp
index ec813bde80..6ae9751c50 100644
--- a/test/main.cpp
+++ b/test/main.cpp
@@ -1,6 +1,10 @@
-#include "gtest/gtest.h"
+#include "util.hpp"
+#include "fixtures/server_environment.hpp"
GTEST_API_ int main(int argc, char *argv[]) {
+ testing::AddGlobalTestEnvironment(new ServerEnvironment("test/headless/server.js"));
+ testing::AddGlobalTestEnvironment(new ServerEnvironment("test/storage/server.js"));
+
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
diff --git a/test/clip_ids.cpp b/test/miscellaneous/clip_ids.cpp
index 18ef9658e5..ebd819e264 100644
--- a/test/clip_ids.cpp
+++ b/test/miscellaneous/clip_ids.cpp
@@ -1,5 +1,5 @@
#include <iostream>
-#include "gtest/gtest.h"
+#include "../util.hpp"
#include <algorithm>
diff --git a/test/miscellaneous/comparisons.cpp b/test/miscellaneous/comparisons.cpp
new file mode 100644
index 0000000000..0daa78ab77
--- /dev/null
+++ b/test/miscellaneous/comparisons.cpp
@@ -0,0 +1,103 @@
+#include <iostream>
+#include "../util.hpp"
+
+#include <mbgl/map/vector_tile.hpp>
+#include <mbgl/style/filter_expression.hpp>
+#include <mbgl/style/filter_expression_private.hpp>
+
+#include <map>
+
+using namespace mbgl;
+
+typedef std::multimap<std::string, mbgl::Value> Properties;
+
+class Extractor {
+public:
+ inline Extractor(const Properties& properties_, FeatureType type_)
+ : properties(properties_)
+ , type(type_)
+ {}
+
+ mapbox::util::optional<Value> getValue(const std::string &key) const {
+ if (key == "$type")
+ return Value(uint64_t(type));
+ auto it = properties.find(key);
+ if (it == properties.end())
+ return mapbox::util::optional<Value>();
+ return it->second;
+ }
+
+ FeatureType getType() const {
+ return type;
+ }
+
+private:
+ const Properties properties;
+ FeatureType type;
+};
+
+FilterExpression parse(const char * expression) {
+ rapidjson::Document doc;
+ doc.Parse<0>(expression);
+ return parseFilterExpression(doc);
+}
+
+bool evaluate(const FilterExpression& expression, const Properties& properties, FeatureType type = FeatureType::Unknown) {
+ return mbgl::evaluate(expression, Extractor(properties, type));
+}
+
+TEST(FilterComparison, EqualsString) {
+ FilterExpression f = parse("[\"==\", \"foo\", \"bar\"]");
+ ASSERT_TRUE(evaluate(f, {{ "foo", std::string("bar") }}));
+ ASSERT_FALSE(evaluate(f, {{ "foo", std::string("baz") }}));
+};
+
+TEST(FilterComparison, EqualsNumber) {
+ FilterExpression f = parse("[\"==\", \"foo\", 0]");
+ ASSERT_TRUE(evaluate(f, {{ "foo", int64_t(0) }}));
+ ASSERT_TRUE(evaluate(f, {{ "foo", uint64_t(0) }}));
+ ASSERT_TRUE(evaluate(f, {{ "foo", double(0) }}));
+ ASSERT_FALSE(evaluate(f, {{ "foo", int64_t(1) }}));
+ ASSERT_FALSE(evaluate(f, {{ "foo", uint64_t(1) }}));
+ ASSERT_FALSE(evaluate(f, {{ "foo", double(1) }}));
+ ASSERT_FALSE(evaluate(f, {{ "foo", std::string("0") }}));
+ ASSERT_FALSE(evaluate(f, {{ "foo", false }}));
+ ASSERT_FALSE(evaluate(f, {{ "foo", true }}));
+ ASSERT_FALSE(evaluate(f, {{}}));
+}
+
+TEST(FilterComparison, EqualsType) {
+ FilterExpression f = parse("[\"==\", \"$type\", \"LineString\"]");
+ ASSERT_FALSE(evaluate(f, {{}}, FeatureType::Point));
+ ASSERT_TRUE(evaluate(f, {{}}, FeatureType::LineString));
+}
+
+TEST(FilterComparison, Any) {
+ ASSERT_FALSE(evaluate(parse("[\"any\"]"), {{}}));
+ ASSERT_TRUE(evaluate(parse("[\"any\", [\"==\", \"foo\", 1]]"),
+ {{ std::string("foo"), int64_t(1) }}));
+ ASSERT_FALSE(evaluate(parse("[\"any\", [\"==\", \"foo\", 0]]"),
+ {{ std::string("foo"), int64_t(1) }}));
+ ASSERT_TRUE(evaluate(parse("[\"any\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]"),
+ {{ std::string("foo"), int64_t(1) }}));
+}
+
+TEST(FilterComparison, All) {
+ ASSERT_TRUE(evaluate(parse("[\"all\"]"), {{}}));
+ ASSERT_TRUE(evaluate(parse("[\"all\", [\"==\", \"foo\", 1]]"),
+ {{ std::string("foo"), int64_t(1) }}));
+ ASSERT_FALSE(evaluate(parse("[\"all\", [\"==\", \"foo\", 0]]"),
+ {{ std::string("foo"), int64_t(1) }}));
+ ASSERT_FALSE(evaluate(parse("[\"all\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]"),
+ {{ std::string("foo"), int64_t(1) }}));
+}
+
+TEST(FilterComparison, None) {
+ ASSERT_TRUE(evaluate(parse("[\"none\"]"), {{}}));
+ ASSERT_FALSE(evaluate(parse("[\"none\", [\"==\", \"foo\", 1]]"),
+ {{ std::string("foo"), int64_t(1) }}));
+ ASSERT_TRUE(evaluate(parse("[\"none\", [\"==\", \"foo\", 0]]"),
+ {{ std::string("foo"), int64_t(1) }}));
+ ASSERT_FALSE(evaluate(parse("[\"none\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]"),
+ {{ std::string("foo"), int64_t(1) }}));
+}
diff --git a/test/enums.cpp b/test/miscellaneous/enums.cpp
index b45fc0ed0d..dc71645128 100644
--- a/test/enums.cpp
+++ b/test/miscellaneous/enums.cpp
@@ -1,5 +1,5 @@
#include <iostream>
-#include "gtest/gtest.h"
+#include "../util.hpp"
#include <algorithm>
diff --git a/test/functions.cpp b/test/miscellaneous/functions.cpp
index 56b2a31706..6543a32d1f 100644
--- a/test/functions.cpp
+++ b/test/miscellaneous/functions.cpp
@@ -1,5 +1,5 @@
#include <iostream>
-#include "gtest/gtest.h"
+#include "../util.hpp"
#include <mbgl/style/function_properties.hpp>
diff --git a/test/merge_lines.cpp b/test/miscellaneous/merge_lines.cpp
index 3c887105fa..0e7328d195 100644
--- a/test/merge_lines.cpp
+++ b/test/miscellaneous/merge_lines.cpp
@@ -1,13 +1,11 @@
-#include "gtest/gtest.h"
+#include "../fixtures/util.hpp"
#include <mbgl/util/merge_lines.hpp>
+const std::u32string a = U"a";
+const std::u32string b = U"b";
-TEST(mergeLines, mergeLines) {
-
- std::u32string a = U"a";
- std::u32string b = U"b";
-
+TEST(MergeLines, SameText) {
// merges lines with the same text
std::vector<mbgl::SymbolFeature> input1 = {
{ {{{0, 0}, {1, 0}, {2, 0}}}, a, "" },
@@ -18,7 +16,7 @@ TEST(mergeLines, mergeLines) {
{ {{{5, 0}, {6, 0}}}, a, "" }
};
- std::vector<mbgl::SymbolFeature> expected1 = {
+ const std::vector<mbgl::SymbolFeature> expected1 = {
{ {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}}}, a, "" },
{ {{{4, 0}, {5, 0}, {6, 0}}}, b, "" },
{ {{{5, 0}, {6, 0}, {7, 0}, {8, 0}, {9, 0}}}, a, "" },
@@ -32,7 +30,9 @@ TEST(mergeLines, mergeLines) {
for (int i = 0; i < 6; i++) {
EXPECT_EQ(input1[i].geometry, expected1[i].geometry);
}
+}
+TEST(MergeLines, BothEnds) {
// mergeLines handles merge from both ends
std::vector<mbgl::SymbolFeature> input2 = {
{ {{{0, 0}, {1, 0}, {2, 0}}}, a, "" },
@@ -40,7 +40,7 @@ TEST(mergeLines, mergeLines) {
{ {{{2, 0}, {3, 0}, {4, 0}}}, a, "" }
};
- std::vector<mbgl::SymbolFeature> expected2 = {
+ const std::vector<mbgl::SymbolFeature> expected2 = {
{ {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0}}}, a, "" },
{ {{}}, a, "" },
{ {{}}, a, "" }
@@ -51,7 +51,9 @@ TEST(mergeLines, mergeLines) {
for (int i = 0; i < 3; i++) {
EXPECT_EQ(input2[i].geometry, expected2[i].geometry);
}
+}
+TEST(MergeLines, CircularLines) {
// mergeLines handles circular lines
std::vector<mbgl::SymbolFeature> input3 = {
{ {{{0, 0}, {1, 0}, {2, 0}}}, a, "" },
@@ -59,7 +61,7 @@ TEST(mergeLines, mergeLines) {
{ {{{4, 0}, {0, 0}}}, a, "" }
};
- std::vector<mbgl::SymbolFeature> expected3 = {
+ const std::vector<mbgl::SymbolFeature> expected3 = {
{ {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {0, 0}}}, a, "" },
{ {{}}, a, "" },
{ {{}}, a, "" }
diff --git a/test/miscellaneous/miscellaneous.gypi b/test/miscellaneous/miscellaneous.gypi
new file mode 100644
index 0000000000..023f28fa6d
--- /dev/null
+++ b/test/miscellaneous/miscellaneous.gypi
@@ -0,0 +1,26 @@
+{
+ 'targets': [
+ { 'target_name': 'test_miscellaneous',
+ 'type': 'executable',
+ 'dependencies': [
+ 'test_base',
+ '../mapboxgl.gyp:mbgl-core',
+ '../mapboxgl.gyp:mbgl-<(platform)',
+ '../mapboxgl.gyp:mbgl-headless',
+ '../deps/gtest/gtest.gyp:gtest'
+ ],
+ 'sources': [
+ 'main.cpp',
+ 'clip_ids.cpp',
+ 'comparisons.cpp',
+ 'enums.cpp',
+ 'functions.cpp',
+ 'rotation_range.cpp',
+ 'style_parser.cpp',
+ 'text_conversions.cpp',
+ 'tile.cpp',
+ 'variant.cpp',
+ ],
+ },
+ ],
+}
diff --git a/test/rotation_range.cpp b/test/miscellaneous/rotation_range.cpp
index 987d422686..3106e900f6 100644
--- a/test/rotation_range.cpp
+++ b/test/miscellaneous/rotation_range.cpp
@@ -1,5 +1,5 @@
#include <iostream>
-#include "gtest/gtest.h"
+#include "../util.hpp"
#include <algorithm>
diff --git a/test/style_parser.cpp b/test/miscellaneous/style_parser.cpp
index fe237b7888..3cef6ae614 100644
--- a/test/style_parser.cpp
+++ b/test/miscellaneous/style_parser.cpp
@@ -1,23 +1,17 @@
-#include "gtest/gtest.h"
+#include "../util.hpp"
#include <mbgl/style/style.hpp>
#include <mbgl/util/io.hpp>
#include <rapidjson/document.h>
-#include "./fixtures/fixture_log.hpp"
+#include "../fixtures/fixture_log.hpp"
#include <iostream>
#include <fstream>
#include <dirent.h>
-const std::string base_directory = []{
- std::string fn = __FILE__;
- fn.erase(fn.find_last_of("/"));
- return fn + "/fixtures/style_parser";
-}();
-
using namespace mbgl;
typedef std::pair<uint32_t, std::string> Message;
@@ -26,7 +20,7 @@ typedef std::vector<Message> Messages;
class StyleParserTest : public ::testing::TestWithParam<std::string> {};
TEST_P(StyleParserTest, ParseStyle) {
- const std::string &base = base_directory + "/" + GetParam();
+ const std::string &base = "test/fixtures/style_parser/" + GetParam();
const std::string style_path = base + ".style.json";
const std::string info = util::read_file(base + ".info.json");
@@ -83,19 +77,18 @@ INSTANTIATE_TEST_CASE_P(StyleParser, StyleParserTest, ::testing::ValuesIn([] {
std::vector<std::string> names;
const std::string ending = ".info.json";
- DIR *dir = opendir(base_directory.c_str());
- if (dir == nullptr) {
- return names;
- }
-
- for (dirent *dp = nullptr; (dp = readdir(dir)) != nullptr;) {
- const std::string name = dp->d_name;
- if (name.length() >= ending.length() && name.compare(name.length() - ending.length(), ending.length(), ending) == 0) {
- names.push_back(name.substr(0, name.length() - ending.length()));
+ const std::string style_directory = "test/fixtures/style_parser";
+ DIR *dir = opendir(style_directory.c_str());
+ if (dir != nullptr) {
+ for (dirent *dp = nullptr; (dp = readdir(dir)) != nullptr;) {
+ const std::string name = dp->d_name;
+ if (name.length() >= ending.length() && name.compare(name.length() - ending.length(), ending.length(), ending) == 0) {
+ names.push_back(name.substr(0, name.length() - ending.length()));
+ }
}
+ closedir(dir);
}
- closedir(dir);
-
+ EXPECT_GT(names.size(), 0ul);
return names;
}()));
diff --git a/test/text_conversions.cpp b/test/miscellaneous/text_conversions.cpp
index 756bc4db1f..4ea62b4686 100644
--- a/test/text_conversions.cpp
+++ b/test/miscellaneous/text_conversions.cpp
@@ -1,5 +1,5 @@
#include <iostream>
-#include "gtest/gtest.h"
+#include "../util.hpp"
#include <mbgl/util/utf.hpp>
#include <mbgl/platform/platform.hpp>
diff --git a/test/tile.cpp b/test/miscellaneous/tile.cpp
index 8a580f55a9..70ffd1ecd8 100644
--- a/test/tile.cpp
+++ b/test/miscellaneous/tile.cpp
@@ -1,5 +1,5 @@
#include <iostream>
-#include "gtest/gtest.h"
+#include "../util.hpp"
#include <mbgl/map/tile.hpp>
diff --git a/test/variant.cpp b/test/miscellaneous/variant.cpp
index e4caba9f2d..979d73925e 100644
--- a/test/variant.cpp
+++ b/test/miscellaneous/variant.cpp
@@ -1,5 +1,5 @@
#include <iostream>
-#include "gtest/gtest.h"
+#include "../util.hpp"
#include <mbgl/style/value.hpp>
#include <mbgl/style/value_comparison.hpp>
diff --git a/test/storage/cache_response.cpp b/test/storage/cache_response.cpp
new file mode 100644
index 0000000000..c775647f77
--- /dev/null
+++ b/test/storage/cache_response.cpp
@@ -0,0 +1,39 @@
+#include "../util.hpp"
+
+#include <uv.h>
+
+#include <mbgl/storage/default/default_file_source.hpp>
+#include <mbgl/storage/default/sqlite_cache.hpp>
+
+TEST(Storage, CacheResponse) {
+ SCOPED_TEST(CacheResponse);
+
+ using namespace mbgl;
+
+ SQLiteCache cache(":memory:");
+ DefaultFileSource fs(&cache, uv_default_loop());
+
+ const Resource resource { Resource::Unknown, "http://127.0.0.1:3000/cache" };
+
+ fs.request(resource, uv_default_loop(), [&](const Response &res) {
+ EXPECT_EQ(res.status, Response::Successful);
+ EXPECT_EQ(res.data, "Response 1");
+ EXPECT_GT(res.expires, 0);
+ EXPECT_EQ(res.modified, 0);
+ EXPECT_EQ(res.etag, "");
+ EXPECT_EQ(res.message, "");
+
+ fs.request(resource, uv_default_loop(), [&, res](const Response &res2) {
+ EXPECT_EQ(res2.status, res.status);
+ EXPECT_EQ(res2.data, res.data);
+ EXPECT_EQ(res2.expires, res.expires);
+ EXPECT_EQ(res2.modified, res.modified);
+ EXPECT_EQ(res2.etag, res.etag);
+ EXPECT_EQ(res2.message, res.message);
+
+ CacheResponse.finish();
+ });
+ });
+
+ uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+}
diff --git a/test/storage/cache_revalidate.cpp b/test/storage/cache_revalidate.cpp
new file mode 100644
index 0000000000..c2bbcbc2a4
--- /dev/null
+++ b/test/storage/cache_revalidate.cpp
@@ -0,0 +1,85 @@
+#include "../util.hpp"
+
+#include <uv.h>
+
+#include <mbgl/storage/default/default_file_source.hpp>
+#include <mbgl/storage/default/sqlite_cache.hpp>
+
+TEST(Storage, CacheRevalidate) {
+ SCOPED_TEST(CacheRevalidateSame)
+ SCOPED_TEST(CacheRevalidateModified)
+ SCOPED_TEST(CacheRevalidateEtag)
+
+ using namespace mbgl;
+
+ SQLiteCache cache(":memory:");
+ DefaultFileSource fs(&cache);
+
+ const Resource revalidateSame { Resource::Unknown, "http://127.0.0.1:3000/revalidate-same" };
+ fs.request(revalidateSame, uv_default_loop(), [&](const Response &res) {
+ EXPECT_EQ(res.status, Response::Successful);
+ EXPECT_EQ(res.data, "Response");
+ EXPECT_EQ(res.expires, 0);
+ EXPECT_EQ(res.modified, 0);
+ EXPECT_EQ(res.etag, "snowfall");
+ EXPECT_EQ(res.message, "");
+
+ fs.request(revalidateSame, uv_default_loop(), [&, res](const Response &res2) {
+ EXPECT_EQ(res2.status, Response::Successful);
+ EXPECT_EQ(res2.data, "Response");
+ // We use this to indicate that a 304 reply came back.
+ EXPECT_GT(res2.expires, 0);
+ EXPECT_EQ(res2.modified, 0);
+ // We're not sending the ETag in the 304 reply, but it should still be there.
+ EXPECT_EQ(res2.etag, "snowfall");
+ EXPECT_EQ(res2.message, "");
+
+ CacheRevalidateSame.finish();
+ });
+ });
+
+ const Resource revalidateModified { Resource::Unknown, "http://127.0.0.1:3000/revalidate-modified" };
+ fs.request(revalidateModified, uv_default_loop(), [&](const Response &res) {
+ EXPECT_EQ(res.status, Response::Successful);
+ EXPECT_EQ(res.data, "Response");
+ EXPECT_EQ(res.expires, 0);
+ EXPECT_EQ(res.modified, 1420070400);
+ EXPECT_EQ(res.etag, "");
+ EXPECT_EQ(res.message, "");
+
+ fs.request(revalidateModified, uv_default_loop(), [&, res](const Response &res2) {
+ EXPECT_EQ(res2.status, Response::Successful);
+ EXPECT_EQ(res2.data, "Response");
+ // We use this to indicate that a 304 reply came back.
+ EXPECT_GT(res2.expires, 0);
+ EXPECT_EQ(res2.modified, 1420070400);
+ EXPECT_EQ(res2.etag, "");
+ EXPECT_EQ(res2.message, "");
+
+ CacheRevalidateModified.finish();
+ });
+ });
+
+ const Resource revalidateEtag { Resource::Unknown, "http://127.0.0.1:3000/revalidate-etag" };
+ fs.request(revalidateEtag, uv_default_loop(), [&](const Response &res) {
+ EXPECT_EQ(res.status, Response::Successful);
+ EXPECT_EQ(res.data, "Response 1");
+ EXPECT_EQ(res.expires, 0);
+ EXPECT_EQ(res.modified, 0);
+ EXPECT_EQ(res.etag, "response-1");
+ EXPECT_EQ(res.message, "");
+
+ fs.request(revalidateEtag, uv_default_loop(), [&, res](const Response &res2) {
+ EXPECT_EQ(res2.status, Response::Successful);
+ EXPECT_EQ(res2.data, "Response 2");
+ EXPECT_EQ(res2.expires, 0);
+ EXPECT_EQ(res2.modified, 0);
+ EXPECT_EQ(res2.etag, "response-2");
+ EXPECT_EQ(res2.message, "");
+
+ CacheRevalidateEtag.finish();
+ });
+ });
+
+ uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+}
diff --git a/test/storage/directory_reading.cpp b/test/storage/directory_reading.cpp
new file mode 100644
index 0000000000..66d0f8f9e7
--- /dev/null
+++ b/test/storage/directory_reading.cpp
@@ -0,0 +1,25 @@
+#include "../util.hpp"
+
+#include <uv.h>
+
+#include <mbgl/storage/default/default_file_source.hpp>
+
+TEST(Storage, ReadDirectory) {
+ SCOPED_TEST(ReadDirectory)
+
+ using namespace mbgl;
+
+ DefaultFileSource fs(nullptr, uv_default_loop());
+
+ fs.request({ Resource::Unknown, "asset://test/fixtures/storage" }, uv_default_loop(), [&](const Response &res) {
+ EXPECT_EQ(res.status, Response::Error);
+ EXPECT_EQ(res.data.size(), 0ul);
+ EXPECT_EQ(res.expires, 0);
+ EXPECT_EQ(res.modified, 0);
+ EXPECT_EQ(res.etag, "");
+ EXPECT_EQ(res.message, "illegal operation on a directory");
+ ReadDirectory.finish();
+ });
+
+ uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+}
diff --git a/test/storage/file_reading.cpp b/test/storage/file_reading.cpp
new file mode 100644
index 0000000000..273b58218e
--- /dev/null
+++ b/test/storage/file_reading.cpp
@@ -0,0 +1,45 @@
+#include "../util.hpp"
+
+#include <uv.h>
+
+#include <mbgl/storage/default/default_file_source.hpp>
+
+TEST(Storage, EmptyFile) {
+ SCOPED_TEST(EmptyFile)
+
+ using namespace mbgl;
+
+ DefaultFileSource fs(nullptr, uv_default_loop());
+
+ fs.request({ Resource::Unknown, "asset://test/fixtures/storage/empty" }, uv_default_loop(), [&](const Response &res) {
+ EXPECT_EQ(res.status, Response::Successful);
+ EXPECT_EQ(res.data.size(), 0ul);
+ EXPECT_EQ(res.expires, 0);
+ EXPECT_GT(res.modified, 0);
+ EXPECT_NE(res.etag, "");
+ EXPECT_EQ(res.message, "");
+ EmptyFile.finish();
+ });
+
+ uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+}
+
+TEST(Storage, NonExistentFile) {
+ SCOPED_TEST(NonExistentFile)
+
+ using namespace mbgl;
+
+ DefaultFileSource fs(nullptr, uv_default_loop());
+
+ fs.request({ Resource::Unknown, "asset://test/fixtures/storage/does_not_exist" }, uv_default_loop(), [&](const Response &res) {
+ EXPECT_EQ(res.status, Response::Error);
+ EXPECT_EQ(res.data.size(), 0ul);
+ EXPECT_EQ(res.expires, 0);
+ EXPECT_EQ(res.modified, 0);
+ EXPECT_EQ(res.etag, "");
+ EXPECT_EQ(res.message, "no such file or directory");
+ NonExistentFile.finish();
+ });
+
+ uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+}
diff --git a/test/storage/http_cancel.cpp b/test/storage/http_cancel.cpp
new file mode 100644
index 0000000000..4da70b965b
--- /dev/null
+++ b/test/storage/http_cancel.cpp
@@ -0,0 +1,50 @@
+#include "../util.hpp"
+
+#include <uv.h>
+
+#include <mbgl/storage/default/default_file_source.hpp>
+#include <mbgl/storage/network_status.hpp>
+
+#include <cmath>
+
+TEST(Storage, HTTPCancel) {
+ SCOPED_TEST(HTTPCancel)
+
+ using namespace mbgl;
+
+ DefaultFileSource fs(nullptr, uv_default_loop());
+
+ auto req = fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(), [&](const Response &) {
+ ADD_FAILURE() << "Callback should not be called";
+ });
+
+ fs.cancel(req);
+ HTTPCancel.finish();
+
+ uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+}
+
+TEST(Storage, HTTPCancelMultiple) {
+ SCOPED_TEST(HTTPCancelMultiple)
+
+ using namespace mbgl;
+
+ DefaultFileSource fs(nullptr, uv_default_loop());
+
+ const Resource resource { Resource::Unknown, "http://127.0.0.1:3000/test" };
+ auto req2 = fs.request(resource, uv_default_loop(), [&](const Response &) {
+ ADD_FAILURE() << "Callback should not be called";
+ });
+ fs.request(resource, uv_default_loop(), [&](const Response &res) {
+ EXPECT_EQ(res.status, Response::Successful);
+ EXPECT_EQ(res.data, "Hello World!");
+ EXPECT_EQ(res.expires, 0);
+ EXPECT_EQ(res.modified, 0);
+ EXPECT_EQ(res.etag, "");
+ EXPECT_EQ(res.message, "");
+ HTTPCancelMultiple.finish();
+ });
+ fs.cancel(req2);
+
+ uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+}
diff --git a/test/storage/http_coalescing.cpp b/test/storage/http_coalescing.cpp
new file mode 100644
index 0000000000..2a82abba2e
--- /dev/null
+++ b/test/storage/http_coalescing.cpp
@@ -0,0 +1,48 @@
+#include "../util.hpp"
+
+#include <uv.h>
+
+#include <mbgl/storage/default/default_file_source.hpp>
+
+TEST(Storage, HTTPCoalescing) {
+ SCOPED_TEST(HTTPCoalescing)
+
+ static int counter = 0;
+ const static int total = 4;
+
+ using namespace mbgl;
+
+ DefaultFileSource fs(nullptr, uv_default_loop());
+
+ static const Response *reference = nullptr;
+
+ const auto complete = [&](const Response &res) {
+ counter++;
+
+ // Make sure all of the Response objects are the same.
+ if (!reference) {
+ reference = &res;
+ } else {
+ EXPECT_EQ(reference, &res);
+ }
+
+ EXPECT_EQ(res.status, Response::Successful);
+ EXPECT_EQ(res.data, "Hello World!");
+ EXPECT_EQ(res.expires, 0);
+ EXPECT_EQ(res.modified, 0);
+ EXPECT_EQ(res.etag, "");
+ EXPECT_EQ(res.message, "");
+
+ if (counter >= total) {
+ HTTPCoalescing.finish();
+ }
+ };
+
+ const Resource resource { Resource::Unknown, "http://127.0.0.1:3000/test" };
+
+ for (int i = 0; i < total; i++) {
+ fs.request(resource, uv_default_loop(), complete);
+ }
+
+ uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+}
diff --git a/test/storage/http_error.cpp b/test/storage/http_error.cpp
new file mode 100644
index 0000000000..3f4b95a102
--- /dev/null
+++ b/test/storage/http_error.cpp
@@ -0,0 +1,63 @@
+#include "../util.hpp"
+
+#include <uv.h>
+
+#include <mbgl/storage/default/default_file_source.hpp>
+#include <mbgl/storage/network_status.hpp>
+
+#include <cmath>
+
+TEST(Storage, HTTPError) {
+ SCOPED_TEST(HTTPTemporaryError)
+ SCOPED_TEST(HTTPConnectionError)
+
+ using namespace mbgl;
+
+ uv_timer_t statusChange;
+ uv_timer_init(uv_default_loop(), &statusChange);
+ uv_timer_start(&statusChange, [](uv_timer_t *, int) {
+ NetworkStatus::Reachable();
+ }, 500, 500);
+ uv_unref((uv_handle_t *)&statusChange);
+
+ DefaultFileSource fs(nullptr, uv_default_loop());
+
+ auto start = uv_hrtime();
+
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3000/temporary-error" }, uv_default_loop(), [&](const Response &res) {
+ const auto duration = double(uv_hrtime() - start) / 1e9;
+ EXPECT_GT(duration, 1) << "Backoff timer didn't wait 1 second";
+ EXPECT_LT(duration, 1.2) << "Backoff timer fired too late";
+ EXPECT_EQ(res.status, Response::Successful);
+ EXPECT_EQ(res.data, "Hello World!");
+ EXPECT_EQ(res.expires, 0);
+ EXPECT_EQ(res.modified, 0);
+ EXPECT_EQ(res.etag, "");
+ EXPECT_EQ(res.message, "");
+
+ HTTPTemporaryError.finish();
+ });
+
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3001/" }, uv_default_loop(), [&](const Response &res) {
+ const auto duration = double(uv_hrtime() - start) / 1e9;
+ // 1.5 seconds == 4 retries, with a 500ms timeout (see above).
+ EXPECT_GT(duration, 1.5) << "Resource wasn't retried the correct number of times";
+ EXPECT_LT(duration, 1.7) << "Resource wasn't retried the correct number of times";
+ EXPECT_EQ(res.status, Response::Error);
+ EXPECT_TRUE(res.message == "Couldn't connect to server" || res.message == "The operation couldn’t be completed. (NSURLErrorDomain error -1004.)");
+ EXPECT_EQ(res.data, "");
+ EXPECT_EQ(res.expires, 0);
+ EXPECT_EQ(res.modified, 0);
+ EXPECT_EQ(res.etag, "");
+ HTTPConnectionError.finish();
+ });
+
+
+ uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+
+ uv_timer_stop(&statusChange);
+ uv_close(reinterpret_cast<uv_handle_t *>(&statusChange), nullptr);
+
+ // Run again so that the timer handle can be properly closed.
+ uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+}
diff --git a/test/storage/http_header_parsing.cpp b/test/storage/http_header_parsing.cpp
new file mode 100644
index 0000000000..0d169e44c6
--- /dev/null
+++ b/test/storage/http_header_parsing.cpp
@@ -0,0 +1,42 @@
+#include "../util.hpp"
+
+#include <uv.h>
+
+#include <mbgl/storage/default/default_file_source.hpp>
+
+#include <cmath>
+
+TEST(Storage, HTTPHeaderParsing) {
+ SCOPED_TEST(HTTPExpiresTest)
+ SCOPED_TEST(HTTPCacheControlTest)
+
+ using namespace mbgl;
+
+ DefaultFileSource fs(nullptr, uv_default_loop());
+
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test?modified=1420794326&expires=1420797926&etag=foo" }, uv_default_loop(), [&](const Response &res) {
+ EXPECT_EQ(res.status, Response::Successful);
+ EXPECT_EQ(res.data, "Hello World!");
+ EXPECT_EQ(res.expires, 1420797926);
+ EXPECT_EQ(res.modified, 1420794326);
+ EXPECT_EQ(res.etag, "foo");
+ EXPECT_EQ(res.message, "");
+ HTTPExpiresTest.finish();
+ });
+
+
+ int64_t now = std::chrono::duration_cast<std::chrono::seconds>(
+ std::chrono::system_clock::now().time_since_epoch()).count();
+
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test?cachecontrol=max-age=120" }, uv_default_loop(), [&](const Response &res) {
+ EXPECT_EQ(res.status, Response::Successful);
+ EXPECT_EQ(res.data, "Hello World!");
+ EXPECT_LT(std::abs(res.expires - now - 120), 2) << "Expiration date isn't about 120 seconds in the future";
+ EXPECT_EQ(res.modified, 0);
+ EXPECT_EQ(res.etag, "");
+ EXPECT_EQ(res.message, "");
+ HTTPCacheControlTest.finish();
+ });
+
+ uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+}
diff --git a/test/storage/http_load.cpp b/test/storage/http_load.cpp
new file mode 100644
index 0000000000..deefef9135
--- /dev/null
+++ b/test/storage/http_load.cpp
@@ -0,0 +1,44 @@
+#include "../util.hpp"
+
+#include <uv.h>
+
+#include <mbgl/storage/default/default_file_source.hpp>
+
+
+
+TEST(Storage, HTTPLoad) {
+ SCOPED_TEST(HTTPLoad)
+
+ using namespace mbgl;
+
+ DefaultFileSource fs(nullptr, uv_default_loop());
+
+ const int concurrency = 50;
+ const int max = 10000;
+ int number = 1;
+
+ std::function<void()> req = [&]() {
+ const auto current = number++;
+ fs.request({ Resource::Unknown, std::string("http://127.0.0.1:3000/load/") + std::to_string(current) }, uv_default_loop(), [&, current](const Response &res) {
+ EXPECT_EQ(res.status, Response::Successful);
+ EXPECT_EQ(res.data, std::string("Request ") + std::to_string(current));
+ EXPECT_EQ(res.expires, 0);
+ EXPECT_EQ(res.modified, 0);
+ EXPECT_EQ(res.etag, "");
+ EXPECT_EQ(res.message, "");
+
+ if (number <= max) {
+ req();
+ } else if (current == max) {
+ HTTPLoad.finish();
+ }
+ });
+ };
+
+
+ for (int i = 0; i < concurrency; i++) {
+ req();
+ }
+
+ uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+}
diff --git a/test/storage/http_noloop.cpp b/test/storage/http_noloop.cpp
new file mode 100644
index 0000000000..cb2b778aa7
--- /dev/null
+++ b/test/storage/http_noloop.cpp
@@ -0,0 +1,37 @@
+#include "../util.hpp"
+
+#include <uv.h>
+
+#include <mbgl/storage/default/default_file_source.hpp>
+#include <mbgl/util/uv.hpp>
+
+TEST(Storage, HTTPNoLoop) {
+ SCOPED_TEST(HTTPNoLoop)
+
+ using namespace mbgl;
+
+ DefaultFileSource fs(nullptr);
+
+ const auto mainThread = uv_thread_self();
+
+ // Async handle that keeps the main loop alive until the thread finished
+ auto async = new uv_async_t;
+ uv_async_init(uv_default_loop(), async, [] (uv_async_t *as, int) {
+ uv::close(as);
+ });
+
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3000/temporary-error" }, [&](const Response &res) {
+ EXPECT_NE(mainThread, uv_thread_self()) << "Response was called in the same thread";
+ EXPECT_EQ(res.status, Response::Successful);
+ EXPECT_EQ(res.data, "Hello World!");
+ EXPECT_EQ(res.expires, 0);
+ EXPECT_EQ(res.modified, 0);
+ EXPECT_EQ(res.etag, "");
+ EXPECT_EQ(res.message, "");
+ HTTPNoLoop.finish();
+
+ uv_async_send(async);
+ });
+
+ uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+}
diff --git a/test/storage/http_other_loop.cpp b/test/storage/http_other_loop.cpp
new file mode 100644
index 0000000000..01779453d5
--- /dev/null
+++ b/test/storage/http_other_loop.cpp
@@ -0,0 +1,26 @@
+#include "../util.hpp"
+
+#include <uv.h>
+
+#include <mbgl/storage/default/default_file_source.hpp>
+
+TEST(Storage, HTTPOtherLoop) {
+ SCOPED_TEST(HTTPOtherLoop)
+
+ using namespace mbgl;
+
+ // This file source launches a separate thread to do the processing.
+ DefaultFileSource fs(nullptr);
+
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(), [&](const Response &res) {
+ EXPECT_EQ(res.status, Response::Successful);
+ EXPECT_EQ(res.data, "Hello World!");
+ EXPECT_EQ(res.expires, 0);
+ EXPECT_EQ(res.modified, 0);
+ EXPECT_EQ(res.etag, "");
+ EXPECT_EQ(res.message, "");
+ HTTPOtherLoop.finish();
+ });
+
+ uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+}
diff --git a/test/storage/http_reading.cpp b/test/storage/http_reading.cpp
new file mode 100644
index 0000000000..0fbabf57f9
--- /dev/null
+++ b/test/storage/http_reading.cpp
@@ -0,0 +1,39 @@
+#include "../util.hpp"
+
+#include <uv.h>
+
+#include <mbgl/storage/default/default_file_source.hpp>
+
+TEST(Storage, HTTPReading) {
+ SCOPED_TEST(HTTPTest)
+ SCOPED_TEST(HTTP404)
+
+ using namespace mbgl;
+
+ DefaultFileSource fs(nullptr, uv_default_loop());
+
+ const auto mainThread = uv_thread_self();
+
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3000/test" }, uv_default_loop(), [&](const Response &res) {
+ EXPECT_EQ(mainThread, uv_thread_self());
+ EXPECT_EQ(res.status, Response::Successful);
+ EXPECT_EQ(res.data, "Hello World!");
+ EXPECT_EQ(res.expires, 0);
+ EXPECT_EQ(res.modified, 0);
+ EXPECT_EQ(res.etag, "");
+ EXPECT_EQ(res.message, "");
+ HTTPTest.finish();
+ });
+
+ fs.request({ Resource::Unknown, "http://127.0.0.1:3000/doesnotexist" }, uv_default_loop(), [&](const Response &res) {
+ EXPECT_EQ(mainThread, uv_thread_self());
+ EXPECT_EQ(res.status, Response::Error);
+ EXPECT_EQ(res.message, "HTTP status code 404");
+ EXPECT_EQ(res.expires, 0);
+ EXPECT_EQ(res.modified, 0);
+ EXPECT_EQ(res.etag, "");
+ HTTP404.finish();
+ });
+
+ uv_run(uv_default_loop(), UV_RUN_DEFAULT);
+}
diff --git a/test/storage/server.js b/test/storage/server.js
new file mode 100755
index 0000000000..b55d70ae71
--- /dev/null
+++ b/test/storage/server.js
@@ -0,0 +1,101 @@
+#!/usr/bin/env node
+/* jshint node: true */
+'use strict';
+
+var express = require('express');
+var app = express();
+
+// We're manually setting Etag headers.
+app.disable('etag');
+
+app.get('/test', function (req, res) {
+ if (req.query.modified) {
+ res.setHeader('Last-Modified', (new Date(req.query.modified * 1000)).toUTCString());
+ }
+ if (req.query.expires) {
+ res.setHeader('Expires', (new Date(req.query.expires * 1000)).toUTCString());
+ }
+ if (req.query.etag) {
+ res.setHeader('ETag', req.query.etag);
+ }
+ if (req.query.cachecontrol) {
+ res.setHeader('Cache-Control', req.query.cachecontrol);
+ }
+ res.send('Hello World!');
+});
+
+
+var cacheCounter = 0;
+app.get('/cache', function(req, res) {
+ res.setHeader('Cache-Control', 'max-age=30'); // Allow caching for 30 seconds
+ res.send('Response ' + (++cacheCounter));
+});
+
+app.get('/revalidate-same', function(req, res) {
+ if (req.headers['if-none-match'] == 'snowfall') {
+ // Second request can be cached for 30 seconds.
+ res.setHeader('Cache-Control', 'max-age=30');
+ res.status(304).end();
+ } else {
+ // First request must always be revalidated.
+ res.setHeader('ETag', 'snowfall');
+ res.setHeader('Cache-Control', 'must-revalidate');
+ res.status(200).send('Response');
+ }
+});
+
+
+app.get('/revalidate-modified', function(req, res) {
+ var jan1 = new Date('jan 1 2015 utc');
+
+ if (req.headers['if-modified-since']) {
+ var modified_since = new Date(req.headers['if-modified-since']);
+ if (modified_since >= jan1) {
+ res.setHeader('Cache-Control', 'max-age=30');
+ res.status(304).end();
+ return;
+ }
+ }
+
+ // First request must always be revalidated.
+ res.setHeader('Last-Modified', jan1.toUTCString());
+ res.setHeader('Cache-Control', 'must-revalidate');
+ res.status(200).send('Response');
+});
+
+
+var revalidateEtagCounter = 1;
+app.get('/revalidate-etag', function(req, res) {
+ res.setHeader('ETag', 'response-' + revalidateEtagCounter);
+ res.setHeader('Cache-Control', 'must-revalidate');
+
+ res.status(200).send('Response ' + revalidateEtagCounter);
+ revalidateEtagCounter++;
+});
+
+
+var temporaryErrorCounter = 0;
+app.get('/temporary-error', function(req, res) {
+ if (temporaryErrorCounter === 0) {
+ res.status(500).end();
+ } else {
+ res.status(200).send('Hello World!');
+ }
+
+ temporaryErrorCounter++;
+});
+
+app.get('/load/:number(\\d+)', function(req, res) {
+ res.send('Request ' + req.params.number);
+});
+
+var server = app.listen(3000, function () {
+ var host = server.address().address;
+ var port = server.address().port;
+ console.log('Test server listening at http://%s:%s', host, port);
+
+ if (process.argv[2]) {
+ // Allow the test to continue running.
+ process.kill(+process.argv[2], 'SIGCONT');
+ }
+});
diff --git a/test/storage/storage.gypi b/test/storage/storage.gypi
new file mode 100644
index 0000000000..b9b7835317
--- /dev/null
+++ b/test/storage/storage.gypi
@@ -0,0 +1,28 @@
+{
+ 'targets': [
+ { 'target_name': 'test_storage',
+ 'type': 'executable',
+ 'dependencies': [
+ 'test_base',
+ '../mapboxgl.gyp:mbgl-core',
+ '../mapboxgl.gyp:mbgl-<(platform)',
+ '../deps/gtest/gtest.gyp:gtest'
+ ],
+ 'sources': [
+ 'main.cpp',
+ 'cache_response.cpp',
+ 'cache_revalidate.cpp',
+ 'directory_reading.cpp',
+ 'file_reading.cpp',
+ 'http_cancel.cpp',
+ 'http_coalescing.cpp',
+ 'http_error.cpp',
+ 'http_header_parsing.cpp',
+ 'http_load.cpp',
+ 'http_noloop.cpp',
+ 'http_other_loop.cpp',
+ 'http_reading.cpp',
+ ],
+ },
+ ],
+}
diff --git a/test/test.gyp b/test/test.gyp
index 85958b1f03..98a9d67834 100644
--- a/test/test.gyp
+++ b/test/test.gyp
@@ -4,265 +4,73 @@
'../gyp/version.gypi',
'../gyp/mbgl-platform.gypi',
],
- 'variables' : {
- 'ldflags': [
- '<@(uv_ldflags)',
- '<@(sqlite3_static_libs)',
- '<@(uv_static_libs)',
- '<@(sqlite3_ldflags)',
- '<@(curl_ldflags)',
- '<@(png_ldflags)',
- ],
- },
'targets': [
- { 'target_name': 'rotation_range',
- 'product_name': 'test_rotation_range',
- 'type': 'executable',
- 'sources': [
- './main.cpp',
- './rotation_range.cpp',
- ],
- 'dependencies': [
- '../deps/gtest/gtest.gyp:gtest',
- '../mapboxgl.gyp:mbgl-standalone',
- ],
- 'include_dirs': [ '../src' ],
- 'conditions': [
- ['OS == "mac"', { 'xcode_settings': { 'OTHER_LDFLAGS': [ '<@(ldflags)' ] }
- }, {
- 'libraries': [ '<@(ldflags)' ],
- }]
- ]
- },
- { 'target_name': 'clip_ids',
- 'product_name': 'test_clip_ids',
- 'type': 'executable',
- 'sources': [
- './main.cpp',
- './clip_ids.cpp',
- ],
- 'dependencies': [
- '../deps/gtest/gtest.gyp:gtest',
- '../mapboxgl.gyp:mbgl-standalone',
- ],
- 'include_dirs': [ '../src' ],
- 'conditions': [
- ['OS == "mac"', { 'xcode_settings': { 'OTHER_LDFLAGS': [ '<@(ldflags)'] }
- }, {
- 'libraries': [ '<@(ldflags)' ],
- }]
- ]
- },
- { 'target_name': 'enums',
- 'product_name': 'test_enums',
- 'type': 'executable',
- 'sources': [
- './main.cpp',
- './enums.cpp',
- ],
- 'dependencies': [
- '../deps/gtest/gtest.gyp:gtest',
- '../mapboxgl.gyp:mbgl-standalone',
- ],
- 'include_dirs': [ '../src' ],
- 'conditions': [
- ['OS == "mac"', { 'xcode_settings': { 'OTHER_LDFLAGS': [ '<@(ldflags)'] }
- }, {
- 'libraries': [ '<@(ldflags)' ],
- }]
- ]
- },
- { 'target_name': 'style_parser',
- 'product_name': 'test_style_parser',
- 'type': 'executable',
- 'sources': [
- './main.cpp',
- './style_parser.cpp',
- './fixtures/fixture_log.hpp',
- './fixtures/fixture_log.cpp',
- ],
- 'dependencies': [
- '../deps/gtest/gtest.gyp:gtest',
- '../mapboxgl.gyp:mbgl-standalone'
- ],
- 'include_dirs': [ '../src' ],
- 'conditions': [
- ['OS == "mac"', { 'xcode_settings': { 'OTHER_LDFLAGS': [ '<@(ldflags)' ] }
- }, {
- 'libraries': [ '<@(ldflags)' ],
- }]
- ]
- },
- { 'target_name': 'variant',
- 'product_name': 'test_variant',
- 'type': 'executable',
- 'sources': [
- './main.cpp',
- './variant.cpp',
- ],
- 'dependencies': [
- '../deps/gtest/gtest.gyp:gtest',
- '../mapboxgl.gyp:mbgl-standalone',
- ],
- 'include_dirs': [ '../src' ],
- 'conditions': [
- ['OS == "mac"', { 'xcode_settings': { 'OTHER_LDFLAGS': [ '<@(ldflags)' ] }
- }, {
- 'libraries': [ '<@(ldflags)' ],
- }]
- ]
- },
- { 'target_name': 'comparisons',
- 'product_name': 'test_comparisons',
- 'type': 'executable',
- 'sources': [
- './main.cpp',
- './comparisons.cpp',
- ],
- 'dependencies': [
- '../deps/gtest/gtest.gyp:gtest',
- '../mapboxgl.gyp:mbgl-standalone',
- ],
- 'include_dirs': [ '../src' ],
- 'conditions': [
- ['OS == "mac"', { 'xcode_settings': { 'OTHER_LDFLAGS': [ '<@(ldflags)' ] }
- }, {
- 'libraries': [ '<@(ldflags)' ],
- }]
- ]
- },
- { 'target_name': 'tile',
- 'product_name': 'test_tile',
- 'type': 'executable',
- 'sources': [
- './main.cpp',
- './tile.cpp',
- ],
- 'dependencies': [
- '../deps/gtest/gtest.gyp:gtest',
- '../mapboxgl.gyp:mbgl-standalone',
- ],
- 'include_dirs': [ '../src' ],
- 'conditions': [
- ['OS == "mac"', { 'xcode_settings': { 'OTHER_LDFLAGS': [ '<@(ldflags)' ] }
- }, {
- 'libraries': [ '<@(ldflags)' ],
- }]
- ]
- },
- { 'target_name': 'functions',
- 'product_name': 'test_functions',
- 'type': 'executable',
- 'sources': [
- './main.cpp',
- './functions.cpp',
- ],
- 'dependencies': [
- '../deps/gtest/gtest.gyp:gtest',
- '../mapboxgl.gyp:mbgl-standalone',
- ],
- 'include_dirs': [ '../src' ],
- 'conditions': [
- ['OS == "mac"', { 'xcode_settings': { 'OTHER_LDFLAGS': [ '<@(ldflags)' ] }
- }, {
- 'libraries': [ '<@(ldflags)' ],
- }]
- ]
- },
- { 'target_name': 'merge_lines',
- 'product_name': 'test_merge_lines',
- 'type': 'executable',
- 'sources': [
- './main.cpp',
- './merge_lines.cpp',
- ],
- 'dependencies': [
- '../deps/gtest/gtest.gyp:gtest',
- '../mapboxgl.gyp:mbgl-standalone',
- ],
- 'include_dirs': [ '../src' ],
- 'conditions': [
- ['OS == "mac"', { 'xcode_settings': { 'OTHER_LDFLAGS': [ '<@(ldflags)' ] }
- }, {
- 'libraries': [ '<@(ldflags)' ],
- }]
- ]
+ { 'target_name': 'test_base',
+ 'type': 'none',
+ 'direct_dependent_settings': {
+ 'include_dirs': [ '../include', '../src' ],
+ },
+ 'link_settings': {
+ 'libraries': [
+ '<@(uv_static_libs)',
+ '<@(glfw3_static_libs)',
+ '<@(sqlite3_static_libs)',
+ '<@(zlib_static_libs)',
+ ],
+ 'xcode_settings': {
+ 'OTHER_LDFLAGS': [
+ '<@(uv_ldflags)',
+ '<@(glfw3_ldflags)',
+ '<@(sqlite3_ldflags)',
+ '<@(zlib_ldflags)',
+ ],
+ 'OTHER_CFLAGS': [ '<@(uv_cflags)' ],
+ 'OTHER_CPLUSPLUSFLAGS': [ '<@(uv_cflags)' ],
+ },
+ 'cflags': [ '<@(uv_cflags)' ],
+ 'cxxflags': [ '<@(uv_cflags)' ],
+ },
},
- { 'target_name': 'headless',
- 'product_name': 'test_headless',
+
+ # Build all targets
+ { 'target_name': 'test',
'type': 'executable',
- 'sources': [
- './headless.cpp',
- './fixtures/fixture_log.cpp',
- ],
- 'conditions': [
- # add libuv include path and OpenGL libs
- ['OS == "mac"',
- {
- 'xcode_settings': {
- 'OTHER_CPLUSPLUSFLAGS': ['<@(uv_cflags)','<@(png_cflags)'],
- 'OTHER_LDFLAGS': ['<@(glfw3_ldflags)', '<@(ldflags)'],
- },
- },
- {
- 'cflags': ['<@(uv_cflags)','<@(png_cflags)'],
- 'libraries': ['<@(glfw3_ldflags)', '<@(ldflags)'],
- }],
- ],
'dependencies': [
- '../deps/gtest/gtest.gyp:gtest',
+ 'test_base',
'../mapboxgl.gyp:mbgl-core',
+ '../mapboxgl.gyp:mbgl-<(platform)',
'../mapboxgl.gyp:mbgl-headless',
- '<(platform_library)',
+ '../deps/gtest/gtest.gyp:gtest'
],
- 'include_dirs': [ '../src' ]
- },
- { 'target_name': 'text_conversions',
- 'product_name': 'test_text_conversions',
- 'type': 'executable',
'sources': [
- './main.cpp',
- './text_conversions.cpp',
+ 'main.cpp',
+
+ 'headless/headless.cpp',
+
+ 'miscellaneous/clip_ids.cpp',
+ 'miscellaneous/comparisons.cpp',
+ 'miscellaneous/enums.cpp',
+ 'miscellaneous/functions.cpp',
+ 'miscellaneous/merge_lines.cpp',
+ 'miscellaneous/rotation_range.cpp',
+ 'miscellaneous/style_parser.cpp',
+ 'miscellaneous/text_conversions.cpp',
+ 'miscellaneous/tile.cpp',
+ 'miscellaneous/variant.cpp',
+
+ 'storage/cache_response.cpp',
+ 'storage/cache_revalidate.cpp',
+ 'storage/directory_reading.cpp',
+ 'storage/file_reading.cpp',
+ 'storage/http_cancel.cpp',
+ 'storage/http_coalescing.cpp',
+ 'storage/http_error.cpp',
+ 'storage/http_header_parsing.cpp',
+ 'storage/http_load.cpp',
+ 'storage/http_noloop.cpp',
+ 'storage/http_other_loop.cpp',
+ 'storage/http_reading.cpp',
],
- 'dependencies': [
- '../deps/gtest/gtest.gyp:gtest',
- '../mapboxgl.gyp:mbgl-standalone',
- '<(platform_library)',
- ],
- 'include_dirs': [ '../src' ],
- 'variables': {
- 'cflags_cc': [
- '-I<(boost_root)/include',
- ]
- },
- 'conditions': [
- ['OS == "mac"', {
- 'xcode_settings': {
- 'OTHER_CPLUSPLUSFLAGS': [ '<@(cflags_cc)' ],
- 'OTHER_LDFLAGS': [ '<@(ldflags)', '-framework Foundation' ]
- },
- }, {
- 'cflags_cc': [ '<@(cflags_cc)' ],
- 'libraries': [ '<@(ldflags)'],
- }]
- ]
},
- # Build all targets
- { 'target_name': 'test',
- 'type': 'none',
- 'dependencies': [
- 'rotation_range',
- 'clip_ids',
- 'enums',
- 'variant',
- 'tile',
- 'functions',
- 'merge_lines',
- 'headless',
- 'style_parser',
- 'comparisons',
- 'text_conversions',
- ],
- }
]
}
diff --git a/test/util.hpp b/test/util.hpp
new file mode 100644
index 0000000000..3511662c88
--- /dev/null
+++ b/test/util.hpp
@@ -0,0 +1,14 @@
+#ifndef MBGL_TEST_UTIL
+#define MBGL_TEST_UTIL
+
+#include <gtest/gtest.h>
+
+#define SCOPED_TEST(name) \
+ static class name { \
+ bool completed = false; \
+ public: \
+ void finish() { EXPECT_FALSE(completed) << #name " was already completed."; completed = true; } \
+ ~name() { if (!completed) ADD_FAILURE() << #name " didn't complete."; } \
+ } name;
+
+#endif