Unskip tests that should be passing now and adjust error messages to reflect recent changes.
428 lines
13 KiB
JavaScript
428 lines
13 KiB
JavaScript
'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('test', key, 'map');
|
|
mkdirp.sync(dir);
|
|
prev[key] = path.join(dir, name);
|
|
return prev;
|
|
}, {});
|
|
}
|
|
|
|
function setup(fileSource, callback) {
|
|
callback(new mbgl.Map(fileSource));
|
|
}
|
|
|
|
test('Map', function(t) {
|
|
t.test('constructor', function(t) {
|
|
t.test('must be called with new', function(t) {
|
|
t.throws(function() {
|
|
mbgl.Map();
|
|
}, /Use the new operator to create new Map objects/);
|
|
|
|
t.end();
|
|
});
|
|
|
|
t.test('should require a FileSource object as first parameter', function(t) {
|
|
t.throws(function() {
|
|
new mbgl.Map();
|
|
}, /Requires a FileSource as first argument/);
|
|
|
|
t.throws(function() {
|
|
new mbgl.Map('fileSource');
|
|
}, /Requires a FileSource as first argument/);
|
|
|
|
t.throws(function() {
|
|
new mbgl.Map({});
|
|
}, /Requires a FileSource as first argument/);
|
|
|
|
t.end();
|
|
});
|
|
|
|
t.test('should require the FileSource object to have request and cancel methods', function(t) {
|
|
var fileSource = new mbgl.FileSource();
|
|
|
|
t.throws(function() {
|
|
new mbgl.Map(fileSource);
|
|
}, /FileSource must have a request member function/);
|
|
|
|
fileSource.request = 'test';
|
|
t.throws(function() {
|
|
new mbgl.Map(fileSource);
|
|
}, /FileSource must have a request member function/);
|
|
|
|
fileSource.request = function() {};
|
|
t.throws(function() {
|
|
new mbgl.Map(fileSource);
|
|
}, /FileSource must have a cancel member function/);
|
|
|
|
fileSource.cancel = 'test';
|
|
t.throws(function() {
|
|
new mbgl.Map(fileSource);
|
|
}, /FileSource must have a cancel member function/);
|
|
|
|
fileSource.cancel = function() {};
|
|
t.doesNotThrow(function() {
|
|
new mbgl.Map(fileSource);
|
|
});
|
|
|
|
t.end();
|
|
});
|
|
|
|
t.end();
|
|
});
|
|
|
|
t.test('load styles', function(t) {
|
|
var fileSource = new mbgl.FileSource();
|
|
fileSource.request = function() {};
|
|
fileSource.cancel = function() {};
|
|
|
|
t.test('requires a string or object as the first parameter', function(t) {
|
|
t.test('requires a map style as first argument', function(t) {
|
|
setup(fileSource, function(map) {
|
|
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) {
|
|
setup(fileSource, function(map) {
|
|
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.end();
|
|
});
|
|
|
|
t.test('accepts an empty stylesheet string', function(t) {
|
|
setup(fileSource, function(map) {
|
|
t.doesNotThrow(function() {
|
|
map.load('{}');
|
|
});
|
|
|
|
map.release();
|
|
|
|
t.end();
|
|
});
|
|
});
|
|
|
|
t.test('accepts a JSON stylesheet', { timeout: 1000 }, function(t) {
|
|
setup(fileSource, function(map) {
|
|
t.doesNotThrow(function() {
|
|
map.load(style);
|
|
});
|
|
|
|
map.release();
|
|
|
|
t.end();
|
|
});
|
|
});
|
|
|
|
t.test('accepts a stringified stylesheet', { timeout: 1000 }, function(t) {
|
|
setup(fileSource, function(map) {
|
|
t.doesNotThrow(function() {
|
|
map.load(JSON.stringify(style));
|
|
});
|
|
|
|
map.release();
|
|
|
|
t.end();
|
|
});
|
|
});
|
|
|
|
t.end();
|
|
});
|
|
|
|
t.test('request coalesting', function(t) {
|
|
var requestList = [];
|
|
var fileSourceDoNotReply = new mbgl.FileSource();
|
|
|
|
// This file source will never reply to any
|
|
// request other than "./fixtures/tiles.tilejson" which
|
|
// will force the cancellation of the pending requests.
|
|
fileSourceDoNotReply.request = function(req) {
|
|
if (req.url != "./fixtures/tiles.tilejson") {
|
|
requestList.push(req);
|
|
return;
|
|
}
|
|
|
|
fs.readFile(path.join('test', req.url), function(err, data) {
|
|
req.respond(err, { data: data });
|
|
});
|
|
};
|
|
|
|
fileSourceDoNotReply.cancel = function() {
|
|
requestList.pop();
|
|
};
|
|
|
|
t.test('merge requests/cancels for the same resource', function(t) {
|
|
var map1 = new mbgl.Map(fileSourceDoNotReply);
|
|
var map2 = new mbgl.Map(fileSourceDoNotReply);
|
|
var map3 = new mbgl.Map(fileSourceDoNotReply);
|
|
var map4 = new mbgl.Map(fileSourceDoNotReply);
|
|
var map5 = new mbgl.Map(fileSourceDoNotReply);
|
|
|
|
map1.load(style);
|
|
map2.load(style);
|
|
map3.load(style);
|
|
map4.load(style);
|
|
map5.load(style);
|
|
|
|
var renderCallback = function(err, data) {}
|
|
|
|
map1.render({}, renderCallback);
|
|
map2.render({}, renderCallback);
|
|
map3.render({}, renderCallback);
|
|
map4.render({}, renderCallback);
|
|
map5.render({}, renderCallback);
|
|
|
|
var checkCancel = function() {
|
|
t.equal(requestList.length, 0);
|
|
t.end();
|
|
};
|
|
|
|
var checkCoalescing = function() {
|
|
// Check if all the 5 vector tile
|
|
// requests are going to merge in
|
|
// a single request.
|
|
t.equal(requestList.length, 1);
|
|
|
|
// This should cause the pending
|
|
// resources to get canceled, but
|
|
// only one cancel is emitted, because
|
|
// all map objects requested the same
|
|
// resource.
|
|
map1.release();
|
|
map2.release();
|
|
map3.release();
|
|
map4.release();
|
|
map5.release();
|
|
|
|
setTimeout(checkCancel, 500);
|
|
};
|
|
|
|
setTimeout(checkCoalescing, 500);
|
|
});
|
|
|
|
var vectorTileRequestCount = 0;
|
|
var fileSourceDelay = new mbgl.FileSource();
|
|
|
|
// This file source will add a little delay before
|
|
// replying so we make sure that all the map objects
|
|
// request the same resource will get a reply from the
|
|
// same request.
|
|
fileSourceDelay.request = function(req) {
|
|
var timeout = 0;
|
|
|
|
if (req.url != "./fixtures/tiles.tilejson") {
|
|
timeout = 500;
|
|
t.ok(++vectorTileRequestCount == 1, "Should make only one request for tiles");
|
|
}
|
|
|
|
var readFile = function() {
|
|
fs.readFile(path.join('test', req.url), function(err, data) {
|
|
req.respond(err, { data: data });
|
|
});
|
|
};
|
|
|
|
setTimeout(readFile, timeout);
|
|
};
|
|
|
|
fileSourceDelay.cancel = function() {
|
|
t.fail("Should never cancel");
|
|
};
|
|
|
|
t.test('one request for the same resource notifies multiple map objects', function(t) {
|
|
var map1 = new mbgl.Map(fileSourceDelay);
|
|
var map2 = new mbgl.Map(fileSourceDelay);
|
|
var map3 = new mbgl.Map(fileSourceDelay);
|
|
var map4 = new mbgl.Map(fileSourceDelay);
|
|
var map5 = new mbgl.Map(fileSourceDelay);
|
|
|
|
map1.load(style);
|
|
map2.load(style);
|
|
map3.load(style);
|
|
map4.load(style);
|
|
map5.load(style);
|
|
|
|
var renderFinishedCount = 0;
|
|
var renderCallback = function(err, data) {
|
|
// We make only one request for tiles, but that
|
|
// should be enough for all these map objects because
|
|
// they all want the same resource.
|
|
if (++renderFinishedCount == 5) {
|
|
map1.release();
|
|
map2.release();
|
|
map3.release();
|
|
map4.release();
|
|
map5.release();
|
|
|
|
t.end();
|
|
}
|
|
}
|
|
|
|
map1.render({}, renderCallback);
|
|
map2.render({}, renderCallback);
|
|
map3.render({}, renderCallback);
|
|
map4.render({}, renderCallback);
|
|
map5.render({}, renderCallback);
|
|
});
|
|
|
|
t.end();
|
|
});
|
|
|
|
t.test('render argument requirements', function(t) {
|
|
var fileSource = new mbgl.FileSource();
|
|
fileSource.request = function(req) {
|
|
fs.readFile(path.join('test', req.url), function(err, data) {
|
|
req.respond(err, { data: data });
|
|
});
|
|
};
|
|
fileSource.cancel = function() {};
|
|
|
|
t.test('requires an object as the first parameter', function(t) {
|
|
setup(fileSource, function(map) {
|
|
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) {
|
|
setup(fileSource, function(map) {
|
|
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) {
|
|
setup(fileSource, function(map) {
|
|
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');
|
|
});
|
|
|
|
setup(fileSource, function(map) {
|
|
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) {
|
|
setup(fileSource, function(map) {
|
|
map.release();
|
|
|
|
t.throws(function() {
|
|
map.release();
|
|
}, /Map resources have already been released/);
|
|
|
|
t.end();
|
|
});
|
|
});
|
|
|
|
t.test('returns an image', function(t) {
|
|
setup(fileSource, function(map) {
|
|
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();
|
|
});
|
|
});
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
t.end();
|
|
});
|
|
|
|
t.end();
|
|
});
|