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
44
45
46 package org.eclipse.jgit.junit;
47
48 import static org.junit.Assert.assertFalse;
49 import static org.junit.Assert.fail;
50
51 import java.io.File;
52 import java.io.IOException;
53 import java.util.ArrayList;
54 import java.util.Collections;
55 import java.util.HashMap;
56 import java.util.HashSet;
57 import java.util.List;
58 import java.util.Map;
59 import java.util.Set;
60 import java.util.TreeSet;
61
62 import org.eclipse.jgit.dircache.DirCache;
63 import org.eclipse.jgit.dircache.DirCacheEntry;
64 import org.eclipse.jgit.internal.storage.file.FileRepository;
65 import org.eclipse.jgit.lib.ConfigConstants;
66 import org.eclipse.jgit.lib.Constants;
67 import org.eclipse.jgit.lib.ObjectId;
68 import org.eclipse.jgit.lib.PersonIdent;
69 import org.eclipse.jgit.lib.Repository;
70 import org.eclipse.jgit.lib.RepositoryCache;
71 import org.eclipse.jgit.storage.file.FileBasedConfig;
72 import org.eclipse.jgit.storage.file.WindowCacheConfig;
73 import org.eclipse.jgit.util.FS;
74 import org.eclipse.jgit.util.FileUtils;
75 import org.eclipse.jgit.util.SystemReader;
76 import org.junit.After;
77 import org.junit.Before;
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96 public abstract class LocalDiskRepositoryTestCase {
97 private static final boolean useMMAP = "true".equals(System
98 .getProperty("jgit.junit.usemmap"));
99
100
101 protected PersonIdent author;
102
103
104 protected PersonIdent committer;
105
106
107
108
109
110 protected MockSystemReader mockSystemReader;
111
112 private final Set<Repository> toClose = new HashSet<>();
113 private File tmp;
114
115 @Before
116 public void setUp() throws Exception {
117 tmp = File.createTempFile("jgit_test_", "_tmp");
118 CleanupThread.deleteOnShutdown(tmp);
119 if (!tmp.delete() || !tmp.mkdir())
120 throw new IOException("Cannot create " + tmp);
121
122 mockSystemReader = new MockSystemReader();
123 mockSystemReader.userGitConfig = new FileBasedConfig(new File(tmp,
124 "usergitconfig"), FS.DETECTED);
125
126
127
128 mockSystemReader.userGitConfig.setBoolean(ConfigConstants.CONFIG_GC_SECTION,
129 null, ConfigConstants.CONFIG_KEY_AUTODETACH, false);
130 mockSystemReader.userGitConfig.save();
131 ceilTestDirectories(getCeilings());
132 SystemReader.setInstance(mockSystemReader);
133
134 author = new PersonIdent("J. Author", "jauthor@example.com");
135 committer = new PersonIdent("J. Committer", "jcommitter@example.com");
136
137 final WindowCacheConfig c = new WindowCacheConfig();
138 c.setPackedGitLimit(128 * WindowCacheConfig.KB);
139 c.setPackedGitWindowSize(8 * WindowCacheConfig.KB);
140 c.setPackedGitMMAP(useMMAP);
141 c.setDeltaBaseCacheLimit(8 * WindowCacheConfig.KB);
142 c.install();
143 }
144
145 protected File getTemporaryDirectory() {
146 return tmp.getAbsoluteFile();
147 }
148
149 protected List<File> getCeilings() {
150 return Collections.singletonList(getTemporaryDirectory());
151 }
152
153 private void ceilTestDirectories(List<File> ceilings) {
154 mockSystemReader.setProperty(Constants.GIT_CEILING_DIRECTORIES_KEY, makePath(ceilings));
155 }
156
157 private static String makePath(List<?> objects) {
158 final StringBuilder stringBuilder = new StringBuilder();
159 for (Object object : objects) {
160 if (stringBuilder.length() > 0)
161 stringBuilder.append(File.pathSeparatorChar);
162 stringBuilder.append(object.toString());
163 }
164 return stringBuilder.toString();
165 }
166
167 @After
168 public void tearDown() throws Exception {
169 RepositoryCache.clear();
170 for (Repository r : toClose)
171 r.close();
172 toClose.clear();
173
174
175
176
177
178 if (useMMAP)
179 System.gc();
180 if (tmp != null)
181 recursiveDelete(tmp, false, true);
182 if (tmp != null && !tmp.exists())
183 CleanupThread.removed(tmp);
184
185 SystemReader.setInstance(null);
186 }
187
188
189 protected void tick() {
190 mockSystemReader.tick(5 * 60);
191 final long now = mockSystemReader.getCurrentTime();
192 final int tz = mockSystemReader.getTimezone(now);
193
194 author = new PersonIdent(author, now, tz);
195 committer = new PersonIdent(committer, now, tz);
196 }
197
198
199
200
201
202
203
204 protected void recursiveDelete(final File dir) {
205 recursiveDelete(dir, false, true);
206 }
207
208 private static boolean recursiveDelete(final File dir,
209 boolean silent, boolean failOnError) {
210 assert !(silent && failOnError);
211 if (!dir.exists())
212 return silent;
213 final File[] ls = dir.listFiles();
214 if (ls != null)
215 for (int k = 0; k < ls.length; k++) {
216 final File e = ls[k];
217 if (e.isDirectory())
218 silent = recursiveDelete(e, silent, failOnError);
219 else if (!e.delete()) {
220 if (!silent)
221 reportDeleteFailure(failOnError, e);
222 silent = !failOnError;
223 }
224 }
225 if (!dir.delete()) {
226 if (!silent)
227 reportDeleteFailure(failOnError, dir);
228 silent = !failOnError;
229 }
230 return silent;
231 }
232
233 private static void reportDeleteFailure(boolean failOnError, File e) {
234 String severity = failOnError ? "ERROR" : "WARNING";
235 String msg = severity + ": Failed to delete " + e;
236 if (failOnError)
237 fail(msg);
238 else
239 System.err.println(msg);
240 }
241
242 public static final int MOD_TIME = 1;
243
244 public static final int SMUDGE = 2;
245
246 public static final int LENGTH = 4;
247
248 public static final int CONTENT_ID = 8;
249
250 public static final int CONTENT = 16;
251
252 public static final int ASSUME_UNCHANGED = 32;
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292 public static String indexState(Repository repo, int includedOptions)
293 throws IllegalStateException, IOException {
294 DirCache dc = repo.readDirCache();
295 StringBuilder sb = new StringBuilder();
296 TreeSet<Long> timeStamps = new TreeSet<>();
297
298
299 if (0 != (includedOptions & MOD_TIME)) {
300 for (int i=0; i<dc.getEntryCount(); ++i)
301 timeStamps.add(Long.valueOf(dc.getEntry(i).getLastModified()));
302 }
303
304
305 for (int i=0; i<dc.getEntryCount(); ++i) {
306 DirCacheEntry entry = dc.getEntry(i);
307 sb.append("["+entry.getPathString()+", mode:" + entry.getFileMode());
308 int stage = entry.getStage();
309 if (stage != 0)
310 sb.append(", stage:" + stage);
311 if (0 != (includedOptions & MOD_TIME)) {
312 sb.append(", time:t"+
313 timeStamps.headSet(Long.valueOf(entry.getLastModified())).size());
314 }
315 if (0 != (includedOptions & SMUDGE))
316 if (entry.isSmudged())
317 sb.append(", smudged");
318 if (0 != (includedOptions & LENGTH))
319 sb.append(", length:"
320 + Integer.toString(entry.getLength()));
321 if (0 != (includedOptions & CONTENT_ID))
322 sb.append(", sha1:" + ObjectId.toString(entry.getObjectId()));
323 if (0 != (includedOptions & CONTENT)) {
324 sb.append(", content:"
325 + new String(repo.open(entry.getObjectId(),
326 Constants.OBJ_BLOB).getCachedBytes(), "UTF-8"));
327 }
328 if (0 != (includedOptions & ASSUME_UNCHANGED))
329 sb.append(", assume-unchanged:"
330 + Boolean.toString(entry.isAssumeValid()));
331 sb.append("]");
332 }
333 return sb.toString();
334 }
335
336
337
338
339
340
341
342
343
344 protected FileRepository createBareRepository() throws IOException {
345 return createRepository(true );
346 }
347
348
349
350
351
352
353
354
355 protected FileRepository createWorkRepository() throws IOException {
356 return createRepository(false );
357 }
358
359
360
361
362
363
364
365
366
367
368
369 private FileRepository createRepository(boolean bare)
370 throws IOException {
371 return createRepository(bare, true );
372 }
373
374
375
376
377
378
379
380
381
382
383
384
385
386 public FileRepository createRepository(boolean bare, boolean autoClose)
387 throws IOException {
388 File gitdir = createUniqueTestGitDir(bare);
389 FileRepository db = new FileRepository(gitdir);
390 assertFalse(gitdir.exists());
391 db.create(bare);
392 if (autoClose) {
393 addRepoToClose(db);
394 }
395 return db;
396 }
397
398
399
400
401
402
403
404
405 public void addRepoToClose(Repository r) {
406 toClose.add(r);
407 }
408
409
410
411
412
413
414
415
416
417 protected File createTempDirectory(String name) throws IOException {
418 File directory = new File(createTempFile(), name);
419 FileUtils.mkdirs(directory);
420 return directory.getCanonicalFile();
421 }
422
423
424
425
426
427
428
429
430
431
432 protected File createUniqueTestGitDir(boolean bare) throws IOException {
433 String gitdirName = createTempFile().getPath();
434 if (!bare)
435 gitdirName += "/";
436 return new File(gitdirName + Constants.DOT_GIT);
437 }
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452 protected File createTempFile() throws IOException {
453 File p = File.createTempFile("tmp_", "", tmp);
454 if (!p.delete()) {
455 throw new IOException("Cannot obtain unique path " + tmp);
456 }
457 return p;
458 }
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476 protected int runHook(final Repository db, final File hook,
477 final String... args) throws IOException, InterruptedException {
478 final String[] argv = new String[1 + args.length];
479 argv[0] = hook.getAbsolutePath();
480 System.arraycopy(args, 0, argv, 1, args.length);
481
482 final Map<String, String> env = cloneEnv();
483 env.put("GIT_DIR", db.getDirectory().getAbsolutePath());
484 putPersonIdent(env, "AUTHOR", author);
485 putPersonIdent(env, "COMMITTER", committer);
486
487 final File cwd = db.getWorkTree();
488 final Process p = Runtime.getRuntime().exec(argv, toEnvArray(env), cwd);
489 p.getOutputStream().close();
490 p.getErrorStream().close();
491 p.getInputStream().close();
492 return p.waitFor();
493 }
494
495 private static void putPersonIdent(final Map<String, String> env,
496 final String type, final PersonIdent who) {
497 final String ident = who.toExternalString();
498 final String date = ident.substring(ident.indexOf("> ") + 2);
499 env.put("GIT_" + type + "_NAME", who.getName());
500 env.put("GIT_" + type + "_EMAIL", who.getEmailAddress());
501 env.put("GIT_" + type + "_DATE", date);
502 }
503
504
505
506
507
508
509
510
511
512
513
514 protected File write(final String body) throws IOException {
515 final File f = File.createTempFile("temp", "txt", tmp);
516 try {
517 write(f, body);
518 return f;
519 } catch (Error e) {
520 f.delete();
521 throw e;
522 } catch (RuntimeException e) {
523 f.delete();
524 throw e;
525 } catch (IOException e) {
526 f.delete();
527 throw e;
528 }
529 }
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545 protected void write(final File f, final String body) throws IOException {
546 JGitTestUtil.write(f, body);
547 }
548
549 protected String read(final File f) throws IOException {
550 return JGitTestUtil.read(f);
551 }
552
553 private static String[] toEnvArray(final Map<String, String> env) {
554 final String[] envp = new String[env.size()];
555 int i = 0;
556 for (Map.Entry<String, String> e : env.entrySet())
557 envp[i++] = e.getKey() + "=" + e.getValue();
558 return envp;
559 }
560
561 private static HashMap<String, String> cloneEnv() {
562 return new HashMap<>(System.getenv());
563 }
564
565 private static final class CleanupThread extends Thread {
566 private static final CleanupThread me;
567 static {
568 me = new CleanupThread();
569 Runtime.getRuntime().addShutdownHook(me);
570 }
571
572 static void deleteOnShutdown(File tmp) {
573 synchronized (me) {
574 me.toDelete.add(tmp);
575 }
576 }
577
578 static void removed(File tmp) {
579 synchronized (me) {
580 me.toDelete.remove(tmp);
581 }
582 }
583
584 private final List<File> toDelete = new ArrayList<>();
585
586 @Override
587 public void run() {
588
589
590
591
592
593 System.gc();
594 synchronized (this) {
595 boolean silent = false;
596 boolean failOnError = false;
597 for (File tmp : toDelete)
598 recursiveDelete(tmp, silent, failOnError);
599 }
600 }
601 }
602 }