Merge branch 'master' into JENKINS-26445-pagination-search

* master: (256 commits)
  Noting #1817
  Noting #1825
  updated changelog for release
  [maven-release-plugin] prepare for next development iteration
  [maven-release-plugin] prepare release jenkins-1.629
  [JENKINS-30139] Oleg wants to keep the data accessible so we have to go for immutable
  [JENKINS-30139] Forgot to change this one
  [JENKINS-30139] Add @Restricted to prevent external use of internal data
  [JENKINS-30139] Switch from a big fat lock to a concurrent collection
  [JENKINS-30374] Update obsolete help message
  updated changelog for release
  [maven-release-plugin] prepare for next development iteration
  [maven-release-plugin] prepare release jenkins-1.628
  noting merge of PR#1816 - use slf4j interceptors
  Manage commons-logging and log4j as provided, include slf4j adapters for tests.
  Exclude commons-logging and log4j in dangerous scopes compile and runtime in plugins as well.
  Only exclude commons-logging and log4j in dangerous scopes compile and runtime.
  Reintroduce enforcer rules as suggested by @jtnord.
  Document allBuilds for Job remote access API
  Include slf4j-jdk14 during tests.
  ...

Conflicts:
	war/src/main/webapp/scripts/hudson-behavior.js
This commit is contained in:
tfennelly committed 2015-09-16 09:34:46 +01:00
commit a305e3dc93
223 files changed
+3309 -978

No files matched your search

+32 -1
View File
@@ -371,7 +371,7 @@ public class FilePathTest {
// Decompress
FilePath outDir = new FilePath(temp.newFolder(filePrefix + "_out"));
final FilePath outFile = outDir.child(tempFile.getName());
tmpDirPath.child( filePrefix + ".tar").untar(outDir, TarCompression.NONE);
tmpDirPath.child(tarFile.getName()).untar(outDir, TarCompression.NONE);
assertEquals("Result file after the roundtrip differs from the initial file",
new FilePath(tempFile).digest(), outFile.digest());
}
@@ -659,4 +659,35 @@ public class FilePathTest {
// test conflict subdir
src.moveAllChildrenTo(dst);
}
@Issue("JENKINS-10629")
@Test
public void testEOFbrokenFlush() throws IOException, InterruptedException {
final File srcFolder = temp.newFolder("src");
// simulate magic structure with magic sizes:
// |- dir/pom.xml (2049)
// |- pom.xml (2049)
// \- small.tar (1537)
final File smallTar = new File(srcFolder, "small.tar");
givenSomeContentInFile(smallTar, 1537);
final File dir = new File(srcFolder, "dir");
dir.mkdirs();
final File pomFile = new File(dir, "pom.xml");
givenSomeContentInFile(pomFile, 2049);
FileUtils.copyFileToDirectory(pomFile, srcFolder);
final File archive = temp.newFile("archive.tar");
// Compress archive
final FilePath tmpDirPath = new FilePath(srcFolder);
int tarred = tmpDirPath.tar(new FileOutputStream(archive), "**");
assertEquals("One file should have been compressed", 3, tarred);
// Decompress
final File dstFolder = temp.newFolder("dst");
dstFolder.mkdirs();
FilePath outDir = new FilePath(dstFolder);
// and now fail when flush is bad!
tmpDirPath.child("../" + archive.getName()).untar(outDir, TarCompression.NONE);
}
}
+125 -10
View File
@@ -24,17 +24,26 @@
*/
package hudson;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.Locale;
import java.util.Properties;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicReference;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.*;
import org.apache.commons.io.FileUtils;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.junit.Assume;
@@ -42,12 +51,13 @@ import org.junit.Test;
import org.jvnet.hudson.test.Issue;
import hudson.util.StreamTaskListener;
import java.io.FileOutputStream;
import java.io.OutputStream;
import org.junit.Rule;
import org.junit.internal.AssumptionViolatedException;
import org.junit.rules.TemporaryFolder;
import com.google.common.collect.Lists;
/**
* @author Kohsuke Kawaguchi
*/
@@ -248,7 +258,17 @@ public class UtilTest {
}
}
@Test public void deleteFile() throws Exception {
@Test
public void testDeleteFile() throws Exception {
File f = tmp.newFile();
// Test: File is deleted
mkfiles(f);
Util.deleteFile(f);
assertFalse("f exists after calling Util.deleteFile", f.exists());
}
@Test
public void testDeleteFile_onWindows() throws Exception {
Assume.assumeTrue(Functions.isWindows());
Class<?> c;
try {
@@ -256,23 +276,118 @@ public class UtilTest {
} catch (ClassNotFoundException x) {
throw new AssumptionViolatedException("prior to JDK 7", x);
}
File d = tmp.getRoot();
try {
File f = new File(d, "f");
OutputStream os = new FileOutputStream(f);
File f = tmp.newFile();
// Test: If we cannot delete a file, we throw explaining why
mkfiles(f);
lockFileForDeletion(f);
try {
Util.deleteFile(f);
fail("should not have been deletable");
} catch (IOException x) {
assertEquals(c, x.getClass());
} finally {
os.close();
assertThat(calcExceptionHierarchy(x), hasItem(c));
assertThat(x.getMessage(), containsString(f.getPath()));
}
} finally {
Util.deleteRecursive(d);
unlockFilesForDeletion();
}
}
@Test
public void testDeleteRecursive() throws Exception {
final File dir = tmp.newFolder();
final File d1 = new File(dir, "d1");
final File d2 = new File(dir, "d2");
final File f1 = new File(dir, "f1");
final File d1f1 = new File(d1, "d1f1");
final File d2f2 = new File(d2, "d1f2");
// Test: Files and directories are deleted
mkdirs(dir, d1, d2);
mkfiles(f1, d1f1, d2f2);
Util.deleteRecursive(dir);
assertFalse("dir exists", dir.exists());
}
@Test
public void testDeleteRecursive_onWindows() throws Exception {
Assume.assumeTrue(Functions.isWindows());
final File dir = tmp.newFolder();
final File d1 = new File(dir, "d1");
final File d2 = new File(dir, "d2");
final File f1 = new File(dir, "f1");
final File d1f1 = new File(d1, "d1f1");
final File d2f2 = new File(d2, "d1f2");
try {
// Test: If we cannot delete a file, we throw
mkdirs(dir, d1, d2);
mkfiles(f1, d1f1, d2f2);
lockFileForDeletion(d1f1);
try {
Util.deleteRecursive(dir);
fail("Expected IOException");
} catch (IOException x) {
assertTrue("dir exists", dir.exists());
assertTrue("d1 exists", d1.exists());
assertTrue("d1f1 exists", d1f1.exists());
assertThat(x.getMessage(), containsString(dir.getPath()));
}
} finally {
unlockFilesForDeletion();
}
}
/** Creates multiple directories. */
private static void mkdirs(File... dirs) {
for( File d : dirs ) {
d.mkdir();
assertTrue(d.getPath(), d.isDirectory());
}
}
/** Creates multiple files, each containing their filename as text content. */
private static void mkfiles(File... files) throws IOException {
for( File f : files )
FileUtils.write(f, f.getName());
}
/** Means of unlocking all the files we have locked, indexed by {@link File}. */
private final Map<File, Callable<Void>> unlockFileCallables = new HashMap<File, Callable<Void>>();
/** Prevents a file from being deleted, so we can stress the deletion code's retries. */
private void lockFileForDeletion(File f) throws IOException, InterruptedException {
assert !unlockFileCallables.containsKey(f) : f + " is already locked." ;
// Limitation: Only works on Windows. On unix we can delete anything we can create.
// On unix, can't use "chmod a-w" on the dir as the code-under-test undoes that.
// On unix, can't use "chattr +i" because that needs root.
// On unix, can't use "chattr +u" because ext fs ignores it.
// On Windows, we can't delete files that are open for reading, so we use that.
assert Functions.isWindows();
final InputStream s = new FileInputStream(f);
unlockFileCallables.put(f, new Callable<Void>() {
public Void call() throws IOException { s.close(); return null; };
});
}
/** Undoes a call to {@link #lockFileForDeletion(File)}. */
private void unlockFileForDeletion(File f) throws Exception {
unlockFileCallables.remove(f).call();
}
/** Undoes all calls to {@link #lockFileForDeletion(File)}. */
private void unlockFilesForDeletion() throws Exception {
while( !unlockFileCallables.isEmpty() ) {
unlockFileForDeletion(unlockFileCallables.keySet().iterator().next());
}
}
/** Returns all classes in the exception hierarchy. */
private static Iterable<Class<?>> calcExceptionHierarchy(Throwable t) {
final List<Class<?>> result = Lists.newArrayList();
for( ; t!=null ; t = t.getCause())
result.add(t.getClass());
return result;
}
@Test
public void testHtmlEscape() {
assertEquals("<br>", Util.escape("\n"));
@@ -9,6 +9,7 @@ import java.util.Collection;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.*;
/**
@@ -44,6 +45,27 @@ public class ItemsTest {
assertEquals("foo/bar", Items.getCanonicalName(foo, "./bar"));
assertEquals("foo/bar/baz/qux", Items.getCanonicalName(foo_bar, "baz/qux"));
assertEquals("foo/baz/qux", Items.getCanonicalName(foo_bar, "../baz/qux"));
try {
Items.getCanonicalName(root, "..");
fail();
} catch (IllegalArgumentException ex) {
assertEquals("Illegal relative path '..' within context ''", ex.getMessage());
}
try {
Items.getCanonicalName(foo, "../..");
fail();
} catch (IllegalArgumentException ex) {
assertEquals("Illegal relative path '../..' within context 'foo'", ex.getMessage());
}
try {
Items.getCanonicalName(root, "foo/../..");
fail();
} catch (IllegalArgumentException ex) {
assertEquals("Illegal relative path 'foo/../..' within context ''", ex.getMessage());
}
}
@Test
@@ -89,7 +89,7 @@ public class ParametersActionTest {
@Test
@Issue("JENKINS-15094")
public void checkNullParamaterValues() {
public void checkNullParameterValues() {
SubTask subtask = mock(SubTask.class);
Build build = mock(Build.class);
@@ -0,0 +1,42 @@
/*
* The MIT License
*
* Copyright (c) 2015 Kanstantsin Shautsou
*
* 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.triggers;
import org.junit.Test;
import org.jvnet.hudson.test.Issue;
/**
* @author Kanstantsin Shautsou
*/
public class SCMTriggerTest {
@Issue({"JENKINS-29790", "JENKINS-29945"})
@Test
public void testNoNPE() throws Exception {
final SCMTrigger scmTrigger = new SCMTrigger("");
scmTrigger.run();
scmTrigger.run(null);
scmTrigger.getProjectActions();
}
}
@@ -0,0 +1,39 @@
/*
* The MIT License
*
* Copyright (c) 2015 Kanstantsin Shautsou
*
* 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.triggers;
import antlr.ANTLRException;
import org.junit.Test;
import org.jvnet.hudson.test.Issue;
/**
* @author Kanstantsin Shautsou
*/
public class TimerTriggerTest {
@Issue("JENKINS-29790")
@Test
public void testNoNPE() throws ANTLRException {
new TimerTrigger("").run();
}
}
@@ -41,7 +41,7 @@ public class ArgumentListBuilderTest {
builder.add("arg");
builder.add("other", "arguments");
assertFalse("There shouldnt be any masked arguments", builder.hasMaskedArguments());
assertFalse("There should not be any masked arguments", builder.hasMaskedArguments());
boolean[] array = builder.toMaskArray();
assertNotNull("The mask array should not be null", array);
assertThat("The mask array was incorrect", array, is(new boolean[] { false, false, false }));
@@ -166,7 +166,7 @@ public class ArgumentListBuilderTest {
ArgumentListBuilder builder = new ArgumentListBuilder();
builder.addKeyValuePairs(null, KEY_VALUES);
assertFalse("There shouldnt be any masked arguments", builder.hasMaskedArguments());
assertFalse("There should not be any masked arguments", builder.hasMaskedArguments());
boolean[] array = builder.toMaskArray();
assertNotNull("The mask array should not be null", array);
assertThat("The mask array was incorrect", array, is(new boolean[] { false, false, false }));
@@ -0,0 +1,43 @@
/*
* The MIT License
*
* Copyright (c) 2015, 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.util;
import org.junit.Assert;
import org.junit.Test;
/**
* @author <a href="mailto:tom.fennelly@gmail.com">tom.fennelly@gmail.com</a>
*/
public class MultipartFormDataParserTest {
@Test
public void testIsMultipart() throws Exception {
Assert.assertFalse("Wrongly identified \"multipart/form-data\"", MultipartFormDataParser.isMultiPartForm(null));
Assert.assertFalse("Wrongly identified \"multipart/form-data\"", MultipartFormDataParser.isMultiPartForm("blah"));
Assert.assertTrue("Failed to identify \"multipart/form-data\"", MultipartFormDataParser.isMultiPartForm("multipart/form-data"));
Assert.assertTrue("Failed to identify \"multipart/form-data\"", MultipartFormDataParser.isMultiPartForm("multipart/form-data;"));
Assert.assertTrue("Failed to identify \"multipart/form-data\"", MultipartFormDataParser.isMultiPartForm("multipart/form-data; boundary=----WebKitFormBoundary8OOwv1Xp4c5XkBmD"));
}
}
@@ -128,6 +128,26 @@ public class RunIdMigratorTest {
assertEquals("{1=→2014-01-02_03-04-05, 2014-01-02_03-04-05={build.xml='<?xml version='1.0' encoding='UTF-8'?>\n<run>\n <stuff>ok</stuff>\n <number>1</number>\n <otherstuff>ok</otherstuff>\n</run>'}}", summarize());
}
@Test public void reverseMatrixAfterNewBuilds() throws Exception {
File root = dir;
dir = new File(dir, "jobs/someproject/Environment=prod/builds");
write("1/build.xml", "<?xml version='1.0' encoding='UTF-8'?>\n<run>\n <stuff>ok</stuff>\n <timestamp>1388649845000</timestamp>\n <otherstuff>ok</otherstuff>\n</run>");
write("legacyIds", "");
assertEquals("{1={build.xml='<?xml version='1.0' encoding='UTF-8'?>\n<run>\n <stuff>ok</stuff>\n <timestamp>1388649845000</timestamp>\n <otherstuff>ok</otherstuff>\n</run>'}, legacyIds=''}", summarize());
RunIdMigrator.main(root.getAbsolutePath());
assertEquals("{1=→2014-01-02_03-04-05, 2014-01-02_03-04-05={build.xml='<?xml version='1.0' encoding='UTF-8'?>\n<run>\n <stuff>ok</stuff>\n <number>1</number>\n <otherstuff>ok</otherstuff>\n</run>'}}", summarize());
}
@Test public void reverseMavenAfterNewBuilds() throws Exception {
File root = dir;
dir = new File(dir, "jobs/someproject/test$test/builds");
write("1/build.xml", "<?xml version='1.0' encoding='UTF-8'?>\n<run>\n <stuff>ok</stuff>\n <timestamp>1388649845000</timestamp>\n <otherstuff>ok</otherstuff>\n</run>");
write("legacyIds", "");
assertEquals("{1={build.xml='<?xml version='1.0' encoding='UTF-8'?>\n<run>\n <stuff>ok</stuff>\n <timestamp>1388649845000</timestamp>\n <otherstuff>ok</otherstuff>\n</run>'}, legacyIds=''}", summarize());
RunIdMigrator.main(root.getAbsolutePath());
assertEquals("{1=→2014-01-02_03-04-05, 2014-01-02_03-04-05={build.xml='<?xml version='1.0' encoding='UTF-8'?>\n<run>\n <stuff>ok</stuff>\n <number>1</number>\n <otherstuff>ok</otherstuff>\n</run>'}}", summarize());
}
// TODO test sane recovery from various error conditions
private void write(String file, String text) throws Exception {