diff --git a/.travis.yml b/.travis.yml index 2d6908b488..cf56d6cc8d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,13 @@ +env: + - DISPLAY=:99.0 + before_install: - "sudo pip install -r requirements.txt" - "npm install -g npm && npm install" before_script: - "rm src/ol/renderer/webgl/*shader.js" + - "sh -e /etc/init.d/xvfb start" script: "./build.py ci" diff --git a/build.py b/build.py index 0ca69bd75d..058d61bc76 100755 --- a/build.py +++ b/build.py @@ -145,6 +145,10 @@ SPEC = [path for path in ifind('test/spec') if path.endswith('.js')] +SPEC_RENDERING = [path + for path in ifind('test_rendering/spec') + if path.endswith('.js')] + TASKS = [path for path in ifind('tasks') if path.endswith('.js')] @@ -172,7 +176,7 @@ def report_sizes(t): virtual('default', 'build') -virtual('ci', 'lint', 'build', 'test', +virtual('ci', 'lint', 'build', 'test', 'test-rendering', 'build/examples/all.combined.js', 'check-examples', 'apidoc') @@ -231,19 +235,28 @@ for glsl_src in GLSL_SRC: shader_src_helper(glsl_src) -@target('build/test/requireall.js', SPEC) -def build_test_requireall_js(t): +def build_requires(task): requires = set() - for dependency in t.dependencies: + for dependency in task.dependencies: for line in open(dependency, 'rU'): match = re.match(r'goog\.provide\(\'(.*)\'\);', line) if match: requires.add(match.group(1)) - with open(t.name, 'wb') as f: + with open(task.name, 'wb') as f: for require in sorted(requires): f.write('goog.require(\'%s\');\n' % (require,)) +@target('build/test_requires.js', SPEC) +def build_test_requires(t): + build_requires(t) + + +@target('build/test_rendering_requires.js', SPEC_RENDERING) +def build_test_rendering_requires(t): + build_requires(t) + + virtual('build-examples', 'examples', 'build/examples/all.combined.js', EXAMPLES_COMBINED) @@ -392,7 +405,8 @@ def examples_star_combined_js(name, match): return Target(name, action=action, dependencies=dependencies) -@target('serve', 'examples', NPM_INSTALL) +@target('serve', 'examples', 'build/test_requires.js', 'build/test_rendering_requires.js', + NPM_INSTALL) def serve(t): t.run('node', 'tasks/serve.js') @@ -401,7 +415,8 @@ virtual('lint', 'build/lint-timestamp', 'build/check-requires-timestamp', 'build/check-whitespace-timestamp', 'jshint') -@target('build/lint-timestamp', SRC, EXAMPLES_SRC, SPEC, precious=True) +@target('build/lint-timestamp', SRC, EXAMPLES_SRC, SPEC, SPEC_RENDERING, + precious=True) def build_lint_src_timestamp(t): t.run('%(GJSLINT)s', '--jslint_error=all', @@ -412,8 +427,8 @@ def build_lint_src_timestamp(t): virtual('jshint', 'build/jshint-timestamp') -@target('build/jshint-timestamp', SRC, EXAMPLES_SRC, SPEC, TASKS, - NPM_INSTALL, precious=True) +@target('build/jshint-timestamp', SRC, EXAMPLES_SRC, SPEC, SPEC_RENDERING, + TASKS, NPM_INSTALL, precious=True) def build_jshint_timestamp(t): t.run(variables.JSHINT, '--verbose', t.newer(t.dependencies)) t.touch() @@ -442,7 +457,8 @@ def _strip_comments(lines): yield lineno, line -@target('build/check-requires-timestamp', SRC, EXAMPLES_SRC, SHADER_SRC, SPEC) +@target('build/check-requires-timestamp', SRC, EXAMPLES_SRC, SHADER_SRC, + SPEC, SPEC_RENDERING) def build_check_requires_timestamp(t): unused_count = 0 all_provides = set() @@ -583,7 +599,7 @@ def build_check_requires_timestamp(t): @target('build/check-whitespace-timestamp', SRC, EXAMPLES_SRC, - SPEC, JSDOC_SRC, precious=True) + SPEC, SPEC_RENDERING, JSDOC_SRC, precious=True) def build_check_whitespace_timestamp(t): CR_RE = re.compile(r'\r') LEADING_WHITESPACE_RE = re.compile(r'\s+') @@ -723,7 +739,7 @@ def check_examples(t): sys.exit(1) -@target('test', NPM_INSTALL, phony=True) +@target('test', NPM_INSTALL, 'build/test_requires.js', phony=True) def test(t): t.run('node', 'tasks/test.js') @@ -733,6 +749,16 @@ def test_coverage(t): t.run('node', 'tasks/test-coverage.js') +@target('test-rendering', 'build/test_rendering_requires.js', + NPM_INSTALL, phony=True) +def test_rendering(t): + # create a temp. profile to run the tests with WebGL + tmp_profile_dir = 'build/slimerjs-profile' + t.rm_rf(tmp_profile_dir) + t.cp_r('test_rendering/slimerjs-profile', tmp_profile_dir) + t.run('node', 'tasks/test-rendering.js') + + @target('fixme', phony=True) def find_fixme(t): regex = re.compile('FIXME|TODO') @@ -797,6 +823,7 @@ The most common targets are: CSS. This is also the default build target which runs when no target is specified. test - Runs the testsuite and displays the results. + test-rendering - Runs the rendering testsuite and displays the results. check - Runs the lint-target, builds some OpenLayers files, and then runs test. Many developers call this target often while working on the code. @@ -806,9 +833,9 @@ Other less frequently used targets are: apidoc - Builds the API-Documentation using JSDoc3. ci - Builds all examples in various modes and usually takes a long time to finish. This target calls the following - targets: lint, build, build-all, test, build-examples, - check-examples and apidoc. This is the target run on - Travis CI. + targets: lint, build, build-all, test, test-rendering, + build-examples, check-examples and apidoc. This is the + target run on Travis CI. test-coverage - Generates a test coverage report in the coverage folder. reallyclean - Remove untracked files from the repository. checkdeps - Checks whether all required development software is diff --git a/package.json b/package.json index 3fdf2de60c..ff89ab49da 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,9 @@ "mocha-phantomjs": "3.5.1", "phantomjs": "1.9.10", "proj4": "2.3.3", - "sinon": "1.10.3" + "sinon": "1.10.3", + "slimerjs-edge": "0.10.0-pre-2", + "resemblejs": "1.2.0" }, "ext": [ "rbush" diff --git a/tasks/serve.js b/tasks/serve.js index a7f792d51b..dfed1e5e8d 100644 --- a/tasks/serve.js +++ b/tasks/serve.js @@ -23,7 +23,10 @@ var createServer = exports.createServer = function(callback) { lib: [ 'src/**/*.js', 'build/ol.ext/*.js', - 'test/spec/**/*.test.js' + 'test/spec/**/*.test.js', + 'test_rendering/spec/**/*.test.js', + 'build/test_requires.js', + 'build/test_rendering_requires.js' ], main: 'examples/*.js' }); @@ -41,12 +44,21 @@ var createServer = exports.createServer = function(callback) { getMain: function(req) { var main; var query = url.parse(req.url, true).query; - if (query.id) { - var referer = req.headers.referer; - if (referer) { - var from = path.join(process.cwd(), - path.dirname(url.parse(referer).pathname)); - main = path.resolve(from, query.id + '.js'); + var referer = req.headers.referer; + var pathName = url.parse(referer).pathname; + if (pathName.indexOf('/test/') === 0) { + main = path.resolve( + path.join(process.cwd(), 'build'), 'test_requires.js'); + } else if (pathName.indexOf('/test_rendering/') === 0) { + main = path.resolve( + path.join(process.cwd(), 'build'), 'test_rendering_requires.js'); + } else { + if (query.id) { + if (referer) { + var from = path.join(process.cwd(), + path.dirname(url.parse(referer).pathname)); + main = path.resolve(from, query.id + '.js'); + } } } return main; diff --git a/tasks/test-coverage.js b/tasks/test-coverage.js index 5e1aab20be..8fc0061e43 100644 --- a/tasks/test-coverage.js +++ b/tasks/test-coverage.js @@ -13,7 +13,7 @@ var wrench = require('wrench'); var path = require('path'); var glob = require('glob'); -var runTestsuite = require('./test'); +var runTestsuite = require('./test').runTests; // setup some pathes var dir = path.join(__dirname, '../src'); diff --git a/tasks/test-rendering.js b/tasks/test-rendering.js new file mode 100644 index 0000000000..d440a52e52 --- /dev/null +++ b/tasks/test-rendering.js @@ -0,0 +1,65 @@ +/** + * This task starts a dev server that provides a script loader for OpenLayers + * and Closure Library and runs rendering tests in SlimerJS. + */ + +var fs = require('fs'); +var path = require('path'); +var spawn = require('child_process').spawn; + +var slimerjs = require('slimerjs-edge'); + +var serve = require('./serve'); +var listen = require('./test').listen; + + +/** + * Create the debug server and run tests. + */ +serve.createServer(function(err, server) { + if (err) { + process.stderr.write(err.message + '\n'); + process.exit(1); + } + + listen(3001, 3005, server, function(err) { + if (err) { + process.stderr.write('Server failed to start: ' + err.message + '\n'); + process.exit(1); + } + + var address = server.address(); + var url = 'http://' + address.address + ':' + address.port; + var profile = path.join(__dirname, '../build/slimerjs-profile'); + var args = [ + '-profile', + profile, + path.join(__dirname, + '../test_rendering/test.js'), + url + '/test_rendering/index.html' + ]; + + var child = spawn(slimerjs.path, args, {stdio: 'inherit'}); + child.on('exit', function(code) { + // FIXME SlimerJS has a problem with returning the correct return + // code when using a custom profile, see + // https://github.com/laurentj/slimerjs/issues/333 + // as a work-around we are currently reading the return code from + // a file created in the profile directory. + // if this issue is fixed we should use the npm package 'slimerjs' + // instead of the nightly build 'slimerjs-edge'. + var exitstatus = path.join(profile, 'exitstatus'); + fs.readFile(exitstatus, {encoding: 'utf-8'}, function(err, data) { + if (err) { + process.stderr.write( + 'Error getting the exit status of SlimerJS' + '\n'); + process.stderr.write(err); + process.exit(1); + } else { + process.exit(data); + } + }); + }); + }); + +}); diff --git a/tasks/test.js b/tasks/test.js index c9c8a96988..e644dea235 100644 --- a/tasks/test.js +++ b/tasks/test.js @@ -81,6 +81,9 @@ if (require.main === module) { }); } -module.exports = runTests; +module.exports = { + runTests: runTests, + listen: listen +}; diff --git a/test/spec/ol/format/geojsonformat.test.js b/test/spec/ol/format/geojsonformat.test.js index df3c46342a..6a4906c9b9 100644 --- a/test/spec/ol/format/geojsonformat.test.js +++ b/test/spec/ol/format/geojsonformat.test.js @@ -693,7 +693,8 @@ describe('ol.format.GeoJSON', function() { var newPoint = format.readGeometry(geojson, { featureProjection: 'EPSG:3857' }); - expect(point.getCoordinates()[0]).to.eql(newPoint.getCoordinates()[0]); + expect(point.getCoordinates()[0]).to.roughlyEqual( + newPoint.getCoordinates()[0], 1e-8); expect( Math.abs(point.getCoordinates()[1] - newPoint.getCoordinates()[1])) .to.be.lessThan(0.0000001); diff --git a/test/test-extensions.js b/test/test-extensions.js index e5fd2a2364..c9cdac18a5 100644 --- a/test/test-extensions.js +++ b/test/test-extensions.js @@ -1,8 +1,13 @@ // FIXME remove afterLoadXml as it uses the wrong XML parser on IE9 -// helper functions for async testing +// helper functions for async testing and other utility functions. (function(global) { + /** + * The default tolerance for image comparisons. + */ + global.IMAGE_TOLERANCE = 1.5; + function afterLoad(type, path, next) { var client = new XMLHttpRequest(); client.open('GET', path, true); @@ -326,5 +331,107 @@ return this; }; + global.createMapDiv = function(width, height) { + var target = document.createElement('div'); + var style = target.style; + style.position = 'absolute'; + style.left = '-1000px'; + style.top = '-1000px'; + style.width = width + 'px'; + style.height = height + 'px'; + document.body.appendChild(target); + + return target; + }; + + global.disposeMap = function(map) { + var target = map.getTarget(); + map.setTarget(null); + goog.dispose(map); + document.body.removeChild(target); + }; + + global.assertWebGL = function(map) { + if(!ol.has.WEBGL) { + expect().fail('No WebGL support!'); + } + }; + + function resembleCanvas(canvas, referenceImage, tolerance, done) { + if (window.showMap) { + document.getElementById('debug').appendChild(canvas); + } + + resemble(referenceImage) + .compareTo(canvas.getContext('2d').getImageData( + 0, 0, canvas.width, canvas.height)) + .onComplete(function(data) { + if(!data.isSameDimensions) { + expect().fail( + 'The dimensions of the reference image and ' + + 'the test canvas are not the same.'); + } + + if (data.misMatchPercentage > tolerance) { + if (window.showDiff) { + var diffImage = new Image(); + diffImage.src = data.getImageDataUrl(); + document.getElementById('debug').appendChild(diffImage); + } + expect(data.misMatchPercentage).to.be.below(tolerance); + } + done(); + }); + }; + + function expectResembleCanvas(map, referenceImage, tolerance, done) { + map.render(); + map.on('postcompose', function(event) { + var canvas = event.context.canvas; + resembleCanvas(canvas, referenceImage, tolerance, done); + }); + }; + + function expectResembleWebGL(map, referenceImage, tolerance, done) { + map.render(); + map.on('postcompose', function(event) { + if (event.frameState.animate) { + // make sure the tile-queue is empty + return; + } + + var webglCanvas = event.glContext.getCanvas(); + expect(webglCanvas).to.be.a(HTMLCanvasElement); + + // draw the WebGL canvas on a new canvas, because we can not create + // a 2d context for that canvas because there is already a webgl context. + var canvas = document.createElement('canvas'); + canvas.width = webglCanvas.width; + canvas.height = webglCanvas.height; + canvas.getContext('2d').drawImage(webglCanvas, 0, 0, + webglCanvas.width, webglCanvas.height); + + resembleCanvas(canvas, referenceImage, tolerance, done); + }); + }; + + /** + * Assert that the given map resembles a reference image. + * + * @param {ol.Map} map A map using the canvas renderer. + * @param {string} referenceImage Path to the reference image. + * @param {number} tolerance The accepted mismatch tolerance. + * @param {function} done A callback to indicate that the test is done. + */ + global.expectResemble = function(map, referenceImage, tolerance, done) { + if (map.getRenderer() instanceof ol.renderer.canvas.Map) { + expectResembleCanvas(map, referenceImage, tolerance, done); + } else if (map.getRenderer() instanceof ol.renderer.webgl.Map) { + expectResembleWebGL(map, referenceImage, tolerance, done); + } else { + expect().fail( + 'resemble only works with the canvas and WebGL renderer.'); + } + }; })(this); diff --git a/test_rendering/README.md b/test_rendering/README.md new file mode 100644 index 0000000000..8f305068a4 --- /dev/null +++ b/test_rendering/README.md @@ -0,0 +1,25 @@ +# Rendering tests + +This directory contains rendering tests which compare a rendered map with a +reference image using [resemble.js](http://huddle.github.io/Resemble.js/). + +Similar to the unit tests, there are two ways to run the tests: directly in the +browser or using [SlimerJS](http://slimerjs.org/) from the command-line. + +To run the tests in the browser, make sure the development server is running +(`./build.py serve`) and open the URL +[http://localhost:3000/test_rendering/index.html](http://localhost:3000/test_rendering/index.html). + +From the command-line the tests can be run with the build target `./build.py test-rendering`. + +## Adding new tests +When creating a new test case, a reference image has to be created. By appending `?generate` +to the URL, a canvas with the rendered map will be shown on the page when calling +`expectResemble`. Then the reference image can simply be created with a right-click +and "Save image as". + +It is recommended to only run a single test case when generating the reference image. + +## Image difference +When a test fails, an image showing the difference between the reference image and the +rendered map can be displayed by appending `?showdiff` to the URL. diff --git a/test_rendering/index.html b/test_rendering/index.html new file mode 100644 index 0000000000..b0293d06d0 --- /dev/null +++ b/test_rendering/index.html @@ -0,0 +1,93 @@ + + + + OL Rendering Test Runner + + + + + +
+ + + + + + + + + + + + + + + +
+ + diff --git a/test_rendering/slimerjs-profile/prefs.js b/test_rendering/slimerjs-profile/prefs.js new file mode 100644 index 0000000000..c2deee1837 --- /dev/null +++ b/test_rendering/slimerjs-profile/prefs.js @@ -0,0 +1,3 @@ +user_pref("webgl.force-enabled", true); +user_pref("webgl.disabled", false); +user_pref("webgl.msaa-force", true); diff --git a/test_rendering/slimerjs-profile/times.json b/test_rendering/slimerjs-profile/times.json new file mode 100755 index 0000000000..84e52a5d3e --- /dev/null +++ b/test_rendering/slimerjs-profile/times.json @@ -0,0 +1,3 @@ +{ +"created": 1427803527460 +} diff --git a/test_rendering/spec/ol/data/icon.png b/test_rendering/spec/ol/data/icon.png new file mode 100644 index 0000000000..ed886623d5 Binary files /dev/null and b/test_rendering/spec/ol/data/icon.png differ diff --git a/test_rendering/spec/ol/data/tiles/osm/5/5/12.png b/test_rendering/spec/ol/data/tiles/osm/5/5/12.png new file mode 100644 index 0000000000..ada08786c9 Binary files /dev/null and b/test_rendering/spec/ol/data/tiles/osm/5/5/12.png differ diff --git a/test_rendering/spec/ol/data/tiles/stamen-labels/5/5/12.png b/test_rendering/spec/ol/data/tiles/stamen-labels/5/5/12.png new file mode 100644 index 0000000000..b88d09eb76 Binary files /dev/null and b/test_rendering/spec/ol/data/tiles/stamen-labels/5/5/12.png differ diff --git a/test_rendering/spec/ol/expected/pan-canvas.png b/test_rendering/spec/ol/expected/pan-canvas.png new file mode 100644 index 0000000000..f9c84f316d Binary files /dev/null and b/test_rendering/spec/ol/expected/pan-canvas.png differ diff --git a/test_rendering/spec/ol/expected/pan-webgl.png b/test_rendering/spec/ol/expected/pan-webgl.png new file mode 100644 index 0000000000..49fe4e01aa Binary files /dev/null and b/test_rendering/spec/ol/expected/pan-webgl.png differ diff --git a/test_rendering/spec/ol/expected/render-canvas.png b/test_rendering/spec/ol/expected/render-canvas.png new file mode 100644 index 0000000000..ed50f1d7d2 Binary files /dev/null and b/test_rendering/spec/ol/expected/render-canvas.png differ diff --git a/test_rendering/spec/ol/expected/render-webgl.png b/test_rendering/spec/ol/expected/render-webgl.png new file mode 100644 index 0000000000..ab1663b974 Binary files /dev/null and b/test_rendering/spec/ol/expected/render-webgl.png differ diff --git a/test_rendering/spec/ol/expected/rotate-canvas.png b/test_rendering/spec/ol/expected/rotate-canvas.png new file mode 100644 index 0000000000..e614cf3c08 Binary files /dev/null and b/test_rendering/spec/ol/expected/rotate-canvas.png differ diff --git a/test_rendering/spec/ol/expected/rotate-webgl.png b/test_rendering/spec/ol/expected/rotate-webgl.png new file mode 100644 index 0000000000..014481d84b Binary files /dev/null and b/test_rendering/spec/ol/expected/rotate-webgl.png differ diff --git a/test_rendering/spec/ol/expected/zoom-canvas.png b/test_rendering/spec/ol/expected/zoom-canvas.png new file mode 100644 index 0000000000..ab3b0abde3 Binary files /dev/null and b/test_rendering/spec/ol/expected/zoom-canvas.png differ diff --git a/test_rendering/spec/ol/expected/zoom-webgl.png b/test_rendering/spec/ol/expected/zoom-webgl.png new file mode 100644 index 0000000000..f89e01ad68 Binary files /dev/null and b/test_rendering/spec/ol/expected/zoom-webgl.png differ diff --git a/test_rendering/spec/ol/layer/expected/2-layers-canvas.png b/test_rendering/spec/ol/layer/expected/2-layers-canvas.png new file mode 100644 index 0000000000..ad069af45c Binary files /dev/null and b/test_rendering/spec/ol/layer/expected/2-layers-canvas.png differ diff --git a/test_rendering/spec/ol/layer/expected/2-layers-webgl.png b/test_rendering/spec/ol/layer/expected/2-layers-webgl.png new file mode 100644 index 0000000000..a982b089de Binary files /dev/null and b/test_rendering/spec/ol/layer/expected/2-layers-webgl.png differ diff --git a/test_rendering/spec/ol/layer/expected/image-canvas.png b/test_rendering/spec/ol/layer/expected/image-canvas.png new file mode 100644 index 0000000000..86f9bedd38 Binary files /dev/null and b/test_rendering/spec/ol/layer/expected/image-canvas.png differ diff --git a/test_rendering/spec/ol/layer/expected/image-webgl.png b/test_rendering/spec/ol/layer/expected/image-webgl.png new file mode 100644 index 0000000000..e0e4a150d8 Binary files /dev/null and b/test_rendering/spec/ol/layer/expected/image-webgl.png differ diff --git a/test_rendering/spec/ol/layer/expected/opacity-canvas.png b/test_rendering/spec/ol/layer/expected/opacity-canvas.png new file mode 100644 index 0000000000..a69bb84d85 Binary files /dev/null and b/test_rendering/spec/ol/layer/expected/opacity-canvas.png differ diff --git a/test_rendering/spec/ol/layer/expected/opacity-webgl.png b/test_rendering/spec/ol/layer/expected/opacity-webgl.png new file mode 100644 index 0000000000..389d138042 Binary files /dev/null and b/test_rendering/spec/ol/layer/expected/opacity-webgl.png differ diff --git a/test_rendering/spec/ol/layer/expected/osm-canvas.png b/test_rendering/spec/ol/layer/expected/osm-canvas.png new file mode 100644 index 0000000000..86f9bedd38 Binary files /dev/null and b/test_rendering/spec/ol/layer/expected/osm-canvas.png differ diff --git a/test_rendering/spec/ol/layer/expected/osm-webgl.png b/test_rendering/spec/ol/layer/expected/osm-webgl.png new file mode 100644 index 0000000000..86f9bedd38 Binary files /dev/null and b/test_rendering/spec/ol/layer/expected/osm-webgl.png differ diff --git a/test_rendering/spec/ol/layer/image.test.js b/test_rendering/spec/ol/layer/image.test.js new file mode 100644 index 0000000000..692090e015 --- /dev/null +++ b/test_rendering/spec/ol/layer/image.test.js @@ -0,0 +1,94 @@ +goog.provide('ol.test.rendering.layer.Image'); + +describe('ol.rendering.layer.Image', function() { + + var target, map; + + function createMap(renderer) { + target = createMapDiv(50, 50); + + map = new ol.Map({ + target: target, + renderer: renderer, + view: new ol.View({ + center: ol.proj.transform( + [-122.416667, 37.783333], 'EPSG:4326', 'EPSG:3857'), + zoom: 5 + }) + }); + return map; + } + + function waitForImages(sources, layerOptions, onImagesLoaded) { + var imagesLoading = 0; + var imagesLoaded = 0; + + var update = function() { + if (imagesLoading === imagesLoaded) { + onImagesLoaded(); + } + }; + + goog.array.forEach(sources, function(source) { + source.on('imageloadstart', function(event) { + imagesLoading++; + }); + source.on('imageloadend', function(event) { + imagesLoaded++; + update(); + }); + source.on('imageloaderror', function(event) { + expect().fail('Image failed to load'); + }); + + var options = { + source: source + }; + goog.object.extend(options, layerOptions); + map.addLayer(new ol.layer.Image(options)); + }); + } + + describe('single image layer', function() { + var source; + + beforeEach(function() { + source = new ol.source.ImageStatic({ + url: 'spec/ol/data/tiles/osm/5/5/12.png', + imageExtent: new ol.tilegrid.XYZ({}).getTileCoordExtent( + [5, 5, -12 - 1]), + projection: ol.proj.get('EPSG:3857') + }); + }); + + afterEach(function() { + disposeMap(map); + }); + + it('tests the canvas renderer', function(done) { + map = createMap('canvas'); + waitForImages([source], {}, function() { + expectResemble(map, 'spec/ol/layer/expected/image-canvas.png', + IMAGE_TOLERANCE, done); + }); + }); + + it('tests the WebGL renderer', function(done) { + assertWebGL(); + map = createMap('webgl'); + waitForImages([source], {}, function() { + expectResemble(map, 'spec/ol/layer/expected/image-webgl.png', + IMAGE_TOLERANCE, done); + }); + }); + }); +}); + +goog.require('goog.array'); +goog.require('goog.object'); +goog.require('ol.proj'); +goog.require('ol.Map'); +goog.require('ol.View'); +goog.require('ol.layer.Image'); +goog.require('ol.source.ImageStatic'); +goog.require('ol.tilegrid.XYZ'); diff --git a/test_rendering/spec/ol/layer/tile.test.js b/test_rendering/spec/ol/layer/tile.test.js new file mode 100644 index 0000000000..6d2340d0e7 --- /dev/null +++ b/test_rendering/spec/ol/layer/tile.test.js @@ -0,0 +1,155 @@ +goog.provide('ol.test.rendering.layer.Tile'); + +describe('ol.rendering.layer.Tile', function() { + + var target, map; + + function createMap(renderer) { + target = createMapDiv(50, 50); + + map = new ol.Map({ + target: target, + renderer: renderer, + view: new ol.View({ + center: ol.proj.transform( + [-122.416667, 37.783333], 'EPSG:4326', 'EPSG:3857'), + zoom: 5 + }) + }); + return map; + } + + function waitForTiles(sources, layerOptions, onTileLoaded) { + var tilesLoading = 0; + var tileLoaded = 0; + + var update = function() { + if (tilesLoading === tileLoaded) { + onTileLoaded(); + } + }; + + goog.array.forEach(sources, function(source) { + source.on('tileloadstart', function(event) { + tilesLoading++; + }); + source.on('tileloadend', function(event) { + tileLoaded++; + update(); + }); + source.on('tileloaderror', function(event) { + expect().fail('Tile failed to load'); + }); + + var options = { + source: source + }; + goog.object.extend(options, layerOptions); + map.addLayer(new ol.layer.Tile(options)); + }); + } + + describe('single tile layer', function() { + var source; + + beforeEach(function() { + source = new ol.source.XYZ({ + url: 'spec/ol/data/tiles/osm/{z}/{x}/{y}.png' + }); + }); + + afterEach(function() { + disposeMap(map); + }); + + it('tests the canvas renderer', function(done) { + map = createMap('canvas'); + waitForTiles([source], {}, function() { + expectResemble(map, 'spec/ol/layer/expected/osm-canvas.png', + IMAGE_TOLERANCE, done); + }); + }); + + it('tests the WebGL renderer', function(done) { + assertWebGL(); + map = createMap('webgl'); + waitForTiles([source], {}, function() { + expectResemble(map, 'spec/ol/layer/expected/osm-webgl.png', + IMAGE_TOLERANCE, done); + }); + }); + }); + + describe('two tile layers', function() { + var source1, source2; + + beforeEach(function() { + source1 = new ol.source.XYZ({ + url: 'spec/ol/data/tiles/osm/{z}/{x}/{y}.png' + }); + source2 = new ol.source.XYZ({ + url: 'spec/ol/data/tiles/stamen-labels/{z}/{x}/{y}.png' + }); + }); + + afterEach(function() { + disposeMap(map); + }); + + it('tests the canvas renderer', function(done) { + map = createMap('canvas'); + waitForTiles([source1, source2], {}, function() { + expectResemble(map, 'spec/ol/layer/expected/2-layers-canvas.png', + IMAGE_TOLERANCE, done); + }); + }); + + it('tests the WebGL renderer', function(done) { + assertWebGL(); + map = createMap('webgl'); + waitForTiles([source1, source2], {}, function() { + expectResemble(map, 'spec/ol/layer/expected/2-layers-webgl.png', + IMAGE_TOLERANCE, done); + }); + }); + }); + + describe('tile layer with opacity', function() { + var source; + + beforeEach(function() { + source = new ol.source.XYZ({ + url: 'spec/ol/data/tiles/osm/{z}/{x}/{y}.png' + }); + }); + + afterEach(function() { + disposeMap(map); + }); + + it('tests the canvas renderer', function(done) { + map = createMap('canvas'); + waitForTiles([source], {opacity: 0.2}, function() { + expectResemble(map, 'spec/ol/layer/expected/opacity-canvas.png', + IMAGE_TOLERANCE, done); + }); + }); + + it('tests the WebGL renderer', function(done) { + assertWebGL(); + map = createMap('webgl'); + waitForTiles([source], {opacity: 0.2}, function() { + expectResemble(map, 'spec/ol/layer/expected/opacity-webgl.png', + IMAGE_TOLERANCE, done); + }); + }); + }); +}); + +goog.require('goog.array'); +goog.require('goog.object'); +goog.require('ol.proj'); +goog.require('ol.Map'); +goog.require('ol.View'); +goog.require('ol.layer.Tile'); +goog.require('ol.source.XYZ'); diff --git a/test_rendering/spec/ol/map.test.js b/test_rendering/spec/ol/map.test.js new file mode 100644 index 0000000000..424de13691 --- /dev/null +++ b/test_rendering/spec/ol/map.test.js @@ -0,0 +1,124 @@ +goog.provide('ol.test.rendering.Map'); + +describe('ol.rendering.Map', function() { + + var target, map; + + function createMap(renderer) { + target = createMapDiv(50, 50); + + var vectorLayer = new ol.layer.Vector({ + source: new ol.source.Vector({ + features: [new ol.Feature({ + geometry: new ol.geom.Point([0, 0]) + })] + }) + }); + + map = new ol.Map({ + target: target, + renderer: renderer, + layers: [vectorLayer], + view: new ol.View({ + projection: 'EPSG:4326', + center: [0, 0], + resolution: 1 + }) + }); + return map; + } + + describe('#render()', function() { + afterEach(function() { + disposeMap(map); + }); + + it('tests the canvas renderer', function(done) { + map = createMap('canvas'); + expectResemble( + map, 'spec/ol/expected/render-canvas.png', IMAGE_TOLERANCE, done); + }); + + it('tests the WebGL renderer', function(done) { + assertWebGL(); + map = createMap('webgl'); + expectResemble( + map, 'spec/ol/expected/render-webgl.png', IMAGE_TOLERANCE, done); + }); + }); + + describe('#pan()', function() { + afterEach(function() { + disposeMap(map); + }); + + it('tests the canvas renderer', function(done) { + map = createMap('canvas'); + map.getView().setCenter([10, 10]); + expectResemble( + map, 'spec/ol/expected/pan-canvas.png', IMAGE_TOLERANCE, done); + }); + + it('tests the WebGL renderer', function(done) { + assertWebGL(); + map = createMap('webgl'); + map.getView().setCenter([10, 10]); + expectResemble( + map, 'spec/ol/expected/pan-webgl.png', IMAGE_TOLERANCE, done); + }); + }); + + describe('#rotate()', function() { + afterEach(function() { + disposeMap(map); + }); + + it('tests the canvas renderer', function(done) { + map = createMap('canvas'); + map.getView().setRotation(90); + map.getView().setCenter([10, 10]); + expectResemble( + map, 'spec/ol/expected/rotate-canvas.png', IMAGE_TOLERANCE, done); + }); + + it('tests the WebGL renderer', function(done) { + assertWebGL(); + map = createMap('webgl'); + map.getView().setRotation(90); + map.getView().setCenter([10, 10]); + expectResemble( + map, 'spec/ol/expected/rotate-webgl.png', IMAGE_TOLERANCE, done); + }); + }); + + describe('#zoom()', function() { + afterEach(function() { + disposeMap(map); + }); + + it('tests the canvas renderer', function(done) { + map = createMap('canvas'); + map.getView().setCenter([10, 10]); + map.getView().setResolution(2); + expectResemble( + map, 'spec/ol/expected/zoom-canvas.png', IMAGE_TOLERANCE, done); + }); + + it('tests the WebGL renderer', function(done) { + assertWebGL(); + map = createMap('webgl'); + map.getView().setCenter([10, 10]); + map.getView().setResolution(2); + expectResemble( + map, 'spec/ol/expected/zoom-webgl.png', IMAGE_TOLERANCE, done); + }); + }); +}); + +goog.require('goog.dispose'); +goog.require('ol.Feature'); +goog.require('ol.geom.Point'); +goog.require('ol.Map'); +goog.require('ol.View'); +goog.require('ol.layer.Vector'); +goog.require('ol.source.Vector'); diff --git a/test_rendering/spec/ol/style/circle.test.js b/test_rendering/spec/ol/style/circle.test.js new file mode 100644 index 0000000000..72a53c1eaf --- /dev/null +++ b/test_rendering/spec/ol/style/circle.test.js @@ -0,0 +1,201 @@ +goog.provide('ol.test.rendering.style.Circle'); + +describe('ol.rendering.style.Circle', function() { + + var target, map, vectorSource; + + function createMap(renderer) { + target = createMapDiv(50, 50); + + vectorSource = new ol.source.Vector(); + vectorLayer = new ol.layer.Vector({ + source: vectorSource + }); + + map = new ol.Map({ + target: target, + renderer: renderer, + layers: [vectorLayer], + view: new ol.View({ + projection: 'EPSG:4326', + center: [0, 0], + resolution: 1 + }) + }); + return map; + } + + describe('#render', function() { + afterEach(function() { + disposeMap(map); + }); + + function createFeatures() { + var feature; + feature = new ol.Feature({ + geometry: new ol.geom.Point([-20, 18]) + }); + feature.setStyle(new ol.style.Style({ + image: new ol.style.Circle({ + radius: 2, + fill: new ol.style.Fill({ + color: '#91E339' + }) + }) + })); + vectorSource.addFeature(feature); + + feature = new ol.Feature({ + geometry: new ol.geom.Point([-10, 18]) + }); + feature.setStyle(new ol.style.Style({ + image: new ol.style.Circle({ + radius: 4, + fill: new ol.style.Fill({ + color: '#5447E6' + }) + }) + })); + vectorSource.addFeature(feature); + + feature = new ol.Feature({ + geometry: new ol.geom.Point([4, 18]) + }); + feature.setStyle(new ol.style.Style({ + image: new ol.style.Circle({ + radius: 6, + fill: new ol.style.Fill({ + color: '#92A8A6' + }) + }) + })); + vectorSource.addFeature(feature); + + feature = new ol.Feature({ + geometry: new ol.geom.Point([-20, 3]) + }); + feature.setStyle(new ol.style.Style({ + image: new ol.style.Circle({ + radius: 2, + fill: new ol.style.Fill({ + color: '#91E339' + }), + stroke: new ol.style.Stroke({ + color: '#000000', + width: 1 + }) + }) + })); + vectorSource.addFeature(feature); + + feature = new ol.Feature({ + geometry: new ol.geom.Point([-10, 3]) + }); + feature.setStyle(new ol.style.Style({ + image: new ol.style.Circle({ + radius: 4, + fill: new ol.style.Fill({ + color: '#5447E6' + }), + stroke: new ol.style.Stroke({ + color: '#000000', + width: 2 + }) + }) + })); + vectorSource.addFeature(feature); + + feature = new ol.Feature({ + geometry: new ol.geom.Point([4, 3]) + }); + feature.setStyle(new ol.style.Style({ + image: new ol.style.Circle({ + radius: 6, + fill: new ol.style.Fill({ + color: '#92A8A6' + }), + stroke: new ol.style.Stroke({ + color: '#000000', + width: 3 + }) + }) + })); + vectorSource.addFeature(feature); + + feature = new ol.Feature({ + geometry: new ol.geom.Point([-20, -15]) + }); + feature.setStyle(new ol.style.Style({ + image: new ol.style.Circle({ + radius: 2, + stroke: new ol.style.Stroke({ + color: '#256308', + width: 1 + }) + }) + })); + vectorSource.addFeature(feature); + + feature = new ol.Feature({ + geometry: new ol.geom.Point([-10, -15]) + }); + feature.setStyle(new ol.style.Style({ + image: new ol.style.Circle({ + radius: 4, + fill: new ol.style.Fill({ + color: 'rgba(0, 0, 255, 0.3)' + }), + stroke: new ol.style.Stroke({ + color: '#256308', + width: 2 + }) + }) + })); + vectorSource.addFeature(feature); + + feature = new ol.Feature({ + geometry: new ol.geom.Point([4, -15]) + }); + feature.setStyle(new ol.style.Style({ + image: new ol.style.Circle({ + radius: 6, + fill: new ol.style.Fill({ + color: 'rgba(235, 45, 70, 0.6)' + }), + stroke: new ol.style.Stroke({ + color: '#256308', + width: 3 + }) + }) + })); + vectorSource.addFeature(feature); + } + + it('tests the canvas renderer', function(done) { + map = createMap('canvas'); + createFeatures(); + expectResemble(map, 'spec/ol/style/expected/circle-canvas.png', + 6.0, done); + }); + + it('tests the WebGL renderer', function(done) { + assertWebGL(); + map = createMap('webgl'); + createFeatures(); + expectResemble(map, 'spec/ol/style/expected/circle-webgl.png', + 7.0, done); + }); + }); +}); + +goog.require('goog.dispose'); +goog.require('ol.Feature'); +goog.require('ol.geom.Point'); +goog.require('ol.Map'); +goog.require('ol.View'); +goog.require('ol.layer.Vector'); +goog.require('ol.source.Vector'); +goog.require('ol.style.Circle'); +goog.require('ol.style.Fill'); +goog.require('ol.style.Style'); +goog.require('ol.style.Stroke'); diff --git a/test_rendering/spec/ol/style/expected/circle-canvas.png b/test_rendering/spec/ol/style/expected/circle-canvas.png new file mode 100644 index 0000000000..466b5281d1 Binary files /dev/null and b/test_rendering/spec/ol/style/expected/circle-canvas.png differ diff --git a/test_rendering/spec/ol/style/expected/circle-webgl.png b/test_rendering/spec/ol/style/expected/circle-webgl.png new file mode 100644 index 0000000000..89c37e992e Binary files /dev/null and b/test_rendering/spec/ol/style/expected/circle-webgl.png differ diff --git a/test_rendering/spec/ol/style/expected/icon-canvas.png b/test_rendering/spec/ol/style/expected/icon-canvas.png new file mode 100644 index 0000000000..cdc0dba6b1 Binary files /dev/null and b/test_rendering/spec/ol/style/expected/icon-canvas.png differ diff --git a/test_rendering/spec/ol/style/expected/icon-webgl.png b/test_rendering/spec/ol/style/expected/icon-webgl.png new file mode 100644 index 0000000000..c08cbfdd54 Binary files /dev/null and b/test_rendering/spec/ol/style/expected/icon-webgl.png differ diff --git a/test_rendering/spec/ol/style/expected/linestring-strokes-canvas.png b/test_rendering/spec/ol/style/expected/linestring-strokes-canvas.png new file mode 100644 index 0000000000..0e354d444a Binary files /dev/null and b/test_rendering/spec/ol/style/expected/linestring-strokes-canvas.png differ diff --git a/test_rendering/spec/ol/style/expected/polygon-fill-and-strokes-canvas.png b/test_rendering/spec/ol/style/expected/polygon-fill-and-strokes-canvas.png new file mode 100644 index 0000000000..b1b5d9d4d0 Binary files /dev/null and b/test_rendering/spec/ol/style/expected/polygon-fill-and-strokes-canvas.png differ diff --git a/test_rendering/spec/ol/style/expected/polygon-types-canvas.png b/test_rendering/spec/ol/style/expected/polygon-types-canvas.png new file mode 100644 index 0000000000..4abfccb058 Binary files /dev/null and b/test_rendering/spec/ol/style/expected/polygon-types-canvas.png differ diff --git a/test_rendering/spec/ol/style/expected/polygon-zindex-canvas.png b/test_rendering/spec/ol/style/expected/polygon-zindex-canvas.png new file mode 100644 index 0000000000..931df4a072 Binary files /dev/null and b/test_rendering/spec/ol/style/expected/polygon-zindex-canvas.png differ diff --git a/test_rendering/spec/ol/style/expected/regularshape-canvas.png b/test_rendering/spec/ol/style/expected/regularshape-canvas.png new file mode 100644 index 0000000000..75cb343754 Binary files /dev/null and b/test_rendering/spec/ol/style/expected/regularshape-canvas.png differ diff --git a/test_rendering/spec/ol/style/expected/regularshape-webgl.png b/test_rendering/spec/ol/style/expected/regularshape-webgl.png new file mode 100644 index 0000000000..ae51d61dc5 Binary files /dev/null and b/test_rendering/spec/ol/style/expected/regularshape-webgl.png differ diff --git a/test_rendering/spec/ol/style/icon.test.js b/test_rendering/spec/ol/style/icon.test.js new file mode 100644 index 0000000000..d89bbd8a5f --- /dev/null +++ b/test_rendering/spec/ol/style/icon.test.js @@ -0,0 +1,85 @@ +goog.provide('ol.test.rendering.style.Icon'); + +describe('ol.rendering.style.Icon', function() { + + var target, map, vectorSource; + + function createMap(renderer) { + target = createMapDiv(50, 50); + + vectorSource = new ol.source.Vector(); + vectorLayer = new ol.layer.Vector({ + source: vectorSource + }); + + map = new ol.Map({ + target: target, + renderer: renderer, + layers: [vectorLayer], + view: new ol.View({ + projection: 'EPSG:4326', + center: [0, 0], + resolution: 1 + }) + }); + return map; + } + + describe('#render', function() { + afterEach(function() { + disposeMap(map); + }); + + function createFeatures(callback) { + var feature; + feature = new ol.Feature({ + geometry: new ol.geom.Point([0, 0]) + }); + + var img = new Image(); + img.onload = function() { + feature.setStyle(new ol.style.Style({ + image: new ol.style.Icon(/** @type {olx.style.IconOptions} */ ({ + anchor: [0.5, 46], + anchorXUnits: 'fraction', + anchorYUnits: 'pixels', + opacity: 0.75, + scale: 0.5, + img: img, + imgSize: [32, 48] + })) + })); + vectorSource.addFeature(feature); + callback(); + }; + img.src = 'spec/ol/data/icon.png'; + } + + it('tests the canvas renderer', function(done) { + map = createMap('canvas'); + createFeatures(function() { + expectResemble(map, 'spec/ol/style/expected/icon-canvas.png', + IMAGE_TOLERANCE, done); + }); + }); + + it('tests the WebGL renderer', function(done) { + assertWebGL(); + map = createMap('webgl'); + createFeatures(function() { + expectResemble(map, 'spec/ol/style/expected/icon-webgl.png', + IMAGE_TOLERANCE, done); + }); + }); + }); +}); + +goog.require('goog.dispose'); +goog.require('ol.Feature'); +goog.require('ol.geom.Point'); +goog.require('ol.Map'); +goog.require('ol.View'); +goog.require('ol.layer.Vector'); +goog.require('ol.source.Vector'); +goog.require('ol.style.Icon'); +goog.require('ol.style.Style'); diff --git a/test_rendering/spec/ol/style/linestring.test.js b/test_rendering/spec/ol/style/linestring.test.js new file mode 100644 index 0000000000..bb8921f4a8 --- /dev/null +++ b/test_rendering/spec/ol/style/linestring.test.js @@ -0,0 +1,102 @@ +goog.provide('ol.test.rendering.style.LineString'); + +describe('ol.rendering.style.LineString', function() { + + var target, map, vectorSource; + + function createMap(renderer) { + target = createMapDiv(50, 50); + + vectorSource = new ol.source.Vector(); + vectorLayer = new ol.layer.Vector({ + source: vectorSource + }); + + map = new ol.Map({ + target: target, + renderer: renderer, + layers: [vectorLayer], + view: new ol.View({ + projection: 'EPSG:4326', + center: [0, 0], + resolution: 1 + }) + }); + return map; + } + + describe('different strokes', function() { + afterEach(function() { + disposeMap(map); + }); + + function createFeatures() { + var feature; + + feature = new ol.Feature({ + geometry: new ol.geom.LineString( + [[-20, 20], [15, 20]] + ) + }); + feature.setStyle(new ol.style.Style({ + stroke: new ol.style.Stroke({color: '#DE213A', width: 3}) + })); + vectorSource.addFeature(feature); + + feature = new ol.Feature({ + geometry: new ol.geom.LineString( + [[-20, 15], [15, 15]] + ) + }); + feature.setStyle(new ol.style.Style({ + stroke: new ol.style.Stroke({color: '#9696EB', width: 1}) + })); + vectorSource.addFeature(feature); + + feature = new ol.Feature({ + geometry: new ol.geom.LineString( + [[-20, 10], [15, 10]] + ) + }); + feature.setStyle([new ol.style.Style({ + stroke: new ol.style.Stroke({color: '#F2F211', width: 5}) + }), new ol.style.Style({ + stroke: new ol.style.Stroke({color: '#292921', width: 1}) + })]); + vectorSource.addFeature(feature); + + feature = new ol.Feature({ + geometry: new ol.geom.LineString( + [[-20, -20], [-2, 0], [15, -20]] + ) + }); + feature.setStyle(new ol.style.Style({ + stroke: new ol.style.Stroke({ + color: '#000000', + width: 2, + lineCap: 'square', + lineJoin: 'round' + }) + })); + vectorSource.addFeature(feature); + } + + it('tests the canvas renderer', function(done) { + map = createMap('canvas'); + createFeatures(); + expectResemble( + map, 'spec/ol/style/expected/linestring-strokes-canvas.png', + 3.0, done); + }); + }); +}); + +goog.require('goog.dispose'); +goog.require('ol.Feature'); +goog.require('ol.geom.LineString'); +goog.require('ol.Map'); +goog.require('ol.View'); +goog.require('ol.layer.Vector'); +goog.require('ol.source.Vector'); +goog.require('ol.style.Style'); +goog.require('ol.style.Stroke'); diff --git a/test_rendering/spec/ol/style/polygon.test.js b/test_rendering/spec/ol/style/polygon.test.js new file mode 100644 index 0000000000..29f7a64d2c --- /dev/null +++ b/test_rendering/spec/ol/style/polygon.test.js @@ -0,0 +1,200 @@ +goog.provide('ol.test.rendering.style.Polygon'); + +describe('ol.rendering.style.Polygon', function() { + + var target, map, vectorSource; + + function createMap(renderer) { + target = createMapDiv(50, 50); + + vectorSource = new ol.source.Vector(); + vectorLayer = new ol.layer.Vector({ + source: vectorSource + }); + + map = new ol.Map({ + target: target, + renderer: renderer, + layers: [vectorLayer], + view: new ol.View({ + projection: 'EPSG:4326', + center: [0, 0], + resolution: 1 + }) + }); + return map; + } + + describe('different types', function() { + afterEach(function() { + disposeMap(map); + }); + + function createFeatures() { + var fill = new ol.style.Fill({color: 'red'}); + + var feature; + // rectangle + feature = new ol.Feature({ + geometry: new ol.geom.Polygon([ + [[-20, 10], [-20, 20], [-5, 20], [-5, 10], [-20, 10]] + ]) + }); + feature.setStyle(new ol.style.Style({ + fill: fill + })); + vectorSource.addFeature(feature); + + // rectangle with 1 hole + feature = new ol.Feature({ + geometry: new ol.geom.Polygon([ + [[0, 10], [0, 20], [15, 20], [15, 10], [0, 10]], + [[5, 13], [10, 13], [10, 17], [5, 17], [5, 13]] + + ]) + }); + feature.setStyle(new ol.style.Style({ + fill: fill + })); + vectorSource.addFeature(feature); + + // rectangle with 2 holes + feature = new ol.Feature({ + geometry: new ol.geom.Polygon([ + [[-20, -20], [-20, 5], [15, 5], [15, -20], [-20, -20]], + [[-18, -18], [-12, -18], [-12, -12], [-18, -12], [-18, -18]], + [[5, -18], [12, -18], [12, -12], [5, -12], [5, -18]] + + ]) + }); + feature.setStyle(new ol.style.Style({ + fill: fill + })); + vectorSource.addFeature(feature); + } + + it('tests the canvas renderer', function(done) { + map = createMap('canvas'); + createFeatures(); + expectResemble(map, 'spec/ol/style/expected/polygon-types-canvas.png', + IMAGE_TOLERANCE, done); + }); + }); + + describe('z-index', function() { + afterEach(function() { + disposeMap(map); + }); + + function createFeatures() { + var feature; + // rectangle with z-index 2 + feature = new ol.Feature({ + geometry: new ol.geom.Polygon([ + [[-20, 10], [-20, 20], [-0, 20], [-0, 10], [-20, 10]] + ]) + }); + feature.setStyle(new ol.style.Style({ + fill: new ol.style.Fill({color: '#E31E10'}), + zIndex: 2 + })); + vectorSource.addFeature(feature); + + // rectangle with z-index 3 + feature = new ol.Feature({ + geometry: new ol.geom.Polygon([ + [[-15, 5], [-15, 15], [5, 15], [5, 5], [-15, 5]] + ]) + }); + feature.setStyle(new ol.style.Style({ + fill: new ol.style.Fill({color: '#1A5E42'}), + zIndex: 3 + })); + vectorSource.addFeature(feature); + + // rectangle with z-index 1 + feature = new ol.Feature({ + geometry: new ol.geom.Polygon([ + [[-10, 0], [-10, 10], [10, 10], [10, 0], [-10, 0]] + ]) + }); + feature.setStyle(new ol.style.Style({ + fill: new ol.style.Fill({color: '#DEDE21'}), + zIndex: 1 + })); + vectorSource.addFeature(feature); + + } + + it('tests the canvas renderer', function(done) { + map = createMap('canvas'); + createFeatures(); + expectResemble(map, 'spec/ol/style/expected/polygon-zindex-canvas.png', + IMAGE_TOLERANCE, done); + }); + }); + + describe('different fills and strokes', function() { + afterEach(function() { + disposeMap(map); + }); + + function createFeatures() { + var feature; + // rectangle + feature = new ol.Feature({ + geometry: new ol.geom.Polygon([ + [[-20, 10], [-20, 20], [-5, 20], [-5, 10], [-20, 10]] + ]) + }); + feature.setStyle(new ol.style.Style({ + fill: new ol.style.Fill({color: '#9696EB'}), + stroke: new ol.style.Stroke({color: '#9696EB', width: 1}) + })); + vectorSource.addFeature(feature); + + // rectangle with 1 hole + feature = new ol.Feature({ + geometry: new ol.geom.Polygon([ + [[0, 10], [0, 20], [15, 20], [15, 10], [0, 10]] + ]) + }); + feature.setStyle(new ol.style.Style({ + fill: new ol.style.Fill({color: 'rgba(255, 0, 0, 0.1)'}), + stroke: new ol.style.Stroke({color: '#DE213A', width: 3}) + })); + vectorSource.addFeature(feature); + + // rectangle with 2 holes + feature = new ol.Feature({ + geometry: new ol.geom.Polygon([ + [[-20, -20], [-20, 5], [15, 5], [15, -20], [-20, -20]] + ]) + }); + feature.setStyle(new ol.style.Style({ + fill: new ol.style.Fill({color: 'rgba(18, 204, 105, 0.3)'}), + stroke: new ol.style.Stroke({color: '#032E17', width: 2}) + })); + vectorSource.addFeature(feature); + } + + it('tests the canvas renderer', function(done) { + map = createMap('canvas'); + createFeatures(); + expectResemble( + map, 'spec/ol/style/expected/polygon-fill-and-strokes-canvas.png', + IMAGE_TOLERANCE, done); + }); + }); +}); + +goog.require('goog.dispose'); +goog.require('ol.Feature'); +goog.require('ol.geom.Polygon'); +goog.require('ol.Map'); +goog.require('ol.View'); +goog.require('ol.layer.Vector'); +goog.require('ol.source.Vector'); +goog.require('ol.style.Fill'); +goog.require('ol.style.Style'); +goog.require('ol.style.Stroke'); diff --git a/test_rendering/spec/ol/style/regularshape.test.js b/test_rendering/spec/ol/style/regularshape.test.js new file mode 100644 index 0000000000..2eaca4b33e --- /dev/null +++ b/test_rendering/spec/ol/style/regularshape.test.js @@ -0,0 +1,129 @@ +goog.provide('ol.test.rendering.style.RegularShape'); + +describe('ol.rendering.style.RegularShape', function() { + + var target, map, vectorSource; + + function createMap(renderer) { + target = createMapDiv(50, 50); + + vectorSource = new ol.source.Vector(); + vectorLayer = new ol.layer.Vector({ + source: vectorSource + }); + + map = new ol.Map({ + target: target, + renderer: renderer, + layers: [vectorLayer], + view: new ol.View({ + projection: 'EPSG:4326', + center: [0, 0], + resolution: 1 + }) + }); + return map; + } + + describe('#render', function() { + afterEach(function() { + disposeMap(map); + }); + + function createFeatures() { + var stroke = new ol.style.Stroke({color: 'black', width: 2}); + var fill = new ol.style.Fill({color: 'red'}); + + var feature; + feature = new ol.Feature({ + geometry: new ol.geom.Point([-15, 15]) + }); + // square + feature.setStyle(new ol.style.Style({ + image: new ol.style.RegularShape({ + fill: fill, + stroke: stroke, + points: 4, + radius: 10, + angle: Math.PI / 4 + }) + })); + vectorSource.addFeature(feature); + + feature = new ol.Feature({ + geometry: new ol.geom.Point([8, 15]) + }); + // triangle + feature.setStyle(new ol.style.Style({ + image: new ol.style.RegularShape({ + fill: fill, + stroke: stroke, + points: 3, + radius: 10, + rotation: Math.PI / 4, + angle: 0 + }) + })); + vectorSource.addFeature(feature); + + feature = new ol.Feature({ + geometry: new ol.geom.Point([-10, -8]) + }); + // star + feature.setStyle(new ol.style.Style({ + image: new ol.style.RegularShape({ + fill: fill, + stroke: stroke, + points: 5, + radius: 10, + radius2: 4, + angle: 0 + }) + })); + vectorSource.addFeature(feature); + + feature = new ol.Feature({ + geometry: new ol.geom.Point([12, -8]) + }); + // cross + feature.setStyle(new ol.style.Style({ + image: new ol.style.RegularShape({ + fill: fill, + stroke: stroke, + points: 4, + radius: 10, + radius2: 0, + angle: 0 + }) + })); + vectorSource.addFeature(feature); + } + + it('tests the canvas renderer', function(done) { + map = createMap('canvas'); + createFeatures(); + expectResemble(map, 'spec/ol/style/expected/regularshape-canvas.png', + 6.0, done); + }); + + it('tests the WebGL renderer', function(done) { + assertWebGL(); + map = createMap('webgl'); + createFeatures(); + expectResemble(map, 'spec/ol/style/expected/regularshape-webgl.png', + IMAGE_TOLERANCE, done); + }); + }); +}); + +goog.require('goog.dispose'); +goog.require('ol.Feature'); +goog.require('ol.geom.Point'); +goog.require('ol.Map'); +goog.require('ol.View'); +goog.require('ol.layer.Vector'); +goog.require('ol.source.Vector'); +goog.require('ol.style.Fill'); +goog.require('ol.style.RegularShape'); +goog.require('ol.style.Style'); +goog.require('ol.style.Stroke'); diff --git a/test_rendering/test.js b/test_rendering/test.js new file mode 100644 index 0000000000..8343ab26c9 --- /dev/null +++ b/test_rendering/test.js @@ -0,0 +1,30 @@ +var url = phantom.args[0]; +var page = require("webpage").create(); + +var v = slimer.geckoVersion; +console.log('Gecko: ' + v.major + '.' + v.minor + '.' + v.patch); + +page.open(url). + then(function(status){ + if (status == "success") { + page.onCallback = function(failedTests) { + if (failedTests.length > 0) { + for (var i = 0; i < failedTests.length; i++) { + var test = failedTests[i]; + console.log(test.title); + console.error(test.errorStack); + console.log(''); + } + console.error(failedTests.length + ' test(s) failed.'); + } else { + console.log('All tests passed.'); + } + page.close(); + phantom.exit(failedTests.length === 0 ? 0 : 1); + } + } else { + console.error("The tests could not be started. Is the server running?"); + page.close(); + phantom.exit(1); + } + });