Skip to content

Commit 0ac4ff4

Browse files
authored
Merge branch 'master' into master
2 parents 7b20062 + 104c97b commit 0ac4ff4

File tree

6 files changed

+935
-3
lines changed

6 files changed

+935
-3
lines changed

com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/AbstractTestStep.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ protected void runAfter() {
208208
* whether preconditions shall be checked
209209
*/
210210
@SuppressFBWarnings("AT_STALE_THREAD_WRITE_OF_PRIMITIVE")
211-
protected static void setCheckPreconditions(final boolean checkPreconditions) {
211+
public static void setCheckPreconditions(final boolean checkPreconditions) {
212212
AbstractTestStep.checkPreconditions = checkPreconditions;
213213
}
214214

@@ -219,7 +219,7 @@ protected static void setCheckPreconditions(final boolean checkPreconditions) {
219219
* whether postconditions shall be checked
220220
*/
221221
@SuppressFBWarnings("AT_STALE_THREAD_WRITE_OF_PRIMITIVE")
222-
protected static void setCheckPostconditions(final boolean checkPostconditions) {
222+
public static void setCheckPostconditions(final boolean checkPostconditions) {
223223
AbstractTestStep.checkPostconditions = checkPostconditions;
224224
}
225225

com.avaloq.tools.ddk.test.core/src/com/avaloq/tools/ddk/test/core/CompoundStep.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public void run() {
5959
* Runs the plannedSteps of this {@link CompoundStep}, making sure that any exception is ignored and the next step executed.
6060
* In the end however, all logged exceptions are reported (thrown as a {@link MultipleTestProblems} exception).
6161
*/
62-
protected void runIgnoreAndContinue() {
62+
public void runIgnoreAndContinue() {
6363
getExecutedSteps().clear();
6464
MultipleTestProblems problemsEncountered = new MultipleTestProblems();
6565
for (final AbstractStep step : getSteps()) {
Lines changed: 325 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,325 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2016 Avaloq Group AG and others.
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v1.0
5+
* which accompanies this distribution, and is available at
6+
* http://www.eclipse.org/legal/epl-v10.html
7+
*
8+
* Contributors:
9+
* Avaloq Group AG - initial API and implementation
10+
*******************************************************************************/
11+
package com.avaloq.tools.ddk.test.core.jupiter;
12+
13+
import java.util.List;
14+
import java.util.Set;
15+
16+
import org.apache.logging.log4j.LogManager;
17+
import org.apache.logging.log4j.Logger;
18+
import org.junit.jupiter.api.AfterEach;
19+
import org.junit.jupiter.api.BeforeEach;
20+
import org.junit.jupiter.api.extension.AfterEachCallback;
21+
import org.junit.jupiter.api.extension.ExtensionContext;
22+
import org.junit.jupiter.api.extension.RegisterExtension;
23+
import org.junit.jupiter.api.extension.TestWatcher;
24+
25+
import com.avaloq.tools.ddk.test.core.AbstractStep;
26+
import com.avaloq.tools.ddk.test.core.AbstractTestStep;
27+
import com.avaloq.tools.ddk.test.core.TestStepListener;
28+
29+
30+
/**
31+
* Abstract base class for system tests.
32+
*/
33+
@SuppressWarnings({"nls"})
34+
public abstract class AbstractSystemTest implements TestStepListener {
35+
// The particular suppress warnings annotation is needed because we WANT to have a unique logger for each of the subclasses
36+
@SuppressWarnings("PMD.LoggerIsNotStaticFinal")
37+
private final Logger logger = LogManager.getLogger(getClass());
38+
private int stepCounter = 1;
39+
private final MultipleTestProblems multipleTestProblems = new MultipleTestProblems();
40+
41+
/** The {@link TestPlan}, that holds all setup- and test-steps of the current test. */
42+
private final TestPlan testPlan = TestPlan.create();
43+
44+
/** The {@link TestPlan}, that holds all setup- and test-steps, which are actually executed. */
45+
private TestPlan executedTestPlan = TestPlan.create();
46+
47+
private static TestPlan previousTestPlan;
48+
private static boolean lastExecutedTestFailed;
49+
private static boolean lastExecutedTestWasSystemTest;
50+
private boolean executingSystemTest;
51+
52+
/**
53+
* Indicates the current state of this {@link AbstractSystemTest}.
54+
*/
55+
private enum TestRunState {
56+
SETUP,
57+
TEST,
58+
TEARDOWN
59+
}
60+
61+
private TestRunState testRunState = TestRunState.SETUP;
62+
63+
/**
64+
* Returns the logger for this test class.
65+
*
66+
* @return the logger for this test class.
67+
*/
68+
protected Logger log() {
69+
return logger;
70+
}
71+
72+
/**
73+
* Registers a junit {@link AfterEachCallback} extension, which is called after a test executes, and checks additional possible error sources.
74+
*/
75+
@RegisterExtension
76+
// CHECKSTYLE:OFF
77+
public AfterEachCallback abstractSystemTestVerifier = new AfterEachCallback() {
78+
79+
@Override
80+
public void afterEach(final ExtensionContext context) throws Exception {
81+
multipleTestProblems.assertEmpty();
82+
83+
}
84+
}; // CHECKSTYLE:ON
85+
86+
/**
87+
* Enables support for unresolved bug tests.
88+
* This declaration must textually be located after the {@code abstractSystemTestVerifier} above.
89+
* This influences the nesting of rules in the chain and bug test aware rule depends on exceptions thrown by the Verifier to succeed.
90+
*/
91+
@RegisterExtension
92+
// CHECKSTYLE:OFF
93+
public BugTestAwareRule bugTestRule = BugTestAwareRule.getInstance();
94+
// CHECKSTYLE:ON
95+
96+
@RegisterExtension
97+
// CHECKSTYLE:OFF
98+
public TestWatcher testWatchman = new AbstractTestWatchman() {
99+
@Override
100+
public void testSuccessful(final ExtensionContext context) {
101+
if (multipleTestProblems.hasProblems()) {
102+
logger.info(context.getDisplayName() + " failed.");
103+
} else {
104+
logger.info(context.getDisplayName() + " succeeded.");
105+
}
106+
}
107+
};
108+
109+
/**
110+
* Setup of the system test.
111+
* Implementations need to specify the setup steps in this method and also provide the @Before annotation.
112+
*/
113+
@BeforeEach
114+
public void setUp() {
115+
AbstractTestStep.registerTestStepListener(this);
116+
}
117+
118+
/**
119+
* Executes the test plan starting with the setup steps and subsequently executing the test steps.
120+
*/
121+
protected final void executeTestPlan() {
122+
cleanUpPreviousTestPlan();
123+
// If current test is no system Test or first executed test, current test plan does not have to be changed
124+
if (!executingSystemTest || previousTestPlan == null || !lastExecutedTestWasSystemTest || lastExecutedTestFailed) {
125+
executedTestPlan = testPlan;
126+
} else {
127+
executedTestPlan = TestPlan.createExecutableTestPlan(testPlan, previousTestPlan);
128+
}
129+
previousTestPlan = testPlan;
130+
try {
131+
AbstractTestStep.setCheckPreconditions(true);
132+
AbstractTestStep.setCheckPostconditions(true);
133+
executedTestPlan.getCompoundSetupStep().run();
134+
AbstractTestStep.setCheckPreconditions(true);
135+
AbstractTestStep.setCheckPostconditions(true);
136+
testRunState = TestRunState.TEST;
137+
executedTestPlan.getCompoundTestStep().run();
138+
lastExecutedTestFailed = false;
139+
// CHECKSTYLE:OFF
140+
} catch (Throwable t) {
141+
// CHECKSTYLE:ON
142+
lastExecutedTestFailed = true;
143+
addTestProblem(t);
144+
} finally {
145+
lastExecutedTestWasSystemTest = executingSystemTest;
146+
testRunState = TestRunState.TEARDOWN; // cannot be put at start of tearDown(), because subclasses may override that method.
147+
}
148+
}
149+
150+
/**
151+
* Undo the steps of the previous test, that are not needed by the current test.
152+
*/
153+
private void cleanUpPreviousTestPlan() {
154+
// No clean up is necessary if one of the following is true:
155+
// - there was no previous test
156+
// - the last test failed (then everything is cleaned up in the previous test)
157+
// - the last test was not a "system test" (then the test cleans up by itself)
158+
if (previousTestPlan == null || lastExecutedTestFailed || !lastExecutedTestWasSystemTest) {
159+
return;
160+
}
161+
TestPlan undoTestPlan = TestPlan.createUndoTestPlan(testPlan, previousTestPlan, executingSystemTest);
162+
AbstractTestStep.setCheckPreconditions(true);
163+
AbstractTestStep.setCheckPostconditions(true);
164+
try {
165+
undoTestPlan.getCompoundTestStep().runIgnoreAndContinue();
166+
// CHECKSTYLE:CHECK-OFF IllegalCatch
167+
} catch (Throwable t) {
168+
// CHECKSTYLE:CHECK-ON IllegalCatch
169+
// ignore problems during tear down
170+
}
171+
try {
172+
undoTestPlan.getCompoundSetupStep().runIgnoreAndContinue();
173+
// CHECKSTYLE:CHECK-OFF IllegalCatch
174+
} catch (Throwable t) {
175+
// CHECKSTYLE:CHECK-ON IllegalCatch
176+
// ignore problems during tear down
177+
}
178+
179+
}
180+
181+
/**
182+
* Executes a system test plan.
183+
*/
184+
protected final void executeSystemTestPlan() {
185+
executingSystemTest = true;
186+
executeTestPlan();
187+
}
188+
189+
/**
190+
* Cleans up the workbench state after a test.
191+
* <p>
192+
* <em>Note:</em> Undoes all test and setup steps in the correct order, if the test is not marked as a System Test.
193+
* </p>
194+
*/
195+
@AfterEach
196+
public void tearDown() {
197+
try {
198+
AbstractTestStep.setCheckPreconditions(true);
199+
AbstractTestStep.setCheckPostconditions(true);
200+
if (!executingSystemTest) {
201+
try {
202+
executedTestPlan.getCompoundTestStep().undo();
203+
// CHECKSTYLE:CHECK-OFF IllegalCatch
204+
} catch (Throwable t) {
205+
// CHECKSTYLE:CHECK-ON IllegalCatch
206+
// ignore problems during tear down
207+
}
208+
try {
209+
executedTestPlan.getCompoundSetupStep().undo();
210+
// CHECKSTYLE:CHECK-OFF IllegalCatch
211+
} catch (Throwable t) {
212+
// CHECKSTYLE:CHECK-ON IllegalCatch
213+
// ignore problems during tear down
214+
}
215+
} else if (lastExecutedTestFailed) {
216+
Set<AbstractStep> filter = executedTestPlan.getAllExecutedSteps();
217+
filter.addAll(TestPlan.getAllStepsWithPreExistingTestEntities(previousTestPlan, testPlan));
218+
TestPlan undoTestPlan = TestPlan.createUndoStepsTestPlan(TestPlan.createReverseTestPlan(TestPlan.createFilteredTestPlan(testPlan, filter)));
219+
try {
220+
undoTestPlan.getCompoundTestStep().runIgnoreAndContinue();
221+
// CHECKSTYLE:CHECK-OFF IllegalCatch
222+
} catch (Throwable t) {
223+
// CHECKSTYLE:CHECK-ON IllegalCatch
224+
// ignore problems during tear down
225+
}
226+
try {
227+
undoTestPlan.getCompoundSetupStep().runIgnoreAndContinue();
228+
// CHECKSTYLE:CHECK-OFF IllegalCatch
229+
} catch (Throwable t) {
230+
// CHECKSTYLE:CHECK-ON IllegalCatch
231+
// ignore problems during tear down
232+
}
233+
}
234+
} finally {
235+
AbstractTestStep.removeTestStepListener(this);
236+
}
237+
}
238+
239+
/**
240+
* Adds a step as setup step. Setup steps are run before the test starts. Therefore, it is not possible to add a setup step after adding a test step.
241+
*
242+
* @param <T>
243+
* the type of the {@link AbstractStep}
244+
* @param setupStep
245+
* the step to append to the list of setup steps
246+
* @return the added {@link AbstractStep}
247+
*/
248+
protected <T extends AbstractStep> T addSetupStep(final T setupStep) {
249+
return testPlan.addSetupStep(setupStep);
250+
}
251+
252+
/**
253+
* Adds a step as test step.
254+
*
255+
* @param <T>
256+
* the type of the {@link AbstractStep}
257+
* @param testStep
258+
* the step to append to the list of test steps
259+
* @return the added {@link AbstractStep}
260+
*/
261+
protected <T extends AbstractStep> T addTestStep(final T testStep) {
262+
return testPlan.addTestStep(testStep);
263+
}
264+
265+
/**
266+
* Adds a new test problem.
267+
*
268+
* @param problem
269+
* the new {@link Throwable} to add
270+
*/
271+
protected void addTestProblem(final Throwable problem) {
272+
multipleTestProblems.addProblem(problem);
273+
logger.error("Error: " + problem.getLocalizedMessage());
274+
}
275+
276+
/**
277+
* Adds a new test problem with a message.
278+
*
279+
* @param message
280+
* the message of the problem
281+
*/
282+
protected void addTestProblem(final String message) {
283+
addTestProblem(new AssertionError(message));
284+
}
285+
286+
/**
287+
* Adds new test problems.
288+
*
289+
* @param problems
290+
* the new {@link Throwable}s to add
291+
*/
292+
protected void addTestProblems(final List<Throwable> problems) {
293+
for (Throwable problem : problems) {
294+
addTestProblem(problem);
295+
}
296+
}
297+
298+
@Override
299+
public void stepStateChanged(final AbstractTestStep testStep, final TestStepState testStepState, final Throwable throwable) {
300+
switch (testStepState) {
301+
case START:
302+
switch (testRunState) {
303+
case SETUP:
304+
logger.info("Setup " + stepCounter++ + ": " + testStep.getName());
305+
break;
306+
case TEST:
307+
logger.info("Test " + stepCounter++ + ": " + testStep.getName());
308+
break;
309+
case TEARDOWN:
310+
logger.info("Teardown: " + testStep.getName());
311+
break;
312+
}
313+
break;
314+
case ERRORED:
315+
logger.error("ERRORED", throwable);
316+
break;
317+
case FAILED:
318+
logger.error("FAILED", throwable);
319+
break;
320+
default:
321+
break;
322+
}
323+
}
324+
325+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2016 Avaloq Group AG and others.
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v1.0
5+
* which accompanies this distribution, and is available at
6+
* http://www.eclipse.org/legal/epl-v10.html
7+
*
8+
* Contributors:
9+
* Avaloq Evolution AG - initial API and implementation
10+
*******************************************************************************/
11+
12+
package com.avaloq.tools.ddk.test.core.jupiter;
13+
14+
import org.apache.logging.log4j.LogManager;
15+
import org.apache.logging.log4j.Logger;
16+
import org.eclipse.jdt.annotation.Nullable;
17+
import org.junit.jupiter.api.extension.BeforeEachCallback;
18+
import org.junit.jupiter.api.extension.ExtensionContext;
19+
import org.junit.jupiter.api.extension.TestWatcher;
20+
import org.opentest4j.TestAbortedException;
21+
22+
23+
@SuppressWarnings("nls")
24+
public abstract class AbstractTestWatchman implements TestWatcher, BeforeEachCallback {
25+
26+
private final Logger logger = LogManager.getLogger(getClass());
27+
28+
@Override
29+
public void beforeEach(final ExtensionContext context) throws Exception {
30+
logger.info(context.getDisplayName() + " started.");
31+
}
32+
33+
@Override
34+
public abstract void testSuccessful(ExtensionContext context);
35+
36+
@Override
37+
public void testFailed(final ExtensionContext context, @Nullable final Throwable cause) {
38+
if (cause instanceof TestAbortedException) {
39+
logger.warn(context.getDisplayName() + " skipped because of failing assumption: " + cause.toString());
40+
} else {
41+
logger.warn(context.getDisplayName() + " failed.");
42+
}
43+
}
44+
45+
}

0 commit comments

Comments
 (0)