All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ch.inftec.ju.testing.db.DbTestAnnotationHandler Maven / Gradle / Ivy

There is a newer version: 4.5.1-11
Show newest version
package ch.inftec.ju.testing.db;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.persistence.EntityManager;

import org.apache.commons.lang3.StringUtils;
import org.junit.Assert;
import org.junit.internal.AssumptionViolatedException;
import org.junit.runner.Description;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;

import ch.inftec.ju.db.JuEmUtil;
import ch.inftec.ju.testing.db.DbDataUtil.ExportBuilder;
import ch.inftec.ju.util.AssertUtil;
import ch.inftec.ju.util.IOUtil;
import ch.inftec.ju.util.JuException;
import ch.inftec.ju.util.JuObjectUtils;
import ch.inftec.ju.util.JuRuntimeException;
import ch.inftec.ju.util.JuStringUtils;
import ch.inftec.ju.util.JuUrl;
import ch.inftec.ju.util.JuUtils;
import ch.inftec.ju.util.ReflectUtils;
import ch.inftec.ju.util.XString;
import ch.inftec.ju.util.xml.XmlUtils;

/**
 * Helper class to handle test annotations like @DataSet and @DataVerify.
 * 

* When calling the execute... methods, the client is responsible that a valid transaction is present. * @author Martin * */ public class DbTestAnnotationHandler implements Serializable { private final Logger logger = LoggerFactory.getLogger(DbTestAnnotationHandler.class); private final List dataSetAnnos; private final List dataSetExportAnnos; private final List postServerCodeAnnos; private final List dataVerifyAnnos; protected final String testClassName; protected final String testMethodName; /** * Readable name of the test method, may be testMethod[0] for parameterized tests. */ private final String testMethodReadableName; public DbTestAnnotationHandler(Method method, Description description) { // Get all annotations for the method and the declaring class (including super classes, but // excluding overridden methods) this.dataSetAnnos = ReflectUtils.getAnnotations(method, DataSet.class, false, true, true); // Reverse the list as we want to start with the base class, then class and method last Collections.reverse(this.dataSetAnnos); this.dataSetExportAnnos = ReflectUtils.getAnnotations(method, DataSetExport.class, true, true, true); this.postServerCodeAnnos = ReflectUtils.getAnnotations(method, PostServerCode.class, true, false, false); this.dataVerifyAnnos = ReflectUtils.getAnnotations(method, DataVerify.class, true, false, false); this.testClassName = method.getDeclaringClass().getName(); this.testMethodName = method.getName(); this.testMethodReadableName = description.getMethodName(); } private Class getTestClass() { try { return Class.forName(this.testClassName); } catch (Exception ex) { throw new JuRuntimeException("Couldn't get test class. Make sure it's on the classpath: " + this.testClassName); } } // private Method getTestMethod() { // return ReflectUtils.getMethod(this.getTestClass(), this.testMethodName, new Class[0]); // } public final void executePreTestAnnotations(JuEmUtil emUtil) throws Exception { // Load test data as defined by annotations DbDataUtil du = new DbDataUtil(emUtil); Integer sequenceValue = null; for (DataSet dataSet : this.dataSetAnnos) { // Run pre initializer this.runInitializer(dataSet.preInitializer(), emUtil.getEm()); if (DataSet.NO_CLEAN_INSERT.equals(dataSet.value())) { // Skip clean-insert } else { // Perform clean-insert of value resource URL resourceUrl = this.resourceToUrl(dataSet.value(), dataSet.resourceDir()); du.buildImport() .from(resourceUrl) .executeCleanInsert(); } // Perform inserts for inserts resources for (String insertResource : dataSet.inserts()) { URL resourceUrl = this.resourceToUrl(insertResource, dataSet.resourceDir()); du.buildImport() .from(resourceUrl) .executeInsert(); } sequenceValue = dataSet.sequenceValue(); // Run post initializer this.runInitializer(dataSet.postInitializer(), emUtil.getEm()); } // Reset the sequences if (sequenceValue != null) { emUtil.resetIdentityGenerationOrSequences(sequenceValue); } } private void runInitializer(Class clazz, EntityManager em) throws Exception { ServerCode initializer = ReflectUtils.newInstance(clazz, false); initializer.init(em); initializer.execute(); } /** * Converts a resourceUrl string to an URL. This also performs paramterized placeholder replacement * if necessary. * @param resource Resource path * @param resourceDir Resource directory in case we need to lookup the resource in the file system * @return Actual resource URL * @throws JuRuntimeException If the resource is not valid */ private URL resourceToUrl(String resource, String resourceDir) { String actualResource = resource; // Perform {param} placeholder replacement if (resource.indexOf(DataSet.PARAM_POSTFIX) > 0) { String parameterizedTestName = this.getParameterizedTestName(); AssertUtil.assertNotNull("Doesn't seem to be parameterized test: " + this.testMethodReadableName, parameterizedTestName); actualResource = actualResource.replace(DataSet.PARAM_POSTFIX, "[" + parameterizedTestName + "]"); } URL url = null; if (!JuUtils.getJuPropertyChain().get("ju-testing.export.compareToResource", Boolean.class) && !StringUtils.isEmpty(resourceDir)) { // Lookup resource in file system Path p = Paths.get(this.getLocalRoot(), resourceDir, actualResource); url = JuUrl.toUrl(p); } else { // Lookup resource as (classpath) resource url = JuUrl.resource().relativeTo(this.getTestClass()).get(actualResource); if (url == null) url = JuUrl.resource(actualResource); } if (url == null) { throw new JuRuntimeException(String.format("Couldn't find resource %s, relative to class %s" , actualResource , this.getTestClass())); } return url; } /** * Gets the local root directory used to resolve resource locations. *

* Can be overridden by extending classes to provide a different root. * @return Root location for resource lookup on the filesystem */ protected String getLocalRoot() { return "."; } /** * Get the name of the parameterized test. * @return Parameterized test name or null if the test is not parameterized. */ private String getParameterizedTestName() { if (this.testMethodReadableName.indexOf("[") < 0 || !this.testMethodReadableName.endsWith("]")) { return null; } else { return this.testMethodReadableName.substring(this.testMethodReadableName.indexOf("[") + 1 , this.testMethodReadableName.length() - 1); } } /** * Extending classes can override this method to perform initialization on the * test class before the test method is invoked. * @param instance */ protected void initTestClass(Object instance) { } public final void executePostServerCode(JuEmUtil emUtil) throws Exception { // Execute post server code for (PostServerCode code : this.postServerCodeAnnos) { Class codeClass = null; if (code.value() == PostServerCode.DEFAULT_SERVER_CODE.class) { String verifierName = StringUtils.capitalize(this.testMethodName + "_code"); Class defaultVerifier = ReflectUtils.getInnerClass(this.getTestClass(), verifierName); AssertUtil.assertNotNull(String.format("Couldn't find Verifier %s as inner class of %s. Make sure it exists and is public static." , verifierName, this.getTestClass()) , defaultVerifier); codeClass = defaultVerifier; } else { codeClass = code.value(); } this.runServerCode(codeClass, emUtil.getEm()); } } public final void executePostTestAnnotations(JuEmUtil emUtil) throws Exception { // Process DataSetExport annotation. We'll just consider the first annotation. Document doc = null; if (this.dataSetExportAnnos.size() > 0) { DataSetExport dataSetExport = this.dataSetExportAnnos.get(0); // Get file name String targetFileName = dataSetExport.exportName(); if (StringUtils.isEmpty(targetFileName)) { // Construct name using class and method name targetFileName = String.format("%s_%s.xml" ,this.getTestClass().getSimpleName() , JuStringUtils.removeNonAlphabeticalLeadingCharacters(this.testMethodReadableName)); } URL tablesDataSestUrl = JuUrl.resource().relativeTo(this.getTestClass()).get(dataSetExport.tablesDataSet()); if (tablesDataSestUrl == null) tablesDataSestUrl = JuUrl.resource(dataSetExport.tablesDataSet()); ExportBuilder eb = new DbDataUtil(emUtil).buildExport() .addTablesByDataSet(tablesDataSestUrl, true); doc = eb.writeToXmlDocument(); if (dataSetExport.doPhysicalExport()) { if (JuUtils.getJuPropertyChain().get("ju-testing.export.compareToResource", Boolean.class, true)) { // Perform export in-memory and compare to resource String resourcePrefix = dataSetExport.resourcePrefix(); String resourcePath = resourcePrefix + "/" + targetFileName; URL resourceUrl = JuUrl.singleResource(resourcePath); String resourceString = new IOUtil().loadTextFromUrl(resourceUrl); String xmlString = eb.writeToXmlString(); logger.debug("Comparing DB export to resource {}", resourceUrl); Assert.assertEquals(resourceString, xmlString); } else { // Perform export to file String targetDirName = dataSetExport.targetDir(); // Create target directory Path targetDirPath = Paths.get(this.getLocalRoot(), targetDirName); Files.createDirectories(targetDirPath); // Build file path Path targetFilePath = targetDirPath.resolve(targetFileName); eb.writeToXmlFile(targetFilePath); } } else { // Log XML if (logger.isInfoEnabled()) { XString xs = new XString(targetFileName); xs.newLine(); xs.addLine(XmlUtils.toString(doc, true, true)); logger.info(xs.toString()); } } if (this.dataSetExportAnnos.size() > 1) { logger.warn("Ignoring DataSetExport annotations as only first is processed"); } } // Run data verifiers (provided the test method and data set export has succeeded) List verifiers = new ArrayList(); // Check for programmatic verifiers for (DataVerify verify : this.dataVerifyAnnos) { Class verifierClass = null; if (verify.value() == DataVerify.DEFAULT_DATA_VERIFIER.class) { String verifierName = StringUtils.capitalize(JuStringUtils.removeNonAlphabeticalLeadingCharacters(this.testMethodName)); Class defaultVerifier = ReflectUtils.getInnerClass(this.getTestClass(), verifierName); AssertUtil.assertNotNull( String.format("Couldn't find Verifier %s as inner class of %s. Make sure it exists and is public static." , verifierName, this.getTestClass()) , defaultVerifier); verifierClass = defaultVerifier; } else { verifierClass = verify.value(); } verifiers.add(this.createVerifier(verifierClass, emUtil.getEm(), doc)); } // Run verifiers for (DataVerifier verifier : verifiers) { verifier.verify(); } } private void runServerCode(Class codeClass, EntityManager em) throws Exception { AssertUtil.assertTrue("Code class must be of type ServerCode: " + codeClass.getName(), ServerCode.class.isAssignableFrom(codeClass)); ServerCode code = (ServerCode) ReflectUtils.newInstance(codeClass, false); code.init(em); try { code.execute(); } catch (Exception ex) { this.handleServerThrowable(ex); } } /** * Handle Server throwables to make sure we can send them to the client. * @param t Throwable * @throws T Handled throwable (may be the same or a converted Throwable) */ protected final void handleServerThrowable(T t) throws T { // Handle non-serializable exceptions if (!IOUtil.isSerializable(t)) { // If we have an assumption failure wrapped in an InvocationTargetException, rethrow a new // AssumptionViolatedException that just containing the message InvocationTargetException ite = JuObjectUtils.as(t, InvocationTargetException.class); if (ite != null && ite.getTargetException() instanceof AssumptionViolatedException) { throw new AssumptionViolatedException(ite.getTargetException().getMessage()); } // Use cause (if possible / serializable) Throwable cause = IOUtil.isSerializable(t.getCause()) ? t.getCause() : new JuException("Original cause was non-serializable. Check server for details."); throw new JuRuntimeException("%s (Original Exception %s was non-serializable)", cause, t.getMessage(), t.getClass().getName()); } else { throw t; } } private DataVerifier createVerifier(Class verifierClass, EntityManager em, Document doc) { AssertUtil.assertTrue("Verifier must be of type DataVerifier: " + verifierClass.getName(), DataVerifier.class.isAssignableFrom(verifierClass)); DataVerifier verifier = (DataVerifier) ReflectUtils.newInstance(verifierClass, false); verifier.init(em, doc); this.initVerifier(verifier); return verifier; } /** * Extending classes can override this method to perform additional initialization on the DataVerifier. * @param verifier DataVerifier */ protected void initVerifier(DataVerifier verifier) { } @Override public String toString() { return String.format("%s.%s()", this.testClassName, this.testMethodReadableName); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy