View Javadoc
1   /*
2    * Copyright (C) 2009, Google Inc. and others
3    *
4    * This program and the accompanying materials are made available under the
5    * terms of the Eclipse Distribution License v. 1.0 which is available at
6    * https://www.eclipse.org/org/documents/edl-v10.php.
7    *
8    * SPDX-License-Identifier: BSD-3-Clause
9    */
10  
11  package org.eclipse.jgit.lib;
12  
13  import static org.eclipse.jgit.junit.Assert.assertEquals;
14  import static org.junit.Assert.assertEquals;
15  
16  import org.eclipse.jgit.junit.MockSystemReader;
17  import org.eclipse.jgit.util.SystemReader;
18  import org.junit.Test;
19  
20  public class ValidRefNameTest {
21  	private static void assertValid(boolean exp, String name) {
22  		SystemReader instance = SystemReader.getInstance();
23  		try {
24  			setUnixSystemReader();
25  			assertEquals("\"" + name + "\"", exp,
26  					Repository.isValidRefName(name));
27  			setWindowsSystemReader();
28  			assertEquals("\"" + name + "\"", exp,
29  					Repository.isValidRefName(name));
30  		} finally {
31  			SystemReader.setInstance(instance);
32  		}
33  	}
34  
35  	private static void setWindowsSystemReader() {
36  		SystemReader.setInstance(new MockSystemReader() {
37  			{
38  				setWindows();
39  			}
40  		});
41  	}
42  
43  	private static void setUnixSystemReader() {
44  		SystemReader.setInstance(new MockSystemReader() {
45  			{
46  				setUnix();
47  			}
48  		});
49  	}
50  
51  	private static void assertInvalidOnWindows(String name) {
52  		SystemReader instance = SystemReader.getInstance();
53  		try {
54  			setUnixSystemReader();
55  			assertEquals("\"" + name + "\"", true,
56  					Repository.isValidRefName(name));
57  			setWindowsSystemReader();
58  			assertEquals("\"" + name + "\"", false,
59  					Repository.isValidRefName(name));
60  		} finally {
61  			SystemReader.setInstance(instance);
62  		}
63  	}
64  
65  	private static void assertNormalized(final String name,
66  			final String expected) {
67  		SystemReader instance = SystemReader.getInstance();
68  		try {
69  			setUnixSystemReader();
70  			String normalized = Repository.normalizeBranchName(name);
71  			assertEquals("Normalization of " + name, expected, normalized);
72  			assertEquals("\"" + normalized + "\"", true,
73  					Repository.isValidRefName(Constants.R_HEADS + normalized));
74  			setWindowsSystemReader();
75  			normalized = Repository.normalizeBranchName(name);
76  			assertEquals("Normalization of " + name, expected, normalized);
77  			assertEquals("\"" + normalized + "\"", true,
78  					Repository.isValidRefName(Constants.R_HEADS + normalized));
79  		} finally {
80  			SystemReader.setInstance(instance);
81  		}
82  	}
83  
84  	@Test
85  	public void testEmptyString() {
86  		assertValid(false, "");
87  		assertValid(false, "/");
88  	}
89  
90  	@Test
91  	public void testMustHaveTwoComponents() {
92  		assertValid(false, "master");
93  		assertValid(true, "heads/master");
94  	}
95  
96  	@Test
97  	public void testValidHead() {
98  		assertValid(true, "refs/heads/master");
99  		assertValid(true, "refs/heads/pu");
100 		assertValid(true, "refs/heads/z");
101 		assertValid(true, "refs/heads/FoO");
102 	}
103 
104 	@Test
105 	public void testValidTag() {
106 		assertValid(true, "refs/tags/v1.0");
107 	}
108 
109 	@Test
110 	public void testNoLockSuffix() {
111 		assertValid(false, "refs/heads/master.lock");
112 	}
113 
114 	@Test
115 	public void testNoDirectorySuffix() {
116 		assertValid(false, "refs/heads/master/");
117 	}
118 
119 	@Test
120 	public void testNoSpace() {
121 		assertValid(false, "refs/heads/i haz space");
122 	}
123 
124 	@Test
125 	public void testNoAsciiControlCharacters() {
126 		for (char c = '\0'; c < ' '; c++)
127 			assertValid(false, "refs/heads/mast" + c + "er");
128 	}
129 
130 	@Test
131 	public void testNoBareDot() {
132 		assertValid(false, "refs/heads/.");
133 		assertValid(false, "refs/heads/..");
134 		assertValid(false, "refs/heads/./master");
135 		assertValid(false, "refs/heads/../master");
136 	}
137 
138 	@Test
139 	public void testNoLeadingOrTrailingDot() {
140 		assertValid(false, ".");
141 		assertValid(false, "refs/heads/.bar");
142 		assertValid(false, "refs/heads/..bar");
143 		assertValid(false, "refs/heads/bar.");
144 	}
145 
146 	@Test
147 	public void testContainsDot() {
148 		assertValid(true, "refs/heads/m.a.s.t.e.r");
149 		assertValid(false, "refs/heads/master..pu");
150 	}
151 
152 	@Test
153 	public void testNoMagicRefCharacters() {
154 		assertValid(false, "refs/heads/master^");
155 		assertValid(false, "refs/heads/^master");
156 		assertValid(false, "^refs/heads/master");
157 
158 		assertValid(false, "refs/heads/master~");
159 		assertValid(false, "refs/heads/~master");
160 		assertValid(false, "~refs/heads/master");
161 
162 		assertValid(false, "refs/heads/master:");
163 		assertValid(false, "refs/heads/:master");
164 		assertValid(false, ":refs/heads/master");
165 	}
166 
167 	@Test
168 	public void testShellGlob() {
169 		assertValid(false, "refs/heads/master?");
170 		assertValid(false, "refs/heads/?master");
171 		assertValid(false, "?refs/heads/master");
172 
173 		assertValid(false, "refs/heads/master[");
174 		assertValid(false, "refs/heads/[master");
175 		assertValid(false, "[refs/heads/master");
176 
177 		assertValid(false, "refs/heads/master*");
178 		assertValid(false, "refs/heads/*master");
179 		assertValid(false, "*refs/heads/master");
180 	}
181 
182 	@Test
183 	public void testValidSpecialCharacterUnixs() {
184 		assertValid(true, "refs/heads/!");
185 		assertValid(true, "refs/heads/#");
186 		assertValid(true, "refs/heads/$");
187 		assertValid(true, "refs/heads/%");
188 		assertValid(true, "refs/heads/&");
189 		assertValid(true, "refs/heads/'");
190 		assertValid(true, "refs/heads/(");
191 		assertValid(true, "refs/heads/)");
192 		assertValid(true, "refs/heads/+");
193 		assertValid(true, "refs/heads/,");
194 		assertValid(true, "refs/heads/-");
195 		assertValid(true, "refs/heads/;");
196 		assertValid(true, "refs/heads/=");
197 		assertValid(true, "refs/heads/@");
198 		assertValid(true, "refs/heads/]");
199 		assertValid(true, "refs/heads/_");
200 		assertValid(true, "refs/heads/`");
201 		assertValid(true, "refs/heads/{");
202 		assertValid(true, "refs/heads/}");
203 
204 		// This is valid on UNIX, but not on Windows
205 		// hence we make in invalid due to non-portability
206 		//
207 		assertValid(false, "refs/heads/\\");
208 
209 		// More invalid characters on Windows, but we allow them
210 		assertInvalidOnWindows("refs/heads/\"");
211 		assertInvalidOnWindows("refs/heads/<");
212 		assertInvalidOnWindows("refs/heads/>");
213 		assertInvalidOnWindows("refs/heads/|");
214 	}
215 
216 	@Test
217 	public void testUnicodeNames() {
218 		assertValid(true, "refs/heads/\u00e5ngstr\u00f6m");
219 	}
220 
221 	@Test
222 	public void testRefLogQueryIsValidRef() {
223 		assertValid(false, "refs/heads/master@{1}");
224 		assertValid(false, "refs/heads/master@{1.hour.ago}");
225 	}
226 
227 	@Test
228 	public void testWindowsReservedNames() {
229 		// re-using code from DirCacheCheckoutTest, hence
230 		// only testing for one of the special names.
231 		assertInvalidOnWindows("refs/heads/con");
232 		assertInvalidOnWindows("refs/con/x");
233 		assertInvalidOnWindows("con/heads/x");
234 		assertValid(true, "refs/heads/conx");
235 		assertValid(true, "refs/heads/xcon");
236 	}
237 
238 	@Test
239 	public void testNormalizeBranchName() {
240 		assertEquals("", Repository.normalizeBranchName(null));
241 		assertEquals("", Repository.normalizeBranchName(""));
242 		assertNormalized("Bug 12345::::Hello World", "Bug_12345-Hello_World");
243 		assertNormalized("Bug 12345 :::: Hello World", "Bug_12345_Hello_World");
244 		assertNormalized("Bug 12345 :::: Hello::: World",
245 				"Bug_12345_Hello-World");
246 		assertNormalized(":::Bug 12345 - Hello World", "Bug_12345_Hello_World");
247 		assertNormalized("---Bug 12345 - Hello World", "Bug_12345_Hello_World");
248 		assertNormalized("Bug 12345 ---- Hello --- World",
249 				"Bug_12345_Hello_World");
250 		assertNormalized("Bug 12345 - Hello World!", "Bug_12345_Hello_World!");
251 		assertNormalized("Bug 12345 : Hello World!", "Bug_12345_Hello_World!");
252 		assertNormalized("Bug 12345 _ Hello World!", "Bug_12345_Hello_World!");
253 		assertNormalized("Bug 12345   -       Hello World!",
254 				"Bug_12345_Hello_World!");
255 		assertNormalized(" Bug 12345   -   Hello World! ",
256 				"Bug_12345_Hello_World!");
257 		assertNormalized(" Bug 12345   -   Hello World!   ",
258 				"Bug_12345_Hello_World!");
259 		assertNormalized("Bug 12345   -   Hello______ World!",
260 				"Bug_12345_Hello_World!");
261 		assertNormalized("_Bug 12345 - Hello World!", "Bug_12345_Hello_World!");
262 	}
263 
264 	@Test
265 	public void testNormalizeWithSlashes() {
266 		assertNormalized("foo/bar/baz", "foo/bar/baz");
267 		assertNormalized("foo/bar.lock/baz.lock", "foo/bar_lock/baz_lock");
268 		assertNormalized("foo/.git/.git~1/bar", "foo/git/git-1/bar");
269 		assertNormalized(".foo/aux/con/com3.txt/com0/prn/lpt1",
270 				"foo/+aux/+con/+com3.txt/com0/+prn/+lpt1");
271 		assertNormalized("foo/../bar///.--ba...z", "foo/bar/ba.z");
272 	}
273 
274 	@Test
275 	public void testNormalizeWithUnicode() {
276 		assertNormalized("f\u00f6\u00f6/.b\u00e0r/*[<>|^~/b\u00e9\\z",
277 				"f\u00f6\u00f6/b\u00e0r/b\u00e9-z");
278 		assertNormalized("\u5165\u53e3 entrance;/.\u51fa\u53e3_*ex*it*/",
279 				"\u5165\u53e3_entrance;/\u51fa\u53e3_ex-it");
280 	}
281 
282 	@Test
283 	public void testNormalizeAlreadyValidRefName() {
284 		assertNormalized("refs/heads/m.a.s.t.e.r", "refs/heads/m.a.s.t.e.r");
285 		assertNormalized("refs/tags/v1.0-20170223", "refs/tags/v1.0-20170223");
286 	}
287 
288 	@Test
289 	public void testNormalizeTrimmedUnicodeAlreadyValidRefName() {
290 		assertNormalized(" \u00e5ngstr\u00f6m\t", "\u00e5ngstr\u00f6m");
291 	}
292 }