All Downloads are FREE. Search and download functionalities are using the official Maven repository.

uk.co.thebadgerset.junit.extensions.AsymptoticTestCase Maven / Gradle / Ivy

Go to download

JUnit Toolkit enhances JUnit with performance testing, asymptotic behaviour analysis, and concurrency testing.

The newest version!
/*
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
 */
package uk.co.thebadgerset.junit.extensions;

import junit.framework.TestCase;

import org.apache.log4j.Logger;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**
 * AsymptoticTestCase is an extension of TestCase for writing unit tests to analyze asymptotic time and space behaviour.
 *
 * 

ParameterizedTestCases allow tests to be defined which have test methods that take a single int argument. Normal * JUnit test methods do not take any arguments. This int argument can be interpreted in any way by the test but it is * intended to denote the 'size' of the test to be run. For example, when testing the performance of a data structure * for different numbers of data elements held in the data structure the int parameter should be interpreted as the * number of elements. Test timings for different numbers of elements can then be captured and the asymptotic behaviour * of the data structure with respect to time analyzed. Any non-parameterized tests defined in extensions of this class * will also be run. * *

TestCases derived from this class may also define tear down methods to clean up their memory usage. This is * intended to be used in conjunction with memory listeners that report the amount of memory a test uses. The idea is * to write a test that allocates memory in the main test method in such a way that it leaves that memory still * allocated at the end of the test. The amount of memory used can then be measured before calling the tear down method * to clean it up. In the data structure example above, a test will allocate as many elements as are requested by the * int parameter and deallocate them in the tear down method. In this way memory readings for different numbers of * elements can be captured and the asymptotic behaviour of the data structure with respect to space analyzed. * *

*
CRC Card
Responsibilities Collaborations *
Store the current int parameter value. {@link TKTestResult} and see {@link AsymptoticTestDecorator} too. *
Invoke parameterized test methods. *
* * @todo If possible try to move the code that invokes the test and setup/teardown methods into {@link TKTestResult} or * {@link AsymptoticTestDecorator} rather than this class. This would mean that tests don't have to extend this * class to do time and space performance analysis, these methods could be added to any JUnit TestCase class * instead. This would be an improvement because existing unit tests wouldn't have to extend a different class to * work with this extension, and also tests that extend other junit extension classes could have parameterized * and tear down methods too. * * @author Rupert Smith */ public class AsymptoticTestCase extends TestCase implements InstrumentedTest { /** Used for logging. */ private static final Logger log = Logger.getLogger(AsymptoticTestCase.class); /** The name of the test case. */ private String testCaseName; /** Thread local for holding measurements on a per thread basis. */ ThreadLocal threadLocalMeasurement = new ThreadLocal() { /** * Sets up a default set test measurements (zeroed, apart from the size param which defaults to 1). * * @return A set of default test measurements. */ protected synchronized TestMeasurements initialValue() { return new TestMeasurements(); } }; /** * Constructs a test case with the given name. * * @param name The name of the test. */ public AsymptoticTestCase(String name) { super(name); log.debug("public AsymptoticTestCase(String " + name + "): called"); testCaseName = name; } /** * Gets the current value of the integer parameter to be passed to the parameterized test. * * @return The current value of the integer parameter. */ public int getN() { log.debug("public int getN(): called"); int n = threadLocalMeasurement.get().n; log.debug("return: " + n); return n; } /** * Sets the current value of the integer parameter to be passed to the parameterized test. * * @param n The new current value of the integer parameter. */ public void setN(int n) { log.debug("public void setN(int " + n + "): called"); threadLocalMeasurement.get().n = n; } /** * Reports how long the test took to run. * * @return The time in milliseconds that the test took to run. */ public long getTestTime() { log.debug("public long getTestTime(): called"); long startTime = threadLocalMeasurement.get().startTime; long endTime = threadLocalMeasurement.get().endTime; long testTime = endTime - startTime; log.debug("return: " + testTime); return testTime; } /** * Reports the memory usage at the start of the test. * * @return The memory usage at the start of the test. */ public long getTestStartMemory() { // log.debug("public long getTestStartMemory(): called"); long startMem = threadLocalMeasurement.get().startMem; // log.debug("return: " + startMem); return startMem; } /** * Reports the memory usage at the end of the test. * * @return The memory usage at the end of the test. */ public long getTestEndMemory() { // log.debug("public long getTestEndMemory(): called"); long endMem = threadLocalMeasurement.get().endMem; // log.debug("return: " + endMem); return endMem; } /** * Resets the instrumentation values to zero, and nulls any references to held measurements so that the memory * can be reclaimed. */ public void reset() { log.debug("public void reset(): called"); threadLocalMeasurement.remove(); } /** * Runs the test method for this test case. * * @throws Throwable Any Throwables from the test methods invoked are allowed to fall through. */ protected void runTest() throws Throwable { log.debug("protected void runTest(): called"); // Check that a test name has been set. This is used to define which method to run. assertNotNull(testCaseName); log.debug("testCaseName = " + testCaseName); // Try to get the method with matching name. Method runMethod = null; boolean isParameterized = false; // Check if a parameterized test method is available. try { // Use getMethod to get all public inherited methods. getDeclaredMethods returns all // methods of this class but excludes the inherited ones. runMethod = getClass().getMethod(testCaseName, int.class); isParameterized = true; } catch (NoSuchMethodException e) { // log.debug("Parameterized method \"" + testCaseName + "\" not found."); } // If no parameterized method is available, try and get the unparmaeterized method. if (runMethod == null) { try { runMethod = getClass().getMethod(testCaseName); isParameterized = false; } catch (NoSuchMethodException e) { fail("Method \"" + testCaseName + "\" not found."); } } // Check that the method is publicly accessable. if (!Modifier.isPublic(runMethod.getModifiers())) { fail("Method \"" + testCaseName + "\" should be public."); } // Try to execute the method, passing it the current int parameter value. Allow any invocation exceptions or // resulting exceptions from the method to fall through. try { Integer paramN = getN(); log.debug("paramN = " + paramN); // Calculate parameters for parameterized tests so new does not get called during memory measurement. Object[] params = new Object[] { paramN }; // Take the test start memory and start time. threadLocalMeasurement.get().startMem = 0; // SizeOf.getUsedMemory(); threadLocalMeasurement.get().startTime = System.nanoTime(); if (isParameterized) { runMethod.invoke(this, params); } else { runMethod.invoke(this); } } catch (InvocationTargetException e) { e.fillInStackTrace(); throw e.getTargetException(); } catch (IllegalAccessException e) { e.fillInStackTrace(); throw e; } finally { // Take the test end memory and end time and calculate how long it took to run. long endTime = System.nanoTime(); threadLocalMeasurement.get().endTime = endTime; log.debug("startTime = " + threadLocalMeasurement.get().startTime + ", endTime = " + endTime + ", testTime = " + getTestTime()); threadLocalMeasurement.get().endMem = 0; // SizeOf.getUsedMemory(); } } /** * The test parameters, encapsulated as a unit for attaching on a per thread basis. */ private static class TestMeasurements { /** Holds the current value of the integer parameter to run tests on. */ public int n = 1; /** Holds the test start memory. */ public long startTime = 0; /** Holds the test end memory. */ public long endTime = 0; /** Holds the test start memory. */ public long startMem = 0; /** Holds the test end memory. */ public long endMem = 0; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy