/* * TestMultiThreadingAndTracing.java of project jchart2d - a Junit * Test that tests many concurrent Threads with their traces writing * to the same chart. * Copyright (C) Achim Westermann, created on 10.05.2005, 22:52:54 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * If you modify or optimize the code in a useful way please let me know. * Achim.Westermann@gmx.de * */ package info.monitorenter.gui.chart; import info.monitorenter.gui.chart.traces.Trace2DLtd; import java.util.LinkedList; import java.util.List; import java.util.WeakHashMap; import junit.framework.Assert; import junit.framework.Test; import junit.framework.TestSuite; /** * Multiple <code>Producers</code> concurrently add points to an amount of * randomly shared <code>ITrace2D</code> instances. * <p> * One <code>Consumer</code> invokes paint on the <code>Chart2D</code> thus * allowing to drop pending changes stored in it. * <p> * * @author <a href="mailto:Achim.Westermann@gmx.de">Achim Westermann </a> * */ public class TestMultiThreadingAndTracing extends TestMultithreading { /** * Runnable that will take a random break between 0 and * <code>sleepRange</code> milliseconds and then consume added <code> * {@link TracePoint2D}</code> (by <code>{@link Producer}</code>) by invoking * <code>{@link Chart2D#paint(java.awt.Graphics)}</code>. * <p> * * @author <a href="mailto:Achim.Westermann@gmx.de">Achim Westermann</a> * * @version $Revision: 1.11 $ */ class Consumer extends TestMultithreading.Consumer { /** The maximum of milliseconds between two add operations. */ private long m_sleepRange; /** Flag to allow stopping this Thread at a defined consitant code position. */ private boolean m_stop = false; /** * Creates an instance that will take a random break between 0 and * <code>sleepRange</code> milliseconds between two add operations. * <p> * * @param sleepRange * the maximum of milliseconds between two add operations. */ Consumer(final long sleepRange) { super(sleepRange); } /** * @see info.monitorenter.gui.chart.TestMultithreading.Consumer#run() */ @Override public void run() { MockGraphics2D mockGraphics = new MockGraphics2D(); while (!(this.m_stop || TestMultiThreadingAndTracing.this.isAllProducersFinished())) { try { Thread.sleep((long) (Math.random() * this.m_sleepRange)); } catch (InterruptedException e) { e.printStackTrace(); this.m_stop = true; } System.out.println('[' + this.getClass().getName() + "] painting..."); TestMultiThreadingAndTracing.this.m_chart.paint(mockGraphics); } } } /** * Producer implementation that sleeps a random range of milliseconds within * <code>{@link #PRODUCER_SLEEPRANGE}</code> and then adds a newly created * <code>{@link TracePoint2D}</code> to a random picked <code>{@link ITrace2D} * </code> for <code>{@link #PRODUCER_ADD_POINT_AMOUNT}</code> times. * <p> */ class Producer extends TestMultithreading.Producer { /** The maximum of milliseconds between two add operations. */ private long m_sleepRange; /** Flag to stop the producer. */ private boolean m_stop = false; /** The amount to add. */ private long m_toAdd; /** * <p> * Constructs a producer that will add <code>toAdd</code> points with random * breaks of milliseconds between <code>maxSleep</code> and zero. * </p> * * @param toAdd * the amount of points to add * @param sleepRange * the maxium time in milliseconds the Thread will sleep between * two points added */ Producer(final long toAdd, final long sleepRange) { super(toAdd, sleepRange); } /** * @see info.monitorenter.gui.chart.TestMultithreading.Producer#run() */ @Override public void run() { ITracePoint2D point; ITrace2D tmpTrace; while (this.m_toAdd > 0 && !this.m_stop) { try { Thread.sleep((long) (Math.random() * this.m_sleepRange)); } catch (InterruptedException e) { e.printStackTrace(); this.m_stop = true; } tmpTrace = TestMultiThreadingAndTracing.this.pickRandomTrace(); if (this.m_toAdd % 10 == 0) { System.out.println('[' + this.getName() + "] adding point to " + tmpTrace.getName() + "... " + this.m_toAdd + " to go..."); } point = new TracePoint2D(this.m_toAdd, this.m_toAdd); TestMultiThreadingAndTracing.this.m_weakMap.put(point, point.toString()); tmpTrace.addPoint(point); this.m_toAdd--; } } } /** <code>{@link ITrace2D}</code> class to use instances of for the test. */ protected static final Class< ? > TRACE_CLASS = Trace2DLtd.class; // configuration /** Amount of traces ( same as threads ) to concurrently paint on the chart. */ protected static final int TRACES_AMOUNT = 10; /** * Test suite for this test class. * <p> * * @return the test suite */ public static Test suite() { TestSuite suite = new TestSuite(); suite.setName(TestMultiThreadingAndTracing.class.getName()); suite.addTest(new TestMultiThreadingAndTracing("testTrace2DLtd")); return suite; } /** The traces of the concurrent threads to test. */ protected List<ITrace2D> m_traces; // //////////////////////////// // Helper methods // //////////////////////////// /** * Creates a test case with the given name. * <p> * * @param testName * the name of the test case. */ public TestMultiThreadingAndTracing(final String testName) { super(testName); } /** * Picks a random trace of the internal list of traces. * <p> * * @return a random trace of the internal list of traces. */ ITrace2D pickRandomTrace() { int index = (int) Math.round(Math.random() * TestMultiThreadingAndTracing.TRACES_AMOUNT); return this.m_traces.get(index); } // //////////////////////////// // Worker classes // //////////////////////////// /** * Writes a report to <code>{@link System#out}</code>. * <p> * * @see info.monitorenter.gui.chart.TestMultithreading#report() */ @Override void report() { long keys = this.m_weakMap.size(); System.out.println("Points remaining in the weakMap: " + keys); System.out.println("System.runFinalization()... "); System.runFinalization(); System.out.println("System.gc()... "); System.gc(); keys = this.m_weakMap.size(); System.out.println("Points remaining in the weakMap: " + keys); keys = 0; for (ITracePoint2D point : this.m_weakMap.keySet()) { keys++; System.out.println("Point " + point.toString() + " was not dropped."); } System.out.println("Points remaining in the weakMap: " + keys); Assert.assertFalse("There are " + keys + " TracePoint2D instances not deleted from the WeakHashMap.", keys > this.m_trace .getMaxSize()); } /** * Creates producers, consumers, traces and other members for the test run. * <p> * * @see info.monitorenter.gui.chart.TestMultithreading#setUp() * * @throws Exception * if something goes wrong. */ @Override public void setUp() throws Exception { this.m_trace = (ITrace2D) TestMultiThreadingAndTracing.TRACE_CLASS.newInstance(); this.m_chart = new Chart2D(); this.m_weakMap = new WeakHashMap<ITracePoint2D, String>(); this.m_producers = new LinkedList<TestMultithreading.Producer>(); for (int add = PRODUCER_AMOUNT; add > 0; add--) { this.m_producers.add(new Producer(PRODUCER_ADD_POINT_AMOUNT, PRODUCER_SLEEPRANGE)); } this.m_traces = new LinkedList<ITrace2D>(); ITrace2D tmpTrace; for (int add = TestMultiThreadingAndTracing.TRACES_AMOUNT; add > 0; add--) { tmpTrace = (ITrace2D) TestMultiThreadingAndTracing.TRACE_CLASS.newInstance(); tmpTrace.setName("Trace-" + add); this.m_traces.add(tmpTrace); } Assert.assertTrue(this.m_chart.getTraces().size() == 0); // add all traces for (ITrace2D trace : this.m_traces) { this.m_chart.addTrace(trace); } } /** * Cleans up / frees handles. * <p> * * @see info.monitorenter.gui.chart.TestMultithreading#tearDown() */ @Override protected void tearDown() throws Exception { super.tearDown(); this.m_traces = null; } }