Skip to content

Commit

Permalink
Merge branch 'citrusframework:main' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
tschlat authored Sep 8, 2023
2 parents 2f804e4 + 4058578 commit 5e9b3d5
Show file tree
Hide file tree
Showing 10 changed files with 140 additions and 78 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
import java.text.DecimalFormatSymbols;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import org.citrusframework.TestResult;

Expand All @@ -34,11 +36,11 @@ public class TestResults {
private static final long serialVersionUID = 1L;

/** Common decimal format for percentage calculation in report **/
private static DecimalFormat decFormat = new DecimalFormat("0.0");
private static final DecimalFormat decFormat = new DecimalFormat("0.0");
private static final String ZERO_PERCENTAGE = "0.0";

/** Collected test results */
private List<TestResult> results = Collections.synchronizedList(new ArrayList<TestResult>());
private final Set<TestResult> results = Collections.synchronizedSet(new LinkedHashSet<>());

/**
* Provides access to results as list generated from synchronized result list.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,7 @@
import org.citrusframework.log.DefaultLogModifier;
import org.citrusframework.log.LogModifier;
import org.citrusframework.message.MessageProcessors;
import org.citrusframework.report.DefaultTestReporters;
import org.citrusframework.report.FailureStackTestListener;
import org.citrusframework.report.MessageListener;
import org.citrusframework.report.MessageListenerAware;
import org.citrusframework.report.MessageListeners;
import org.citrusframework.report.TestActionListener;
import org.citrusframework.report.TestActionListenerAware;
import org.citrusframework.report.TestActionListeners;
import org.citrusframework.report.TestListener;
import org.citrusframework.report.TestListenerAware;
import org.citrusframework.report.TestListeners;
import org.citrusframework.report.TestReporter;
import org.citrusframework.report.TestReporterAware;
import org.citrusframework.report.TestReporters;
import org.citrusframework.report.TestSuiteListener;
import org.citrusframework.report.TestSuiteListenerAware;
import org.citrusframework.report.TestSuiteListeners;
import org.citrusframework.report.*;
import org.citrusframework.spi.ReferenceRegistry;
import org.citrusframework.spi.ReferenceResolver;
import org.citrusframework.spi.SimpleReferenceResolver;
Expand Down Expand Up @@ -329,6 +313,16 @@ public void bind(String name, Object value) {
}
}

public TestResults getTestResults() {
return testReporters.getTestResults();
}

public void handleTestResults(TestResults testResults) {
if (!getTestResults().equals(testResults)) {
testResults.doWithResults(result -> getTestResults().addResult(result));
}
}

/**
* Citrus context builder.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ public DefaultTestCaseRunner(TestCase testCase, TestContext context) {
this.testCase.setIncremental(true);
}

public TestContext getContext() {
return context;
}

@Override
public void start() {
testCase.start(context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,24 +27,45 @@
import org.citrusframework.message.Message;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.helpers.NOPLoggerFactory;
import org.springframework.util.StringUtils;

/**
* Simple logging reporter printing test start and ending to the console/logger.
* <p/>
* This class provides an option for disablement, allowing you to suppress logging for specific instances
* and delegate the logging to another facility, which could potentially be a subclass of {@link LoggingReporter}.
* It's important to note that when an instance of this class is disabled, it will not perform any logging,
* irrespective of the severity level.
* <p/>
* Implementation note: The disablement of the reporter is achieved by using a {@link org.slf4j.helpers.NOPLogger},
* meaning that this class should primarily focus on logging operations and not extend beyond that functionality.
*
* @author Christoph Deppisch
*/
public class LoggingReporter extends AbstractTestReporter implements MessageListener, TestSuiteListener, TestListener, TestActionListener {

/** Inbound message logger */
private static final Logger INBOUND_MSG_LOGGER = LoggerFactory.getLogger("Logger.Message_IN");
private static Logger inboundMessageLogger = LoggerFactory.getLogger("Logger.Message_IN");

/** The inbound message logger used when the reporter is enabled */
private static final Logger enabledInboundMessageLogger = inboundMessageLogger;

/** Outbound message logger */
private static final Logger OUTBOUND_MSG_LOGGER = LoggerFactory.getLogger("Logger.Message_OUT");
private static Logger outboundMessageLogger = LoggerFactory.getLogger("Logger.Message_OUT");

/** The inbound message logger used when the reporter is enabled */
private static final Logger enabledOutboundMessageLogger = outboundMessageLogger;

/** Logger */
private static Logger log = LoggerFactory.getLogger(LoggingReporter.class);

/** The standard logger used when the reporter is enabled */
private static final Logger enabledLog = log;

/** A {@link org.slf4j.helpers.NOPLogger} used in case the reporter is not enabled. */
private static final Logger noOpLogger = new NOPLoggerFactory().getLogger(LoggingReporter.class.getName());

@Override
public void generate(TestResults testResults) {
separator();
Expand Down Expand Up @@ -228,12 +249,12 @@ public void onTestActionSkipped(TestCase testCase, TestAction testAction) {

@Override
public void onInboundMessage(Message message, TestContext context) {
INBOUND_MSG_LOGGER.debug(message.print(context));
inboundMessageLogger.debug(message.print(context));
}

@Override
public void onOutboundMessage(Message message, TestContext context) {
OUTBOUND_MSG_LOGGER.debug(message.print(context));
outboundMessageLogger.debug(message.print(context));
}

/**
Expand Down Expand Up @@ -284,4 +305,23 @@ protected void debug(String line) {
protected boolean isDebugEnabled() {
return log.isDebugEnabled();
}

/**
* Sets the enablement state of the reporter.
*/
public void setEnabled(boolean enabled) {
if (enabled) {
log = enabledLog;
inboundMessageLogger = enabledInboundMessageLogger;
outboundMessageLogger = enabledOutboundMessageLogger;
} else {
log = noOpLogger;
inboundMessageLogger = noOpLogger;
outboundMessageLogger = noOpLogger;
}
}

protected boolean isEnabled() {
return log != noOpLogger;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,12 @@ public void run(CitrusFrameworkMethod frameworkMethod) {
if (frameworkMethod.getMethod().getAnnotation(CitrusTestSource.class) != null) {
testLoader = createTestLoader(frameworkMethod.getTestName(), frameworkMethod.getPackageName(),
frameworkMethod.getSource(), frameworkMethod.getSourceType());

CitrusAnnotations.injectTestRunner(testLoader, runner);
} else {
testLoader = new DefaultTestLoader();
}

CitrusAnnotations.injectAll(testLoader, citrus, ctx);
CitrusAnnotations.injectTestRunner(testLoader, runner);
testLoader.doWithTestCase(t -> JUnit4Helper.invokeTestMethod(this, frameworkMethod, ctx));
testLoader.load();
} else if (frameworkMethod.getMethod().getAnnotation(CitrusXmlTest.class) != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,14 +100,13 @@ public void run(CitrusFrameworkMethod frameworkMethod) {
if (frameworkMethod.getMethod().getAnnotation(CitrusTestSource.class) != null) {
testLoader = createTestLoader(frameworkMethod.getTestName(), frameworkMethod.getPackageName(),
frameworkMethod.getSource(), frameworkMethod.getSourceType());

CitrusAnnotations.injectTestRunner(testLoader, runner);
} else {
testLoader = new DefaultTestLoader();
testLoader.configureTestCase(t -> testCase = t);
}

CitrusAnnotations.injectAll(testLoader, citrus, ctx);
CitrusAnnotations.injectTestRunner(testLoader, runner);
testLoader.doWithTestCase(t -> JUnit4Helper.invokeTestMethod(this, frameworkMethod, ctx));
testLoader.load();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
import org.citrusframework.common.TestLoader;
import org.citrusframework.context.TestContext;
import org.citrusframework.exceptions.CitrusRuntimeException;
import org.junit.jupiter.api.extension.AfterAllCallback;
import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.AfterTestExecutionCallback;
import org.junit.jupiter.api.extension.BeforeAllCallback;
Expand All @@ -55,7 +54,7 @@
* @author Christoph Deppisch
*/
public class CitrusExtension implements BeforeAllCallback, InvocationInterceptor,
AfterTestExecutionCallback, ParameterResolver, TestInstancePostProcessor, TestExecutionExceptionHandler, AfterEachCallback, AfterAllCallback {
AfterTestExecutionCallback, ParameterResolver, TestInstancePostProcessor, TestExecutionExceptionHandler, AfterEachCallback {

/** Test suite name */
private static final String SUITE_NAME = "citrus-junit5-suite";
Expand All @@ -76,41 +75,25 @@ public void beforeAll(ExtensionContext extensionContext) {

if (beforeSuite) {
beforeSuite = false;
// Assertion: If the beforeAll callback is called for a test, the annotated tags are currently
// included by the groups filter of surefire / failsafe or no specific filter is defined and
// all groups / tags will run anyway.
final String[] tags = extensionContext.getTags().toArray(new String[0]);
extensionContext.getTestClass().map(Class::getName).or(() ->
extensionContext.getTestMethod().map(method -> method.getDeclaringClass().getName()+":"+method.getName())
).ifPresentOrElse(suiteName ->
CitrusExtensionHelper
.getCitrus(extensionContext)
.beforeSuite(suiteName, tags),
() -> CitrusExtensionHelper
.getCitrus(extensionContext)
.beforeSuite(SUITE_NAME, tags)
);
}
}

@Override
public void afterAll(ExtensionContext extensionContext) throws Exception {
if (afterSuite) {
afterSuite = false;
// Assertion: If the afterAll callback is called for a test, the annotated tags are currently
// included by the groups filter of surefire / failsafe or no specific filter is defined and
// all groups / tags did run anyway.
final String[] tags = extensionContext.getTags().toArray(new String[0]);
extensionContext.getTestClass().map(Class::getName).or(() ->
extensionContext.getTestMethod().map(meth -> meth.getDeclaringClass().getName()+":"+meth.getName())
).ifPresentOrElse(suiteName ->
CitrusExtensionHelper
.getCitrus(extensionContext)
.afterSuite(suiteName, tags),
() -> CitrusExtensionHelper
.getCitrus(extensionContext)
.afterSuite(SUITE_NAME, tags)
);
// Assertion: If the beforeAll callback is called for a test, the annotated tags are currently
// included by the groups filter of surefire / failsafe or no specific filter is defined and
// all groups / tags will run anyway.

//initialize "after all test run hook"
String[] tags = extensionContext.getTags().toArray(new String[0]);
String suiteName = extensionContext.getTestClass()
.map(Class::getName)
.orElseGet(() ->
extensionContext.getTestMethod()
.map(meth -> meth.getDeclaringClass().getName()+ ":" + meth.getName())
.orElse(SUITE_NAME)
);

extensionContext.getRoot().getStore(ExtensionContext.Namespace.GLOBAL).put("afterSuiteCallback",
new AfterSuiteCallback(extensionContext, suiteName, tags));

CitrusExtensionHelper.getCitrus(extensionContext).beforeSuite(suiteName, tags);
}
}

Expand Down Expand Up @@ -211,4 +194,25 @@ default void before(CitrusContext context) {
default void after(CitrusContext context) {
}
}

private static class AfterSuiteCallback implements ExtensionContext.Store.CloseableResource {

private final ExtensionContext extensionContext;
private final String suiteName;
private final String[] tags;

public AfterSuiteCallback(ExtensionContext extensionContext, String suiteName, String... tags) {
this.extensionContext = extensionContext;
this.suiteName = suiteName;
this.tags = tags;
}

@Override
public void close() throws Throwable {
if (afterSuite) {
afterSuite = false;
CitrusExtensionHelper.getCitrus(extensionContext).afterSuite(suiteName, tags);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@
import java.lang.reflect.Method;

import org.citrusframework.Citrus;
import org.citrusframework.CitrusSpringContext;
import org.citrusframework.CitrusSpringContextProvider;
import org.citrusframework.TestCaseRunner;
import org.citrusframework.junit.jupiter.CitrusExtension;
import org.citrusframework.junit.jupiter.CitrusExtensionHelper;
import org.junit.jupiter.api.extension.AfterAllCallback;
import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.AfterTestExecutionCallback;
import org.junit.jupiter.api.extension.BeforeAllCallback;
Expand Down Expand Up @@ -55,7 +55,7 @@
* @author Christoph Deppisch
*/
public class CitrusSpringExtension implements BeforeAllCallback, BeforeTestExecutionCallback, InvocationInterceptor,
AfterTestExecutionCallback, ParameterResolver, TestInstancePostProcessor, TestExecutionExceptionHandler, AfterEachCallback, AfterAllCallback {
AfterTestExecutionCallback, ParameterResolver, TestInstancePostProcessor, TestExecutionExceptionHandler, AfterEachCallback {

private Citrus citrus;
private ApplicationContext applicationContext;
Expand All @@ -67,11 +67,6 @@ public void beforeAll(ExtensionContext extensionContext) {
delegate.beforeAll(extensionContext);
}

@Override
public void afterAll(ExtensionContext extensionContext) throws Exception {
delegate.afterAll(extensionContext);
}

@Override
public void handleTestExecutionException(ExtensionContext extensionContext, Throwable throwable) throws Throwable {
delegate.handleTestExecutionException(extensionContext, throwable);
Expand Down Expand Up @@ -119,12 +114,30 @@ public Object resolveParameter(ParameterContext parameterContext, ExtensionConte
*/
protected Citrus getCitrus(ExtensionContext extensionContext) {
ApplicationContext ctx = SpringExtension.getApplicationContext(extensionContext);
if (applicationContext == null) {
applicationContext = ctx;
citrus = Citrus.newInstance(new CitrusSpringContextProvider(ctx));
} else if (!applicationContext.equals(ctx)) {
Citrus existing = null;
if (!CitrusExtensionHelper.requiresCitrus(extensionContext)) {
existing = CitrusExtensionHelper.getCitrus(extensionContext);
}

if (applicationContext == null || !applicationContext.equals(ctx)) {
applicationContext = ctx;
citrus = Citrus.newInstance(new CitrusSpringContextProvider(ctx));
}

if (citrus == null) {
if (existing != null && existing.getCitrusContext() instanceof CitrusSpringContext &&
((CitrusSpringContext) existing.getCitrusContext()).getApplicationContext().equals(applicationContext)) {
citrus = existing;
} else {
citrus = Citrus.newInstance(new CitrusSpringContextProvider(applicationContext));

if (existing != null) {
citrus.getCitrusContext().handleTestResults(existing.getCitrusContext().getTestResults());
}
}
} else if (existing == null ||
!(existing.getCitrusContext() instanceof CitrusSpringContext) ||
!((CitrusSpringContext) existing.getCitrusContext()).getApplicationContext().equals(applicationContext)) {
citrus = Citrus.newInstance(new CitrusSpringContextProvider(applicationContext));
}

return citrus;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ public void extractVariables(Message message, TestContext context) {
public static final class Builder implements VariableExtractor.Builder<JsonPathVariableExtractor, Builder>, MessageProcessorAdapter, ValidationContextAdapter {
private final Map<String, Object> expressions = new LinkedHashMap<>();

public static Builder fromJsonPath() {
return new Builder();
}

@Override
public Builder expressions(Map<String, Object> expressions) {
this.expressions.putAll(expressions);
Expand Down
Loading

0 comments on commit 5e9b3d5

Please sign in to comment.