You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

NoteMapTest.java 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  1. /*
  2. * Copyright (C) 2010, Google Inc.
  3. * and other copyright owners as documented in the project's IP log.
  4. *
  5. * This program and the accompanying materials are made available
  6. * under the terms of the Eclipse Distribution License v1.0 which
  7. * accompanies this distribution, is reproduced below, and is
  8. * available at http://www.eclipse.org/org/documents/edl-v10.php
  9. *
  10. * All rights reserved.
  11. *
  12. * Redistribution and use in source and binary forms, with or
  13. * without modification, are permitted provided that the following
  14. * conditions are met:
  15. *
  16. * - Redistributions of source code must retain the above copyright
  17. * notice, this list of conditions and the following disclaimer.
  18. *
  19. * - Redistributions in binary form must reproduce the above
  20. * copyright notice, this list of conditions and the following
  21. * disclaimer in the documentation and/or other materials provided
  22. * with the distribution.
  23. *
  24. * - Neither the name of the Eclipse Foundation, Inc. nor the
  25. * names of its contributors may be used to endorse or promote
  26. * products derived from this software without specific prior
  27. * written permission.
  28. *
  29. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  30. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  31. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  32. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  33. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  34. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  35. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  36. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  37. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  38. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  39. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  40. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  41. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  42. */
  43. package org.eclipse.jgit.notes;
  44. import static org.junit.Assert.assertEquals;
  45. import static org.junit.Assert.assertFalse;
  46. import static org.junit.Assert.assertNotNull;
  47. import static org.junit.Assert.assertNotSame;
  48. import static org.junit.Assert.assertNull;
  49. import static org.junit.Assert.assertSame;
  50. import static org.junit.Assert.assertTrue;
  51. import java.io.IOException;
  52. import java.util.Iterator;
  53. import org.eclipse.jgit.junit.RepositoryTestCase;
  54. import org.eclipse.jgit.junit.TestRepository;
  55. import org.eclipse.jgit.lib.CommitBuilder;
  56. import org.eclipse.jgit.lib.Constants;
  57. import org.eclipse.jgit.lib.MutableObjectId;
  58. import org.eclipse.jgit.lib.ObjectInserter;
  59. import org.eclipse.jgit.lib.ObjectReader;
  60. import org.eclipse.jgit.lib.Repository;
  61. import org.eclipse.jgit.revwalk.RevBlob;
  62. import org.eclipse.jgit.revwalk.RevCommit;
  63. import org.eclipse.jgit.revwalk.RevTree;
  64. import org.eclipse.jgit.treewalk.TreeWalk;
  65. import org.eclipse.jgit.util.RawParseUtils;
  66. import org.junit.After;
  67. import org.junit.Before;
  68. import org.junit.Test;
  69. public class NoteMapTest extends RepositoryTestCase {
  70. private TestRepository<Repository> tr;
  71. private ObjectReader reader;
  72. private ObjectInserter inserter;
  73. @Override
  74. @Before
  75. public void setUp() throws Exception {
  76. super.setUp();
  77. tr = new TestRepository<>(db);
  78. reader = db.newObjectReader();
  79. inserter = db.newObjectInserter();
  80. }
  81. @Override
  82. @After
  83. public void tearDown() throws Exception {
  84. reader.close();
  85. inserter.close();
  86. super.tearDown();
  87. }
  88. @Test
  89. public void testReadFlatTwoNotes() throws Exception {
  90. RevBlob a = tr.blob("a");
  91. RevBlob b = tr.blob("b");
  92. RevBlob data1 = tr.blob("data1");
  93. RevBlob data2 = tr.blob("data2");
  94. RevCommit r = tr.commit() //
  95. .add(a.name(), data1) //
  96. .add(b.name(), data2) //
  97. .create();
  98. tr.parseBody(r);
  99. NoteMap map = NoteMap.read(reader, r);
  100. assertNotNull("have map", map);
  101. assertTrue("has note for a", map.contains(a));
  102. assertTrue("has note for b", map.contains(b));
  103. assertEquals(data1, map.get(a));
  104. assertEquals(data2, map.get(b));
  105. assertFalse("no note for data1", map.contains(data1));
  106. assertNull("no note for data1", map.get(data1));
  107. }
  108. @Test
  109. public void testReadFanout2_38() throws Exception {
  110. RevBlob a = tr.blob("a");
  111. RevBlob b = tr.blob("b");
  112. RevBlob data1 = tr.blob("data1");
  113. RevBlob data2 = tr.blob("data2");
  114. RevCommit r = tr.commit() //
  115. .add(fanout(2, a.name()), data1) //
  116. .add(fanout(2, b.name()), data2) //
  117. .create();
  118. tr.parseBody(r);
  119. NoteMap map = NoteMap.read(reader, r);
  120. assertNotNull("have map", map);
  121. assertTrue("has note for a", map.contains(a));
  122. assertTrue("has note for b", map.contains(b));
  123. assertEquals(data1, map.get(a));
  124. assertEquals(data2, map.get(b));
  125. assertFalse("no note for data1", map.contains(data1));
  126. assertNull("no note for data1", map.get(data1));
  127. }
  128. @Test
  129. public void testReadFanout2_2_36() throws Exception {
  130. RevBlob a = tr.blob("a");
  131. RevBlob b = tr.blob("b");
  132. RevBlob data1 = tr.blob("data1");
  133. RevBlob data2 = tr.blob("data2");
  134. RevCommit r = tr.commit() //
  135. .add(fanout(4, a.name()), data1) //
  136. .add(fanout(4, b.name()), data2) //
  137. .create();
  138. tr.parseBody(r);
  139. NoteMap map = NoteMap.read(reader, r);
  140. assertNotNull("have map", map);
  141. assertTrue("has note for a", map.contains(a));
  142. assertTrue("has note for b", map.contains(b));
  143. assertEquals(data1, map.get(a));
  144. assertEquals(data2, map.get(b));
  145. assertFalse("no note for data1", map.contains(data1));
  146. assertNull("no note for data1", map.get(data1));
  147. }
  148. @Test
  149. public void testReadFullyFannedOut() throws Exception {
  150. RevBlob a = tr.blob("a");
  151. RevBlob b = tr.blob("b");
  152. RevBlob data1 = tr.blob("data1");
  153. RevBlob data2 = tr.blob("data2");
  154. RevCommit r = tr.commit() //
  155. .add(fanout(38, a.name()), data1) //
  156. .add(fanout(38, b.name()), data2) //
  157. .create();
  158. tr.parseBody(r);
  159. NoteMap map = NoteMap.read(reader, r);
  160. assertNotNull("have map", map);
  161. assertTrue("has note for a", map.contains(a));
  162. assertTrue("has note for b", map.contains(b));
  163. assertEquals(data1, map.get(a));
  164. assertEquals(data2, map.get(b));
  165. assertFalse("no note for data1", map.contains(data1));
  166. assertNull("no note for data1", map.get(data1));
  167. }
  168. @Test
  169. public void testGetCachedBytes() throws Exception {
  170. final String exp = "this is test data";
  171. RevBlob a = tr.blob("a");
  172. RevBlob data = tr.blob(exp);
  173. RevCommit r = tr.commit() //
  174. .add(a.name(), data) //
  175. .create();
  176. tr.parseBody(r);
  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. @Test
  183. public void testWriteUnchangedFlat() throws Exception {
  184. RevBlob a = tr.blob("a");
  185. RevBlob b = tr.blob("b");
  186. RevBlob data1 = tr.blob("data1");
  187. RevBlob data2 = tr.blob("data2");
  188. RevCommit r = tr.commit() //
  189. .add(a.name(), data1) //
  190. .add(b.name(), data2) //
  191. .add(".gitignore", "") //
  192. .add("zoo-animals.txt", "") //
  193. .create();
  194. tr.parseBody(r);
  195. NoteMap map = NoteMap.read(reader, r);
  196. assertTrue("has note for a", map.contains(a));
  197. assertTrue("has note for b", map.contains(b));
  198. RevCommit n = commitNoteMap(map);
  199. assertNotSame("is new commit", r, n);
  200. assertSame("same tree", r.getTree(), n.getTree());
  201. }
  202. @Test
  203. public void testWriteUnchangedFanout2_38() throws Exception {
  204. RevBlob a = tr.blob("a");
  205. RevBlob b = tr.blob("b");
  206. RevBlob data1 = tr.blob("data1");
  207. RevBlob data2 = tr.blob("data2");
  208. RevCommit r = tr.commit() //
  209. .add(fanout(2, a.name()), data1) //
  210. .add(fanout(2, b.name()), data2) //
  211. .add(".gitignore", "") //
  212. .add("zoo-animals.txt", "") //
  213. .create();
  214. tr.parseBody(r);
  215. NoteMap map = NoteMap.read(reader, r);
  216. assertTrue("has note for a", map.contains(a));
  217. assertTrue("has note for b", map.contains(b));
  218. // This is a non-lazy map, so we'll be looking at the leaf buckets.
  219. RevCommit n = commitNoteMap(map);
  220. assertNotSame("is new commit", r, n);
  221. assertSame("same tree", r.getTree(), n.getTree());
  222. // Use a lazy-map for the next round of the same test.
  223. map = NoteMap.read(reader, r);
  224. n = commitNoteMap(map);
  225. assertNotSame("is new commit", r, n);
  226. assertSame("same tree", r.getTree(), n.getTree());
  227. }
  228. @Test
  229. public void testCreateFromEmpty() throws Exception {
  230. RevBlob a = tr.blob("a");
  231. RevBlob b = tr.blob("b");
  232. RevBlob data1 = tr.blob("data1");
  233. RevBlob data2 = tr.blob("data2");
  234. NoteMap map = NoteMap.newEmptyMap();
  235. assertFalse("no a", map.contains(a));
  236. assertFalse("no b", map.contains(b));
  237. map.set(a, data1);
  238. map.set(b, data2);
  239. assertEquals(data1, map.get(a));
  240. assertEquals(data2, map.get(b));
  241. map.remove(a);
  242. map.remove(b);
  243. assertFalse("no a", map.contains(a));
  244. assertFalse("no b", map.contains(b));
  245. map.set(a, "data1", inserter);
  246. assertEquals(data1, map.get(a));
  247. map.set(a, null, inserter);
  248. assertFalse("no a", map.contains(a));
  249. }
  250. @Test
  251. public void testEditFlat() throws Exception {
  252. RevBlob a = tr.blob("a");
  253. RevBlob b = tr.blob("b");
  254. RevBlob data1 = tr.blob("data1");
  255. RevBlob data2 = tr.blob("data2");
  256. RevCommit r = tr.commit() //
  257. .add(a.name(), data1) //
  258. .add(b.name(), data2) //
  259. .add(".gitignore", "") //
  260. .add("zoo-animals.txt", b) //
  261. .create();
  262. tr.parseBody(r);
  263. NoteMap map = NoteMap.read(reader, r);
  264. map.set(a, data2);
  265. map.set(b, null);
  266. map.set(data1, b);
  267. map.set(data2, null);
  268. assertEquals(data2, map.get(a));
  269. assertEquals(b, map.get(data1));
  270. assertFalse("no b", map.contains(b));
  271. assertFalse("no data2", map.contains(data2));
  272. MutableObjectId id = new MutableObjectId();
  273. for (int p = 42; p > 0; p--) {
  274. id.setByte(1, p);
  275. map.set(id, data1);
  276. }
  277. for (int p = 42; p > 0; p--) {
  278. id.setByte(1, p);
  279. assertTrue("contains " + id, map.contains(id));
  280. }
  281. RevCommit n = commitNoteMap(map);
  282. map = NoteMap.read(reader, n);
  283. assertEquals(data2, map.get(a));
  284. assertEquals(b, map.get(data1));
  285. assertFalse("no b", map.contains(b));
  286. assertFalse("no data2", map.contains(data2));
  287. assertEquals(b, TreeWalk
  288. .forPath(reader, "zoo-animals.txt", n.getTree()).getObjectId(0));
  289. }
  290. @Test
  291. public void testEditFanout2_38() throws Exception {
  292. RevBlob a = tr.blob("a");
  293. RevBlob b = tr.blob("b");
  294. RevBlob data1 = tr.blob("data1");
  295. RevBlob data2 = tr.blob("data2");
  296. RevCommit r = tr.commit() //
  297. .add(fanout(2, a.name()), data1) //
  298. .add(fanout(2, b.name()), data2) //
  299. .add(".gitignore", "") //
  300. .add("zoo-animals.txt", b) //
  301. .create();
  302. tr.parseBody(r);
  303. NoteMap map = NoteMap.read(reader, r);
  304. map.set(a, data2);
  305. map.set(b, null);
  306. map.set(data1, b);
  307. map.set(data2, null);
  308. assertEquals(data2, map.get(a));
  309. assertEquals(b, map.get(data1));
  310. assertFalse("no b", map.contains(b));
  311. assertFalse("no data2", map.contains(data2));
  312. RevCommit n = commitNoteMap(map);
  313. map.set(a, null);
  314. map.set(data1, null);
  315. assertFalse("no a", map.contains(a));
  316. assertFalse("no data1", map.contains(data1));
  317. map = NoteMap.read(reader, n);
  318. assertEquals(data2, map.get(a));
  319. assertEquals(b, map.get(data1));
  320. assertFalse("no b", map.contains(b));
  321. assertFalse("no data2", map.contains(data2));
  322. assertEquals(b, TreeWalk
  323. .forPath(reader, "zoo-animals.txt", n.getTree()).getObjectId(0));
  324. }
  325. @Test
  326. public void testLeafSplitsWhenFull() throws Exception {
  327. RevBlob data1 = tr.blob("data1");
  328. MutableObjectId idBuf = new MutableObjectId();
  329. RevCommit r = tr.commit() //
  330. .add(data1.name(), data1) //
  331. .create();
  332. tr.parseBody(r);
  333. NoteMap map = NoteMap.read(reader, r);
  334. for (int i = 0; i < 254; i++) {
  335. idBuf.setByte(Constants.OBJECT_ID_LENGTH - 1, i);
  336. map.set(idBuf, data1);
  337. }
  338. RevCommit n = commitNoteMap(map);
  339. try (TreeWalk tw = new TreeWalk(reader)) {
  340. tw.reset(n.getTree());
  341. while (tw.next()) {
  342. assertFalse("no fan-out subtree", tw.isSubtree());
  343. }
  344. }
  345. for (int i = 254; i < 256; i++) {
  346. idBuf.setByte(Constants.OBJECT_ID_LENGTH - 1, i);
  347. map.set(idBuf, data1);
  348. }
  349. idBuf.setByte(Constants.OBJECT_ID_LENGTH - 2, 1);
  350. map.set(idBuf, data1);
  351. n = commitNoteMap(map);
  352. // The 00 bucket is fully split.
  353. String path = fanout(38, idBuf.name());
  354. try (TreeWalk tw = TreeWalk.forPath(reader, path, n.getTree())) {
  355. assertNotNull("has " + path, tw);
  356. }
  357. // The other bucket is not.
  358. path = fanout(2, data1.name());
  359. try (TreeWalk tw = TreeWalk.forPath(reader, path, n.getTree())) {
  360. assertNotNull("has " + path, tw);
  361. }
  362. }
  363. @Test
  364. public void testRemoveDeletesTreeFanout2_38() throws Exception {
  365. RevBlob a = tr.blob("a");
  366. RevBlob data1 = tr.blob("data1");
  367. RevTree empty = tr.tree();
  368. RevCommit r = tr.commit() //
  369. .add(fanout(2, a.name()), data1) //
  370. .create();
  371. tr.parseBody(r);
  372. NoteMap map = NoteMap.read(reader, r);
  373. map.set(a, null);
  374. RevCommit n = commitNoteMap(map);
  375. assertEquals("empty tree", empty, n.getTree());
  376. }
  377. @Test
  378. public void testIteratorEmptyMap() {
  379. Iterator<Note> it = NoteMap.newEmptyMap().iterator();
  380. assertFalse(it.hasNext());
  381. }
  382. @Test
  383. public void testIteratorFlatTree() throws Exception {
  384. RevBlob a = tr.blob("a");
  385. RevBlob b = tr.blob("b");
  386. RevBlob data1 = tr.blob("data1");
  387. RevBlob data2 = tr.blob("data2");
  388. RevBlob nonNote = tr.blob("non note");
  389. RevCommit r = tr.commit() //
  390. .add(a.name(), data1) //
  391. .add(b.name(), data2) //
  392. .add("nonNote", nonNote) //
  393. .create();
  394. tr.parseBody(r);
  395. Iterator it = NoteMap.read(reader, r).iterator();
  396. assertEquals(2, count(it));
  397. }
  398. @Test
  399. public void testIteratorFanoutTree2_38() throws Exception {
  400. RevBlob a = tr.blob("a");
  401. RevBlob b = tr.blob("b");
  402. RevBlob data1 = tr.blob("data1");
  403. RevBlob data2 = tr.blob("data2");
  404. RevBlob nonNote = tr.blob("non note");
  405. RevCommit r = tr.commit() //
  406. .add(fanout(2, a.name()), data1) //
  407. .add(fanout(2, b.name()), data2) //
  408. .add("nonNote", nonNote) //
  409. .create();
  410. tr.parseBody(r);
  411. Iterator it = NoteMap.read(reader, r).iterator();
  412. assertEquals(2, count(it));
  413. }
  414. @Test
  415. public void testIteratorFanoutTree2_2_36() throws Exception {
  416. RevBlob a = tr.blob("a");
  417. RevBlob b = tr.blob("b");
  418. RevBlob data1 = tr.blob("data1");
  419. RevBlob data2 = tr.blob("data2");
  420. RevBlob nonNote = tr.blob("non note");
  421. RevCommit r = tr.commit() //
  422. .add(fanout(4, a.name()), data1) //
  423. .add(fanout(4, b.name()), data2) //
  424. .add("nonNote", nonNote) //
  425. .create();
  426. tr.parseBody(r);
  427. Iterator it = NoteMap.read(reader, r).iterator();
  428. assertEquals(2, count(it));
  429. }
  430. @Test
  431. public void testIteratorFullyFannedOut() throws Exception {
  432. RevBlob a = tr.blob("a");
  433. RevBlob b = tr.blob("b");
  434. RevBlob data1 = tr.blob("data1");
  435. RevBlob data2 = tr.blob("data2");
  436. RevBlob nonNote = tr.blob("non note");
  437. RevCommit r = tr.commit() //
  438. .add(fanout(38, a.name()), data1) //
  439. .add(fanout(38, b.name()), data2) //
  440. .add("nonNote", nonNote) //
  441. .create();
  442. tr.parseBody(r);
  443. Iterator it = NoteMap.read(reader, r).iterator();
  444. assertEquals(2, count(it));
  445. }
  446. @Test
  447. public void testShorteningNoteRefName() throws Exception {
  448. String expectedShortName = "review";
  449. String noteRefName = Constants.R_NOTES + expectedShortName;
  450. assertEquals(expectedShortName, NoteMap.shortenRefName(noteRefName));
  451. String nonNoteRefName = Constants.R_HEADS + expectedShortName;
  452. assertEquals(nonNoteRefName, NoteMap.shortenRefName(nonNoteRefName));
  453. }
  454. private RevCommit commitNoteMap(NoteMap map) throws IOException {
  455. tr.tick(600);
  456. CommitBuilder builder = new CommitBuilder();
  457. builder.setTreeId(map.writeTree(inserter));
  458. tr.setAuthorAndCommitter(builder);
  459. return tr.getRevWalk().parseCommit(inserter.insert(builder));
  460. }
  461. private static String fanout(int prefix, String name) {
  462. StringBuilder r = new StringBuilder();
  463. int i = 0;
  464. for (; i < prefix && i < name.length(); i += 2) {
  465. if (i != 0)
  466. r.append('/');
  467. r.append(name.charAt(i + 0));
  468. r.append(name.charAt(i + 1));
  469. }
  470. if (i < name.length()) {
  471. if (i != 0)
  472. r.append('/');
  473. r.append(name.substring(i));
  474. }
  475. return r.toString();
  476. }
  477. private static int count(Iterator it) {
  478. int c = 0;
  479. while (it.hasNext()) {
  480. c++;
  481. it.next();
  482. }
  483. return c;
  484. }
  485. }