/******************************************************************************* * Copyright (c) 2000, 2017 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * *******************************************************************************/ package org.eclipse.dltk.core.tests.model; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import org.eclipse.dltk.core.tests.TestSupport; import junit.framework.AssertionFailedError; import junit.framework.Protectable; import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestListener; import junit.framework.TestResult; import junit.framework.TestSuite; /** * A test case class that can be set up (using the setUpSuite() method) and tore * down (using the teardDownSuite() method) once for all test cases of this * class. */ public abstract class SuiteOfTestCases extends TestCase { /* * A test suite that initialize the test case's fields once, then that * copies the values of these fields intto each subsequent test case. */ public static class Suite extends TestSuite { SuiteOfTestCases currentTestCase; /* * Creates a new suite on the given class. This class must be a subclass * of SetupableTestSuite. */ public Suite(Class<? extends SuiteOfTestCases> theClass) { super(theClass); } /** * Creates a new suite on the given class. Only the methods specified in * the second parameter and included in the suite. * * @param theClass * @param methodNames */ public Suite(Class<? extends SuiteOfTestCases> theClass, String... methodNames) { super(theClass.getName()); for (int i = 0; i < methodNames.length; ++i) { final String methodName = methodNames[i]; try { final Method method = theClass.getMethod(methodName, new Class[0]); if (Modifier.isPublic(method.getModifiers()) && !Modifier.isStatic(method.getModifiers())) { addTest(createTest(theClass, methodName)); } else { warning(methodName, "Wrong modifiers"); } } catch (SecurityException e) { warning(methodName, e.toString()); } catch (NoSuchMethodException e) { warning(methodName, e.toString()); } } } private void warning(final String name, final String message) { addTest(new TestCase(name) { @Override protected void runTest() { fail(message); } }); } public Suite(String name) { super(name); } void initialize(SuiteOfTestCases test) { Class<?> currentClass = test.getClass(); while (currentClass != null && !currentClass.equals(SuiteOfTestCases.class)) { Field[] fields = currentClass.getDeclaredFields(); for (int i = 0, length = fields.length; i < length; i++) { Field field = fields[i]; // skip static and final fields int modifiers = field.getModifiers(); if (Modifier.isStatic(modifiers) || Modifier.isFinal(modifiers)) continue; // make the field accessible field.setAccessible(true); try { Object value = field.get(this.currentTestCase); field.set(test, value); } catch (IllegalAccessException e) { } } currentClass = currentClass.getSuperclass(); } } @Override public void run(final TestResult result) { Protectable p = () -> { try { // run suite (first test run will setup the suite) superRun(result); } finally { // tear down the suite if (Suite.this.currentTestCase != null) { // protect against empty test suite Suite.this.currentTestCase.tearDownSuite(); } } }; result.runProtected(this, p); } public void superRun(TestResult result) { super.run(result); } @Override public void runTest(Test test, TestResult result) { if (test instanceof SuiteOfTestCases) { final SuiteOfTestCases current = (SuiteOfTestCases) test; if (this.currentTestCase == null) { // setup suite try { current.setUpSuite(); } catch (Exception e) { e.printStackTrace(); } } else { // copy the values of the previous current test case's // fields into the current one this.initialize(current); } current.parentSuite = this; try { super.runTest(test, result); } finally { current.parentSuite = null; // make current this.currentTestCase = current; } } else { /* * If there was error in TestCase constructor - the test will * not be of the SuiteOfTestCases type */ super.runTest(test, result); } } } Suite parentSuite; private final static Map<String, Suite> initializedSuites = new HashMap<>(); public SuiteOfTestCases(String name) { super(name); } /** * Setup the test suite once before all test cases run. */ public void setUpSuite() throws Exception { } /** * Tear down the test suite once after all test cases have run. */ public void tearDownSuite() throws Exception { } /** * Convenience method for subclasses of {@link SuiteOfTestCases}, identical * to * * <pre> * TestSupport.notYetImplemented(this); * </pre> * * @see TestSupport#notYetImplemented(junit.framework.TestCase) * @return <false> when not itself already in the call stack */ public boolean notYetImplemented() { return TestSupport.notYetImplemented(this); } @Override public void run(TestResult result) { if (TestSupport.ignored(this)) return; if (parentSuite == null) { final String className = getClass().getName(); parentSuite = initializedSuites.get(className); if (parentSuite == null) { parentSuite = new Suite(getClass().getName()); initializedSuites.put(getClass().getName(), parentSuite); System.out.println("setUpSuite() in " + getClass().getName()); // TODO (alex) tearDownSuite() not executed final AtomicBoolean errors = new AtomicBoolean(); final TestListener listener = new TestListener() { @Override public void startTest(Test test) { } @Override public void endTest(Test test) { } @Override public void addFailure(Test test, AssertionFailedError t) { errors.set(true); } @Override public void addError(Test test, Throwable t) { errors.set(true); } }; result.addListener(listener); result.runProtected(this, () -> setUpSuite()); result.removeListener(listener); if (errors.get()) { return; } } else if (parentSuite.currentTestCase != null) { parentSuite.initialize(this); } parentSuite.currentTestCase = this; } super.run(result); } }