Java tutorial
/* * Copyright (C) 2011 Saarland University * * This file is part of Javalanche. * * Javalanche is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Javalanche 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 Public License for more details. * * You should have received a copy of the GNU Lesser Public License * along with Javalanche. If not, see <http://www.gnu.org/licenses/>. */ package de.unisb.cs.st.javalanche.mutation.runtime.testDriver.junit; import java.io.File; import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.time.StopWatch; import org.apache.log4j.Logger; import org.junit.internal.builders.AllDefaultPossibilitiesBuilder; import org.junit.runner.Description; import org.junit.runner.RunWith; import org.junit.runner.Runner; import org.junit.runner.manipulation.Filter; import org.junit.runner.manipulation.Filterable; import org.junit.runner.manipulation.NoTestsRemainException; import org.junit.runner.notification.Failure; import org.junit.runner.notification.RunListener; import org.junit.runner.notification.RunNotifier; import org.junit.runners.BlockJUnit4ClassRunner; import org.junit.runners.model.InitializationError; import de.unisb.cs.st.javalanche.mutation.properties.ConfigurationLocator; import de.unisb.cs.st.javalanche.mutation.runtime.testDriver.MutationTestDriver; import de.unisb.cs.st.javalanche.mutation.runtime.testDriver.MutationTestRunnable; import de.unisb.cs.st.javalanche.mutation.runtime.testDriver.SingleTestResult; import de.unisb.cs.st.javalanche.mutation.runtime.testDriver.SingleTestResult.TestOutcome; /** * Allows to use Junit4 Tests for Mutation Testing. * * @author David Schuler * */ public class Junit4MutationTestDriver extends MutationTestDriver { private static Logger logger = Logger.getLogger(Junit4MutationTestDriver.class); private Map<String, Description> allTests = new HashMap<String, Description>(); private Runner masterRunner; private static Map<Description, Runner> runners = new HashMap<Description, Runner>(); public Junit4MutationTestDriver() { masterRunner = null; Throwable t = null; try { masterRunner = Junit4Util.getRunner(); } catch (ClassNotFoundException e) { t = e; } catch (InitializationError e) { t = e; } finally { if (t != null) { String message = "Could not initialize tests suite for:" + ConfigurationLocator.getJavalancheConfiguration().getTestNames(); logger.warn(message, t); throw new RuntimeException(message, t); } } allTests = getTests(masterRunner); removeExludedTests(); } private void removeExludedTests() { String excludes = ConfigurationLocator.getJavalancheConfiguration().getExcludedTests(); if (excludes != null) { List<String> excludeList = new ArrayList<String>(); if (excludes.startsWith("file://")) { String fileName = excludes.substring(7); try { excludeList = FileUtils.readLines(new File(fileName)); } catch (IOException e) { e.printStackTrace(); } } else { String[] split = excludes.split(":"); excludeList = Arrays.asList(split); } for (String string : excludeList) { if (allTests.containsKey(string)) { allTests.remove(string); } } } } public static Map<String, Description> getTests(Runner r) { Map<String, Description> testMap = new HashMap<String, Description>(); List<Description> descs = new ArrayList<Description>(); Description description = r.getDescription(); logger.debug(description); descs.add(description); while (descs.size() > 0) { Description d = descs.remove(0); ArrayList<Description> children = d.getChildren(); if (children != null && children.size() > 0) { descs.addAll(children); } else { String testName = getTestName(d); String insertTestName = testName; int count = 0; while (testMap.containsKey(insertTestName)) { count++; insertTestName = testName + "-instance-" + count; } logger.debug("Got test case: " + insertTestName + " Desc: " + d); testMap.put(insertTestName, d); } } return testMap; } private static String getTestName(Description d) { return d.getClassName() + "." + d.getMethodName(); } @Override protected List<String> getAllTests() { return new ArrayList<String>(allTests.keySet()); } private static Runner getRunner(Description desc, boolean useSuite) { String className = getClassName(desc); Runner tcr = null; try { Class<?> clazz; clazz = Class.forName(className); logger.info("Creating Runner for " + className); Class<? extends Runner> runWithRunner = getRunWithRunner(clazz, useSuite); Constructor<? extends Runner> constructor = runWithRunner.getConstructor(Class.class); tcr = constructor.newInstance(clazz); // logger.debug("Runner Type " + tcr.getClass()); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } catch (SecurityException e) { throw new RuntimeException(e); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } catch (IllegalArgumentException e) { throw new RuntimeException(e); } catch (InstantiationException e) { throw new RuntimeException(e); } catch (IllegalAccessException e) { throw new RuntimeException(e); } catch (InvocationTargetException e) { logger.warn("Invocation Exception ", e); throw new RuntimeException(e); } return tcr; } private static Class<? extends Runner> getRunWithRunner(Class<?> clazz, boolean useSuite) { RunWith runWithAnnotation = clazz.getAnnotation(RunWith.class); if (runWithAnnotation == null) { AllDefaultPossibilitiesBuilder builder = new AllDefaultPossibilitiesBuilder(useSuite); try { return builder.runnerForClass(clazz).getClass(); } catch (Throwable e) { throw new RuntimeException(e); } // return BlockJUnit4ClassRunner.class; } Class<? extends Runner> runner = runWithAnnotation.value(); if (!runnerImplementsFilterable(runner)) return BlockJUnit4ClassRunner.class; return runner; } private static boolean runnerImplementsFilterable(Class<? extends Runner> runner) { for (Class<?> interfaze : runner.getInterfaces()) { if (Filterable.class.equals(interfaze)) return true; } return false; } private static String getClassName(Description desc) { String s = desc.toString(); int start = s.indexOf('('); int end = s.lastIndexOf(')'); return s.substring(start + 1, end); } private static void runTest(final Description desc, RunListener runListener, Runner masterRunner) { try { // StopWatch stp = new StopWatch(); // stp.start(); Runner r = getRunner(desc); // long time1 = stp.getTime(); // logger.info("Time to get runner: " + time1); RunNotifier notifier = new RunNotifier(); notifier.addListener(runListener); r.run(notifier); } catch (NoTestsRemainException e) { logger.warn("No test remain for test " + desc, e); throw new RuntimeException(e); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } catch (InitializationError e) { throw new RuntimeException(e); } } public static Runner getRunner(final Description desc) throws ClassNotFoundException, InitializationError, NoTestsRemainException { if (runners.containsKey(desc)) { Runner runner = runners.get(desc); if (runner != null) { return runner; } } Runner r = Junit4Util.getRunner(); Filter f = Filter.matchMethodDescription(desc); ((Filterable) r).filter(f); // runners.put(desc, r); return r; } private static class TestRunListener extends RunListener { List<Failure> failures = new ArrayList<Failure>(); List<Failure> errors = new ArrayList<Failure>(); String message = null; @Override public void testFailure(Failure failure) throws Exception { // if(message != null){ // logger.warn("Message exists " + message); // } Throwable e = failure.getException(); logger.debug("Adding failure: " + failure + "Exception of failure: " + failure.getException()); // Junit4 does distinguish between failures and errors. Thus, the // type of the exception is checked. if (e instanceof AssertionError) { failures.add(failure); } else { errors.add(failure); } if (failure != null) { message = failure.getMessage(); logger.debug("Setting failure message: " + message); if (ConfigurationLocator.getJavalancheConfiguration().storeTestMessages()) { message += "\n" + failure.getTrace(); } } } public List<Failure> getFailures() { return failures; } public void addFailure(Description desc, Throwable t) { try { testFailure(new Failure(desc, t)); } catch (Exception e) { throw new RuntimeException("Could not add test failure: " + e, e); } } public List<Failure> getErrors() { return errors; } public String getMessage() { return message; } } @Override protected MutationTestRunnable getTestRunnable(final String testName) { MutationTestRunnable r = new MutationTestRunnable() { boolean finished = false; TestRunListener runListener = new TestRunListener(); private StopWatch stopWatch = new StopWatch(); public void run() { // try { // stopWatch.start(); final Description desc = allTests.get(testName); logger.debug("Start running " + desc); runTest(desc, runListener, masterRunner); logger.debug("Run finished " + desc); setFinished(); } private synchronized void setFinished() { finished = true; } public synchronized boolean hasFinished() { return finished; } public SingleTestResult getResult() { String message = runListener.getMessage(); TestOutcome outcome = TestOutcome.PASS; if (runListener.getFailures().size() > 0) { outcome = TestOutcome.FAIL; } else if (runListener.getErrors().size() > 0) { outcome = TestOutcome.ERROR; } SingleTestResult res = new SingleTestResult(testName, message, outcome, stopWatch.getTime()); return res; } public void setFailed(String message, Throwable t) { Exception e; if (t != null) { e = new Exception(message, t); } else { e = new Exception(message); } runListener.addFailure(allTests.get(testName), e); } }; return r; } }