Skip to content

Commit

Permalink
Merge pull request #43 from mathworks/CI-78-remote-agent-support-minimal
Browse files Browse the repository at this point in the history
Added changes to support running MATLAB on remote agents.
  • Loading branch information
nbhoski authored Dec 2, 2019
2 parents e824674 + 84602d8 commit 6548b9e
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 43 deletions.
67 changes: 42 additions & 25 deletions src/main/java/com/mathworks/ci/MatlabBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public class MatlabBuilder extends Builder implements SimpleBuildStep {
private static final String MATLAB_RUNNER_RESOURCE =
"com/mathworks/ci/MatlabBuilder/runMatlabTests.m";
private static final String AUTOMATIC_OPTION = "RunTestsAutomaticallyOption";
private String nodeSpecificfileSeparator;


@DataBoundConstructor
Expand Down Expand Up @@ -101,6 +102,7 @@ private String getCustomMatlabCommand() {
private void setEnv(EnvVars env) {
this.env = env;
}


@Extension
public static class MatlabDescriptor extends BuildStepDescriptor<Builder> {
Expand Down Expand Up @@ -190,14 +192,15 @@ public FormValidation getFirstErrorOrWarning(
final MatrixPatternResolver resolver = new MatrixPatternResolver(matlabRoot);
if (!resolver.hasVariablePattern()) {
try {
rel = new MatlabReleaseInfo(matlabRoot);
FilePath matlabRootPath = new FilePath(new File(matlabRoot));
rel = new MatlabReleaseInfo(matlabRootPath);
if (rel.verLessThan(BASE_MATLAB_VERSION_RUNTESTS_SUPPORT)) {
return FormValidation
.error(Message.getValue("Builder.matlab.test.support.error"));
}
} catch (MatlabVersionNotFoundException e) {
return FormValidation
.error(Message.getValue("Builder.invalid.matlab.root.error"));
.warning(Message.getValue("Builder.invalid.matlab.root.warning"));
}
}
return FormValidation.ok();
Expand Down Expand Up @@ -257,7 +260,8 @@ public FormValidation doCheckTaCoberturaChkBx(@QueryParameter boolean taCobertur
}

Function<String, FormValidation> chkCoberturaSupport = (String matlabRoot) -> {
rel = new MatlabReleaseInfo(matlabRoot);
FilePath matlabRootPath = new FilePath(new File(matlabRoot));
rel = new MatlabReleaseInfo(matlabRootPath);
final MatrixPatternResolver resolver = new MatrixPatternResolver(matlabRoot);
if(!resolver.hasVariablePattern()) {
try {
Expand All @@ -266,7 +270,7 @@ public FormValidation doCheckTaCoberturaChkBx(@QueryParameter boolean taCobertur
.warning(Message.getValue("Builder.matlab.cobertura.support.warning"));
}
} catch (MatlabVersionNotFoundException e) {
return FormValidation.error(Message.getValue("Builder.invalid.matlab.root.error"));
return FormValidation.warning(Message.getValue("Builder.invalid.matlab.root.warning"));
}
}

Expand Down Expand Up @@ -393,40 +397,44 @@ public String getStringByName(String memberName) {
public void perform(@Nonnull Run<?, ?> build, @Nonnull FilePath workspace,
@Nonnull Launcher launcher, @Nonnull TaskListener listener)
throws InterruptedException, IOException {
final boolean isLinuxLauncher = launcher.isUnix();
//Set the environment variable specific to the this build
setEnv(build.getEnvironment(listener));
nodeSpecificfileSeparator = getNodeSpecificFileSeperator(launcher);

// Invoke MATLAB command and transfer output to standard
// Output Console

buildResult = execMatlabCommand(build, workspace, launcher, listener, isLinuxLauncher);
buildResult = execMatlabCommand(workspace, launcher, listener);

if (buildResult != 0) {
build.setResult(Result.FAILURE);
}
}

private synchronized int execMatlabCommand(Run<?, ?> build, FilePath workspace, Launcher launcher,
TaskListener listener, boolean isLinuxLauncher)
private synchronized int execMatlabCommand(FilePath workspace, Launcher launcher,
TaskListener listener)
throws IOException, InterruptedException {
setEnv(build.getEnvironment(listener));
final String testRunMode = this.getTestRunTypeList().getDescriptor().getId();

// Copy MATLAB scratch file into the workspace only if Automatic option is selected.
if (testRunMode.contains(AUTOMATIC_OPTION)) {
copyMatlabScratchFileInWorkspace(MATLAB_RUNNER_RESOURCE, MATLAB_RUNNER_TARGET_FILE,
workspace, getClass().getClassLoader());
}
ProcStarter matlabLauncher;
try {
MatlabReleaseInfo rel = new MatlabReleaseInfo(getLocalMatlab());
FilePath nodeSpecificMatlabRoot = new FilePath(launcher.getChannel(),getLocalMatlab());
MatlabReleaseInfo rel = new MatlabReleaseInfo(nodeSpecificMatlabRoot);
matlabLauncher = launcher.launch().pwd(workspace).envs(this.env);
if (rel.verLessThan(BASE_MATLAB_VERSION_BATCH_SUPPORT)) {
ListenerLogDecorator outStream = new ListenerLogDecorator(listener);
matlabLauncher = matlabLauncher.cmds(constructDefaultMatlabCommand(isLinuxLauncher)).stderr(outStream);
matlabLauncher = matlabLauncher.cmds(constructDefaultMatlabCommand(launcher.isUnix())).stderr(outStream);
} else {
matlabLauncher = matlabLauncher.cmds(constructMatlabCommandWithBatch()).stdout(listener);
}
} catch (MatlabVersionNotFoundException e) {

//Check the test run mode option selected by user and identify the target workspace to copy the scratch file.
final String testRunMode = this.getTestRunTypeList().getDescriptor().getId();

// Copy MATLAB scratch file into the workspace only if Automatic option is selected.
if (testRunMode.contains(AUTOMATIC_OPTION)) {
FilePath targetWorkspace = new FilePath(launcher.getChannel(), workspace.getRemote());
copyMatlabScratchFileInWorkspace(MATLAB_RUNNER_RESOURCE, MATLAB_RUNNER_TARGET_FILE, targetWorkspace);
}
} catch (Exception e) {
listener.getLogger().println(e.getMessage());
return 1;
}
Expand All @@ -450,7 +458,7 @@ public List<String> constructMatlabCommandWithBatch() {
}

matlabDefaultArgs =
Arrays.asList(getLocalMatlab() + File.separator + "bin" + File.separator + "matlab",
Arrays.asList(getLocalMatlab() + nodeSpecificfileSeparator + "bin" + nodeSpecificfileSeparator + "matlab",
"-batch", runCommand);

return matlabDefaultArgs;
Expand All @@ -473,7 +481,7 @@ public List<String> constructDefaultMatlabCommand(boolean isLinuxLauncher) {

private String[] getPreRunnerSwitches() {
String[] preRunnerSwitches =
{getLocalMatlab() + File.separator + "bin" + File.separator + "matlab", "-nosplash",
{getLocalMatlab() + nodeSpecificfileSeparator + "bin" + nodeSpecificfileSeparator + "matlab", "-nosplash",
"-nodesktop", "-noAppIcon"};
return preRunnerSwitches;
}
Expand Down Expand Up @@ -504,13 +512,22 @@ private String[] getRunnerSwitch() {
}

private void copyMatlabScratchFileInWorkspace(String matlabRunnerResourcePath,
String matlabRunnerTarget, FilePath workspace, ClassLoader classLoader)
String matlabRunnerTarget, FilePath targetWorkspace)
throws IOException, InterruptedException {
final ClassLoader classLoader = getClass().getClassLoader();
FilePath targetFile =
new FilePath(targetWorkspace, Message.getValue(matlabRunnerTarget));
InputStream in = classLoader.getResourceAsStream(matlabRunnerResourcePath);
Path target =
new File(workspace.getRemote(), Message.getValue(matlabRunnerTarget)).toPath();

Files.copy(in, target, StandardCopyOption.REPLACE_EXISTING);
targetFile.copyFrom(in);
}

private String getNodeSpecificFileSeperator(Launcher launcher) {
if (launcher.isUnix()) {
return "/";
} else {
return "\\";
}
}

}
23 changes: 14 additions & 9 deletions src/main/java/com/mathworks/ci/MatlabReleaseInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,25 @@
*/

import java.io.File;
import java.io.IOException;
import java.nio.file.NotDirectoryException;
import java.util.HashMap;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.commons.collections.MapUtils;
import org.jenkinsci.remoting.RoleChecker;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.FilePath;
import hudson.FilePath.FileCallable;
import hudson.remoting.VirtualChannel;

public class MatlabReleaseInfo {
private String matlabRoot;
private FilePath matlabRoot;
private static final String VERSION_INFO_FILE = "VersionInfo.xml";
private static final String VERSION_INFO_ROOT_TAG = "MathWorks_version_info";
private static final String RELEASE_TAG = "release";
Expand All @@ -34,8 +39,8 @@ public class MatlabReleaseInfo {
};

private Map<String, String> versionInfoCache = new HashMap<String, String>();

public MatlabReleaseInfo(String matlabRoot) {
public MatlabReleaseInfo(FilePath matlabRoot) {
this.matlabRoot = matlabRoot;
}

Expand Down Expand Up @@ -73,12 +78,12 @@ public boolean verLessThan(double version) throws MatlabVersionNotFoundException
private Map<String, String> getVersionInfoFromFile() throws MatlabVersionNotFoundException {
if (MapUtils.isEmpty(versionInfoCache)) {
try {
File versionFile = new File(this.matlabRoot + File.separator + VERSION_INFO_FILE);
if(versionFile.isFile()) {
FilePath versionFile = new FilePath(this.matlabRoot, VERSION_INFO_FILE);
if(versionFile.exists()) {
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(versionFile);

Document doc = dBuilder.parse(versionFile.read());
doc.getDocumentElement().normalize();
NodeList nList = doc.getElementsByTagName(VERSION_INFO_ROOT_TAG);

Expand All @@ -99,7 +104,7 @@ private Map<String, String> getVersionInfoFromFile() throws MatlabVersionNotFoun
}
}
}
else if(!new File(this.matlabRoot).exists()){
else if(!this.matlabRoot.exists()){
throw new NotDirectoryException("Invalid matlabroot path");
}else {
versionInfoCache.putAll(VERSION_OLDER_THAN_17A);
Expand All @@ -112,4 +117,4 @@ else if(!new File(this.matlabRoot).exists()){
}
return versionInfoCache;
}
}
}
4 changes: 2 additions & 2 deletions src/main/resources/config.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@

Builder.display.name = Run MATLAB Tests
Builder.matlab.runner.target.file.name = runMatlabTests.m
Builder.matlab.cobertura.support.warning = To generate a Cobertura report, use MATLAB R2017b or a newer version.
Builder.invalid.matlab.root.error = Unable to launch MATLAB from the specified location. Verify the MATLAB root folder path.
Builder.matlab.cobertura.support.warning = To generate a Cobertura report, use MATLAB R2017b or a newer release.
Builder.invalid.matlab.root.warning = Unable to find MATLAB from the specified location on this system(but perhaps it exists on some agents)
Builder.matlab.root.empty.error = Full path to the MATLAB root folder is required.
Builder.matlab.test.support.error = To run tests with the Jenkins plugin, use MATLAB R2013a or a newer version.
builder.matlab.automatictestoption.display.name = Automatic
Expand Down
12 changes: 7 additions & 5 deletions src/test/java/com/mathworks/ci/MatlabBuilderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import static org.junit.Assert.assertFalse;
import hudson.EnvVars;
import hudson.FilePath;
import hudson.model.FreeStyleBuild;
import hudson.model.Result;
import hudson.slaves.EnvironmentVariablesNodeProperty;
Expand Down Expand Up @@ -245,7 +246,8 @@ public void verifyBuildStatusWhenTestFails() throws Exception {

@Test
public void verifyVerlessThan() throws Exception {
MatlabReleaseInfo rel = new MatlabReleaseInfo(getMatlabroot("R2017a"));
FilePath matlabRoot = new FilePath(new File(getMatlabroot("R2017a")));
MatlabReleaseInfo rel = new MatlabReleaseInfo(matlabRoot);

// verLessthan() will check all the versions against 9.2 which is version of R2017a
assertFalse(rel.verLessThan(9.1));
Expand Down Expand Up @@ -383,7 +385,7 @@ public void verifyInvalidMatlabRootDisplaysError() throws Exception {
project.getBuildersList().add(this.matlabBuilder);
this.matlabBuilder.setMatlabRoot("/fake/matlab/path");
HtmlPage page = jenkins.createWebClient().goTo("job/test0/configure");
WebAssert.assertTextPresent(page, TestMessage.getValue("Builder.invalid.matlab.root.error"));
WebAssert.assertTextPresent(page, TestMessage.getValue("Builder.invalid.matlab.root.warning"));
}

/*
Expand All @@ -396,7 +398,7 @@ public void verifyValidMatlabRootDoesntDisplayError() throws Exception {
project.getBuildersList().add(this.matlabBuilder);
this.matlabBuilder.setMatlabRoot(getMatlabroot("R2018b"));
HtmlPage page = jenkins.createWebClient().goTo("job/test0/configure");
WebAssert.assertTextNotPresent(page, TestMessage.getValue("Builder.invalid.matlab.root.error"));
WebAssert.assertTextNotPresent(page, TestMessage.getValue("Builder.invalid.matlab.root.warning"));
}

/*
Expand Down Expand Up @@ -429,8 +431,8 @@ public void verifyCoberturaError() throws Exception {
coberturaChkBx.setChecked(true);
Thread.sleep(2000);
String pageText = page.asText();
String filteredPageText = pageText.replaceFirst(TestMessage.getValue("Builder.invalid.matlab.root.error"), "");
Assert.assertTrue(filteredPageText.contains(TestMessage.getValue("Builder.invalid.matlab.root.error")));
String filteredPageText = pageText.replaceFirst(TestMessage.getValue("Builder.invalid.matlab.root.warning"), "");
Assert.assertTrue(filteredPageText.contains(TestMessage.getValue("Builder.invalid.matlab.root.warning")));
}

/*
Expand Down
4 changes: 2 additions & 2 deletions src/test/resources/testconfig.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@

Verify.matlab.invokes.positive = MATLAB is invoking positive tests
Verify.build.ignore.test.failure = Build Ignored test failure
Builder.matlab.cobertura.support.warning = To generate a Cobertura report, use MATLAB R2017b or a newer version.
Builder.invalid.matlab.root.error = Unable to launch MATLAB from the specified location. Verify the MATLAB root folder path.
Builder.matlab.cobertura.support.warning = To generate a Cobertura report, use MATLAB R2017b or a newer release.
Builder.invalid.matlab.root.warning = Unable to find MATLAB from the specified location on this system(but perhaps it exists on some agents)
Builder.matlab.root.empty.error = Full path to the MATLAB root folder is required.

0 comments on commit 6548b9e

Please sign in to comment.