add fsTick() to RepositoryTestCase
[jgit.git] / org.eclipse.jgit.test / tst / org / eclipse / jgit / lib / RacyGitTests.java
blobe208b27e6bcb2ff53a06194857b6bfc1d81300f1
1 /*
2 * Copyright (C) 2010, Christian Halstrick <christian.halstrick@sap.com>
3 * and other copyright owners as documented in the project's IP log.
5 * This program and the accompanying materials are made available
6 * under the terms of the Eclipse Distribution License v1.0 which
7 * accompanies this distribution, is reproduced below, and is
8 * available at http://www.eclipse.org/org/documents/edl-v10.php
10 * All rights reserved.
12 * Redistribution and use in source and binary forms, with or
13 * without modification, are permitted provided that the following
14 * conditions are met:
16 * - Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
19 * - Redistributions in binary form must reproduce the above
20 * copyright notice, this list of conditions and the following
21 * disclaimer in the documentation and/or other materials provided
22 * with the distribution.
24 * - Neither the name of the Eclipse Foundation, Inc. nor the
25 * names of its contributors may be used to endorse or promote
26 * products derived from this software without specific prior
27 * written permission.
29 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43 package org.eclipse.jgit.lib;
45 import java.io.File;
46 import java.io.FileNotFoundException;
47 import java.io.FileOutputStream;
48 import java.io.IOException;
49 import java.util.TreeSet;
51 import org.eclipse.jgit.dircache.DirCacheBuilder;
52 import org.eclipse.jgit.dircache.DirCacheEntry;
53 import org.eclipse.jgit.treewalk.FileTreeIterator;
54 import org.eclipse.jgit.treewalk.FileTreeIteratorWithTimeControl;
55 import org.eclipse.jgit.treewalk.NameConflictTreeWalk;
57 public class RacyGitTests extends RepositoryTestCase {
58 public void testIterator() throws IllegalStateException, IOException,
59 InterruptedException {
60 TreeSet<Long> modTimes = new TreeSet<Long>();
61 File lastFile = null;
62 for (int i = 0; i < 10; i++) {
63 lastFile = new File(db.getWorkTree(), "0." + i);
64 lastFile.createNewFile();
65 if (i == 5)
66 fsTick(lastFile);
68 modTimes.add(fsTick(lastFile));
69 for (int i = 0; i < 10; i++) {
70 lastFile = new File(db.getWorkTree(), "1." + i);
71 lastFile.createNewFile();
73 modTimes.add(fsTick(lastFile));
74 for (int i = 0; i < 10; i++) {
75 lastFile = new File(db.getWorkTree(), "2." + i);
76 lastFile.createNewFile();
77 if (i % 4 == 0)
78 fsTick(lastFile);
80 FileTreeIteratorWithTimeControl fileIt = new FileTreeIteratorWithTimeControl(
81 db, modTimes);
82 NameConflictTreeWalk tw = new NameConflictTreeWalk(db);
83 tw.reset();
84 tw.addTree(fileIt);
85 tw.setRecursive(true);
86 FileTreeIterator t;
87 long t0 = 0;
88 for (int i = 0; i < 10; i++) {
89 assertTrue(tw.next());
90 t = tw.getTree(0, FileTreeIterator.class);
91 if (i == 0)
92 t0 = t.getEntryLastModified();
93 else
94 assertEquals(t0, t.getEntryLastModified());
96 long t1 = 0;
97 for (int i = 0; i < 10; i++) {
98 assertTrue(tw.next());
99 t = tw.getTree(0, FileTreeIterator.class);
100 if (i == 0) {
101 t1 = t.getEntryLastModified();
102 assertTrue(t1 > t0);
103 } else
104 assertEquals(t1, t.getEntryLastModified());
106 long t2 = 0;
107 for (int i = 0; i < 10; i++) {
108 assertTrue(tw.next());
109 t = tw.getTree(0, FileTreeIterator.class);
110 if (i == 0) {
111 t2 = t.getEntryLastModified();
112 assertTrue(t2 > t1);
113 } else
114 assertEquals(t2, t.getEntryLastModified());
118 public void testRacyGitDetection() throws IOException,
119 IllegalStateException, InterruptedException {
120 TreeSet<Long> modTimes = new TreeSet<Long>();
121 File lastFile;
123 // wait to ensure that modtimes of the file doesn't match last index
124 // file modtime
125 modTimes.add(fsTick(db.getIndexFile()));
127 // create two files
128 addToWorkDir("a", "a");
129 lastFile = addToWorkDir("b", "b");
131 // wait to ensure that file-modTimes and therefore index entry modTime
132 // doesn't match the modtime of index-file after next persistance
133 modTimes.add(fsTick(lastFile));
135 // now add both files to the index. No racy git expected
136 addToIndex(modTimes);
138 assertEquals(
139 "[a, mode:100644, time:t0, length:1, sha1:2e65efe2a145dda7ee51d1741299f848e5bf752e]" +
140 "[b, mode:100644, time:t0, length:1, sha1:63d8dbd40c23542e740659a7168a0ce3138ea748]",
141 indexState(SMUDGE | MOD_TIME | LENGTH | CONTENT_ID));
143 // Remember the last modTime of index file. All modifications times of
144 // further modification are translated to this value so it looks that
145 // files have been modified in the same time slot as the index file
146 modTimes.add(Long.valueOf(db.getIndexFile().lastModified()));
148 // modify one file
149 addToWorkDir("a", "a2");
150 // now update the index the index. 'a' has to be racily clean -- because
151 // it's modification time is exactly the same as the previous index file
152 // mod time.
153 addToIndex(modTimes);
155 db.readDirCache();
156 // although racily clean a should not be reported as being dirty
157 assertEquals(
158 "[a, mode:100644, time:t1, smudged, length:0]" +
159 "[b, mode:100644, time:t0, length:1]",
160 indexState(SMUDGE|MOD_TIME|LENGTH));
163 private void addToIndex(TreeSet<Long> modTimes)
164 throws FileNotFoundException, IOException {
165 DirCacheBuilder builder = db.lockDirCache().builder();
166 FileTreeIterator fIt = new FileTreeIteratorWithTimeControl(
167 db, modTimes);
168 DirCacheEntry dce;
169 while (!fIt.eof()) {
170 dce = new DirCacheEntry(fIt.getEntryPathString());
171 dce.setFileMode(fIt.getEntryFileMode());
172 dce.setLastModified(fIt.getEntryLastModified());
173 dce.setLength((int) fIt.getEntryLength());
174 dce.setObjectId(fIt.getEntryObjectId());
175 builder.add(dce);
176 fIt.next(1);
178 builder.commit();
181 private File addToWorkDir(String path, String content) throws IOException {
182 File f = new File(db.getWorkTree(), path);
183 FileOutputStream fos = new FileOutputStream(f);
184 try {
185 fos.write(content.getBytes(Constants.CHARACTER_ENCODING));
186 return f;
187 } finally {
188 fos.close();