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 = 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 }