diff --git a/INSTALL.md b/INSTALL.md index cfb6cfc4..751c5ef8 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -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 diff --git a/test/acceptance/limits.js b/test/acceptance/limits.js index e7e4a305..ef91dac4 100644 --- a/test/acceptance/limits.js +++ b/test/acceptance/limits.js @@ -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); } diff --git a/test/acceptance/multilayer.js b/test/acceptance/multilayer.js index acd13ddc..5375fc87 100644 --- a/test/acceptance/multilayer.js +++ b/test/acceptance/multilayer.js @@ -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); } @@ -404,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); }); @@ -443,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); }); @@ -1019,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); } diff --git a/test/acceptance/overviews_queries.js b/test/acceptance/overviews_queries.js index 8024ba72..0434ebfc 100644 --- a/test/acceptance/overviews_queries.js +++ b/test/acceptance/overviews_queries.js @@ -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); }; } diff --git a/test/acceptance/ported/blend.js b/test/acceptance/ported/blend.js index eca436ae..1e844d80 100644 --- a/test/acceptance/ported/blend.js +++ b/test/acceptance/ported/blend.js @@ -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.ok(!err); - done(); - }); + assert.imageBufferIsSimilarToFile(res.body, blendPngFixture(zxy), IMAGE_TOLERANCE_PER_MIL, + function(err) { + assert.ok(!err); + done(); + } + ); }); }); }); diff --git a/test/acceptance/ported/blend_filtering.js b/test/acceptance/ported/blend_filtering.js index c1b11933..dc8cd470 100644 --- a/test/acceptance/ported/blend_filtering.js +++ b/test/acceptance/ported/blend_filtering.js @@ -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.ok(!err); - done(); - }); + assert.imageBufferIsSimilarToFile(res.body, blendPngFixture(filteredLayers), IMG_TOLERANCE_PER_MIL, + function(err) { + assert.ok(!err); + done(); + } + ); }); }); }); diff --git a/test/acceptance/ported/blend_http_fallback.js b/test/acceptance/ported/blend_http_fallback.js index 3c35c5fc..91e832f6 100644 --- a/test/acceptance/ported/blend_http_fallback.js +++ b/test/acceptance/ported/blend_http_fallback.js @@ -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.ok(!err, err); - done(); - }); + assert.imageBufferIsSimilarToFile(res.body, blendPngFixture(filteredLayers), IMG_TOLERANCE_PER_MIL, + function(err) { + assert.ok(!err, err); + done(); + } + ); }); }); }); diff --git a/test/acceptance/ported/external_resources.js b/test/acceptance/ported/external_resources.js index a0affa83..6f73b52e 100644 --- a/test/acceptance/ported/external_resources.js +++ b/test/acceptance/ported/external_resources.js @@ -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); }; } diff --git a/test/acceptance/ported/limits.js b/test/acceptance/ported/limits.js index f032ff3f..c111c87c 100644 --- a/test/acceptance/ported/limits.js +++ b/test/acceptance/ported/limits.js @@ -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) { - finish(function(finishErr) { - done(err || finishErr); - }); - }); + assert.imageBufferIsSimilarToFile(res.body, fixtureImage, IMAGE_EQUALS_TOLERANCE_PER_MIL, + function(err) { + finish(function(finishErr) { + done(err || finishErr); + }); + } + ); }); }); }); diff --git a/test/acceptance/ported/multilayer.js b/test/acceptance/ported/multilayer.js index 7e9b984c..eb337070 100644 --- a/test/acceptance/ported/multilayer.js +++ b/test/acceptance/ported/multilayer.js @@ -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); }); diff --git a/test/acceptance/ported/raster.js b/test/acceptance/ported/raster.js index 00d34509..cd3d43b9 100644 --- a/test/acceptance/ported/raster.js +++ b/test/acceptance/ported/raster.js @@ -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 { diff --git a/test/acceptance/ported/server_gettile.js b/test/acceptance/ported/server_gettile.js index 7dad9aa3..580e8a61 100644 --- a/test/acceptance/ported/server_gettile.js +++ b/test/acceptance/ported/server_gettile.js @@ -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) { - - finish(function(finishErr) { - done(err || finishErr); - }); - }); + assert.imageBufferIsSimilarToFile(res.body, imageFixture, IMAGE_EQUALS_TOLERANCE_PER_MIL, + function(err) { + finish(function(finishErr) { + done(err || finishErr); + }); + } + ); }); }); }); diff --git a/test/acceptance/ported/server_png8_format.js b/test/acceptance/ported/server_png8_format.js index fdd5a131..39c89c66 100644 --- a/test/acceptance/ported/server_png8_format.js +++ b/test/acceptance/ported/server_png8_format.js @@ -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; diff --git a/test/acceptance/ported/torque_png.js b/test/acceptance/ported/torque_png.js index fef2c621..69eaadab 100644 --- a/test/acceptance/ported/torque_png.js +++ b/test/acceptance/ported/torque_png.js @@ -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.ok(!err); - done(); - }); + assert.imageBufferIsSimilarToFile(res.body, torquePngFixture(zxy), IMAGE_TOLERANCE_PER_MIL, + function(err) { + assert.ok(!err); + done(); + } + ); }); }); }); diff --git a/test/acceptance/ported/wrap.js b/test/acceptance/ported/wrap.js index 1f46b5c3..64a8e2f4 100644 --- a/test/acceptance/ported/wrap.js +++ b/test/acceptance/ported/wrap.js @@ -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.ok(!err); - done(); - }); + assert.imageBufferIsSimilarToFile(res.body, blendPngFixture(fixtureZxy), IMG_TOLERANCE_PER_MIL, + function(err) { + assert.ok(!err); + done(); + } + ); }); }); }); diff --git a/test/support/assert.js b/test/support/assert.js index 3e66265b..a53d6fbf 100644 --- a/test/support/assert.js +++ b/test/support/assert.js @@ -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); + var referenceImageFilePath = path.resolve(referenceImageRelativeFilePath); + var referenceImageBuffer = fs.readFileSync(referenceImageFilePath, { encoding: null }); + + assert.imageBuffersAreSimilar(buffer, referenceImageBuffer, tolerance, callback); +}; + +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')); + + imagesAreSimilar(testImage, referenceImage, tolerance, callback); +}; + +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 })); + + imagesAreSimilar(testImage, referenceImage, tolerance, function(err) { + if (err) { + var testImageFilePath = randomImagePath(); + testImage.save(testImageFilePath); + } callback(err); }); }; -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'); - - imageFilesAreEqual(imageFilePathA, imageFilePathB, tolerance, function(err, similarity) { - callback(err, [imageFilePathA, imageFilePathB], similarity); - }); -}; - -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; -} - -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 - ); - - exec(imageMagickCmd, function(err, stdout, stderr) { - if (err) { - fs.unlinkSync(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