rearrange tests and add storage tests

This commit is contained in:
Konstantin Käfer
2015-01-21 19:30:58 +01:00
parent 5503aef690
commit 8a1fce547e
49 changed files with 1119 additions and 500 deletions

1
.gitignore vendored
View File

@@ -8,6 +8,7 @@
/config-ios.gypi
/config-android.gypi
/build
/test/node_modules
/include/mbgl/shader/shaders.hpp
/src/shader/shaders_gl.cpp
/src/shader/shaders_gles2.cpp

View File

@@ -105,10 +105,12 @@ android:
test: build/test/Makefile
$(MAKE) -C build/test BUILDTYPE=$(BUILDTYPE) V=$(V) test
./scripts/run_tests.sh
test_%: build/test/Makefile
$(MAKE) -C build/test BUILDTYPE=$(BUILDTYPE) V=$(V) $*
(cd build/$(BUILDTYPE) && exec ./test_$*)
test-%: build/test/Makefile
$(MAKE) -C build/test BUILDTYPE=$(BUILDTYPE) V=$(V) test
build/$(BUILDTYPE)/test --gtest_filter=$*
# build Mac OS X project for Xcode
xtest: build/test/test.xcodeproj

View File

@@ -52,9 +52,14 @@ void log_gl_string(GLenum name, const char *label) {
}
}
// Returns the path to the default cache database on this system.
std::string defaultCacheDatabase() {
return mbgl::android::cachePath + "/mbgl-cache.db";
}
NativeMapView::NativeMapView(JNIEnv *env, jobject obj_)
: mbgl::View(*this),
fileSource(mbgl::platform::defaultCacheDatabase()),
fileSource(defaultCacheDatabase()),
map(*this, fileSource) {
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::NativeMapView");

View File

@@ -33,7 +33,6 @@
],
},
'sources': [
'../platform/android/cache_database_data.cpp',
'../platform/android/log_android.cpp',
'../platform/android/asset_request_libzip.cpp',
'../platform/default/string_stdlib.cpp',

View File

@@ -30,11 +30,11 @@
],
},
'sources': [
'../platform/default/cache_database_tmp.cpp',
'../platform/default/shader_cache_tmp.cpp',
'../platform/default/log_stderr.cpp',
'../platform/default/string_stdlib.cpp',
'../platform/default/http_request_curl.cpp',
'../platform/default/asset_request_libuv.cpp',
'../platform/default/http_request_baton_curl.cpp',
'../platform/default/application_root.cpp',
'../platform/default/image.cpp',
'../platform/default/image_reader.cpp',

View File

@@ -9,7 +9,7 @@
'version',
],
'sources': [
'../platform/osx/cache_database_application_support.mm',
'../platform/osx/shader_cache_application_support.mm',
'../platform/darwin/log_nslog.mm',
'../platform/darwin/string_nsstring.mm',
'../platform/darwin/http_request_cocoa.mm',
@@ -33,6 +33,7 @@
],
'xcode_settings': {
'OTHER_LDFLAGS': [
'-framework Foundation',
'-framework ImageIO',
'-framework CoreServices',
],

View File

@@ -17,8 +17,8 @@ std::string uppercase(const std::string &string);
// Lowercase a string, potentially using platform-specific routines.
std::string lowercase(const std::string &string);
// Returns the path to the default cache database on this system.
std::string defaultCacheDatabase();
// Returns the path to the default shader cache on this system.
std::string defaultShaderCache();
std::string applicationRoot();

View File

@@ -64,7 +64,7 @@ int main(int argc, char *argv[]) {
sigaction(SIGINT, &sigIntHandler, NULL);
view = new GLFWView();
mbgl::CachingHTTPFileSource fileSource(mbgl::platform::defaultCacheDatabase());
mbgl::CachingHTTPFileSource fileSource();
mbgl::Map map(*view, fileSource);
// Load settings

View File

@@ -72,12 +72,35 @@
}
@end
// Returns the path to the default cache database on this system.
std::string defaultCacheDatabase() {
NSArray *paths =
NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
if ([paths count] == 0) {
// Disable the cache if we don't have a location to write.
return "";
}
NSString *path = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"Mapbox GL"];
if (![[NSFileManager defaultManager] createDirectoryAtPath:path
withIntermediateDirectories:YES
attributes:nil
error:nil]) {
// Disable the cache if we couldn't create the directory.
return "";
}
return [[path stringByAppendingPathComponent:@"cache.db"] UTF8String];
}
int main() {
mbgl::Log::Set<mbgl::NSLogBackend>();
GLFWView view;
mbgl::SQLiteCache cache(mbgl::platform::defaultCacheDatabase());
mbgl::SQLiteCache cache(defaultCacheDatabase());
mbgl::DefaultFileSource fileSource(&cache);
mbgl::Map map(view, fileSource);

View File

@@ -1,13 +0,0 @@
#include <mbgl/platform/platform.hpp>
#include <mbgl/android/jni.hpp>
namespace mbgl {
namespace platform {
// Returns the path to the default cache database on this system.
std::string defaultCacheDatabase() {
return mbgl::android::cachePath + "/mbgl-cache.db";
}
}
}

View File

@@ -1,31 +0,0 @@
#import <Foundation/Foundation.h>
#include <mbgl/platform/platform.hpp>
namespace mbgl {
namespace platform {
// Returns the path to the default cache database on this system.
std::string defaultCacheDatabase() {
NSArray *paths =
NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
if ([paths count] == 0) {
// Disable the cache if we don't have a location to write.
return "";
}
NSString *path = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"Mapbox GL"];
if (![[NSFileManager defaultManager] createDirectoryAtPath:path
withIntermediateDirectories:YES
attributes:nil
error:nil]) {
// Disable the cache if we couldn't create the directory.
return "";
}
return [[path stringByAppendingPathComponent:@"cache.db"] UTF8String];
}
}
}

View File

@@ -3,13 +3,13 @@
set -e
set -o pipefail
cd build/${BUILDTYPE:-Release}
for TEST in ./test_* ; do
for TEST in build/${BUILDTYPE:-Release}/test* ; do
# allow writing core files
ulimit -c unlimited -S
echo 'ulimit -c: '`ulimit -c`
echo '/proc/sys/kernel/core_pattern: '`cat /proc/sys/kernel/core_pattern`
if [ -f /proc/sys/kernel/core_pattern ]; then
echo '/proc/sys/kernel/core_pattern: '`cat /proc/sys/kernel/core_pattern`
fi
if [[ ${TRAVIS_OS_NAME} == "linux" ]]; then
sysctl kernel.core_pattern

View File

@@ -92,7 +92,6 @@ void Sprite::load(FileSource& fileSource) {
void Sprite::complete() {
if (loadedImage && loadedJSON) {
Log::Info(Event::Sprite, "loaded %s", spriteURL.c_str());
promise.set_value();
}
}

View File

@@ -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>

View File

@@ -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;
}
}

View File

@@ -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;
}
}

51
test/fixtures/server_environment.hpp vendored Normal file
View File

@@ -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

0
test/fixtures/storage/empty vendored Normal file
View File

View File

@@ -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;
}()));

View File

@@ -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',
],
},
],
}

19
test/headless/server.js Executable file
View File

@@ -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');
}
});

View File

@@ -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();
}

View File

@@ -1,5 +1,5 @@
#include <iostream>
#include "gtest/gtest.h"
#include "../util.hpp"
#include <algorithm>

View File

@@ -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) }}));
}

View File

@@ -1,5 +1,5 @@
#include <iostream>
#include "gtest/gtest.h"
#include "../util.hpp"
#include <algorithm>

View File

@@ -1,5 +1,5 @@
#include <iostream>
#include "gtest/gtest.h"
#include "../util.hpp"
#include <mbgl/style/function_properties.hpp>

View File

@@ -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, "" }

View File

@@ -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',
],
},
],
}

View File

@@ -1,5 +1,5 @@
#include <iostream>
#include "gtest/gtest.h"
#include "../util.hpp"
#include <algorithm>

View File

@@ -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;
}()));

View File

@@ -1,5 +1,5 @@
#include <iostream>
#include "gtest/gtest.h"
#include "../util.hpp"
#include <mbgl/util/utf.hpp>
#include <mbgl/platform/platform.hpp>

View File

@@ -1,5 +1,5 @@
#include <iostream>
#include "gtest/gtest.h"
#include "../util.hpp"
#include <mbgl/map/tile.hpp>

View File

@@ -1,5 +1,5 @@
#include <iostream>
#include "gtest/gtest.h"
#include "../util.hpp"
#include <mbgl/style/value.hpp>
#include <mbgl/style/value_comparison.hpp>

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

101
test/storage/server.js Executable file
View File

@@ -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');
}
});

28
test/storage/storage.gypi Normal file
View File

@@ -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',
],
},
],
}

View File

@@ -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': 'headless',
'product_name': 'test_headless',
'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',
'../mapboxgl.gyp:mbgl-core',
'../mapboxgl.gyp:mbgl-headless',
'<(platform_library)',
],
'include_dirs': [ '../src' ]
},
{ 'target_name': 'text_conversions',
'product_name': 'test_text_conversions',
'type': 'executable',
'sources': [
'./main.cpp',
'./text_conversions.cpp',
],
'dependencies': [
'../deps/gtest/gtest.gyp:gtest',
'../mapboxgl.gyp:mbgl-standalone',
'<(platform_library)',
],
'include_dirs': [ '../src' ],
'variables': {
'cflags_cc': [
'-I<(boost_root)/include',
]
{ '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)' ],
},
'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',
'type': 'executable',
'dependencies': [
'rotation_range',
'clip_ids',
'enums',
'variant',
'tile',
'functions',
'merge_lines',
'headless',
'style_parser',
'comparisons',
'text_conversions',
'test_base',
'../mapboxgl.gyp:mbgl-core',
'../mapboxgl.gyp:mbgl-<(platform)',
'../mapboxgl.gyp:mbgl-headless',
'../deps/gtest/gtest.gyp:gtest'
],
}
'sources': [
'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',
],
},
]
}

14
test/util.hpp Normal file
View File

@@ -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