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