/* * Copyright Technophobia Ltd 2012 * * This file is part of Substeps. * * Substeps 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 3 of the License, or * (at your option) any later version. * * Substeps 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 Substeps. If not, see <http://www.gnu.org/licenses/>. */ package com.technophobia.substeps.runner; import static org.hamcrest.CoreMatchers.is; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import org.junit.Assert; import org.junit.Before; import org.junit.runner.Description; /** * * * @author imoore * */ public abstract class BaseJunitFeatureRunnerTest implements TestCallback { protected TestNotifier testNotifier = null; // List<MethodInvocation> invocations = null; protected List<DescriptionSnapshot> descriptionSnapshots = null; protected int descriptionSnapshotsIdx = 0; protected List<String> descriptionKeys = null; protected List<AssertionError> assertions = null; @Before public void before() { testNotifier = new TestNotifier(); // invocations = new ArrayList<MethodInvocation>(); descriptionKeys = new ArrayList<String>(); descriptionSnapshots = new ArrayList<DescriptionSnapshot>(); descriptionSnapshotsIdx = 0; assertions = new ArrayList<AssertionError>(); } protected Map<Class<?>, Object> getImplsCache(final JunitFeatureRunner runner) { Map<Class<?>, Object> implsCache = null; try { final Field runnerField = runner.getClass().getDeclaredField("runner"); runnerField.setAccessible(true); // final Field runnerField = // runner.getClass().getDeclaredField("scenarioRunner"); // runnerField.setAccessible(true); final Object runnerObject = runnerField.get(runner); final Field implCacheField = runnerObject.getClass().getDeclaredField("methodExecutor"); implCacheField.setAccessible(true); final Object implementationCacheObject = implCacheField.get(runnerObject); final Field instanceMapField = implementationCacheObject.getClass().getDeclaredField("instanceMap"); instanceMapField.setAccessible(true); implsCache = (Map<Class<?>, Object>) instanceMapField.get(implementationCacheObject); } catch (final Exception e) { e.printStackTrace(); } Assert.assertNotNull("implsCache should not be null", implsCache); return implsCache; } /** * @param description */ protected void buildDescriptionKeys(final Description description) { // add this description final String displayName = description.getDisplayName(); final String key = displayName.split(" ")[0]; descriptionKeys.add(key); if (description.getChildren() != null) { for (final Description child : description.getChildren()) { buildDescriptionKeys(child); } } } /** * {@inheritDoc} */ public void doCallback(final String methodName, final String[] params) { System.out.println("got callback from method: " + methodName); // NB. this method will be called for every step - some of which will be // defined as substeps and therefore not have a description // methods may be called multiple times, however they will have // different descriptions... // where are we in the list of anticipated invocations? // check that all of the started in the snapshot have been fired // NB. this method is called as if it's the running test, so any failure // has no bearing on this test class succeeding or failing try { assertNotificationState(); } catch (final java.lang.AssertionError ae) { assertions.add(ae); } // final Description current = testNotifier.getCurrentlyRunning(); // if (current != null) // { // final String displayName = current.getDisplayName(); // // final String key = displayName.split(" ")[0]; // // } descriptionSnapshotsIdx++; // from this method name we want to get back to the description // check that when this method is invoked, fireTestStarted has been // invoked for this test, but not finished, and finished or failed for // prior tests and nothing for subsequent } /** * */ private void assertNotificationState() { final Description current = testNotifier.getCurrentlyRunning(); if (!descriptionSnapshots.isEmpty()) { final DescriptionSnapshot snapshot = descriptionSnapshots.get(descriptionSnapshotsIdx); // TODO - add a currently running to the snapshot printState(snapshot); final List<String> shouldHaveBeenStarted = Arrays.asList(snapshot.getStarted()); if (shouldHaveBeenStarted != null) { Assert.assertThat("incorrect number of starts", testNotifier.getAllStarted().size(), is(shouldHaveBeenStarted.size() + 1)); // NB. + 1 because of the root description Assert.assertTrue("Not all descriptions that should have been started, have been", toKeyList(testNotifier.getAllStarted()).containsAll(shouldHaveBeenStarted)); } else { Assert.assertThat(testNotifier.getAllStarted().size(), is(0)); } } // TODO check completetions } List<String> toKeyList(final List<Description> dess) { List<String> rtn = null; if (dess != null) { rtn = new ArrayList<String>(); for (final Description d : dess) { rtn.add(d.getDisplayName().split(" ")[0]); } } return rtn; } /** * @param snapshot */ private void printState(final DescriptionSnapshot snapshot) { final String[] shouldHaveBeenStarted = snapshot.getStarted(); if (shouldHaveBeenStarted != null) { for (final String s : shouldHaveBeenStarted) { System.out.println("should have started: " + s); } } else { System.out.println("nothing should have been started"); } for (final Description des : testNotifier.getAllStarted()) { System.out.println("actually started: " + des.getDisplayName()); } } public static class DescriptionSnapshot { private final String[] started; private final String[] finished; private final String[] failed; /** * @param started * @param finished * @param failed */ public DescriptionSnapshot(final String[] started, final String[] finished, final String[] failed) { super(); this.started = started; this.finished = finished; this.failed = failed; } public String[] getStarted() { return started; } public String[] getFinished() { return finished; } public String[] getFailed() { return failed; } } }