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 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  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 java.io.IOException;
  45. import org.eclipse.jgit.junit.TestRepository;
  46. import org.eclipse.jgit.lib.CommitBuilder;
  47. import org.eclipse.jgit.lib.Constants;
  48. import org.eclipse.jgit.lib.MutableObjectId;
  49. import org.eclipse.jgit.lib.ObjectInserter;
  50. import org.eclipse.jgit.lib.ObjectReader;
  51. import org.eclipse.jgit.lib.Repository;
  52. import org.eclipse.jgit.lib.RepositoryTestCase;
  53. import org.eclipse.jgit.revwalk.RevBlob;
  54. import org.eclipse.jgit.revwalk.RevCommit;
  55. import org.eclipse.jgit.revwalk.RevTree;
  56. import org.eclipse.jgit.treewalk.TreeWalk;
  57. import org.eclipse.jgit.util.RawParseUtils;
  58. public class NoteMapTest extends RepositoryTestCase {
  59. private TestRepository<Repository> tr;
  60. private ObjectReader reader;
  61. private ObjectInserter inserter;
  62. @Override
  63. protected void setUp() throws Exception {
  64. super.setUp();
  65. tr = new TestRepository<Repository>(db);
  66. reader = db.newObjectReader();
  67. inserter = db.newObjectInserter();
  68. }
  69. @Override
  70. protected void tearDown() throws Exception {
  71. reader.release();
  72. inserter.release();
  73. super.tearDown();
  74. }
  75. public void testReadFlatTwoNotes() throws Exception {
  76. RevBlob a = tr.blob("a");
  77. RevBlob b = tr.blob("b");
  78. RevBlob data1 = tr.blob("data1");
  79. RevBlob data2 = tr.blob("data2");
  80. RevCommit r = tr.commit() //
  81. .add(a.name(), data1) //
  82. .add(b.name(), data2) //
  83. .create();
  84. tr.parseBody(r);
  85. NoteMap map = NoteMap.read(reader, r);
  86. assertNotNull("have map", map);
  87. assertTrue("has note for a", map.contains(a));
  88. assertTrue("has note for b", map.contains(b));
  89. assertEquals(data1, map.get(a));
  90. assertEquals(data2, map.get(b));
  91. assertFalse("no note for data1", map.contains(data1));
  92. assertNull("no note for data1", map.get(data1));
  93. }
  94. public void testReadFanout2_38() throws Exception {
  95. RevBlob a = tr.blob("a");
  96. RevBlob b = tr.blob("b");
  97. RevBlob data1 = tr.blob("data1");
  98. RevBlob data2 = tr.blob("data2");
  99. RevCommit r = tr.commit() //
  100. .add(fanout(2, a.name()), data1) //
  101. .add(fanout(2, b.name()), data2) //
  102. .create();
  103. tr.parseBody(r);
  104. NoteMap map = NoteMap.read(reader, r);
  105. assertNotNull("have map", map);
  106. assertTrue("has note for a", map.contains(a));
  107. assertTrue("has note for b", map.contains(b));
  108. assertEquals(data1, map.get(a));
  109. assertEquals(data2, map.get(b));
  110. assertFalse("no note for data1", map.contains(data1));
  111. assertNull("no note for data1", map.get(data1));
  112. }
  113. public void testReadFanout2_2_36() throws Exception {
  114. RevBlob a = tr.blob("a");
  115. RevBlob b = tr.blob("b");
  116. RevBlob data1 = tr.blob("data1");
  117. RevBlob data2 = tr.blob("data2");
  118. RevCommit r = tr.commit() //
  119. .add(fanout(4, a.name()), data1) //
  120. .add(fanout(4, b.name()), data2) //
  121. .create();
  122. tr.parseBody(r);
  123. NoteMap map = NoteMap.read(reader, r);
  124. assertNotNull("have map", map);
  125. assertTrue("has note for a", map.contains(a));
  126. assertTrue("has note for b", map.contains(b));
  127. assertEquals(data1, map.get(a));
  128. assertEquals(data2, map.get(b));
  129. assertFalse("no note for data1", map.contains(data1));
  130. assertNull("no note for data1", map.get(data1));
  131. }
  132. public void testReadFullyFannedOut() throws Exception {
  133. RevBlob a = tr.blob("a");
  134. RevBlob b = tr.blob("b");
  135. RevBlob data1 = tr.blob("data1");
  136. RevBlob data2 = tr.blob("data2");
  137. RevCommit r = tr.commit() //
  138. .add(fanout(38, a.name()), data1) //
  139. .add(fanout(38, b.name()), data2) //
  140. .create();
  141. tr.parseBody(r);
  142. NoteMap map = NoteMap.read(reader, r);
  143. assertNotNull("have map", map);
  144. assertTrue("has note for a", map.contains(a));
  145. assertTrue("has note for b", map.contains(b));
  146. assertEquals(data1, map.get(a));
  147. assertEquals(data2, map.get(b));
  148. assertFalse("no note for data1", map.contains(data1));
  149. assertNull("no note for data1", map.get(data1));
  150. }
  151. public void testGetCachedBytes() throws Exception {
  152. final String exp = "this is test data";
  153. RevBlob a = tr.blob("a");
  154. RevBlob data = tr.blob(exp);
  155. RevCommit r = tr.commit() //
  156. .add(a.name(), data) //
  157. .create();
  158. tr.parseBody(r);
  159. NoteMap map = NoteMap.read(reader, r);
  160. byte[] act = map.getCachedBytes(a, exp.length() * 4);
  161. assertNotNull("has data for a", act);
  162. assertEquals(exp, RawParseUtils.decode(act));
  163. }
  164. public void testWriteUnchangedFlat() throws Exception {
  165. RevBlob a = tr.blob("a");
  166. RevBlob b = tr.blob("b");
  167. RevBlob data1 = tr.blob("data1");
  168. RevBlob data2 = tr.blob("data2");
  169. RevCommit r = tr.commit() //
  170. .add(a.name(), data1) //
  171. .add(b.name(), data2) //
  172. .add(".gitignore", "") //
  173. .add("zoo-animals.txt", "") //
  174. .create();
  175. tr.parseBody(r);
  176. NoteMap map = NoteMap.read(reader, r);
  177. assertTrue("has note for a", map.contains(a));
  178. assertTrue("has note for b", map.contains(b));
  179. RevCommit n = commitNoteMap(map);
  180. assertNotSame("is new commit", r, n);
  181. assertSame("same tree", r.getTree(), n.getTree());
  182. }
  183. public void testWriteUnchangedFanout2_38() 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(fanout(2, a.name()), data1) //
  190. .add(fanout(2, 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. // This is a non-lazy map, so we'll be looking at the leaf buckets.
  199. RevCommit n = commitNoteMap(map);
  200. assertNotSame("is new commit", r, n);
  201. assertSame("same tree", r.getTree(), n.getTree());
  202. // Use a lazy-map for the next round of the same test.
  203. map = NoteMap.read(reader, r);
  204. n = commitNoteMap(map);
  205. assertNotSame("is new commit", r, n);
  206. assertSame("same tree", r.getTree(), n.getTree());
  207. }
  208. public void testCreateFromEmpty() 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. NoteMap map = NoteMap.newEmptyMap();
  214. assertFalse("no a", map.contains(a));
  215. assertFalse("no b", map.contains(b));
  216. map.set(a, data1);
  217. map.set(b, data2);
  218. assertEquals(data1, map.get(a));
  219. assertEquals(data2, map.get(b));
  220. map.remove(a);
  221. map.remove(b);
  222. assertFalse("no a", map.contains(a));
  223. assertFalse("no b", map.contains(b));
  224. map.set(a, "data1", inserter);
  225. assertEquals(data1, map.get(a));
  226. map.set(a, null, inserter);
  227. assertFalse("no a", map.contains(a));
  228. }
  229. public void testEditFlat() 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. RevCommit r = tr.commit() //
  235. .add(a.name(), data1) //
  236. .add(b.name(), data2) //
  237. .add(".gitignore", "") //
  238. .add("zoo-animals.txt", b) //
  239. .create();
  240. tr.parseBody(r);
  241. NoteMap map = NoteMap.read(reader, r);
  242. map.set(a, data2);
  243. map.set(b, null);
  244. map.set(data1, b);
  245. map.set(data2, null);
  246. assertEquals(data2, map.get(a));
  247. assertEquals(b, map.get(data1));
  248. assertFalse("no b", map.contains(b));
  249. assertFalse("no data2", map.contains(data2));
  250. MutableObjectId id = new MutableObjectId();
  251. for (int p = 42; p > 0; p--) {
  252. id.setByte(1, p);
  253. map.set(id, data1);
  254. }
  255. for (int p = 42; p > 0; p--) {
  256. id.setByte(1, p);
  257. assertTrue("contains " + id, map.contains(id));
  258. }
  259. RevCommit n = commitNoteMap(map);
  260. map = NoteMap.read(reader, n);
  261. assertEquals(data2, map.get(a));
  262. assertEquals(b, map.get(data1));
  263. assertFalse("no b", map.contains(b));
  264. assertFalse("no data2", map.contains(data2));
  265. assertEquals(b, TreeWalk
  266. .forPath(reader, "zoo-animals.txt", n.getTree()).getObjectId(0));
  267. }
  268. public void testEditFanout2_38() throws Exception {
  269. RevBlob a = tr.blob("a");
  270. RevBlob b = tr.blob("b");
  271. RevBlob data1 = tr.blob("data1");
  272. RevBlob data2 = tr.blob("data2");
  273. RevCommit r = tr.commit() //
  274. .add(fanout(2, a.name()), data1) //
  275. .add(fanout(2, b.name()), data2) //
  276. .add(".gitignore", "") //
  277. .add("zoo-animals.txt", b) //
  278. .create();
  279. tr.parseBody(r);
  280. NoteMap map = NoteMap.read(reader, r);
  281. map.set(a, data2);
  282. map.set(b, null);
  283. map.set(data1, b);
  284. map.set(data2, null);
  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. RevCommit n = commitNoteMap(map);
  290. map.set(a, null);
  291. map.set(data1, null);
  292. assertFalse("no a", map.contains(a));
  293. assertFalse("no data1", map.contains(data1));
  294. map = NoteMap.read(reader, n);
  295. assertEquals(data2, map.get(a));
  296. assertEquals(b, map.get(data1));
  297. assertFalse("no b", map.contains(b));
  298. assertFalse("no data2", map.contains(data2));
  299. assertEquals(b, TreeWalk
  300. .forPath(reader, "zoo-animals.txt", n.getTree()).getObjectId(0));
  301. }
  302. public void testLeafSplitsWhenFull() throws Exception {
  303. RevBlob data1 = tr.blob("data1");
  304. MutableObjectId idBuf = new MutableObjectId();
  305. RevCommit r = tr.commit() //
  306. .add(data1.name(), data1) //
  307. .create();
  308. tr.parseBody(r);
  309. NoteMap map = NoteMap.read(reader, r);
  310. for (int i = 0; i < 254; i++) {
  311. idBuf.setByte(Constants.OBJECT_ID_LENGTH - 1, i);
  312. map.set(idBuf, data1);
  313. }
  314. RevCommit n = commitNoteMap(map);
  315. TreeWalk tw = new TreeWalk(reader);
  316. tw.reset(n.getTree());
  317. while (tw.next())
  318. assertFalse("no fan-out subtree", tw.isSubtree());
  319. for (int i = 254; i < 256; i++) {
  320. idBuf.setByte(Constants.OBJECT_ID_LENGTH - 1, i);
  321. map.set(idBuf, data1);
  322. }
  323. idBuf.setByte(Constants.OBJECT_ID_LENGTH - 2, 1);
  324. map.set(idBuf, data1);
  325. n = commitNoteMap(map);
  326. // The 00 bucket is fully split.
  327. String path = fanout(38, idBuf.name());
  328. tw = TreeWalk.forPath(reader, path, n.getTree());
  329. assertNotNull("has " + path, tw);
  330. // The other bucket is not.
  331. path = fanout(2, data1.name());
  332. tw = TreeWalk.forPath(reader, path, n.getTree());
  333. assertNotNull("has " + path, tw);
  334. }
  335. public void testRemoveDeletesTreeFanout2_38() throws Exception {
  336. RevBlob a = tr.blob("a");
  337. RevBlob data1 = tr.blob("data1");
  338. RevTree empty = tr.tree();
  339. RevCommit r = tr.commit() //
  340. .add(fanout(2, a.name()), data1) //
  341. .create();
  342. tr.parseBody(r);
  343. NoteMap map = NoteMap.read(reader, r);
  344. map.set(a, null);
  345. RevCommit n = commitNoteMap(map);
  346. assertEquals("empty tree", empty, n.getTree());
  347. }
  348. private RevCommit commitNoteMap(NoteMap map) throws IOException {
  349. tr.tick(600);
  350. CommitBuilder builder = new CommitBuilder();
  351. builder.setTreeId(map.writeTree(inserter));
  352. tr.setAuthorAndCommitter(builder);
  353. return tr.getRevWalk().parseCommit(inserter.insert(builder));
  354. }
  355. private static String fanout(int prefix, String name) {
  356. StringBuilder r = new StringBuilder();
  357. int i = 0;
  358. for (; i < prefix && i < name.length(); i += 2) {
  359. if (i != 0)
  360. r.append('/');
  361. r.append(name.charAt(i + 0));
  362. r.append(name.charAt(i + 1));
  363. }
  364. if (i < name.length()) {
  365. if (i != 0)
  366. r.append('/');
  367. r.append(name.substring(i));
  368. }
  369. return r.toString();
  370. }
  371. }