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
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
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
;
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
>();
62 for (int i
= 0; i
< 10; i
++) {
63 lastFile
= new File(db
.getWorkTree(), "0." + i
);
64 lastFile
.createNewFile();
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();
80 FileTreeIteratorWithTimeControl fileIt
= new FileTreeIteratorWithTimeControl(
82 NameConflictTreeWalk tw
= new NameConflictTreeWalk(db
);
85 tw
.setRecursive(true);
88 for (int i
= 0; i
< 10; i
++) {
89 assertTrue(tw
.next());
90 t
= tw
.getTree(0, FileTreeIterator
.class);
92 t0
= t
.getEntryLastModified();
94 assertEquals(t0
, t
.getEntryLastModified());
97 for (int i
= 0; i
< 10; i
++) {
98 assertTrue(tw
.next());
99 t
= tw
.getTree(0, FileTreeIterator
.class);
101 t1
= t
.getEntryLastModified();
104 assertEquals(t1
, t
.getEntryLastModified());
107 for (int i
= 0; i
< 10; i
++) {
108 assertTrue(tw
.next());
109 t
= tw
.getTree(0, FileTreeIterator
.class);
111 t2
= t
.getEntryLastModified();
114 assertEquals(t2
, t
.getEntryLastModified());
118 public void testRacyGitDetection() throws IOException
,
119 IllegalStateException
, InterruptedException
{
120 TreeSet
<Long
> modTimes
= new TreeSet
<Long
>();
123 // wait to ensure that modtimes of the file doesn't match last index
125 modTimes
.add(fsTick(db
.getIndexFile()));
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
);
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()));
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
153 addToIndex(modTimes
);
156 // although racily clean a should not be reported as being dirty
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(
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());
181 private File
addToWorkDir(String path
, String content
) throws IOException
{
182 File f
= new File(db
.getWorkTree(), path
);
183 FileOutputStream fos
= new FileOutputStream(f
);
185 fos
.write(content
.getBytes(Constants
.CHARACTER_ENCODING
));