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.internal.storage.reftable;
45
46 import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
47 import static org.eclipse.jgit.lib.Ref.Storage.NEW;
48 import static org.eclipse.jgit.lib.Ref.Storage.PACKED;
49 import static org.junit.Assert.assertEquals;
50 import static org.junit.Assert.assertFalse;
51 import static org.junit.Assert.assertTrue;
52
53 import java.io.ByteArrayOutputStream;
54 import java.io.IOException;
55 import java.util.Arrays;
56
57 import org.eclipse.jgit.internal.storage.io.BlockSource;
58 import org.eclipse.jgit.internal.storage.reftable.ReftableWriter.Stats;
59 import org.eclipse.jgit.lib.ObjectId;
60 import org.eclipse.jgit.lib.ObjectIdRef;
61 import org.eclipse.jgit.lib.Ref;
62 import org.junit.Test;
63
64 public class ReftableCompactorTest {
65 private static final String MASTER = "refs/heads/master";
66 private static final String NEXT = "refs/heads/next";
67
68 @Test
69 public void noTables() throws IOException {
70 ReftableCompactor compactor = new ReftableCompactor();
71 try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
72 compactor.compact(out);
73 }
74 Stats stats = compactor.getStats();
75 assertEquals(0, stats.minUpdateIndex());
76 assertEquals(0, stats.maxUpdateIndex());
77 assertEquals(0, stats.refCount());
78 }
79
80 @Test
81 public void oneTable() throws IOException {
82 byte[] inTab;
83 try (ByteArrayOutputStream inBuf = new ByteArrayOutputStream()) {
84 ReftableWriter writer = new ReftableWriter()
85 .setMinUpdateIndex(0)
86 .setMaxUpdateIndex(0)
87 .begin(inBuf);
88
89 writer.writeRef(ref(MASTER, 1));
90 writer.finish();
91 inTab = inBuf.toByteArray();
92 }
93
94 byte[] outTab;
95 ReftableCompactor compactor = new ReftableCompactor();
96 try (ByteArrayOutputStream outBuf = new ByteArrayOutputStream()) {
97 compactor.tryAddFirst(read(inTab));
98 compactor.compact(outBuf);
99 outTab = outBuf.toByteArray();
100 }
101 Stats stats = compactor.getStats();
102 assertEquals(0, stats.minUpdateIndex());
103 assertEquals(0, stats.maxUpdateIndex());
104 assertEquals(1, stats.refCount());
105
106 ReftableReader rr = read(outTab);
107 try (RefCursor rc = rr.allRefs()) {
108 assertTrue(rc.next());
109 assertEquals(MASTER, rc.getRef().getName());
110 assertEquals(id(1), rc.getRef().getObjectId());
111 assertEquals(0, rc.getRef().getUpdateIndex());
112 }
113 }
114
115 @Test
116 public void twoTablesOneRef() throws IOException {
117 byte[] inTab1;
118 try (ByteArrayOutputStream inBuf = new ByteArrayOutputStream()) {
119 ReftableWriter writer = new ReftableWriter()
120 .setMinUpdateIndex(0)
121 .setMaxUpdateIndex(0)
122 .begin(inBuf);
123
124 writer.writeRef(ref(MASTER, 1));
125 writer.finish();
126 inTab1 = inBuf.toByteArray();
127 }
128
129 byte[] inTab2;
130 try (ByteArrayOutputStream inBuf = new ByteArrayOutputStream()) {
131 ReftableWriter writer = new ReftableWriter()
132 .setMinUpdateIndex(1)
133 .setMaxUpdateIndex(1)
134 .begin(inBuf);
135
136 writer.writeRef(ref(MASTER, 2));
137 writer.finish();
138 inTab2 = inBuf.toByteArray();
139 }
140
141 byte[] outTab;
142 ReftableCompactor compactor = new ReftableCompactor();
143 try (ByteArrayOutputStream outBuf = new ByteArrayOutputStream()) {
144 compactor.addAll(Arrays.asList(read(inTab1), read(inTab2)));
145 compactor.compact(outBuf);
146 outTab = outBuf.toByteArray();
147 }
148 Stats stats = compactor.getStats();
149 assertEquals(0, stats.minUpdateIndex());
150 assertEquals(1, stats.maxUpdateIndex());
151 assertEquals(1, stats.refCount());
152
153 ReftableReader rr = read(outTab);
154 try (RefCursor rc = rr.allRefs()) {
155 assertTrue(rc.next());
156 assertEquals(MASTER, rc.getRef().getName());
157 assertEquals(id(2), rc.getRef().getObjectId());
158 assertEquals(1, rc.getRef().getUpdateIndex());
159 }
160 }
161
162 @Test
163 public void twoTablesTwoRefs() throws IOException {
164 byte[] inTab1;
165 try (ByteArrayOutputStream inBuf = new ByteArrayOutputStream()) {
166 ReftableWriter writer = new ReftableWriter()
167 .setMinUpdateIndex(0)
168 .setMaxUpdateIndex(0)
169 .begin(inBuf);
170
171 writer.writeRef(ref(MASTER, 1));
172 writer.writeRef(ref(NEXT, 2));
173 writer.finish();
174 inTab1 = inBuf.toByteArray();
175 }
176
177 byte[] inTab2;
178 try (ByteArrayOutputStream inBuf = new ByteArrayOutputStream()) {
179 ReftableWriter writer = new ReftableWriter()
180 .setMinUpdateIndex(1)
181 .setMaxUpdateIndex(1)
182 .begin(inBuf);
183
184 writer.writeRef(ref(MASTER, 3));
185 writer.finish();
186 inTab2 = inBuf.toByteArray();
187 }
188
189 byte[] outTab;
190 ReftableCompactor compactor = new ReftableCompactor();
191 try (ByteArrayOutputStream outBuf = new ByteArrayOutputStream()) {
192 compactor.addAll(Arrays.asList(read(inTab1), read(inTab2)));
193 compactor.compact(outBuf);
194 outTab = outBuf.toByteArray();
195 }
196 Stats stats = compactor.getStats();
197 assertEquals(0, stats.minUpdateIndex());
198 assertEquals(1, stats.maxUpdateIndex());
199 assertEquals(2, stats.refCount());
200
201 ReftableReader rr = read(outTab);
202 try (RefCursor rc = rr.allRefs()) {
203 assertTrue(rc.next());
204 assertEquals(MASTER, rc.getRef().getName());
205 assertEquals(id(3), rc.getRef().getObjectId());
206 assertEquals(1, rc.getRef().getUpdateIndex());
207
208 assertTrue(rc.next());
209 assertEquals(NEXT, rc.getRef().getName());
210 assertEquals(id(2), rc.getRef().getObjectId());
211 assertEquals(0, rc.getRef().getUpdateIndex());
212 }
213 }
214
215 @Test
216 public void twoTablesIncludeOneDelete() throws IOException {
217 byte[] inTab1;
218 try (ByteArrayOutputStream inBuf = new ByteArrayOutputStream()) {
219 ReftableWriter writer = new ReftableWriter()
220 .setMinUpdateIndex(0)
221 .setMaxUpdateIndex(0)
222 .begin(inBuf);
223
224 writer.writeRef(ref(MASTER, 1));
225 writer.finish();
226 inTab1 = inBuf.toByteArray();
227 }
228
229 byte[] inTab2;
230 try (ByteArrayOutputStream inBuf = new ByteArrayOutputStream()) {
231 ReftableWriter writer = new ReftableWriter()
232 .setMinUpdateIndex(1)
233 .setMaxUpdateIndex(1)
234 .begin(inBuf);
235
236 writer.writeRef(tombstone(MASTER));
237 writer.finish();
238 inTab2 = inBuf.toByteArray();
239 }
240
241 byte[] outTab;
242 ReftableCompactor compactor = new ReftableCompactor();
243 try (ByteArrayOutputStream outBuf = new ByteArrayOutputStream()) {
244 compactor.setIncludeDeletes(true);
245 compactor.addAll(Arrays.asList(read(inTab1), read(inTab2)));
246 compactor.compact(outBuf);
247 outTab = outBuf.toByteArray();
248 }
249 Stats stats = compactor.getStats();
250 assertEquals(0, stats.minUpdateIndex());
251 assertEquals(1, stats.maxUpdateIndex());
252 assertEquals(1, stats.refCount());
253
254 ReftableReader rr = read(outTab);
255 try (RefCursor rc = rr.allRefs()) {
256 assertFalse(rc.next());
257 }
258 }
259
260 @Test
261 public void twoTablesNotIncludeOneDelete() throws IOException {
262 byte[] inTab1;
263 try (ByteArrayOutputStream inBuf = new ByteArrayOutputStream()) {
264 ReftableWriter writer = new ReftableWriter()
265 .setMinUpdateIndex(0)
266 .setMaxUpdateIndex(0)
267 .begin(inBuf);
268
269 writer.writeRef(ref(MASTER, 1));
270 writer.finish();
271 inTab1 = inBuf.toByteArray();
272 }
273
274 byte[] inTab2;
275 try (ByteArrayOutputStream inBuf = new ByteArrayOutputStream()) {
276 ReftableWriter writer = new ReftableWriter()
277 .setMinUpdateIndex(1)
278 .setMaxUpdateIndex(1)
279 .begin(inBuf);
280
281 writer.writeRef(tombstone(MASTER));
282 writer.finish();
283 inTab2 = inBuf.toByteArray();
284 }
285
286 byte[] outTab;
287 ReftableCompactor compactor = new ReftableCompactor();
288 try (ByteArrayOutputStream outBuf = new ByteArrayOutputStream()) {
289 compactor.setIncludeDeletes(false);
290 compactor.addAll(Arrays.asList(read(inTab1), read(inTab2)));
291 compactor.compact(outBuf);
292 outTab = outBuf.toByteArray();
293 }
294 Stats stats = compactor.getStats();
295 assertEquals(0, stats.minUpdateIndex());
296 assertEquals(1, stats.maxUpdateIndex());
297 assertEquals(0, stats.refCount());
298
299 ReftableReader rr = read(outTab);
300 try (RefCursor rc = rr.allRefs()) {
301 assertFalse(rc.next());
302 }
303 }
304
305 private static Ref ref(String name, int id) {
306 return new ObjectIdRef.PeeledNonTag(PACKED, name, id(id));
307 }
308
309 private static Ref tombstone(String name) {
310 return new ObjectIdRef.Unpeeled(NEW, name, null);
311 }
312
313 private static ObjectId id(int i) {
314 byte[] buf = new byte[OBJECT_ID_LENGTH];
315 buf[0] = (byte) (i & 0xff);
316 buf[1] = (byte) ((i >>> 8) & 0xff);
317 buf[2] = (byte) ((i >>> 16) & 0xff);
318 buf[3] = (byte) (i >>> 24);
319 return ObjectId.fromRaw(buf);
320 }
321
322 private static ReftableReader read(byte[] table) {
323 return new ReftableReader(BlockSource.from(table));
324 }
325 }