UtilTest.java 13.1 KB
Newer Older
K
kohsuke 已提交
1 2 3
/*
 * The MIT License
 * 
4 5
 * Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi,
 * Daniel Dyer, Erik Ramfelt, Richard Bair, id:cactusman
K
kohsuke 已提交
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
 * 
 * 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.
 */
K
kohsuke 已提交
25 26 27 28
package hudson;

import java.util.Map;
import java.util.HashMap;
29
import java.util.Locale;
30
import java.util.Properties;
31
import java.io.ByteArrayOutputStream;
K
kohsuke 已提交
32
import java.io.File;
33
import java.io.IOException;
K
kohsuke 已提交
34

35
import static org.junit.Assert.*;
36
import org.junit.Assume;
37
import org.junit.Test;
38 39
import org.jvnet.hudson.test.Bug;

K
kohsuke 已提交
40
import hudson.util.StreamTaskListener;
41 42
import java.io.FileOutputStream;
import java.io.OutputStream;
43
import org.junit.Rule;
44
import org.junit.internal.AssumptionViolatedException;
45
import org.junit.rules.TemporaryFolder;
K
kohsuke 已提交
46 47 48 49

/**
 * @author Kohsuke Kawaguchi
 */
50
public class UtilTest {
51 52 53

    @Rule public TemporaryFolder tmp = new TemporaryFolder();

54
    @Test
K
kohsuke 已提交
55 56 57
    public void testReplaceMacro() {
        Map<String,String> m = new HashMap<String,String>();
        m.put("A","a");
58
        m.put("A.B","a-b");
K
kohsuke 已提交
59 60
        m.put("AA","aa");
        m.put("B","B");
61 62
        m.put("DOLLAR", "$");
        m.put("ENCLOSED", "a${A}");
K
kohsuke 已提交
63 64 65 66 67 68

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

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

70 71 72
        assertEquals("aaB",Util.replaceMacro("${AA}B",m));
        assertEquals("${AAB}",Util.replaceMacro("${AAB}",m));

73 74
        // $ escaping
        assertEquals("asd$${AA}dd", Util.replaceMacro("asd$$$${AA}dd",m));
K
kohsuke 已提交
75 76
        assertEquals("$", Util.replaceMacro("$$",m));
        assertEquals("$$", Util.replaceMacro("$$$$",m));
77 78 79 80
        
        // dots
        assertEquals("a.B", Util.replaceMacro("$A.B", m));
        assertEquals("a-b", Util.replaceMacro("${A.B}", m));
81

82
    	// test that more complex scenarios work
83
        assertEquals("/a/B/aa", Util.replaceMacro("/$A/$B/$AA",m));
84 85
        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));
86
        assertEquals("$$aa$Ba${A}$it", Util.replaceMacro("$$$DOLLAR${AA}$$B${ENCLOSED}$it",m));
K
kohsuke 已提交
87
    }
88

89
    @Test
90 91 92 93 94 95
    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 已提交
96
        assertEquals(Messages.Util_month(11), Util.getTimeSpanString(31017600000L));
97
        // And 362 days.
C
cactusman 已提交
98
        assertEquals(Messages.Util_month(12), Util.getTimeSpanString(31276800000L));
99 100

        // 11.25 years - Check that if the first unit has 2 or more digits, a second unit isn't used.
C
cactusman 已提交
101
        assertEquals(Messages.Util_year(11), Util.getTimeSpanString(354780000000L));
102
        // 9.25 years - Check that if the first unit has only 1 digit, a second unit is used.
C
cactusman 已提交
103
        assertEquals(Messages.Util_year(9)+ " " + Messages.Util_month(3), Util.getTimeSpanString(291708000000L));
104
        // 67 seconds
C
cactusman 已提交
105
        assertEquals(Messages.Util_minute(1) + " " + Messages.Util_second(7), Util.getTimeSpanString(67000L));
106
        // 17 seconds - Check that times less than a minute only use seconds.
C
cactusman 已提交
107
        assertEquals(Messages.Util_second(17), Util.getTimeSpanString(17000L));
108 109 110 111 112
        // 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
113
        assertEquals(Messages.Util_second(0.1), Util.getTimeSpanString(101L));
114 115 116 117
        // 17ms
        assertEquals(Messages.Util_millisecond(17), Util.getTimeSpanString(17L));
        // 1ms
        assertEquals(Messages.Util_millisecond(1), Util.getTimeSpanString(1L));
118 119 120 121 122 123 124 125 126
        // 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); }
127
    }
C
cactusman 已提交
128

129 130 131 132

    /**
     * Test that Strings that contain spaces are correctly URL encoded.
     */
133
    @Test
134 135 136 137 138
    public void testEncodeSpaces() {
        final String urlWithSpaces = "http://hudson/job/Hudson Job";
        String encoded = Util.encode(urlWithSpaces);
        assertEquals(encoded, "http://hudson/job/Hudson%20Job");
    }
139
        
140 141 142
    /**
     * Test the rawEncode() method.
     */
143
    @Test
144 145 146 147
    public void testRawEncode() {
        String[] data = {  // Alternating raw,encoded
            "abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnopqrstuvwxyz",
            "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
148
            "01234567890!@$&*()-_=+',.", "01234567890!@$&*()-_=+',.",
149 150
            " \"#%/:;<>?", "%20%22%23%25%2F%3A%3B%3C%3E%3F",
            "[\\]^`{|}~", "%5B%5C%5D%5E%60%7B%7C%7D%7E",
151
            "d\u00E9velopp\u00E9s", "d%C3%A9velopp%C3%A9s",
152 153 154 155 156 157
        };
        for (int i = 0; i < data.length; i += 2) {
            assertEquals("test " + i, data[i + 1], Util.rawEncode(data[i]));
        }
    }

158 159 160
    /**
     * Test the tryParseNumber() method.
     */
161
    @Test
162 163 164 165 166 167
    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 已提交
168

169
    @Test
K
kohsuke 已提交
170
    public void testSymlink() throws Exception {
171
        Assume.assumeTrue(!Functions.isWindows());
K
kohsuke 已提交
172

173 174
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        StreamTaskListener l = new StreamTaskListener(baos);
175
        File d = tmp.getRoot();
K
kohsuke 已提交
176 177
        try {
            new FilePath(new File(d, "a")).touch(0);
178
            assertNull(Util.resolveSymlink(new File(d, "a")));
K
kohsuke 已提交
179
            Util.createSymlink(d,"a","x", l);
180
            assertEquals("a",Util.resolveSymlink(new File(d,"x")));
K
kohsuke 已提交
181 182 183 184

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

            String log = baos.toString();
189
            if (log.length() > 0)
190 191
                System.err.println("log output: " + log);

192
            assertEquals(buf.toString(),Util.resolveSymlink(new File(d,"x")));
193 194 195 196 197 198 199
            
            
            // test linking from another directory
            File anotherDir = new File(d,"anotherDir");
            assertTrue("Couldn't create "+anotherDir,anotherDir.mkdir());
            
            Util.createSymlink(d,"a","anotherDir/link",l);
200
            assertEquals("a",Util.resolveSymlink(new File(d,"anotherDir/link")));
201 202 203
            
            // JENKINS-12331: either a bug in createSymlink or this isn't supposed to work: 
            //assertTrue(Util.isSymlink(new File(d,"anotherDir/link")));
204 205 206 207 208 209 210 211

            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());
            }
212 213 214 215 216
        } finally {
            Util.deleteRecursive(d);
        }
    }
    
217
    @Test
218 219 220 221 222
    public void testIsSymlink() throws IOException, InterruptedException {
        Assume.assumeTrue(!Functions.isWindows());
        
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        StreamTaskListener l = new StreamTaskListener(baos);
223
        File d = tmp.getRoot();
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
        try {
            new FilePath(new File(d, "original")).touch(0);
            assertFalse(Util.isSymlink(new File(d, "original")));
            Util.createSymlink(d,"original","link", l);
            
            assertTrue(Util.isSymlink(new File(d, "link")));
            
            // 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")));
            
            File anotherDir = new File(d,"anotherDir");
            assertTrue("Couldn't create "+anotherDir,anotherDir.mkdir());
            
            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 已提交
242
        } finally {
243 244 245 246 247 248 249 250 251 252 253 254
            Util.deleteRecursive(d);
        }
    }

    @Test public void deleteFile() throws Exception {
        Assume.assumeTrue(Functions.isWindows());
        Class<?> c;
        try {
            c = Class.forName("java.nio.file.FileSystemException");
        } catch (ClassNotFoundException x) {
            throw new AssumptionViolatedException("prior to JDK 7", x);
        }
255
        File d = tmp.getRoot();
256 257 258 259 260 261 262 263 264 265 266 267
        try {
            File f = new File(d, "f");
            OutputStream os = new FileOutputStream(f);
            try {
                Util.deleteFile(f);
                fail("should not have been deletable");
            } catch (IOException x) {
                assertEquals(c, x.getClass());
            } finally {
                os.close();
            }
        } finally {
K
kohsuke 已提交
268 269 270
            Util.deleteRecursive(d);
        }
    }
S
Seiji Sogabe 已提交
271

272 273
    @Test
    public void testHtmlEscape() {
S
Seiji Sogabe 已提交
274
        assertEquals("<br>", Util.escape("\n"));
275
        assertEquals("&lt;a&gt;", Util.escape("<a>"));
276
        assertEquals("&#039;&quot;", Util.escape("'\""));
S
Seiji Sogabe 已提交
277 278
        assertEquals("&nbsp; ", Util.escape("  "));
    }
279 280 281 282 283 284
    
    /**
     * Compute 'known-correct' digests and see if I still get them when computed concurrently
     * to another digest.
     */
    @Bug(10346)
285
    @Test
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
    public void testDigestThreadSafety() throws InterruptedException {
    	String a = "abcdefgh";
    	String b = "123456789";
    	
    	String digestA = Util.getDigestOf(a);
    	String digestB = Util.getDigestOf(b);
    	
    	DigesterThread t1 = new DigesterThread(a, digestA);
    	DigesterThread t2 = new DigesterThread(b, digestB);
    	
    	t1.start();
    	t2.start();
    	
    	t1.join();
    	t2.join();
    	
    	if (t1.error != null) {
    		fail(t1.error);
    	}
    	if (t2.error != null) {
    		fail(t2.error);
    	}
    }
    
    private static class DigesterThread extends Thread {
    	private String string;
		private String expectedDigest;
		
		private String error;

		public DigesterThread(String string, String expectedDigest) {
    		this.string = string;
    		this.expectedDigest = expectedDigest;
    	}
		
		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 已提交
331

332
    @Test
K
Kohsuke Kawaguchi 已提交
333 334 335 336 337 338 339 340 341
    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"));
    }
342 343 344 345 346 347 348 349 350 351

    @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());
    }
K
kohsuke 已提交
352
}