package hudson.plugins.sctmexecutor;
import hudson.FilePath;
import hudson.Launcher;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.BuildListener;
import hudson.model.Hudson;
import hudson.plugins.sctmexecutor.exceptions.SCTMException;
import hudson.plugins.sctmexecutor.service.ISCTMService;
import hudson.plugins.sctmexecutor.service.SCTMReRunProxy;
import hudson.plugins.sctmexecutor.service.SCTMService;
import hudson.tasks.Builder;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.kohsuke.stapler.DataBoundConstructor;
/**
* Executes a specified execution definition on Borland's SilkCentral Test Manager.
*
* @author Thomas Fuerer
*
*/
public final class SCTMExecutor extends Builder {
static final int OPT_NO_BUILD_NUMBER = 1;
static final int OPT_USE_THIS_BUILD_NUMBER = 2;
static final int OPT_USE_SPECIFICJOB_BUILDNUMBER = 3;
static final int OPT_USE_LATEST_SCTM_BUILDNUMBER = 4;
private static final Logger LOGGER = Logger.getLogger("hudson.plugins.sctmexecutor"); //$NON-NLS-1$
private final int projectId;
private final String execDefIds;
private final int delay;
private final int buildNumberUsageOption;
private final String jobName;
private final boolean continueOnError;
private final boolean collectResults;
private final boolean ignoreSetupCleanup;
private String productVersion;
private boolean succeed;
private String product;
@DataBoundConstructor
public SCTMExecutor(final int projectId, final String execDefIds, final int delay, final int buildNumberUsageOption,
final String jobName, final boolean contOnErr, final boolean collectResults, final boolean ignoreSetupCleanup, String productVersion) {
this.projectId = projectId;
this.execDefIds = execDefIds;
this.delay = delay;
this.buildNumberUsageOption = buildNumberUsageOption;
this.jobName = jobName;
this.continueOnError = contOnErr;
this.collectResults = collectResults;
this.ignoreSetupCleanup = ignoreSetupCleanup;
this.productVersion = productVersion;
}
private ISCTMService createSctmService(final int projectId, List<Integer> execDefIdList) {
SCTMExecutorDescriptor descriptor = getDescriptor();
String serviceURL = descriptor.getServiceURL();
ISCTMService service = null;
try {
service = new SCTMReRunProxy(new SCTMService(serviceURL, descriptor.getUser(), descriptor
.getPassword(), projectId));
this.product = service.getProductName(execDefIdList.get(0));
} catch (SCTMException e) {
LOGGER.log(Level.SEVERE, MessageFormat.format(
"Creating a remote connection to SCTM host ({0}) failed.", serviceURL), e); //$NON-NLS-1$
}
return service;
}
@Override
public SCTMExecutorDescriptor getDescriptor() {
return (SCTMExecutorDescriptor) Hudson.getInstance().getDescriptor(getClass());
}
public String getExecDefIds() {
return execDefIds;
}
public int getProjectId() {
return projectId;
}
public int getDelay() {
return delay;
}
public int getBuildNumberUsageOption() {
return this.buildNumberUsageOption;
}
public String getJobName() {
return this.jobName;
}
public boolean isContinueOnError() {
return this.continueOnError;
}
public boolean isignoreSetupCleanup() {
return this.ignoreSetupCleanup;
}
public boolean isCollectResults() {
return this.collectResults;
}
public String getProductVersion() {
return productVersion;
}
@Override
public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener)
throws InterruptedException, IOException {
SCTMExecutorDescriptor descriptor = getDescriptor();
String serviceURL = descriptor.getServiceURL();
List<Integer> execDefIdList = Utils.csvToIntList(this.execDefIds);
ISCTMService service = createSctmService(projectId, execDefIdList);
try {
listener.getLogger().println(Messages.getString("SCTMExecutor.log.successfulLogin")); //$NON-NLS-1$
FilePath rootDir = createResultDir(build.number, build, listener);
Collection<Thread> executions = new ArrayList<Thread>(execDefIdList.size());
int buildNumber = -1;
buildNumber = getOrAddBuildNumber(build, listener, execDefIdList.get(0), service);
for (Integer execDefId : execDefIdList) {
ITestResultWriter resultWriter = null;
if (collectResults)
// resultWriter = new StdXMLResultWriter(rootDir, serviceURL, String.valueOf(build.number),
// this.ignoreSetupCleanup);
resultWriter = new SCTMResultWriter(rootDir, service, ignoreSetupCleanup);
Runnable resultCollector = new ExecutionRunnable(service, execDefId, buildNumber, resultWriter, listener
.getLogger());
Thread t = new Thread(resultCollector);
executions.add(t);
t.start();
if (delay > 0 && execDefIdList.size() > 1)
Thread.sleep(delay * 1000);
}
for (Thread t : executions) {
t.join();
}
succeed = true;
} catch (SCTMException e) {
LOGGER.log(Level.SEVERE, MessageFormat.format(
"Creating a remote connection to SCTM host ({0}) failed.", serviceURL), e); //$NON-NLS-1$
listener.fatalError(e.getMessage());
succeed = false;
}
return continueOnError || succeed;
}
private int getOrAddBuildNumber(AbstractBuild<?, ?> build, BuildListener listener, int nodeId, ISCTMService service) throws SCTMException {
switch (this.buildNumberUsageOption) {
case OPT_USE_THIS_BUILD_NUMBER:
case OPT_USE_SPECIFICJOB_BUILDNUMBER:
int buildnumber = -1;
if (this.buildNumberUsageOption == OPT_USE_THIS_BUILD_NUMBER)
buildnumber = build.number;
else if (this.buildNumberUsageOption == OPT_USE_SPECIFICJOB_BUILDNUMBER)
buildnumber = getBuildNumberFromUpStreamProject(jobName, build.getProject().getTransitiveUpstreamProjects(), listener);
try {
if (!service.buildNumberExists(product, productVersion, buildnumber)) {
listener.getLogger().println(MessageFormat.format(Messages.getString("SCTMExecutor.msg.info.addBuildNumber"), buildnumber)); //$NON-NLS-1$
if (!service.addBuildNumber(product, productVersion, buildnumber))
buildnumber = -1;
} else
listener.getLogger().println(MessageFormat.format(Messages.getString("SCTMExecutor.msg.info.buildnumberExists"), buildnumber)); //$NON-NLS-1$
} catch (IllegalArgumentException e) {
listener.error(e.getMessage());
buildnumber = -1;
}
return buildnumber;
case OPT_USE_LATEST_SCTM_BUILDNUMBER:
return service.getLatestSCTMBuildnumber(product, productVersion);
default:
return -1;
}
}
private int getBuildNumberFromUpStreamProject(String projectName, Set<AbstractProject> upstreamProjects,
BuildListener listener) {
for (AbstractProject<?, ?> project : upstreamProjects) {
if (project.getName().equals(projectName))
return project.getLastSuccessfulBuild().getNumber();
}
listener.error(MessageFormat.format(Messages.getString("SCTMExecutor.err.notAUpstreamJob"), projectName)); //$NON-NLS-1$
return -1;
}
private FilePath createResultDir(int currentBuildNo, AbstractBuild<?, ?> build, BuildListener listener)
throws IOException, InterruptedException {
FilePath rootDir = build.getWorkspace();
if (rootDir == null) {
LOGGER.severe("Cannot write the result file because slave is not connected."); //$NON-NLS-1$
listener.error(Messages.getString("SCTMExecutor.log.slaveNotConnected")); //$NON-NLS-1$
throw new RuntimeException();
}
final String buildNo = String.valueOf(currentBuildNo);
rootDir = new FilePath(rootDir, "SCTMResults"); //$NON-NLS-1$
FilePath buildResults = new FilePath(rootDir, buildNo); //$NON-NLS-1$
if (rootDir.exists()) {
boolean found = false;
for (FilePath file : rootDir.list()) {
if (!file.getName().equals(buildNo)) // delete results of old builds
file.deleteRecursive();
else
found = true;
}
if (!found)
buildResults.mkdirs();
} else
buildResults.mkdirs();
return buildResults;
}
}