UtilTest.java 23.3 KB
Newer Older
K
kohsuke 已提交
1 2
/*
 * The MIT License
3
 *
4 5
 * Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi,
 * Daniel Dyer, Erik Ramfelt, Richard Bair, id:cactusman
6
 *
K
kohsuke 已提交
7 8 9 10 11 12
 * 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:
13
 *
K
kohsuke 已提交
14 15
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
16
 *
K
kohsuke 已提交
17 18 19 20 21 22 23 24
 * 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.
 */
K
kohsuke 已提交
25 26
package hudson;

27
import hudson.model.TaskListener;
28
import hudson.os.WindowsUtil;
J
Josh Soref 已提交
29
import hudson.util.StreamTaskListener;
30
import org.apache.commons.io.FileUtils;
31 32
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
33
import org.junit.Assume;
J
Josh Soref 已提交
34
import org.junit.Rule;
35
import org.junit.Test;
M
Matt Sicker 已提交
36
import org.junit.rules.ExpectedException;
J
Josh Soref 已提交
37
import org.junit.rules.TemporaryFolder;
38
import org.jvnet.hudson.test.Issue;
39

J
Josh Soref 已提交
40 41 42 43 44 45 46 47 48
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.PosixFilePermissions;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
49

J
Josh Soref 已提交
50 51 52
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.startsWith;
import static org.junit.Assert.*;
K
kohsuke 已提交
53 54 55 56

/**
 * @author Kohsuke Kawaguchi
 */
57
public class UtilTest {
58 59 60

    @Rule public TemporaryFolder tmp = new TemporaryFolder();

M
Matt Sicker 已提交
61 62
    @Rule public ExpectedException expectedException = ExpectedException.none();

63
    @Test
K
kohsuke 已提交
64 65 66
    public void testReplaceMacro() {
        Map<String,String> m = new HashMap<String,String>();
        m.put("A","a");
67
        m.put("A.B","a-b");
K
kohsuke 已提交
68 69
        m.put("AA","aa");
        m.put("B","B");
70 71
        m.put("DOLLAR", "$");
        m.put("ENCLOSED", "a${A}");
K
kohsuke 已提交
72 73 74 75 76 77

        // longest match
        assertEquals("aa",Util.replaceMacro("$AA",m));

        // invalid keys are ignored
        assertEquals("$AAB",Util.replaceMacro("$AAB",m));
78

79 80 81
        assertEquals("aaB",Util.replaceMacro("${AA}B",m));
        assertEquals("${AAB}",Util.replaceMacro("${AAB}",m));

82 83
        // $ escaping
        assertEquals("asd$${AA}dd", Util.replaceMacro("asd$$$${AA}dd",m));
K
kohsuke 已提交
84 85
        assertEquals("$", Util.replaceMacro("$$",m));
        assertEquals("$$", Util.replaceMacro("$$$$",m));
86

87 88 89
        // dots
        assertEquals("a.B", Util.replaceMacro("$A.B", m));
        assertEquals("a-b", Util.replaceMacro("${A.B}", m));
90

91
    	// test that more complex scenarios work
92
        assertEquals("/a/B/aa", Util.replaceMacro("/$A/$B/$AA",m));
93 94
        assertEquals("a-aa", Util.replaceMacro("$A-$AA",m));
        assertEquals("/a/foo/can/B/you-believe_aa~it?", Util.replaceMacro("/$A/foo/can/$B/you-believe_$AA~it?",m));
95
        assertEquals("$$aa$Ba${A}$it", Util.replaceMacro("$$$DOLLAR${AA}$$B${ENCLOSED}$it",m));
K
kohsuke 已提交
96
    }
97

98
    @Test
99 100 101 102 103 104
    public void testTimeSpanString() {
        // Check that amounts less than 365 days are not rounded up to a whole year.
        // In the previous implementation there were 360 days in a year.
        // We're still working on the assumption that a month is 30 days, so there will
        // be 5 days at the end of the year that will be "12 months" but not "1 year".
        // First check 359 days.
C
cactusman 已提交
105
        assertEquals(Messages.Util_month(11), Util.getTimeSpanString(31017600000L));
106
        // And 362 days.
C
cactusman 已提交
107
        assertEquals(Messages.Util_month(12), Util.getTimeSpanString(31276800000L));
108 109

        // 11.25 years - Check that if the first unit has 2 or more digits, a second unit isn't used.
C
cactusman 已提交
110
        assertEquals(Messages.Util_year(11), Util.getTimeSpanString(354780000000L));
111
        // 9.25 years - Check that if the first unit has only 1 digit, a second unit is used.
C
cactusman 已提交
112
        assertEquals(Messages.Util_year(9)+ " " + Messages.Util_month(3), Util.getTimeSpanString(291708000000L));
113
        // 67 seconds
C
cactusman 已提交
114
        assertEquals(Messages.Util_minute(1) + " " + Messages.Util_second(7), Util.getTimeSpanString(67000L));
115
        // 17 seconds - Check that times less than a minute only use seconds.
C
cactusman 已提交
116
        assertEquals(Messages.Util_second(17), Util.getTimeSpanString(17000L));
117 118 119 120 121
        // 1712ms -> 1.7sec
        assertEquals(Messages.Util_second(1.7), Util.getTimeSpanString(1712L));
        // 171ms -> 0.17sec
        assertEquals(Messages.Util_second(0.17), Util.getTimeSpanString(171L));
        // 101ms -> 0.10sec
122
        assertEquals(Messages.Util_second(0.1), Util.getTimeSpanString(101L));
123 124 125 126
        // 17ms
        assertEquals(Messages.Util_millisecond(17), Util.getTimeSpanString(17L));
        // 1ms
        assertEquals(Messages.Util_millisecond(1), Util.getTimeSpanString(1L));
127 128 129 130 131 132 133 134 135
        // Test HUDSON-2843 (locale with comma as fraction separator got exception for <10 sec)
        Locale saveLocale = Locale.getDefault();
        Locale.setDefault(Locale.GERMANY);
        try {
            // Just verifying no exception is thrown:
            assertNotNull("German locale", Util.getTimeSpanString(1234));
            assertNotNull("German locale <1 sec", Util.getTimeSpanString(123));
        }
        finally { Locale.setDefault(saveLocale); }
136
    }
C
cactusman 已提交
137

138 139 140 141

    /**
     * Test that Strings that contain spaces are correctly URL encoded.
     */
142
    @Test
143 144 145 146 147
    public void testEncodeSpaces() {
        final String urlWithSpaces = "http://hudson/job/Hudson Job";
        String encoded = Util.encode(urlWithSpaces);
        assertEquals(encoded, "http://hudson/job/Hudson%20Job");
    }
148

149 150 151
    /**
     * Test the rawEncode() method.
     */
152
    @Test
153 154 155 156
    public void testRawEncode() {
        String[] data = {  // Alternating raw,encoded
            "abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnopqrstuvwxyz",
            "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
157
            "01234567890!@$&*()-_=+',.", "01234567890!@$&*()-_=+',.",
158 159
            " \"#%/:;<>?", "%20%22%23%25%2F%3A%3B%3C%3E%3F",
            "[\\]^`{|}~", "%5B%5C%5D%5E%60%7B%7C%7D%7E",
160
            "d\u00E9velopp\u00E9s", "d%C3%A9velopp%C3%A9s",
A
Alex Earl 已提交
161
            "Foo \uD800\uDF98 Foo", "Foo%20%F0%90%8E%98%20Foo"
162 163 164 165 166 167
        };
        for (int i = 0; i < data.length; i += 2) {
            assertEquals("test " + i, data[i + 1], Util.rawEncode(data[i]));
        }
    }

168 169 170
    /**
     * Test the tryParseNumber() method.
     */
171
    @Test
172 173 174 175 176 177
    public void testTryParseNumber() {
        assertEquals("Successful parse did not return the parsed value", 20, Util.tryParseNumber("20", 10).intValue());
        assertEquals("Failed parse did not return the default value", 10, Util.tryParseNumber("ss", 10).intValue());
        assertEquals("Parsing empty string did not return the default value", 10, Util.tryParseNumber("", 10).intValue());
        assertEquals("Parsing null string did not return the default value", 10, Util.tryParseNumber(null, 10).intValue());
    }
K
kohsuke 已提交
178

179
    @Test
K
kohsuke 已提交
180
    public void testSymlink() throws Exception {
M
Matt Sicker 已提交
181
        Assume.assumeFalse(Functions.isWindows());
K
kohsuke 已提交
182

183 184
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        StreamTaskListener l = new StreamTaskListener(baos);
185
        File d = tmp.getRoot();
K
kohsuke 已提交
186 187
        try {
            new FilePath(new File(d, "a")).touch(0);
188
            assertNull(Util.resolveSymlink(new File(d, "a")));
K
kohsuke 已提交
189
            Util.createSymlink(d,"a","x", l);
190
            assertEquals("a",Util.resolveSymlink(new File(d,"x")));
K
kohsuke 已提交
191 192 193 194

            // test a long name
            StringBuilder buf = new StringBuilder(768);
            for( int i=0; i<768; i++)
195
                buf.append((char)('0'+(i%10)));
K
kohsuke 已提交
196
            Util.createSymlink(d,buf.toString(),"x", l);
197 198

            String log = baos.toString();
199
            if (log.length() > 0)
200 201
                System.err.println("log output: " + log);

202
            assertEquals(buf.toString(),Util.resolveSymlink(new File(d,"x")));
203 204


205 206 207
            // test linking from another directory
            File anotherDir = new File(d,"anotherDir");
            assertTrue("Couldn't create "+anotherDir,anotherDir.mkdir());
208

209
            Util.createSymlink(d,"a","anotherDir/link",l);
210
            assertEquals("a",Util.resolveSymlink(new File(d,"anotherDir/link")));
211 212

            // JENKINS-12331: either a bug in createSymlink or this isn't supposed to work:
213
            //assertTrue(Util.isSymlink(new File(d,"anotherDir/link")));
214 215 216 217 218 219 220 221

            File external = File.createTempFile("something", "");
            try {
                Util.createSymlink(d, external.getAbsolutePath(), "outside", l);
                assertEquals(external.getAbsolutePath(), Util.resolveSymlink(new File(d, "outside")));
            } finally {
                assertTrue(external.delete());
            }
222 223 224 225
        } finally {
            Util.deleteRecursive(d);
        }
    }
226

227
    @Test
228
    public void testIsSymlink() throws IOException, InterruptedException {
M
Matt Sicker 已提交
229
        Assume.assumeFalse(Functions.isWindows());
230

231 232
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        StreamTaskListener l = new StreamTaskListener(baos);
233
        File d = tmp.getRoot();
234 235 236 237
        try {
            new FilePath(new File(d, "original")).touch(0);
            assertFalse(Util.isSymlink(new File(d, "original")));
            Util.createSymlink(d,"original","link", l);
238

239
            assertTrue(Util.isSymlink(new File(d, "link")));
240

241 242 243 244
            // test linking to another directory
            File dir = new File(d,"dir");
            assertTrue("Couldn't create "+dir,dir.mkdir());
            assertFalse(Util.isSymlink(new File(d,"dir")));
245

246 247
            File anotherDir = new File(d,"anotherDir");
            assertTrue("Couldn't create "+anotherDir,anotherDir.mkdir());
248

249 250 251
            Util.createSymlink(d,"dir","anotherDir/symlinkDir",l);
            // JENKINS-12331: either a bug in createSymlink or this isn't supposed to work:
            // assertTrue(Util.isSymlink(new File(d,"anotherDir/symlinkDir")));
K
kohsuke 已提交
252
        } finally {
253 254 255 256
            Util.deleteRecursive(d);
        }
    }

257 258 259
    @Test
    public void testIsSymlink_onWindows_junction() throws Exception {
        Assume.assumeTrue("Uses Windows-specific features", Functions.isWindows());
M
Matt Sicker 已提交
260
        File targetDir = tmp.newFolder("targetDir");
261
        File d = tmp.newFolder("dir");
262
        File junction = WindowsUtil.createJunction(new File(d, "junction"), targetDir);
M
Matt Sicker 已提交
263 264 265 266 267 268 269 270 271 272 273
        assertTrue(Util.isSymlink(junction));
    }

    @Test
    @Issue("JENKINS-55448")
    public void testIsSymlink_ParentIsJunction() throws IOException, InterruptedException {
        Assume.assumeTrue("Uses Windows-specific features", Functions.isWindows());
        File targetDir = tmp.newFolder();
        File file = new File(targetDir, "test-file");
        new FilePath(file).touch(System.currentTimeMillis());
        File dir = tmp.newFolder();
274
        File junction = WindowsUtil.createJunction(new File(dir, "junction"), targetDir);
M
Matt Sicker 已提交
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289

        assertTrue(Util.isSymlink(junction));
        assertFalse(Util.isSymlink(file));
    }

    @Test
    @Issue("JENKINS-55448")
    public void testIsSymlink_ParentIsSymlink() throws IOException, InterruptedException {
        File folder = tmp.newFolder();
        File file = new File(folder, "test-file");
        new FilePath(file).touch(System.currentTimeMillis());
        Path link = tmp.getRoot().toPath().resolve("sym-link");
        Path pathWithSymlinkParent = Files.createSymbolicLink(link, folder.toPath()).resolve("test-file");
        assertTrue(Util.isSymlink(link));
        assertFalse(Util.isSymlink(pathWithSymlinkParent));
290 291
    }

292 293
    @Test
    public void testHtmlEscape() {
S
Seiji Sogabe 已提交
294
        assertEquals("<br>", Util.escape("\n"));
295
        assertEquals("&lt;a&gt;", Util.escape("<a>"));
296
        assertEquals("&#039;&quot;", Util.escape("'\""));
S
Seiji Sogabe 已提交
297 298
        assertEquals("&nbsp; ", Util.escape("  "));
    }
299

300 301 302 303
    /**
     * Compute 'known-correct' digests and see if I still get them when computed concurrently
     * to another digest.
     */
304
    @Issue("JENKINS-10346")
305
    @Test
306 307 308
    public void testDigestThreadSafety() throws InterruptedException {
    	String a = "abcdefgh";
    	String b = "123456789";
309

310 311
    	String digestA = Util.getDigestOf(a);
    	String digestB = Util.getDigestOf(b);
312

313 314
    	DigesterThread t1 = new DigesterThread(a, digestA);
    	DigesterThread t2 = new DigesterThread(b, digestB);
315

316 317
    	t1.start();
    	t2.start();
318

319 320
    	t1.join();
    	t2.join();
321

322 323 324 325 326 327 328
    	if (t1.error != null) {
    		fail(t1.error);
    	}
    	if (t2.error != null) {
    		fail(t2.error);
    	}
    }
329

330 331 332
    private static class DigesterThread extends Thread {
    	private String string;
		private String expectedDigest;
333

334 335 336 337 338 339
		private String error;

		public DigesterThread(String string, String expectedDigest) {
    		this.string = string;
    		this.expectedDigest = expectedDigest;
    	}
340

341 342 343 344 345 346 347 348 349 350
		public void run() {
			for (int i=0; i < 1000; i++) {
				String digest = Util.getDigestOf(this.string);
				if (!this.expectedDigest.equals(digest)) {
					this.error = "Expected " + this.expectedDigest + ", but got " + digest;
					break;
				}
			}
		}
    }
K
Kohsuke Kawaguchi 已提交
351

352
    @Test
K
Kohsuke Kawaguchi 已提交
353 354 355 356 357 358 359 360 361
    public void testIsAbsoluteUri() {
        assertTrue(Util.isAbsoluteUri("http://foobar/"));
        assertTrue(Util.isAbsoluteUri("mailto:kk@kohsuke.org"));
        assertTrue(Util.isAbsoluteUri("d123://test/"));
        assertFalse(Util.isAbsoluteUri("foo/bar/abc:def"));
        assertFalse(Util.isAbsoluteUri("foo?abc:def"));
        assertFalse(Util.isAbsoluteUri("foo#abc:def"));
        assertFalse(Util.isAbsoluteUri("foo/bar"));
    }
362

363 364 365 366 367 368 369 370 371 372 373 374
    @Test
    @Issue("SECURITY-276")
    public void testIsSafeToRedirectTo() {
        assertFalse(Util.isSafeToRedirectTo("http://foobar/"));
        assertFalse(Util.isSafeToRedirectTo("mailto:kk@kohsuke.org"));
        assertFalse(Util.isSafeToRedirectTo("d123://test/"));
        assertFalse(Util.isSafeToRedirectTo("//google.com"));

        assertTrue(Util.isSafeToRedirectTo("foo/bar/abc:def"));
        assertTrue(Util.isSafeToRedirectTo("foo?abc:def"));
        assertTrue(Util.isSafeToRedirectTo("foo#abc:def"));
        assertTrue(Util.isSafeToRedirectTo("foo/bar"));
375 376 377 378 379 380
        assertTrue(Util.isSafeToRedirectTo("/"));
        assertTrue(Util.isSafeToRedirectTo("/foo"));
        assertTrue(Util.isSafeToRedirectTo(".."));
        assertTrue(Util.isSafeToRedirectTo("../.."));
        assertTrue(Util.isSafeToRedirectTo("/#foo"));
        assertTrue(Util.isSafeToRedirectTo("/?foo"));
381 382
    }

383 384 385 386 387 388 389 390 391
    @Test
    public void loadProperties() throws IOException {

        assertEquals(0, Util.loadProperties("").size());

        Properties p = Util.loadProperties("k.e.y=va.l.ue");
        assertEquals(p.toString(), "va.l.ue", p.get("k.e.y"));
        assertEquals(p.toString(), 1, p.size());
    }
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443

    @Test
    public void isRelativePathUnix() {
        assertThat("/", not(aRelativePath()));
        assertThat("/foo/bar", not(aRelativePath()));
        assertThat("/foo/../bar", not(aRelativePath()));
        assertThat("", aRelativePath());
        assertThat(".", aRelativePath());
        assertThat("..", aRelativePath());
        assertThat("./foo", aRelativePath());
        assertThat("./foo/bar", aRelativePath());
        assertThat("./foo/bar/", aRelativePath());
    }

    @Test
    public void isRelativePathWindows() {
        assertThat("\\", aRelativePath());
        assertThat("\\foo\\bar", aRelativePath());
        assertThat("\\foo\\..\\bar", aRelativePath());
        assertThat("", aRelativePath());
        assertThat(".", aRelativePath());
        assertThat(".\\foo", aRelativePath());
        assertThat(".\\foo\\bar", aRelativePath());
        assertThat(".\\foo\\bar\\", aRelativePath());
        assertThat("\\\\foo", aRelativePath());
        assertThat("\\\\foo\\", not(aRelativePath()));
        assertThat("\\\\foo\\c", not(aRelativePath()));
        assertThat("C:", aRelativePath());
        assertThat("z:", aRelativePath());
        assertThat("0:", aRelativePath());
        assertThat("c:.", aRelativePath());
        assertThat("c:\\", not(aRelativePath()));
        assertThat("c:/", not(aRelativePath()));
    }

    private static RelativePathMatcher aRelativePath() {
        return new RelativePathMatcher();
    }

    private static class RelativePathMatcher extends BaseMatcher<String> {

        @Override
        public boolean matches(Object item) {
            return Util.isRelativePath((String) item);
        }

        @Override
        public void describeTo(Description description) {
            description.appendText("a relative path");
        }
    }

444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481
    @Test
    public void testIsDescendant() throws IOException {
        File root;
        File other;
        if (Functions.isWindows()) {
            root = new File("C:\\Temp");
            other = new File("C:\\Windows");
        } else {
            root = new File("/tmp");
            other = new File("/usr");

        }
        assertTrue(Util.isDescendant(root, new File(root,"child")));
        assertTrue(Util.isDescendant(root, new File(new File(root,"child"), "grandchild")));
        assertFalse(Util.isDescendant(root, other));
        assertFalse(Util.isDescendant(root, new File(other, "child")));

        assertFalse(Util.isDescendant(new File(root,"child"), root));
        assertFalse(Util.isDescendant(new File(new File(root,"child"), "grandchild"), root));

        //.. whithin root
        File convoluted = new File(root, "child");
        convoluted = new File(convoluted, "..");
        convoluted = new File(convoluted, "child");
        assertTrue(Util.isDescendant(root, convoluted));

        //.. going outside of root
        convoluted = new File(root, "..");
        convoluted = new File(convoluted, other.getName());
        convoluted = new File(convoluted, "child");
        assertFalse(Util.isDescendant(root, convoluted));

        //. on root
        assertTrue(Util.isDescendant(new File(root, "."), new File(root, "child")));
        //. on both
        assertTrue(Util.isDescendant(new File(root, "."), new File(new File(root, "child"), ".")));
    }

482 483 484 485 486 487 488 489 490 491 492 493 494 495 496
    @Test
    public void testModeToPermissions() throws Exception {
        assertEquals(PosixFilePermissions.fromString("rwxrwxrwx"), Util.modeToPermissions(0777));
        assertEquals(PosixFilePermissions.fromString("rwxr-xrwx"), Util.modeToPermissions(0757));
        assertEquals(PosixFilePermissions.fromString("rwxr-x---"), Util.modeToPermissions(0750));
        assertEquals(PosixFilePermissions.fromString("r-xr-x---"), Util.modeToPermissions(0550));
        assertEquals(PosixFilePermissions.fromString("r-xr-----"), Util.modeToPermissions(0540));
        assertEquals(PosixFilePermissions.fromString("--xr-----"), Util.modeToPermissions(0140));
        assertEquals(PosixFilePermissions.fromString("--xr---w-"), Util.modeToPermissions(0142));
        assertEquals(PosixFilePermissions.fromString("--xr--rw-"), Util.modeToPermissions(0146));
        assertEquals(PosixFilePermissions.fromString("-wxr--rw-"), Util.modeToPermissions(0346));
        assertEquals(PosixFilePermissions.fromString("---------"), Util.modeToPermissions(0000));

        assertEquals("Non-permission bits should be ignored", PosixFilePermissions.fromString("r-xr-----"), Util.modeToPermissions(0100540));

M
Matt Sicker 已提交
497 498
        expectedException.expectMessage(startsWith("Invalid mode"));
        Util.modeToPermissions(01777);
499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514
    }

    @Test
    public void testPermissionsToMode() throws Exception {
        assertEquals(0777, Util.permissionsToMode(PosixFilePermissions.fromString("rwxrwxrwx")));
        assertEquals(0757, Util.permissionsToMode(PosixFilePermissions.fromString("rwxr-xrwx")));
        assertEquals(0750, Util.permissionsToMode(PosixFilePermissions.fromString("rwxr-x---")));
        assertEquals(0550, Util.permissionsToMode(PosixFilePermissions.fromString("r-xr-x---")));
        assertEquals(0540, Util.permissionsToMode(PosixFilePermissions.fromString("r-xr-----")));
        assertEquals(0140, Util.permissionsToMode(PosixFilePermissions.fromString("--xr-----")));
        assertEquals(0142, Util.permissionsToMode(PosixFilePermissions.fromString("--xr---w-")));
        assertEquals(0146, Util.permissionsToMode(PosixFilePermissions.fromString("--xr--rw-")));
        assertEquals(0346, Util.permissionsToMode(PosixFilePermissions.fromString("-wxr--rw-")));
        assertEquals(0000, Util.permissionsToMode(PosixFilePermissions.fromString("---------")));
    }

515
    @Test
516 517 518 519 520 521 522 523 524
    public void testDifferenceDays() throws Exception {
        Date may_6_10am = parseDate("2018-05-06 10:00:00"); 
        Date may_6_11pm55 = parseDate("2018-05-06 23:55:00"); 
        Date may_7_01am = parseDate("2018-05-07 01:00:00"); 
        Date may_7_11pm = parseDate("2018-05-07 11:00:00"); 
        Date may_8_08am = parseDate("2018-05-08 08:00:00"); 
        Date june_3_08am = parseDate("2018-06-03 08:00:00"); 
        Date june_9_08am = parseDate("2018-06-09 08:00:00"); 
        Date june_9_08am_nextYear = parseDate("2019-06-09 08:00:00"); 
W
Wadeck Follonier 已提交
525 526 527 528 529 530 531 532 533 534 535
        
        assertEquals(0, Util.daysBetween(may_6_10am, may_6_11pm55));
        assertEquals(1, Util.daysBetween(may_6_10am, may_7_01am));
        assertEquals(1, Util.daysBetween(may_6_11pm55, may_7_01am));
        assertEquals(2, Util.daysBetween(may_6_10am, may_8_08am));
        assertEquals(1, Util.daysBetween(may_7_11pm, may_8_08am));
        
        // larger scale
        assertEquals(28, Util.daysBetween(may_6_10am, june_3_08am));
        assertEquals(34, Util.daysBetween(may_6_10am, june_9_08am));
        assertEquals(365 + 34, Util.daysBetween(may_6_10am, june_9_08am_nextYear));
536 537
        
        // reverse order
W
Wadeck Follonier 已提交
538
        assertEquals(-1, Util.daysBetween(may_8_08am, may_7_11pm));
539
    }
540 541 542 543
    
    private Date parseDate(String dateString) throws ParseException {
        return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").parse(dateString);
    }
544 545

    @Test
546
    @Issue("SECURITY-904")
547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581
    public void resolveSymlinkToFile() throws Exception {
        //  root
        //      /a
        //          /aa
        //              aa.txt
        //          /_b => symlink to /root/b
        //      /b
        //          /_a => symlink to /root/a
        File root = tmp.getRoot();
        File a = new File(root, "a");
        File aa = new File(a, "aa");
        aa.mkdirs();
        File aaTxt = new File(aa, "aa.txt");
        FileUtils.write(aaTxt, "aa");

        File b = new File(root, "b");
        b.mkdir();

        File _a = new File(b, "_a");
        Util.createSymlink(_a.getParentFile(), a.getAbsolutePath(), _a.getName(), TaskListener.NULL);

        File _b = new File(a, "_b");
        Util.createSymlink(_b.getParentFile(), b.getAbsolutePath(), _b.getName(), TaskListener.NULL);

        assertTrue(Files.isSymbolicLink(_a.toPath()));
        assertTrue(Files.isSymbolicLink(_b.toPath()));

        // direct symlinks are resolved
        assertEquals(Util.resolveSymlinkToFile(_a), a);
        assertEquals(Util.resolveSymlinkToFile(_b), b);

        // intermediate symlinks are NOT resolved
        assertNull(Util.resolveSymlinkToFile(new File(_a, "aa")));
        assertNull(Util.resolveSymlinkToFile(new File(_a, "aa/aa.txt")));
    }
K
kohsuke 已提交
582
}