View Javadoc
1   /*
2    * Copyright (C) 2009, Google Inc.
3    * and other copyright owners as documented in the project's IP log.
4    *
5    * This program and the accompanying materials are made available
6    * under the terms of the Eclipse Distribution License v1.0 which
7    * accompanies this distribution, is reproduced below, and is
8    * available at http://www.eclipse.org/org/documents/edl-v10.php
9    *
10   * All rights reserved.
11   *
12   * Redistribution and use in source and binary forms, with or
13   * without modification, are permitted provided that the following
14   * conditions are met:
15   *
16   * - Redistributions of source code must retain the above copyright
17   *   notice, this list of conditions and the following disclaimer.
18   *
19   * - Redistributions in binary form must reproduce the above
20   *   copyright notice, this list of conditions and the following
21   *   disclaimer in the documentation and/or other materials provided
22   *   with the distribution.
23   *
24   * - Neither the name of the Eclipse Foundation, Inc. nor the
25   *   names of its contributors may be used to endorse or promote
26   *   products derived from this software without specific prior
27   *   written permission.
28   *
29   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42   */
43  
44  package org.eclipse.jgit.lib;
45  
46  import static org.eclipse.jgit.junit.Assert.assertEquals;
47  import static org.junit.Assert.assertEquals;
48  
49  import org.eclipse.jgit.junit.MockSystemReader;
50  import org.eclipse.jgit.util.SystemReader;
51  import org.junit.Test;
52  
53  public class ValidRefNameTest {
54  	private static void assertValid(boolean exp, String name) {
55  		SystemReader instance = SystemReader.getInstance();
56  		try {
57  			setUnixSystemReader();
58  			assertEquals("\"" + name + "\"", exp,
59  					Repository.isValidRefName(name));
60  			setWindowsSystemReader();
61  			assertEquals("\"" + name + "\"", exp,
62  					Repository.isValidRefName(name));
63  		} finally {
64  			SystemReader.setInstance(instance);
65  		}
66  	}
67  
68  	private static void setWindowsSystemReader() {
69  		SystemReader.setInstance(new MockSystemReader() {
70  			{
71  				setWindows();
72  			}
73  		});
74  	}
75  
76  	private static void setUnixSystemReader() {
77  		SystemReader.setInstance(new MockSystemReader() {
78  			{
79  				setUnix();
80  			}
81  		});
82  	}
83  
84  	private static void assertInvalidOnWindows(String name) {
85  		SystemReader instance = SystemReader.getInstance();
86  		try {
87  			setUnixSystemReader();
88  			assertEquals("\"" + name + "\"", true,
89  					Repository.isValidRefName(name));
90  			setWindowsSystemReader();
91  			assertEquals("\"" + name + "\"", false,
92  					Repository.isValidRefName(name));
93  		} finally {
94  			SystemReader.setInstance(instance);
95  		}
96  	}
97  
98  	private static void assertNormalized(final String name,
99  			final String expected) {
100 		SystemReader instance = SystemReader.getInstance();
101 		try {
102 			setUnixSystemReader();
103 			String normalized = Repository.normalizeBranchName(name);
104 			assertEquals("Normalization of " + name, expected, normalized);
105 			assertEquals("\"" + normalized + "\"", true,
106 					Repository.isValidRefName(Constants.R_HEADS + normalized));
107 			setWindowsSystemReader();
108 			normalized = Repository.normalizeBranchName(name);
109 			assertEquals("Normalization of " + name, expected, normalized);
110 			assertEquals("\"" + normalized + "\"", true,
111 					Repository.isValidRefName(Constants.R_HEADS + normalized));
112 		} finally {
113 			SystemReader.setInstance(instance);
114 		}
115 	}
116 
117 	@Test
118 	public void testEmptyString() {
119 		assertValid(false, "");
120 		assertValid(false, "/");
121 	}
122 
123 	@Test
124 	public void testMustHaveTwoComponents() {
125 		assertValid(false, "master");
126 		assertValid(true, "heads/master");
127 	}
128 
129 	@Test
130 	public void testValidHead() {
131 		assertValid(true, "refs/heads/master");
132 		assertValid(true, "refs/heads/pu");
133 		assertValid(true, "refs/heads/z");
134 		assertValid(true, "refs/heads/FoO");
135 	}
136 
137 	@Test
138 	public void testValidTag() {
139 		assertValid(true, "refs/tags/v1.0");
140 	}
141 
142 	@Test
143 	public void testNoLockSuffix() {
144 		assertValid(false, "refs/heads/master.lock");
145 	}
146 
147 	@Test
148 	public void testNoDirectorySuffix() {
149 		assertValid(false, "refs/heads/master/");
150 	}
151 
152 	@Test
153 	public void testNoSpace() {
154 		assertValid(false, "refs/heads/i haz space");
155 	}
156 
157 	@Test
158 	public void testNoAsciiControlCharacters() {
159 		for (char c = '\0'; c < ' '; c++)
160 			assertValid(false, "refs/heads/mast" + c + "er");
161 	}
162 
163 	@Test
164 	public void testNoBareDot() {
165 		assertValid(false, "refs/heads/.");
166 		assertValid(false, "refs/heads/..");
167 		assertValid(false, "refs/heads/./master");
168 		assertValid(false, "refs/heads/../master");
169 	}
170 
171 	@Test
172 	public void testNoLeadingOrTrailingDot() {
173 		assertValid(false, ".");
174 		assertValid(false, "refs/heads/.bar");
175 		assertValid(false, "refs/heads/..bar");
176 		assertValid(false, "refs/heads/bar.");
177 	}
178 
179 	@Test
180 	public void testContainsDot() {
181 		assertValid(true, "refs/heads/m.a.s.t.e.r");
182 		assertValid(false, "refs/heads/master..pu");
183 	}
184 
185 	@Test
186 	public void testNoMagicRefCharacters() {
187 		assertValid(false, "refs/heads/master^");
188 		assertValid(false, "refs/heads/^master");
189 		assertValid(false, "^refs/heads/master");
190 
191 		assertValid(false, "refs/heads/master~");
192 		assertValid(false, "refs/heads/~master");
193 		assertValid(false, "~refs/heads/master");
194 
195 		assertValid(false, "refs/heads/master:");
196 		assertValid(false, "refs/heads/:master");
197 		assertValid(false, ":refs/heads/master");
198 	}
199 
200 	@Test
201 	public void testShellGlob() {
202 		assertValid(false, "refs/heads/master?");
203 		assertValid(false, "refs/heads/?master");
204 		assertValid(false, "?refs/heads/master");
205 
206 		assertValid(false, "refs/heads/master[");
207 		assertValid(false, "refs/heads/[master");
208 		assertValid(false, "[refs/heads/master");
209 
210 		assertValid(false, "refs/heads/master*");
211 		assertValid(false, "refs/heads/*master");
212 		assertValid(false, "*refs/heads/master");
213 	}
214 
215 	@Test
216 	public void testValidSpecialCharacterUnixs() {
217 		assertValid(true, "refs/heads/!");
218 		assertValid(true, "refs/heads/#");
219 		assertValid(true, "refs/heads/$");
220 		assertValid(true, "refs/heads/%");
221 		assertValid(true, "refs/heads/&");
222 		assertValid(true, "refs/heads/'");
223 		assertValid(true, "refs/heads/(");
224 		assertValid(true, "refs/heads/)");
225 		assertValid(true, "refs/heads/+");
226 		assertValid(true, "refs/heads/,");
227 		assertValid(true, "refs/heads/-");
228 		assertValid(true, "refs/heads/;");
229 		assertValid(true, "refs/heads/=");
230 		assertValid(true, "refs/heads/@");
231 		assertValid(true, "refs/heads/]");
232 		assertValid(true, "refs/heads/_");
233 		assertValid(true, "refs/heads/`");
234 		assertValid(true, "refs/heads/{");
235 		assertValid(true, "refs/heads/}");
236 
237 		// This is valid on UNIX, but not on Windows
238 		// hence we make in invalid due to non-portability
239 		//
240 		assertValid(false, "refs/heads/\\");
241 
242 		// More invalid characters on Windows, but we allow them
243 		assertInvalidOnWindows("refs/heads/\"");
244 		assertInvalidOnWindows("refs/heads/<");
245 		assertInvalidOnWindows("refs/heads/>");
246 		assertInvalidOnWindows("refs/heads/|");
247 	}
248 
249 	@Test
250 	public void testUnicodeNames() {
251 		assertValid(true, "refs/heads/\u00e5ngstr\u00f6m");
252 	}
253 
254 	@Test
255 	public void testRefLogQueryIsValidRef() {
256 		assertValid(false, "refs/heads/master@{1}");
257 		assertValid(false, "refs/heads/master@{1.hour.ago}");
258 	}
259 
260 	@Test
261 	public void testWindowsReservedNames() {
262 		// re-using code from DirCacheCheckoutTest, hence
263 		// only testing for one of the special names.
264 		assertInvalidOnWindows("refs/heads/con");
265 		assertInvalidOnWindows("refs/con/x");
266 		assertInvalidOnWindows("con/heads/x");
267 		assertValid(true, "refs/heads/conx");
268 		assertValid(true, "refs/heads/xcon");
269 	}
270 
271 	@Test
272 	public void testNormalizeBranchName() {
273 		assertEquals("", Repository.normalizeBranchName(null));
274 		assertEquals("", Repository.normalizeBranchName(""));
275 		assertNormalized("Bug 12345::::Hello World", "Bug_12345-Hello_World");
276 		assertNormalized("Bug 12345 :::: Hello World", "Bug_12345_Hello_World");
277 		assertNormalized("Bug 12345 :::: Hello::: World",
278 				"Bug_12345_Hello-World");
279 		assertNormalized(":::Bug 12345 - Hello World", "Bug_12345_Hello_World");
280 		assertNormalized("---Bug 12345 - Hello World", "Bug_12345_Hello_World");
281 		assertNormalized("Bug 12345 ---- Hello --- World",
282 				"Bug_12345_Hello_World");
283 		assertNormalized("Bug 12345 - Hello World!", "Bug_12345_Hello_World!");
284 		assertNormalized("Bug 12345 : Hello World!", "Bug_12345_Hello_World!");
285 		assertNormalized("Bug 12345 _ Hello World!", "Bug_12345_Hello_World!");
286 		assertNormalized("Bug 12345   -       Hello World!",
287 				"Bug_12345_Hello_World!");
288 		assertNormalized(" Bug 12345   -   Hello World! ",
289 				"Bug_12345_Hello_World!");
290 		assertNormalized(" Bug 12345   -   Hello World!   ",
291 				"Bug_12345_Hello_World!");
292 		assertNormalized("Bug 12345   -   Hello______ World!",
293 				"Bug_12345_Hello_World!");
294 		assertNormalized("_Bug 12345 - Hello World!", "Bug_12345_Hello_World!");
295 	}
296 
297 	@Test
298 	public void testNormalizeWithSlashes() {
299 		assertNormalized("foo/bar/baz", "foo/bar/baz");
300 		assertNormalized("foo/bar.lock/baz.lock", "foo/bar_lock/baz_lock");
301 		assertNormalized("foo/.git/.git~1/bar", "foo/git/git-1/bar");
302 		assertNormalized(".foo/aux/con/com3.txt/com0/prn/lpt1",
303 				"foo/+aux/+con/+com3.txt/com0/+prn/+lpt1");
304 		assertNormalized("foo/../bar///.--ba...z", "foo/bar/ba.z");
305 	}
306 
307 	@Test
308 	public void testNormalizeWithUnicode() {
309 		assertNormalized("f\u00f6\u00f6/.b\u00e0r/*[<>|^~/b\u00e9\\z",
310 				"f\u00f6\u00f6/b\u00e0r/b\u00e9-z");
311 		assertNormalized("\u5165\u53e3 entrance;/.\u51fa\u53e3_*ex*it*/",
312 				"\u5165\u53e3_entrance;/\u51fa\u53e3_ex-it");
313 	}
314 
315 	@Test
316 	public void testNormalizeAlreadyValidRefName() {
317 		assertNormalized("refs/heads/m.a.s.t.e.r", "refs/heads/m.a.s.t.e.r");
318 		assertNormalized("refs/tags/v1.0-20170223", "refs/tags/v1.0-20170223");
319 	}
320 
321 	@Test
322 	public void testNormalizeTrimmedUnicodeAlreadyValidRefName() {
323 		assertNormalized(" \u00e5ngstr\u00f6m\t", "\u00e5ngstr\u00f6m");
324 	}
325 }