/**
* Copyright (C) 2010-2017 Gordon Fraser, Andrea Arcuri and EvoSuite
* contributors
*
* This file is part of EvoSuite.
*
* EvoSuite is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3.0 of the License, or
* (at your option) any later version.
*
* EvoSuite is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with EvoSuite. If not, see <http://www.gnu.org/licenses/>.
*/
package org.evosuite.jenkins.scm;
import com.cloudbees.plugins.credentials.CredentialsProvider;
import com.cloudbees.plugins.credentials.common.StandardUsernameCredentials;
import com.cloudbees.plugins.credentials.domains.URIRequirementBuilder;
import org.evosuite.jenkins.recorder.EvoSuiteRecorder;
import java.io.File;
import java.io.IOException;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import hudson.FilePath;
import hudson.Launcher;
import hudson.maven.AbstractMavenProject;
import hudson.maven.MavenModule;
import hudson.maven.MavenModuleSet;
import hudson.model.AbstractBuild;
import hudson.model.BuildListener;
import hudson.plugins.mercurial.HgExe;
import hudson.plugins.mercurial.MercurialInstallation;
import hudson.plugins.mercurial.MercurialSCM;
import hudson.security.ACL;
import hudson.util.ArgumentListBuilder;
import jenkins.model.Jenkins;
/**
* Mercurial wrapper to handle hg commands, such commit and push.
*
* @author José Campos
*/
public class Mercurial implements SCM {
private final HgExe hgClient;
public Mercurial(MercurialSCM mercurialSCM, AbstractMavenProject<?, ?> project, AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener)
throws IOException, InterruptedException {
// TODO check whether there is an issue between "Default" and "(Default)"
MercurialInstallation mercurialInstallation = null;
for (MercurialInstallation inst : MercurialInstallation.allInstallations()) {
if (inst.getName().equals(mercurialSCM.getInstallation())) {
mercurialInstallation = inst;
break;
}
}
assert mercurialInstallation != null;
// get credentials (username-password, ssh key-passphrase
StandardUsernameCredentials credentials = this.getCredentials(mercurialSCM, project);
listener.getLogger().println(EvoSuiteRecorder.LOG_PREFIX + "Credentials " + credentials.getDescription());
// get a MercurialClient to handle hg commands
this.hgClient = new HgExe(mercurialInstallation, credentials, launcher, Jenkins.getInstance(), listener, build.getEnvironment(listener));
}
@Override
public int commit(AbstractMavenProject<?, ?> project, AbstractBuild<?, ?> build,
BuildListener listener, String branchName, String ctgBestsDir) {
try {
listener.getLogger().println(EvoSuiteRecorder.LOG_PREFIX + "Commiting new test cases");
Set<String> branches = this.getBranches(build.getWorkspace(), listener);
if (!branches.contains(branchName)) {
// create a new branch called "evosuite-tests" to commit and
// push the new generated test suites
listener.getLogger()
.println(EvoSuiteRecorder.LOG_PREFIX + "There is no branch called " + branchName);
if (this.hgClient.run("branch", branchName).pwd(build.getWorkspace()).join() != 0) {
listener.getLogger().println(EvoSuiteRecorder.LOG_PREFIX + "Unable to create a new branch called " + branchName);
return -1;
}
}
// switch to EVOSUITE_BRANCH
if (this.hgClient.run("update", branchName).pwd(build.getWorkspace()).join() != 0) {
listener.getLogger().println(EvoSuiteRecorder.LOG_PREFIX + "Unable to switch to branch " + branchName);
return -1;
}
// start adding all removed files to commit
if (this.hgClient.run("remove", "--after").pwd(build.getWorkspace()).join() != 0) {
this.rollback(build, listener);
return -1;
}
MavenModuleSet prj = (MavenModuleSet) project;
// parse list of new and modified files
int number_of_files_committed = 0;
for (MavenModule module : prj.getModules()) {
for (String file : this.parseStatus(this.hgClient.popen(build.getWorkspace(), listener,
true, new ArgumentListBuilder("status")),
(module.getRelativePath().isEmpty() ? "" : module.getRelativePath() + File.separator) + ctgBestsDir)) {
if (this.hgClient.run("add", file).pwd(build.getWorkspace()).join() != 0) {
this.rollback(build, listener);
return -1;
}
number_of_files_committed++;
}
}
if (number_of_files_committed == 0) {
listener.getLogger().println(EvoSuiteRecorder.LOG_PREFIX + "Nothing to commit");
return 0;
}
// commit
String commit_msg = SCM.COMMIT_MSG_PREFIX + build.getProject().getName().replace(" ", "_") + "-" + build.getNumber();
listener.getLogger().println(EvoSuiteRecorder.LOG_PREFIX + commit_msg);
if (this.hgClient.run("commit", "--message", commit_msg).pwd(build.getWorkspace()).join() != 0) {
this.rollback(build, listener);
return -1;
}
return number_of_files_committed++;
} catch (IOException | InterruptedException e) {
e.printStackTrace();
return -1;
}
}
@Override
public boolean push(AbstractBuild<?, ?> build, BuildListener listener, String branchName) {
try {
listener.getLogger().println(EvoSuiteRecorder.LOG_PREFIX + "Pushing new test cases");
if (this.hgClient.run("push").pwd(build.getWorkspace()).join() != 0) {
return false;
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
return false;
}
return true;
}
@Override
public void rollback(AbstractBuild<?, ?> build, BuildListener listener) {
listener.getLogger().println(EvoSuiteRecorder.LOG_PREFIX + "Rollback, cleaning up workspace");
// TODO
}
private Set<String> parseStatus(String status, String ctgBestsDir) {
Set<String> result = new LinkedHashSet<String>();
Matcher m = Pattern.compile("[?AMR!]\\s(.*" + ctgBestsDir + ".*)").matcher(status);
while (m.find()) {
result.add(m.group(1));
}
return result;
}
private StandardUsernameCredentials getCredentials(MercurialSCM mercurialSCM, AbstractMavenProject<?, ?> project) {
return CredentialsProvider.lookupCredentials(StandardUsernameCredentials.class, project, ACL.SYSTEM,
URIRequirementBuilder.fromUri(mercurialSCM.getSource()).build()).get(0);
}
private Set<String> getBranches(FilePath workspace, BuildListener listener) throws InterruptedException, IOException {
String rawBranches = this.hgClient.popen(workspace, listener, true, new ArgumentListBuilder("branches"));
Set<String> branches = new LinkedHashSet<String>();
for (String line: rawBranches.split("\n")) {
// line should contain: <branchName> <revision>:<hash>
String[] seperatedByWhitespace = line.split("\\s+");
String branchName = seperatedByWhitespace[0];
branches.add(branchName);
}
return branches;
}
}