/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.isis.core.integtestsupport;
import java.util.List;
import com.google.common.base.Throwables;
import org.junit.Rule;
import org.junit.rules.ExpectedException;
import org.junit.rules.MethodRule;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.Statement;
import org.apache.isis.applib.DomainObjectContainer;
import org.apache.isis.applib.NonRecoverableException;
import org.apache.isis.applib.RecoverableException;
import org.apache.isis.applib.fixtures.FixtureClock;
import org.apache.isis.applib.fixturescripts.FixtureScript;
import org.apache.isis.applib.services.clock.ClockService;
import org.apache.isis.applib.services.registry.ServiceRegistry;
import org.apache.isis.applib.services.scratchpad.Scratchpad;
import org.apache.isis.applib.services.sessmgmt.SessionManagementService;
import org.apache.isis.applib.services.wrapper.WrapperFactory;
import org.apache.isis.applib.services.xactn.TransactionService;
import org.apache.isis.core.runtime.system.transaction.IsisTransactionManager;
import org.apache.isis.core.specsupport.scenarios.ScenarioExecution;
import org.apache.isis.core.specsupport.specs.CukeGlueAbstract;
import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2;
import org.apache.isis.core.unittestsupport.jmocking.JUnitRuleMockery2.Mode;
/**
* Base class for integration tests.
*
* <p>
* There is substantial overlap with {@link CukeGlueAbstract}, and it would be possible to factor
* out a common base class. Both delegate to an underlying {@link ScenarioExecution}, and provide
* a bunch of helper methods. The reason this has not been done is mostly to make it easier to see
* the equivalence of these two classes.
*
* <p>
* The only real differences between this class and {@link CukeGlueAbstract} is that this class
* uses JUnit rules to automatically perform {@link IsisTransactionRule transaction management} and
* uses JUnit rules for {@link ExpectedException exception handling}. In {@link CukeGlueAbstract} these
* are required (by Cucumber-JVM) to be explicitly handled in the step definitions.
*/
public abstract class IntegrationTestAbstract {
/**
* @deprecated - just inject domain services into test instead.
*/
@Deprecated
protected static ScenarioExecution scenarioExecution() {
return ScenarioExecution.current();
}
// //////////////////////////////////////
/**
* @deprecated - instead just inject {@link TransactionService} into test and use {@link TransactionService#nextTransaction()} instead.
*/
@Deprecated
protected void nextTransaction() {
scenarioExecution().endTran(true);
scenarioExecution().beginTran();
}
/**
* @deprecated - instead just inject {@link SessionManagementService} or {@link TransactionService} into test and use either {@link SessionManagementService#nextSession()} or {@link TransactionService#nextTransaction()} instead.
*/
@Deprecated
protected void nextRequest() {
nextTransaction();
}
/**
* @deprecated - instead just inject {@link SessionManagementService} into test and use {@link SessionManagementService#nextSession()} instead.
*/
@Deprecated
protected void nextSession() {
scenarioExecution().endTran(true);
scenarioExecution().closeSession();
scenarioExecution().openSession();
scenarioExecution().beginTran();
}
/**
* If just require the current time, use {@link ClockService}.
*/
protected FixtureClock getFixtureClock() {
return ((FixtureClock)FixtureClock.getInstance());
}
// //////////////////////////////////////
/**
* @deprecated - just inject {@link Scratchpad} service into test and use {@link Scratchpad#get(Object)} instead.
*/
@Deprecated
public Object getVar(final String type, final String id) {
return scenarioExecution().getVar(type, id);
}
/**
* @deprecated - just inject {@link Scratchpad} service into test and use {@link Scratchpad#get(Object)} instead.
*/
@Deprecated
public <X> X getVar(final String type, final String id, final Class<X> cls) {
return scenarioExecution().getVar(type, id ,cls);
}
/**
* @deprecated - just inject {@link Scratchpad} service into test and use {@link Scratchpad#put(Object, Object)} instead.
*/
@Deprecated
public void putVar(final String type, final String id, final Object value) {
scenarioExecution().putVar(type, id, value);
}
/**
* @deprecated - just inject {@link Scratchpad} service into test and use {@link Scratchpad#put(Object, Object)} (setting to <tt>null</tt>) instead.
*/
@Deprecated
public void removeVar(final String type, final String id) {
scenarioExecution().removeVar(type, id);
}
/**
* @deprecated - instead just inject service into test; optionally use {@link ServiceRegistry} service to lookup other services.
*/
@Deprecated
protected <T> T service(final Class<T> cls) {
return scenarioExecution().service(cls);
}
/**
* @deprecated - instead just inject {@link org.apache.isis.applib.DomainObjectContainer} into test.
*/
@Deprecated
protected DomainObjectContainer container() {
return scenarioExecution().container();
}
/**
* @deprecated - instead just inject {@link org.apache.isis.applib.services.wrapper.WrapperFactory} into test.
*/
@Deprecated
protected WrapperFactory wrapperFactory() {
return scenarioExecution().wrapperFactory();
}
/**
* Convenience method
*/
protected <T> T wrap(final T obj) {
return scenarioExecution().wrapperFactory().wrap(obj);
}
/**
* Convenience method
*/
protected <T> T unwrap(final T obj) {
return scenarioExecution().wrapperFactory().unwrap(obj);
}
/**
* Convenience method
*/
protected <T> T mixin(final Class<T> mixinClass, final Object mixedIn) {
return container().mixin(mixinClass, mixedIn);
}
// //////////////////////////////////////
/**
* The order is important; this rule is outermost, and must - at a minimum - come before
* the {@link #expectedExceptions} rule.
*/
@Rule
public IsisTransactionRule isisTransactionRule = new IsisTransactionRule();
private static class IsisTransactionRule implements MethodRule {
@Override
public Statement apply(final Statement base, final FrameworkMethod method, final Object target) {
final IsisSystemForTest isft = IsisSystemForTest.get();
return new Statement() {
@Override
public void evaluate() throws Throwable {
isft.getContainer().injectServicesInto(target);
isft.beginTran();
try {
base.evaluate();
isft.endTran();
isft.nextSession();
} catch(final Throwable e) {
// if test failed to clean up after itself, then take care of it here.
endTransactionTilDone();
isft.nextSession();
final List<Throwable> causalChain = Throwables.getCausalChain(e);
// if underlying cause is an applib-defined exception, throw that rather than Isis' wrapper exception
for (final Throwable cause : causalChain) {
if(cause instanceof RecoverableException ||
cause instanceof NonRecoverableException) {
throw cause;
}
}
throw e;
}
}
protected void endTransactionTilDone() {
IsisTransactionManager tranMgr = isft.getIsisSessionFactory().getCurrentSession()
.getPersistenceSession().getTransactionManager();
int count = 0;
while(tranMgr.getTransactionLevel() > 0 &&
count++ < 10 // just in case, to prevent an infinite loop...
) {
try {
tranMgr.endTransaction();
} catch(Exception ignore) {
// ignore
}
}
}
};
}
}
// //////////////////////////////////////
/**
* Convenience method to avoid some boilerplate and rename (as more in keeping with the
* {@link org.apache.isis.applib.fixturescripts.FixtureScript} API compared to the older
* {@link org.apache.isis.applib.fixtures.InstallableFixture} API).
*
* @deprecated - just inject {@link org.apache.isis.applib.fixturescripts.FixtureScripts} service for your application, and call. If multiple fixture scripts, create an anonymous subclass of {@link org.apache.isis.applib.fixturescripts.FixtureScript} and override {@link org.apache.isis.applib.fixturescripts.FixtureScript#execute(org.apache.isis.applib.fixturescripts.FixtureScript.ExecutionContext)} execute.
*/
@Deprecated
protected static void runScript(final FixtureScript... fixtureScripts) {
scenarioExecution().install(fixtureScripts);
}
// //////////////////////////////////////
@Rule
public JUnitRuleMockery2 context = JUnitRuleMockery2.createFor(Mode.INTERFACES_AND_CLASSES);
@Rule
public ExpectedException expectedExceptions = ExpectedException.none();
@Rule
public ExceptionRecognizerTranslate exceptionRecognizerTranslations = ExceptionRecognizerTranslate.create();
}