1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44 package org.eclipse.jgit.notes;
45
46 import static org.junit.Assert.assertEquals;
47 import static org.junit.Assert.assertFalse;
48 import static org.junit.Assert.assertNotNull;
49 import static org.junit.Assert.assertNotSame;
50 import static org.junit.Assert.assertNull;
51 import static org.junit.Assert.assertSame;
52 import static org.junit.Assert.assertTrue;
53
54 import java.io.IOException;
55 import java.util.Iterator;
56
57 import org.eclipse.jgit.junit.RepositoryTestCase;
58 import org.eclipse.jgit.junit.TestRepository;
59 import org.eclipse.jgit.lib.CommitBuilder;
60 import org.eclipse.jgit.lib.Constants;
61 import org.eclipse.jgit.lib.MutableObjectId;
62 import org.eclipse.jgit.lib.ObjectInserter;
63 import org.eclipse.jgit.lib.ObjectReader;
64 import org.eclipse.jgit.lib.Repository;
65 import org.eclipse.jgit.revwalk.RevBlob;
66 import org.eclipse.jgit.revwalk.RevCommit;
67 import org.eclipse.jgit.revwalk.RevTree;
68 import org.eclipse.jgit.treewalk.TreeWalk;
69 import org.eclipse.jgit.util.RawParseUtils;
70 import org.junit.After;
71 import org.junit.Before;
72 import org.junit.Test;
73
74 public class NoteMapTest extends RepositoryTestCase {
75 private TestRepository<Repository> tr;
76
77 private ObjectReader reader;
78
79 private ObjectInserter inserter;
80
81 @Override
82 @Before
83 public void setUp() throws Exception {
84 super.setUp();
85
86 tr = new TestRepository<>(db);
87 reader = db.newObjectReader();
88 inserter = db.newObjectInserter();
89 }
90
91 @Override
92 @After
93 public void tearDown() throws Exception {
94 reader.close();
95 inserter.close();
96 super.tearDown();
97 }
98
99 @Test
100 public void testReadFlatTwoNotes() throws Exception {
101 RevBlob a = tr.blob("a");
102 RevBlob b = tr.blob("b");
103 RevBlob data1 = tr.blob("data1");
104 RevBlob data2 = tr.blob("data2");
105
106 RevCommit r = tr.commit()
107 .add(a.name(), data1)
108 .add(b.name(), data2)
109 .create();
110 tr.parseBody(r);
111
112 NoteMap map = NoteMap.read(reader, r);
113 assertNotNull("have map", map);
114
115 assertTrue("has note for a", map.contains(a));
116 assertTrue("has note for b", map.contains(b));
117 assertEquals(data1, map.get(a));
118 assertEquals(data2, map.get(b));
119
120 assertFalse("no note for data1", map.contains(data1));
121 assertNull("no note for data1", map.get(data1));
122 }
123
124 @Test
125 public void testReadFanout2_38() throws Exception {
126 RevBlob a = tr.blob("a");
127 RevBlob b = tr.blob("b");
128 RevBlob data1 = tr.blob("data1");
129 RevBlob data2 = tr.blob("data2");
130
131 RevCommit r = tr.commit()
132 .add(fanout(2, a.name()), data1)
133 .add(fanout(2, b.name()), data2)
134 .create();
135 tr.parseBody(r);
136
137 NoteMap map = NoteMap.read(reader, r);
138 assertNotNull("have map", map);
139
140 assertTrue("has note for a", map.contains(a));
141 assertTrue("has note for b", map.contains(b));
142 assertEquals(data1, map.get(a));
143 assertEquals(data2, map.get(b));
144
145 assertFalse("no note for data1", map.contains(data1));
146 assertNull("no note for data1", map.get(data1));
147 }
148
149 @Test
150 public void testReadFanout2_2_36() throws Exception {
151 RevBlob a = tr.blob("a");
152 RevBlob b = tr.blob("b");
153 RevBlob data1 = tr.blob("data1");
154 RevBlob data2 = tr.blob("data2");
155
156 RevCommit r = tr.commit()
157 .add(fanout(4, a.name()), data1)
158 .add(fanout(4, b.name()), data2)
159 .create();
160 tr.parseBody(r);
161
162 NoteMap map = NoteMap.read(reader, r);
163 assertNotNull("have map", map);
164
165 assertTrue("has note for a", map.contains(a));
166 assertTrue("has note for b", map.contains(b));
167 assertEquals(data1, map.get(a));
168 assertEquals(data2, map.get(b));
169
170 assertFalse("no note for data1", map.contains(data1));
171 assertNull("no note for data1", map.get(data1));
172 }
173
174 @Test
175 public void testReadFullyFannedOut() throws Exception {
176 RevBlob a = tr.blob("a");
177 RevBlob b = tr.blob("b");
178 RevBlob data1 = tr.blob("data1");
179 RevBlob data2 = tr.blob("data2");
180
181 RevCommit r = tr.commit()
182 .add(fanout(38, a.name()), data1)
183 .add(fanout(38, b.name()), data2)
184 .create();
185 tr.parseBody(r);
186
187 NoteMap map = NoteMap.read(reader, r);
188 assertNotNull("have map", map);
189
190 assertTrue("has note for a", map.contains(a));
191 assertTrue("has note for b", map.contains(b));
192 assertEquals(data1, map.get(a));
193 assertEquals(data2, map.get(b));
194
195 assertFalse("no note for data1", map.contains(data1));
196 assertNull("no note for data1", map.get(data1));
197 }
198
199 @Test
200 public void testGetCachedBytes() throws Exception {
201 final String exp = "this is test data";
202 RevBlob a = tr.blob("a");
203 RevBlob data = tr.blob(exp);
204
205 RevCommit r = tr.commit()
206 .add(a.name(), data)
207 .create();
208 tr.parseBody(r);
209
210 NoteMap map = NoteMap.read(reader, r);
211 byte[] act = map.getCachedBytes(a, exp.length() * 4);
212 assertNotNull("has data for a", act);
213 assertEquals(exp, RawParseUtils.decode(act));
214 }
215
216 @Test
217 public void testWriteUnchangedFlat() throws Exception {
218 RevBlob a = tr.blob("a");
219 RevBlob b = tr.blob("b");
220 RevBlob data1 = tr.blob("data1");
221 RevBlob data2 = tr.blob("data2");
222
223 RevCommit r = tr.commit()
224 .add(a.name(), data1)
225 .add(b.name(), data2)
226 .add(".gitignore", "")
227 .add("zoo-animals.txt", "")
228 .create();
229 tr.parseBody(r);
230
231 NoteMap map = NoteMap.read(reader, r);
232 assertTrue("has note for a", map.contains(a));
233 assertTrue("has note for b", map.contains(b));
234
235 RevCommit n = commitNoteMap(map);
236 assertNotSame("is new commit", r, n);
237 assertSame("same tree", r.getTree(), n.getTree());
238 }
239
240 @Test
241 public void testWriteUnchangedFanout2_38() throws Exception {
242 RevBlob a = tr.blob("a");
243 RevBlob b = tr.blob("b");
244 RevBlob data1 = tr.blob("data1");
245 RevBlob data2 = tr.blob("data2");
246
247 RevCommit r = tr.commit()
248 .add(fanout(2, a.name()), data1)
249 .add(fanout(2, b.name()), data2)
250 .add(".gitignore", "")
251 .add("zoo-animals.txt", "")
252 .create();
253 tr.parseBody(r);
254
255 NoteMap map = NoteMap.read(reader, r);
256 assertTrue("has note for a", map.contains(a));
257 assertTrue("has note for b", map.contains(b));
258
259
260 RevCommit n = commitNoteMap(map);
261 assertNotSame("is new commit", r, n);
262 assertSame("same tree", r.getTree(), n.getTree());
263
264
265 map = NoteMap.read(reader, r);
266 n = commitNoteMap(map);
267 assertNotSame("is new commit", r, n);
268 assertSame("same tree", r.getTree(), n.getTree());
269 }
270
271 @Test
272 public void testCreateFromEmpty() throws Exception {
273 RevBlob a = tr.blob("a");
274 RevBlob b = tr.blob("b");
275 RevBlob data1 = tr.blob("data1");
276 RevBlob data2 = tr.blob("data2");
277
278 NoteMap map = NoteMap.newEmptyMap();
279 assertFalse("no a", map.contains(a));
280 assertFalse("no b", map.contains(b));
281
282 map.set(a, data1);
283 map.set(b, data2);
284
285 assertEquals(data1, map.get(a));
286 assertEquals(data2, map.get(b));
287
288 map.remove(a);
289 map.remove(b);
290
291 assertFalse("no a", map.contains(a));
292 assertFalse("no b", map.contains(b));
293
294 map.set(a, "data1", inserter);
295 assertEquals(data1, map.get(a));
296
297 map.set(a, null, inserter);
298 assertFalse("no a", map.contains(a));
299 }
300
301 @Test
302 public void testEditFlat() throws Exception {
303 RevBlob a = tr.blob("a");
304 RevBlob b = tr.blob("b");
305 RevBlob data1 = tr.blob("data1");
306 RevBlob data2 = tr.blob("data2");
307
308 RevCommit r = tr.commit()
309 .add(a.name(), data1)
310 .add(b.name(), data2)
311 .add(".gitignore", "")
312 .add("zoo-animals.txt", b)
313 .create();
314 tr.parseBody(r);
315
316 NoteMap map = NoteMap.read(reader, r);
317 map.set(a, data2);
318 map.set(b, null);
319 map.set(data1, b);
320 map.set(data2, null);
321
322 assertEquals(data2, map.get(a));
323 assertEquals(b, map.get(data1));
324 assertFalse("no b", map.contains(b));
325 assertFalse("no data2", map.contains(data2));
326
327 MutableObjectId id = new MutableObjectId();
328 for (int p = 42; p > 0; p--) {
329 id.setByte(1, p);
330 map.set(id, data1);
331 }
332
333 for (int p = 42; p > 0; p--) {
334 id.setByte(1, p);
335 assertTrue("contains " + id, map.contains(id));
336 }
337
338 RevCommit n = commitNoteMap(map);
339 map = NoteMap.read(reader, n);
340 assertEquals(data2, map.get(a));
341 assertEquals(b, map.get(data1));
342 assertFalse("no b", map.contains(b));
343 assertFalse("no data2", map.contains(data2));
344 assertEquals(b, TreeWalk
345 .forPath(reader, "zoo-animals.txt", n.getTree()).getObjectId(0));
346 }
347
348 @Test
349 public void testEditFanout2_38() throws Exception {
350 RevBlob a = tr.blob("a");
351 RevBlob b = tr.blob("b");
352 RevBlob data1 = tr.blob("data1");
353 RevBlob data2 = tr.blob("data2");
354
355 RevCommit r = tr.commit()
356 .add(fanout(2, a.name()), data1)
357 .add(fanout(2, b.name()), data2)
358 .add(".gitignore", "")
359 .add("zoo-animals.txt", b)
360 .create();
361 tr.parseBody(r);
362
363 NoteMap map = NoteMap.read(reader, r);
364 map.set(a, data2);
365 map.set(b, null);
366 map.set(data1, b);
367 map.set(data2, null);
368
369 assertEquals(data2, map.get(a));
370 assertEquals(b, map.get(data1));
371 assertFalse("no b", map.contains(b));
372 assertFalse("no data2", map.contains(data2));
373 RevCommit n = commitNoteMap(map);
374
375 map.set(a, null);
376 map.set(data1, null);
377 assertFalse("no a", map.contains(a));
378 assertFalse("no data1", map.contains(data1));
379
380 map = NoteMap.read(reader, n);
381 assertEquals(data2, map.get(a));
382 assertEquals(b, map.get(data1));
383 assertFalse("no b", map.contains(b));
384 assertFalse("no data2", map.contains(data2));
385 assertEquals(b, TreeWalk
386 .forPath(reader, "zoo-animals.txt", n.getTree()).getObjectId(0));
387 }
388
389 @Test
390 public void testLeafSplitsWhenFull() throws Exception {
391 RevBlob data1 = tr.blob("data1");
392 MutableObjectId idBuf = new MutableObjectId();
393
394 RevCommit r = tr.commit()
395 .add(data1.name(), data1)
396 .create();
397 tr.parseBody(r);
398
399 NoteMap map = NoteMap.read(reader, r);
400 for (int i = 0; i < 254; i++) {
401 idBuf.setByte(Constants.OBJECT_ID_LENGTH - 1, i);
402 map.set(idBuf, data1);
403 }
404
405 RevCommit n = commitNoteMap(map);
406 try (TreeWalk tw = new TreeWalk(reader)) {
407 tw.reset(n.getTree());
408 while (tw.next()) {
409 assertFalse("no fan-out subtree", tw.isSubtree());
410 }
411 }
412
413 for (int i = 254; i < 256; i++) {
414 idBuf.setByte(Constants.OBJECT_ID_LENGTH - 1, i);
415 map.set(idBuf, data1);
416 }
417 idBuf.setByte(Constants.OBJECT_ID_LENGTH - 2, 1);
418 map.set(idBuf, data1);
419 n = commitNoteMap(map);
420
421
422 String path = fanout(38, idBuf.name());
423 try (TreeWalk tw = TreeWalk.forPath(reader, path, n.getTree())) {
424 assertNotNull("has " + path, tw);
425 }
426
427
428 path = fanout(2, data1.name());
429 try (TreeWalk tw = TreeWalk.forPath(reader, path, n.getTree())) {
430 assertNotNull("has " + path, tw);
431 }
432 }
433
434 @Test
435 public void testRemoveDeletesTreeFanout2_38() throws Exception {
436 RevBlob a = tr.blob("a");
437 RevBlob data1 = tr.blob("data1");
438 RevTree empty = tr.tree();
439
440 RevCommit r = tr.commit()
441 .add(fanout(2, a.name()), data1)
442 .create();
443 tr.parseBody(r);
444
445 NoteMap map = NoteMap.read(reader, r);
446 map.set(a, null);
447
448 RevCommit n = commitNoteMap(map);
449 assertEquals("empty tree", empty, n.getTree());
450 }
451
452 @Test
453 public void testIteratorEmptyMap() {
454 Iterator<Note> it = NoteMap.newEmptyMap().iterator();
455 assertFalse(it.hasNext());
456 }
457
458 @Test
459 public void testIteratorFlatTree() throws Exception {
460 RevBlob a = tr.blob("a");
461 RevBlob b = tr.blob("b");
462 RevBlob data1 = tr.blob("data1");
463 RevBlob data2 = tr.blob("data2");
464 RevBlob nonNote = tr.blob("non note");
465
466 RevCommit r = tr.commit()
467 .add(a.name(), data1)
468 .add(b.name(), data2)
469 .add("nonNote", nonNote)
470 .create();
471 tr.parseBody(r);
472
473 Iterator it = NoteMap.read(reader, r).iterator();
474 assertEquals(2, count(it));
475 }
476
477 @Test
478 public void testIteratorFanoutTree2_38() throws Exception {
479 RevBlob a = tr.blob("a");
480 RevBlob b = tr.blob("b");
481 RevBlob data1 = tr.blob("data1");
482 RevBlob data2 = tr.blob("data2");
483 RevBlob nonNote = tr.blob("non note");
484
485 RevCommit r = tr.commit()
486 .add(fanout(2, a.name()), data1)
487 .add(fanout(2, b.name()), data2)
488 .add("nonNote", nonNote)
489 .create();
490 tr.parseBody(r);
491
492 Iterator it = NoteMap.read(reader, r).iterator();
493 assertEquals(2, count(it));
494 }
495
496 @Test
497 public void testIteratorFanoutTree2_2_36() throws Exception {
498 RevBlob a = tr.blob("a");
499 RevBlob b = tr.blob("b");
500 RevBlob data1 = tr.blob("data1");
501 RevBlob data2 = tr.blob("data2");
502 RevBlob nonNote = tr.blob("non note");
503
504 RevCommit r = tr.commit()
505 .add(fanout(4, a.name()), data1)
506 .add(fanout(4, b.name()), data2)
507 .add("nonNote", nonNote)
508 .create();
509 tr.parseBody(r);
510
511 Iterator it = NoteMap.read(reader, r).iterator();
512 assertEquals(2, count(it));
513 }
514
515 @Test
516 public void testIteratorFullyFannedOut() throws Exception {
517 RevBlob a = tr.blob("a");
518 RevBlob b = tr.blob("b");
519 RevBlob data1 = tr.blob("data1");
520 RevBlob data2 = tr.blob("data2");
521 RevBlob nonNote = tr.blob("non note");
522
523 RevCommit r = tr.commit()
524 .add(fanout(38, a.name()), data1)
525 .add(fanout(38, b.name()), data2)
526 .add("nonNote", nonNote)
527 .create();
528 tr.parseBody(r);
529
530 Iterator it = NoteMap.read(reader, r).iterator();
531 assertEquals(2, count(it));
532 }
533
534 @Test
535 public void testShorteningNoteRefName() throws Exception {
536 String expectedShortName = "review";
537 String noteRefName = Constants.R_NOTES + expectedShortName;
538 assertEquals(expectedShortName, NoteMap.shortenRefName(noteRefName));
539 String nonNoteRefName = Constants.R_HEADS + expectedShortName;
540 assertEquals(nonNoteRefName, NoteMap.shortenRefName(nonNoteRefName));
541 }
542
543 private RevCommit commitNoteMap(NoteMap map) throws IOException {
544 tr.tick(600);
545
546 CommitBuilder builder = new CommitBuilder();
547 builder.setTreeId(map.writeTree(inserter));
548 tr.setAuthorAndCommitter(builder);
549 return tr.getRevWalk().parseCommit(inserter.insert(builder));
550 }
551
552 private static String fanout(int prefix, String name) {
553 StringBuilder r = new StringBuilder();
554 int i = 0;
555 for (; i < prefix && i < name.length(); i += 2) {
556 if (i != 0)
557 r.append('/');
558 r.append(name.charAt(i + 0));
559 r.append(name.charAt(i + 1));
560 }
561 if (i < name.length()) {
562 if (i != 0)
563 r.append('/');
564 r.append(name.substring(i));
565 }
566 return r.toString();
567 }
568
569 private static int count(Iterator it) {
570 int c = 0;
571 while (it.hasNext()) {
572 c++;
573 it.next();
574 }
575 return c;
576 }
577 }