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