blob: 8ec32ba529855b1a69ca15f0cf2ea2855cb56c4c [file] [log] [blame]
/*
* Copyright 2006-2012 The Apache Software Foundation.
*
* Licensed 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
*
* https://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.jdo.exectck;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.io.FileUtils;
import org.apache.jdo.exectck.Utilities.InvocationResult;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
/**
* Goal that runs the JDO TCK against the Reference Implementation (RI) or an implementation under
* test (IUT).
*/
@Mojo(name = "runtck")
public class RunTCK extends AbstractTCKMojo {
private static final String TCK_PARAM_ON_FAILURE_FAIL_FAST = "failFast";
private static final String TCK_PARAM_ON_FAILURE_FAIL_EVENTUALLY = "failGoal";
private static final String TCK_PARAM_ON_FAILURE_LOG_ONLY = "logOnly";
private static final String TCK_LOG_FILE = "tck.txt";
/** To skip running of TCK, set to false. */
@Parameter(property = "jdo.tck.doRunTCK", defaultValue = "true", required = true)
private boolean doRunTCK;
/** To run the RunTCK plugin goal in verbose mode. */
@Parameter(property = "jdo.tck.runTCKVerbose", defaultValue = "false", required = true)
private boolean runtckVerbose;
/** Define handling of TCK failures. */
@Parameter(property = "jdo.tck.onFailure", defaultValue = "failGoal", required = true)
private String onFailure;
/** Run the TCK in a debugger. */
@Parameter(property = "jdo.tck.debugTCK", defaultValue = "false", required = true)
private boolean debugTCK;
/** Location of third party libraries such as JNDI. */
@Parameter(
property = "project.lib.ext.directory",
defaultValue = "${basedir}/../lib/ext",
required = true)
private String extLibsDirectory;
/** To skip jndi PMF Tests set to true. */
@Parameter(property = "jdo.tck.skipJndi", defaultValue = "false", required = true)
private boolean skipJndi;
/** Location of implementation log file. */
@Parameter(
property = "jdo.tck.impl.logfile",
defaultValue = "${user.dir}/datanucleus.txt",
required = true)
private String implLogFile;
/** Name of file in src/conf containing pmf properties. */
@Parameter(property = "jdo.tck.pmfproperties", defaultValue = "jdori-pmf.properties")
private String pmfProperties;
/**
* Name of file in src/conf containing property jdo.tck.exclude, whose value is a list of files to
* be excluded from testing.
*/
@Parameter(property = "jdo.tck.excludefile", defaultValue = "exclude.list", required = true)
private String exclude;
/** Run the TCK tests in verbose mode. */
@Parameter(property = "jdo.tck.verbose", defaultValue = "false")
private String verbose;
/** To retain test output for debugging, set to false. */
@Parameter(property = "jdo.tck.cleanupaftertest", defaultValue = "true")
private String cleanupaftertest;
/** Properties to use in accessing database. */
@Parameter(
property = "database.runtck.sysproperties",
defaultValue = "-Dderby.system.home=${basedir}/target/database/derby")
private String dbproperties; // NOTE: only allows for one db
/** Properties to use in accessing database. */
@Parameter(
property = "jdo.tck.signaturefile",
defaultValue = "${basedir}/src/main/resources/conf/jdo-signatures.txt")
private String signaturefile;
/** JVM properties. */
@Parameter(property = "jdo.tck.jvmproperties", defaultValue = "-Xmx512m")
private String jvmproperties;
/** User-supplied arguments for debug directives. */
@Parameter(
property = "jdo.tck.debug.jvmargs",
defaultValue =
"-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=${jdo.tck.debug.port}")
private String debugDirectives;
/** Class used to run a batch of tests. */
@Parameter(
property = "jdo.tck.testrunnerclass",
defaultValue = "org.apache.jdo.tck.util.BatchTestRunner",
required = true)
private String testRunnerClass;
/** Class used to output test result and configuration information. */
@Parameter(
property = "jdo.tck.resultprinterclass",
defaultValue = "org.apache.jdo.tck.util.BatchResultPrinter",
required = true)
private String resultPrinterClass;
/**
* Helper method returning the trimmed value of the specified property.
*
* @param props the Properties object
* @param key the key of the property to be returned
* @return the trimmed property value or the empty string if the property is not defined. +
*/
private String getTrimmedPropertyValue(Properties props, String key) {
String value = props.getProperty(key);
return value == null ? "" : value.trim();
}
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
if (!doRunTCK) {
System.out.println("Skipping RunTCK goal!");
return;
}
Properties props = null;
boolean alreadyran = false;
String runonce = "false";
List<String> propsString = new ArrayList<String>();
List<String> command;
String cpString = null;
InvocationResult result;
File fromFile = null;
File toFile = null;
if (impl.equals("iut")) {
pmfProperties = "iut-pmf.properties";
}
if (cfgs == null) {
if (cfgList != null) {
cfgs = new ArrayList<String>();
PropertyUtils.string2List(cfgList, (List<String>) cfgs);
} else {
// Fallback to "src/conf/configurations.list"
setCfgListFromFile();
if (cfgList != null) {
cfgs = new ArrayList<String>();
PropertyUtils.string2List(cfgList, (List<String>) cfgs);
}
if (cfgList == null) {
throw new MojoExecutionException(
"Could not find configurations to run TCK. "
+ "Set cfgList parameter on command line or cfgs in pom.xml");
}
}
}
PropertyUtils.string2Set(dblist, dbs);
PropertyUtils.string2Set(identitytypes, idtypes);
System.out.println(
"*>TCK to be run for implementation '"
+ impl
+ "' on \n"
+ " configurations: "
+ cfgs.toString()
+ "\n"
+ " databases: "
+ dbs.toString()
+ "\n"
+ " identitytypes: "
+ identitytypes.toString());
// Properties required for test execution
System.out.println("cleanupaftertest is " + cleanupaftertest);
propsString.add("-DResultPrinterClass=" + resultPrinterClass);
propsString.add("-Dverbose=" + verbose);
propsString.add("-Djdo.tck.cleanupaftertest=" + cleanupaftertest);
propsString.add("-Djdo.tck.skipJndi=" + skipJndi);
propsString.add(
"-DPMFProperties="
+ buildDirectory
+ File.separator
+ "classes"
+ File.separator
+ pmfProperties);
propsString.add(
"-DPMF2Properties="
+ buildDirectory
+ File.separator
+ "classes"
+ File.separator
+ pmfProperties);
String excludeFile = confDirectory + File.separator + exclude;
propsString.add(
"-Djdo.tck.exclude="
+ getTrimmedPropertyValue(PropertyUtils.getProperties(excludeFile), "jdo.tck.exclude"));
// Create configuration log directory
String timestamp = Utilities.now();
String thisLogDir = logsDirectory + File.separator + timestamp + File.separator;
String cfgDirName = thisLogDir + "configuration";
File cfgDir = new File(cfgDirName);
if (!(cfgDir.exists()) && !(cfgDir.mkdirs())) {
throw new MojoExecutionException("Failed to create directory " + cfgDirName);
}
propsString.add("-Djdo.tck.log.directory=" + thisLogDir);
try {
copyLog4j2ConfigurationFile();
} catch (IOException ex) {
Logger.getLogger(RunTCK.class.getName()).log(Level.SEVERE, null, ex);
}
// Copy JDO config files to classes dir
try {
fromFile = new File(confDirectory + File.separator + impl + "-jdoconfig.xml");
toFile =
new File(
buildDirectory
+ File.separator
+ "classes"
+ File.separator
+ "META-INF"
+ File.separator
+ "jdoconfig.xml");
FileUtils.copyFile(fromFile, toFile);
fromFile = new File(confDirectory + File.separator + impl + "-persistence.xml");
toFile =
new File(
buildDirectory
+ File.separator
+ "classes"
+ File.separator
+ "META-INF"
+ File.separator
+ "persistence.xml");
FileUtils.copyFile(fromFile, toFile);
} catch (IOException ex) {
Logger.getLogger(RunTCK.class.getName()).log(Level.SEVERE, null, ex);
}
// Get ClassLoader URLs to build classpath below
URL[] cpURLs = ((URLClassLoader) Thread.currentThread().getContextClassLoader()).getURLs();
ArrayList<URL> urlList = new ArrayList<URL>(Arrays.asList(cpURLs));
// Get contents of pmf properties file to build new file below
String pmfPropsReadFileName = confDirectory + File.separator + pmfProperties;
String defaultPropsContents = "";
try {
defaultPropsContents = Utilities.readFile(pmfPropsReadFileName);
} catch (IOException ex) {
Logger.getLogger(RunTCK.class.getName()).log(Level.SEVERE, null, ex);
}
// Reset logfile content (may not be empty if previous run crashed)
resetFileContent(implLogFile);
resetFileContent(TCK_LOG_FILE);
int failureCount = 0;
for (String db : dbs) {
System.setProperty("jdo.tck.database", db);
alreadyran = false;
for (String idtype : idtypes) {
List<String> idPropsString = new ArrayList<String>();
idPropsString.addAll(propsString);
idPropsString.add("-Djdo.tck.identitytype=" + idtype);
String enhancedDirName =
buildDirectory
+ File.separator
+ "enhanced"
+ File.separator
+ impl
+ File.separator
+ idtype
+ File.separator;
File enhancedDir = new File(enhancedDirName);
if (!(enhancedDir.exists())) {
throw new MojoExecutionException(
"Could not find enhanced directory "
+ enhancedDirName
+ ". Execute Enhance goal before RunTCK.");
}
// Set classpath string: add new entries to URLS from loader
ArrayList<URL> cpList = new ArrayList<URL>();
cpList.addAll(urlList);
try {
URL url1 = enhancedDir.toURI().toURL();
URL url2 =
new File(buildDirectory + File.separator + "classes" + File.separator)
.toURI()
.toURL();
if (runtckVerbose) {
System.out.println("url2 is " + url2.toString());
}
cpList.add(url1);
cpList.add(url2);
String[] jars = {"jar"};
Iterator<File> fi = FileUtils.iterateFiles(new File(extLibsDirectory), jars, true);
while (fi.hasNext()) {
cpList.add(fi.next().toURI().toURL());
}
for (String dependency : this.dependencyClasspath.split(File.pathSeparator)) {
cpList.add(new File(dependency).toURI().toURL());
}
} catch (MalformedURLException ex) {
ex.printStackTrace();
Logger.getLogger(RunTCK.class.getName()).log(Level.SEVERE, null, ex);
}
cpString = Utilities.urls2ClasspathString(cpList);
if (runtckVerbose) {
System.out.println("\nClasspath is " + cpString);
}
for (String cfg : cfgs) {
List<String> cfgPropsString = new ArrayList<String>();
cfgPropsString.addAll(idPropsString);
// Parse conf file and set properties String
props = PropertyUtils.getProperties(confDirectory + File.separator + cfg);
cfgPropsString.add(
"-Djdo.tck.testdata=" + getTrimmedPropertyValue(props, "jdo.tck.testdata"));
cfgPropsString.add(
"-Djdo.tck.standarddata=" + getTrimmedPropertyValue(props, "jdo.tck.standarddata"));
cfgPropsString.add(
"-Djdo.tck.mapping.companyfactory="
+ getTrimmedPropertyValue(props, "jdo.tck.mapping.companyfactory"));
// innerPropsString.append("-Djdo.tck.description=\"" +
// props.getProperty("jdo.tck.description") + "\"");
cfgPropsString.add(
"-Djdo.tck.requiredOptions="
+ getTrimmedPropertyValue(props, "jdo.tck.requiredOptions"));
cfgPropsString.add("-Djdo.tck.signaturefile=" + signaturefile);
String mapping = getTrimmedPropertyValue(props, "jdo.tck.mapping");
if (mapping == null) {
throw new MojoExecutionException("Could not find mapping value in conf file: " + cfg);
}
String classes = getTrimmedPropertyValue(props, "jdo.tck.classes");
String excludeList =
getTrimmedPropertyValue(PropertyUtils.getProperties(excludeFile), "jdo.tck.exclude");
if (classes == null) {
throw new MojoExecutionException("Could not find classes value in conf file: " + cfg);
}
classes = Utilities.removeSubstrs(classes, excludeList);
if (classes.equals("")) {
System.out.println("Skipping configuration " + cfg + ": classes excluded");
continue;
}
List<String> classesList = Arrays.asList(classes.split(" "));
cfgPropsString.add("-Djdo.tck.schemaname=" + idtype + mapping);
cfgPropsString.add("-Djdo.tck.cfg=" + cfg);
runonce = getTrimmedPropertyValue(props, "runOnce");
runonce = (runonce == null) ? "false" : runonce;
// Add Mapping and schemaname to properties file
StringBuffer propsFileData = new StringBuffer();
propsFileData.append("\n### Properties below added by maven 2 goal RunTCK.jdori");
propsFileData.append("\njavax.jdo.mapping.Schema=" + idtype + mapping);
mapping = (mapping.equals("0")) ? "" : mapping;
propsFileData.append("\njavax.jdo.option.Mapping=standard" + mapping);
propsFileData.append("\n");
String pmfPropsWriteFileName =
buildDirectory + File.separator + "classes" + File.separator + pmfProperties;
try {
BufferedWriter out = new BufferedWriter(new FileWriter(pmfPropsWriteFileName, false));
out.write(defaultPropsContents + propsFileData.toString());
out.close();
} catch (IOException ex) {
Logger.getLogger(RunTCK.class.getName()).log(Level.SEVERE, null, ex);
}
// build command line string
command = new ArrayList<String>();
command.add("java");
command.add("-cp");
command.add(cpString);
command.addAll(cfgPropsString);
command.add(dbproperties);
command.add(jvmproperties);
if (debugTCK) {
command.add(debugDirectives);
}
command.add(testRunnerClass);
command.addAll(classesList);
if (runonce.equals("true") && alreadyran) {
continue;
}
if (debugTCK) {
System.out.println("Using debug arguments: \n" + debugDirectives);
}
// invoke class runner
System.out.print(
"*> Running tests for "
+ cfg
+ " with "
+ idtype
+ " on '"
+ db
+ "'"
+ " mapping="
+ mapping
+ " ... ");
try {
result = (new Utilities()).invokeTest(command);
if (result.getExitValue() == 0) {
System.out.println("success");
} else {
System.out.println("FAIL");
failureCount++;
}
if (runtckVerbose) {
System.out.println("\nCommand line is: \n" + command.toString());
System.out.println("Test exit value is " + result.getExitValue());
System.out.println("Test result output:\n" + result.getOutputString());
System.out.println("Test result error:\n" + result.getErrorString());
}
} catch (java.lang.RuntimeException re) {
System.out.println("Exception on command " + command);
}
// Move log to per-test location
String idname = "dsid";
if (idtype.trim().equals("applicationidentity")) {
idname = "app";
}
String configName = cfg;
if (cfg.indexOf('.') > 0) {
configName = configName.substring(0, cfg.indexOf('.'));
}
String testLogFilename = thisLogDir + idname + "-" + configName + "-" + impl + ".txt";
try {
File logFile = new File(implLogFile);
FileUtils.copyFile(logFile, new File(testLogFilename));
resetFileContent(implLogFile);
} catch (Exception e) {
System.out.println(">> Error copying implementation log file: " + e.getMessage());
}
String tckLogFilename = thisLogDir + idname + "-" + configName + "-" + TCK_LOG_FILE;
try {
File logFile = new File(TCK_LOG_FILE);
FileUtils.copyFile(logFile, new File(tckLogFilename));
resetFileContent(TCK_LOG_FILE);
} catch (Exception e) {
System.out.println(">> Error copying tck log file: " + e.getMessage());
}
if (runonce.equals("true")) {
alreadyran = true;
}
if (TCK_PARAM_ON_FAILURE_FAIL_FAST.equals(onFailure) && failureCount > 0) {
break;
}
}
if (TCK_PARAM_ON_FAILURE_FAIL_FAST.equals(onFailure) && failureCount > 0) {
break;
}
}
if (TCK_PARAM_ON_FAILURE_FAIL_FAST.equals(onFailure) && failureCount > 0) {
break;
}
}
// Remove log file
try {
FileUtils.forceDeleteOnExit(new File(implLogFile));
} catch (Exception e) {
System.out.println(">> Error deleting log file: " + e.getMessage());
}
try {
FileUtils.forceDeleteOnExit(new File(TCK_LOG_FILE));
} catch (Exception e) {
System.out.println(">> Error deleting log file: " + e.getMessage());
}
// Output results
command = new ArrayList<String>();
command.add("java");
command.add("-cp");
command.add(cpString);
command.add("org.apache.jdo.tck.util.ResultSummary");
command.add(thisLogDir);
result = (new Utilities()).invokeTest(command, new File(buildDirectory));
// Create system configuration description file
command.set(3, "org.apache.jdo.tck.util.SystemCfgSummary");
command.set(4, cfgDirName);
command.add("system_config.txt");
result = (new Utilities()).invokeTest(command, new File(buildDirectory));
// Copy metadata from enhanced to configuration logs directory
for (String idtype : idtypes) {
String fromDirName =
buildDirectory
+ File.separator
+ "enhanced"
+ File.separator
+ impl
+ File.separator
+ idtype
+ File.separator;
String[] metadataExtensions = {"jdo", "jdoquery", "orm", "xml", "properties"};
String fromFileName = null;
String pkgName = null;
int startIdx = -1;
// iterator over list of abs name of metadata files in src
Iterator<File> fi = FileUtils.iterateFiles(new File(fromDirName), metadataExtensions, true);
while (fi.hasNext()) {
try {
fromFile = fi.next();
fromFileName = fromFile.toString();
if ((startIdx = fromFileName.indexOf(idtype + File.separator)) > -1) {
// fully specified name of file (idtype + package + filename)
pkgName = fromFileName.substring(startIdx);
toFile = new File(cfgDirName + File.separator + pkgName);
FileUtils.copyFile(fromFile, toFile);
}
} catch (IOException ex) {
throw new MojoExecutionException(
"Failed to copy files from "
+ fromFileName
+ " to "
+ toFile.toString()
+ ": "
+ ex.getLocalizedMessage());
}
}
}
if (TCK_PARAM_ON_FAILURE_FAIL_FAST.equals(onFailure) && failureCount > 0) {
throw new MojoExecutionException("Aborted TCK test run after 1 failure.");
}
if (TCK_PARAM_ON_FAILURE_FAIL_EVENTUALLY.equals(onFailure) && failureCount > 0) {
throw new MojoExecutionException("There were " + failureCount + " TCK test failures.");
}
}
}