summaryrefslogtreecommitdiff
path: root/platform/node/test
diff options
context:
space:
mode:
Diffstat (limited to 'platform/node/test')
-rw-r--r--platform/node/test/compare.js27
-rw-r--r--platform/node/test/expected/gzip/success.pngbin0 -> 42195 bytes
-rw-r--r--platform/node/test/expected/gzip/unhandled.pngbin0 -> 5636 bytes
-rw-r--r--platform/node/test/expected/map/image.pngbin0 -> 42195 bytes
-rw-r--r--platform/node/test/fixtures/style.json31
-rw-r--r--platform/node/test/fixtures/tiles/0-0-0.vector.pbfbin0 -> 9660 bytes
-rw-r--r--platform/node/test/js/consecutive.test.js82
-rw-r--r--platform/node/test/js/gzip.test.js125
-rw-r--r--platform/node/test/js/map.test.js280
-rw-r--r--platform/node/test/render.test.js175
10 files changed, 720 insertions, 0 deletions
diff --git a/platform/node/test/compare.js b/platform/node/test/compare.js
new file mode 100644
index 0000000000..3e5221de55
--- /dev/null
+++ b/platform/node/test/compare.js
@@ -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();
+};
diff --git a/platform/node/test/expected/gzip/success.png b/platform/node/test/expected/gzip/success.png
new file mode 100644
index 0000000000..de41e0fe2b
--- /dev/null
+++ b/platform/node/test/expected/gzip/success.png
Binary files differ
diff --git a/platform/node/test/expected/gzip/unhandled.png b/platform/node/test/expected/gzip/unhandled.png
new file mode 100644
index 0000000000..ddb90d8b8f
--- /dev/null
+++ b/platform/node/test/expected/gzip/unhandled.png
Binary files differ
diff --git a/platform/node/test/expected/map/image.png b/platform/node/test/expected/map/image.png
new file mode 100644
index 0000000000..de41e0fe2b
--- /dev/null
+++ b/platform/node/test/expected/map/image.png
Binary files differ
diff --git a/platform/node/test/fixtures/style.json b/platform/node/test/fixtures/style.json
new file mode 100644
index 0000000000..222ac82bf8
--- /dev/null
+++ b/platform/node/test/fixtures/style.json
@@ -0,0 +1,31 @@
+{
+ "version": 8,
+ "name": "Empty",
+ "sources": {
+ "mapbox": {
+ "type": "vector",
+ "maxzoom": 15,
+ "tiles": [
+ "./fixtures/tiles/{z}-{x}-{y}.vector.pbf"
+ ]
+ }
+ },
+ "layers": [
+ {
+ "id": "background",
+ "type": "background",
+ "paint": {
+ "background-color": "white"
+ }
+ },
+ {
+ "id": "water",
+ "type": "fill",
+ "source": "mapbox",
+ "source-layer": "water",
+ "paint": {
+ "fill-color": "blue"
+ }
+ }
+ ]
+}
diff --git a/platform/node/test/fixtures/tiles/0-0-0.vector.pbf b/platform/node/test/fixtures/tiles/0-0-0.vector.pbf
new file mode 100644
index 0000000000..87628e06cf
--- /dev/null
+++ b/platform/node/test/fixtures/tiles/0-0-0.vector.pbf
Binary files differ
diff --git a/platform/node/test/js/consecutive.test.js b/platform/node/test/js/consecutive.test.js
new file mode 100644
index 0000000000..928ca1d837
--- /dev/null
+++ b/platform/node/test/js/consecutive.test.js
@@ -0,0 +1,82 @@
+'use strict';
+
+/* jshint node:true */
+
+var test = require('tape');
+var mbgl = require('../../../..');
+var fs = require('fs');
+var path = require('path');
+
+var suitePath = path.join(__dirname, '../../../../test/suite');
+
+function renderTest(style, info, dir, key) {
+ return function (t) {
+ var completed = 0;
+ var remaining = 10;
+ var start = +new Date;
+
+ var options = {};
+ options.request = function(req) {
+ fs.readFile(path.join(suitePath, decodeURIComponent(req.url)), function(err, data) {
+ req.respond(err, { data: data });
+ t.error(err);
+ });
+ };
+ options.cancel = function() {};
+ options.ratio = 1.0;
+
+ var map = new mbgl.Map(options);
+ map.load(style);
+
+ function render() {
+ map.render(info[key], function(err, image) {
+ t.error(err);
+
+ t.ok(true, 'render @ ' + ((+new Date) - start) + 'ms');
+ if (++completed === remaining) {
+ map.release();
+ t.end();
+ } else {
+ render();
+ }
+ });
+ }
+
+ render();
+ };
+}
+
+function rewriteLocalSchema(uri) {
+ return uri.replace(/^local:\/\//, '');
+}
+
+test('Consecutive', function(t) {
+ var dir = 'line-join';
+ var k = 'round';
+
+ var style = require(path.join(suitePath, 'tests', dir, 'style.json')),
+ info = require(path.join(suitePath, 'tests', dir, 'info.json'));
+
+ for (var k in style.sources) {
+ var source = style.sources[k];
+
+ if (source.tiles) {
+ source.tiles = source.tiles.map(rewriteLocalSchema);
+ }
+
+ if (source.url) {
+ source.url = rewriteLocalSchema(source.url);
+ }
+ }
+
+ if (style.sprite) style.sprite = rewriteLocalSchema(style.sprite);
+ if (style.glyphs) style.glyphs = rewriteLocalSchema(style.glyphs);
+
+ style = JSON.stringify(style);
+
+ for (k in info) {
+ t.test(dir + ' ' + k, renderTest(style, info, dir, k));
+ }
+
+ t.end();
+});
diff --git a/platform/node/test/js/gzip.test.js b/platform/node/test/js/gzip.test.js
new file mode 100644
index 0000000000..4545a18cf5
--- /dev/null
+++ b/platform/node/test/js/gzip.test.js
@@ -0,0 +1,125 @@
+'use strict';
+
+/* jshint node: true */
+
+var test = require('tape').test;
+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('../fixtures/style.json');
+var PNG = require('pngjs').PNG;
+var compare = require('../compare.js');
+
+var server = http.createServer(st({ path: path.join(__dirname, '..') }));
+server.listen(0);
+
+function filePath(name) {
+ return ['expected', 'actual', 'diff'].reduce(function(prev, key) {
+ var dir = path.join(__dirname, '..', key, 'gzip');
+ mkdirp.sync(dir);
+ prev[key] = path.join(dir, name);
+ return prev;
+ }, {});
+}
+
+function setup(options, callback) {
+ callback(new mbgl.Map(options));
+}
+
+function getOptions(gzip, t) {
+ return {
+ 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);
+ });
+ },
+ ratio: 1.0
+ };
+}
+
+test('gzip', function(t) {
+ t.test('success', function(t) {
+ mbgl.on('message', function(msg) {
+ if (msg.severity == 'ERROR') t.error(msg);
+ });
+
+ setup(getOptions(true, t), function(map) {
+ map.load(style);
+ map.render({}, function(err, data) {
+ mbgl.removeAllListeners('message');
+ map.release();
+
+ t.error(err);
+
+ var filename = filePath('success.png');
+
+ var png = new PNG({
+ width: data.width,
+ height: data.height
+ });
+
+ png.data = data.pixels;
+
+ if (process.env.UPDATE) {
+ png.pack()
+ .pipe(fs.createWriteStream(filename.expected))
+ .on('finish', t.end);
+ } else {
+ png.pack()
+ .pipe(fs.createWriteStream(filename.actual))
+ .on('finish', function() {
+ compare(filename.actual, filename.expected, filename.diff, t, function(err, diff) {
+ t.error(err);
+ t.ok(diff <= 0.01, 'actual matches expected');
+ t.end();
+ });
+ });
+ }
+ });
+ });
+ });
+
+ t.test('unhandled', function(t) {
+ mbgl.once('message', function(msg) {
+ if (msg.severity == 'ERROR') {
+ t.ok(msg, 'emits error');
+ t.equal(msg.class, 'Style');
+ t.equal(msg.severity, 'ERROR');
+ t.ok(msg.text.match(/pbf unknown field type exception/), 'error text matches');
+ }
+ });
+
+ setup(getOptions(false, t), function(map) {
+ map.load(style);
+ map.render({}, function(err, data) {
+ map.release();
+
+ t.ok(err, 'returns error');
+ t.ok(err.message.match(/Failed to parse/), 'error text matches');
+
+ t.end();
+ });
+ });
+ });
+
+ t.test('teardown', function(t) {
+ server.close(t.end);
+ });
+});
diff --git a/platform/node/test/js/map.test.js b/platform/node/test/js/map.test.js
new file mode 100644
index 0000000000..09870abaaa
--- /dev/null
+++ b/platform/node/test/js/map.test.js
@@ -0,0 +1,280 @@
+'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 style = require('../fixtures/style.json');
+var PNG = require('pngjs').PNG;
+var compare = require('../compare.js');
+
+function filePath(name) {
+ return ['expected', 'actual', 'diff'].reduce(function(prev, key) {
+ var dir = path.join(__dirname, '..', key, 'map');
+ mkdirp.sync(dir);
+ prev[key] = path.join(dir, name);
+ return prev;
+ }, {});
+}
+
+test('Map', function(t) {
+ t.test('must be constructed with new', function(t) {
+ t.throws(function() {
+ mbgl.Map();
+ }, /Use the new operator to create new Map objects/);
+
+ t.end();
+ });
+
+ t.test('must be constructed with options object', function(t) {
+ t.throws(function() {
+ new mbgl.Map();
+ }, /Requires an options object as first argument/);
+
+ t.throws(function() {
+ new mbgl.Map('options');
+ }, /Requires an options object as first argument/);
+
+ t.end();
+ });
+
+ t.test('requires request and ratio options', function(t) {
+ var options = {};
+
+ t.throws(function() {
+ new mbgl.Map(options);
+ }, /Options object must have a 'request' method/);
+
+ options.request = 'test';
+ t.throws(function() {
+ new mbgl.Map(options);
+ }, /Options object must have a 'request' method/);
+
+ options.request = function() {};
+ options.cancel = 'test';
+ t.throws(function() {
+ new mbgl.Map(options);
+ }, /Options object 'cancel' property must be a function/);
+
+ options.cancel = function() {};
+ t.throws(function() {
+ new mbgl.Map(options);
+ }, /Options object must have a numerical 'ratio' property/);
+
+ options.ratio = 'test';
+ t.throws(function() {
+ new mbgl.Map(options);
+ }, /Options object must have a numerical 'ratio' property/);
+
+ options.ratio = 1.0;
+ t.doesNotThrow(function() {
+ new mbgl.Map(options);
+ });
+
+ t.end();
+ });
+
+ t.test('.load', function(t) {
+ var options = {
+ request: function() {},
+ ratio: 1
+ };
+
+ t.test('requires a map style as first argument', function(t) {
+ var map = new mbgl.Map(options);
+
+ t.throws(function() {
+ map.load();
+ }, /Requires a map style as first argument/);
+
+ map.release();
+ t.end();
+ });
+
+ t.test('expect either an object or array at root', { timeout: 1000 }, function(t) {
+ var map = new mbgl.Map(options);
+
+ mbgl.once('message', function(msg) {
+ t.equal(msg.severity, 'ERROR');
+ t.equal(msg.class, 'ParseStyle');
+ t.ok(msg.text.match(/Expect either an object or array at root/));
+
+ map.release();
+ t.end();
+ });
+
+ map.load('invalid');
+ });
+
+ t.test('accepts an empty stylesheet string', function(t) {
+ var map = new mbgl.Map(options);
+
+ t.doesNotThrow(function() {
+ map.load('{}');
+ });
+
+ map.release();
+ t.end();
+ });
+
+ t.test('accepts a JSON stylesheet', { timeout: 1000 }, function(t) {
+ var map = new mbgl.Map(options);
+
+ t.doesNotThrow(function() {
+ map.load(style);
+ });
+
+ map.release();
+ t.end();
+ });
+
+ t.test('accepts a stringified stylesheet', { timeout: 1000 }, function(t) {
+ var map = new mbgl.Map(options);
+
+ t.doesNotThrow(function() {
+ map.load(JSON.stringify(style));
+ });
+
+ map.release();
+ t.end();
+ });
+
+ t.test('does not immediately trigger any tile loads', function(t) {
+ var map = new mbgl.Map({
+ request: function(req) {
+ t.fail('unexpected request ' + req.url);
+ },
+ ratio: 1
+ });
+
+ map.load(style);
+
+ setTimeout(function() {
+ map.release();
+ t.end();
+ }, 100);
+ });
+ });
+
+ t.test('.render', function(t) {
+ var options = {
+ request: function(req) {
+ fs.readFile(path.join(__dirname, '..', req.url), function(err, data) {
+ req.respond(err, { data: data });
+ });
+ },
+ ratio: 1
+ };
+
+ t.test('requires an object as the first parameter', function(t) {
+ var map = new mbgl.Map(options);
+
+ t.throws(function() {
+ map.render();
+ }, /First argument must be an options object/);
+
+ t.throws(function() {
+ map.render('invalid');
+ }, /First argument must be an options object/);
+
+ map.release();
+ t.end();
+ });
+
+ t.test('requires a callback as the second parameter', function(t) {
+ var map = new mbgl.Map(options);
+
+ t.throws(function() {
+ map.render({});
+ }, /Second argument must be a callback function/);
+
+ t.throws(function() {
+ map.render({}, 'invalid');
+ }, /Second argument must be a callback function/);
+
+ map.release();
+ t.end();
+ });
+
+ t.test('requires a style to be set', function(t) {
+ var map = new mbgl.Map(options);
+
+ t.throws(function() {
+ map.render({}, function() {});
+ }, /Style is not loaded/);
+
+ map.release();
+ t.end();
+ });
+
+ t.test('returns an error', function(t) {
+ mbgl.on('message', function(msg) {
+ t.ok(msg, 'emits error');
+ t.equal(msg.class, 'Style');
+ t.equal(msg.severity, 'ERROR');
+ t.ok(msg.text.match(/Failed to load/), 'error text matches');
+ });
+
+ var map = new mbgl.Map(options);
+ map.load(style);
+ map.render({ zoom: 1 }, function(err, data) {
+ mbgl.removeAllListeners('message');
+ map.release();
+
+ t.ok(err, 'returns error');
+ t.ok(err.message.match(/Failed to load/), 'error text matches');
+
+ t.end();
+ });
+ });
+
+ t.test('double release', function(t) {
+ var map = new mbgl.Map(options);
+ map.release();
+
+ t.throws(function() {
+ map.release();
+ }, /Map resources have already been released/);
+
+ t.end();
+ });
+
+ t.test('returns an image', function(t) {
+ var map = new mbgl.Map(options);
+ map.load(style);
+ map.render({}, function(err, data) {
+ t.error(err);
+
+ map.release();
+
+ var filename = filePath('image.png');
+
+ var png = new PNG({
+ width: data.width,
+ height: data.height
+ });
+
+ png.data = data.pixels;
+
+ if (process.env.UPDATE) {
+ png.pack()
+ .pipe(fs.createWriteStream(filename.expected))
+ .on('finish', t.end);
+ } else {
+ png.pack()
+ .pipe(fs.createWriteStream(filename.actual))
+ .on('finish', function() {
+ compare(filename.actual, filename.expected, filename.diff, t, function(err, diff) {
+ t.error(err);
+ t.ok(diff <= 0.01, 'actual matches expected');
+ t.end();
+ });
+ });
+ }
+ });
+ });
+ });
+});
diff --git a/platform/node/test/render.test.js b/platform/node/test/render.test.js
new file mode 100644
index 0000000000..10a7afcb52
--- /dev/null
+++ b/platform/node/test/render.test.js
@@ -0,0 +1,175 @@
+'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 PNG = require('pngjs').PNG;
+var compare = require('./compare.js');
+var suitePath = path.join(__dirname, '../../../test/suite');
+
+function template(name) {
+ return fs.readFileSync(path.join(suitePath, 'templates', name + '.html.tmpl')).toString();
+}
+
+var results = '';
+var resultTemplate = template('result');
+
+function format(tmpl, kwargs) {
+ return tmpl.replace(/\{\{|\}\}|\{([^}]+)\}/g, function(match, key) {
+ if (match === '{{') return '{';
+ if (match === '}}') return '}';
+ return kwargs[key];
+ });
+}
+
+function renderTest(style, info, base, key) {
+ var dir = path.join(suitePath, 'tests', base, key);
+ mkdirp.sync(dir);
+
+ return function(t) {
+ var watchdog = setTimeout(function() {
+ t.fail('timed out after 20 seconds');
+ }, 20000);
+
+ t.once('end', function() {
+ clearTimeout(watchdog);
+
+ if (map) {
+ map.release();
+ map = null;
+ }
+ });
+
+ var options = {};
+ options.request = function(req) {
+ var url = decodeURIComponent(req.url);
+ fs.readFile(path.join(suitePath, url), function(err, data) {
+ req.respond(err, { data: data });
+ });
+ };
+ options.ratio = info[key].pixelRatio || 1;
+
+ var map = new mbgl.Map(options);
+ map.load(style);
+
+ map.render(info[key], function(err, data) {
+ if (err) {
+ t.error(err);
+ return t.end();
+ }
+
+ var expected = path.join(dir, 'expected.png');
+ var actual = path.join(dir, 'actual.png');
+ var diff = path.join(dir, 'diff.png');
+
+ var png = new PNG({
+ width: data.width,
+ height: data.height
+ });
+
+ png.data = data.pixels;
+
+ if (process.env.UPDATE) {
+ png.pack()
+ .pipe(fs.createWriteStream(expected))
+ .on('finish', t.end);
+ } else {
+ png.pack()
+ .pipe(fs.createWriteStream(actual))
+ .on('finish', function() {
+ compare(actual, expected, diff, t, function(err, diff) {
+ t.error(err);
+
+ var allowed = 0.001;
+
+ if ('diff' in info[key]) {
+ if (typeof info[key].diff === 'number') {
+ allowed = info[key].diff;
+ } else if ('native' in info[key].diff) {
+ allowed = info[key].diff.native;
+ }
+ }
+
+ results += format(resultTemplate, {
+ name: base,
+ key: key,
+ color: diff <= allowed ? 'green' : 'red',
+ error: err ? '<p>' + err + '</p>' : '',
+ difference: diff,
+ zoom: info.zoom || 0,
+ center: info.center || [0, 0],
+ bearing: info.bearing || 0,
+ width: info.width || 512,
+ height: info.height || 512
+ });
+
+ if (!info[key].ignored || !('native' in info[key].ignored)) {
+ t.ok(diff <= allowed, 'expected ' + diff + ' to be less than ' + allowed);
+ }
+
+ t.end();
+ });
+ });
+ }
+ });
+ };
+}
+
+function rewriteLocalSchema(url) {
+ var regex = /^local:\/\//;
+ if (url instanceof Array) {
+ return url.map(function(str) {
+ return str.replace(regex, '');
+ });
+ } else if (typeof url === "string") {
+ return url.replace(regex, '');
+ }
+}
+
+var tests;
+
+if (process.argv[1] === __filename) {
+ tests = process.argv.slice(2);
+}
+
+test('Render', function(t) {
+ fs.readdirSync(path.join(suitePath, 'tests')).forEach(function(dir) {
+ if (dir === 'index.html' || dir[0] === '.') return;
+ if (tests && tests.length && tests.indexOf(dir) < 0) return;
+
+ var style = require(path.join(suitePath, 'tests', dir, 'style.json')),
+ info = require(path.join(suitePath, 'tests', dir, 'info.json'));
+
+ for (var k in style.sources) {
+ var source = style.sources[k];
+
+ if (source.tiles) {
+ source.tiles = source.tiles.map(rewriteLocalSchema);
+ }
+
+ if (source.url) {
+ source.url = rewriteLocalSchema(source.url);
+ }
+ }
+
+ if (style.sprite) style.sprite = rewriteLocalSchema(style.sprite);
+ if (style.glyphs) style.glyphs = rewriteLocalSchema(style.glyphs);
+
+ style = JSON.stringify(style);
+
+ for (k in info) {
+ (info[k].native === false ? t.skip : t.test)(dir + ' ' + k, renderTest(style, info, dir, k));
+ }
+ });
+
+ t.test('results', function(t) {
+ var p = path.join(suitePath, 'tests', 'index.html');
+ fs.writeFileSync(p, format(template('results'), {results: results}));
+ console.warn('Results at: ' + p);
+ t.end();
+ });
+});