/*
* Copyright [1999-2016] 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.io.IOException;
import java.io.PrintWriter;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.ConsoleHandler;
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import org.apache.commons.lang.StringUtils;
import org.ensembl.healthcheck.configuration.ConfigureTestGroups;
import org.ensembl.healthcheck.configurationmanager.ConfigurationException;
import org.ensembl.healthcheck.testcase.EnsTestCase;
import org.ensembl.healthcheck.testcase.SingleDatabaseTestCase;
import org.ensembl.healthcheck.util.DBUtils;
import com.mysql.jdbc.Driver;
import uk.co.flamingpenguin.jewel.cli.ArgumentValidationException;
import uk.co.flamingpenguin.jewel.cli.CliFactory;
import uk.co.flamingpenguin.jewel.cli.Option;
/**
* Simple class to run one or more tests or groups on a single database
*
* @author dstaines
*
*/
public class StandaloneTestRunner {
/**
*
*/
private static final String WRITE_STDOUT = "-";
/**
* Options that specify how the tests run
*
* @author dstaines
*
*/
public interface StandaloneTestOptions extends ConfigureTestGroups {
@Option(helpRequest = true, description = "display help")
boolean getHelp();
@Option(shortName = "o", longName = "output_file", defaultValue = "failures.txt", description = "File to write any failures to (use '-' for standard out)")
String getOutputFile();
@Option(shortName = "v", longName="verbose", description = "Show detailed debugging output")
boolean isVerbose();
@Option(shortName = "d", longName="dbname", description = "Database to test")
String getDbname();
@Option(shortName = "u", longName="user", description = "Username for test database")
String getUser();
@Option(shortName = "h", longName="host", description = "Host for test database")
String getHost();
@Option(shortName = "p", longName="pass", description = "Password for test database")
String getPassword();
boolean isPassword();
@Option(shortName = "P", longName="port", description = "Port for test database")
int getPort();
@Option(longName = "compara_dbname", defaultValue = "ensembl_compara_master", description = "Name of compara master database")
String getComparaMasterDbname();
boolean isComparaMasterDbname();
@Option(longName = "compara_host", description = "Compara master database host")
String getComparaHost();
boolean isComparaHost();
@Option(longName = "compara_port", description = "Compara master database port")
int getComparaPort();
boolean isComparaPort();
@Option(longName = "compara_user", description = "Compara master database user")
String getComparaUser();
boolean isComparaUser();
@Option(longName = "compara_pass", description = "Compara master database password")
String getComparaPassword();
boolean isComparaPassword();
@Option(longName = "prod_dbname", defaultValue = "ensembl_production", description = "Name of production database")
String getProductionDbname();
boolean isProductionDbname();
@Option(longName = "prod_host", description = "Production database host")
String getProductionHost();
boolean isProductionHost();
@Option(longName = "prod_port", description = "Production database port")
int getProductionPort();
boolean isProductionPort();
@Option(longName = "prod_user", description = "Production database user")
String getProductionUser();
boolean isProductionUser();
@Option(longName = "prod_pass", description = "Production database password")
String getProductionPassword();
boolean isProductionPassword();
@Option(longName = "secondary_host", description = "Secondary database host")
String getSecondaryHost();
boolean isSecondaryHost();
@Option(longName = "secondary_port", description = "Secondary database port")
int getSecondaryPort();
boolean isSecondaryPort();
@Option(longName = "secondary_user", description = "Secondary database user")
String getSecondaryUser();
boolean isSecondaryUser();
@Option(longName = "secondary_pass", description = "Secondary database password")
String getSecondaryPassword();
boolean isSecondaryPassword();
@Option(longName = "release", shortName = "r", description = "Current release")
String getRelease();
boolean isRelease();
}
/**
* @param args
*/
public static void main(String[] args) {
StandaloneTestOptions options = null;
try {
options = CliFactory.parseArguments(StandaloneTestOptions.class, args);
} catch (ArgumentValidationException e) {
System.err.println(e.getMessage());
System.exit(2);
}
StandaloneTestRunner runner = new StandaloneTestRunner(options);
if (!StringUtils.isEmpty(options.getOutputFile()) && !options.getOutputFile().equals(WRITE_STDOUT)) {
File outfile = new File(options.getOutputFile());
if (outfile.exists()) {
runner.getLogger().fine("Deleting existing output file " + options.getOutputFile());
if (!outfile.delete()) {
runner.getLogger().fine("Could not delete existing output file " + options.getOutputFile());
System.exit(3);
}
}
}
StandaloneReporter reporter = new StandaloneReporter(runner.getLogger());
ReportManager.setReporter(reporter);
boolean result = runner.runAll();
if (!result) {
if (options.getOutputFile().equals(WRITE_STDOUT)) {
runner.getLogger().severe("Failures detected - writing details to screen");
try {
PrintWriter writer = new PrintWriter(System.out);
reporter.writeFailures(writer);
writer.close();
} catch (IOException e) {
System.err.println(e.getMessage());
System.exit(4);
}
} else {
runner.getLogger().severe("Failures detected - writing details to " + options.getOutputFile());
reporter.writeFailureFile(options.getOutputFile());
}
} else {
runner.getLogger().info("Completed healthchecks with no failures");
}
System.exit(result ? 0 : 1);
}
private Logger logger;
private final StandaloneTestOptions options;
private DatabaseRegistryEntry productionDb;
private DatabaseRegistryEntry comparaMasterDb;
private DatabaseRegistryEntry testDb;
private DatabaseServer primaryServer;
private DatabaseServer secondaryServer;
public StandaloneTestRunner(StandaloneTestOptions options) {
this.options = options;
getLogger().fine("Connecting to primary server " + options.getHost());
DBUtils.overrideMainDatabaseServer(getPrimaryServer());
if (options.isSecondaryHost()) {
getLogger().fine("Connecting to secondary server " + options.getSecondaryHost());
DBUtils.overrideSecondaryDatabaseServer(getSecondaryServer());
}
if (options.isRelease()) {
DBUtils.setRelease(options.getRelease());
}
System.setProperty("compara_master.database", options.getComparaMasterDbname());
}
public Logger getLogger() {
if (logger == null) {
logger = Logger.getLogger(StandaloneTestRunner.class.getCanonicalName());
ConsoleHandler localConsoleHandler = new ConsoleHandler();
localConsoleHandler.setFormatter(new Formatter() {
DateFormat format = new SimpleDateFormat("dd-M-yyyy hh:mm:ss");
@Override
public String format(LogRecord record) {
return String.format("%s %s %s : %s%n", format.format(new Date(record.getMillis())),
record.getSourceClassName(), record.getLevel().toString(), record.getMessage());
}
});
if (options.isVerbose()) {
localConsoleHandler.setLevel(Level.ALL);
logger.setLevel(Level.ALL);
} else {
localConsoleHandler.setLevel(Level.INFO);
logger.setLevel(Level.INFO);
}
logger.setUseParentHandlers(false);
logger.addHandler(localConsoleHandler);
}
return logger;
}
public DatabaseRegistryEntry getProductionDb() {
if (productionDb == null && options.isProductionHost()) {
getLogger().info("Connecting to production database " + options.getProductionDbname());
productionDb = new DatabaseRegistryEntry(new DatabaseServer(options.getProductionHost(),
String.valueOf(options.getProductionPort()), options.getProductionUser(),
options.isProductionPassword() ? options.getProductionPassword() : null, Driver.class.getName()),
options.getProductionDbname(), null, null);
}
return productionDb;
}
public DatabaseRegistryEntry getComparaMasterDb() {
if (comparaMasterDb == null && options.isProductionHost()) {
getLogger().info("Connecting to compara master database " + options.getComparaMasterDbname());
comparaMasterDb = new DatabaseRegistryEntry(new DatabaseServer(options.getProductionHost(),
String.valueOf(options.getProductionPort()), options.getProductionUser(),
options.isProductionPassword() ? options.getProductionPassword() : null, Driver.class.getName()),
options.getComparaMasterDbname(), null, null);
}
return comparaMasterDb;
}
public DatabaseRegistryEntry getTestDb() {
if (testDb == null) {
getLogger().info("Connecting to test database " + options.getDbname());
testDb = new DatabaseRegistryEntry(getPrimaryServer(), options.getDbname(), null, null);
if(testDb.getConnection()==null) {
throw new ConfigurationException("Test database "+options.getDbname()+" not found");
}
}
return testDb;
}
public DatabaseServer getPrimaryServer() {
if (primaryServer == null)
primaryServer = new DatabaseServer(options.getHost(), String.valueOf(options.getPort()), options.getUser(),
options.isPassword() ? options.getPassword() : null, Driver.class.getName());
return primaryServer;
}
public DatabaseServer getSecondaryServer() {
if (secondaryServer == null) {
secondaryServer = new DatabaseServer(options.getSecondaryHost(), String.valueOf(options.getSecondaryPort()),
options.getSecondaryUser(), options.isSecondaryPassword() ? options.getSecondaryPassword() : null,
Driver.class.getName());
}
return secondaryServer;
}
private TestRegistry testRegistry;
private TestRegistry getTestRegistry() {
if (testRegistry == null) {
try {
this.testRegistry = new ConfigurationBasedTestRegistry(options);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (UnknownTestTypeException e) {
throw new RuntimeException(e);
}
}
return testRegistry;
}
public boolean runAll() {
boolean success = true;
for (EnsTestCase testCase : getTestRegistry().getAll()) {
success &= runTestCase(testCase);
}
return success;
}
public boolean runTestCase(EnsTestCase test) {
boolean success = true;
if (SingleDatabaseTestCase.class.isAssignableFrom(test.getClass())) {
getLogger().info("Executing testcase " + test.getName());
test.setProductionDatabase(getProductionDb());
test.setComparaMasterDatabase(getComparaMasterDb());
ReportManager.startTestCase(test, getTestDb());
boolean result = ((SingleDatabaseTestCase) test).run(getTestDb());
ReportManager.finishTestCase(test, result, getTestDb());
getLogger().info(test.getName() + " " + (result ? "succeeded" : "failed"));
success &= result;
} else {
getLogger().fine("Skipping non-single testcase " + test.getName());
}
return success;
}
}