Skip to content

Commit

Permalink
Merge pull request #83 from armfergom/JENKINS-33382
Browse files Browse the repository at this point in the history
[JENKINS-33382] MSBuild acceptance tests.
  • Loading branch information
rsandell committed Apr 12, 2016
2 parents 2d593b8 + 3e340c1 commit 6c83277
Show file tree
Hide file tree
Showing 10 changed files with 332 additions and 4 deletions.
102 changes: 102 additions & 0 deletions src/main/java/org/jenkinsci/test/acceptance/junit/WithOS.java
@@ -0,0 +1,102 @@
package org.jenkinsci.test.acceptance.junit;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Documented;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.Arrays;

import org.apache.commons.lang.SystemUtils;
import org.jenkinsci.test.acceptance.controller.JenkinsController;
import org.jenkinsci.test.acceptance.controller.LocalController;
import org.junit.internal.AssumptionViolatedException;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;

import com.google.inject.Inject;

/**
*
* Indicates that the test and Jenkins instance must be running
* on one of the operating systems provided. If this condition is not
* met, this test will be skipped.
*
* <p>
* This check can only be <strong>trusted to do its job correctly</strong> when tests
* are being run with a LocalController. When using a different controller,
* the machine running Jenkins can be different from the one running the tests so
* the test may pass or fail when it should not. If tests are not being run with a
* LocalController, the test will be skipped.
*/
@Retention(RUNTIME)
@Target({METHOD, TYPE})
@Inherited
@Documented
@RuleAnnotation(value = WithOS.RuleImpl.class, priority = -10)
public @interface WithOS {

public enum OS {
WINDOWS,
LINUX,
MAC
}

OS[] os();

public class RuleImpl implements TestRule {
@Inject JenkinsController controller;

@Override
public Statement apply(final Statement base, final Description d) {
return new Statement() {

@Override
public void evaluate() throws Throwable {
Class<?> testSuite = d.getTestClass();
check(d.getAnnotation(WithOS.class), testSuite);
check(testSuite.getAnnotation(WithOS.class), testSuite);

base.evaluate();
}

private void check(WithOS withos, Class<?> testCase) {
if (withos == null) return;

// Checks performed in this annotation only makes sense if Jenkins and Tests are being executed in the same machine
if (!(controller instanceof LocalController)) {
throw new AssumptionViolatedException("Test skipped. WithOS should be used with a local controller, otherwise it cannot be fully trusted to work as expected.");
}

String errorMsg = "Test and Jenkins instance must be running on any of the following operating systems: " + Arrays.toString(withos.os());
for (OS _os : withos.os()) {
switch (_os) {
case LINUX:
if (!SystemUtils.IS_OS_LINUX) {
throw new AssumptionViolatedException(errorMsg);
}
break;
case WINDOWS:
if (!SystemUtils.IS_OS_WINDOWS) {
throw new AssumptionViolatedException(errorMsg);
}
break;
case MAC:
if (!SystemUtils.IS_OS_MAC) {
throw new AssumptionViolatedException(errorMsg);
}
break;
default:
break;
}
}

}
};
}
}
}
@@ -0,0 +1,14 @@
package org.jenkinsci.test.acceptance.msbuild;

import org.jenkinsci.test.acceptance.po.JenkinsConfig;
import org.jenkinsci.test.acceptance.po.ToolInstallation;
import org.jenkinsci.test.acceptance.po.ToolInstallationPageObject;

@ToolInstallationPageObject(installer = "", name = "MSBuild")
public class MSBuildInstallation extends ToolInstallation {

public MSBuildInstallation(JenkinsConfig context, String path) {
super(context, path);
}

}
@@ -0,0 +1,34 @@
package org.jenkinsci.test.acceptance.msbuild;

import org.jenkinsci.test.acceptance.po.AbstractStep;
import org.jenkinsci.test.acceptance.po.BuildStep;
import org.jenkinsci.test.acceptance.po.Control;
import org.jenkinsci.test.acceptance.po.Describable;
import org.jenkinsci.test.acceptance.po.Job;

@Describable("Build a Visual Studio project or solution using MSBuild")
public class MSBuildStep extends AbstractStep implements BuildStep {

private final Control msBuildName = control("msBuildName");
private final Control msBuildFile = control("msBuildFile");
private final Control cmdLineArgs = control("cmdLineArgs");

public MSBuildStep(Job parent, String path) {
super(parent, path);
}

public MSBuildStep setMSBuildName(String buildName) {
msBuildName.select(buildName);
return this;
}

public MSBuildStep setMSBuildFile(String buildFile) {
msBuildFile.set(buildFile);
return this;
}

public MSBuildStep setCmdLineArgs(String cmdArgs) {
cmdLineArgs.set(cmdArgs);
return this;
}
}
30 changes: 26 additions & 4 deletions src/main/java/org/jenkinsci/test/acceptance/po/Job.java
Expand Up @@ -17,9 +17,15 @@
import java.util.regex.Pattern;
import java.util.zip.GZIPOutputStream;

import javax.inject.Inject;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.SystemUtils;
import org.codehaus.plexus.util.Base64;
import org.jenkinsci.test.acceptance.controller.JenkinsController;
import org.jenkinsci.test.acceptance.controller.LocalController;
import org.jenkinsci.test.acceptance.junit.Resource;
import org.junit.internal.AssumptionViolatedException;
import org.openqa.selenium.WebElement;
import org.zeroturnaround.zip.ZipUtil;

Expand All @@ -41,6 +47,12 @@ public List<Parameter> getParameters() {
return parameters;
}

/**
* The controller that starts/stops Jenkins
*/
@Inject
public JenkinsController controller;

private List<Parameter> parameters = new ArrayList<>();

// TODO these controls (and some methods) actually belong in a subclass corresponding to AbstractProject
Expand Down Expand Up @@ -185,6 +197,8 @@ public void copyResource(Resource resource) {
/**
* "Copy" any file from the System into the Workspace using a zipFIle.
*
* Differentiates when the file is being run on Windows or Unix based machines.
*
* @param file
*/
public void copyFile(File file) {
Expand All @@ -194,10 +208,18 @@ public void copyFile(File file) {
ZipUtil.pack(file, tmp);
byte[] archive = IOUtils.toByteArray(new FileInputStream(tmp));

addShellStep(String.format(
"base64 --decode << ENDOFFILE > archive.zip && unzip -o archive.zip \n%s\nENDOFFILE",
new String(Base64.encodeBase64Chunked(archive))
));
if (SystemUtils.IS_OS_WINDOWS) {
if (!(controller instanceof LocalController)) {
// TODO: Make it work for RemoteJenkinsController like in Unix (below)
throw new AssumptionViolatedException("Copying files in Windows is only supported if a LocalController is in use. Test will be skipped.");
}
addBatchStep("xcopy " + file.getAbsolutePath() + " %cd% /E");
} else {
addShellStep(String.format(
"base64 --decode << ENDOFFILE > archive.zip && unzip -o archive.zip \n%s\nENDOFFILE",
new String(Base64.encodeBase64Chunked(archive))
));
}
} catch (IOException e) {
throw new AssertionError(e);
} finally {
Expand Down
108 changes: 108 additions & 0 deletions src/test/java/plugins/MSBuildPluginTest.java
@@ -0,0 +1,108 @@
package plugins;

import static org.junit.Assert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;

import org.jenkinsci.test.acceptance.junit.AbstractJUnitTest;
import org.jenkinsci.test.acceptance.junit.Native;
import org.jenkinsci.test.acceptance.junit.WithOS;
import org.jenkinsci.test.acceptance.junit.WithPlugins;
import org.jenkinsci.test.acceptance.msbuild.MSBuildStep;
import org.jenkinsci.test.acceptance.po.Build;
import org.jenkinsci.test.acceptance.po.FreeStyleJob;
import org.junit.Test;

/**
* This test is designed to be run on a Windows machine with MSBuild installed and in the PATH
*
* Tests will be skipped if:
* - MSBUild.exe is not in the path. This implies that machine running Jenkins is windows.
* - The test and Jenkins are being run on different machines
*/
@WithPlugins("msbuild")
@Native({"MSBuild"})
@WithOS(os = {WithOS.OS.WINDOWS})
public class MSBuildPluginTest extends AbstractJUnitTest {

/**
* Builds a FreeStyle job with a MSBuild step with the configuration passed as parameter
*
* @param workspacePath The workspace where the project is located
* @param buildFile The build file (.proj or .sln)
* @param cmdArguments the command line arguments to execute. Can be null.
* @return a job
* @throws IllegalArgumentException if workspacePath or buildFile are null
*/
private FreeStyleJob msBuildJob(String workspacePath, String buildFile, String cmdArguments) {
if (workspacePath != null && buildFile != null) {
FreeStyleJob job = jenkins.jobs.create(FreeStyleJob.class);
job.copyDir(resource(workspacePath));
MSBuildStep msBuildStep = job.addBuildStep(MSBuildStep.class);
msBuildStep.setMSBuildFile(buildFile);
if (cmdArguments != null && !cmdArguments.isEmpty()) {
msBuildStep.setCmdLineArgs(cmdArguments);
}
job.save();
return job;
} else {
throw new IllegalArgumentException("Workspace and buildFile must be different from null.");
}
}

@Test
public void buildProjNoCmdLineArgumentsTest() {
FreeStyleJob job = msBuildJob("/msbuild_plugin/projProject/", "project.proj", null);

// Job should run successfully
Build b = job.scheduleBuild();
b.shouldSucceed();
assertThat(b.getConsole(), containsString("Build succeeded."));
assertThat(b.getConsole(), not(containsString("Done building project \"project.proj\".")));
}

@Test
public void buildWithDefaultProjTest() {
FreeStyleJob job = msBuildJob("/msbuild_plugin/projProject/", "", null);

// Job should run successfully
Build b = job.scheduleBuild();
b.shouldSucceed();
assertThat(b.getConsole(), containsString("Build succeeded."));
assertThat(b.getConsole(), not(containsString("Done building project \"project.proj\".")));
}

@Test
public void buildProjCmdLineArgumentsTest() {
FreeStyleJob job = msBuildJob("/msbuild_plugin/projProject/", "project.proj", "/verbosity:detailed");

// Job should run successfully
Build b = job.scheduleBuild();
b.shouldSucceed();
String console = b.getConsole();
assertThat(console, containsString("Build succeeded."));
assertThat(console, containsString("Done Building Project"));
}

@Test
public void buildSlnNoCmdLineArgumentsTest() {
FreeStyleJob job = msBuildJob("/msbuild_plugin/slnProject/", "project.sln", null);

// Job should run successfully
Build b = job.scheduleBuild();
b.shouldSucceed();
assertThat(b.getConsole(), containsString("Build succeeded."));
}

@Test
public void buildSlnCmdLineArgumentsTest() {
FreeStyleJob job = msBuildJob("/msbuild_plugin/slnProject/", "project.sln", "/verbosity:detailed");

// Job should run successfully
Build b = job.scheduleBuild();
b.shouldSucceed();
String console = b.getConsole();
assertThat(console, containsString("Build succeeded."));
assertThat(console, containsString("Done Building Project"));
}
}
13 changes: 13 additions & 0 deletions src/test/resources/msbuild_plugin/projProject/HelloWorld.cs
@@ -0,0 +1,13 @@
using System;

class HelloWorld
{
static void Main()
{
#if DebugConfig
Console.WriteLine("WE ARE IN THE DEBUG CONFIGURATION");
#endif

Console.WriteLine("Hello, world!");
}
}
8 changes: 8 additions & 0 deletions src/test/resources/msbuild_plugin/projProject/project.proj
@@ -0,0 +1,8 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Compile Include="HelloWorld.cs" />
</ItemGroup>
<Target Name="Build">
<Csc Sources="@(Compile)"/>
</Target>
</Project>
13 changes: 13 additions & 0 deletions src/test/resources/msbuild_plugin/slnProject/HelloWorld.cs
@@ -0,0 +1,13 @@
using System;

class HelloWorld
{
static void Main()
{
#if DebugConfig
Console.WriteLine("WE ARE IN THE DEBUG CONFIGURATION");
#endif

Console.WriteLine("Hello, world!");
}
}
8 changes: 8 additions & 0 deletions src/test/resources/msbuild_plugin/slnProject/project.proj
@@ -0,0 +1,8 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Compile Include="HelloWorld.cs" />
</ItemGroup>
<Target Name="Build">
<Csc Sources="@(Compile)"/>
</Target>
</Project>
6 changes: 6 additions & 0 deletions src/test/resources/msbuild_plugin/slnProject/project.sln
@@ -0,0 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.23107.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HelloWorld", "project.proj", "{00D09290-F731-49A7-B960-C30AEBE8B1F2}"
EndProject

0 comments on commit 6c83277

Please sign in to comment.