Merge branch 'master' of github.com:CartoDB/Windshaft-cartodb into analysis-layers

This commit is contained in:
Raul Ochoa 2016-02-24 10:35:28 +01:00
commit b9d2e297b6
25 changed files with 1496 additions and 698 deletions

View File

@ -16,10 +16,6 @@ Make sure that you have the requirements needed. These are
- CartoDB 0.9.5+ (for `CDB_QueryTables`)
- Varnish (http://www.varnish-cache.org)
- For running the testsuite
- ImageMagick (http://www.imagemagick.org)
On Ubuntu 14.04 the dependencies can be installed with
```shell

21
NEWS.md
View File

@ -1,8 +1,27 @@
# Changelog
## 2.26.0
Released 2016-mm-dd
Announcements:
- Upgrades windshaft to [1.13.0](https://github.com/CartoDB/Windshaft/releases/tag/1.13.0)
## 2.25.2
Released 2016-02-22
Bug fixes:
- Correct URLs for widgets in named maps #381
## 2.25.1
Released 2016-mm-dd
Released 2016-02-22
Announcements:
- Upgrades windshaft to [1.11.1](https://github.com/CartoDB/Windshaft/releases/tag/1.11.1)
## 2.25.0

View File

@ -207,6 +207,8 @@ MapController.prototype.create = function(req, res, prepareConfigFn) {
if (err) {
self.sendError(req, res, err, 'ANONYMOUS LAYERGROUP');
} else {
addWidgetsUrl(req.context.user, layergroup);
res.set('X-Layergroup-Id', layergroup.layergroupid);
self.send(req, res, layergroup, 200);
}
@ -281,6 +283,8 @@ MapController.prototype.instantiateTemplate = function(req, res, prepareParamsFn
var templateHash = self.templateMaps.fingerPrint(mapConfigProvider.template).substring(0, 8);
layergroup.layergroupid = cdbuser + '@' + templateHash + '@' + layergroup.layergroupid;
addWidgetsUrl(req.context.user, layergroup);
res.set('X-Layergroup-Id', layergroup.layergroupid);
self.surrogateKeysCache.tag(res, new NamedMapsCacheEntry(cdbuser, mapConfigProvider.getTemplateName()));
@ -364,9 +368,6 @@ MapController.prototype.afterLayergroupCreate = function(req, res, mapconfig, la
layergroup.layergroupid = layergroup.layergroupid + ':' + result.lastUpdatedTime;
layergroup.last_updated = new Date(result.lastUpdatedTime).toISOString();
// TODO this should take into account several URL patterns
addWidgetsUrl(username, layergroup);
if (req.method === 'GET') {
var tableCacheEntry = new TablesCacheEntry(result.affectedTables);
var ttl = global.environment.varnish.layergroupTtl || 86400;

1457
npm-shrinkwrap.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
{
"private": true,
"name": "windshaft-cartodb",
"version": "2.25.1",
"version": "2.26.0",
"description": "A map tile server for CartoDB",
"keywords": [
"cartodb"
@ -26,7 +26,7 @@
"node-statsd": "~0.0.7",
"underscore" : "~1.6.0",
"dot": "~1.0.2",
"windshaft": "1.11.0",
"windshaft": "1.13.0",
"step": "~0.0.6",
"queue-async": "~1.0.7",
"request": "~2.62.0",

View File

@ -292,7 +292,8 @@ describe('render limits', function() {
if (err) {
done(err);
}
assert.imageEqualsFile(res.body, './test/fixtures/render-timeout-fallback.png', 25,
var referenceImagePath = './test/fixtures/render-timeout-fallback.png';
assert.imageBufferIsSimilarToFile(res.body, referenceImagePath, 25,
function(imgErr/*, similarity*/) {
done(imgErr);
}

View File

@ -122,7 +122,7 @@ describe(suiteName, function() {
' WHERE m.tabname = any ((SELECT tablenames from querytables)::regclass[])');
}
assert.imageEqualsFile(res.body, 'test/fixtures/test_table_0_0_0_multilayer1.png',
assert.imageBufferIsSimilarToFile(res.body, 'test/fixtures/test_table_0_0_0_multilayer1.png',
IMAGE_EQUALS_HIGHER_TOLERANCE_PER_MIL, function(err/*, similarity*/) {
next(err);
}
@ -190,8 +190,18 @@ describe(suiteName, function() {
});
it("should include serverMedata in the response", function(done) {
describe('server-metadata', function() {
var serverMetadata;
beforeEach(function() {
serverMetadata = global.environment.serverMetadata;
global.environment.serverMetadata = { cdn_url : { http:'test', https: 'tests' } };
});
afterEach(function() {
global.environment.serverMetadata = serverMetadata;
});
it("should include serverMedata in the response", function(done) {
var layergroup = {
version: '1.0.0',
layers: [
@ -224,6 +234,8 @@ describe(suiteName, function() {
);
});
});
it("get creation requests has cache", function(done) {
@ -392,7 +404,8 @@ describe(suiteName, function() {
' WHERE m.tabname = any ((SELECT tablenames from querytables)::regclass[])');
}
assert.imageEqualsFile(res.body, 'test/fixtures/test_multilayer_bbox.png', IMAGE_EQUALS_TOLERANCE_PER_MIL,
var referenceImagePath = 'test/fixtures/test_multilayer_bbox.png';
assert.imageBufferIsSimilarToFile(res.body, referenceImagePath, IMAGE_EQUALS_TOLERANCE_PER_MIL,
function(err/*, similarity*/) {
next(err);
});
@ -431,7 +444,8 @@ describe(suiteName, function() {
' WHERE m.tabname = any ((SELECT tablenames from querytables)::regclass[])');
}
assert.imageEqualsFile(res.body, 'test/fixtures/test_multilayer_bbox.png', IMAGE_EQUALS_TOLERANCE_PER_MIL,
var referenceImagePath = 'test/fixtures/test_multilayer_bbox.png';
assert.imageBufferIsSimilarToFile(res.body, referenceImagePath, IMAGE_EQUALS_TOLERANCE_PER_MIL,
function(err/*, similarity*/) {
next(err);
});
@ -1007,7 +1021,7 @@ describe(suiteName, function() {
}, {}, function(res) {
assert.equal(res.statusCode, 200, res.body);
assert.equal(res.headers['content-type'], "image/png");
assert.imageEqualsFile(res.body, windshaft_fixtures + '/test_default_mapnik_point.png',
assert.imageBufferIsSimilarToFile(res.body, windshaft_fixtures + '/test_default_mapnik_point.png',
IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err/*, similarity*/) {
next(err);
}

View File

@ -70,8 +70,7 @@ describe('tests from old api translated to multilayer', function() {
},
function(res) {
var parsed = JSON.parse(res.body);
assert.ok(parsed.errors[0].match(/^style0/));
assert.ok(parsed.errors[0].match(/missing closing/));
assert.ok(parsed.errors[0].match(/<css input>:1:1: Unclosed block/));
done();
}
);

View File

@ -30,7 +30,8 @@ describe('overviews_queries', function() {
if (err) {
return done(err);
}
assert.imageEqualsFile(tile.body, './test/fixtures/' + fixture, IMAGE_EQUALS_TOLERANCE_PER_MIL, done);
var referenceImagePath = './test/fixtures/' + fixture;
assert.imageBufferIsSimilarToFile(tile.body, referenceImagePath, IMAGE_EQUALS_TOLERANCE_PER_MIL, done);
};
}

View File

@ -95,10 +95,12 @@ describe('blend png renderer', function() {
var zxy = [tileRequest.z, tileRequest.x, tileRequest.y];
it('tile all/' + zxy.join('/') + '.png', function (done) {
testClient.getTileLayer(plainTorqueMapConfig(testScenario.plainColor), tileRequest, function(err, res) {
assert.imageEqualsFile(res.body, blendPngFixture(zxy), IMAGE_TOLERANCE_PER_MIL, function(err) {
assert.imageBufferIsSimilarToFile(res.body, blendPngFixture(zxy), IMAGE_TOLERANCE_PER_MIL,
function(err) {
assert.ok(!err);
done();
});
}
);
});
});
});

View File

@ -156,10 +156,12 @@ describe('blend layer filtering', function() {
it('should filter on ' + layerFilter + '/1/0/0.png', function (done) {
testClient.getTileLayer(mapConfig, tileRequest, function(err, res) {
assert.imageEqualsFile(res.body, blendPngFixture(filteredLayers), IMG_TOLERANCE_PER_MIL, function(err) {
assert.imageBufferIsSimilarToFile(res.body, blendPngFixture(filteredLayers), IMG_TOLERANCE_PER_MIL,
function(err) {
assert.ok(!err);
done();
});
}
);
});
});
});

View File

@ -111,10 +111,12 @@ describe('blend http fallback', function() {
it('should fallback on http error while blending layers ' + layerFilter + '/1/0/0.png', function (done) {
testClient.getTileLayer(mapConfig, tileRequest, function(err, res) {
assert.imageEqualsFile(res.body, blendPngFixture(filteredLayers), IMG_TOLERANCE_PER_MIL, function(err) {
assert.imageBufferIsSimilarToFile(res.body, blendPngFixture(filteredLayers), IMG_TOLERANCE_PER_MIL,
function(err) {
assert.ok(!err, err);
done();
});
}
);
});
});
});

View File

@ -57,7 +57,8 @@ describe('external resources', function() {
if (err) {
return done(err);
}
assert.imageEqualsFile(res.body, './test/fixtures/' + fixture, IMAGE_EQUALS_TOLERANCE_PER_MIL, done);
var referenceImagePath = './test/fixtures/' + fixture;
assert.imageBufferIsSimilarToFile(res.body, referenceImagePath, IMAGE_EQUALS_TOLERANCE_PER_MIL, done);
};
}

View File

@ -84,11 +84,13 @@ describe.skip('render limits', function() {
testClient.withLayergroup(slowQueryMapConfig, options, function(err, requestTile, finish) {
var tileUrl = '/0/0/0.png';
requestTile(tileUrl, options, function(err, res) {
assert.imageEqualsFile(res.body, fixtureImage, IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err) {
assert.imageBufferIsSimilarToFile(res.body, fixtureImage, IMAGE_EQUALS_TOLERANCE_PER_MIL,
function(err) {
finish(function(finishErr) {
done(err || finishErr);
});
});
}
);
});
});
});

View File

@ -117,7 +117,8 @@ describe('multilayer', function() {
assert.equal(res.statusCode, 200, res.body);
assert.equal(res.headers['content-type'], "image/png");
checkCORSHeaders(res);
assert.imageEqualsFile(res.body, './test/fixtures/test_bigpoint_red.png', IMAGE_EQUALS_TOLERANCE_PER_MIL,
var referenceImagePath = './test/fixtures/test_bigpoint_red.png';
assert.imageBufferIsSimilarToFile(res.body, referenceImagePath, IMAGE_EQUALS_TOLERANCE_PER_MIL,
function(err) {
next(err);
});
@ -191,7 +192,8 @@ describe('multilayer', function() {
}, {}, function(res) {
assert.equal(res.statusCode, 200, res.body);
assert.equal(res.headers['content-type'], "image/png");
assert.imageEqualsFile(res.body, './test/acceptance/ported/fixtures/test_table_0_0_0_multilayer1.png',
var referenceImagePath = './test/acceptance/ported/fixtures/test_table_0_0_0_multilayer1.png';
assert.imageBufferIsSimilarToFile(res.body, referenceImagePath,
IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err) {
next(err);
});
@ -302,7 +304,8 @@ describe('multilayer', function() {
}, {}, function(res) {
assert.equal(res.statusCode, 200, res.body);
assert.equal(res.headers['content-type'], "image/png");
assert.imageEqualsFile(res.body, './test/acceptance/ported/fixtures/test_table_0_0_0_multilayer1.png',
var referenceImagePath = './test/acceptance/ported/fixtures/test_table_0_0_0_multilayer1.png';
assert.imageBufferIsSimilarToFile(res.body, referenceImagePath,
IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err) {
next(err);
});
@ -425,7 +428,8 @@ describe('multilayer', function() {
}, {}, function(res) {
assert.equal(res.statusCode, 200, res.body);
assert.equal(res.headers['content-type'], "image/png");
assert.imageEqualsFile(res.body, './test/acceptance/ported/fixtures/test_table_0_0_0_multilayer1.png',
var referenceImagePath = './test/acceptance/ported/fixtures/test_table_0_0_0_multilayer1.png';
assert.imageBufferIsSimilarToFile(res.body, referenceImagePath,
IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err) {
next(err);
});
@ -541,7 +545,8 @@ describe('multilayer', function() {
}, {}, function(res) {
assert.equal(res.statusCode, 200, res.body);
assert.equal(res.headers['content-type'], "image/png");
assert.imageEqualsFile(res.body, './test/acceptance/ported/fixtures/test_table_0_0_0_multilayer1.png',
var referenceImagePath = './test/acceptance/ported/fixtures/test_table_0_0_0_multilayer1.png';
assert.imageBufferIsSimilarToFile(res.body, referenceImagePath,
IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err) {
next(err);
});
@ -727,7 +732,8 @@ describe('multilayer', function() {
}, {}, function(res) {
assert.equal(res.statusCode, 200, res.body);
assert.equal(res.headers['content-type'], "image/png");
assert.imageEqualsFile(res.body, './test/acceptance/ported/fixtures/test_table_0_0_0_multilayer2.png',
var referenceImagePath = './test/acceptance/ported/fixtures/test_table_0_0_0_multilayer2.png';
assert.imageBufferIsSimilarToFile(res.body, referenceImagePath,
IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err) {
next(err);
});
@ -761,7 +767,8 @@ describe('multilayer', function() {
}, {}, function(res) {
assert.equal(res.statusCode, 200, res.body);
assert.equal(res.headers['content-type'], "image/png");
assert.imageEqualsFile(res.body, './test/acceptance/ported/fixtures/test_table_0_0_0_multilayer3.png',
var referenceImagePath = './test/acceptance/ported/fixtures/test_table_0_0_0_multilayer3.png';
assert.imageBufferIsSimilarToFile(res.body, referenceImagePath,
IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err) {
next(err);
});
@ -856,7 +863,8 @@ describe('multilayer', function() {
}, {}, function(res) {
assert.equal(res.statusCode, 200, res.body);
assert.equal(res.headers['content-type'], "image/png");
assert.imageEqualsFile(res.body, './test/acceptance/ported/fixtures/test_table_0_0_0_multilayer4.png',
var referenceImagePath = './test/acceptance/ported/fixtures/test_table_0_0_0_multilayer4.png';
assert.imageBufferIsSimilarToFile(res.body, referenceImagePath,
IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err) {
next(err);
});
@ -1259,7 +1267,7 @@ describe('multilayer', function() {
assert.equal(res.statusCode, 200, res.body);
assert.equal(res.headers['content-type'], "image/png");
checkCORSHeaders(res);
assert.imageEqualsFile(res.body, './test/fixtures/test_bigpoint_red.png',
assert.imageBufferIsSimilarToFile(res.body, './test/fixtures/test_bigpoint_red.png',
IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err) {
next(err);
});

View File

@ -87,7 +87,7 @@ describe('raster', function() {
assert.equal(res.statusCode, 200, res.body);
assert.deepEqual(res.headers['content-type'], "image/png");
var next = this;
assert.imageEqualsFile(res.body,
assert.imageBufferIsSimilarToFile(res.body,
'./test/fixtures/raster_gray_rect.png',
IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err) {
try {

View File

@ -34,7 +34,9 @@ describe('server_gettile', function() {
if (err) {
return done(err);
}
assert.imageEqualsFile(res.body, './test/fixtures/' + fixture, IMAGE_EQUALS_TOLERANCE_PER_MIL, done);
assert.imageBufferIsSimilarToFile(
res.body, './test/fixtures/' + fixture, IMAGE_EQUALS_TOLERANCE_PER_MIL, done
);
};
}
@ -113,12 +115,13 @@ describe('server_gettile', function() {
assert.ok(res.headers.hasOwnProperty('x-windshaft-cache'), "Did not hit renderer cache on second time");
assert.ok(res.headers['x-windshaft-cache'] >= 0);
assert.imageEqualsFile(res.body, imageFixture, IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err) {
assert.imageBufferIsSimilarToFile(res.body, imageFixture, IMAGE_EQUALS_TOLERANCE_PER_MIL,
function(err) {
finish(function(finishErr) {
done(err || finishErr);
});
});
}
);
});
});
});

View File

@ -108,7 +108,7 @@ describe('server_png8_format', function() {
assert.equal(responsePng8.headers['content-type'], "image/png");
bufferPng8 = responsePng8.body;
assert.ok(bufferPng8.length < bufferPng32.length);
assert.imageBuffersAreEqual(bufferPng32, bufferPng8, IMAGE_EQUALS_TOLERANCE_PER_MIL,
assert.imageBuffersAreSimilar(bufferPng32, bufferPng8, IMAGE_EQUALS_TOLERANCE_PER_MIL,
function(err, imagePaths, similarity) {
keysToDelete['map_cfg|' + LayergroupToken.parse(layergroupId).token] = 0;

View File

@ -83,10 +83,12 @@ describe('torque png renderer', function() {
var zxy = [z, x, y];
it('tile ' + zxy.join('/') + '.torque.png', function (done) {
testClient.getTileLayer(torquePngPointsMapConfig, tileRequest, function(err, res) {
assert.imageEqualsFile(res.body, torquePngFixture(zxy), IMAGE_TOLERANCE_PER_MIL, function(err) {
assert.imageBufferIsSimilarToFile(res.body, torquePngFixture(zxy), IMAGE_TOLERANCE_PER_MIL,
function(err) {
assert.ok(!err);
done();
});
}
);
});
});
});

View File

@ -107,10 +107,12 @@ describe('wrap x coordinate', function() {
var fixtureZxy = [testScenario.fixture.z, testScenario.fixture.x, testScenario.fixture.y];
it('tile all/' + zxy.join('/') + '.png', function (done) {
testClient.getTileLayer(plainTorqueMapConfig(testScenario.plainColor), tileRequest, function(err, res) {
assert.imageEqualsFile(res.body, blendPngFixture(fixtureZxy), IMG_TOLERANCE_PER_MIL, function(err) {
assert.imageBufferIsSimilarToFile(res.body, blendPngFixture(fixtureZxy), IMG_TOLERANCE_PER_MIL,
function(err) {
assert.ok(!err);
done();
});
}
);
});
});
});

View File

@ -313,8 +313,19 @@ describe('template_api', function() {
});
});
it("instance endpoint should return server metadata", function(done){
describe('server-metadata', function() {
var serverMetadata;
beforeEach(function() {
serverMetadata = global.environment.serverMetadata;
global.environment.serverMetadata = { cdn_url : { http:'test', https: 'tests' } };
});
afterEach(function() {
global.environment.serverMetadata = serverMetadata;
});
it("instance endpoint should return server metadata", function(done){
var tmpl = _.clone(template_acceptance1);
tmpl.name = "rambotemplate2";
@ -359,6 +370,7 @@ describe('template_api', function() {
}
);
});
});

View File

@ -0,0 +1,250 @@
var assert = require('../../support/assert');
var step = require('step');
var url = require('url');
var queue = require('queue-async');
var helper = require('../../support/test_helper');
var CartodbWindshaft = require('../../../lib/cartodb/server');
var serverOptions = require('../../../lib/cartodb/server_options');
var server = new CartodbWindshaft(serverOptions);
var LayergroupToken = require('../../../lib/cartodb/models/layergroup_token');
describe('named-maps widgets', function() {
var username = 'localhost';
var widgetsTemplateName = 'widgets-template';
var layergroupid;
var layergroup;
var keysToDelete;
beforeEach(function(done) {
keysToDelete = {};
var widgetsTemplate = {
version: '0.0.1',
name: widgetsTemplateName,
layergroup: {
version: '1.5.0',
layers: [
{
type: 'cartodb',
options: {
sql: "select * from populated_places_simple_reduced_private",
cartocss: '#layer { marker-fill: blue; }',
cartocss_version: '2.3.0',
widgets: {
pop_max_formula_sum: {
type: 'formula',
options: {
column: 'pop_max',
operation: 'sum'
}
},
country_places_count: {
type: 'aggregation',
options: {
column: 'adm0_a3',
aggregation: 'count'
}
},
pop_max: {
type: 'histogram',
options: {
column: 'pop_max'
}
}
}
}
}
]
}
};
var template_params = {};
step(
function createTemplate()
{
var next = this;
assert.response(
server,
{
url: '/api/v1/map/named?api_key=1234',
method: 'POST',
headers: {
host: username,
'Content-Type': 'application/json'
},
data: JSON.stringify(widgetsTemplate)
},
{
status: 200
},
function(res, err) {
next(err, res);
}
);
},
function instantiateTemplate(err, res) {
assert.ifError(err);
assert.deepEqual(JSON.parse(res.body), { template_id: widgetsTemplateName });
var next = this;
assert.response(
server,
{
url: '/api/v1/map/named/' + widgetsTemplateName,
method: 'POST',
headers: {
host: username,
'Content-Type': 'application/json'
},
data: JSON.stringify(template_params)
},
{
status: 200
},
function(res) {
next(null, res);
}
);
},
function fetchTile(err, res) {
assert.ifError(err);
layergroup = JSON.parse(res.body);
assert.ok(layergroup.hasOwnProperty('layergroupid'), "Missing 'layergroupid' from: " + res.body);
layergroupid = layergroup.layergroupid;
keysToDelete['map_cfg|' + LayergroupToken.parse(layergroup.layergroupid).token] = 0;
keysToDelete['user:localhost:mapviews:global'] = 5;
return done();
}
);
});
afterEach(function(done) {
step(
function deleteTemplate(err) {
assert.ifError(err);
var next = this;
assert.response(
server,
{
url: '/api/v1/map/named/' + widgetsTemplateName + '?api_key=1234',
method: 'DELETE',
headers: {
host: username
}
},
{
status: 204
},
function(res, err) {
next(err, res);
}
);
},
function deleteRedisKeys(err) {
assert.ifError(err);
helper.deleteRedisKeys(keysToDelete, done);
}
);
});
function getWidget(widgetName, callback) {
assert.response(
server,
{
url: '/api/v1/map/' + layergroupid + '/0/widget/' + widgetName,
method: 'GET',
headers: {
host: username
}
},
{
status: 200
},
function(res, err) {
if (err) {
return callback(err);
}
var parsedBody = JSON.parse(res.body);
return callback(err, res, parsedBody);
}
);
}
it('should be able to retrieve widgets from all URLs', function(done) {
var widgetsPaths = layergroup.metadata.layers.reduce(function(paths, layer) {
var widgets = layer.widgets || {};
Object.keys(widgets).forEach(function(widget) {
paths.push(url.parse(widgets[widget].url.http).path);
});
return paths;
}, []);
var widgetsQueue = queue(widgetsPaths.length);
widgetsPaths.forEach(function(path) {
widgetsQueue.defer(function(path, done) {
assert.response(
server,
{
url: path,
method: 'GET',
headers: {
host: username
}
},
{
status: 200
},
function(res, err) {
if (err) {
return done(err);
}
var parsedBody = JSON.parse(res.body);
return done(null, parsedBody);
}
);
}, path);
});
widgetsQueue.awaitAll(function(err, results) {
assert.equal(results.length, 3);
done(err);
});
});
it("should retrieve aggregation", function(done) {
getWidget('country_places_count', function(err, response, aggregation) {
assert.ok(!err, err);
assert.equal(aggregation.type, 'aggregation');
assert.equal(aggregation.max, 769);
return done();
});
});
it("should retrieve histogram", function(done) {
getWidget('pop_max', function(err, response, histogram) {
assert.ok(!err, err);
assert.equal(histogram.type, 'histogram');
assert.equal(histogram.bin_width, 743250);
return done();
});
});
});

View File

@ -1,7 +1,6 @@
// Cribbed from the ever prolific Konstantin Kaefer
// https://github.com/mapbox/tilelive-mapnik/blob/master/test/support/assert.js
var exec = require('child_process').exec;
var fs = require('fs');
var path = require('path');
var util = require('util');
@ -13,7 +12,7 @@ var request = require('request');
var assert = module.exports = exports = require('assert');
/**
* Takes an image data as an input and an image path and compare them using ImageMagick fuzz algorithm, if case the
* Takes an image data as an input and an image path and compare them using mapnik.Image.compare mechanism, in case the
* similarity is not within the tolerance limit it will callback with an error.
*
* @param buffer The image data to compare from
@ -22,70 +21,39 @@ var assert = module.exports = exports = require('assert');
* @param {function} callback Will call to home with null in case there is no error, otherwise with the error itself
* @see FUZZY in http://www.imagemagick.org/script/command-line-options.php#metric
*/
assert.imageEqualsFile = function(buffer, referenceImageRelativeFilePath, tolerance, callback) {
assert.imageBufferIsSimilarToFile = function(buffer, referenceImageRelativeFilePath, tolerance, callback) {
callback = callback || function(err) { assert.ifError(err); };
var referenceImageFilePath = path.resolve(referenceImageRelativeFilePath),
testImageFilePath = createImageFromBuffer(buffer, 'test');
imageFilesAreEqual(testImageFilePath, referenceImageFilePath, tolerance, function(err) {
fs.unlinkSync(testImageFilePath);
callback(err);
});
var referenceImageFilePath = path.resolve(referenceImageRelativeFilePath);
var referenceImageBuffer = fs.readFileSync(referenceImageFilePath, { encoding: null });
assert.imageBuffersAreSimilar(buffer, referenceImageBuffer, tolerance, callback);
};
assert.imageBuffersAreEqual = function(bufferA, bufferB, tolerance, callback) {
var randStr = (Math.random() * 1e16).toString().substring(0, 8);
var imageFilePathA = createImageFromBuffer(bufferA, randStr + '-a'),
imageFilePathB = createImageFromBuffer(bufferB, randStr + '-b');
assert.imageBuffersAreSimilar = function(bufferA, bufferB, tolerance, callback) {
var testImage = mapnik.Image.fromBytes(Buffer.isBuffer(bufferA) ? bufferA : new Buffer(bufferA, 'binary'));
var referenceImage = mapnik.Image.fromBytes(Buffer.isBuffer(bufferB) ? bufferB : new Buffer(bufferB, 'binary'));
imageFilesAreEqual(imageFilePathA, imageFilePathB, tolerance, function(err, similarity) {
callback(err, [imageFilePathA, imageFilePathB], similarity);
});
imagesAreSimilar(testImage, referenceImage, tolerance, callback);
};
function createImageFromBuffer(buffer, nameHint) {
var imageFilePath = path.resolve('test/results/png/image-' + nameHint + '-' + Date.now() + '.png');
var err = fs.writeFileSync(imageFilePath, buffer, 'binary');
assert.ifError(err);
return imageFilePath;
}
assert.imageIsSimilarToFile = function(testImage, referenceImageRelativeFilePath, tolerance, callback) {
callback = callback || function(err) { assert.ifError(err); };
function imageFilesAreEqual(testImageFilePath, referenceImageFilePath, tolerance, callback) {
var resultFilePath = path.resolve(util.format('/tmp/windshaft-result-%s-diff.png', Date.now()));
var imageMagickCmd = util.format(
'compare -metric fuzz "%s" "%s" "%s"',
testImageFilePath, referenceImageFilePath, resultFilePath
);
var referenceImageFilePath = path.resolve(referenceImageRelativeFilePath);
exec(imageMagickCmd, function(err, stdout, stderr) {
var referenceImage = mapnik.Image.fromBytes(fs.readFileSync(referenceImageFilePath, { encoding: null }));
imagesAreSimilar(testImage, referenceImage, tolerance, function(err) {
if (err) {
fs.unlinkSync(testImageFilePath);
var testImageFilePath = randomImagePath();
testImage.save(testImageFilePath);
}
callback(err);
} else {
stderr = stderr.trim();
var metrics = stderr.match(/([0-9]*) \((.*)\)/);
if ( ! metrics ) {
callback(new Error("No match for " + stderr));
return;
}
var similarity = parseFloat(metrics[2]),
tolerancePerMil = (tolerance / 1000);
if (similarity > tolerancePerMil) {
err = new Error(util.format(
'Images %s and %s are not equal (got %d similarity, expected %d). Result %s',
testImageFilePath, referenceImageFilePath, similarity, tolerancePerMil, resultFilePath)
);
err.similarity = similarity;
callback(err, similarity);
} else {
fs.unlinkSync(resultFilePath);
callback(null, similarity);
}
}
});
}
};
assert.imagesAreSimilar = function(testImage, referenceImage, tolerance, callback) {
function imagesAreSimilar(testImage, referenceImage, tolerance, callback) {
if (testImage.width() !== referenceImage.width() || testImage.height() !== referenceImage.height()) {
return callback(new Error('Images are not the same size'));
}
@ -103,27 +71,10 @@ assert.imagesAreSimilar = function(testImage, referenceImage, tolerance, callbac
} else {
callback(null, similarity);
}
};
assert.imageIsSimilarToFile = function(testImage, referenceImageRelativeFilePath, tolerance, callback) {
callback = callback || function(err) { assert.ifError(err); };
var referenceImageFilePath = path.resolve(referenceImageRelativeFilePath);
var referenceImage = mapnik.Image.fromBytes(fs.readFileSync(referenceImageFilePath, { encoding: null }));
assert.imagesAreSimilar(testImage, referenceImage, tolerance, function(err) {
if (err) {
var testImageFilePath = randomImagePath();
testImage.save(testImageFilePath);
}
callback(err);
});
};
function randomImagePath(nameHint) {
nameHint = nameHint || 'test';
return path.resolve('test/results/png/image-' + nameHint + '-' + Date.now() + '.png');
function randomImagePath() {
return path.resolve('test/results/png/image-test-' + Date.now() + '.png');
}
// jshint maxcomplexity:9

View File

@ -7385,3 +7385,8 @@ GRANT ALL ON TABLE populated_places_simple_reduced TO :TESTUSER;
GRANT SELECT ON TABLE populated_places_simple_reduced TO :PUBLICUSER;
VACUUM ANALYZE populated_places_simple_reduced;
create table populated_places_simple_reduced_private AS
select * from populated_places_simple_reduced;
GRANT ALL ON TABLE populated_places_simple_reduced_private TO :TESTUSER;

View File

@ -74,6 +74,14 @@ function checkSurrogateKey(res, expectedKey) {
assert.deepEqual(keys, expectedKeys);
}
var redisClient;
beforeEach(function() {
if (!redisClient) {
redisClient = redis.createClient(global.environment.redis.port);
}
});
//global afterEach to capture test suites that leave keys in redis
afterEach(function(done) {
@ -110,7 +118,6 @@ afterEach(function(done) {
}
Object.keys(databasesTasks).forEach(function(db) {
var redisClient = redis.createClient(global.environment.redis.port);
redisClient.select(db, function() {
// Check that we start with an empty redis db
redisClient.keys("*", function(err, keys) {
@ -137,6 +144,7 @@ function deleteRedisKeys(keysToDelete, callback) {
var redisClient = redis.createClient(global.environment.redis.port);
redisClient.select(keysToDelete[k], function() {
redisClient.del(k, function(err, deletedKeysCount) {
redisClient.quit();
assert.notStrictEqual(deletedKeysCount, 0, 'No KEYS deleted for: [db=' + keysToDelete[k] + ']' + k);
taskDone(k);
});