diff --git a/changelog.html b/changelog.html
index 2f749666a2..ba00204bf6 100644
--- a/changelog.html
+++ b/changelog.html
@@ -56,11 +56,17 @@ Upcoming changes
-
- Channel reader thread can end up consuming 100% CPU
+ Channel reader thread can end up consuming 100% CPU.
(issue 23471)
-
- CancelledKeyException can cause all JNLP slaves to disconnect (and the problem remains until restart)
+ CancelledKeyException can cause all JNLP slaves to disconnect (and the problem remains until restart).
(issue 24050)
+
-
+ Consider dynamic label assignments for label load statistics.
+ (issue 15576)
+
-
+ Use Windows line endings for batch file build steps.
+ (issue 7478)
diff --git a/core/src/main/java/hudson/Plugin.java b/core/src/main/java/hudson/Plugin.java
index 20f671e8dc..e2a061fbda 100644
--- a/core/src/main/java/hudson/Plugin.java
+++ b/core/src/main/java/hudson/Plugin.java
@@ -48,7 +48,10 @@ import com.thoughtworks.xstream.XStream;
* Base class of Hudson plugin.
*
*
- * A plugin needs to derive from this class.
+ * A plugin may derive from this class, or it may directly define extension
+ * points annotated with {@link hudson.Extension}. For a list of extension
+ * points, see
+ * https://wiki.jenkins-ci.org/display/JENKINS/Extension+points.
*
*
* One instance of a plugin is created by Hudson, and used as the entry point
diff --git a/core/src/main/java/hudson/model/LoadStatistics.java b/core/src/main/java/hudson/model/LoadStatistics.java
index 81ebae78de..b996f040b4 100644
--- a/core/src/main/java/hudson/model/LoadStatistics.java
+++ b/core/src/main/java/hudson/model/LoadStatistics.java
@@ -233,7 +233,7 @@ public abstract class LoadStatistics {
int q=0;
for (Queue.BuildableItem bi : bis) {
for (SubTask st : Tasks.getSubTasksOf(bi.task))
- if (st.getAssignedLabel()==l)
+ if (bi.getAssignedLabelFor(st)==l)
q++;
}
return q;
diff --git a/core/src/main/java/hudson/model/Queue.java b/core/src/main/java/hudson/model/Queue.java
index 830eb851f6..da478515f4 100644
--- a/core/src/main/java/hudson/model/Queue.java
+++ b/core/src/main/java/hudson/model/Queue.java
@@ -834,11 +834,11 @@ public class Queue extends ResourceController implements Saveable {
int r = 0;
for (BuildableItem bi : buildables.values())
for (SubTask st : bi.task.getSubTasks())
- if (null==l || st.getAssignedLabel()==l)
+ if (null==l || bi.getAssignedLabelFor(st)==l)
r++;
for (BuildableItem bi : pendings.values())
for (SubTask st : bi.task.getSubTasks())
- if (null==l || st.getAssignedLabel()==l)
+ if (null==l || bi.getAssignedLabelFor(st)==l)
r++;
return r;
}
@@ -1414,6 +1414,21 @@ public class Queue extends ResourceController implements Saveable {
}
return task.getAssignedLabel();
}
+
+ /**
+ * Test if the specified {@link SubTask} needs to be run on a node with a particular label, and
+ * return that {@link Label}. Otherwise null, indicating it can run on anywhere.
+ *
+ *
+ * This code takes {@link LabelAssignmentAction} into account, then falls back to {@link SubTask#getAssignedLabel()}
+ */
+ public Label getAssignedLabelFor(SubTask st) {
+ for (LabelAssignmentAction laa : getActions(LabelAssignmentAction.class)) {
+ Label l = laa.getAssignedLabel(st);
+ if (l!=null) return l;
+ }
+ return st.getAssignedLabel();
+ }
/**
* Convenience method that returns a read only view of the {@link Cause}s associated with this item in the queue.
diff --git a/core/src/main/java/hudson/model/labels/LabelAtomProperty.java b/core/src/main/java/hudson/model/labels/LabelAtomProperty.java
index e0dfd53259..efaad2ab77 100644
--- a/core/src/main/java/hudson/model/labels/LabelAtomProperty.java
+++ b/core/src/main/java/hudson/model/labels/LabelAtomProperty.java
@@ -42,6 +42,7 @@ import java.util.Collections;
* with the {@link LabelAtom} object.
*
* @author Kohsuke Kawaguchi
+ * @see hudson.model.labels.LabelAtomPropertyDescriptor
* @since 1.373
*/
@ExportedBean
diff --git a/core/src/main/java/hudson/model/labels/LabelAtomPropertyDescriptor.java b/core/src/main/java/hudson/model/labels/LabelAtomPropertyDescriptor.java
index 43724c73c4..82312097dc 100644
--- a/core/src/main/java/hudson/model/labels/LabelAtomPropertyDescriptor.java
+++ b/core/src/main/java/hudson/model/labels/LabelAtomPropertyDescriptor.java
@@ -31,6 +31,11 @@ import hudson.model.Descriptor;
*
*
* Put {@link Extension} on your descriptor implementation to have it auto-registered.
+ *
+ *
+ * When extending this class, override Descriptor.getDisplayName(). In the
+ * context of LabelAtomPropertyDescriptor, this function is used to determine the label of
+ * the checkbox shown in the label's configuration page to activate the extension.
*
* @author Kohsuke Kawaguchi
* @since 1.373
diff --git a/core/src/main/java/hudson/tasks/BatchFile.java b/core/src/main/java/hudson/tasks/BatchFile.java
index 0943e9147f..b2506474be 100644
--- a/core/src/main/java/hudson/tasks/BatchFile.java
+++ b/core/src/main/java/hudson/tasks/BatchFile.java
@@ -26,9 +26,11 @@ package hudson.tasks;
import hudson.FilePath;
import hudson.Extension;
import hudson.model.AbstractProject;
+import hudson.util.LineEndingConversion;
import net.sf.json.JSONObject;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.StaplerRequest;
+import java.io.ObjectStreamException;
/**
* Executes commands by using Windows batch file.
@@ -38,7 +40,7 @@ import org.kohsuke.stapler.StaplerRequest;
public class BatchFile extends CommandInterpreter {
@DataBoundConstructor
public BatchFile(String command) {
- super(command);
+ super(LineEndingConversion.convertEOL(command, LineEndingConversion.EOLType.Windows));
}
public String[] buildCommandLine(FilePath script) {
@@ -46,13 +48,17 @@ public class BatchFile extends CommandInterpreter {
}
protected String getContents() {
- return command+"\r\nexit %ERRORLEVEL%";
+ return LineEndingConversion.convertEOL(command+"\r\nexit %ERRORLEVEL%",LineEndingConversion.EOLType.Windows);
}
protected String getFileExtension() {
return ".bat";
}
+ private Object readResolve() throws ObjectStreamException {
+ return new BatchFile(command);
+ }
+
@Extension
public static final class DescriptorImpl extends BuildStepDescriptor {
@Override
diff --git a/core/src/main/java/hudson/tasks/Shell.java b/core/src/main/java/hudson/tasks/Shell.java
index 5a3a2053db..17ce0b8819 100644
--- a/core/src/main/java/hudson/tasks/Shell.java
+++ b/core/src/main/java/hudson/tasks/Shell.java
@@ -32,6 +32,8 @@ import hudson.remoting.Callable;
import hudson.remoting.VirtualChannel;
import hudson.util.FormValidation;
import java.io.IOException;
+import java.io.ObjectStreamException;
+import hudson.util.LineEndingConversion;
import net.sf.json.JSONObject;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.StaplerRequest;
@@ -50,29 +52,7 @@ import java.util.logging.Logger;
public class Shell extends CommandInterpreter {
@DataBoundConstructor
public Shell(String command) {
- super(fixCrLf(command));
- }
-
- /**
- * Fix CR/LF and always make it Unix style.
- */
- private static String fixCrLf(String s) {
- // eliminate CR
- int idx;
- while((idx=s.indexOf("\r\n"))!=-1)
- s = s.substring(0,idx)+s.substring(idx+1);
-
- //// add CR back if this is for Windows
- //if(isWindows()) {
- // idx=0;
- // while(true) {
- // idx = s.indexOf('\n',idx);
- // if(idx==-1) break;
- // s = s.substring(0,idx)+'\r'+s.substring(idx);
- // idx+=2;
- // }
- //}
- return s;
+ super(LineEndingConversion.convertEOL(command, LineEndingConversion.EOLType.Unix));
}
/**
@@ -80,7 +60,7 @@ public class Shell extends CommandInterpreter {
* makes the shell think the file is a binary file and not a script. Adding
* a leading line feed works around this problem.
*/
- private static String addCrForNonASCII(String s) {
+ private static String addLineFeedForNonASCII(String s) {
if(!s.startsWith("#!")) {
if (s.indexOf('\n')!=0) {
return "\n" + s;
@@ -105,7 +85,7 @@ public class Shell extends CommandInterpreter {
}
protected String getContents() {
- return addCrForNonASCII(fixCrLf(command));
+ return addLineFeedForNonASCII(LineEndingConversion.convertEOL(command,LineEndingConversion.EOLType.Unix));
}
protected String getFileExtension() {
@@ -117,6 +97,10 @@ public class Shell extends CommandInterpreter {
return (DescriptorImpl)super.getDescriptor();
}
+ private Object readResolve() throws ObjectStreamException {
+ return new Shell(command);
+ }
+
@Extension
public static class DescriptorImpl extends BuildStepDescriptor {
/**
diff --git a/core/src/main/java/hudson/tools/AbstractCommandInstaller.java b/core/src/main/java/hudson/tools/AbstractCommandInstaller.java
index 07d91c6c0c..18431fcda1 100644
--- a/core/src/main/java/hudson/tools/AbstractCommandInstaller.java
+++ b/core/src/main/java/hudson/tools/AbstractCommandInstaller.java
@@ -48,7 +48,7 @@ public abstract class AbstractCommandInstaller extends ToolInstaller {
public AbstractCommandInstaller(String label, String command, String toolHome) {
super(label);
- this.command = fixCrLf(command);
+ this.command = command;
this.toolHome = toolHome;
}
@@ -84,19 +84,6 @@ public abstract class AbstractCommandInstaller extends ToolInstaller {
return dir.child(getToolHome());
}
- /**
- * Fix CR/LF and always make it Unix style.
- */
- //TODO: replace by a Windows style
- private static String fixCrLf(String s) {
- // eliminate CR
- int idx;
- while ((idx = s.indexOf("\r\n")) != -1) {
- s = s.substring(0, idx) + s.substring(idx + 1);
- }
- return s;
- }
-
public static abstract class Descriptor
extends ToolInstallerDescriptor {
diff --git a/core/src/main/java/hudson/tools/BatchCommandInstaller.java b/core/src/main/java/hudson/tools/BatchCommandInstaller.java
index 415dbb700f..2085fe7fa0 100644
--- a/core/src/main/java/hudson/tools/BatchCommandInstaller.java
+++ b/core/src/main/java/hudson/tools/BatchCommandInstaller.java
@@ -26,7 +26,9 @@ package hudson.tools;
import hudson.Extension;
import hudson.FilePath;
+import hudson.util.LineEndingConversion;
import org.kohsuke.stapler.DataBoundConstructor;
+import java.io.ObjectStreamException;
/**
* Installs tool via script execution of Batch script.
@@ -37,7 +39,7 @@ public class BatchCommandInstaller extends AbstractCommandInstaller {
@DataBoundConstructor
public BatchCommandInstaller(String label, String command, String toolHome) {
- super(label, command, toolHome);
+ super(label, LineEndingConversion.convertEOL(command, LineEndingConversion.EOLType.Windows), toolHome);
}
@Override
@@ -50,6 +52,10 @@ public class BatchCommandInstaller extends AbstractCommandInstaller {
String[] cmd = {"cmd", "/c", "call", script.getRemote()};
return cmd;
}
+
+ private Object readResolve() throws ObjectStreamException {
+ return new BatchCommandInstaller(getLabel(), getCommand(), getToolHome());
+ }
@Extension
public static class DescriptorImpl extends Descriptor {
diff --git a/core/src/main/java/hudson/tools/CommandInstaller.java b/core/src/main/java/hudson/tools/CommandInstaller.java
index b02137f0f5..6f2506dd04 100644
--- a/core/src/main/java/hudson/tools/CommandInstaller.java
+++ b/core/src/main/java/hudson/tools/CommandInstaller.java
@@ -26,7 +26,9 @@ package hudson.tools;
import hudson.Extension;
import hudson.FilePath;
+import hudson.util.LineEndingConversion;
import org.kohsuke.stapler.DataBoundConstructor;
+import java.io.ObjectStreamException;
/**
* Installs a tool by running an arbitrary shell command.
@@ -36,7 +38,7 @@ public class CommandInstaller extends AbstractCommandInstaller {
@DataBoundConstructor
public CommandInstaller(String label, String command, String toolHome) {
- super(label, command, toolHome);
+ super(label, LineEndingConversion.convertEOL(command, LineEndingConversion.EOLType.Unix), toolHome);
}
@Override
@@ -50,6 +52,10 @@ public class CommandInstaller extends AbstractCommandInstaller {
return cmd;
}
+ private Object readResolve() throws ObjectStreamException {
+ return new CommandInstaller(getLabel(), getCommand(), getToolHome());
+ }
+
@Extension
public static class DescriptorImpl extends Descriptor {
diff --git a/core/src/main/java/hudson/util/LineEndingConversion.java b/core/src/main/java/hudson/util/LineEndingConversion.java
new file mode 100644
index 0000000000..744ff4a4ea
--- /dev/null
+++ b/core/src/main/java/hudson/util/LineEndingConversion.java
@@ -0,0 +1,66 @@
+package hudson.util;
+
+/**
+ * Converts line endings of a string.
+ *
+ * @since TODO
+ * @author David Ruhmann
+ */
+public class LineEndingConversion {
+
+ /**
+ * Supported line ending types for conversion
+ */
+ public enum EOLType {
+ CR,
+ CRLF,
+ LF,
+ LFCR,
+ Mac,
+ Unix,
+ Windows
+ }
+
+ /**
+ * Convert line endings of a string to the given type. Default to Unix type.
+ *
+ * @param input
+ * The string containing line endings to be converted.
+ * @param type
+ * Type of line endings to convert the string into.
+ * @return
+ * String updated with the new line endings or null if given null.
+ */
+ public static String convertEOL(String input, EOLType type) {
+ if (null == input || 0 == input.length()) {
+ return input;
+ }
+ // Convert line endings to Unix LF,
+ // which also sets up the string for other conversions
+ input = input.replace("\r\n","\n");
+ input = input.replace("\r","\n");
+ switch (type) {
+ case CR:
+ case Mac:
+ // Convert line endings to CR
+ input = input.replace("\n", "\r");
+ break;
+ case CRLF:
+ case Windows:
+ // Convert line endings to Windows CR/LF
+ input = input.replace("\n", "\r\n");
+ break;
+ default:
+ case LF:
+ case Unix:
+ // Conversion already completed
+ return input;
+ case LFCR:
+ // Convert line endings to LF/CR
+ input = input.replace("\n", "\n\r");
+ break;
+ }
+ return input;
+ }
+}
+
diff --git a/core/src/main/java/jenkins/model/GlobalConfiguration.java b/core/src/main/java/jenkins/model/GlobalConfiguration.java
index b6e873f952..92eabc4ea7 100644
--- a/core/src/main/java/jenkins/model/GlobalConfiguration.java
+++ b/core/src/main/java/jenkins/model/GlobalConfiguration.java
@@ -6,17 +6,28 @@ import hudson.model.Describable;
import hudson.model.Descriptor;
/**
- * Convenient base class for extensions that contributes to the system configuration page but nothing else.
+ * Convenient base class for extensions that contributes to the system configuration page but nothing
+ * else, or to manage the global configuration of a plugin implementing several extension points.
*
*
- * All {@link Descriptor}s are capable of contributing fragment to the system config page, so
- * this extension point is is really only for those who don't want to contribute anything else.
- * If you are implementing other extension points and that would like to expose some global configuration,
- * you can do so with global.groovy from your {@link Descriptor} instance.
+ * All {@link Descriptor}s are capable of contributing fragment to the system config page. If you are
+ * implementing other extension points that need to expose some global configuration, you can do so
+ * with global.groovy or global.jelly from your {@link Descriptor} instance. However
+ * each global.* file will appear as its own section in the global configuration page.
+ *
+ *
+ * An option to present a single section for your plugin in the Jenkins global configuration page is
+ * to use this class to manage the configuration for your plugin and its extension points. To access
+ * properties defined in your GlobalConfiguration subclass, here are two possibilities:
+ *
- @{@link javax.inject.Inject} into your other {@link hudson.Extension}s (so this does not work
+ * for classes not annotated with {@link hudson.Extension})
+ * - access it via a call to
GlobalConfiguration.all().get(<your GlobalConfiguration subclass>.class)
+ *
*
* Views
*
- * Subtypes of this class should define config.groovy that gets pulled into the system configuration page.
+ * Subtypes of this class should define a config.groovy file or config.jelly file
+ * that gets pulled into the system configuration page.
*
*
* @author Kohsuke Kawaguchi
diff --git a/test/src/test/java/hudson/model/LabelLoadStatisticsQueueLengthTest.java b/test/src/test/java/hudson/model/LabelLoadStatisticsQueueLengthTest.java
new file mode 100644
index 0000000000..18a957f3db
--- /dev/null
+++ b/test/src/test/java/hudson/model/LabelLoadStatisticsQueueLengthTest.java
@@ -0,0 +1,240 @@
+package hudson.model;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import hudson.model.Descriptor.FormException;
+import hudson.model.LoadStatistics.LoadStatisticsUpdater;
+import hudson.model.MultiStageTimeSeries.TimeScale;
+import hudson.model.Node.Mode;
+import hudson.model.Queue.WaitingItem;
+import hudson.model.labels.LabelAssignmentAction;
+import hudson.model.queue.SubTask;
+import hudson.slaves.DumbSlave;
+import hudson.slaves.NodeProperty;
+import hudson.slaves.RetentionStrategy;
+
+import java.io.IOException;
+import java.util.Collections;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.jvnet.hudson.test.Bug;
+import org.jvnet.hudson.test.JenkinsRule;
+
+/**
+ * Test that a {@link Label}'s {@link LoadStatistics#queueLength} correctly
+ * reflects the queued builds.
+ */
+public class LabelLoadStatisticsQueueLengthTest {
+ private static final String LABEL_STRING = LabelLoadStatisticsQueueLengthTest.class
+ .getSimpleName();
+
+ private static final String ALT_LABEL_STRING = LABEL_STRING + "alt";
+
+ private static final String PROJECT_NAME = LabelLoadStatisticsQueueLengthTest.class
+ .getSimpleName();
+
+ private static final String PARAMETER_NAME = "parameter";
+
+ private static final Cause CAUSE = new Cause() {
+ @Override
+ public String getShortDescription() {
+ return "Build caused by test.";
+ }
+ };
+
+ @Rule
+ public JenkinsRule j = new JenkinsRule();
+
+ /**
+ * Setup
+ */
+ @Before
+ public void createNodeWithLabels() throws IOException, FormException {
+ // Node with this test's labels is required in order for the labels to
+ // be considered valid.
+ DumbSlave node = new DumbSlave(
+ LabelLoadStatisticsQueueLengthTest.class.getSimpleName(), "",
+ "", "1", Mode.NORMAL, LABEL_STRING + " " + ALT_LABEL_STRING,
+ null, RetentionStrategy.NOOP,
+ Collections.> emptyList());
+ j.getInstance().addNode(node);
+ }
+
+ /**
+ * Teardown
+ */
+ @After
+ public void clearQueue() {
+ j.getInstance().getQueue().clear();
+ }
+
+ /**
+ * Verify that when a {@link Label} is assigned to a queued build using a
+ * {@link LabelAssignmentAction}, that label's
+ * {@link LoadStatistics#queueLength} reflects the number of items in the
+ * queue, and continues to do so if the {@link Project}'s label is changed.
+ */
+ @Test
+ public void queueLengthReflectsBuildableItemsAssignedLabel()
+ throws Exception {
+ final Label label = Label.get(LABEL_STRING);
+ final Label altLabel = Label.get(ALT_LABEL_STRING);
+
+ FreeStyleProject project = createTestProject();
+
+ // Before queueing the builds the rolling queue length should be 0.
+ assertTrue(
+ "Initially the rolling queue length for the label is 0.",
+ label.loadStatistics.queueLength.getLatest(TimeScale.SEC10) == 0f);
+
+ // Add the job to the build queue several times with an assigned label.
+ for (int i = 0; i < 3; i++) {
+ project.scheduleBuild(0, CAUSE, new LabelAssignmentActionImpl(),
+ new ParametersAction(new StringParameterValue(
+ PARAMETER_NAME, String.valueOf(i))));
+ }
+
+ // Verify that the real queue length is 3.
+ assertEquals("The job is queued as often as it was scheduled.", 3, j
+ .getInstance().getQueue().getItems(project).size());
+
+ maintainQueueAndForceRunOfLoadStatisticsUpdater(project);
+
+ float labelQueueLength = label.loadStatistics.queueLength
+ .getLatest(TimeScale.SEC10);
+ assertTrue(
+ "After LoadStatisticsUpdater runs, the queue length load statistic for the label is greater than 0.",
+ labelQueueLength > 0f);
+
+ // Assign an alternate label to the project and update the load stats.
+ project.setAssignedLabel(altLabel);
+ maintainQueueAndForceRunOfLoadStatisticsUpdater(project);
+
+ // Verify that the queue length load stat continues to reflect the labels assigned to the items in the queue.
+ float labelQueueLengthNew = label.loadStatistics.queueLength
+ .getLatest(TimeScale.SEC10);
+ assertTrue(
+ "After assigning an alternate label to the job, the queue length load statistic for the queued builds should not decrease.",
+ labelQueueLengthNew >= labelQueueLength);
+ }
+
+ /**
+ * Verify that when a {@link Label} is assigned to a {@link Project}, that
+ * label's {@link LoadStatistics#queueLength} reflects the number of items
+ * in the queue scheduled for that project, and updates if the project's
+ * label is changed.
+ */
+ @Test
+ public void queueLengthReflectsJobsAssignedLabel() throws Exception {
+ final Label label = Label.get(LABEL_STRING);
+ final Label altLabel = Label.get(ALT_LABEL_STRING);
+
+ FreeStyleProject project = createTestProject();
+ // Assign a label to the job.
+ project.setAssignedLabel(label);
+
+ // Before queueing the builds the rolling queue lengths should be 0.
+ assertTrue(
+ "Initially the rolling queue length for the label is 0.",
+ label.loadStatistics.queueLength.getLatest(TimeScale.SEC10) == 0f);
+ assertTrue(
+ "Initially the rolling queue length for the alt label is 0.",
+ altLabel.loadStatistics.queueLength.getLatest(TimeScale.SEC10) == 0f);
+
+ // Add the job to the build queue several times.
+ for (int i = 0; i < 3; i++) {
+ project.scheduleBuild(0, CAUSE,
+ new ParametersAction(new StringParameterValue(
+ PARAMETER_NAME, String.valueOf(i))));
+ }
+
+ // Verify that the real queue length is 3.
+ assertEquals("The job is queued as often as it was scheduled.", 3, j
+ .getInstance().getQueue().getItems(project).size());
+
+ maintainQueueAndForceRunOfLoadStatisticsUpdater(project);
+
+ float labelQueueLength = label.loadStatistics.queueLength
+ .getLatest(TimeScale.SEC10);
+ assertTrue(
+ "After LoadStatisticsUpdater runs, the queue length load statistic for the label is greater than 0.",
+ labelQueueLength > 0f);
+
+ // Assign an alternate label to the job and update the load stats.
+ project.setAssignedLabel(altLabel);
+ maintainQueueAndForceRunOfLoadStatisticsUpdater(project);
+
+ // Verify that the queue length load stats of the labels reflect the newly project's newly assigned label.
+ float labelQueueLengthNew = label.loadStatistics.queueLength
+ .getLatest(TimeScale.SEC10);
+ assertTrue(
+ "After assigning an alternate label to the job, the queue length load statistic for the queued builds should decrease.",
+ labelQueueLengthNew < labelQueueLength);
+
+ float altLabelQueueLength = altLabel.loadStatistics.queueLength
+ .getLatest(TimeScale.SEC10);
+ assertTrue(
+ "After assigning an alternate label to the job, the queue length load statistic for the alternate label should be greater than 0.",
+ altLabelQueueLength > 0f);
+ }
+
+ private FreeStyleProject createTestProject() throws IOException {
+ FreeStyleProject project = j.createFreeStyleProject(PROJECT_NAME);
+ // In order to queue multiple builds of the job it needs to be
+ // parameterised.
+ project.addProperty(new ParametersDefinitionProperty(
+ new StringParameterDefinition(PARAMETER_NAME, "0")));
+ // Prevent builds from being queued as blocked by allowing concurrent
+ // builds.
+ project.setConcurrentBuild(true);
+ return project;
+ }
+
+ private void maintainQueueAndForceRunOfLoadStatisticsUpdater(
+ FreeStyleProject project) throws InterruptedException {
+ Queue queue = j.getInstance().getQueue();
+
+ // ensure the queued items are in the right state, i.e. buildable rather
+ // than waiting
+ int count = 0;
+ while (queue.getItem(project) instanceof WaitingItem && ++count < 100) {
+ queue.maintain();
+ Thread.sleep(10);
+ }
+
+ assertTrue(
+ "After waiting there are buildable items in the build queue.",
+ queue.getBuildableItems().size() > 0);
+
+ // create a LoadStatisticsUpdater, and run it in order to update the
+ // load stats for all the labels
+ LoadStatisticsUpdater updater = new LoadStatisticsUpdater();
+ updater.doRun();
+ }
+
+ private static class LabelAssignmentActionImpl implements
+ LabelAssignmentAction {
+ @Override
+ public String getIconFileName() {
+ return null;
+ }
+
+ @Override
+ public String getDisplayName() {
+ return LABEL_STRING + " LabelAssignmentAction";
+ }
+
+ @Override
+ public String getUrlName() {
+ return null;
+ }
+
+ @Override
+ public Label getAssignedLabel(SubTask p_task) {
+ return Label.get(LABEL_STRING);
+ }
+ }
+}
diff --git a/test/src/test/java/hudson/tasks/BatchFileTest.java b/test/src/test/java/hudson/tasks/BatchFileTest.java
new file mode 100644
index 0000000000..8cc16fd10a
--- /dev/null
+++ b/test/src/test/java/hudson/tasks/BatchFileTest.java
@@ -0,0 +1,30 @@
+package hudson.tasks;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.jvnet.hudson.test.Issue;
+import org.jvnet.hudson.test.JenkinsRule;
+
+/**
+ * Tests for the BatchFile tasks class.
+ *
+ * @author David Ruhmann
+ */
+public class BatchFileTest {
+
+ @Rule
+ public JenkinsRule rule = new JenkinsRule();
+
+ @Issue("JENKINS-7478")
+ @Test
+ public void validateBatchFileCommandEOL() throws Exception {
+ BatchFile obj = new BatchFile("echo A\necho B\recho C");
+ rule.assertStringContains(obj.getCommand(), "echo A\r\necho B\r\necho C");
+ }
+
+ @Test
+ public void validateBatchFileContents() throws Exception {
+ BatchFile obj = new BatchFile("echo A\necho B\recho C");
+ rule.assertStringContains(obj.getContents(), "echo A\r\necho B\r\necho C\r\nexit %ERRORLEVEL%");
+ }
+}
diff --git a/test/src/test/java/hudson/tasks/ShellTest.java b/test/src/test/java/hudson/tasks/ShellTest.java
index e1e0d7d867..8aadb4f4ba 100644
--- a/test/src/test/java/hudson/tasks/ShellTest.java
+++ b/test/src/test/java/hudson/tasks/ShellTest.java
@@ -7,7 +7,6 @@ import hudson.model.FreeStyleBuild;
import hudson.model.FreeStyleProject;
import org.apache.commons.io.FileUtils;
import org.jvnet.hudson.test.FakeLauncher;
-import org.jvnet.hudson.test.HudsonTestCase;
import org.jvnet.hudson.test.PretendSlave;
import java.io.File;
@@ -15,23 +14,47 @@ import java.io.IOException;
import java.io.PrintStream;
import java.util.List;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.jvnet.hudson.test.JenkinsRule;
+
/**
+ * Tests for the Shell tasks class
+ *
* @author Kohsuke Kawaguchi
*/
-public class ShellTest extends HudsonTestCase {
+public class ShellTest {
+
+ @Rule
+ public JenkinsRule rule = new JenkinsRule();
+
+ @Test
+ public void validateShellCommandEOL() throws Exception {
+ Shell obj = new Shell("echo A\r\necho B\recho C");
+ rule.assertStringContains(obj.getCommand(), "echo A\necho B\necho C");
+ }
+
+ @Test
+ public void validateShellContents() throws Exception {
+ Shell obj = new Shell("echo A\r\necho B\recho C");
+ rule.assertStringContains(obj.getContents(), "\necho A\necho B\necho C");
+ }
+
+ @Test
public void testBasic() throws Exception {
// If we're on Windows, don't bother doing this.
if (Functions.isWindows())
return;
// TODO: define a FakeLauncher implementation with easymock so that this kind of assertions can be simplified.
- PretendSlave s = createPretendSlave(new FakeLauncher() {
+ PretendSlave s = rule.createPretendSlave(new FakeLauncher() {
public Proc onLaunch(ProcStarter p) throws IOException {
// test the command line argument.
List cmds = p.cmds();
- assertEquals("/bin/sh",cmds.get(0));
- assertEquals("-xe",cmds.get(1));
- assertTrue(new File(cmds.get(2)).exists());
+ rule.assertStringContains("/bin/sh",cmds.get(0));
+ rule.assertStringContains("-xe",cmds.get(1));
+ Assert.assertTrue(new File(cmds.get(2)).exists());
// fake the execution
PrintStream ps = new PrintStream(p.stdout());
@@ -41,13 +64,13 @@ public class ShellTest extends HudsonTestCase {
return new FinishedProc(0);
}
});
- FreeStyleProject p = createFreeStyleProject();
+ FreeStyleProject p = rule.createFreeStyleProject();
p.getBuildersList().add(new Shell("echo abc"));
p.setAssignedNode(s);
- FreeStyleBuild b = assertBuildStatusSuccess(p.scheduleBuild2(0).get());
+ FreeStyleBuild b = rule.assertBuildStatusSuccess(p.scheduleBuild2(0).get());
- assertEquals(1,s.numLaunch);
- assertTrue(FileUtils.readFileToString(b.getLogFile()).contains("Hudson was here"));
+ Assert.assertEquals(1,s.numLaunch);
+ Assert.assertTrue(FileUtils.readFileToString(b.getLogFile()).contains("Hudson was here"));
}
}
diff --git a/test/src/test/java/hudson/tools/BatchCommandInstallerTest.java b/test/src/test/java/hudson/tools/BatchCommandInstallerTest.java
new file mode 100644
index 0000000000..e14a90b405
--- /dev/null
+++ b/test/src/test/java/hudson/tools/BatchCommandInstallerTest.java
@@ -0,0 +1,22 @@
+package hudson.tools;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.jvnet.hudson.test.JenkinsRule;
+
+/**
+ * Tests for the BatchCommandInstaller tools class.
+ *
+ * @author David Ruhmann
+ */
+public class BatchCommandInstallerTest {
+
+ @Rule
+ public JenkinsRule rule = new JenkinsRule();
+
+ @Test
+ public void validateBatchCommandInstallerCommandEOL() throws Exception {
+ BatchCommandInstaller obj = new BatchCommandInstaller("", "echo A\necho B\recho C", "");
+ rule.assertStringContains(obj.getCommand(), "echo A\r\necho B\r\necho C");
+ }
+}
diff --git a/test/src/test/java/hudson/tools/CommandInstallerTest.java b/test/src/test/java/hudson/tools/CommandInstallerTest.java
new file mode 100644
index 0000000000..7a58c1ec8c
--- /dev/null
+++ b/test/src/test/java/hudson/tools/CommandInstallerTest.java
@@ -0,0 +1,22 @@
+package hudson.tools;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.jvnet.hudson.test.JenkinsRule;
+
+/**
+ * Tests for the CommandInstaller tools class.
+ *
+ * @author David Ruhmann
+ */
+public class CommandInstallerTest {
+
+ @Rule
+ public JenkinsRule rule = new JenkinsRule();
+
+ @Test
+ public void validateCommandInstallerCommandEOL() throws Exception {
+ CommandInstaller obj = new CommandInstaller("", "echo A\r\necho B\recho C", "");
+ rule.assertStringContains(obj.getCommand(), "echo A\necho B\necho C");
+ }
+}
diff --git a/test/src/test/java/hudson/util/LineEndingConversionTest.java b/test/src/test/java/hudson/util/LineEndingConversionTest.java
new file mode 100644
index 0000000000..66773fbbb9
--- /dev/null
+++ b/test/src/test/java/hudson/util/LineEndingConversionTest.java
@@ -0,0 +1,28 @@
+package hudson.util;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.jvnet.hudson.test.Issue;
+import org.jvnet.hudson.test.JenkinsRule;
+
+/**
+ * Tests for the LineEndingConversion util class.
+ *
+ * @author David Ruhmann
+ */
+public class LineEndingConversionTest {
+
+ @Rule
+ public JenkinsRule rule = new JenkinsRule();
+
+ @Issue("JENKINS-7478")
+ @Test
+ public void validateWindowsEOL() throws Exception {
+ rule.assertStringContains(LineEndingConversion.convertEOL("echo A\necho B\recho C", LineEndingConversion.EOLType.Windows), "echo A\r\necho B\r\necho C");
+ }
+
+ @Test
+ public void validateUnixEOL() throws Exception {
+ rule.assertStringContains(LineEndingConversion.convertEOL("echo A\r\necho B\recho C", LineEndingConversion.EOLType.Unix), "echo A\necho B\necho C");
+ }
+}