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