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