/*
* 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.testcase;
import java.io.IOException;
import java.sql.Connection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.ensembl.healthcheck.DatabaseRegistryEntry;
import org.ensembl.healthcheck.ReportManager;
import org.ensembl.healthcheck.util.ActionAppendable;
import org.ensembl.healthcheck.util.ProcessExec;
/**
* <p>
* Abstract class providing methods for running tests that are started from
* the shell in a separate process.
* </p>
*
* @author mnuhn
*
*/
public abstract class AbstractShellBasedTestCase extends SingleDatabaseTestCase {
/**
* <p>
* Specify here, if the test implementing this class uses speciesIds. If
* true, the method
* </p>
*
* createCommandLine(dbre, speciesId);
*
* <p>
* will be called for creating commands. If the test implementing this
* class doesn't use speciesId, set to false and
* </p>
*
* createCommandLine(dbre);
*
* <p>
* will be called to get the command for executing the shell based test.
* </p>
*/
boolean isSpeciesIdAware = false;
public boolean isSpeciesIdAware() {
return isSpeciesIdAware;
}
public void setSpeciesIdAware(boolean iteratesOverSpeciesIds) {
this.isSpeciesIdAware = iteratesOverSpeciesIds;
}
/**
* <p>
* Creates a default processor for output. Sends everything to
* the method dispatchMessage.
* </p>
*
*/
protected Appendable createOutputProcessor(final EnsTestCase e, final Connection c) {
return
new ActionAppendable() {
@Override public void process(String message) {
dispatchMessage(e, c, message.trim());
}
};
}
/**
* <p>
* Forward any message received from the healthcheck to the ReportManager.
* Meant to be overridden with behaviour specific to the test.
* </p>
*
* @param message
*/
protected void dispatchMessage(final EnsTestCase e, final Connection c, String message) {
ReportManager.correct(e, c, message);
}
/**
* <p>
* Creates an Appendable object to which stdout is delegated. Overwrite
* this method to determine what is to be done with output sent to stdout
* from the subprocess.
* </p>
*
*/
protected Appendable createStdoutProcessor(final EnsTestCase e, final Connection c) {
return createOutputProcessor(e, c);
}
/**
* <p>
* Creates an Appendable object to which stderr is delegated. Overwrite
* this method to determine what is to be done with output sent to stderr
* from the subprocess.
* </p>
*
*/
protected Appendable createStderrProcessor(final EnsTestCase e, final Connection c) {
return createOutputProcessor(e, c);
}
/**
* Use this, if your test uses species ids.
*
* @param dbre
* @param speciesId
* @return command line
*/
protected String createCommandLine(final DatabaseRegistryEntry dbre, int speciesId) { return null; }
/**
* Use this, if your test is independent of species ids.
*
* @param dbre
* @return command line
*/
protected String createCommandLine(final DatabaseRegistryEntry dbre) { return null; }
/**
* Preservers the system environment by default. If your test requires
* certain environment variables to be set, override this method.
*
* @return hash of environment variables
*/
protected Map<String,String> environmentVarsToSet() {
Map<String,String> environmentVars = new HashMap<String,String>(System.getenv());
return environmentVars;
}
public boolean runShellTest(final DatabaseRegistryEntry dbre, int speciesId, boolean useSpeciesId) {
boolean passes = true;
final EnsTestCase currentTestCase = this;
Appendable out = createStdoutProcessor(currentTestCase, dbre.getConnection());
Appendable err = createStderrProcessor(currentTestCase, dbre.getConnection());
String lastCmdThatWasRun = "";
try {
String shellCmd;
if (useSpeciesId) {
shellCmd = createCommandLine(dbre, speciesId);
} else {
shellCmd = createCommandLine(dbre);
}
logger.info(
"Running: "
+ shellCmd
);
int exit = 1;
if (!StringUtils.isEmpty(shellCmd)) {
//
// Running the command by creating an array avoids the
// problem of java breaking the command down at spaces to
// divide it into command and arguments.
//
// The command is passed to the bash so things like pipes and
// backticks are interpreted.
//
String[] cmdLineItems = new String[] {
"/bin/bash",
"-c",
shellCmd,
};
exit = ProcessExec.exec(
cmdLineItems,
out,
err,
false,
environmentVarsToSet()
);
lastCmdThatWasRun = shellCmd;
} else {
throw new RuntimeException("Shell based test "+this.getName()+" has not returned a valid command line");
}
if (exit == 0) {
ReportManager.correct(
this,
dbre.getConnection(),
"Command \n"
+ lastCmdThatWasRun
+ "\ncompleted successfully"
);
} else {
ReportManager.problem(
this,
dbre.getConnection(),
"Command \n"
+ lastCmdThatWasRun
+ "\ndid not complete successfully"
);
passes = false;
}
} catch (IOException e) {
ReportManager.problem(
this,
dbre.getConnection(),
"Could not execute "
+ lastCmdThatWasRun
+ "\nGot the following error: "
+ e.getMessage()
);
passes = false;
}
return passes;
}
@Override
public boolean run(final DatabaseRegistryEntry dbre) {
boolean passes = true;
if (isSpeciesIdAware) {
List<Integer> dbre_speciesIds = dbre.getSpeciesIds();
// Make sure species ids were configured. If not, the perl test will
// not be run. The warning message may be overlooked by a user,
// therefore the test is set to fail in order to get attention.
//
if (dbre_speciesIds.size() == 0) {
logger.warning(
"No species ids! Perhaps no databases were configured?"
+ " This test will not be run."
);
passes = false;
}
for (int speciesId : dbre_speciesIds) {
// Once a test has failed, should not do anymore tests on
// other species ids.
//
passes = passes && runShellTest(dbre, speciesId, true);
}
} else {
passes = runShellTest(dbre, 0, false);
}
return passes;
}
};