| /* |
| * 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."); |
| } |
| } |
| } |