Skip to content

Commit

Permalink
Merge pull request #93 from jglick/docker-fixtures
Browse files Browse the repository at this point in the history
[JENKINS-40808] Using docker-fixtures repository
  • Loading branch information
jglick committed May 19, 2017
2 parents 5cc6e1c + 8e54357 commit 2903930
Show file tree
Hide file tree
Showing 18 changed files with 625 additions and 257 deletions.
2 changes: 2 additions & 0 deletions Jenkinsfile
@@ -0,0 +1,2 @@
// TODO add Windows containers once we get Windows 2016 agents and DockerRule can handle Windows containers
buildPlugin(platforms: ['docker'])
23 changes: 13 additions & 10 deletions pom.xml
Expand Up @@ -3,7 +3,7 @@
<parent>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>plugin</artifactId>
<version>2.27</version>
<version>2.29</version>
<relativePath />
</parent>
<artifactId>mercurial</artifactId>
Expand All @@ -15,7 +15,6 @@

<properties>
<jenkins.version>1.642.3</jenkins.version>
<no-test-jar>false</no-test-jar>
<scm-api-plugin.version>2.1.0</scm-api-plugin.version>
</properties>

Expand Down Expand Up @@ -58,14 +57,6 @@
<url>https://github.com/jenkinsci/${project.artifactId}-plugin</url>
<tag>HEAD</tag>
</scm>
<issueManagement>
<system>JIRA</system>
<url>http://issues.jenkins-ci.org/secure/IssueNavigator.jspa?reset=true&amp;jqlQuery=project+%3D+Jenkins+AND+component+%3D+mercurial+AND+resolution+%3D+Unresolved</url>
</issueManagement>
<ciManagement>
<system>jenkins</system>
<url>https://buildhive.cloudbees.com/job/jenkinsci/job/mercurial-plugin/</url>
</ciManagement>
<repositories>
<repository>
<id>repo.jenkins-ci.org</id>
Expand Down Expand Up @@ -171,5 +162,17 @@
<version>1.22</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.test</groupId>
<artifactId>docker-fixtures</artifactId>
<version>1.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>ssh-slaves</artifactId>
<version>1.12</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Expand Up @@ -72,21 +72,21 @@ public class MercurialInstallation extends ToolInstallation implements
@Deprecated
public MercurialInstallation(String name, String home, String executable,
boolean debug, boolean useCaches,
boolean useSharing, List<? extends ToolProperty<?>> properties) {
boolean useSharing, @CheckForNull List<? extends ToolProperty<?>> properties) {
this(name, home, executable, debug, useCaches, null, useSharing, null, properties);
}

/** for backwards compatibility */
@Deprecated
public MercurialInstallation(String name, String home, String executable,
boolean debug, boolean useCaches,
boolean useSharing, String config, List<? extends ToolProperty<?>> properties) {
boolean useSharing, String config, @CheckForNull List<? extends ToolProperty<?>> properties) {
this(name, home, executable, debug, useCaches, null, useSharing, config, properties);
}

@DataBoundConstructor public MercurialInstallation(String name, String home, String executable,
boolean debug, boolean useCaches, String masterCacheRoot,
boolean useSharing, String config, List<? extends ToolProperty<?>> properties) {
boolean useSharing, String config, @CheckForNull List<? extends ToolProperty<?>> properties) {
super(name, home, properties);
this.executable = Util.fixEmpty(executable);
this.debug = debug;
Expand All @@ -101,7 +101,12 @@ public String getExecutable() {
}

String executableWithSubstitution(String home) {
return getExecutable().replace("INSTALLATION", home);
String _executable = getExecutable();
if (home.isEmpty() && _executable.contains("INSTALLATION")) {
// No home location defined on this node, so fall back to looking for Mercurial in the path.
return "hg";
}
return _executable.replace("INSTALLATION", home);
}

public boolean getDebug() {
Expand Down
Expand Up @@ -5,4 +5,7 @@
Normally <code>INSTALLATION/bin/hg</code> but you could specify another name
such as <code>INSTALLATION/my-mercurial.py</code> or even look for Mercurial
in the default path (leaving installation directory blank) using just <code>hg</code>.
If you are configuring per-node tool home locations,
you may leave the installation directory undefined on some nodes,
in which case just <code>hg</code> (i.e., from <code>$PATH</code>) is used.
</div>
Expand Up @@ -44,6 +44,7 @@ public Change compare(MercurialSCM scm, Launcher launcher, TaskListener listener

FreeStyleProject project = j.createFreeStyleProject();

// TODO switch to MercurialContainer
MercurialSCM scm = new MercurialSCM(null, tmp.getRoot().getPath(), null, null, null, null, false, null);
project.setScm(scm);
File repo = tmp.getRoot();
Expand Down
5 changes: 2 additions & 3 deletions src/test/java/hudson/plugins/mercurial/CustomConfigTest.java
Expand Up @@ -27,9 +27,7 @@
import hudson.FilePath;
import hudson.model.FreeStyleBuild;
import hudson.model.FreeStyleProject;
import hudson.tools.ToolProperty;
import java.io.File;
import java.util.Collections;
import static org.junit.Assert.*;
import org.junit.Rule;
import org.junit.Test;
Expand All @@ -46,9 +44,10 @@ public class CustomConfigTest {
@Bug(5723)
@Test public void customConfiguration() throws Exception {
File repo = tmp.getRoot();
// TODO switch to MercurialContainer
m.hg(repo, "init");
m.touchAndCommit(repo, "f");
r.jenkins.getDescriptorByType(MercurialInstallation.DescriptorImpl.class).setInstallations(new MercurialInstallation("test", "", "hg", false, false, false, "[format]\nusestore = false", Collections.<ToolProperty<?>>emptyList()));
r.jenkins.getDescriptorByType(MercurialInstallation.DescriptorImpl.class).setInstallations(new MercurialInstallation("test", "", "hg", false, false, false, "[format]\nusestore = false", null));
FreeStyleProject p = r.createFreeStyleProject();
MercurialSCM scm = new MercurialSCM(repo.getPath());
scm.setInstallation("test");
Expand Down
Expand Up @@ -24,6 +24,7 @@ public class DisableChangeLogTest {

@Before public void setUp() throws Exception {
repo = tmp.getRoot();
// TODO switch to MercurialContainer
j.jenkins
.getDescriptorByType(MercurialInstallation.DescriptorImpl.class)
.setInstallations(
Expand Down
5 changes: 2 additions & 3 deletions src/test/java/hudson/plugins/mercurial/EnvVarTest.java
Expand Up @@ -30,9 +30,7 @@
import hudson.model.FreeStyleProject;
import hudson.model.ParametersDefinitionProperty;
import hudson.model.StringParameterDefinition;
import hudson.tools.ToolProperty;
import java.io.File;
import java.util.Collections;
import static org.junit.Assert.*;
import org.junit.Rule;
import org.junit.Test;
Expand All @@ -54,6 +52,7 @@ public class EnvVarTest {
EnvVars env = new EnvVars( key, val );

// 'repo' and 'repoExpanded' should be the same; 'repo' will contain a non expanded environment variable.
// TODO switch to MercurialContainer
File repo, repoExpanded;
repo = new File( tmp.getRoot() + "/$" + key );
repoExpanded = new File( tmp.getRoot() + "/" + val );
Expand All @@ -71,7 +70,7 @@ public class EnvVarTest {
m.touchAndCommit(repoExpanded, "f");

// Set up installation.
r.jenkins.getDescriptorByType(MercurialInstallation.DescriptorImpl.class).setInstallations(new MercurialInstallation("test", "", "hg", false, false, false, "[format]\nusestore = false", Collections.<ToolProperty<?>>emptyList()));
r.jenkins.getDescriptorByType(MercurialInstallation.DescriptorImpl.class).setInstallations(new MercurialInstallation("test", "", "hg", false, false, false, "[format]\nusestore = false", null));

// Create free style project.
FreeStyleProject project = r.createFreeStyleProject();
Expand Down
222 changes: 222 additions & 0 deletions src/test/java/hudson/plugins/mercurial/FunctionalTest.java
@@ -0,0 +1,222 @@
/*
* The MIT License
*
* Copyright 2017 CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

package hudson.plugins.mercurial;

import hudson.FilePath;
import hudson.model.FreeStyleProject;
import hudson.model.Slave;
import java.io.File;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.jenkinsci.test.acceptance.docker.DockerRule;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.jvnet.hudson.test.BuildWatcher;
import org.jvnet.hudson.test.Issue;
import org.jvnet.hudson.test.JenkinsRule;

/**
* Runs various functional tests against different system configurations.
*/
@RunWith(Parameterized.class)
public class FunctionalTest {

@Rule public JenkinsRule j = new JenkinsRule();
@Rule public MercurialRule m = new MercurialRule(j);
@Rule public TemporaryFolder tmp = new TemporaryFolder();
@Rule public DockerRule<MercurialContainer> container = new DockerRule<MercurialContainer>(MercurialContainer.class);
@ClassRule public static BuildWatcher buildWatcher = new BuildWatcher();

/** Whether to run builds on a slave, or only on master. */
@Parameterized.Parameter(0) public boolean useSlave;
public interface MercurialInstallationFactory {
@CheckForNull MercurialInstallation create(@Nonnull JenkinsRule j, @Nonnull DockerRule<MercurialContainer> container, @CheckForNull Slave slave, @Nullable MercurialContainer.Version version) throws Exception;
@Override String toString();
}
/** How Mercurial is configured. */
@Parameterized.Parameter(1) public MercurialInstallationFactory mercurialInstallationFactory;
/** Version of Mercurial to run (if {@link #useSlave}). */
@Parameterized.Parameter(2) public MercurialContainer.Version mercurialVersion;
@Parameterized.Parameters(name="{index}: slave={0} {1} {2}") public static Object[][] data() {
MercurialInstallationFactory defaultFactory = new MercurialInstallationFactory() {
@Override public String toString() {return "default";}
@Override public MercurialInstallation create(JenkinsRule j, DockerRule<MercurialContainer> container, Slave slave, MercurialContainer.Version version) throws Exception {
if (slave != null) {
return container.get().createInstallation(j, version, false, false, false, "", slave);
} else {
assert version == null;
return null;
}
}
};
MercurialInstallationFactory cachingFactory = new MercurialInstallationFactory() {
@Override public String toString() {return "caching";}
@Override public MercurialInstallation create(JenkinsRule j, DockerRule<MercurialContainer> container, Slave slave, MercurialContainer.Version version) throws Exception {
if (slave != null) {
return container.get().createInstallation(j, version, false, true, false, "", slave);
} else {
// TODO pull up common code here into superclass; or simply switch to @DataBoundSetter so we can create a stock installation (except in default / !useSlave) and then customize it
assert version == null;
MercurialInstallation inst = new MercurialInstallation("whatever", "", "hg", false, true, false, null);
j.jenkins.getDescriptorByType(MercurialInstallation.DescriptorImpl.class).setInstallations(inst); // TODO cf. MercurialContainer, caller should do it
return inst;
}
}
};
MercurialInstallationFactory sharingFactory = new MercurialInstallationFactory() {
@Override public String toString() {return "sharing";}
@Override public MercurialInstallation create(JenkinsRule j, DockerRule<MercurialContainer> container, Slave slave, MercurialContainer.Version version) throws Exception {
if (slave != null) {
return container.get().createInstallation(j, version, false, true, true, "", slave);
} else {
assert version == null;
MercurialInstallation inst = new MercurialInstallation("whatever", "", "hg", false, true, true, null);
j.jenkins.getDescriptorByType(MercurialInstallation.DescriptorImpl.class).setInstallations(inst);
return inst;
}
}
};
MercurialInstallationFactory debugFactory = new MercurialInstallationFactory() {
@Override public String toString() {return "debug";}
@Override public MercurialInstallation create(JenkinsRule j, DockerRule<MercurialContainer> container, Slave slave, MercurialContainer.Version version) throws Exception {
if (slave != null) {
return container.get().createInstallation(j, version, true, false, false, "", slave);
} else {
assert version == null;
MercurialInstallation inst = new MercurialInstallation("whatever", "", "hg", true, false, false, null);
j.jenkins.getDescriptorByType(MercurialInstallation.DescriptorImpl.class).setInstallations(inst);
return inst;
}
}
};
return new Object[][] {
{false, defaultFactory, null},
{true, defaultFactory, MercurialContainer.Version.HG1},
{true, defaultFactory, MercurialContainer.Version.HG2},
{true, defaultFactory, MercurialContainer.Version.HG3},
{true, defaultFactory, MercurialContainer.Version.HG4},
{false, cachingFactory, null},
// Skip testing caching with older Hg versions since a locally installed version might be 3.x+,
// in which case master sends a new version and we get abort: xfer.hg: unknown bundle version 20
// cf. https://hglabhq.com/blog/2014/4/29/what-s-new-in-mercurial-3-0
{true, cachingFactory, MercurialContainer.Version.HG3},
{true, cachingFactory, MercurialContainer.Version.HG4},
{false, sharingFactory, null},
// Sharing implies caching, so same issue with bundle version.
{true, sharingFactory, MercurialContainer.Version.HG3},
{true, sharingFactory, MercurialContainer.Version.HG4},
{false, debugFactory, null},
// Do not waste time looking at old versions for this.
{true, debugFactory, MercurialContainer.Version.HG4},
};
}

private FilePath repo;
private Slave slave;
private MercurialInstallation inst;

@Before public void setUp() throws Exception {
slave = useSlave ? container.get().createSlave(j) : null;
inst = mercurialInstallationFactory.create(j, container, slave, mercurialVersion);
if (inst != null && inst.isUseCaches() || slave == null) {
// Set up test repository on master, if we have hg installed locally.
repo = new FilePath(tmp.getRoot());
} else {
// Set up test repository on agent.
repo = slave.getRootPath().child("repo");
repo.mkdirs();
m.withNode(slave);
m.withInstallation(inst);
}
}

@Issue({"JENKINS-13329", "JENKINS-15829"})
@Test public void basics() throws Exception {
FreeStyleProject p = j.createFreeStyleProject();
p.setScm(new MercurialSCM(inst != null ? inst.getName() : null, repo.getRemote(), null, null, null, null, false));
if (slave != null) {
p.setAssignedNode(slave);
}
m.hg(repo, "init");
m.touchAndCommit(repo, "a");
String log = m.buildAndCheck(p, "a");
assertClone(log, true);
m.touchAndCommit(repo, "b");
log = m.buildAndCheck(p, "b");
assertClone(log, false);
}

private void assertClone(String log, boolean cloneExpected) {
if (cloneExpected) {
assertTrue(log, log.contains(" clone --"));
} else {
assertTrue(log, log.contains(" update --"));
assertFalse(log, log.contains(" clone --"));
}
}

@Issue("JENKINS-4281")
@Test public void branches() throws Exception {
m.hg(repo, "init");
m.touchAndCommit(repo, "init");
m.hg(repo, "tag", "init");
m.touchAndCommit(repo, "default-1");
m.hg(repo, "update", "--clean", "init");
m.hg(repo, "branch", "b");
m.touchAndCommit(repo, "b-1");
FreeStyleProject p = j.createFreeStyleProject();
// Clone off b.
p.setScm(new MercurialSCM(inst != null ? inst.getName() : null, repo.getRemote(), "b", null, null, null, false));
m.buildAndCheck(p, "b-1");
m.hg(repo, "update", "--clean", "default");
m.touchAndCommit(repo, "default-2");
// Changes in default should be ignored.
assertFalse(m.pollSCMChanges(p).hasChanges());
m.hg(repo, "update", "--clean", "b");
m.touchAndCommit(repo, "b-2");
// But changes in b should be pulled.
assertTrue(m.pollSCMChanges(p).hasChanges());
m.buildAndCheck(p, "b-2");
// Switch to default branch with an existing workspace.
p.setScm(new MercurialSCM(inst != null ? inst.getName() : null, repo.getRemote(), null, null, null, null, false));
// Should now consider preexisting changesets in default to be poll
// triggers.
assertTrue(m.pollSCMChanges(p).hasChanges());
// Should switch working copy to default branch.
m.buildAndCheck(p, "default-2");
m.touchAndCommit(repo, "b-3");
// Changes in other branch should be ignored.
assertFalse(m.pollSCMChanges(p).hasChanges());
}

}

0 comments on commit 2903930

Please sign in to comment.