From 9daeea823b85c2a447afca73c4d35efe2d969cf1 Mon Sep 17 00:00:00 2001 From: luccioman Date: Sat, 10 Nov 2018 22:00:49 +0100 Subject: Fixed concurrency issue on cache used for circles rendering Without synchronization lock, concurrent rendering of images including circles could lead to glitches as reported in issue #248 --- .../net/yacy/visualization/CircleToolTest.java | 119 +++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 test/java/net/yacy/visualization/CircleToolTest.java (limited to 'test/java') diff --git a/test/java/net/yacy/visualization/CircleToolTest.java b/test/java/net/yacy/visualization/CircleToolTest.java new file mode 100644 index 000000000..14fda8c23 --- /dev/null +++ b/test/java/net/yacy/visualization/CircleToolTest.java @@ -0,0 +1,119 @@ +// CircleToolTest.java +// Copyright 2018 by luccioman; https://github.com/luccioman +// +// This is a part of YaCy, a peer-to-peer based web search engine +// +// LICENSE +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +package net.yacy.visualization; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import org.bouncycastle.util.Arrays; +import org.junit.Assert; +import org.junit.Test; + +import net.yacy.peers.graphics.EncodedImage; + +/** + * Unit tests for the {@link CircleTool} class. + */ +public class CircleToolTest { + + /** + * Check circle function consistency when run on multiple concurrent threads. + * + * @throws Exception when an unexpected error occurred + */ + @Test + public void testCircleConcurrentConsistency() throws Exception { + final int concurrency = 20; + final int side = 600; + final String ext = "png"; + + final Callable drawTask = () -> { + final RasterPlotter raster = new RasterPlotter(side, side, RasterPlotter.DrawMode.MODE_SUB, "FFFFFF"); + for (int radius = side / 4; radius < side / 2; radius++) { + raster.setColor(RasterPlotter.GREEN); + CircleTool.circle(raster, side / 2, side / 2, radius, 100); + raster.setColor(RasterPlotter.RED); + CircleTool.circle(raster, side / 2, side / 2, radius, 0, 45); + } + + final EncodedImage image = new EncodedImage(raster, ext, true); + return image.getImage().getBytes(); + }; + + /* Generate a reference image without concurrency */ + CircleTool.clearcache(); + final byte[] refImageBytes = drawTask.call(); + + /* Write the reference image to the file system to enable manual visual check */ + final Path outputPath = Paths.get(System.getProperty("java.io.tmpdir", ""), "CircleToolTest." + ext); + try { + Files.write(outputPath, refImageBytes); + System.out.println("Wrote CircleTool.circle() test image to file " + outputPath.toAbsolutePath()); + } catch (final IOException e) { + /* + * Even if output file writing failed we do not make the test fail as this is + * not the purpose of the test + */ + e.printStackTrace(); + } + + /* + * Generate the same image multiple times in concurrent threads without initial + * cache + */ + CircleTool.clearcache(); + final ExecutorService executor = Executors.newFixedThreadPool(concurrency); + final ArrayList> futures = new ArrayList<>(); + for (int i = 0; i < concurrency; i++) { + futures.add(executor.submit(drawTask)); + } + try { + for (final Future future : futures) { + /* Check that all concurrently generated images are equal to the reference */ + final byte[] imageBytes = future.get(); + if (!Arrays.areEqual(refImageBytes, imageBytes)) { + /* Write the image in error to file system to enable manual visual check */ + final Path errOutputPath = Paths.get(System.getProperty("java.io.tmpdir", ""), + "CircleToolTestError." + ext); + try { + Files.write(errOutputPath, imageBytes); + System.out.println( + "Wrote CircleTool.circle() error image to file " + errOutputPath.toAbsolutePath()); + } catch (final IOException e) { + e.printStackTrace(); + } + Assert.fail(); + } + } + } finally { + executor.shutdown(); + } + } + +} -- cgit v1.2.3