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 package org.eclipse.jgit.internal.storage.file;
44
45 import static java.nio.charset.StandardCharsets.UTF_8;
46 import static org.junit.Assert.assertFalse;
47 import static org.junit.Assert.assertNull;
48 import static org.junit.Assert.assertThrows;
49 import static org.junit.Assert.assertTrue;
50 import static org.mockito.ArgumentMatchers.any;
51 import static org.mockito.Mockito.mock;
52 import static org.mockito.Mockito.verify;
53
54 import java.io.File;
55 import java.io.IOException;
56 import java.io.PrintWriter;
57 import java.text.MessageFormat;
58 import java.util.Collection;
59 import java.util.Collections;
60 import java.util.Set;
61 import java.util.concurrent.Callable;
62 import java.util.concurrent.ExecutorService;
63 import java.util.concurrent.Executors;
64 import java.util.concurrent.Future;
65
66 import org.eclipse.jgit.internal.JGitText;
67 import org.eclipse.jgit.junit.RepositoryTestCase;
68 import org.eclipse.jgit.lib.ConfigConstants;
69 import org.eclipse.jgit.lib.Constants;
70 import org.eclipse.jgit.lib.ObjectId;
71 import org.eclipse.jgit.revwalk.RevCommit;
72 import org.eclipse.jgit.storage.file.FileBasedConfig;
73 import org.eclipse.jgit.util.FS;
74 import org.junit.Assume;
75 import org.junit.Test;
76 import org.mockito.Mockito;
77
78 public class ObjectDirectoryTest extends RepositoryTestCase {
79
80 @Test
81 public void testConcurrentInsertionOfBlobsToTheSameNewFanOutDirectory()
82 throws Exception {
83 ExecutorService e = Executors.newCachedThreadPool();
84 for (int i=0; i < 100; ++i) {
85 ObjectDirectory dir = createBareRepository().getObjectDatabase();
86 for (Future f : e.invokeAll(blobInsertersForTheSameFanOutDir(dir))) {
87 f.get();
88 }
89 }
90 }
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107 @Test
108 public void testScanningForPackfiles() throws Exception {
109 ObjectId unknownID = ObjectId
110 .fromString("c0ffee09d0b63d694bf49bc1e6847473f42d4a8c");
111 GC gc = new GC(db);
112 gc.setExpireAgeMillis(0);
113 gc.setPackExpireAgeMillis(0);
114
115
116
117 try (FileRepository receivingDB = new FileRepository(
118 db.getDirectory())) {
119
120
121 FileBasedConfig cfg = receivingDB.getConfig();
122 cfg.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
123 ConfigConstants.CONFIG_KEY_TRUSTFOLDERSTAT, true);
124 cfg.save();
125
126
127
128 ObjectId id = commitFile("file.txt", "test", "master").getId();
129 gc.gc();
130 assertFalse(receivingDB.getObjectDatabase().has(unknownID));
131 assertTrue(receivingDB.getObjectDatabase().hasPackedObject(id));
132
133
134 File packsFolder = receivingDB.getObjectDatabase()
135 .getPackDirectory();
136
137
138
139 File tmpFile = new File(packsFolder, "1.tmp");
140 RevCommit id2 = commitFile("file.txt", "test2", "master");
141
142
143
144 fsTick(null);
145
146
147
148
149
150
151
152
153 assertTrue(tmpFile.createNewFile());
154 assertFalse(receivingDB.getObjectDatabase().has(unknownID));
155
156
157
158 gc.gc();
159
160
161
162
163
164
165
166
167
168
169
170 Thread.sleep(2600);
171
172 File[] ret = packsFolder.listFiles(
173 (File dir, String name) -> name.endsWith(".pack"));
174 assertTrue(ret != null && ret.length == 1);
175 FS fs = db.getFS();
176 Assume.assumeTrue(fs.lastModifiedInstant(tmpFile)
177 .equals(fs.lastModifiedInstant(ret[0])));
178
179
180 assertFalse(receivingDB.getObjectDatabase().has(unknownID));
181 assertTrue(receivingDB.getObjectDatabase().has(id2));
182 }
183 }
184
185 @Test
186 public void testShallowFile()
187 throws Exception {
188 FileRepository repository = createBareRepository();
189 ObjectDirectory dir = repository.getObjectDatabase();
190
191 String commit = "d3148f9410b071edd4a4c85d2a43d1fa2574b0d2";
192 try (PrintWriter writer = new PrintWriter(
193 new File(repository.getDirectory(), Constants.SHALLOW),
194 UTF_8.name())) {
195 writer.println(commit);
196 }
197 Set<ObjectId> shallowCommits = dir.getShallowCommits();
198 assertTrue(shallowCommits.remove(ObjectId.fromString(commit)));
199 assertTrue(shallowCommits.isEmpty());
200 }
201
202 @Test
203 public void testOpenLooseObjectSuppressStaleFileHandleException()
204 throws Exception {
205 ObjectId id = ObjectId
206 .fromString("873fb8d667d05436d728c52b1d7a09528e6eb59b");
207 WindowCursor curs = new WindowCursor(db.getObjectDatabase());
208
209 LooseObjects mock = mock(LooseObjects.class);
210 UnpackedObjectCache unpackedObjectCacheMock = mock(
211 UnpackedObjectCache.class);
212
213 Mockito.when(mock.getObjectLoader(any(), any(), any()))
214 .thenThrow(new IOException("Stale File Handle"));
215 Mockito.when(mock.open(curs, id)).thenCallRealMethod();
216 Mockito.when(mock.unpackedObjectCache())
217 .thenReturn(unpackedObjectCacheMock);
218
219 assertNull(mock.open(curs, id));
220 verify(unpackedObjectCacheMock).remove(id);
221 }
222
223 @Test
224 public void testOpenLooseObjectPropagatesIOExceptions() throws Exception {
225 ObjectId id = ObjectId
226 .fromString("873fb8d667d05436d728c52b1d7a09528e6eb59b");
227 WindowCursor curs = new WindowCursor(db.getObjectDatabase());
228
229 LooseObjects mock = mock(LooseObjects.class);
230
231 Mockito.when(mock.getObjectLoader(any(), any(), any()))
232 .thenThrow(new IOException("some IO failure"));
233 Mockito.when(mock.open(curs, id)).thenCallRealMethod();
234
235 assertThrows(IOException.class, () -> mock.open(curs, id));
236 }
237
238 @Test
239 public void testShallowFileCorrupt() throws Exception {
240 FileRepository repository = createBareRepository();
241 ObjectDirectory dir = repository.getObjectDatabase();
242
243 String commit = "X3148f9410b071edd4a4c85d2a43d1fa2574b0d2";
244 try (PrintWriter writer = new PrintWriter(
245 new File(repository.getDirectory(), Constants.SHALLOW),
246 UTF_8.name())) {
247 writer.println(commit);
248 }
249 assertThrows(
250 MessageFormat.format(JGitText.get().badShallowLine, commit),
251 IOException.class, () -> dir.getShallowCommits());
252 }
253
254 private Collection<Callable<ObjectId>> blobInsertersForTheSameFanOutDir(
255 final ObjectDirectory dir) {
256 Callable<ObjectId> callable = () -> dir.newInserter()
257 .insert(Constants.OBJ_BLOB, new byte[0]);
258 return Collections.nCopies(4, callable);
259 }
260
261 }