diff --git a/README.md b/README.md index a01c4242a..82e6f7c89 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,7 @@ var config = { grainstore: { datasource: { user:'postgres', host: '127.0.0.1', - port: 5432 + port: 5432 } }, //see grainstore npm for other options renderCache: { @@ -162,6 +162,12 @@ try `redis-cli flushall`. Windshaft caches MML files in Redis than can contain S and may not expire them when you change your Windshaft config, but manually flushing redis should do the trick. +### Uncaught Error: Command failed running tests +You need [ImageMagick](http://www.imagemagick.org/) to run some tests. In Mac OS X +there are some issues running with latest versions of ImageMagick. If you use +[Homebrew](http://brew.sh/) you can try with a +[modified Formula to install ImageMagick version 6.7.7](https://gist.github.com/rochoa/10017167). + -- Thanks to the Mapnik and Mapbox team for making such flexible tools diff --git a/lib/windshaft/server.js b/lib/windshaft/server.js index 8969c9422..a41383b8a 100644 --- a/lib/windshaft/server.js +++ b/lib/windshaft/server.js @@ -8,7 +8,6 @@ var express = require('express') , mapnik = require('mapnik') , Step = require('step') , semver = require('semver') - , LRU = require('lru-cache') , Profiler = require('step-profiler') , PSQL = require('./psql') , StatsD = require('node-statsd').StatsD diff --git a/package.json b/package.json index ec675bdfd..f9b9490cd 100644 --- a/package.json +++ b/package.json @@ -6,14 +6,14 @@ "keywords": [ "cartodb" ], - "url": "https://github.com/Vizzuality/Windshaft", + "url": "https://github.com/CartoDB/Windshaft", "licenses": [{ "type": "BSD", - "url": "https://github.com/Vizzuality/Windshaft/blob/master/LICENSE" + "url": "https://github.com/CartoDB/Windshaft/blob/master/LICENSE" }], "repository": { "type": "git", - "url": "git://github.com/Vizzuality/Windshaft.git" + "url": "git://github.com/CartoDB/Windshaft.git" }, "author": "Vizzuality (http://vizzuality.com)", "contributors": [ @@ -32,7 +32,6 @@ "mapnik": "~0.7.17", "semver": "~1.1.0", "redis-mpool": "~0.0.2", - "lru-cache": "~2.3.0", "carto": "http://github.com/CartoDB/carto/tarball/0.9.5-cdb3", "step-profiler": "git://github.com/CartoDB/node-step-profiler.git#0.0.1", "underscore.string": "~1.1.6", diff --git a/test/acceptance/multilayer.js b/test/acceptance/multilayer.js index d25590ab9..6a1f4acf5 100644 --- a/test/acceptance/multilayer.js +++ b/test/acceptance/multilayer.js @@ -29,6 +29,8 @@ suite('multilayer', function() { mapnik.register_system_fonts(); var available_system_fonts = _.keys(mapnik.fontFiles()); + var IMAGE_EQUALS_TOLERANCE_PER_MIL = 20; + checkCORSHeaders = function(res) { var h = res.headers['access-control-allow-headers']; assert.ok(h); @@ -227,8 +229,7 @@ suite('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', 2, - function(err, similarity) { + assert.imageEqualsFile(res.body, './test/fixtures/test_bigpoint_red.png', IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err) { next(err); }); }); @@ -301,8 +302,7 @@ suite('multilayer', function() { }, {}, function(res) { assert.equal(res.statusCode, 200, res.body); assert.equal(res.headers['content-type'], "image/png"); - assert.imageEqualsFile(res.body, './test/fixtures/test_table_0_0_0_multilayer1.png', 2, - function(err, similarity) { + assert.imageEqualsFile(res.body, './test/fixtures/test_table_0_0_0_multilayer1.png', IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err) { next(err); }); }); @@ -412,8 +412,7 @@ suite('multilayer', function() { }, {}, function(res) { assert.equal(res.statusCode, 200, res.body); assert.equal(res.headers['content-type'], "image/png"); - assert.imageEqualsFile(res.body, './test/fixtures/test_table_0_0_0_multilayer1.png', 2, - function(err, similarity) { + assert.imageEqualsFile(res.body, './test/fixtures/test_table_0_0_0_multilayer1.png', IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err) { next(err); }); }); @@ -521,8 +520,7 @@ suite('multilayer', function() { }, {}, function(res) { assert.equal(res.statusCode, 200, res.body); assert.equal(res.headers['content-type'], "image/png"); - assert.imageEqualsFile(res.body, './test/fixtures/test_table_0_0_0_multilayer1.png', 2, - function(err, similarity) { + assert.imageEqualsFile(res.body, './test/fixtures/test_table_0_0_0_multilayer1.png', IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err) { next(err); }); }); @@ -635,8 +633,7 @@ suite('multilayer', function() { }, {}, function(res) { assert.equal(res.statusCode, 200, res.body); assert.equal(res.headers['content-type'], "image/png"); - assert.imageEqualsFile(res.body, './test/fixtures/test_table_0_0_0_multilayer1.png', 2, - function(err, similarity) { + assert.imageEqualsFile(res.body, './test/fixtures/test_table_0_0_0_multilayer1.png', IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err) { next(err); }); }); @@ -884,8 +881,7 @@ suite('multilayer', function() { }, {}, function(res) { assert.equal(res.statusCode, 200, res.body); assert.equal(res.headers['content-type'], "image/png"); - assert.imageEqualsFile(res.body, './test/fixtures/test_table_0_0_0_multilayer2.png', 2, - function(err, similarity) { + assert.imageEqualsFile(res.body, './test/fixtures/test_table_0_0_0_multilayer2.png', IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err) { next(err); }); }); @@ -918,8 +914,7 @@ suite('multilayer', function() { }, {}, function(res) { assert.equal(res.statusCode, 200, res.body); assert.equal(res.headers['content-type'], "image/png"); - assert.imageEqualsFile(res.body, './test/fixtures/test_table_0_0_0_multilayer3.png', 2, - function(err, similarity) { + assert.imageEqualsFile(res.body, './test/fixtures/test_table_0_0_0_multilayer3.png', IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err) { next(err); }); }); @@ -1014,8 +1009,7 @@ suite('multilayer', function() { }, {}, function(res) { assert.equal(res.statusCode, 200, res.body); assert.equal(res.headers['content-type'], "image/png"); - assert.imageEqualsFile(res.body, './test/fixtures/test_table_0_0_0_multilayer4.png', 2, - function(err, similarity) { + assert.imageEqualsFile(res.body, './test/fixtures/test_table_0_0_0_multilayer4.png', IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err) { next(err); }); }); diff --git a/test/acceptance/server_gettile.js b/test/acceptance/server_gettile.js index eea47f30d..a887b0532 100644 --- a/test/acceptance/server_gettile.js +++ b/test/acceptance/server_gettile.js @@ -44,6 +44,8 @@ suite('server_gettile', function() { var mapnik_version = global.environment.mapnik_version || mapnik.versions.mapnik; + var IMAGE_EQUALS_TOLERANCE_PER_MIL = 25; + var default_style; if ( semver.satisfies(mapnik_version, '<2.1.0') ) { // 2.0.0 default @@ -107,7 +109,7 @@ suite('server_gettile', function() { status: 200, headers: { 'Content-Type': 'image/png' } }, function(res){ - assert.imageEqualsFile(res.body, './test/fixtures/test_table_13_4011_3088.png', 2, function(err, similarity) { + assert.imageEqualsFile(res.body, './test/fixtures/test_table_13_4011_3088.png', IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err) { if (err) throw err; assert.deepEqual(res.headers['content-type'], "image/png"); done(); @@ -196,7 +198,7 @@ suite('server_gettile', function() { status: 200, headers: { 'Content-Type': 'image/png' } }, function(res){ - assert.imageEqualsFile(res.body, './test/fixtures/test_table_13_4011_3088_limit_2.png', 2, function(err, similarity) { + assert.imageEqualsFile(res.body, './test/fixtures/test_table_13_4011_3088_limit_2.png', IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err) { if (err) throw err; assert.deepEqual(res.headers['content-type'], "image/png"); done(); @@ -293,8 +295,7 @@ suite('server_gettile', function() { status: 200, headers: { 'Content-Type': 'image/png' } }, function(res){ - assert.imageEqualsFile(res.body, './test/fixtures/test_table_13_4011_3088_styled.png', 2, - function(err, similarity) { + assert.imageEqualsFile(res.body, './test/fixtures/test_table_13_4011_3088_styled.png', IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err) { if (err) throw err; done(); }); @@ -311,8 +312,7 @@ suite('server_gettile', function() { },{}, function(res){ assert.ok(!res.headers.hasOwnProperty('x-windshaft-cache'), "Did hit renderer cache on first time"); - assert.imageEqualsFile(res.body, './test/fixtures/test_table_13_4011_3088_styled.png', 2, - function(err, similarity) { + assert.imageEqualsFile(res.body, './test/fixtures/test_table_13_4011_3088_styled.png', IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err) { if (err) { done(err); return; } assert.response(server, { url: '/database/windshaft_test/table/test_table/13/4011/3087.png?cache_buster=5&' + style, @@ -341,8 +341,7 @@ suite('server_gettile', function() { },{}, function(res){ assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body); assert.equal(res.headers['content-type'], "image/png"); - assert.imageEqualsFile(res.body, './test/fixtures/test_table_13_4011_3088_styled_black.png', 2, - function(err, similarity) { + assert.imageEqualsFile(res.body, './test/fixtures/test_table_13_4011_3088_styled_black.png', IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err, similarity) { if (err) throw err; done(); }); @@ -358,8 +357,7 @@ suite('server_gettile', function() { },{}, function(res){ assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body); assert.equal(res.headers['content-type'], "image/png"); - assert.imageEqualsFile(res.body, './test/fixtures/test_table_13_4011_3088_styled_black.png', 2, - function(err, similarity) { + assert.imageEqualsFile(res.body, './test/fixtures/test_table_13_4011_3088_styled_black.png', IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err) { if (err) throw err; done(); }); @@ -376,8 +374,7 @@ suite('server_gettile', function() { status: 200, headers: { 'Content-Type': 'image/png' } }, function(res){ - assert.imageEqualsFile(res.body, './test/fixtures/test_table_13_4011_3088_styled_black.png', 2, - function(err, similarity) { + assert.imageEqualsFile(res.body, './test/fixtures/test_table_13_4011_3088_styled_black.png', IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err) { if (err) throw err; done(); }); @@ -406,7 +403,7 @@ suite('server_gettile', function() { status: 200, headers: { 'Content-Type': 'image/png' } }, function(res){ - assert.imageEqualsFile(res.body, './test/fixtures/test_table_13_4011_3088_styled_black.png', 2, function(err, similarity) { + assert.imageEqualsFile(res.body, './test/fixtures/test_table_13_4011_3088_styled_black.png', IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err) { if (err) throw err; assert.deepEqual(res.headers['content-type'], "image/png"); @@ -420,7 +417,7 @@ suite('server_gettile', function() { status: 200, headers: { 'Content-Type': 'image/png' } }, function(res){ - assert.imageEqualsFile(res.body, './test/fixtures/test_table_13_4011_3088_styled_black.png', 2, function(err, similarity) { + assert.imageEqualsFile(res.body, './test/fixtures/test_table_13_4011_3088_styled_black.png', IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err) { if (err) throw err; assert.deepEqual(res.headers['content-type'], "image/png"); @@ -433,12 +430,12 @@ suite('server_gettile', function() { status: 200, headers: { 'Content-Type': 'image/png' } }, function(res){ - assert.imageEqualsFile(res.body, './test/fixtures/test_table_13_4011_3088.png', 2, function(err, similarity) { + assert.imageEqualsFile(res.body, './test/fixtures/test_table_13_4011_3088.png', IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err) { if (err) throw err; assert.deepEqual(res.headers['content-type'], "image/png"); done(); }); - }); + }); }); }); }); @@ -471,8 +468,7 @@ suite('server_gettile', function() { },{}, function(res){ assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body); assert.equal(res.headers['content-type'], "image/png"); - assert.imageEqualsFile(res.body, './test/fixtures/test_default_mapnik_point.png', 2, - function(err, similarity) { + assert.imageEqualsFile(res.body, './test/fixtures/test_default_mapnik_point.png', IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err) { if (err) throw err; done(); }); @@ -545,8 +541,7 @@ suite('server_gettile', function() { status: 200, headers: { 'Content-Type': 'image/png' } }, function(res){ - assert.imageEqualsFile(res.body, './test/fixtures/test_table_13_4011_3088_styled.png', 2, - function(err, similarity) { + assert.imageEqualsFile(res.body, './test/fixtures/test_table_13_4011_3088_styled.png', IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err) { err = err ? null : new Error("Tile starts with unexpected style!"); next(err); }); @@ -583,8 +578,7 @@ suite('server_gettile', function() { status: 200, headers: { 'Content-Type': 'image/png' } }, function(res){ - assert.imageEqualsFile(res.body, './test/fixtures/test_table_13_4011_3088_styled.png', 2, - function(err, similarity) { + assert.imageEqualsFile(res.body, './test/fixtures/test_table_13_4011_3088_styled.png', IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err) { if (err) { next(err); return; } next(null); }); @@ -614,8 +608,7 @@ suite('server_gettile', function() { status: 200, headers: { 'Content-Type': 'image/png' } }, function(res){ - assert.imageEqualsFile(res.body, './test/fixtures/test_table_13_4011_3088_svg1.png', 2, - function(err, similarity) { + assert.imageEqualsFile(res.body, './test/fixtures/test_table_13_4011_3088_svg1.png', IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err) { next(err); }); }); @@ -650,8 +643,7 @@ suite('server_gettile', function() { },{}, function(res){ assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body); assert.equal(res.headers['content-type'], "image/png"); - assert.imageEqualsFile(res.body, './test/fixtures/test_table_13_4011_3088_svg2.png', 2, - function(err, similarity) { + assert.imageEqualsFile(res.body, './test/fixtures/test_table_13_4011_3088_svg2.png', IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err) { if (err) { next(err); return; } next(null); }); @@ -668,8 +660,7 @@ suite('server_gettile', function() { },{ }, function(res){ assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body); assert.equal(res.headers['content-type'], "image/png"); - assert.imageEqualsFile(res.body, './test/fixtures/test_table_13_4011_3088_svg1.png', 2, - function(err, similarity) { + assert.imageEqualsFile(res.body, './test/fixtures/test_table_13_4011_3088_svg1.png', IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err) { next(err); }); }); @@ -685,8 +676,7 @@ suite('server_gettile', function() { },{ }, function(res){ assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body); assert.equal(res.headers['content-type'], "image/png"); - assert.imageEqualsFile(res.body, './test/fixtures/test_table_13_4011_3088_svg2.png', 2, - function(err, similarity) { + assert.imageEqualsFile(res.body, './test/fixtures/test_table_13_4011_3088_svg2.png', IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err) { if (err) { next(err); return; } next(null); }); @@ -718,8 +708,7 @@ suite('server_gettile', function() { headers: { 'Content-Type': 'image/png' } }, function(res){ numrequests = res_serv_status.numrequests; - assert.imageEqualsFile(res.body, './test/fixtures/test_table_13_4011_3088_svg2.png', 2, - function(err, similarity) { + assert.imageEqualsFile(res.body, './test/fixtures/test_table_13_4011_3088_svg2.png', IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err) { next(err); }); }); @@ -745,8 +734,7 @@ suite('server_gettile', function() { assert.equal(res.statusCode, 200, res.statusCode + ': ' + res.body); assert.equal(res_serv_status.numrequests, numrequests+1); assert.equal(res.headers['content-type'], "image/png"); - assert.imageEqualsFile(res.body, './test/fixtures/test_table_13_4011_3088_svg2.png', 2, - function(err, similarity) { + assert.imageEqualsFile(res.body, './test/fixtures/test_table_13_4011_3088_svg2.png', IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err) { next(err); }); }); @@ -765,8 +753,7 @@ suite('server_gettile', function() { // millstone should not make another request assert.equal(res_serv_status.numrequests, numrequests+1); assert.equal(res.headers['content-type'], "image/png"); - assert.imageEqualsFile(res.body, './test/fixtures/test_table_13_4011_3088_svg2.png', 2, - function(err, similarity) { + assert.imageEqualsFile(res.body, './test/fixtures/test_table_13_4011_3088_svg2.png', IMAGE_EQUALS_TOLERANCE_PER_MIL, function(err) { next(err); }); }); diff --git a/test/support/assert.js b/test/support/assert.js index 37b5b77d6..111ff9201 100644 --- a/test/support/assert.js +++ b/test/support/assert.js @@ -1,29 +1,39 @@ // Cribbed from the ever prolific Konstantin Kaefer // https://github.com/mapbox/tilelive-mapnik/blob/master/test/support/assert.js -var fs = require('fs'); -var http = require('http'); -var path = require('path'); -var exec = require('child_process').exec; +var exec = require('child_process').exec, + fs = require('fs'), + http = require('http'), + path = require('path'), + util = require('util'); var assert = module.exports = exports = require('assert'); -// -// @param tol tolerated mean color distance, as a percent. See FUZZY in -// http://www.imagemagick.org/script/command-line-options.php#metric -// -assert.imageEqualsFile = function(buffer, file_b, tol, callback) { +/** + * Takes an image data as an input and an image path and compare them using ImageMagick fuzz algorithm, if case the + * similarity is not within the tolerance limit it will callback with an error. + * + * @param buffer The image data to compare from + * @param {string} referenceImageRelativeFilePath The relative file to compare against + * @param {number} tolerance tolerated mean color distance, as a per mil (‰) + * @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) { if (!callback) callback = function(err) { if (err) throw err; }; - file_b = path.resolve(file_b); - var file_a = '/tmp/windshaft-test-image-' + (Math.random() * 1e16); // TODO: make predictable - var err = fs.writeFileSync(file_a, buffer, 'binary'); + var referenceImageFilePath = path.resolve(referenceImageRelativeFilePath), + testImageFilePath = '/tmp/windshaft-test-image-' + (Math.random() * 1e16); // TODO: make predictable + var err = fs.writeFileSync(testImageFilePath, buffer, 'binary'); if (err) throw err; - var fuzz = tol + '%'; - exec('compare -metric fuzz "' + file_a + '" "' + - file_b + '" /dev/null', function(err, stdout, stderr) { + var imageMagickCmd = util.format( + 'compare -metric fuzz "%s" "%s" /dev/null', + testImageFilePath, referenceImageFilePath + ); + + exec(imageMagickCmd, function(err, stdout, stderr) { if (err) { - fs.unlinkSync(file_a); + fs.unlinkSync(testImageFilePath); callback(err); } else { stderr = stderr.trim(); @@ -32,15 +42,18 @@ assert.imageEqualsFile = function(buffer, file_b, tol, callback) { callback(new Error("No match for " + stderr)); return; } - var similarity = parseFloat(metrics[2]); - if ( similarity > (tol/100) ) { - var err = new Error('Images not equal(' + similarity + '): ' + - file_a + ' ' + file_b); - err.similarity = similarity; - callback(err); + 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)', + testImageFilePath, referenceImageFilePath, similarity, tolerancePerMil) + ); + err.similarity = similarity; + callback(err); } else { - fs.unlinkSync(file_a); - callback(null); + fs.unlinkSync(testImageFilePath); + callback(null); } } }); @@ -53,7 +66,7 @@ assert.imageEqualsFile = function(buffer, file_b, tol, callback) { * @param {Server} server * @param {Object} req * @param {Object|Function} res - * @param {String} msg + * @param {String|Function} msg */ assert.response = function(server, req, res, msg){ var port = 5555;