/* * Copyright [1999-2015] Wellcome Trust Sanger Institute and the EMBL-European Bioinformatics Institute * Copyright [2016-2017] EMBL-European Bioinformatics Institute * * 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 * * 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.ensembl.healthcheck; import java.io.File; import java.sql.Connection; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.commons.lang.StringUtils; import org.ensembl.healthcheck.ReporterFactory.ReporterType; import org.ensembl.healthcheck.TestRegistryFactory.TestRegistryType; import org.ensembl.healthcheck.configuration.ConfigurationUserParameters; import org.ensembl.healthcheck.configuration.ConfigureConfiguration; import org.ensembl.healthcheck.configuration.ConfigureHealthcheckDatabase; import org.ensembl.healthcheck.configuration.ConfigureHost; import org.ensembl.healthcheck.configuration.ConfigureTestGroups; import org.ensembl.healthcheck.configurationmanager.ConfigurationDumper; import org.ensembl.healthcheck.configurationmanager.ConfigurationException; import org.ensembl.healthcheck.configurationmanager.ConfigurationFactory; import org.ensembl.healthcheck.configurationmanager.ConfigurationFactory.ConfigurationType; import org.ensembl.healthcheck.testcase.EnsTestCase; import org.ensembl.healthcheck.testcase.MultiDatabaseTestCase; import org.ensembl.healthcheck.testcase.OrderedDatabaseTestCase; import org.ensembl.healthcheck.testcase.SingleDatabaseTestCase; import org.ensembl.healthcheck.util.ConnectionBasedSqlTemplateImpl; import org.ensembl.healthcheck.util.CreateHealthCheckDB; import org.ensembl.healthcheck.util.DBUtils; import org.ensembl.healthcheck.util.SqlTemplate; /** * * Runs test which can be configured on the command line and stores them in a * database. * */ public class ConfigurableTestRunner extends TestRunner { /** * */ private static final String GET_DIVISION_DBS = "select division_db.db_name from division_db " + "join division using (division_id) where division_db.is_current=1 and (division.name=? or division.shortname=?)"; /** * */ private static final String GET_DIVISION_SPECIES_DBS = "select " + "concat(species.db_name,'_',db.db_type,'_',db.db_release,'_',db.db_assembly) from db " + "join species using (species_id) join division_species using (species_id) " + "join division using (division_id) where db.is_current=1 and species.is_current=1 and (division.name=? or division.shortname=?)"; // static final Logger log = Logger.getLogger(ConfigurableTestRunner.class // .getCanonicalName()); /** * Name of a properties file from which parameters can be taken, if they * have not been set by the user anywhere. */ protected final static String DEFAULT_PROPERTIES_FILE = "database.defaults.properties"; /** * The configuration object from which configuration information is * retrieved. */ protected final ConfigurationUserParameters configuration; /** * The type of reporter used. */ protected final ReporterType reporterType; /** * The type of test registry used. */ protected final TestRegistryType testRegistryType; protected final SystemPropertySetter systemPropertySetter; protected final TestRegistry testRegistry; protected final Reporter reporter; /** * @param configuration * - A configuration object of type ConfigurationUserParameters * * Creates a ConfigurableTestRunner using the parameters from the * configuration object. * */ public ConfigurableTestRunner(ConfigurationUserParameters configuration) { logger.config("Using classpath: \n\n" + Debug.classpathToString()); this.configuration = configuration; this.systemPropertySetter = new SystemPropertySetter(configuration); this.testRegistryType = getTestRegistryType(configuration .getTestRegistryType()); this.reporterType = getReporterType(configuration.getReporterType()); this.testRegistry = getTestRegistry(this.testRegistryType, configuration); this.reporter = getReporter(this.reporterType); String repair = configuration.getRepair().toLowerCase(); this.doRepair = (repair.equals("do") || repair.equals("1") || repair.equals("yes")); this.showRepair = (repair.equals("show")); /* configuration is cast to a lesser interface to compartmentalise options for * different packages. * */ DBUtils.setHostConfiguration((ConfigureHost) configuration); } /** * @param args * - The command line arguments * * Creates a ConfigurableTestRunner using command line arguments. * The configuration parameters are taken from the command line * and from the properties files that can be specifies on the * command line with the --conf option. The * DEFAULT_PROPERTIES_FILE is added as well. * */ public ConfigurableTestRunner(String[] args) { this(createConfigurationObj(args)); } /** * Used for created layered constructors. */ protected static ReporterType getReporterType(String reporterType) { ReporterType r; try { r = ReporterType.valueOf(reporterType.toUpperCase()); } catch (IllegalArgumentException e) { throw new ConfigurationException( "Parameter reporterType has been set to an illegal value: " + reporterType); } return r; } /** * Used for created layered constructors. */ protected static TestRegistryType getTestRegistryType( String testRegistryType) { TestRegistryType t; try { t = TestRegistryType.valueOf(testRegistryType); } catch (IllegalArgumentException e) { throw new ConfigurationException( "Parameter reportertype has been set to an illegal value: " + testRegistryType); } return t; } /** * @return TestRegistry * * Creates a TestRegistry. The type of TestRegistry is determined by * the configuration object. * */ protected TestRegistry getTestRegistry( TestRegistryType testRegistryType, ConfigureTestGroups configuration) { TestRegistryFactory testRegistryFactory = new TestRegistryFactory( // Downcast of configuration, the TestRegistryFactory and the // Registries it produces from it only have access to the // params in the ConfigureTestGroups interface // (ConfigureTestGroups) configuration); logger.config("Using test registry of type: " + testRegistryType); TestRegistry testRegistry = null; try { testRegistry = testRegistryFactory .getTestRegistry(testRegistryType); } catch (TestRegistryCreationException e) { throw new ConfigurationException(e); } logger.config("Using testregistry with this configuration:\n" + testRegistry.toString()); return testRegistry; } /** * @return Reporter * * Creates a reporter. The type of reporter is determined by the * configuration object. * */ protected Reporter getReporter(ReporterType reporterType) { Reporter reporter = new ReporterFactory().getTestReporter(reporterType); logger.config("Using reporter of type: " + reporterType); return reporter; } /** * Used for creating layered constructors. */ protected static ConfigurationUserParameters createConfigurationObj( String[] args) { Logger logger = Logger.getLogger("ConfigurationUserParameters"); // A temporary configuration object for accessing the command line // parameters in which the user configures where the configuration // files are located. Since only this information is of interest at // this point the configuration object is subcast to the // ConfigureConfiguration interface. // ConfigureConfiguration conf = new ConfigurationFactory<ConfigurationUserParameters>( ConfigurationUserParameters.class, (Object[]) args) .getConfiguration(ConfigurationType.Commandline); // Get the list of property files the user specified and add the // default property file to it. // List<File> propertyFileNames = new ArrayList<File>(); // The user is not required to provide a property file. In this case // only the // command line arguments and the default properties file will be used. // if (conf.isConf()) { propertyFileNames.addAll(conf.getConf()); } propertyFileNames.add(new File(DEFAULT_PROPERTIES_FILE)); // Use this to create the final configuration object. ConfigurationFactory<ConfigurationUserParameters> confFact = new ConfigurationFactory( ConfigurationUserParameters.class, args, propertyFileNames); // Users may want to know what went into the the configuration of this, // So some information on where the configuration information came // from is compiled here and sent to the logger. // StringBuffer msg = new StringBuffer(); // // Create a few logging messages about the configuration information // that that will be used. Useful for debugging. // // Information about the command line arguments and which property files // are used msg.append("Creating configuration for this run.\n\n"); msg.append("The following arguments were specified on the command line:\n"); for (String arg : args) { if (arg.startsWith("-")) { msg.append("\n"); } msg.append(" " + arg); } msg.append("\n\nThe following property files will be used:\n\n"); for (File propertyFileName : propertyFileNames) { msg.append(" - " + propertyFileName.getName() + "\n"); } logger.config(msg.toString()); // Finally create the configuration object. ConfigurationUserParameters configuration = confFact .getConfiguration(ConfigurationType.Cascading); // Show user the final configuration settings that will be used. logger.config("The following settings will be used:\n\n" + new ConfigurationDumper<ConfigurationUserParameters>() .dump(configuration)); return configuration; } public static void main(String[] args) { ConfigurableTestRunner configurableTestRunner = new ConfigurableTestRunner( args); try { configurableTestRunner.run(); } catch (ConfigurationException e) { configurableTestRunner.logger.log(Level.SEVERE, e.getMessage()); } } protected DatabaseServer connectToDatabase(ConfigureHost conf) { DatabaseServer ds = new DatabaseServer(conf.getHost(), conf.getPort(), conf.getUser(), conf.getPassword(), conf.getDriver()); return ds; } List<Class<? extends EnsTestCase>> getAllRegisteredTestClasses(TestRegistry testRegistry) { List<Class<? extends EnsTestCase>> registeredClasses = new ArrayList<Class<? extends EnsTestCase>>(); for (EnsTestCase currentTestCase : testRegistry.getAll()) { registeredClasses.add(currentTestCase.getClass()); } return registeredClasses; } protected void run() { TestRegistry testRegistry = this.testRegistry; Reporter reporter = this.reporter; String outputLevelString = configuration.getOutputLevel(); setOutputLevel(outputLevelString); ReportManager.setOutputLevel(outputLevel); ReportManager.setReporter(reporter); DatabaseServer ds = connectToDatabase(configuration); if (this.reporterType == ReporterType.DATABASE && configuration.isEndSession()) { logger.info("Finishing reporter session"); systemPropertySetter.setPropertiesForReportManager_connectToOutputDatabase(); ReportManager.connectToOutputDatabase(); ReportManager.setSessionID(Long.valueOf(configuration.getEndSession())); ReportManager.endDatabaseSession(); logger.info("Finished reporter session"); return; } List<String> testDatabases = new ArrayList<String>(getTestDatabases()); Species globalSpecies = null; if (configuration.isSpecies()) { globalSpecies = Species.resolveAlias(configuration.getSpecies()); if (globalSpecies != Species.UNKNOWN) { logger.info("Will override guessed species with " + globalSpecies + " for all databases"); } else { String msg = "Argument " + configuration.getSpecies() + " to -species not recognised"; logger.severe(msg); throw new ConfigurationException(msg); } } DatabaseType globalDatabaseType = null; if (configuration.isDbType()) { globalDatabaseType = DatabaseType.resolveAlias(configuration .getDbType()); if (globalDatabaseType != DatabaseType.UNKNOWN) { logger.info("Will override guessed database types with " + globalDatabaseType + " for all databases"); } else { String msg = "Argument " + configuration.getDbType() + " to -type not recognised"; logger.severe(msg); throw new ConfigurationException(msg); } } DatabaseRegistry databasesToTestRegistry = new DatabaseRegistry( testDatabases, globalDatabaseType, globalSpecies, false); if (databasesToTestRegistry.getAll().length == 0) { logger.warning("Warning: no databases configured!"); throw new RuntimeException("No databases configured"); } if (this.reporterType == ReporterType.DATABASE) { // Create the database to which tests will be written CreateHealthCheckDB c = new CreateHealthCheckDB( (ConfigureHealthcheckDatabase) configuration); boolean reportDatabaseExistsAlready = c .databaseExists(configuration.getOutputDatabase()); if (reportDatabaseExistsAlready) { logger.info("Reporting database " + configuration.getOutputDatabase() + " already exists, will reuse."); } else { logger.info("Reporting database " + configuration.getOutputDatabase() + " does not exist, will create."); c.run(); } systemPropertySetter .setPropertiesForReportManager_connectToOutputDatabase(); ReportManager.connectToOutputDatabase(); systemPropertySetter .setPropertiesForReportManager_createDatabaseSession(); if (configuration.isSessionID()) { ReportManager.reuseDatabaseSession(Long.valueOf(configuration.getSessionID())); } else { ReportManager.createDatabaseSession(); } } // When writing to a database, this must only be run after calling // // ReportManager.connectToOutputDatabase() // // Otherwise reporting problems won't work. // complainAboutDatabasesNotFound(databasesToTestRegistry, testDatabases); systemPropertySetter.setPropertiesForHealthchecks(); logger.info("Running tests"); List<Class<? extends EnsTestCase>> testsThrowingAnException = new ArrayList<Class<? extends EnsTestCase>>(); List<Class<? extends EnsTestCase>> testsSkippedLongRunning = new ArrayList<Class<? extends EnsTestCase>>(); List<Class<? extends EnsTestCase>> testsSkippedForUnknownReason = new ArrayList<Class<? extends EnsTestCase>>(); List<Class<? extends EnsTestCase>> testsApplyingToNoDb = findTestsApplyingToNoDb(testRegistry, databasesToTestRegistry); if (!testsApplyingToNoDb.isEmpty()) { ReportManager.problem( new TestRunnerSelfCheck(), "Skipped tests", "These tests apply to none of the databases that will be tested:\n" + testListToBulletPoints(testsApplyingToNoDb) ); } Map<Class<? extends EnsTestCase>, List<DatabaseRegistryEntry>> exceptionToDb; try { TestRunStats accounting = runAllTestsWithAccounting(databasesToTestRegistry, testRegistry, false); exceptionToDb = accounting.getExceptionToDb(); if (!exceptionToDb.isEmpty()) { for (Class<? extends EnsTestCase> currentTestClass : exceptionToDb.keySet()) { ReportManager.problem( new TestRunnerSelfCheck(), "Skipped tests", "The following test died with an exception: " + currentTestClass.getName() + "\n" + "on the following databases: \n" + dbreListToBulletPoints( exceptionToDb.get(currentTestClass) ) ); } } for (Class<? extends EnsTestCase> currentTestCase : accounting.getTrackCompletionStatus().keySet()) { if (accounting.getTrackCompletionStatus().get(currentTestCase).equals(TestRunStats.CompletionStatus.DIED_WITH_EXCEPTION)) { testsThrowingAnException.add(currentTestCase); } if (accounting.getTrackCompletionStatus().get(currentTestCase).equals(TestRunStats.CompletionStatus.SKIPPED_LONG_RUNNING)) { testsSkippedLongRunning.add(currentTestCase); } } testsSkippedForUnknownReason = getAllRegisteredTestClasses(testRegistry); testsSkippedForUnknownReason.removeAll(accounting.getTestsRun()); testsSkippedForUnknownReason.removeAll(testsThrowingAnException); testsSkippedForUnknownReason.removeAll(testsSkippedLongRunning); testsSkippedForUnknownReason.removeAll(testsApplyingToNoDb); } catch (Throwable e) { logger.severe("Execution of tests failed: " + e.getMessage()); logger.log(Level.FINE, "Execution of tests failed: " + e.getMessage(), e); } if (!testsSkippedForUnknownReason.isEmpty()) { ReportManager.problem( new TestRunnerSelfCheck(), "Skipped tests", "The following tests were skipped for no known reason:\n" + testListToBulletPoints(testsSkippedForUnknownReason) ); } if (!testsSkippedLongRunning.isEmpty()) { ReportManager.correct( new TestRunnerSelfCheck(), "Skipped tests", "The following tests were not run, because they are long running and the run was configured to skip these:\n" + testListToBulletPoints(testsSkippedLongRunning) ); } logger.info("Done running tests\n\n"); boolean printFailureText = true; if (this.reporterType == ReporterType.DATABASE) { if (!configuration.isSessionID()) { logger.info("Finishing reporter session"); ReportManager.endDatabaseSession(); logger.info("Finished reporter session"); } } else { logger.info("Printing output by test"); printReportsByTest(outputLevel, printFailureText); } } public static String getDefaultPropertiesFile() { return DEFAULT_PROPERTIES_FILE; } /** * <p> * Generates a Collection<String> of the databases to be tested. * </p> * * <p> * The databases to be tested are the ones specified explicitly via the * testDatabases parameter and the ones specified by the division * parameter. * </p> * * <p> * If a division parameter has been set, this method will try to query the * production database. If a production database has not been configured, * a ConfigurationException is thrown. * </p> * */ protected Collection<String> getTestDatabases() { Collection<String> dbs = new HashSet<String>(); if (configuration.isTestDatabases() && configuration.getTestDatabases().size() > 0) { for (String db : configuration.getTestDatabases()) { if (!StringUtils.isEmpty(db)) { dbs.add(db); } } } if (configuration.isDivisions() && configuration.getDivisions().size() > 0) { if (!configuration.isProductionDatabase() && !configuration.isOutputHost()) { throw new ConfigurationException( "Parameters production.database and output.host etc. must be set to use test_divisions"); } else { // don't want to have to do this, but need to query production // separately Connection conn = null; SqlTemplate template = null; try { conn = DBUtils.openConnection( configuration.getOutputDriver(), configuration.getOutputHost(), configuration.getOutputPort(), configuration.getOutputUser(), configuration.getOutputPassword(), configuration.getProductionDatabase()); template = new ConnectionBasedSqlTemplateImpl(conn); for (String division : configuration.getDivisions()) { dbs.addAll(template.queryForDefaultObjectList( GET_DIVISION_SPECIES_DBS, String.class, division, division)); dbs.addAll(template.queryForDefaultObjectList( GET_DIVISION_DBS, String.class, division, division)); } } catch (SQLException e) { throw new RuntimeException( "Could not open connection to production db " + configuration.getProductionDatabase(), e); } finally { DBUtils.closeQuietly(conn); } } } if (dbs.isEmpty()) { logger.warning( "No test databases found - Parameters test_databases or test_divisions have not been set - testing all databases..."); } return dbs; } /** * Run appropriate tests against databases. Also run show/repair methods if * the test implements the Repair interface and the appropriate flags are * set. * * @param databaseRegistry * The DatabaseRegistry to use. * @param testRegistry * The TestRegistry to use. * @param skipSlow * If true, skip long-running tests. */ protected TestRunStats runAllTestsWithAccounting(DatabaseRegistry databaseRegistry, TestRegistry testRegistry, boolean skipSlow) { logger.info("Running all tests with accounting"); int numberOfTestsRun = 0; HashSet<Class<? extends EnsTestCase>> testsRun = new HashSet<Class<? extends EnsTestCase>>(); Map<Class<? extends EnsTestCase>,TestRunStats.CompletionStatus> trackCompletionStatus = new HashMap<Class<? extends EnsTestCase>,TestRunStats.CompletionStatus>(); Map< Class<? extends EnsTestCase>, List<DatabaseRegistryEntry> > exceptionToDb = new HashMap< Class<? extends EnsTestCase>, List<DatabaseRegistryEntry> >(); // -------------------------------- // Single-database tests // run the appropriate tests on each of them for (DatabaseRegistryEntry database : databaseRegistry.getAll()) { logger.info("Processing database "+database.getName()); for (SingleDatabaseTestCase testCase : testRegistry.getAllSingle( groupsToRun, database.getType())) { if (!testCase.isLongRunning() || (testCase.isLongRunning() && !skipSlow)) { try { logger.info("Executing "+testCase.getTestName()+" on "+database.getName()); ReportManager.startTestCase(testCase, database); testCase.types(); boolean result = testCase.run(database); testsRun.add(testCase.getClass()); trackCompletionStatus.put(testCase.getClass(), TestRunStats.CompletionStatus.COMPLETED); ReportManager .finishTestCase(testCase, result, database); checkRepair(testCase, database); numberOfTestsRun++; logger.info("Completed executing "+testCase.getTestName()+" on "+database.getName()); } catch (Throwable e) { trackCompletionStatus.put(testCase.getClass(), TestRunStats.CompletionStatus.DIED_WITH_EXCEPTION); if (!exceptionToDb.containsKey(testCase.getClass())) { exceptionToDb.put(testCase.getClass(), new ArrayList<DatabaseRegistryEntry>()); } exceptionToDb.get(testCase.getClass()).add(database); String msg = "Could not execute test " + testCase.getName() + " on " + database.getName() + ": " + e.getMessage(); logger.log(Level.WARNING, msg, e); } } else { logger.info("Skipping long-running test " + testCase.getName()); trackCompletionStatus.put(testCase.getClass(), TestRunStats.CompletionStatus.SKIPPED_LONG_RUNNING); } } // foreach test } // foreach DB // -------------------------------- // Multi-database tests // here we just pass the whole DatabaseRegistry to each test // and let the test decide what to do for (MultiDatabaseTestCase testCase : testRegistry .getAllMulti(groupsToRun)) { if (!testCase.isLongRunning() || (testCase.isLongRunning() && !skipSlow)) { try { ReportManager.startTestCase(testCase, null); logger.info("Starting test " + testCase.getName() + " "); testCase.types(); boolean result = testCase.run(databaseRegistry); testsRun.add(testCase.getClass()); trackCompletionStatus.put(testCase.getClass(), TestRunStats.CompletionStatus.COMPLETED); ReportManager.finishTestCase(testCase, result, null); logger.info(testCase.getName() + " " + (result ? "PASSED" : "FAILED")); numberOfTestsRun++; } catch (Throwable e) { //TODO If we had a throwable then we should mark the test as failed String msg = "Could not execute test " + testCase.getName() + ": " + e.getMessage(); logger.log(Level.WARNING, msg, e); trackCompletionStatus.put(testCase.getClass(), TestRunStats.CompletionStatus.DIED_WITH_EXCEPTION); } } else { logger.info("Skipping long-running test " + testCase.getName()); trackCompletionStatus.put(testCase.getClass(), TestRunStats.CompletionStatus.SKIPPED_LONG_RUNNING); } } // foreach test // -------------------------------- // Ordered database tests // getAll() should give back databases in the order they were specified // on the command line DatabaseRegistryEntry[] orderedDatabases = databaseRegistry.getAll(); for (OrderedDatabaseTestCase testCase : testRegistry .getAllOrdered(groupsToRun)) { ReportManager.startTestCase(testCase, null); try { boolean result = testCase.run(orderedDatabases); testsRun.add(testCase.getClass()); ReportManager.finishTestCase(testCase, result, null); logger.info(testCase.getName() + " " + (result ? "PASSED" : "FAILED")); } catch (Throwable e) { //TODO If we had a throwable then we should mark the test as failed String msg = "Could not execute test " + testCase.getName() + ": " + e.getMessage(); logger.log(Level.WARNING, msg, e); } numberOfTestsRun++; } // foreach test // -------------------------------- if (numberOfTestsRun == 0) { logger.warning("Warning: no tests were run."); } return new TestRunStats(testsRun, trackCompletionStatus, exceptionToDb); } // runAllTests /** * <p> * Users specify the exact names of databases and these are used to * initialise the DatabaseRegistry object. * </p> * * <p> * The DatabaseRegistry however uses them as regular expressions to which * databases may match or not. If no matching database was found, it does * not complain about it. The database will appear as if it has passed. * </p> * * <p> * This is not the desired behaviour in the configurable testrunner. If * users misspell a database name, this should flag as an error. * </p> * * @param databasesToTestRegistry * @param testDatabases */ void complainAboutDatabasesNotFound(DatabaseRegistry databasesToTestRegistry, List<String> testDatabases) { HashSet<String> namesOfDbsFound = new HashSet<String>(); for (DatabaseRegistryEntry currentDRE: databasesToTestRegistry.getAll()) { namesOfDbsFound.add(currentDRE.getName()); } for (String dbName : testDatabases) { if (!namesOfDbsFound.contains(dbName)) { ReportManager.problem( new TestRunnerSelfCheck(), "Configuration problem", "Database " + dbName + " has been specified for testing, but it doesn't exist on the server!" ); } } } /** * <p> * Convert a list of tests into a string in which the names are listed * in bullet points. The list is sorted by class names. * </p> * * <p> * Useful for printing. * </p> * * @param listOfTests * @return Stringified version of the list as bullet points */ protected String testListToBulletPoints(List<Class<? extends EnsTestCase>> listOfTests) { StringBuffer missingTestToString = new StringBuffer(); // Sort list before printing. Otherwise they will appear in an // arbitrary order and appear as new every day on the admin site. // Collections.sort(listOfTests, new Comparator<Class<? extends EnsTestCase>>() { @Override public int compare( Class<? extends EnsTestCase> o1, Class<? extends EnsTestCase> o2) { return o1.getName().compareTo(o2.getName()); } }); for (Class<? extends EnsTestCase> currentMissingTest : listOfTests) { missingTestToString.append(" - " + currentMissingTest.getName() + "\n"); } return missingTestToString.toString(); } /** * <p> * Convert a list of DatabaseRegistryEntry into a string in which the * names are listed in bullet points. The list is sorted using the * {@link DatabaseRegistryEntry#compareTo(DatabaseRegistryEntry)} method. * </p> * * <p> * Useful for printing. * </p> * * @return Stringified version of the list as bullet points */ protected String dbreListToBulletPoints(List<DatabaseRegistryEntry> listOfDbres) { // Sort list before printing. Otherwise they will appear in an // arbitrary order and appear as new every day on the admin site. // Collections.sort(listOfDbres, new Comparator<DatabaseRegistryEntry>() { @Override public int compare( DatabaseRegistryEntry o1, DatabaseRegistryEntry o2) { return o1.compareTo(o2); } }); StringBuffer listOfDbresToString = new StringBuffer(); for (DatabaseRegistryEntry currentDbre : listOfDbres) { listOfDbresToString.append(" - " + currentDbre.getName() + "\n"); } return listOfDbresToString.toString(); } List<Class<? extends EnsTestCase>> findTestsApplyingToNoDb(TestRegistry testRegistry, DatabaseRegistry databasesToTestRegistry) { HashSet<DatabaseType> databaseTypesRegistered = new HashSet<DatabaseType>(); for (DatabaseRegistryEntry dbre : databasesToTestRegistry.getAll()) { databaseTypesRegistered.add(dbre.getType()); } List<Class<? extends EnsTestCase>> testsApplyingToNoDb = new ArrayList<Class<? extends EnsTestCase>>(); for (Class<? extends EnsTestCase> currentTest : getAllRegisteredTestClasses(testRegistry)) { DatabaseType[] dbT; /* * Instead of instantiating a test directly, a group of this one * test is created and then retrieved from it. * * The reason is that "getTests" has method calls to set the types * of databases to which this test can be applied. * * By instantiating the test this way, the tescase is initialised * the same way as it will be when it is run by the testrunner. * */ GroupOfTests g = new GroupOfTests(); g.addTest(currentTest); dbT = g.getTests().iterator().next().getAppliesToTypes(); boolean currentTestAppliesToADb = false; for (DatabaseType currentDbt : dbT) { if (databaseTypesRegistered.contains(currentDbt)) { currentTestAppliesToADb = true; if (currentTestAppliesToADb) { break; } } } if (!currentTestAppliesToADb) { testsApplyingToNoDb.add(currentTest); } } return testsApplyingToNoDb; } } class TestRunnerSelfCheck extends EnsTestCase { public TestRunnerSelfCheck() { setTeamResponsible(Team.RELEASE_COORDINATOR); } }; class TestRunStats { protected enum CompletionStatus { COMPLETED, SKIPPED_LONG_RUNNING, DIED_WITH_EXCEPTION } public HashSet<Class<? extends EnsTestCase>> getTestsRun() { return testsRun; } public Map<Class<? extends EnsTestCase>, CompletionStatus> getTrackCompletionStatus() { return trackCompletionStatus; } protected final HashSet<Class<? extends EnsTestCase>> testsRun; protected final Map<Class<? extends EnsTestCase>, CompletionStatus> trackCompletionStatus; protected final Map< Class<? extends EnsTestCase>, List<DatabaseRegistryEntry> > exceptionToDb; public Map<Class<? extends EnsTestCase>, List<DatabaseRegistryEntry>> getExceptionToDb() { return exceptionToDb; } public TestRunStats( HashSet<Class<? extends EnsTestCase>> testsRun, Map<Class<? extends EnsTestCase>, CompletionStatus> trackCompletionStatus, Map< Class<? extends EnsTestCase>, List<DatabaseRegistryEntry> > exceptionToDb ) { this.testsRun = testsRun; this.trackCompletionStatus = trackCompletionStatus; this.exceptionToDb = exceptionToDb; } }