Reduce multi-level buffered streams in transport code
[jgit/MarioXXX.git] / org.eclipse.jgit.test / tst / org / eclipse / jgit / lib / ConcurrentRepackTest.java
blob69430ed334a3874748a013e98fe4c73bfce8a04a
1 /*
2 * Copyright (C) 2009-2010, Google Inc.
3 * Copyright (C) 2009, Robin Rosenberg <robin.rosenberg@dewire.com>
4 * and other copyright owners as documented in the project's IP log.
6 * This program and the accompanying materials are made available
7 * under the terms of the Eclipse Distribution License v1.0 which
8 * accompanies this distribution, is reproduced below, and is
9 * available at http://www.eclipse.org/org/documents/edl-v10.php
11 * All rights reserved.
13 * Redistribution and use in source and binary forms, with or
14 * without modification, are permitted provided that the following
15 * conditions are met:
17 * - Redistributions of source code must retain the above copyright
18 * notice, this list of conditions and the following disclaimer.
20 * - Redistributions in binary form must reproduce the above
21 * copyright notice, this list of conditions and the following
22 * disclaimer in the documentation and/or other materials provided
23 * with the distribution.
25 * - Neither the name of the Eclipse Foundation, Inc. nor the
26 * names of its contributors may be used to endorse or promote
27 * products derived from this software without specific prior
28 * written permission.
30 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
31 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
32 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
33 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
34 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
35 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
36 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
37 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
38 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
39 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
40 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
41 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
42 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
45 package org.eclipse.jgit.lib;
47 import java.io.BufferedOutputStream;
48 import java.io.File;
49 import java.io.FileOutputStream;
50 import java.io.IOException;
51 import java.io.OutputStream;
52 import java.util.Arrays;
54 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
55 import org.eclipse.jgit.errors.MissingObjectException;
56 import org.eclipse.jgit.revwalk.RevObject;
57 import org.eclipse.jgit.revwalk.RevWalk;
59 public class ConcurrentRepackTest extends RepositoryTestCase {
60 public void setUp() throws Exception {
61 WindowCacheConfig windowCacheConfig = new WindowCacheConfig();
62 windowCacheConfig.setPackedGitOpenFiles(1);
63 WindowCache.reconfigure(windowCacheConfig);
64 super.setUp();
67 protected void tearDown() throws Exception {
68 super.tearDown();
69 WindowCacheConfig windowCacheConfig = new WindowCacheConfig();
70 WindowCache.reconfigure(windowCacheConfig);
73 public void testObjectInNewPack() throws IncorrectObjectTypeException,
74 IOException {
75 // Create a new object in a new pack, and test that it is present.
77 final Repository eden = createBareRepository();
78 final RevObject o1 = writeBlob(eden, "o1");
79 pack(eden, o1);
80 assertEquals(o1.name(), parse(o1).name());
83 public void testObjectMovedToNewPack1()
84 throws IncorrectObjectTypeException, IOException {
85 // Create an object and pack it. Then remove that pack and put the
86 // object into a different pack file, with some other object. We
87 // still should be able to access the objects.
89 final Repository eden = createBareRepository();
90 final RevObject o1 = writeBlob(eden, "o1");
91 final File[] out1 = pack(eden, o1);
92 assertEquals(o1.name(), parse(o1).name());
94 final RevObject o2 = writeBlob(eden, "o2");
95 pack(eden, o2, o1);
97 // Force close, and then delete, the old pack.
99 whackCache();
100 delete(out1);
102 // Now here is the interesting thing. Will git figure the new
103 // object exists in the new pack, and not the old one.
105 assertEquals(o2.name(), parse(o2).name());
106 assertEquals(o1.name(), parse(o1).name());
109 public void testObjectMovedWithinPack()
110 throws IncorrectObjectTypeException, IOException {
111 // Create an object and pack it.
113 final Repository eden = createBareRepository();
114 final RevObject o1 = writeBlob(eden, "o1");
115 final File[] out1 = pack(eden, o1);
116 assertEquals(o1.name(), parse(o1).name());
118 // Force close the old pack.
120 whackCache();
122 // Now overwrite the old pack in place. This method of creating a
123 // different pack under the same file name is partially broken. We
124 // should also have a different file name because the list of objects
125 // within the pack has been modified.
127 final RevObject o2 = writeBlob(eden, "o2");
128 final PackWriter pw = new PackWriter(eden, NullProgressMonitor.INSTANCE);
129 pw.addObject(o2);
130 pw.addObject(o1);
131 write(out1, pw);
133 // Try the old name, then the new name. The old name should cause the
134 // pack to reload when it opens and the index and pack mismatch.
136 assertEquals(o1.name(), parse(o1).name());
137 assertEquals(o2.name(), parse(o2).name());
140 public void testObjectMovedToNewPack2()
141 throws IncorrectObjectTypeException, IOException {
142 // Create an object and pack it. Then remove that pack and put the
143 // object into a different pack file, with some other object. We
144 // still should be able to access the objects.
146 final Repository eden = createBareRepository();
147 final RevObject o1 = writeBlob(eden, "o1");
148 final File[] out1 = pack(eden, o1);
149 assertEquals(o1.name(), parse(o1).name());
151 final ObjectLoader load1 = db.openBlob(o1);
152 assertNotNull(load1);
154 final RevObject o2 = writeBlob(eden, "o2");
155 pack(eden, o2, o1);
157 // Force close, and then delete, the old pack.
159 whackCache();
160 delete(out1);
162 // Now here is the interesting thing... can the loader we made
163 // earlier still resolve the object, even though its underlying
164 // pack is gone, but the object still exists.
166 final ObjectLoader load2 = db.openBlob(o1);
167 assertNotNull(load2);
168 assertNotSame(load1, load2);
170 final byte[] data2 = load2.getCachedBytes();
171 final byte[] data1 = load1.getCachedBytes();
172 assertNotNull(data2);
173 assertNotNull(data1);
174 assertNotSame(data1, data2); // cache should be per-pack, not per object
175 assertTrue(Arrays.equals(data1, data2));
176 assertEquals(load2.getType(), load1.getType());
179 private static void whackCache() {
180 final WindowCacheConfig config = new WindowCacheConfig();
181 config.setPackedGitOpenFiles(1);
182 WindowCache.reconfigure(config);
185 private RevObject parse(final AnyObjectId id)
186 throws MissingObjectException, IOException {
187 return new RevWalk(db).parseAny(id);
190 private File[] pack(final Repository src, final RevObject... list)
191 throws IOException {
192 final PackWriter pw = new PackWriter(src, NullProgressMonitor.INSTANCE);
193 for (final RevObject o : list) {
194 pw.addObject(o);
197 final ObjectId name = pw.computeName();
198 final File packFile = fullPackFileName(name, ".pack");
199 final File idxFile = fullPackFileName(name, ".idx");
200 final File[] files = new File[] { packFile, idxFile };
201 write(files, pw);
202 return files;
205 private static void write(final File[] files, final PackWriter pw)
206 throws IOException {
207 final long begin = files[0].getParentFile().lastModified();
208 OutputStream out;
210 out = new BufferedOutputStream(new FileOutputStream(files[0]));
211 try {
212 pw.writePack(out);
213 } finally {
214 out.close();
217 out = new BufferedOutputStream(new FileOutputStream(files[1]));
218 try {
219 pw.writeIndex(out);
220 } finally {
221 out.close();
224 touch(begin, files[0].getParentFile());
227 private static void delete(final File[] list) {
228 final long begin = list[0].getParentFile().lastModified();
229 for (final File f : list) {
230 f.delete();
231 assertFalse(f + " was removed", f.exists());
233 touch(begin, list[0].getParentFile());
236 private static void touch(final long begin, final File dir) {
237 while (begin >= dir.lastModified()) {
238 try {
239 Thread.sleep(25);
240 } catch (InterruptedException ie) {
243 dir.setLastModified(System.currentTimeMillis());
247 private File fullPackFileName(final ObjectId name, final String suffix) {
248 final File packdir = new File(db.getObjectsDirectory(), "pack");
249 return new File(packdir, "pack-" + name.name() + suffix);
252 private RevObject writeBlob(final Repository repo, final String data)
253 throws IOException {
254 final RevWalk revWalk = new RevWalk(repo);
255 final byte[] bytes = Constants.encode(data);
256 final ObjectWriter ow = new ObjectWriter(repo);
257 final ObjectId id = ow.writeBlob(bytes);
258 try {
259 parse(id);
260 fail("Object " + id.name() + " should not exist in test repository");
261 } catch (MissingObjectException e) {
262 // Ok
264 return revWalk.lookupBlob(id);