您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

NoteMapTest.java 14KB

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