diff --git a/.gitignore b/.gitignore index cf1aea98a..2f53e5d17 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ -/build -/lib -/node_modules -/test/results -image.png +build +lib +node_modules +test/actual +test/diff diff --git a/README.md b/README.md index 4aa9d42b9..ede241bc3 100644 --- a/README.md +++ b/README.md @@ -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); +}); + +``` + diff --git a/package.json b/package.json index 0998cc15f..1e99cdc64 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/test/compare.js b/test/compare.js new file mode 100644 index 000000000..aaac14f36 --- /dev/null +++ b/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/test/expected/gzip/success.png b/test/expected/gzip/success.png new file mode 100644 index 000000000..b39617e1f Binary files /dev/null and b/test/expected/gzip/success.png differ diff --git a/test/expected/gzip/unhandled.png b/test/expected/gzip/unhandled.png new file mode 100644 index 000000000..ddb90d8b8 Binary files /dev/null and b/test/expected/gzip/unhandled.png differ diff --git a/test/expected/map/image.png b/test/expected/map/image.png new file mode 100644 index 000000000..b39617e1f Binary files /dev/null and b/test/expected/map/image.png differ diff --git a/test/js/gzip.test.js b/test/js/gzip.test.js new file mode 100644 index 000000000..6866db059 --- /dev/null +++ b/test/js/gzip.test.js @@ -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(); +}); diff --git a/test/js/map.test.js b/test/js/map.test.js index 50bab644d..5b9a751a5 100644 --- a/test/js/map.test.js +++ b/test/js/map.test.js @@ -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(); + }); + }); + } }); }); }); diff --git a/test/render.test.js b/test/render.test.js index b48f52908..0dea16a9d 100644 --- a/test/render.test.js +++ b/test/render.test.js @@ -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(); - } + }); }); } });