View Javadoc
1   /*
2    * Copyright (C) 2008-2010, Google Inc.
3    * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
4    *
5    * This program and the accompanying materials are made available under the
6    * terms of the Eclipse Distribution License v. 1.0 which is available at
7    * https://www.eclipse.org/org/documents/edl-v10.php.
8    *
9    * SPDX-License-Identifier: BSD-3-Clause
10   */
11  
12  package org.eclipse.jgit.lib;
13  
14  import static java.lang.Integer.valueOf;
15  import static java.nio.charset.StandardCharsets.UTF_8;
16  import static org.eclipse.jgit.junit.JGitTestUtil.concat;
17  import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
18  import static org.eclipse.jgit.lib.Constants.OBJ_BAD;
19  import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
20  import static org.eclipse.jgit.lib.Constants.OBJ_COMMIT;
21  import static org.eclipse.jgit.lib.Constants.OBJ_TAG;
22  import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
23  import static org.eclipse.jgit.lib.Constants.encode;
24  import static org.eclipse.jgit.lib.Constants.encodeASCII;
25  import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.DUPLICATE_ENTRIES;
26  import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.EMPTY_NAME;
27  import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.FULL_PATHNAME;
28  import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.HAS_DOT;
29  import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.HAS_DOTDOT;
30  import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.HAS_DOTGIT;
31  import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.NULL_SHA1;
32  import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.TREE_NOT_SORTED;
33  import static org.eclipse.jgit.lib.ObjectChecker.ErrorType.ZERO_PADDED_FILEMODE;
34  import static org.eclipse.jgit.util.RawParseUtils.decode;
35  import static org.junit.Assert.assertEquals;
36  import static org.junit.Assert.assertSame;
37  import static org.junit.Assert.assertThrows;
38  import static org.junit.Assert.fail;
39  
40  import java.text.MessageFormat;
41  
42  import org.eclipse.jgit.errors.CorruptObjectException;
43  import org.eclipse.jgit.internal.JGitText;
44  import org.junit.Before;
45  import org.junit.Test;
46  
47  public class ObjectCheckerTest {
48  	private static final ObjectChecker SECRET_KEY_CHECKER = new ObjectChecker() {
49  		@Override
50  		public void checkBlob(byte[] raw) throws CorruptObjectException {
51  			String in = decode(raw);
52  			if (in.contains("secret_key")) {
53  				throw new CorruptObjectException("don't add a secret key");
54  			}
55  		}
56  	};
57  
58  	private static final ObjectChecker SECRET_KEY_BLOB_CHECKER = new ObjectChecker() {
59  		@Override
60  		public BlobObjectChecker newBlobObjectChecker() {
61  			return new BlobObjectChecker() {
62  				private boolean containSecretKey;
63  
64  				@Override
65  				public void update(byte[] in, int offset, int len) {
66  					String str = decode(in, offset, offset + len);
67  					if (str.contains("secret_key")) {
68  						containSecretKey = true;
69  					}
70  				}
71  
72  				@Override
73  				public void endBlob(AnyObjectId id)
74  						throws CorruptObjectException {
75  					if (containSecretKey) {
76  						throw new CorruptObjectException(
77  								"don't add a secret key");
78  					}
79  				}
80  			};
81  		}
82  	};
83  
84  	private ObjectChecker checker;
85  
86  	@Before
87  	public void setUp() throws Exception {
88  		checker = new ObjectChecker();
89  	}
90  
91  	@Test
92  	public void testInvalidType() {
93  		String msg = MessageFormat.format(
94  				JGitText.get().corruptObjectInvalidType2,
95  				valueOf(OBJ_BAD));
96  		assertCorrupt(msg, OBJ_BAD, new byte[0]);
97  	}
98  
99  	@Test
100 	public void testCheckBlob() throws CorruptObjectException {
101 		// Any blob should pass...
102 		checker.checkBlob(new byte[0]);
103 		checker.checkBlob(new byte[1]);
104 
105 		checker.check(OBJ_BLOB, new byte[0]);
106 		checker.check(OBJ_BLOB, new byte[1]);
107 	}
108 
109 	@Test
110 	public void testCheckBlobNotCorrupt() throws CorruptObjectException {
111 		SECRET_KEY_CHECKER.check(OBJ_BLOB, encodeASCII("key = \"public_key\""));
112 	}
113 
114 	@Test
115 	public void testCheckBlobCorrupt() {
116 		assertThrows(CorruptObjectException.class, () -> SECRET_KEY_CHECKER
117 				.check(OBJ_BLOB, encodeASCII("key = \"secret_key\"")));
118 	}
119 
120 	@Test
121 	public void testCheckBlobWithBlobObjectCheckerNotCorrupt()
122 			throws CorruptObjectException {
123 		SECRET_KEY_BLOB_CHECKER.check(OBJ_BLOB,
124 				encodeASCII("key = \"public_key\""));
125 	}
126 
127 	@Test
128 	public void testCheckBlobWithBlobObjectCheckerCorrupt() {
129 		assertThrows(CorruptObjectException.class, () -> SECRET_KEY_BLOB_CHECKER
130 				.check(OBJ_BLOB, encodeASCII("key = \"secret_key\"")));
131 	}
132 
133 	@Test
134 	public void testValidCommitNoParent() throws CorruptObjectException {
135 		StringBuilder b = new StringBuilder();
136 
137 		b.append("tree ");
138 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
139 		b.append('\n');
140 
141 		b.append("author A. U. Thor <author@localhost> 1 +0000\n");
142 		b.append("committer A. U. Thor <author@localhost> 1 +0000\n");
143 
144 		byte[] data = encodeASCII(b.toString());
145 		checker.checkCommit(data);
146 		checker.check(OBJ_COMMIT, data);
147 	}
148 
149 	@Test
150 	public void testValidCommitBlankAuthor() throws CorruptObjectException {
151 		StringBuilder b = new StringBuilder();
152 
153 		b.append("tree ");
154 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
155 		b.append('\n');
156 
157 		b.append("author <> 0 +0000\n");
158 		b.append("committer <> 0 +0000\n");
159 
160 		byte[] data = encodeASCII(b.toString());
161 		checker.checkCommit(data);
162 		checker.check(OBJ_COMMIT, data);
163 	}
164 
165 	@Test
166 	public void testCommitCorruptAuthor() throws CorruptObjectException {
167 		StringBuilder b = new StringBuilder();
168 		b.append("tree be9bfa841874ccc9f2ef7c48d0c76226f89b7189\n");
169 		b.append("author b <b@c> <b@c> 0 +0000\n");
170 		b.append("committer <> 0 +0000\n");
171 
172 		byte[] data = encodeASCII(b.toString());
173 		assertCorrupt("bad date", OBJ_COMMIT, data);
174 		checker.setAllowInvalidPersonIdent(true);
175 		checker.checkCommit(data);
176 
177 		checker.setAllowInvalidPersonIdent(false);
178 		assertSkipListAccepts(OBJ_COMMIT, data);
179 	}
180 
181 	@Test
182 	public void testCommitCorruptCommitter() throws CorruptObjectException {
183 		StringBuilder b = new StringBuilder();
184 		b.append("tree be9bfa841874ccc9f2ef7c48d0c76226f89b7189\n");
185 		b.append("author <> 0 +0000\n");
186 		b.append("committer b <b@c> <b@c> 0 +0000\n");
187 
188 		byte[] data = encodeASCII(b.toString());
189 		assertCorrupt("bad date", OBJ_COMMIT, data);
190 		checker.setAllowInvalidPersonIdent(true);
191 		checker.checkCommit(data);
192 
193 		checker.setAllowInvalidPersonIdent(false);
194 		assertSkipListAccepts(OBJ_COMMIT, data);
195 	}
196 
197 	@Test
198 	public void testValidCommit1Parent() throws CorruptObjectException {
199 		StringBuilder b = new StringBuilder();
200 
201 		b.append("tree ");
202 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
203 		b.append('\n');
204 
205 		b.append("parent ");
206 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
207 		b.append('\n');
208 
209 		b.append("author A. U. Thor <author@localhost> 1 +0000\n");
210 		b.append("committer A. U. Thor <author@localhost> 1 +0000\n");
211 
212 		byte[] data = encodeASCII(b.toString());
213 		checker.checkCommit(data);
214 		checker.check(OBJ_COMMIT, data);
215 	}
216 
217 	@Test
218 	public void testValidCommit2Parent() throws CorruptObjectException {
219 		StringBuilder b = new StringBuilder();
220 
221 		b.append("tree ");
222 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
223 		b.append('\n');
224 
225 		b.append("parent ");
226 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
227 		b.append('\n');
228 
229 		b.append("parent ");
230 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
231 		b.append('\n');
232 
233 		b.append("author A. U. Thor <author@localhost> 1 +0000\n");
234 		b.append("committer A. U. Thor <author@localhost> 1 +0000\n");
235 
236 		byte[] data = encodeASCII(b.toString());
237 		checker.checkCommit(data);
238 		checker.check(OBJ_COMMIT, data);
239 	}
240 
241 	@Test
242 	public void testValidCommit128Parent() throws CorruptObjectException {
243 		StringBuilder b = new StringBuilder();
244 
245 		b.append("tree ");
246 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
247 		b.append('\n');
248 
249 		for (int i = 0; i < 128; i++) {
250 			b.append("parent ");
251 			b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
252 			b.append('\n');
253 		}
254 
255 		b.append("author A. U. Thor <author@localhost> 1 +0000\n");
256 		b.append("committer A. U. Thor <author@localhost> 1 +0000\n");
257 
258 		byte[] data = encodeASCII(b.toString());
259 		checker.checkCommit(data);
260 		checker.check(OBJ_COMMIT, data);
261 	}
262 
263 	@Test
264 	public void testValidCommitNormalTime() throws CorruptObjectException {
265 		StringBuilder b = new StringBuilder();
266 		String when = "1222757360 -0730";
267 
268 		b.append("tree ");
269 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
270 		b.append('\n');
271 
272 		b.append("author A. U. Thor <author@localhost> " + when + "\n");
273 		b.append("committer A. U. Thor <author@localhost> " + when + "\n");
274 
275 		byte[] data = encodeASCII(b.toString());
276 		checker.checkCommit(data);
277 		checker.check(OBJ_COMMIT, data);
278 	}
279 
280 	@Test
281 	public void testInvalidCommitNoTree1() {
282 		StringBuilder b = new StringBuilder();
283 		b.append("parent ");
284 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
285 		b.append('\n');
286 		assertCorrupt("no tree header", OBJ_COMMIT, b);
287 	}
288 
289 	@Test
290 	public void testInvalidCommitNoTree2() {
291 		StringBuilder b = new StringBuilder();
292 		b.append("trie ");
293 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
294 		b.append('\n');
295 		assertCorrupt("no tree header", OBJ_COMMIT, b);
296 	}
297 
298 	@Test
299 	public void testInvalidCommitNoTree3() {
300 		StringBuilder b = new StringBuilder();
301 		b.append("tree");
302 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
303 		b.append('\n');
304 		assertCorrupt("no tree header", OBJ_COMMIT, b);
305 	}
306 
307 	@Test
308 	public void testInvalidCommitNoTree4() {
309 		StringBuilder b = new StringBuilder();
310 		b.append("tree\t");
311 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
312 		b.append('\n');
313 		assertCorrupt("no tree header", OBJ_COMMIT, b);
314 	}
315 
316 	@Test
317 	public void testInvalidCommitInvalidTree1() {
318 		StringBuilder b = new StringBuilder();
319 		b.append("tree ");
320 		b.append("zzzzfa841874ccc9f2ef7c48d0c76226f89b7189");
321 		b.append('\n');
322 		assertCorrupt("invalid tree", OBJ_COMMIT, b);
323 	}
324 
325 	@Test
326 	public void testInvalidCommitInvalidTree2() {
327 		StringBuilder b = new StringBuilder();
328 		b.append("tree ");
329 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
330 		b.append("z\n");
331 		assertCorrupt("invalid tree", OBJ_COMMIT, b);
332 	}
333 
334 	@Test
335 	public void testInvalidCommitInvalidTree3() {
336 		StringBuilder b = new StringBuilder();
337 		b.append("tree ");
338 		b.append("be9b");
339 		b.append("\n");
340 
341 		byte[] data = encodeASCII(b.toString());
342 		assertCorrupt("invalid tree", OBJ_COMMIT, data);
343 	}
344 
345 	@Test
346 	public void testInvalidCommitInvalidTree4() {
347 		StringBuilder b = new StringBuilder();
348 		b.append("tree  ");
349 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
350 		b.append('\n');
351 		assertCorrupt("invalid tree", OBJ_COMMIT, b);
352 	}
353 
354 	@Test
355 	public void testInvalidCommitInvalidParent1() {
356 		StringBuilder b = new StringBuilder();
357 		b.append("tree ");
358 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
359 		b.append('\n');
360 		b.append("parent ");
361 		b.append("\n");
362 		assertCorrupt("invalid parent", OBJ_COMMIT, b);
363 	}
364 
365 	@Test
366 	public void testInvalidCommitInvalidParent2() {
367 		StringBuilder b = new StringBuilder();
368 		b.append("tree ");
369 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
370 		b.append('\n');
371 		b.append("parent ");
372 		b.append("zzzzfa841874ccc9f2ef7c48d0c76226f89b7189");
373 		b.append("\n");
374 		assertCorrupt("invalid parent", OBJ_COMMIT, b);
375 	}
376 
377 	@Test
378 	public void testInvalidCommitInvalidParent3() {
379 		StringBuilder b = new StringBuilder();
380 		b.append("tree ");
381 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
382 		b.append('\n');
383 		b.append("parent  ");
384 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
385 		b.append("\n");
386 		assertCorrupt("invalid parent", OBJ_COMMIT, b);
387 	}
388 
389 	@Test
390 	public void testInvalidCommitInvalidParent4() {
391 		StringBuilder b = new StringBuilder();
392 		b.append("tree ");
393 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
394 		b.append('\n');
395 		b.append("parent  ");
396 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
397 		b.append("z\n");
398 		assertCorrupt("invalid parent", OBJ_COMMIT, b);
399 	}
400 
401 	@Test
402 	public void testInvalidCommitInvalidParent5() {
403 		StringBuilder b = new StringBuilder();
404 		b.append("tree ");
405 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
406 		b.append('\n');
407 		b.append("parent\t");
408 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
409 		b.append("\n");
410 
411 		byte[] data = encodeASCII(b.toString());
412 		// Yes, really, we complain about author not being
413 		// found as the invalid parent line wasn't consumed.
414 		assertCorrupt("no author", OBJ_COMMIT, data);
415 	}
416 
417 	@Test
418 	public void testInvalidCommitNoAuthor() throws CorruptObjectException {
419 		StringBuilder b = new StringBuilder();
420 		b.append("tree ");
421 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
422 		b.append('\n');
423 		b.append("committer A. U. Thor <author@localhost> 1 +0000\n");
424 
425 		byte[] data = encodeASCII(b.toString());
426 		assertCorrupt("no author", OBJ_COMMIT, data);
427 		assertSkipListAccepts(OBJ_COMMIT, data);
428 	}
429 
430 	@Test
431 	public void testInvalidCommitNoCommitter1() throws CorruptObjectException {
432 		StringBuilder b = new StringBuilder();
433 		b.append("tree ");
434 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
435 		b.append('\n');
436 		b.append("author A. U. Thor <author@localhost> 1 +0000\n");
437 
438 		byte[] data = encodeASCII(b.toString());
439 		assertCorrupt("no committer", OBJ_COMMIT, data);
440 		assertSkipListAccepts(OBJ_COMMIT, data);
441 	}
442 
443 	@Test
444 	public void testInvalidCommitNoCommitter2() throws CorruptObjectException {
445 		StringBuilder b = new StringBuilder();
446 		b.append("tree ");
447 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
448 		b.append('\n');
449 		b.append("author A. U. Thor <author@localhost> 1 +0000\n");
450 		b.append("\n");
451 
452 		byte[] data = encodeASCII(b.toString());
453 		assertCorrupt("no committer", OBJ_COMMIT, data);
454 		assertSkipListAccepts(OBJ_COMMIT, data);
455 	}
456 
457 	@Test
458 	public void testInvalidCommitInvalidAuthor1()
459 			throws CorruptObjectException {
460 		StringBuilder b = new StringBuilder();
461 		b.append("tree ");
462 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
463 		b.append('\n');
464 		b.append("author A. U. Thor <foo 1 +0000\n");
465 
466 		byte[] data = encodeASCII(b.toString());
467 		assertCorrupt("bad email", OBJ_COMMIT, data);
468 		assertSkipListAccepts(OBJ_COMMIT, data);
469 	}
470 
471 	@Test
472 	public void testInvalidCommitInvalidAuthor2()
473 			throws CorruptObjectException {
474 		StringBuilder b = new StringBuilder();
475 		b.append("tree ");
476 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
477 		b.append('\n');
478 		b.append("author A. U. Thor foo> 1 +0000\n");
479 
480 		byte[] data = encodeASCII(b.toString());
481 		assertCorrupt("missing email", OBJ_COMMIT, data);
482 		assertSkipListAccepts(OBJ_COMMIT, data);
483 	}
484 
485 	@Test
486 	public void testInvalidCommitInvalidAuthor3()
487 			throws CorruptObjectException {
488 		StringBuilder b = new StringBuilder();
489 		b.append("tree ");
490 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
491 		b.append('\n');
492 		b.append("author 1 +0000\n");
493 
494 		byte[] data = encodeASCII(b.toString());
495 		assertCorrupt("missing email", OBJ_COMMIT, data);
496 		assertSkipListAccepts(OBJ_COMMIT, data);
497 	}
498 
499 	@Test
500 	public void testInvalidCommitInvalidAuthor4()
501 			throws CorruptObjectException {
502 		StringBuilder b = new StringBuilder();
503 		b.append("tree ");
504 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
505 		b.append('\n');
506 		b.append("author a <b> +0000\n");
507 
508 		byte[] data = encodeASCII(b.toString());
509 		assertCorrupt("bad date", OBJ_COMMIT, data);
510 		assertSkipListAccepts(OBJ_COMMIT, data);
511 	}
512 
513 	@Test
514 	public void testInvalidCommitInvalidAuthor5()
515 			throws CorruptObjectException {
516 		StringBuilder b = new StringBuilder();
517 		b.append("tree ");
518 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
519 		b.append('\n');
520 		b.append("author a <b>\n");
521 
522 		byte[] data = encodeASCII(b.toString());
523 		assertCorrupt("bad date", OBJ_COMMIT, data);
524 		assertSkipListAccepts(OBJ_COMMIT, data);
525 	}
526 
527 	@Test
528 	public void testInvalidCommitInvalidAuthor6()
529 			throws CorruptObjectException {
530 		StringBuilder b = new StringBuilder();
531 		b.append("tree ");
532 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
533 		b.append('\n');
534 		b.append("author a <b> z");
535 
536 		byte[] data = encodeASCII(b.toString());
537 		assertCorrupt("bad date", OBJ_COMMIT, data);
538 		assertSkipListAccepts(OBJ_COMMIT, data);
539 	}
540 
541 	@Test
542 	public void testInvalidCommitInvalidAuthor7()
543 			throws CorruptObjectException {
544 		StringBuilder b = new StringBuilder();
545 		b.append("tree ");
546 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
547 		b.append('\n');
548 		b.append("author a <b> 1 z");
549 
550 		byte[] data = encodeASCII(b.toString());
551 		assertCorrupt("bad time zone", OBJ_COMMIT, data);
552 		assertSkipListAccepts(OBJ_COMMIT, data);
553 	}
554 
555 	@Test
556 	public void testInvalidCommitInvalidCommitter()
557 			throws CorruptObjectException {
558 		StringBuilder b = new StringBuilder();
559 		b.append("tree ");
560 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
561 		b.append('\n');
562 		b.append("author a <b> 1 +0000\n");
563 		b.append("committer a <");
564 
565 		byte[] data = encodeASCII(b.toString());
566 		assertCorrupt("bad email", OBJ_COMMIT, data);
567 		assertSkipListAccepts(OBJ_COMMIT, data);
568 	}
569 
570 	@Test
571 	public void testValidTag() throws CorruptObjectException {
572 		StringBuilder b = new StringBuilder();
573 		b.append("object ");
574 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
575 		b.append('\n');
576 		b.append("type commit\n");
577 		b.append("tag test-tag\n");
578 		b.append("tagger A. U. Thor <author@localhost> 1 +0000\n");
579 
580 		byte[] data = encodeASCII(b.toString());
581 		checker.checkTag(data);
582 		checker.check(OBJ_TAG, data);
583 	}
584 
585 	@Test
586 	public void testInvalidTagNoObject1() {
587 		assertCorrupt("no object header", OBJ_TAG, new byte[0]);
588 	}
589 
590 	@Test
591 	public void testInvalidTagNoObject2() {
592 		StringBuilder b = new StringBuilder();
593 		b.append("object\t");
594 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
595 		b.append('\n');
596 		assertCorrupt("no object header", OBJ_TAG, b);
597 	}
598 
599 	@Test
600 	public void testInvalidTagNoObject3() {
601 		StringBuilder b = new StringBuilder();
602 		b.append("obejct ");
603 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
604 		b.append('\n');
605 		assertCorrupt("no object header", OBJ_TAG, b);
606 	}
607 
608 	@Test
609 	public void testInvalidTagNoObject4() {
610 		StringBuilder b = new StringBuilder();
611 		b.append("object ");
612 		b.append("zz9bfa841874ccc9f2ef7c48d0c76226f89b7189");
613 		b.append('\n');
614 		assertCorrupt("invalid object", OBJ_TAG, b);
615 	}
616 
617 	@Test
618 	public void testInvalidTagNoObject5() {
619 		StringBuilder b = new StringBuilder();
620 		b.append("object ");
621 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
622 		b.append(" \n");
623 		assertCorrupt("invalid object", OBJ_TAG, b);
624 	}
625 
626 	@Test
627 	public void testInvalidTagNoObject6() {
628 		StringBuilder b = new StringBuilder();
629 		b.append("object ");
630 		b.append("be9");
631 		assertCorrupt("invalid object", OBJ_TAG, b);
632 	}
633 
634 	@Test
635 	public void testInvalidTagNoType1() {
636 		StringBuilder b = new StringBuilder();
637 		b.append("object ");
638 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
639 		b.append('\n');
640 		assertCorrupt("no type header", OBJ_TAG, b);
641 	}
642 
643 	@Test
644 	public void testInvalidTagNoType2() {
645 		StringBuilder b = new StringBuilder();
646 		b.append("object ");
647 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
648 		b.append('\n');
649 		b.append("type\tcommit\n");
650 		assertCorrupt("no type header", OBJ_TAG, b);
651 	}
652 
653 	@Test
654 	public void testInvalidTagNoType3() {
655 		StringBuilder b = new StringBuilder();
656 		b.append("object ");
657 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
658 		b.append('\n');
659 		b.append("tpye commit\n");
660 		assertCorrupt("no type header", OBJ_TAG, b);
661 	}
662 
663 	@Test
664 	public void testInvalidTagNoType4() {
665 		StringBuilder b = new StringBuilder();
666 		b.append("object ");
667 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
668 		b.append('\n');
669 		b.append("type commit");
670 		assertCorrupt("no tag header", OBJ_TAG, b);
671 	}
672 
673 	@Test
674 	public void testInvalidTagNoTagHeader1() {
675 		StringBuilder b = new StringBuilder();
676 		b.append("object ");
677 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
678 		b.append('\n');
679 		b.append("type commit\n");
680 		assertCorrupt("no tag header", OBJ_TAG, b);
681 	}
682 
683 	@Test
684 	public void testInvalidTagNoTagHeader2() {
685 		StringBuilder b = new StringBuilder();
686 		b.append("object ");
687 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
688 		b.append('\n');
689 		b.append("type commit\n");
690 		b.append("tag\tfoo\n");
691 		assertCorrupt("no tag header", OBJ_TAG, b);
692 	}
693 
694 	@Test
695 	public void testInvalidTagNoTagHeader3() {
696 		StringBuilder b = new StringBuilder();
697 		b.append("object ");
698 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
699 		b.append('\n');
700 		b.append("type commit\n");
701 		b.append("tga foo\n");
702 		assertCorrupt("no tag header", OBJ_TAG, b);
703 	}
704 
705 	@Test
706 	public void testValidTagHasNoTaggerHeader() throws CorruptObjectException {
707 		StringBuilder b = new StringBuilder();
708 		b.append("object ");
709 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
710 		b.append('\n');
711 		b.append("type commit\n");
712 		b.append("tag foo\n");
713 		checker.checkTag(encodeASCII(b.toString()));
714 	}
715 
716 	@Test
717 	public void testInvalidTagInvalidTaggerHeader1()
718 			throws CorruptObjectException {
719 		StringBuilder b = new StringBuilder();
720 		b.append("object ");
721 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
722 		b.append('\n');
723 		b.append("type commit\n");
724 		b.append("tag foo\n");
725 		b.append("tagger \n");
726 
727 		byte[] data = encodeASCII(b.toString());
728 		assertCorrupt("missing email", OBJ_TAG, data);
729 		checker.setAllowInvalidPersonIdent(true);
730 		checker.checkTag(data);
731 
732 		checker.setAllowInvalidPersonIdent(false);
733 		assertSkipListAccepts(OBJ_TAG, data);
734 	}
735 
736 	@Test
737 	public void testInvalidTagInvalidTaggerHeader3()
738 			throws CorruptObjectException {
739 		StringBuilder b = new StringBuilder();
740 		b.append("object ");
741 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
742 		b.append('\n');
743 		b.append("type commit\n");
744 		b.append("tag foo\n");
745 		b.append("tagger a < 1 +000\n");
746 
747 		byte[] data = encodeASCII(b.toString());
748 		assertCorrupt("bad email", OBJ_TAG, data);
749 		assertSkipListAccepts(OBJ_TAG, data);
750 	}
751 
752 	@Test
753 	public void testValidEmptyTree() throws CorruptObjectException {
754 		checker.checkTree(new byte[0]);
755 		checker.check(OBJ_TREE, new byte[0]);
756 	}
757 
758 	@Test
759 	public void testValidTree1() throws CorruptObjectException {
760 		StringBuilder b = new StringBuilder();
761 		entry(b, "100644 regular-file");
762 		checker.checkTree(encodeASCII(b.toString()));
763 	}
764 
765 	@Test
766 	public void testValidTree2() throws CorruptObjectException {
767 		StringBuilder b = new StringBuilder();
768 		entry(b, "100755 executable");
769 		checker.checkTree(encodeASCII(b.toString()));
770 	}
771 
772 	@Test
773 	public void testValidTree3() throws CorruptObjectException {
774 		StringBuilder b = new StringBuilder();
775 		entry(b, "40000 tree");
776 		checker.checkTree(encodeASCII(b.toString()));
777 	}
778 
779 	@Test
780 	public void testValidTree4() throws CorruptObjectException {
781 		StringBuilder b = new StringBuilder();
782 		entry(b, "120000 symlink");
783 		checker.checkTree(encodeASCII(b.toString()));
784 	}
785 
786 	@Test
787 	public void testValidTree5() throws CorruptObjectException {
788 		StringBuilder b = new StringBuilder();
789 		entry(b, "160000 git link");
790 		checker.checkTree(encodeASCII(b.toString()));
791 	}
792 
793 	@Test
794 	public void testValidTree6() throws CorruptObjectException {
795 		StringBuilder b = new StringBuilder();
796 		entry(b, "100644 .a");
797 		checker.checkTree(encodeASCII(b.toString()));
798 	}
799 
800 	@Test
801 	public void testValidTreeWithGitmodules() throws CorruptObjectException {
802 		ObjectId treeId = ObjectId
803 				.fromString("0123012301230123012301230123012301230123");
804 		StringBuilder b = new StringBuilder();
805 		ObjectId blobId = entry(b, "100644 .gitmodules");
806 
807 		byte[] data = encodeASCII(b.toString());
808 		checker.checkTree(treeId, data);
809 		assertEquals(1, checker.getGitsubmodules().size());
810 		assertEquals(treeId, checker.getGitsubmodules().get(0).getTreeId());
811 		assertEquals(blobId, checker.getGitsubmodules().get(0).getBlobId());
812 	}
813 
814 	/*
815 	 * Windows case insensitivity and long file name handling
816 	 * means that .gitmodules has many synonyms.
817 	 *
818 	 * Examples inspired by git.git's t/t0060-path-utils.sh, by
819 	 * Johannes Schindelin and Congyi Wu.
820 	 */
821 	@Test
822 	public void testNTFSGitmodules() throws CorruptObjectException {
823 		for (String gitmodules : new String[] {
824 			".GITMODULES",
825 			".gitmodules",
826 			".Gitmodules",
827 			".gitmoduleS",
828 			"gitmod~1",
829 			"GITMOD~1",
830 			"gitmod~4",
831 			"GI7EBA~1",
832 			"gi7eba~9",
833 			"GI7EB~10",
834 			"GI7E~123",
835 			"~1000000",
836 			"~9999999"
837 		}) {
838 			checker = new ObjectChecker(); // Reset the ObjectChecker state.
839 			checker.setSafeForWindows(true);
840 			ObjectId treeId = ObjectId
841 					.fromString("0123012301230123012301230123012301230123");
842 			StringBuilder b = new StringBuilder();
843 			ObjectId blobId = entry(b, "100644 " + gitmodules);
844 
845 			byte[] data = encodeASCII(b.toString());
846 			checker.checkTree(treeId, data);
847 			assertEquals(1, checker.getGitsubmodules().size());
848 			assertEquals(treeId, checker.getGitsubmodules().get(0).getTreeId());
849 			assertEquals(blobId, checker.getGitsubmodules().get(0).getBlobId());
850 		}
851 	}
852 
853 	@Test
854 	public void testNotGitmodules() throws CorruptObjectException {
855 		for (String notGitmodules : new String[] {
856 			".gitmodu",
857 			".gitmodules oh never mind",
858 		}) {
859 			checker = new ObjectChecker(); // Reset the ObjectChecker state.
860 			checker.setSafeForWindows(true);
861 			ObjectId treeId = ObjectId
862 					.fromString("0123012301230123012301230123012301230123");
863 			StringBuilder b = new StringBuilder();
864 			entry(b, "100644 " + notGitmodules);
865 
866 			byte[] data = encodeASCII(b.toString());
867 			checker.checkTree(treeId, data);
868 			assertEquals(0, checker.getGitsubmodules().size());
869 		}
870 	}
871 
872 	/*
873 	 * TODO HFS: match ".gitmodules" case-insensitively, after stripping out
874 	 * certain zero-length Unicode code points that HFS+ strips out
875 	 */
876 
877 	@Test
878 	public void testValidTreeWithGitmodulesUppercase()
879 			throws CorruptObjectException {
880 		ObjectId treeId = ObjectId
881 				.fromString("0123012301230123012301230123012301230123");
882 		StringBuilder b = new StringBuilder();
883 		ObjectId blobId = entry(b, "100644 .GITMODULES");
884 
885 		byte[] data = encodeASCII(b.toString());
886 		checker.setSafeForWindows(true);
887 		checker.checkTree(treeId, data);
888 		assertEquals(1, checker.getGitsubmodules().size());
889 		assertEquals(treeId, checker.getGitsubmodules().get(0).getTreeId());
890 		assertEquals(blobId, checker.getGitsubmodules().get(0).getBlobId());
891 	}
892 
893 	@Test
894 	public void testTreeWithInvalidGitmodules() throws CorruptObjectException {
895 		ObjectId treeId = ObjectId
896 				.fromString("0123012301230123012301230123012301230123");
897 		StringBuilder b = new StringBuilder();
898 		entry(b, "100644 .gitmodulez");
899 
900 		byte[] data = encodeASCII(b.toString());
901 		checker.checkTree(treeId, data);
902 		checker.setSafeForWindows(true);
903 		assertEquals(0, checker.getGitsubmodules().size());
904 	}
905 
906 	@Test
907 	public void testNullSha1InTreeEntry() throws CorruptObjectException {
908 		byte[] data = concat(
909 				encodeASCII("100644 A"), new byte[] { '\0' },
910 				new byte[OBJECT_ID_LENGTH]);
911 		assertCorrupt("entry points to null SHA-1", OBJ_TREE, data);
912 		assertSkipListAccepts(OBJ_TREE, data);
913 		checker.setIgnore(NULL_SHA1, true);
914 		checker.checkTree(data);
915 	}
916 
917 	@Test
918 	public void testValidPosixTree() throws CorruptObjectException {
919 		checkOneName("a<b>c:d|e");
920 		checkOneName("test ");
921 		checkOneName("test.");
922 		checkOneName("NUL");
923 	}
924 
925 	@Test
926 	public void testValidTreeSorting1() throws CorruptObjectException {
927 		StringBuilder b = new StringBuilder();
928 		entry(b, "100644 fooaaa");
929 		entry(b, "100755 foobar");
930 		checker.checkTree(encodeASCII(b.toString()));
931 	}
932 
933 	@Test
934 	public void testValidTreeSorting2() throws CorruptObjectException {
935 		StringBuilder b = new StringBuilder();
936 		entry(b, "100755 fooaaa");
937 		entry(b, "100644 foobar");
938 		checker.checkTree(encodeASCII(b.toString()));
939 	}
940 
941 	@Test
942 	public void testValidTreeSorting3() throws CorruptObjectException {
943 		StringBuilder b = new StringBuilder();
944 		entry(b, "40000 a");
945 		entry(b, "100644 b");
946 		checker.checkTree(encodeASCII(b.toString()));
947 	}
948 
949 	@Test
950 	public void testValidTreeSorting4() throws CorruptObjectException {
951 		StringBuilder b = new StringBuilder();
952 		entry(b, "100644 a");
953 		entry(b, "40000 b");
954 		checker.checkTree(encodeASCII(b.toString()));
955 	}
956 
957 	@Test
958 	public void testValidTreeSorting5() throws CorruptObjectException {
959 		StringBuilder b = new StringBuilder();
960 		entry(b, "100644 a.c");
961 		entry(b, "40000 a");
962 		entry(b, "100644 a0c");
963 		checker.checkTree(encodeASCII(b.toString()));
964 	}
965 
966 	@Test
967 	public void testValidTreeSorting6() throws CorruptObjectException {
968 		StringBuilder b = new StringBuilder();
969 		entry(b, "40000 a");
970 		entry(b, "100644 apple");
971 		checker.checkTree(encodeASCII(b.toString()));
972 	}
973 
974 	@Test
975 	public void testValidTreeSorting7() throws CorruptObjectException {
976 		StringBuilder b = new StringBuilder();
977 		entry(b, "40000 an orang");
978 		entry(b, "40000 an orange");
979 		checker.checkTree(encodeASCII(b.toString()));
980 	}
981 
982 	@Test
983 	public void testValidTreeSorting8() throws CorruptObjectException {
984 		StringBuilder b = new StringBuilder();
985 		entry(b, "100644 a");
986 		entry(b, "100644 a0c");
987 		entry(b, "100644 b");
988 		checker.checkTree(encodeASCII(b.toString()));
989 	}
990 
991 	@Test
992 	public void testAcceptTreeModeWithZero() throws CorruptObjectException {
993 		StringBuilder b = new StringBuilder();
994 		entry(b, "040000 a");
995 		byte[] data = encodeASCII(b.toString());
996 		checker.setAllowLeadingZeroFileMode(true);
997 		checker.checkTree(data);
998 
999 		checker.setAllowLeadingZeroFileMode(false);
1000 		assertSkipListAccepts(OBJ_TREE, data);
1001 
1002 		checker.setIgnore(ZERO_PADDED_FILEMODE, true);
1003 		checker.checkTree(data);
1004 	}
1005 
1006 	@Test
1007 	public void testInvalidTreeModeStartsWithZero1() {
1008 		StringBuilder b = new StringBuilder();
1009 		entry(b, "0 a");
1010 		assertCorrupt("mode starts with '0'", OBJ_TREE, b);
1011 	}
1012 
1013 	@Test
1014 	public void testInvalidTreeModeStartsWithZero2() {
1015 		StringBuilder b = new StringBuilder();
1016 		entry(b, "0100644 a");
1017 		assertCorrupt("mode starts with '0'", OBJ_TREE, b);
1018 	}
1019 
1020 	@Test
1021 	public void testInvalidTreeModeStartsWithZero3() {
1022 		StringBuilder b = new StringBuilder();
1023 		entry(b, "040000 a");
1024 		assertCorrupt("mode starts with '0'", OBJ_TREE, b);
1025 	}
1026 
1027 	@Test
1028 	public void testInvalidTreeModeNotOctal1() {
1029 		StringBuilder b = new StringBuilder();
1030 		entry(b, "8 a");
1031 		assertCorrupt("invalid mode character", OBJ_TREE, b);
1032 	}
1033 
1034 	@Test
1035 	public void testInvalidTreeModeNotOctal2() {
1036 		StringBuilder b = new StringBuilder();
1037 		entry(b, "Z a");
1038 		byte[] data = encodeASCII(b.toString());
1039 		assertCorrupt("invalid mode character", OBJ_TREE, data);
1040 		assertSkipListRejects("invalid mode character", OBJ_TREE, data);
1041 	}
1042 
1043 	@Test
1044 	public void testInvalidTreeModeNotSupportedMode1() {
1045 		StringBuilder b = new StringBuilder();
1046 		entry(b, "1 a");
1047 		byte[] data = encodeASCII(b.toString());
1048 		assertCorrupt("invalid mode 1", OBJ_TREE, data);
1049 		assertSkipListRejects("invalid mode 1", OBJ_TREE, data);
1050 	}
1051 
1052 	@Test
1053 	public void testInvalidTreeModeNotSupportedMode2() {
1054 		StringBuilder b = new StringBuilder();
1055 		entry(b, "170000 a");
1056 		assertCorrupt("invalid mode " + 0170000, OBJ_TREE, b);
1057 	}
1058 
1059 	@Test
1060 	public void testInvalidTreeModeMissingName() {
1061 		StringBuilder b = new StringBuilder();
1062 		b.append("100644");
1063 		assertCorrupt("truncated in mode", OBJ_TREE, b);
1064 	}
1065 
1066 	@Test
1067 	public void testInvalidTreeNameContainsSlash()
1068 			throws CorruptObjectException {
1069 		StringBuilder b = new StringBuilder();
1070 		entry(b, "100644 a/b");
1071 		byte[] data = encodeASCII(b.toString());
1072 		assertCorrupt("name contains '/'", OBJ_TREE, data);
1073 		assertSkipListAccepts(OBJ_TREE, data);
1074 		checker.setIgnore(FULL_PATHNAME, true);
1075 		checker.checkTree(data);
1076 	}
1077 
1078 	@Test
1079 	public void testInvalidTreeNameIsEmpty() throws CorruptObjectException {
1080 		StringBuilder b = new StringBuilder();
1081 		entry(b, "100644 ");
1082 		byte[] data = encodeASCII(b.toString());
1083 		assertCorrupt("zero length name", OBJ_TREE, data);
1084 		assertSkipListAccepts(OBJ_TREE, data);
1085 		checker.setIgnore(EMPTY_NAME, true);
1086 		checker.checkTree(data);
1087 	}
1088 
1089 	@Test
1090 	public void testInvalidTreeNameIsDot() throws CorruptObjectException {
1091 		StringBuilder b = new StringBuilder();
1092 		entry(b, "100644 .");
1093 		byte[] data = encodeASCII(b.toString());
1094 		assertCorrupt("invalid name '.'", OBJ_TREE, data);
1095 		assertSkipListAccepts(OBJ_TREE, data);
1096 		checker.setIgnore(HAS_DOT, true);
1097 		checker.checkTree(data);
1098 	}
1099 
1100 	@Test
1101 	public void testInvalidTreeNameIsDotDot() throws CorruptObjectException {
1102 		StringBuilder b = new StringBuilder();
1103 		entry(b, "100644 ..");
1104 		byte[] data = encodeASCII(b.toString());
1105 		assertCorrupt("invalid name '..'", OBJ_TREE, data);
1106 		assertSkipListAccepts(OBJ_TREE, data);
1107 		checker.setIgnore(HAS_DOTDOT, true);
1108 		checker.checkTree(data);
1109 	}
1110 
1111 	@Test
1112 	public void testInvalidTreeNameIsGit() throws CorruptObjectException {
1113 		StringBuilder b = new StringBuilder();
1114 		entry(b, "100644 .git");
1115 		byte[] data = encodeASCII(b.toString());
1116 		assertCorrupt("invalid name '.git'", OBJ_TREE, data);
1117 		assertSkipListAccepts(OBJ_TREE, data);
1118 		checker.setIgnore(HAS_DOTGIT, true);
1119 		checker.checkTree(data);
1120 	}
1121 
1122 	@Test
1123 	public void testInvalidTreeNameIsMixedCaseGit()
1124 			throws CorruptObjectException {
1125 		StringBuilder b = new StringBuilder();
1126 		entry(b, "100644 .GiT");
1127 		byte[] data = encodeASCII(b.toString());
1128 		assertCorrupt("invalid name '.GiT'", OBJ_TREE, data);
1129 		assertSkipListAccepts(OBJ_TREE, data);
1130 		checker.setIgnore(HAS_DOTGIT, true);
1131 		checker.checkTree(data);
1132 	}
1133 
1134 	@Test
1135 	public void testInvalidTreeNameIsMacHFSGit() throws CorruptObjectException {
1136 		StringBuilder b = new StringBuilder();
1137 		entry(b, "100644 .gi\u200Ct");
1138 		byte[] data = encode(b.toString());
1139 
1140 		// Fine on POSIX.
1141 		checker.checkTree(data);
1142 
1143 		// Rejected on Mac OS.
1144 		checker.setSafeForMacOS(true);
1145 		assertCorrupt(
1146 				"invalid name '.gi\u200Ct' contains ignorable Unicode characters",
1147 				OBJ_TREE, data);
1148 		assertSkipListAccepts(OBJ_TREE, data);
1149 		checker.setIgnore(HAS_DOTGIT, true);
1150 		checker.checkTree(data);
1151 	}
1152 
1153 	@Test
1154 	public void testInvalidTreeNameIsMacHFSGit2()
1155 			throws CorruptObjectException {
1156 		StringBuilder b = new StringBuilder();
1157 		entry(b, "100644 \u206B.git");
1158 		byte[] data = encode(b.toString());
1159 
1160 		// Fine on POSIX.
1161 		checker.checkTree(data);
1162 
1163 		// Rejected on Mac OS.
1164 		checker.setSafeForMacOS(true);
1165 		assertCorrupt(
1166 				"invalid name '\u206B.git' contains ignorable Unicode characters",
1167 				OBJ_TREE, data);
1168 		assertSkipListAccepts(OBJ_TREE, data);
1169 		checker.setIgnore(HAS_DOTGIT, true);
1170 		checker.checkTree(data);
1171 	}
1172 
1173 	@Test
1174 	public void testInvalidTreeNameIsMacHFSGit3()
1175 			throws CorruptObjectException {
1176 		StringBuilder b = new StringBuilder();
1177 		entry(b, "100644 .git\uFEFF");
1178 		byte[] data = encode(b.toString());
1179 
1180 		// Fine on POSIX.
1181 		checker.checkTree(data);
1182 
1183 		// Rejected on Mac OS.
1184 		checker.setSafeForMacOS(true);
1185 		assertCorrupt(
1186 				"invalid name '.git\uFEFF' contains ignorable Unicode characters",
1187 				OBJ_TREE, data);
1188 		assertSkipListAccepts(OBJ_TREE, data);
1189 		checker.setIgnore(HAS_DOTGIT, true);
1190 		checker.checkTree(data);
1191 	}
1192 
1193 
1194 
1195 	@Test
1196 	public void testInvalidTreeNameIsMacHFSGitCorruptUTF8AtEnd()
1197 			throws CorruptObjectException {
1198 		byte[] data = concat(encode("100644 .git"),
1199 				new byte[] { (byte) 0xef });
1200 		StringBuilder b = new StringBuilder();
1201 		entry(b, "");
1202 		data = concat(data, encode(b.toString()));
1203 
1204 		// Fine on POSIX.
1205 		checker.checkTree(data);
1206 
1207 		// Rejected on Mac OS.
1208 		checker.setSafeForMacOS(true);
1209 		assertCorrupt(
1210 				"invalid name contains byte sequence '0xef' which is not a valid UTF-8 character",
1211 				OBJ_TREE, data);
1212 		assertSkipListAccepts(OBJ_TREE, data);
1213 	}
1214 
1215 	@Test
1216 	public void testInvalidTreeNameIsMacHFSGitCorruptUTF8AtEnd2()
1217 			throws CorruptObjectException {
1218 		byte[] data = concat(encode("100644 .git"),
1219 				new byte[] {
1220 				(byte) 0xe2, (byte) 0xab });
1221 		StringBuilder b = new StringBuilder();
1222 		entry(b, "");
1223 		data = concat(data, encode(b.toString()));
1224 
1225 		// Fine on POSIX.
1226 		checker.checkTree(data);
1227 
1228 		// Rejected on Mac OS.
1229 		checker.setSafeForMacOS(true);
1230 		assertCorrupt(
1231 				"invalid name contains byte sequence '0xe2ab' which is not a valid UTF-8 character",
1232 				OBJ_TREE, data);
1233 		assertSkipListAccepts(OBJ_TREE, data);
1234 	}
1235 
1236 	@Test
1237 	public void testInvalidTreeNameIsNotMacHFSGit()
1238 			throws CorruptObjectException {
1239 		StringBuilder b = new StringBuilder();
1240 		entry(b, "100644 .git\u200Cx");
1241 		byte[] data = encode(b.toString());
1242 		checker.setSafeForMacOS(true);
1243 		checker.checkTree(data);
1244 	}
1245 
1246 	@Test
1247 	public void testInvalidTreeNameIsNotMacHFSGit2()
1248 			throws CorruptObjectException {
1249 		StringBuilder b = new StringBuilder();
1250 		entry(b, "100644 .kit\u200C");
1251 		byte[] data = encode(b.toString());
1252 		checker.setSafeForMacOS(true);
1253 		checker.checkTree(data);
1254 	}
1255 
1256 	@Test
1257 	public void testInvalidTreeNameIsNotMacHFSGitOtherPlatform()
1258 			throws CorruptObjectException {
1259 		StringBuilder b = new StringBuilder();
1260 		entry(b, "100644 .git\u200C");
1261 		byte[] data = encode(b.toString());
1262 		checker.checkTree(data);
1263 	}
1264 
1265 	@Test
1266 	public void testInvalidTreeNameIsDotGitDot() throws CorruptObjectException {
1267 		StringBuilder b = new StringBuilder();
1268 		entry(b, "100644 .git.");
1269 		byte[] data = encodeASCII(b.toString());
1270 		assertCorrupt("invalid name '.git.'", OBJ_TREE, data);
1271 		assertSkipListAccepts(OBJ_TREE, data);
1272 		checker.setIgnore(HAS_DOTGIT, true);
1273 		checker.checkTree(data);
1274 	}
1275 
1276 	@Test
1277 	public void testValidTreeNameIsDotGitDotDot()
1278 			throws CorruptObjectException {
1279 		StringBuilder b = new StringBuilder();
1280 		entry(b, "100644 .git..");
1281 		checker.checkTree(encodeASCII(b.toString()));
1282 	}
1283 
1284 	@Test
1285 	public void testInvalidTreeNameIsDotGitSpace()
1286 			throws CorruptObjectException {
1287 		StringBuilder b = new StringBuilder();
1288 		entry(b, "100644 .git ");
1289 		byte[] data = encodeASCII(b.toString());
1290 		assertCorrupt("invalid name '.git '", OBJ_TREE, data);
1291 		assertSkipListAccepts(OBJ_TREE, data);
1292 		checker.setIgnore(HAS_DOTGIT, true);
1293 		checker.checkTree(data);
1294 	}
1295 
1296 	@Test
1297 	public void testInvalidTreeNameIsDotGitSomething()
1298 			throws CorruptObjectException {
1299 		StringBuilder b = new StringBuilder();
1300 		entry(b, "100644 .gitfoobar");
1301 		byte[] data = encodeASCII(b.toString());
1302 		checker.checkTree(data);
1303 	}
1304 
1305 	@Test
1306 	public void testInvalidTreeNameIsDotGitSomethingSpaceSomething()
1307 			throws CorruptObjectException {
1308 		StringBuilder b = new StringBuilder();
1309 		entry(b, "100644 .gitfoo bar");
1310 		byte[] data = encodeASCII(b.toString());
1311 		checker.checkTree(data);
1312 	}
1313 
1314 	@Test
1315 	public void testInvalidTreeNameIsDotGitSomethingDot()
1316 			throws CorruptObjectException {
1317 		StringBuilder b = new StringBuilder();
1318 		entry(b, "100644 .gitfoobar.");
1319 		byte[] data = encodeASCII(b.toString());
1320 		checker.checkTree(data);
1321 	}
1322 
1323 	@Test
1324 	public void testInvalidTreeNameIsDotGitSomethingDotDot()
1325 			throws CorruptObjectException {
1326 		StringBuilder b = new StringBuilder();
1327 		entry(b, "100644 .gitfoobar..");
1328 		byte[] data = encodeASCII(b.toString());
1329 		checker.checkTree(data);
1330 	}
1331 
1332 	@Test
1333 	public void testInvalidTreeNameIsDotGitDotSpace()
1334 			throws CorruptObjectException {
1335 		StringBuilder b = new StringBuilder();
1336 		entry(b, "100644 .git. ");
1337 		byte[] data = encodeASCII(b.toString());
1338 		assertCorrupt("invalid name '.git. '", OBJ_TREE, data);
1339 		assertSkipListAccepts(OBJ_TREE, data);
1340 		checker.setIgnore(HAS_DOTGIT, true);
1341 		checker.checkTree(data);
1342 	}
1343 
1344 	@Test
1345 	public void testInvalidTreeNameIsDotGitSpaceDot()
1346 			throws CorruptObjectException {
1347 		StringBuilder b = new StringBuilder();
1348 		entry(b, "100644 .git . ");
1349 		byte[] data = encodeASCII(b.toString());
1350 		assertCorrupt("invalid name '.git . '", OBJ_TREE, data);
1351 		assertSkipListAccepts(OBJ_TREE, data);
1352 		checker.setIgnore(HAS_DOTGIT, true);
1353 		checker.checkTree(data);
1354 	}
1355 
1356 	@Test
1357 	public void testInvalidTreeNameIsGITTilde1() throws CorruptObjectException {
1358 		StringBuilder b = new StringBuilder();
1359 		entry(b, "100644 GIT~1");
1360 		byte[] data = encodeASCII(b.toString());
1361 		assertCorrupt("invalid name 'GIT~1'", OBJ_TREE, data);
1362 		assertSkipListAccepts(OBJ_TREE, data);
1363 		checker.setIgnore(HAS_DOTGIT, true);
1364 		checker.checkTree(data);
1365 	}
1366 
1367 	@Test
1368 	public void testInvalidTreeNameIsGiTTilde1() throws CorruptObjectException {
1369 		StringBuilder b = new StringBuilder();
1370 		entry(b, "100644 GiT~1");
1371 		byte[] data = encodeASCII(b.toString());
1372 		assertCorrupt("invalid name 'GiT~1'", OBJ_TREE, data);
1373 		assertSkipListAccepts(OBJ_TREE, data);
1374 		checker.setIgnore(HAS_DOTGIT, true);
1375 		checker.checkTree(data);
1376 	}
1377 
1378 	@Test
1379 	public void testValidTreeNameIsGitTilde11() throws CorruptObjectException {
1380 		StringBuilder b = new StringBuilder();
1381 		entry(b, "100644 GIT~11");
1382 		byte[] data = encodeASCII(b.toString());
1383 		checker.checkTree(data);
1384 	}
1385 
1386 	@Test
1387 	public void testInvalidTreeTruncatedInName() {
1388 		StringBuilder b = new StringBuilder();
1389 		b.append("100644 b");
1390 		byte[] data = encodeASCII(b.toString());
1391 		assertCorrupt("truncated in name", OBJ_TREE, data);
1392 		assertSkipListRejects("truncated in name", OBJ_TREE, data);
1393 	}
1394 
1395 	@Test
1396 	public void testInvalidTreeTruncatedInObjectId() {
1397 		StringBuilder b = new StringBuilder();
1398 		b.append("100644 b\0\1\2");
1399 		byte[] data = encodeASCII(b.toString());
1400 		assertCorrupt("truncated in object id", OBJ_TREE, data);
1401 		assertSkipListRejects("truncated in object id", OBJ_TREE, data);
1402 	}
1403 
1404 	@Test
1405 	public void testInvalidTreeBadSorting1() throws CorruptObjectException {
1406 		StringBuilder b = new StringBuilder();
1407 		entry(b, "100644 foobar");
1408 		entry(b, "100644 fooaaa");
1409 		byte[] data = encodeASCII(b.toString());
1410 
1411 		assertCorrupt("incorrectly sorted", OBJ_TREE, data);
1412 
1413 		ObjectId id = idFor(OBJ_TREE, data);
1414 		try {
1415 			checker.check(id, OBJ_TREE, data);
1416 			fail("Did not throw CorruptObjectException");
1417 		} catch (CorruptObjectException e) {
1418 			assertSame(TREE_NOT_SORTED, e.getErrorType());
1419 			assertEquals("treeNotSorted: object " + id.name()
1420 					+ ": incorrectly sorted", e.getMessage());
1421 		}
1422 
1423 		assertSkipListAccepts(OBJ_TREE, data);
1424 		checker.setIgnore(TREE_NOT_SORTED, true);
1425 		checker.checkTree(data);
1426 	}
1427 
1428 	@Test
1429 	public void testInvalidTreeBadSorting2() throws CorruptObjectException {
1430 		StringBuilder b = new StringBuilder();
1431 		entry(b, "40000 a");
1432 		entry(b, "100644 a.c");
1433 		byte[] data = encodeASCII(b.toString());
1434 		assertCorrupt("incorrectly sorted", OBJ_TREE, data);
1435 		assertSkipListAccepts(OBJ_TREE, data);
1436 		checker.setIgnore(TREE_NOT_SORTED, true);
1437 		checker.checkTree(data);
1438 	}
1439 
1440 	@Test
1441 	public void testInvalidTreeBadSorting3() throws CorruptObjectException {
1442 		StringBuilder b = new StringBuilder();
1443 		entry(b, "100644 a0c");
1444 		entry(b, "40000 a");
1445 		byte[] data = encodeASCII(b.toString());
1446 		assertCorrupt("incorrectly sorted", OBJ_TREE, data);
1447 		assertSkipListAccepts(OBJ_TREE, data);
1448 		checker.setIgnore(TREE_NOT_SORTED, true);
1449 		checker.checkTree(data);
1450 	}
1451 
1452 	@Test
1453 	public void testInvalidTreeDuplicateNames1_File()
1454 			throws CorruptObjectException {
1455 		StringBuilder b = new StringBuilder();
1456 		entry(b, "100644 a");
1457 		entry(b, "100644 a");
1458 		byte[] data = encodeASCII(b.toString());
1459 		assertCorrupt("duplicate entry names", OBJ_TREE, data);
1460 		assertSkipListAccepts(OBJ_TREE, data);
1461 		checker.setIgnore(DUPLICATE_ENTRIES, true);
1462 		checker.checkTree(data);
1463 	}
1464 
1465 	@Test
1466 	public void testInvalidTreeDuplicateNames1_Tree()
1467 			throws CorruptObjectException {
1468 		StringBuilder b = new StringBuilder();
1469 		entry(b, "40000 a");
1470 		entry(b, "40000 a");
1471 		byte[] data = encodeASCII(b.toString());
1472 		assertCorrupt("duplicate entry names", OBJ_TREE, data);
1473 		assertSkipListAccepts(OBJ_TREE, data);
1474 		checker.setIgnore(DUPLICATE_ENTRIES, true);
1475 		checker.checkTree(data);
1476 	}
1477 
1478 	@Test
1479 	public void testInvalidTreeDuplicateNames2() throws CorruptObjectException {
1480 		StringBuilder b = new StringBuilder();
1481 		entry(b, "100644 a");
1482 		entry(b, "100755 a");
1483 		byte[] data = encodeASCII(b.toString());
1484 		assertCorrupt("duplicate entry names", OBJ_TREE, data);
1485 		assertSkipListAccepts(OBJ_TREE, data);
1486 		checker.setIgnore(DUPLICATE_ENTRIES, true);
1487 		checker.checkTree(data);
1488 	}
1489 
1490 	@Test
1491 	public void testInvalidTreeDuplicateNames3() throws CorruptObjectException {
1492 		StringBuilder b = new StringBuilder();
1493 		entry(b, "100644 a");
1494 		entry(b, "40000 a");
1495 		byte[] data = encodeASCII(b.toString());
1496 		assertCorrupt("duplicate entry names", OBJ_TREE, data);
1497 		assertSkipListAccepts(OBJ_TREE, data);
1498 		checker.setIgnore(DUPLICATE_ENTRIES, true);
1499 		checker.checkTree(data);
1500 	}
1501 
1502 	@Test
1503 	public void testInvalidTreeDuplicateNames4() throws CorruptObjectException {
1504 		StringBuilder b = new StringBuilder();
1505 		entry(b, "100644 a");
1506 		entry(b, "100644 a.c");
1507 		entry(b, "100644 a.d");
1508 		entry(b, "100644 a.e");
1509 		entry(b, "40000 a");
1510 		entry(b, "100644 zoo");
1511 		byte[] data = encodeASCII(b.toString());
1512 		assertCorrupt("duplicate entry names", OBJ_TREE, data);
1513 		assertSkipListAccepts(OBJ_TREE, data);
1514 		checker.setIgnore(DUPLICATE_ENTRIES, true);
1515 		checker.checkTree(data);
1516 	}
1517 
1518 	@Test
1519 	public void testInvalidTreeDuplicateNames5()
1520 			throws CorruptObjectException {
1521 		StringBuilder b = new StringBuilder();
1522 		entry(b, "100644 A");
1523 		entry(b, "100644 a");
1524 		byte[] data = b.toString().getBytes(UTF_8);
1525 		checker.setSafeForWindows(true);
1526 		assertCorrupt("duplicate entry names", OBJ_TREE, data);
1527 		assertSkipListAccepts(OBJ_TREE, data);
1528 		checker.setIgnore(DUPLICATE_ENTRIES, true);
1529 		checker.checkTree(data);
1530 	}
1531 
1532 	@Test
1533 	public void testInvalidTreeDuplicateNames6()
1534 			throws CorruptObjectException {
1535 		StringBuilder b = new StringBuilder();
1536 		entry(b, "100644 A");
1537 		entry(b, "100644 a");
1538 		byte[] data = b.toString().getBytes(UTF_8);
1539 		checker.setSafeForMacOS(true);
1540 		assertCorrupt("duplicate entry names", OBJ_TREE, data);
1541 		assertSkipListAccepts(OBJ_TREE, data);
1542 		checker.setIgnore(DUPLICATE_ENTRIES, true);
1543 		checker.checkTree(data);
1544 	}
1545 
1546 	@Test
1547 	public void testInvalidTreeDuplicateNames7()
1548 			throws CorruptObjectException {
1549 		StringBuilder b = new StringBuilder();
1550 		entry(b, "100644 \u0065\u0301");
1551 		entry(b, "100644 \u00e9");
1552 		byte[] data = b.toString().getBytes(UTF_8);
1553 		checker.setSafeForMacOS(true);
1554 		assertCorrupt("duplicate entry names", OBJ_TREE, data);
1555 		assertSkipListAccepts(OBJ_TREE, data);
1556 		checker.setIgnore(DUPLICATE_ENTRIES, true);
1557 		checker.checkTree(data);
1558 	}
1559 
1560 	@Test
1561 	public void testInvalidTreeDuplicateNames8()
1562 			throws CorruptObjectException {
1563 		StringBuilder b = new StringBuilder();
1564 		entry(b, "100644 A");
1565 		checker.setSafeForMacOS(true);
1566 		checker.checkTree(b.toString().getBytes(UTF_8));
1567 	}
1568 
1569 	@Test
1570 	public void testRejectNulInPathSegment() {
1571 		try {
1572 			checker.checkPathSegment(encodeASCII("a\u0000b"), 0, 3);
1573 			fail("incorrectly accepted NUL in middle of name");
1574 		} catch (CorruptObjectException e) {
1575 			assertEquals("name contains byte 0x00", e.getMessage());
1576 		}
1577 	}
1578 
1579 	@Test
1580 	public void testRejectSpaceAtEndOnWindows() {
1581 		checker.setSafeForWindows(true);
1582 		try {
1583 			checkOneName("test ");
1584 			fail("incorrectly accepted space at end");
1585 		} catch (CorruptObjectException e) {
1586 			assertEquals("invalid name ends with ' '", e.getMessage());
1587 		}
1588 	}
1589 
1590 	@Test
1591 	public void testBug477090() throws CorruptObjectException {
1592 		checker.setSafeForMacOS(true);
1593 		final byte[] bytes = {
1594 				// U+221E 0xe2889e INFINITY ∞
1595 				(byte) 0xe2, (byte) 0x88, (byte) 0x9e,
1596 				// .html
1597 				0x2e, 0x68, 0x74, 0x6d, 0x6c };
1598 		checker.checkPathSegment(bytes, 0, bytes.length);
1599 	}
1600 
1601 	@Test
1602 	public void testRejectDotAtEndOnWindows() {
1603 		checker.setSafeForWindows(true);
1604 		try {
1605 			checkOneName("test.");
1606 			fail("incorrectly accepted dot at end");
1607 		} catch (CorruptObjectException e) {
1608 			assertEquals("invalid name ends with '.'", e.getMessage());
1609 		}
1610 	}
1611 
1612 	@Test
1613 	public void testRejectDevicesOnWindows() {
1614 		checker.setSafeForWindows(true);
1615 
1616 		String[] bad = { "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3",
1617 				"COM4", "COM5", "COM6", "COM7", "COM8", "COM9", "LPT1", "LPT2",
1618 				"LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9" };
1619 		for (String b : bad) {
1620 			try {
1621 				checkOneName(b);
1622 				fail("incorrectly accepted " + b);
1623 			} catch (CorruptObjectException e) {
1624 				assertEquals("invalid name '" + b + "'", e.getMessage());
1625 			}
1626 			try {
1627 				checkOneName(b + ".txt");
1628 				fail("incorrectly accepted " + b + ".txt");
1629 			} catch (CorruptObjectException e) {
1630 				assertEquals("invalid name '" + b + "'", e.getMessage());
1631 			}
1632 		}
1633 	}
1634 
1635 	@Test
1636 	public void testRejectInvalidWindowsCharacters() {
1637 		checker.setSafeForWindows(true);
1638 		rejectName('<');
1639 		rejectName('>');
1640 		rejectName(':');
1641 		rejectName('"');
1642 		rejectName('\\');
1643 		rejectName('|');
1644 		rejectName('?');
1645 		rejectName('*');
1646 
1647 		for (int i = 1; i <= 31; i++)
1648 			rejectName((byte) i);
1649 	}
1650 
1651 	private void rejectName(char c) {
1652 		try {
1653 			checkOneName("te" + c + "st");
1654 			fail("incorrectly accepted with " + c);
1655 		} catch (CorruptObjectException e) {
1656 
1657 			assertEquals("char '" + c + "' not allowed in Windows filename", e.getMessage());
1658 		}
1659 	}
1660 
1661 	private void rejectName(byte c) {
1662 		String h = Integer.toHexString(c);
1663 		try {
1664 			checkOneName("te" + ((char) c) + "st");
1665 			fail("incorrectly accepted with 0x" + h);
1666 		} catch (CorruptObjectException e) {
1667 			assertEquals("byte 0x" + h + " not allowed in Windows filename", e.getMessage());
1668 		}
1669 	}
1670 
1671 
1672 	@Test
1673 	public void testRejectInvalidCharacter() {
1674 		try {
1675 			checkOneName("te/st");
1676 			fail("incorrectly accepted with /");
1677 		} catch (CorruptObjectException e) {
1678 
1679 			assertEquals("name contains '/'", e.getMessage());
1680 		}
1681 	}
1682 
1683 	private void checkOneName(String name) throws CorruptObjectException {
1684 		StringBuilder b = new StringBuilder();
1685 		entry(b, "100644 " + name);
1686 		checker.checkTree(encodeASCII(b.toString()));
1687 	}
1688 
1689 	/*
1690 	 * Returns the id generated for the entry
1691 	 */
1692 	private static ObjectId entry(StringBuilder b, String modeName) {
1693 		byte[] id = new byte[OBJECT_ID_LENGTH];
1694 
1695 		b.append(modeName);
1696 		b.append('\0');
1697 		for (int i = 0; i < OBJECT_ID_LENGTH; i++) {
1698 			b.append((char) i);
1699 			id[i] = (byte) i;
1700 		}
1701 
1702 		return ObjectId.fromRaw(id);
1703 	}
1704 
1705 	private void assertCorrupt(String msg, int type, StringBuilder b) {
1706 		assertCorrupt(msg, type, encodeASCII(b.toString()));
1707 	}
1708 
1709 	private void assertCorrupt(String msg, int type, byte[] data) {
1710 		try {
1711 			checker.check(type, data);
1712 			fail("Did not throw CorruptObjectException");
1713 		} catch (CorruptObjectException e) {
1714 			assertEquals(msg, e.getMessage());
1715 		}
1716 	}
1717 
1718 	private void assertSkipListAccepts(int type, byte[] data)
1719 			throws CorruptObjectException {
1720 		ObjectId id = idFor(type, data);
1721 		checker.setSkipList(set(id));
1722 		checker.check(id, type, data);
1723 		checker.setSkipList(null);
1724 	}
1725 
1726 	private void assertSkipListRejects(String msg, int type, byte[] data) {
1727 		ObjectId id = idFor(type, data);
1728 		checker.setSkipList(set(id));
1729 		try {
1730 			checker.check(id, type, data);
1731 			fail("Did not throw CorruptObjectException");
1732 		} catch (CorruptObjectException e) {
1733 			assertEquals(msg, e.getMessage());
1734 		}
1735 		checker.setSkipList(null);
1736 	}
1737 
1738 	private static ObjectIdSet set(ObjectId... ids) {
1739 		return (AnyObjectId objectId) -> {
1740 			for (ObjectId id : ids) {
1741 				if (id.equals(objectId)) {
1742 					return true;
1743 				}
1744 			}
1745 			return false;
1746 		};
1747 	}
1748 
1749 	@SuppressWarnings("resource")
1750 	private static ObjectId idFor(int type, byte[] raw) {
1751 		return new ObjectInserter.Formatter().idFor(type, raw);
1752 	}
1753 }