org.kuali.rice.test.RiceTestCase Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of rice-it-internal-tools Show documentation
Show all versions of rice-it-internal-tools Show documentation
This module contains rice's internal integration testing tools. This module is internal to rice and should not used outside of the rice project. It should only be depended on by rice integration test code. This module can depend on any part of rice.
/**
* Copyright 2005-2014 The Kuali Foundation
*
* Licensed under the Educational Community 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.opensource.org/licenses/ecl2.php
*
* 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.kuali.rice.test;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import org.junit.After;
import org.junit.Before;
import org.kuali.rice.core.api.config.property.Config;
import org.kuali.rice.core.api.config.property.ConfigContext;
import org.kuali.rice.core.api.lifecycle.BaseLifecycle;
import org.kuali.rice.core.api.lifecycle.Lifecycle;
import org.kuali.rice.core.framework.resourceloader.SpringResourceLoader;
import org.kuali.rice.core.impl.config.property.JAXBConfigImpl;
import org.kuali.rice.test.data.PerSuiteUnitTestData;
import org.kuali.rice.test.lifecycles.PerSuiteDataLoaderLifecycle;
import org.springframework.beans.factory.BeanCreationNotAllowedException;
import org.springframework.core.io.FileSystemResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import javax.xml.namespace.QName;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
/**
* Useful superclass for all Rice test cases. Handles setup of test utilities and a test environment. Configures the
* Spring test environment providing a template method for custom context files in test mode. Also provides a template method
* for running custom transactional setUp. Tear down handles automatic tear down of objects created inside the test
* environment.
*
* @author Kuali Rice Team ([email protected])
* @since 0.9
*/
public abstract class RiceTestCase extends BaseRiceTestCase {
protected static final Logger LOG = Logger.getLogger(RiceTestCase.class);
private static final String ALT_LOG4J_CONFIG_LOCATION_PROP = "alt.log4j.config.location";
private static final String DEFAULT_LOG4J_CONFIG = "classpath:rice-testharness-default-log4j.properties";
protected static final String DEFAULT_TEST_HARNESS_SPRING_BEANS = "classpath:TestHarnessSpringBeans.xml";
protected static boolean SUITE_LIFE_CYCLES_RAN = false;
protected static boolean SUITE_LIFE_CYCLES_FAILED = false;
protected static String failedSuiteTestName;
protected List perTestLifeCycles = new LinkedList();
protected List suiteLifeCycles = new LinkedList();
private static Set perSuiteDataLoaderLifecycleNamesRun = new HashSet();
private List reports = new ArrayList();
private SpringResourceLoader testHarnessSpringResourceLoader;
private boolean clearTables = true;
private long testStart;
private long testEnd;
@Override
@Before
public void setUp() throws Exception {
testStart = System.currentTimeMillis();
try {
configureLogging();
logBeforeRun();
final long initTime = System.currentTimeMillis();
setUpInternal();
report("Time to start all Lifecycles: " + (System.currentTimeMillis() - initTime));
} catch (Throwable e) {
e.printStackTrace();
tearDown();
throw new RuntimeException(e);
}
}
/**
* Internal setUp() implementation which is invoked by the main setUp() and wrapped with exception handling
*
* Subclasses should override this method if they want to
* add set up steps that should occur in the standard set up process, wrapped by
* exception handling.
*/
protected void setUpInternal() throws Exception {
assertNotNull(getModuleName());
setModuleName(getModuleName());
setBaseDirSystemProperty(getModuleName());
this.perTestLifeCycles = getPerTestLifecycles();
this.suiteLifeCycles = getSuiteLifecycles();
if (SUITE_LIFE_CYCLES_FAILED) {
fail("Suite Lifecycles startup failed on test " + failedSuiteTestName + "!!! Please see logs for details.");
}
if (!SUITE_LIFE_CYCLES_RAN) {
try {
startLifecycles(this.suiteLifeCycles);
SUITE_LIFE_CYCLES_RAN = true;
} catch (Throwable e) {
e.printStackTrace();
SUITE_LIFE_CYCLES_RAN = false;
SUITE_LIFE_CYCLES_FAILED = true;
failedSuiteTestName = getFullTestName();
tearDown();
stopLifecycles(this.suiteLifeCycles);
throw new RuntimeException(e);
}
}
startSuiteDataLoaderLifecycles();
startLifecycles(this.perTestLifeCycles);
}
/**
* This block is walking up the class hierarchy of the current unit test looking for PerSuiteUnitTestData annotations. If it finds one,
* it will run it once, then add it to a set so that it does not get run again. This is needed so that multiple
* tests can extend from the same suite and so that there can be multiple suites throughout the test source branch.
*
* @throws Exception if a PerSuiteDataLoaderLifecycle is unable to be started
*/
protected void startSuiteDataLoaderLifecycles() throws Exception {
List classes = TestUtilities.getHierarchyClassesToHandle(getClass(), new Class[] { PerSuiteUnitTestData.class }, perSuiteDataLoaderLifecycleNamesRun);
for (Class c: classes) {
new PerSuiteDataLoaderLifecycle(c).start();
perSuiteDataLoaderLifecycleNamesRun.add(c.getName());
}
}
/**
* maven will set this property and find resources from the config based on it. This makes eclipse testing work because
* we have to put the basedir in our config files in order to find things when testing from maven
*/
protected void setBaseDirSystemProperty(String moduleBaseDir) {
if (System.getProperty("basedir") == null) {
final String userDir = System.getProperty("user.dir");
System.setProperty("basedir", userDir + ((userDir.endsWith(File.separator + "it" + File.separator + moduleBaseDir)) ? "" : File.separator + "it" + File.separator + moduleBaseDir));
}
}
/**
* the absolute path on the file system to the root folder of the maven module containing a child of this class
* e.g. for krad: [rice-project-dir]/it/krad
*
*
* the user.dir property can be set on the CLI or IDE run configuration e.g. -Duser.dir=/some/dir
*
* @return the value of a system property 'user.dir' if it exists, null if not
*/
protected String getUserDir() {
return System.getProperty("user.dir");
}
protected void setModuleName(String moduleName) {
if (System.getProperty("module.name") == null) {
System.setProperty("module.name", moduleName);
}
}
@Override
@After
public void tearDown() throws Exception {
// wait for outstanding threads to complete for 1 minute
ThreadMonitor.tearDown(60000);
try {
stopLifecycles(this.perTestLifeCycles);
// Avoid failing test for creation of bean in destroy.
} catch (BeanCreationNotAllowedException bcnae) {
LOG.warn("BeanCreationNotAllowedException during stopLifecycles during tearDown " + bcnae.getMessage());
}
testEnd = System.currentTimeMillis();
report("Total time to run test: " + (testEnd - testStart));
logAfterRun();
}
protected void logBeforeRun() {
LOG.info("##############################################################");
LOG.info("# Starting test " + getFullTestName() + "...");
LOG.info("# " + dumpMemory());
LOG.info("##############################################################");
}
protected void logAfterRun() {
LOG.info("##############################################################");
LOG.info("# ...finished test " + getFullTestName());
LOG.info("# " + dumpMemory());
for (final String report : this.reports) {
LOG.info("# " + report);
}
LOG.info("##############################################################\n\n\n");
}
protected String getFullTestName() {
return getClass().getSimpleName() + "." + getName();
}
/**
* configures logging using custom properties file if specified, or the default one.
* Log4j also uses any file called log4.properties in the classpath
*
* To configure a custom logging file, set a JVM system property on using -D. For example
* -Dalt.log4j.config.location=file:/home/me/kuali/test/dev/log4j.properties
*
*
* The above option can also be set in the run configuration for the unit test in the IDE.
* To avoid log4j using files called log4j.properties that are defined in the classpath, add the following system property:
* -Dlog4j.defaultInitOverride=true
*
* @throws IOException
*/
protected void configureLogging() throws IOException {
ResourceLoader resourceLoader = new FileSystemResourceLoader();
String altLog4jConfigLocation = System.getProperty(ALT_LOG4J_CONFIG_LOCATION_PROP);
Resource log4jConfigResource = null;
if (!StringUtils.isEmpty(altLog4jConfigLocation)) {
log4jConfigResource = resourceLoader.getResource(altLog4jConfigLocation);
}
if (log4jConfigResource == null || !log4jConfigResource.exists()) {
System.out.println("Alternate Log4j config resource does not exist! " + altLog4jConfigLocation);
System.out.println("Using default log4j configuration: " + DEFAULT_LOG4J_CONFIG);
log4jConfigResource = resourceLoader.getResource(DEFAULT_LOG4J_CONFIG);
} else {
System.out.println("Using alternate log4j configuration at: " + altLog4jConfigLocation);
}
Properties p = new Properties();
p.load(log4jConfigResource.getInputStream());
PropertyConfigurator.configure(p);
}
/**
* Executes the start() method of each of the lifecycles in the given list.
*/
protected void startLifecycles(List lifecycles) throws Exception {
for (Lifecycle lifecycle : lifecycles) {
lifecycle.start();
}
}
/**
* Executes the stop() method of each of the lifecyles in the given list. The
* List of lifecycles is processed in reverse order.
*/
protected void stopLifecycles(List lifecycles) throws Exception {
int lifecyclesSize = lifecycles.size() - 1;
for (int i = lifecyclesSize; i >= 0; i--) {
try {
if (lifecycles.get(i) == null) {
LOG.warn("Attempted to stop a null lifecycle");
} else {
if (lifecycles.get(i).isStarted()) {
LOG.warn("Attempting to stop a lifecycle " + lifecycles.get(i).getClass());
lifecycles.get(i).stop();
}
}
} catch (Exception e) {
LOG.error("Failed to shutdown one of the lifecycles!", e);
}
}
}
/**
* Returns the List of Lifecycles to start when the unit test suite is started
*/
protected List getSuiteLifecycles() {
List lifecycles = new LinkedList();
/**
* Initializes Rice configuration from the test harness configuration file.
*/
lifecycles.add(new BaseLifecycle() {
@Override
public void start() throws Exception {
Config config = getTestHarnessConfig();
ConfigContext.init(config);
super.start();
}
});
/**
* Loads the TestHarnessSpringBeans.xml file which obtains connections to the DB for us
*/
lifecycles.add(getTestHarnessSpringResourceLoader());
/**
* Establishes the TestHarnessServiceLocator so that it has a reference to the Spring context
* created from TestHarnessSpringBeans.xml
*/
lifecycles.add(new BaseLifecycle() {
@Override
public void start() throws Exception {
TestHarnessServiceLocator.setContext(getTestHarnessSpringResourceLoader().getContext());
super.start();
}
});
/**
* Clears the tables in the database.
*/
if (clearTables) {
lifecycles.add(new ClearDatabaseLifecycle());
}
/**
* Loads Suite Test Data
*/
lifecycles.add(new BaseLifecycle() {
@Override
public void start() throws Exception {
loadSuiteTestData();
super.start();
}
});
Lifecycle loadApplicationLifecycle = getLoadApplicationLifecycle();
if (loadApplicationLifecycle != null) {
lifecycles.add(loadApplicationLifecycle);
}
return lifecycles;
}
/**
* This should return a Lifecycle that can be used to load the application
* being tested. For example, this could start a Jetty Server which loads
* the application, or load a Spring context to establish a set of services,
* or any other application startup activities that the test depends upon.
*/
protected Lifecycle getLoadApplicationLifecycle() {
// by default return null, do nothing
return null;
}
/**
* @return Lifecycles run every test run
*/
protected List getPerTestLifecycles() {
List lifecycles = new LinkedList();
lifecycles.add(getPerTestDataLoaderLifecycle());
lifecycles.add(new BaseLifecycle() {
@Override
public void start() throws Exception {
loadPerTestData();
super.start();
}
});
return lifecycles;
}
/**
* A method that can be overridden to load test data for the unit test Suite.
*/
protected void loadSuiteTestData() throws Exception {
// do nothing by default, subclass can override
}
/**
* A method that can be overridden to load test data on a test-by-test basis
*/
protected void loadPerTestData() throws Exception {
// do nothing by default, subclass can override
}
protected void report(final String report) {
this.reports.add(report);
}
protected String dumpMemory() {
final long total = Runtime.getRuntime().totalMemory();
final long free = Runtime.getRuntime().freeMemory();
final long max = Runtime.getRuntime().maxMemory();
return "[Memory] max: " + max + ", total: " + total + ", free: " + free;
}
public SpringResourceLoader getTestHarnessSpringResourceLoader() {
if (testHarnessSpringResourceLoader == null) {
testHarnessSpringResourceLoader = new SpringResourceLoader(new QName("TestHarnessSpringContext"), getTestHarnessSpringBeansLocation(), null);
}
return testHarnessSpringResourceLoader;
}
/**
* Returns the location of the test harness spring beans context file.
* Subclasses may override to specify a different location.
* @return the location of the test harness spring beans context file.
*/
protected List getTestHarnessSpringBeansLocation() {
return Collections.singletonList( DEFAULT_TEST_HARNESS_SPRING_BEANS );
}
protected Config getTestHarnessConfig() throws Exception {
Config config = new JAXBConfigImpl(getConfigLocations(), System.getProperties());
config.parseConfig();
return config;
}
/**
* Subclasses may override this method to customize the location(s) of the Rice configuration.
* By default it is: classpath:META-INF/" + getModuleName().toLowerCase() + "-test-config.xml"
* @return List of config locations to add to this tests config location.
*/
protected List getConfigLocations() {
List configLocations = new ArrayList();
configLocations.add(getRiceMasterDefaultConfigFile());
configLocations.add(getModuleTestConfigLocation());
return configLocations;
}
protected String getModuleTestConfigLocation() {
return "classpath:META-INF/" + getModuleName().toLowerCase() + "-test-config.xml";
}
protected String getRiceMasterDefaultConfigFile() {
return "classpath:META-INF/test-config-defaults.xml";
}
/**
* same as the module directory in the project.
*
* @return name of module that the tests located
*/
protected abstract String getModuleName();
protected void setClearTables(boolean clearTables) {
this.clearTables = clearTables;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy