Merge pull request #98 from mapbox/test-gzip

add tests for gzipped vector tiles
This commit is contained in:
Saman Bemel-Benrud
2015-03-12 13:34:59 -04:00
10 changed files with 217 additions and 61 deletions

10
.gitignore vendored
View File

@@ -1,5 +1,5 @@
/build
/lib
/node_modules
/test/results
image.png
build
lib
node_modules
test/actual
test/diff

View File

@@ -41,27 +41,11 @@ The first argument passed to `map.render` is an options object, all keys are opt
longitude: {longitude}, // number (coordinate), defaults to 0
bearing: {bearing}, // number (in degrees, counter-clockwise from north), defaults to 0
ratio: {ratio} // number (scale factor), defaults to 1.0,
classes: {classes} // array of strings, optional *
classes: {classes} // array of strings, optional
}
```
* More about classes: https://github.com/mapbox/mapbox-gl-js/blob/master/API.md#working-with-style-classes
## Mapbox API Access tokens
To use styles that rely on Mapbox vector tiles, you must register a [API access token](https://www.mapbox.com/developers/api/#access-tokens) on the map. Use the `setAccessToken` method before load:
```js
var map = mbgl.Map(fileSource);
var style = mapboxStyle; // includes a datasource with a reference to something like `mapbox://mapbox.mapbox-streets-v6`
map.setAccessToken(process.env.MAPBOX_ACCESS_TOKEN);
map.load(style);
map.render({}, function(err, image) {
if (err) throw err;
fs.writeFileSync('image.png', image);
});
```
_More about classes: https://github.com/mapbox/mapbox-gl-js/blob/master/API.md#working-with-style-classes_
## Testing
@@ -170,3 +154,20 @@ fileSource.cancel = function(req) {
```
Note that in reality, Mapbox GL uses two types of protocols: `asset://` for files that should be loaded from some local static system, and `http://` (and `https://`), which should be loaded from the internet. However, stylesheets are free to use other protocols too, if your implementation of FileSource supports these; e.g. you could use `s3://` to indicate that files are supposed to be loaded from S3.
## Mapbox API Access tokens
To use styles that rely on Mapbox vector tiles, you must register a [API access token](https://www.mapbox.com/developers/api/#access-tokens) on the map. Use the `setAccessToken` method before load:
```js
var map = mbgl.Map(fileSource);
var style = mapboxStyle; // includes a datasource with a reference to something like `mapbox://mapbox.mapbox-streets-v6`
map.setAccessToken(process.env.MAPBOX_ACCESS_TOKEN);
map.load(style);
map.render({}, function(err, image) {
if (err) throw err;
fs.writeFileSync('image.png', image);
});
```

View File

@@ -28,8 +28,10 @@
"aws-sdk": "^2.1.9",
"mapbox-gl-test-suite": "git://github.com/mapbox/mapbox-gl-test-suite#master",
"mkdirp": "^0.5.0",
"request": "^2.53.0",
"tap-spec": "^2.2.1",
"tape": "^3.5.0"
"tape": "^3.5.0",
"st": "^0.5.3"
},
"scripts": {
"install": "node-pre-gyp install || make",

27
test/compare.js Normal file
View File

@@ -0,0 +1,27 @@
var spawn = require('child_process').spawn;
module.exports = function compare(actual, expected, diff, t, callback) {
var compare = spawn('compare', ['-metric', 'MAE', actual, expected, diff]);
var error = '';
compare.stderr.on('data', function(data) {
error += data.toString();
});
compare.on('error', function(err) {
t.error(err);
});
compare.on('exit', function (code) {
// The compare program returns 2 on error otherwise 0 if the images are similar or 1 if they are dissimilar.
if (code === 2) {
callback(error.trim(), Infinity);
} else {
var match = error.match(/^\d+(?:\.\d+)?\s+\(([^\)]+)\)\s*$/);
var difference = match ? parseFloat(match[1]) : Infinity;
callback(match ? '' : error, difference);
}
});
compare.stdin.end();
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

BIN
test/expected/map/image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

130
test/js/gzip.test.js Normal file
View File

@@ -0,0 +1,130 @@
'use strict';
/* jshint node: true */
var test = require('tape');
var mbgl = require('../..');
var fs = require('fs');
var path = require('path');
var mkdirp = require('mkdirp');
var http = require('http');
var request = require('request');
var st = require('st');
var style = require('../../test/fixtures/style.json');
var compare = require('../compare.js');
var dirPath = path.join(path.dirname(require.resolve('../../package.json')), 'test');
var server = http.createServer(st({ path: dirPath }));
server.listen(0);
function filePath(name) {
return ['expected', 'actual', 'diff'].reduce(function(prev, key) {
var dir = path.join('test', key, 'gzip');
mkdirp.sync(dir);
prev[key] = path.join(dir, name);
return prev;
}, {});
}
function setup(fileSource, callback) {
callback(new mbgl.Map(fileSource));
}
function getFileSource(gzip, t) {
var fileSource = new mbgl.FileSource();
fileSource.request = function(req) {
var parts = req.url.split('.');
var filetype = parts[parts.length - 1];
request({
url: 'http://localhost:' + server.address().port + path.join('/', req.url),
encoding: null,
gzip: filetype === 'pbf' ? gzip : true,
headers: {
'Accept-Encoding': 'gzip'
}
}, function (err, res, body) {
t.error(err);
var response = {};
response.data = res.body;
req.respond(null, response);
});
};
fileSource.cancel = function(req) {};
return fileSource;
}
test('gzip', function(t) {
t.test('success', function(t) {
mbgl.on('message', function(msg) {
t.error(msg);
});
setup(getFileSource(true, t), function(map) {
map.load(style);
map.render({}, function(err, image) {
mbgl.removeAllListeners('message');
mbgl.compressPNG(image, function(err, image) {
t.error(err);
var filename = filePath('success.png');
if (process.env.UPDATE) {
fs.writeFile(filename.expected, image, function(err) {
t.error(err);
t.end();
});
} else {
fs.writeFile(filename.actual, image, function(err) {
t.error(err);
compare(filename.actual, filename.expected, filename.diff, t, function(error, difference) {
t.ok(difference <= 0.01, 'actual matches expected');
t.end();
});
});
}
});
});
});
});
t.test('unhandled', function(t) {
mbgl.once('message', function(msg) {
t.ok(msg, 'emits error');
t.equal(msg.class, 'ParseTile');
t.equal(msg.severity, 'ERROR');
t.ok(msg.text.match(/failed: pbf unknown field type exception/), 'pbf unknown field type exception');
});
setup(getFileSource(false, t), function(map) {
map.load(style);
map.render({}, function(err, image) {
mbgl.compressPNG(image, function(err, image) {
t.error(err);
var filename = filePath('unhandled.png');
if (process.env.UPDATE) {
fs.writeFile(filename.expected, image, function(err) {
t.error(err);
server.close(t.end);
});
} else {
fs.writeFile(filename.actual, image, function(err) {
t.error(err);
compare(filename.actual, filename.expected, filename.diff, t, function(error, difference) {
t.ok(difference <= 0.001, 'actual matches expected');
server.close(t.end);
});
});
}
});
});
});
});
t.end();
});

View File

@@ -7,10 +7,17 @@ var mbgl = require('../..');
var fs = require('fs');
var path = require('path');
var mkdirp = require('mkdirp');
mkdirp.sync('test/results');
var style = require('../fixtures/style.json');
var compare = require('../compare.js');
function filePath(name) {
return ['expected', 'actual', 'diff'].reduce(function(prev, key) {
var dir = path.join('test', key, 'map');
mkdirp.sync(dir);
prev[key] = path.join(dir, name);
return prev;
}, {});
}
function setup(fileSource, callback) {
callback(new mbgl.Map(fileSource));
@@ -175,10 +182,25 @@ test('Map', function(t) {
map.load(style);
map.render({}, function(err, image) {
t.error(err);
mbgl.compressPNG(image, function(err, data) {
mbgl.compressPNG(image, function(err, image) {
t.error(err);
fs.writeFileSync('test/results/image.png', data);
t.end();
var filename = filePath('image.png');
if (process.env.UPDATE) {
fs.writeFile(filename.expected, image, function(err) {
t.error(err);
t.end();
});
} else {
fs.writeFile(filename.actual, image, function(err) {
t.error(err);
compare(filename.actual, filename.expected, filename.diff, t, function(error, difference) {
t.ok(difference <= 0.01, 'actual matches expected');
t.end();
});
});
}
});
});
});

View File

@@ -7,8 +7,7 @@ var mbgl = require('..');
var fs = require('fs');
var path = require('path');
var mkdirp = require('mkdirp');
var spawn = require('child_process').spawn;
var compare = require('./compare.js');
var suitePath = path.dirname(require.resolve('mapbox-gl-test-suite/package.json'));
function template(name) {
@@ -56,8 +55,8 @@ function renderTest(style, info, base, key) {
mkdirp.sync(dir);
var expected = path.join(dir, 'expected.png');
var actual = path.join(dir, 'actual.png');
var diff = path.join(dir, 'diff.png');
var actual = path.join(dir, 'actual.png');
var diff = path.join(dir, 'diff.png');
if (process.env.UPDATE) {
fs.writeFile(expected, image, function(err) {
@@ -67,32 +66,7 @@ function renderTest(style, info, base, key) {
} else {
fs.writeFile(actual, image, function(err) {
t.error(err);
var compare = spawn('compare', ['-metric', 'MAE', actual, expected, diff]);
var error = '';
compare.stderr.on('data', function (data) {
error += data.toString();
});
compare.on('error', function(err) {
t.error(err);
});
compare.on('exit', function (code) {
// The compare program returns 2 on error otherwise 0 if the images are similar or 1 if they are dissimilar.
if (code === 2) {
writeResult(error.trim(), Infinity);
} else {
var match = error.match(/^\d+(?:\.\d+)?\s+\(([^\)]+)\)\s*$/);
var difference = match ? parseFloat(match[1]) : Infinity;
writeResult(match ? '' : error, difference);
}
});
compare.stdin.end();
function writeResult(error, difference) {
compare(actual, expected, diff, t, function(error, difference) {
var allowedDifference = ('diff' in info) ? info.diff : 0.001;
var color = difference <= allowedDifference ? 'green' : 'red';
@@ -111,7 +85,7 @@ function renderTest(style, info, base, key) {
t.ok(difference <= allowedDifference, 'actual matches expected');
t.end();
}
});
});
}
});