Merge pull request #370 from mapbox/style-parser-test

Malformed stylesheet tests
This commit is contained in:
Konstantin Käfer
2014-08-12 14:19:05 +02:00
32 changed files with 2762 additions and 159 deletions

1
.gitignore vendored
View File

@@ -17,3 +17,4 @@
/src/shader/shaders_gles2.cpp
/bin/style.bin.js
/test/fixtures/styles/index.html
/test/fixtures/style_parser/styles

View File

@@ -3,7 +3,7 @@
var fs = require('fs');
var path = require('path');
var mkdirp = require('./mkdirp');
var mkdirp = require('mkdirp');
try { var glsl = require('mapbox-glsl-optimizer'); } catch(err) {}

View File

@@ -1,97 +0,0 @@
var path = require('path');
var fs = require('fs');
module.exports = mkdirP.mkdirp = mkdirP.mkdirP = mkdirP;
function mkdirP (p, opts, f, made) {
if (typeof opts === 'function') {
f = opts;
opts = {};
}
else if (!opts || typeof opts !== 'object') {
opts = { mode: opts };
}
var mode = opts.mode;
var xfs = opts.fs || fs;
if (mode === undefined) {
mode = 0777 & (~process.umask());
}
if (!made) made = null;
var cb = f || function () {};
p = path.resolve(p);
xfs.mkdir(p, mode, function (er) {
if (!er) {
made = made || p;
return cb(null, made);
}
switch (er.code) {
case 'ENOENT':
mkdirP(path.dirname(p), opts, function (er, made) {
if (er) cb(er, made);
else mkdirP(p, opts, cb, made);
});
break;
// In the case of any other error, just see if there's a dir
// there already. If so, then hooray! If not, then something
// is borked.
default:
xfs.stat(p, function (er2, stat) {
// if the stat fails, then that's super weird.
// let the original error be the failure reason.
if (er2 || !stat.isDirectory()) cb(er, made)
else cb(null, made);
});
break;
}
});
}
mkdirP.sync = function sync (p, opts, made) {
if (!opts || typeof opts !== 'object') {
opts = { mode: opts };
}
var mode = opts.mode;
var xfs = opts.fs || fs;
if (mode === undefined) {
mode = 0777 & (~process.umask());
}
if (!made) made = null;
p = path.resolve(p);
try {
xfs.mkdirSync(p, mode);
made = made || p;
}
catch (err0) {
switch (err0.code) {
case 'ENOENT' :
made = sync(path.dirname(p), opts, made);
sync(p, opts, made);
break;
// In the case of any other error, just see if there's a dir
// there already. If so, then hooray! If not, then something
// is borked.
default:
var stat;
try {
stat = xfs.statSync(p);
}
catch (err1) {
throw err0;
}
if (!stat.isDirectory()) throw err0;
break;
}
}
return made;
};

View File

@@ -1,7 +1,8 @@
{
"name": "mbgl-native",
"version": "0.0.1",
"dependencies": {
"mapbox-glsl-optimizer": "~0.1.0"
}
"name": "mbgl-native",
"version": "0.0.1",
"dependencies": {
"mapbox-glsl-optimizer": "~0.1.0",
"mkdirp": "^0.5.0"
}
}

9
configure vendored
View File

@@ -22,6 +22,11 @@ parser.add_option("--node",
dest="node",
help="Name of the node executable (defaults to node)")
parser.add_option("--npm",
action="store",
dest="npm",
help="Name of the npm executable (defaults to npm)")
parser.add_option("--pkg-config-root",
action="store",
dest="pkgconfig_root",
@@ -59,6 +64,10 @@ def configure_mbgl(o):
o['variables']['node'] = options.node
else:
o['variables']['node'] = 'node'
if options.npm:
o['variables']['npm'] = options.npm
else:
o['variables']['npm'] = 'npm'
o['target_defaults']['default_configuration'] = 'Debug' if options.debug else 'Release'

View File

@@ -28,6 +28,7 @@ public:
public:
Style();
~Style();
void loadJSON(const uint8_t *const data);

View File

@@ -8,6 +8,9 @@
'target_name': 'shaders',
'type': 'none',
'hard_dependency': 1,
'dependencies': [
'npm_install'
],
'actions': [
{
'action_name': 'Build Shaders',
@@ -33,6 +36,23 @@
]
}
},
{
'target_name': 'npm_install',
'type': 'none',
'hard_dependency': 1,
'actions': [
{
'action_name': 'npm install',
'inputs': [
'bin/package.json',
],
'outputs': [
'bin/node_modules',
],
'action': ['./scripts/npm_install.sh', '<@(npm)']
}
],
},
{
'target_name': 'touch_styles',
'type': 'none',
@@ -65,6 +85,20 @@
'destination': '<(PRODUCT_DIR)'
}],
},
{
'target_name': 'copy_fixtures',
'type': 'none',
'hard_dependency': 1,
'dependencies': [
'bundle_styles'
],
'copies': [
{
'files': [ 'styles' ],
'destination': 'test/fixtures/style_parser'
}
]
},
{
'target_name': 'copy_certificate_bundle',
'type': 'none',
@@ -90,7 +124,7 @@
'<!@(find include -name "*.hpp")',
'<!@(find include -name "*.h")',
'<!@(find src -name "*.glsl")',
'bin/style.js'
'bin/style.json'
],
'xcode_settings': {
'SDKROOT': 'macosx',
@@ -157,7 +191,7 @@
'<!@(find include -name "*.hpp")',
'<!@(find include -name "*.h")',
'<!@(find src -name "*.glsl")',
'bin/style.js'
'bin/style.json'
],
'xcode_settings': {
'SDKROOT': 'iphoneos',
@@ -213,4 +247,4 @@
}
}
]
}
}

8
scripts/npm_install.sh Executable file
View File

@@ -0,0 +1,8 @@
#!/usr/bin/env bash
set -e
set -o pipefail
cd bin
$1 install --clang=1
cd ../

View File

@@ -40,6 +40,9 @@ if [[ $MISSING_DEPS != "" ]]; then
exit 1
fi
NODE=`which node`
NPM=`which npm`
if [ ! -d 'mapnik-packaging/.git' ]; then
git clone --depth=1 https://github.com/mapnik/mapnik-packaging.git
fi
@@ -92,7 +95,8 @@ cd ../../
./configure \
--pkg-config-root=`pwd`/mapnik-packaging/osx/out/build-cpp11-libcpp-universal/lib/pkgconfig \
--boost=`pwd`/mapnik-packaging/osx/out/build-cpp11-libcpp-universal \
--node=`which node`
--npm=$NPM \
--node=$NODE
elif [ ${UNAME} = 'Linux' ]; then
@@ -122,5 +126,6 @@ cd ../../
./configure \
--pkg-config-root=`pwd`/mapnik-packaging/osx/out/build-cpp11-libstdcpp-gcc-x86_64-linux/lib/pkgconfig \
--boost=`pwd`/mapnik-packaging/osx/out/build-cpp11-libstdcpp-gcc-x86_64-linux \
--node=`which node`
--npm=$NPM \
--node=$NODE
fi

View File

@@ -19,6 +19,11 @@ Style::Style()
: mtx(std::make_unique<uv::rwlock>()) {
}
// Note: This constructor is seemingly empty, but we need to declare it anyway
// because this file includes uv_detail.hpp, which has the declarations necessary
// for deleting the std::unique_ptr<uv::rwlock>.
Style::~Style() {}
void Style::updateProperties(float z, timestamp now) {
uv::writelock lock(mtx);

View File

@@ -46,7 +46,7 @@ void StyleParser::parseConstants(JSVal value) {
}
}
} else {
throw Style::exception("constants must be an object");
Log::Warning(Event::ParseStyle, "constants must be an object");
}
}
@@ -84,7 +84,7 @@ template<> bool StyleParser::parseRenderProperty(JSVal value, std::string &targe
target = { property.GetString(), property.GetStringLength() };
return true;
} else {
fprintf(stderr, "[WARNING] '%s' must be a string\n", name);
Log::Warning(Event::ParseStyle, "'%s' must be a string", name);
}
}
return false;
@@ -97,7 +97,7 @@ template<> bool StyleParser::parseRenderProperty(JSVal value, float &target, con
target = property.GetDouble();
return true;
} else {
fprintf(stderr, "[WARNING] '%s' must be a number\n", name);
Log::Warning(Event::ParseStyle, "'%s' must be a number", name);
}
}
return false;
@@ -109,14 +109,14 @@ template<> bool StyleParser::parseRenderProperty(JSVal value, uint16_t &target,
if (property.IsUint()) {
unsigned int value = property.GetUint();
if (value > std::numeric_limits<uint16_t>::max()) {
fprintf(stderr, "[WARNING] values for %s that are larger than %d are not supported\n", name, std::numeric_limits<uint16_t>::max());
Log::Warning(Event::ParseStyle, "values for %s that are larger than %d are not supported", name, std::numeric_limits<uint16_t>::max());
return false;
}
target = value;
return true;
} else {
fprintf(stderr, "[WARNING] %s must be an unsigned integer\n", name);
Log::Warning(Event::ParseStyle, "%s must be an unsigned integer", name);
}
}
return false;
@@ -129,7 +129,7 @@ template<> bool StyleParser::parseRenderProperty(JSVal value, int32_t &target, c
target = property.GetInt();
return true;
} else {
fprintf(stderr, "[WARNING] %s must be an integer\n", name);
Log::Warning(Event::ParseStyle, "%s must be an integer", name);
}
}
return false;
@@ -144,10 +144,10 @@ template<> bool StyleParser::parseRenderProperty(JSVal value, vec2<float> &targe
target.y = property[(rapidjson::SizeType)1].GetDouble();
return true;
} else {
fprintf(stderr, "[WARNING] %s must have at least two members\n", name);
Log::Warning(Event::ParseStyle, "%s must have at least two members", name);
}
} else {
fprintf(stderr, "[WARNING] %s must be a n array of numbers\n", name);
Log::Warning(Event::ParseStyle, "%s must be an array of numbers", name);
}
}
return false;
@@ -161,7 +161,7 @@ bool StyleParser::parseRenderProperty(JSVal value, T &target, const char *name)
target = Parser({ property.GetString(), property.GetStringLength() });
return true;
} else {
fprintf(stderr, "[WARNING] %s must have one of the enum values\n", name);
Log::Warning(Event::ParseStyle, "%s must have one of the enum values", name);
}
}
return false;
@@ -192,7 +192,7 @@ void StyleParser::parseSources(JSVal value) {
sources.emplace(std::move(name), std::make_shared<StyleSource>(SourceInfo { type, url, tile_size, min_zoom, max_zoom }));
}
} else {
throw Style::exception("sources must be an object");
Log::Warning(Event::ParseStyle, "sources must be an object");
}
}
@@ -200,7 +200,7 @@ void StyleParser::parseSources(JSVal value) {
Color parseColor(JSVal value) {
if (!value.IsString()) {
fprintf(stderr, "[WARNING] color value must be a string\n");
Log::Warning(Event::ParseStyle, "color value must be a string");
return Color{{ 0, 0, 0, 0 }};
}
@@ -223,7 +223,7 @@ bool StyleParser::parseFunctionArgument(JSVal value) {
} else if (rvalue.IsNumber()) {
return rvalue.GetDouble();
} else {
fprintf(stderr, "[WARNING] function argument must be a boolean or numeric value");
Log::Warning(Event::ParseStyle, "function argument must be a boolean or numeric value");
return false;
}
}
@@ -234,7 +234,7 @@ float StyleParser::parseFunctionArgument(JSVal value) {
if (rvalue.IsNumber()) {
return rvalue.GetDouble();
} else {
fprintf(stderr, "[WARNING] function argument must be a numeric value");
Log::Warning(Event::ParseStyle, "function argument must be a numeric value");
return 0.0f;
}
}
@@ -251,7 +251,7 @@ template <> inline float defaultBaseValue<Color>() { return 1.0; }
template <typename T>
std::tuple<bool, Function<T>> StyleParser::parseFunction(JSVal value) {
if (!value.HasMember("stops")) {
fprintf(stderr, "[WARNING] stops function must specify a stops array\n");
Log::Warning(Event::ParseStyle, "function must specify a function type");
return std::tuple<bool, Function<T>> { false, ConstantFunction<T>(T()) };
}
@@ -262,13 +262,13 @@ std::tuple<bool, Function<T>> StyleParser::parseFunction(JSVal value) {
if (value_base.IsNumber()) {
base = value_base.GetDouble();
} else {
fprintf(stderr, "[WARNING] base must be numeric\n");
Log::Warning(Event::ParseStyle, "base must be numeric");
}
}
JSVal value_stops = value["stops"];
if (!value_stops.IsArray()) {
fprintf(stderr, "[WARNING] stops function must specify a stops array\n");
Log::Warning(Event::ParseStyle, "stops function must specify a stops array");
return std::tuple<bool, Function<T>> { false, ConstantFunction<T>(T()) };
}
@@ -277,19 +277,19 @@ std::tuple<bool, Function<T>> StyleParser::parseFunction(JSVal value) {
JSVal stop = value_stops[i];
if (stop.IsArray()) {
if (stop.Size() != 2) {
fprintf(stderr, "[WARNING] stop must have zoom level and value specification\n");
Log::Warning(Event::ParseStyle, "stop must have zoom level and value specification");
return std::tuple<bool, Function<T>> { false, ConstantFunction<T>(T()) };
}
JSVal z = stop[rapidjson::SizeType(0)];
if (!z.IsNumber()) {
fprintf(stderr, "[WARNING] zoom level in stop must be a number\n");
Log::Warning(Event::ParseStyle, "zoom level in stop must be a number");
return std::tuple<bool, Function<T>> { false, ConstantFunction<T>(T()) };
}
stops.emplace_back(z.GetDouble(), parseFunctionArgument<T>(stop[rapidjson::SizeType(1)]));
} else {
fprintf(stderr, "[WARNING] function argument must be a numeric value\n");
Log::Warning(Event::ParseStyle, "function argument must be a numeric value");
return std::tuple<bool, Function<T>> { false, ConstantFunction<T>(T()) };
}
}
@@ -352,7 +352,7 @@ bool StyleParser::parseOptionalProperty(const char *property_name, T &target, JS
template<> std::tuple<bool, std::string> StyleParser::parseProperty(JSVal value, const char *property_name) {
if (!value.IsString()) {
fprintf(stderr, "[WARNING] value of '%s' must be a string\n", property_name);
Log::Warning(Event::ParseStyle, "value of '%s' must be a string", property_name);
return std::tuple<bool, std::string> { false, std::string() };
}
@@ -361,7 +361,7 @@ template<> std::tuple<bool, std::string> StyleParser::parseProperty(JSVal value,
template<> std::tuple<bool, TranslateAnchorType> StyleParser::parseProperty(JSVal value, const char *property_name) {
if (!value.IsString()) {
fprintf(stderr, "[WARNING] value of '%s' must be a string\n", property_name);
Log::Warning(Event::ParseStyle, "value of '%s' must be a string", property_name);
return std::tuple<bool, TranslateAnchorType> { false, TranslateAnchorType::Map };
}
@@ -370,7 +370,7 @@ template<> std::tuple<bool, TranslateAnchorType> StyleParser::parseProperty(JSVa
template<> std::tuple<bool, RotateAnchorType> StyleParser::parseProperty<RotateAnchorType>(JSVal value, const char *property_name) {
if (!value.IsString()) {
fprintf(stderr, "[WARNING] value of '%s' must be a string\n", property_name);
Log::Warning(Event::ParseStyle, "value of '%s' must be a string", property_name);
return std::tuple<bool, RotateAnchorType> { false, RotateAnchorType::Map };
}
@@ -403,7 +403,7 @@ template<> std::tuple<bool, Function<bool>> StyleParser::parseProperty(JSVal val
} else if (value.IsBool()) {
return std::tuple<bool, Function<bool>> { true, ConstantFunction<bool>(value.GetBool()) };
} else {
fprintf(stderr, "[WARNING] value of '%s' must be convertible to boolean, or a boolean function\n", property_name);
Log::Warning(Event::ParseStyle, "value of '%s' must be convertible to boolean, or a boolean function", property_name);
return std::tuple<bool, Function<bool>> { false, ConstantFunction<bool>(false) };
}
}
@@ -416,7 +416,7 @@ template<> std::tuple<bool, Function<float>> StyleParser::parseProperty(JSVal va
} else if (value.IsBool()) {
return std::tuple<bool, Function<float>> { true, ConstantFunction<float>(value.GetBool()) };
} else {
fprintf(stderr, "[WARNING] value of '%s' must be a number, or a number function\n", property_name);
Log::Warning(Event::ParseStyle, "value of '%s' must be a number, or a number function", property_name);
return std::tuple<bool, Function<float>> { false, ConstantFunction<float>(0) };
}
}
@@ -427,7 +427,7 @@ template<> std::tuple<bool, Function<Color>> StyleParser::parseProperty(JSVal va
} else if (value.IsString()) {
return std::tuple<bool, Function<Color>> { true, ConstantFunction<Color>(parseColor(value)) };
} else {
fprintf(stderr, "[WARNING] value of '%s' must be a color, or a color function\n", property_name);
Log::Warning(Event::ParseStyle, "value of '%s' must be a color, or a color function", property_name);
return std::tuple<bool, Function<Color>> { false, ConstantFunction<Color>(Color {{ 0, 0, 0, 0 }}) };
}
}
@@ -437,11 +437,11 @@ bool StyleParser::parseOptionalProperty(const char *property_name, const std::ve
if (value.HasMember(property_name)) {
JSVal rvalue = replaceConstant(value[property_name]);
if (!rvalue.IsArray()) {
throw Style::exception("array value must be an array");
Log::Warning(Event::ParseStyle, "array value must be an array");
}
if (rvalue.Size() != keys.size()) {
throw Style::exception("array value has unexpected number of elements");
Log::Warning(Event::ParseStyle, "array value has unexpected number of elements");
}
for (uint16_t i = 0; i < keys.size(); i++) {
@@ -464,27 +464,27 @@ std::unique_ptr<StyleLayerGroup> StyleParser::createLayers(JSVal value) {
}
return group;
} else {
throw Style::exception("layers must be an array");
Log::Warning(Event::ParseStyle, "layers must be an array");
}
}
std::shared_ptr<StyleLayer> StyleParser::createLayer(JSVal value) {
if (value.IsObject()) {
if (!value.HasMember("id")) {
fprintf(stderr, "[WARNING] layer must have an id\n");
Log::Warning(Event::ParseStyle, "layer must have an id");
return nullptr;
}
JSVal id = value["id"];
if (!id.IsString()) {
fprintf(stderr, "[WARNING] layer id must be a string\n");
Log::Warning(Event::ParseStyle, "layer id must be a string");
return nullptr;
}
const std::string layer_id = { id.GetString(), id.GetStringLength() };
if (layers.find(layer_id) != layers.end()) {
fprintf(stderr, "[WARNING] duplicate layer id %s\n", layer_id.c_str());
Log::Warning(Event::ParseStyle, "duplicate layer id %s", layer_id.c_str());
return nullptr;
}
@@ -510,7 +510,7 @@ std::shared_ptr<StyleLayer> StyleParser::createLayer(JSVal value) {
return layer;
} else {
fprintf(stderr, "[WARNING] layer must be an object\n");
Log::Warning(Event::ParseStyle, "layer must be an object");
return nullptr;
}
}
@@ -533,7 +533,7 @@ void StyleParser::parseLayer(std::pair<JSVal, std::shared_ptr<StyleLayer>> &pair
if (value.HasMember("type")) {
JSVal type = value["type"];
if (!type.IsString()) {
fprintf(stderr, "[WARNING] layer type of '%s' must be a string\n", layer->id.c_str());
Log::Warning(Event::ParseStyle, "layer type of '%s' must be a string", layer->id.c_str());
} else {
layer->type = StyleLayerTypeClass(std::string { type.GetString(), type.GetStringLength() });
}
@@ -541,7 +541,7 @@ void StyleParser::parseLayer(std::pair<JSVal, std::shared_ptr<StyleLayer>> &pair
// Make sure we have not previously attempted to parse this layer.
if (std::find(stack.begin(), stack.end(), layer.get()) != stack.end()) {
fprintf(stderr, "[WARNING] layer reference of '%s' is circular\n", layer->id.c_str());
Log::Warning(Event::ParseStyle, "layer reference of '%s' is circular", layer->id.c_str());
return;
}
@@ -670,13 +670,13 @@ std::unique_ptr<RasterizeProperties> StyleParser::parseRasterize(JSVal value) {
void StyleParser::parseReference(JSVal value, std::shared_ptr<StyleLayer> &layer) {
if (!value.IsString()) {
fprintf(stderr, "[WARNING] layer ref of '%s' must be a string\n", layer->id.c_str());
Log::Warning(Event::ParseStyle, "layer ref of '%s' must be a string", layer->id.c_str());
return;
}
const std::string ref { value.GetString(), value.GetStringLength() };
auto it = layers.find(ref);
if (it == layers.end()) {
fprintf(stderr, "[WARNING] layer '%s' references unknown layer %s\n", layer->id.c_str(), ref.c_str());
Log::Warning(Event::ParseStyle, "layer '%s' references unknown layer %s", layer->id.c_str(), ref.c_str());
// We cannot parse this layer further.
return;
}
@@ -692,7 +692,7 @@ void StyleParser::parseReference(JSVal value, std::shared_ptr<StyleLayer> &layer
layer->type = reference->type;
if (reference->layers) {
fprintf(stderr, "[WARNING] layer '%s' references composite layer\n", layer->id.c_str());
Log::Warning(Event::ParseStyle, "layer '%s' references composite layer", layer->id.c_str());
// We cannot parse this layer further.
return;
} else {
@@ -716,11 +716,10 @@ void StyleParser::parseBucket(JSVal value, std::shared_ptr<StyleLayer> &layer) {
if (source_it != sources.end()) {
layer->bucket->style_source = source_it->second;
} else {
fprintf(stderr, "[WARNING] can't find source '%s' required for layer '%s'\n",
source_name.c_str(), layer->id.c_str());
Log::Warning(Event::ParseStyle, "can't find source '%s' required for layer '%s'", source_name.c_str(), layer->id.c_str());
}
} else {
fprintf(stderr, "[WARNING] source of layer '%s' must be a string\n", layer->id.c_str());
Log::Warning(Event::ParseStyle, "source of layer '%s' must be a string", layer->id.c_str());
}
}
@@ -729,7 +728,7 @@ void StyleParser::parseBucket(JSVal value, std::shared_ptr<StyleLayer> &layer) {
if (value_source_layer.IsString()) {
layer->bucket->source_layer = { value_source_layer.GetString(), value_source_layer.GetStringLength() };
} else {
fprintf(stderr, "[WARNING] source-layer of layer '%s' must be a string\n", layer->id.c_str());
Log::Warning(Event::ParseStyle, "source-layer of layer '%s' must be a string", layer->id.c_str());
}
}
@@ -809,7 +808,7 @@ FilterExpression StyleParser::parseFilter(JSVal value, FilterExpression::Operato
expression.add(parseFilter(replaceConstant(value[i])));
}
} else {
fprintf(stderr, "[WARNING] expression must be either an array or an object\n");
Log::Warning(Event::ParseStyle, "expression must be either an array or an object");
}
return expression;
@@ -851,7 +850,7 @@ std::vector<Value> StyleParser::parseValues(JSVal value) {
void StyleParser::parseRender(JSVal value, std::shared_ptr<StyleLayer> &layer) {
if (!value.IsObject()) {
fprintf(stderr, "[WARNING] render property of layer '%s' must be an object\n", layer->id.c_str());
Log::Warning(Event::ParseStyle, "render property of layer '%s' must be an object", layer->id.c_str());
return;
}

View File

@@ -44,4 +44,4 @@ std::vector<FixtureLogBackend::LogMessage> FixtureLogBackend::unchecked() const
return os << "]" << std::endl;
}
}
}

View File

@@ -0,0 +1,6 @@
{
"default": {
"log": [
]
}
}

View File

@@ -0,0 +1,40 @@
{
"version": 3,
"sources": {
"mapbox": {
"type": "vector",
"url": "mapbox://mapbox.mapbox-terrain-v1,mapbox.mapbox-streets-v5",
"maxZoom": 14
}
},
"constants": {
"@land":"r44,239,225)",
"@snow":"f4f8foNGbjf#",
"@crop":"#eerLznieed4"
},
"layers": [{
"id": "background",
"type": "background",
"style": {
"background-color": "@land"
}
}, {
"id": "landcover_snow",
"source": "mapbox",
"source-layer": "landcover",
"filter": { "class": "snow" },
"type": "fill",
"style": {
"fill-color": "@snow"
}
}, {
"id": "landcover_crop",
"source": "mapbox",
"source-layer": "landcover",
"filter": { "class": "crop" },
"type": "fill",
"style": {
"fill-color": "@crop"
}
}]
}

View File

@@ -0,0 +1,6 @@
{
"default": {
"log": [
]
}
}

2244
test/fixtures/style_parser/default.style.json vendored Executable file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,7 @@
{
"default": {
"log": [
[1, "WARNING", "ParseStyle", "function argument must be a numeric value"]
]
}
}

View File

@@ -0,0 +1,22 @@
{
"version": 3,
"sources": {
"mapbox": {
"type": "vector",
"url": "mapbox://mapbox.mapbox-terrain-v1,mapbox.mapbox-streets-v5",
"maxZoom": 14
}
},
"layers": [{
"id": "waterway_river_canal",
"source": "mapbox",
"source-layer": "waterway",
"filter": { "type": ["river", "canal"] },
"type": "line",
"style": {
"line-width": {
"stops": [0]
}
}
}]
}

View File

@@ -0,0 +1,7 @@
{
"default": {
"log": [
[1, "WARNING", "ParseStyle", "function must specify a function type"]
]
}
}

View File

@@ -0,0 +1,20 @@
{
"version": 3,
"sources": {
"mapbox": {
"type": "vector",
"url": "mapbox://mapbox.mapbox-terrain-v1,mapbox.mapbox-streets-v5",
"maxZoom": 14
}
},
"layers": [{
"id": "waterway_river_canal",
"source": "mapbox",
"source-layer": "waterway",
"filter": { "type": ["river", "canal"] },
"type": "line",
"style": {
"line-width": {}
}
}]
}

View File

@@ -0,0 +1,7 @@
{
"default": {
"log": [
[1, "WARNING", "ParseStyle", "value of 'line-opacity' must be a number, or a number function"]
]
}
}

View File

@@ -0,0 +1,22 @@
{
"version": 3,
"sources": {
"mapbox": {
"type": "vector",
"url": "mapbox://mapbox.mapbox-terrain-v1,mapbox.mapbox-streets-v5",
"maxZoom": 14
}
},
"layers": [{
"id": "contour_line_loud",
"source": "mapbox",
"source-layer": "contour",
"filter": { "index": 5 },
"type": "line",
"style": {
"line-color": "#008",
"line-width": 0.9,
"line-opacity": null
}
}]
}

View File

@@ -0,0 +1,7 @@
{
"default": {
"log": [
[1, "WARNING", "ParseStyle", "value of 'line-width' must be a number, or a number function"]
]
}
}

View File

@@ -0,0 +1,20 @@
{
"version": 3,
"sources": {
"mapbox": {
"type": "vector",
"url": "mapbox://mapbox.mapbox-terrain-v1,mapbox.mapbox-streets-v5",
"maxZoom": 14
}
},
"layers": [{
"id": "waterway_river_canal",
"source": "mapbox",
"source-layer": "waterway",
"filter": { "type": ["river", "canal"] },
"type": "line",
"style": {
"line-width": null
}
}]
}

View File

@@ -0,0 +1,7 @@
{
"default": {
"log": [
[1, "WARNING", "ParseStyle", "stop must have zoom level and value specification"]
]
}
}

View File

@@ -0,0 +1,45 @@
{
"version": 3,
"sources": {
"mapbox": {
"type": "vector",
"url": "mapbox://mapbox.mapbox-terrain-v1,mapbox.mapbox-streets-v5",
"maxZoom": 14
}
},
"layers": [{
"id": "landcover_grass",
"source": "mapbox",
"source-layer": "landcover",
"filter": { "class": "grass" },
"type": "fill",
"style": {
"fill-opacity": {
"stops": []
}
}
}, {
"id": "landcover_scrub",
"source": "mapbox",
"source-layer": "landcover",
"filter": { "class": "scrub" },
"type": "fill",
"style": {
"fill-opacity": {
"stops": [[12]]
}
}
}, {
"id": "landcover_wood",
"source": "mapbox",
"source-layer": "landcover",
"filter": { "class": "wood" },
"type": "fill",
"style": {
"fill-color": "@wood",
"fill-opacity": {
"stops": [[12, 1], [13, 0.8], [16, 0.2]]
}
}
}]
}

View File

@@ -0,0 +1,7 @@
{
"default": {
"log": [
[1, "WARNING", "ParseStyle", "stops function must specify a stops array"]
]
}
}

View File

@@ -0,0 +1,22 @@
{
"version": 3,
"sources": {
"mapbox": {
"type": "vector",
"url": "mapbox://mapbox.mapbox-terrain-v1,mapbox.mapbox-streets-v5",
"maxZoom": 14
}
},
"layers": [{
"id": "waterway_river_canal",
"source": "mapbox",
"source-layer": "waterway",
"filter": { "type": ["river", "canal"] },
"type": "line",
"style": {
"line-width": {
"stops": null
}
}
}]
}

View File

@@ -0,0 +1,7 @@
{
"default": {
"log": [
[1, "WARNING", "ParseStyle", "value of 'text-size' must be a number, or a number function"]
]
}
}

View File

@@ -0,0 +1,20 @@
{
"version": 3,
"sources": {
"mapbox": {
"type": "vector",
"url": "mapbox://mapbox.mapbox-terrain-v1,mapbox.mapbox-streets-v5",
"maxZoom": 14
}
},
"layers": [{
"id": "country_label",
"source": "mapbox",
"source-layer": "country_label",
"filter": { "$type": "Point" },
"type": "text",
"style": {
"text-size": null
}
}]
}

101
test/style_parser.cpp Normal file
View File

@@ -0,0 +1,101 @@
#include "gtest/gtest.h"
#include <mbgl/style/style.hpp>
#include <mbgl/util/io.hpp>
#include <rapidjson/document.h>
#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;
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 style_path = base + ".style.json";
const std::string info = util::read_file(base + ".info.json");
// Parse settings.
rapidjson::Document doc;
doc.Parse<0>((const char *const)info.c_str());
ASSERT_EQ(false, doc.HasParseError());
ASSERT_EQ(true, doc.IsObject());
std::ifstream stylefile(style_path);
ASSERT_TRUE(stylefile.good());
std::stringstream stylejson;
stylejson << stylefile.rdbuf();
const FixtureLogBackend &log = Log::Set<FixtureLogBackend>();
Style style;
style.loadJSON((const uint8_t *)stylejson.str().c_str());
for (auto it = doc.MemberBegin(), end = doc.MemberEnd(); it != end; it++) {
const std::string name { it->name.GetString(), it->name.GetStringLength() };
const rapidjson::Value &value = it->value;
ASSERT_EQ(true, value.IsObject());
if (value.HasMember("log")) {
const rapidjson::Value &js_log = value["log"];
ASSERT_EQ(true, js_log.IsArray());
for (rapidjson::SizeType i = 0; i < js_log.Size(); i++) {
const rapidjson::Value &js_entry = js_log[i];
ASSERT_EQ(true, js_entry.IsArray());
const uint32_t count = js_entry[rapidjson::SizeType(0)].GetUint();
const FixtureLogBackend::LogMessage message {
EventSeverityClass(js_entry[rapidjson::SizeType(1)].GetString()),
EventClass(js_entry[rapidjson::SizeType(2)].GetString()),
js_entry[rapidjson::SizeType(3)].GetString()
};
EXPECT_EQ(count, log.count(message)) << "Message: " << message << std::endl;
}
}
const auto &unchecked = log.unchecked();
if (unchecked.size()) {
std::cerr << "Unchecked Log Messages (" << base << "/" << name << "): " << std::endl << unchecked;
}
ASSERT_EQ(0, unchecked.size());
}
}
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()));
}
}
closedir(dir);
return names;
}()));

View File

@@ -71,7 +71,7 @@
],
"dependencies": [
"../deps/gtest/gtest.gyp:gtest",
"../mapboxgl.gyp:mapboxgl"
"../mapboxgl.gyp:mapboxgl",
]
},
{
@@ -87,7 +87,7 @@
],
"dependencies": [
"../deps/gtest/gtest.gyp:gtest",
"../mapboxgl.gyp:mapboxgl"
"../mapboxgl.gyp:mapboxgl",
]
},
{
@@ -103,7 +103,26 @@
],
"dependencies": [
"../deps/gtest/gtest.gyp:gtest",
"../mapboxgl.gyp:mapboxgl"
"../mapboxgl.gyp:mapboxgl",
]
},
{
"target_name": "style_parser",
"product_name": "test_style_parser",
"type": "executable",
"libraries": [
"-lpthread",
],
"sources": [
"./main.cpp",
"./style_parser.cpp",
"./fixtures/fixture_log.hpp",
"./fixtures/fixture_log.cpp",
],
"dependencies": [
"../deps/gtest/gtest.gyp:gtest",
"../mapboxgl.gyp:mapboxgl",
"../mapboxgl.gyp:copy_fixtures",
]
},
{
@@ -119,7 +138,7 @@
],
"dependencies": [
"../deps/gtest/gtest.gyp:gtest",
"../mapboxgl.gyp:mapboxgl"
"../mapboxgl.gyp:mapboxgl",
]
},
{
@@ -135,7 +154,7 @@
],
"dependencies": [
"../deps/gtest/gtest.gyp:gtest",
"../mapboxgl.gyp:mapboxgl"
"../mapboxgl.gyp:mapboxgl",
]
},
{
@@ -151,7 +170,7 @@
],
"dependencies": [
"../deps/gtest/gtest.gyp:gtest",
"../mapboxgl.gyp:mapboxgl"
"../mapboxgl.gyp:mapboxgl",
]
},
{
@@ -167,7 +186,7 @@
],
"dependencies": [
"../deps/gtest/gtest.gyp:gtest",
"../mapboxgl.gyp:mapboxgl"
"../mapboxgl.gyp:mapboxgl",
]
},
{
@@ -203,6 +222,7 @@
"tile",
"functions",
"headless",
"style_parser",
"comparisons",
],
}