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