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