Merge branch 'master' into malformed

This commit is contained in:
Mike Morris
2014-07-09 12:57:54 -07:00
31 changed files with 378 additions and 48 deletions

View File

@@ -11,6 +11,9 @@ env:
matrix:
- BUILDTYPE=Release
- BUILDTYPE=Debug
global:
- secure: "bG4YYWMfl9API0MSRgmOaJrlGLv06tRg9KJNawBieZvBJbITPpxVGJZT3/l/SEJ+Rl15e2dRex4k+MGQlmT2SqPQxTEYWv1qxNigKPPcla7IWeNmWWqW8uVvFjdglojgBOK2k/xErVQtA4zDfi3mwSXH4DKwquXWsoEKmX2SV7M="
- secure: "Cbvap9ubVKgjPe3hUhI6JGeDZzBXHpOG9RaYKh+SdoIPhKnlJiNOYm1egomi+e4uqJInlFKuVHTw7Ng9Cun6Zm0jIxpkSchv1GpsR7hmB3UGnGed19Dw8121FwuUaktN+4YnbVlsyd+u8EHD3+h58t4eELrLrZolM4rS7DL6caA="
before_install:
- source ./scripts/setup_travis.sh

View File

@@ -29,12 +29,16 @@ test: build/test/Makefile
# Runs all test cases
run-tests: test
(cd build/${BUILDTYPE} && exec find . -maxdepth 1 -name "test_*" -exec {} \;)
BUILDTYPE=$(BUILDTYPE) ./scripts/run_tests.sh
test/%: build/test/Makefile
$(MAKE) -C build/test BUILDTYPE=$(BUILDTYPE) V=$(V) $*
(cd build/$(BUILDTYPE) && exec ./test_$*)
# build Mac OS X project for Xcode
xtest: config.gypi clear_xcode_cache node
deps/run_gyp test/test.gyp --depth=. -Goutput_dir=.. --generator-output=./build -f xcode
open ./build/test/test.xcodeproj
##### Makefile builds ##########################################################

View File

@@ -29,14 +29,14 @@ private:
private:
#ifdef LLMR_USE_CGL
#if LLMR_USE_CGL
CGLContextObj gl_context;
GLuint fbo = 0;
GLuint fbo_depth_stencil = 0;
GLuint fbo_color = 0;
#endif
#ifdef LLMR_USE_GLX
#if LLMR_USE_GLX
GLXContext gl_context = nullptr;
XVisualInfo *x_info = nullptr;
Display *x_display = nullptr;

View File

@@ -0,0 +1,20 @@
#ifndef LLMR_UTIL_ERROR
#define LLMR_UTIL_ERROR
#include <stdexcept>
#include <string>
namespace llmr {
namespace error {
struct style_parse : std::exception {
inline style_parse(size_t offset, const char *msg) : offset(offset), msg(msg) {}
inline const char* what() const noexcept { return msg.c_str(); }
const size_t offset;
const std::string msg;
};
}
}
#endif

View File

@@ -13,7 +13,7 @@ std::string compress_png(int width, int height, void *rgba, bool flip = false);
class Image {
public:
Image(const std::string &img);
Image(const std::string &img, bool flip = false);
~Image();
inline const char *getData() const { return img; }

View File

@@ -7,6 +7,7 @@ namespace llmr {
namespace util {
void write_file(const std::string &filename, const std::string &data);
std::string read_file(const std::string &filename);
}
}

View File

@@ -7,7 +7,7 @@
{
'target_name': 'shaders',
'type': 'none',
# 'hard_dependency': 1,
'hard_dependency': 1,
'actions': [
{
'action_name': 'Build Shaders',

10
scripts/run_tests.sh Executable file
View File

@@ -0,0 +1,10 @@
#!/usr/bin/env bash
set -e
set -o pipefail
cd build/$BUILDTYPE
for TEST in test_* ; do
./$TEST
done

View File

@@ -4,6 +4,7 @@
#include <llmr/style/style_bucket.hpp>
#include <llmr/util/constants.hpp>
#include <llmr/util/time.hpp>
#include <llmr/util/error.hpp>
#include <csscolorparser/csscolorparser.hpp>
#include <rapidjson/document.h>
@@ -102,6 +103,9 @@ void Style::loadJSON(const uint8_t *const data) {
rapidjson::Document doc;
doc.Parse<0>((const char *const)data);
if (doc.HasParseError()) {
throw error::style_parse(doc.GetErrorOffset(), doc.GetParseError());
}
StyleParser parser;
parser.parse(const_cast<const rapidjson::Document &>(doc));

View File

@@ -84,7 +84,7 @@ void warningHandler(png_structp, png_const_charp error_msg) {
fprintf(stderr, "PNG: %s\n", error_msg);
}
Image::Image(const std::string &data) {
Image::Image(const std::string &data, bool flip) {
Buffer buffer(data);
if (buffer.length < 8 || !png_check_sig((const png_bytep)buffer.data, 8)) {
@@ -148,7 +148,7 @@ Image::Image(const std::string &data) {
png_bytep *rows = nullptr;
} pointers(height);
for (unsigned i = 0; i < height; ++i) {
pointers.rows[i] = (png_bytep)(surface + (i * rowbytes));
pointers.rows[flip ? height - 1 - i : i] = (png_bytep)(surface + (i * rowbytes));
}
// Read image data

View File

@@ -1,8 +1,15 @@
#include <llmr/util/io.hpp>
#include <cstdio>
#include <iostream>
#include <sstream>
#include <fstream>
#include <stdexcept>
void llmr::util::write_file(const std::string &filename, const std::string &data) {
namespace llmr {
namespace util {
void write_file(const std::string &filename, const std::string &data) {
FILE *fd = fopen(filename.c_str(), "wb");
if (fd) {
fwrite(data.data(), sizeof(std::string::value_type), data.size(), fd);
@@ -11,3 +18,17 @@ void llmr::util::write_file(const std::string &filename, const std::string &data
fprintf(stderr, "Failed to open file\n");
}
}
std::string read_file(const std::string &filename) {
std::ifstream file(filename);
if (file.good()) {
std::stringstream data;
data << file.rdbuf();
return data.str();
} else {
throw std::runtime_error("Cannot read file " + filename);
}
}
}
}

100
test/fixtures/fixture_request.cpp vendored Normal file
View File

@@ -0,0 +1,100 @@
#include <llmr/platform/platform.hpp>
#include <llmr/platform/request.hpp>
#include <llmr/util/uv.hpp>
const std::string base_directory = []{
std::string fn = __FILE__;
fn.erase(fn.find_last_of("/"));
return fn;
}();
namespace llmr {
std::shared_ptr<platform::Request>
platform::request_http(const std::string &url,
std::function<void(Response *)> callback,
std::shared_ptr<uv::loop> loop) {
uv_loop_t *l = nullptr;
if (loop) {
l = **loop;
} else {
l = uv_default_loop();
}
std::string clean_url = base_directory + "/" + url;
auto pos = clean_url.find("://");
if (pos != std::string::npos) {
clean_url.replace(pos, 3, "/");
}
std::replace(clean_url.begin(), clean_url.end(), '+', ' ');
std::shared_ptr<Request> req = std::make_shared<Request>(url, callback, loop);
int err;
uv_fs_t open_req;
err = uv_fs_open(l, &open_req, clean_url.c_str(), O_RDONLY, S_IRUSR, nullptr);
uv_fs_req_cleanup(&open_req);
if (err < 0) {
req->res->code = err;
req->res->error_message = uv_strerror(err);
req->complete();
return req;
}
uv_file fd = err;
uv_fs_t stat_req;
uv_fs_fstat(l, &stat_req, fd, nullptr);
uv_fs_req_cleanup(&stat_req);
err = uv_fs_fstat(l, &stat_req, fd, nullptr);
if (err < 0) {
req->res->code = err;
req->res->error_message = uv_strerror(err);
req->complete();
return req;
}
const uint64_t size = static_cast<const uv_stat_t*>(stat_req.ptr)->st_size;
std::string body;
body.resize(size);
uv_buf_t uvbuf = uv_buf_init(const_cast<char *>(body.data()), body.size());
uv_fs_t read_req;
err = uv_fs_read(l, &read_req, fd, &uvbuf, 1, 0, nullptr);
uv_fs_req_cleanup(&read_req);
if (err < 0) {
req->res->code = err;
req->res->error_message = uv_strerror(err);
req->complete();
return req;
}
uv_fs_t close_req;
err = uv_fs_close(l, &close_req, fd, nullptr);
uv_fs_req_cleanup(&close_req);
if (err < 0) {
req->res->code = err;
req->res->error_message = uv_strerror(err);
req->complete();
return req;
}
req->res->body.swap(body);
req->res->code = 200;
req->complete();
return req;
}
void platform::cancel_request_http(const std::shared_ptr<Request> &req) {
if (req) {
req->cancelled = true;
}
}
} // end namespace llmr

1
test/fixtures/sprites/outdoors.json vendored Normal file

File diff suppressed because one or more lines are too long

BIN
test/fixtures/sprites/outdoors.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

BIN
test/fixtures/styles/0.actual.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

BIN
test/fixtures/styles/0.expected.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

8
test/fixtures/styles/0.info.json vendored Normal file
View File

@@ -0,0 +1,8 @@
{
"zoom": 0,
"latitude": 0,
"longitude": 0,
"angle": 0,
"width": 512,
"height": 512
}

34
test/fixtures/styles/0.style.json vendored Normal file
View File

@@ -0,0 +1,34 @@
{
"version": 3,
"sources": {
"mapbox": {
"type": "vector",
"url": "tiles/%d-%d-%d.vector.pbf",
"tileSize": 512,
"maxZoom": 14
}
},
"layers": [
{
"id": "background",
"render": {
"type": "background"
},
"style": {
"background-color": "red"
}
},
{
"id": "water",
"source": "mapbox",
"source-layer": "water",
"render": {
"type": "fill"
},
"style": {
"fill-color": "blue",
"fill-antialias": false
}
}
]
}

BIN
test/fixtures/styles/1.actual.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

BIN
test/fixtures/styles/1.expected.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

8
test/fixtures/styles/1.info.json vendored Normal file
View File

@@ -0,0 +1,8 @@
{
"zoom": 0,
"latitude": 0,
"longitude": 0,
"angle": 0,
"width": 512,
"height": 512
}

33
test/fixtures/styles/1.style.json vendored Normal file
View File

@@ -0,0 +1,33 @@
{
"version": 3,
"sources": {
"mapbox": {
"type": "vector",
"url": "tiles/%d-%d-%d.vector.pbf",
"tileSize": 512,
"maxZoom": 14
}
},
"layers": [
{
"id": "background",
"render": {
"type": "background"
},
"style": {
"background-color": "red"
}
},
{
"id": "water",
"source": "mapbox",
"source-layer": "water",
"render": {
"type": "fill"
},
"style": {
"fill-color": "blue"
}
}
]
}

BIN
test/fixtures/tiles/0-0-0.vector.pbf vendored Normal file

Binary file not shown.

BIN
test/fixtures/tiles/2-1-1.vector.pbf vendored Normal file

Binary file not shown.

BIN
test/fixtures/tiles/2-1-2.vector.pbf vendored Normal file

Binary file not shown.

BIN
test/fixtures/tiles/2-2-1.vector.pbf vendored Normal file

Binary file not shown.

BIN
test/fixtures/tiles/2-2-2.vector.pbf vendored Normal file

Binary file not shown.

View File

@@ -3,18 +3,39 @@
#include <llmr/map/map.hpp>
#include <llmr/util/image.hpp>
#include <llmr/util/io.hpp>
#include <llmr/util/timer.hpp>
#include <llmr/util/std.hpp>
#include <rapidjson/document.h>
#include "../common/headless_view.hpp"
#include <iostream>
#include <fstream>
const std::string base_directory = []{
std::string fn = __FILE__;
fn.erase(fn.find_last_of("/"));
return fn;
}();
TEST(Headless, initialize) {
const int width = 1024;
const int height = 768;
class HeadlessTest : public ::testing::TestWithParam<const char *> {};
llmr::util::timer timer;
TEST_P(HeadlessTest, render) {
const char *base = GetParam();
const std::string style = llmr::util::read_file(base_directory + "/fixtures/styles/" + base + ".style.json");
const std::string info = llmr::util::read_file(base_directory + "/fixtures/styles/" + base + ".info.json");
const std::string expected_image = base_directory + "/fixtures/styles/" + base + ".expected.png";
const std::string actual_image = base_directory + "/fixtures/styles/" + base + ".actual.png";
// Parse settings.
rapidjson::Document doc;
doc.Parse<0>((const char *const)info.c_str());
ASSERT_EQ(false, doc.HasParseError());
const double zoom = doc.HasMember("zoom") ? doc["zoom"].GetDouble() : 0;
const double angle = doc.HasMember("angle") ? doc["angle"].GetDouble() : 0;
const double longitude = doc.HasMember("longitude") ? doc["longitude"].GetDouble() : 0;
const double latitude = doc.HasMember("latitude") ? doc["latitude"].GetDouble() : 0;
const unsigned int width = doc.HasMember("width") ? doc["width"].GetUint() : 0;
const unsigned int height = doc.HasMember("height") ? doc["height"].GetUint() : 0;
// Setup OpenGL
llmr::HeadlessView view;
@@ -23,41 +44,27 @@ TEST(Headless, initialize) {
view.resize(width, height);
map.resize(width, height);
timer.report("map setup");
map.setStyleJSON(style);
std::ifstream stylefile("./style.min.js");
ASSERT_TRUE(stylefile.good());
std::stringstream stylejson;
stylejson << stylefile.rdbuf();
map.setStyleJSON(stylejson.str());
map.setLonLatZoom(0, 0, 2);
map.setAngle(0);
map.setLonLatZoom(longitude, latitude, zoom);
map.setAngle(angle);
map.setDebug(false);
timer.report("map style");
// Run the loop. It will terminate when we don't have any further listeners.
map.run();
timer.report("map loop");
const std::unique_ptr<uint32_t[]> pixels(new uint32_t[width * height]);
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.get());
uint32_t *pixels = new uint32_t[width * height];
const std::string image = llmr::util::compress_png(width, height, pixels.get(), true);
llmr::util::write_file(actual_image, image);
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
timer.report("gl readpixels");
std::string result = llmr::util::compress_png(width, height, pixels, true);
timer.report("compress png");
llmr::util::write_file("out.png", result);
timer.report("save file");
delete[] pixels;
timer.report("destruct");
const std::string expected_image_data(llmr::util::read_file(expected_image));
const llmr::util::Image expected(expected_image_data, true);
ASSERT_EQ(width, expected.getWidth());
ASSERT_EQ(height, expected.getHeight());
ASSERT_EQ(0, std::memcmp(pixels.get(), expected.getData(), width * height * 4));
}
INSTANTIATE_TEST_CASE_P(Headless, HeadlessTest,
::testing::Values("0", "1"));

View File

@@ -15,6 +15,11 @@
'-framework OpenGL',
],
},
'xcode_settings': {
'OTHER_LDFLAGS': [
'-framework OpenGL',
],
},
}, {
'link_settings': {
'libraries': [
@@ -161,16 +166,13 @@
"./headless.cpp",
"../common/headless_view.hpp",
"../common/headless_view.cpp",
"../common/curl_request.hpp",
"../common/curl_request.cpp",
"./fixtures/fixture_request.cpp",
],
"dependencies": [
"../deps/gtest/gtest.gyp:gtest",
"../llmr.gyp:llmr-x86",
"link_gl",
"link_curl",
'../llmr.gyp:copy_default_stylesheet',
'../llmr.gyp:copy_certificate_bundle',
]
},
{

View File

@@ -0,0 +1,74 @@
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "mapbox-gl-native travis resources",
"Resources": {
"BuildUser": {
"Type": "AWS::IAM::User",
"Properties": {
"Policies": [
{
"PolicyName": "list",
"PolicyDocument": {
"Statement": [
{
"Action": [
"s3:ListBucket"
],
"Effect": "Allow",
"Resource": [
"arn:aws:s3:::mapbox-gl-testing"
]
}
]
}
},
{
"PolicyName": "build",
"PolicyDocument": {
"Statement": [
{
"Action": [
"s3:DeleteObject",
"s3:GetObject",
"s3:GetObjectAcl",
"s3:PutObject",
"s3:PutObjectAcl"
],
"Effect": "Allow",
"Resource": [
"arn:aws:s3:::mapbox-gl-testing/*"
]
}
]
}
}
]
}
},
"BuildUserKey": {
"Type": "AWS::IAM::AccessKey",
"Properties": {
"UserName": {
"Ref": "BuildUser"
}
}
}
},
"Outputs": {
"AccessKeyId": {
"Value": {
"Ref": "BuildUserKey"
}
},
"SecretAccessKey": {
"Value": {
"Fn::GetAtt": [
"BuildUserKey",
"SecretAccessKey"
]
}
}
}
}