View Javadoc
1   /*
2    * Copyright (C) 2009, 2020 Google Inc. and others
3    *
4    * This program and the accompanying materials are made available under the
5    * terms of the Eclipse Distribution License v. 1.0 which is available at
6    * https://www.eclipse.org/org/documents/edl-v10.php.
7    *
8    * SPDX-License-Identifier: BSD-3-Clause
9    */
10  
11  package org.eclipse.jgit.dircache;
12  
13  import static java.time.Instant.EPOCH;
14  import static org.junit.Assert.assertArrayEquals;
15  import static org.junit.Assert.assertEquals;
16  import static org.junit.Assert.assertFalse;
17  import static org.junit.Assert.assertSame;
18  import static org.junit.Assert.assertTrue;
19  import static org.junit.Assert.fail;
20  
21  import java.io.ByteArrayInputStream;
22  import java.io.ByteArrayOutputStream;
23  import java.io.IOException;
24  import java.security.MessageDigest;
25  import java.time.Instant;
26  import java.util.concurrent.TimeUnit;
27  
28  import org.eclipse.jgit.dircache.DirCache.DirCacheVersion;
29  import org.eclipse.jgit.lib.Constants;
30  import org.eclipse.jgit.lib.FileMode;
31  import org.eclipse.jgit.lib.ObjectId;
32  import org.eclipse.jgit.util.MutableInteger;
33  import org.junit.Test;
34  
35  public class DirCacheEntryTest {
36  	@Test
37  	public void testIsValidPath() {
38  		assertTrue(isValidPath("a"));
39  		assertTrue(isValidPath("a/b"));
40  		assertTrue(isValidPath("ab/cd/ef"));
41  
42  		assertFalse(isValidPath(""));
43  		assertFalse(isValidPath("/a"));
44  		assertFalse(isValidPath("a//b"));
45  		assertFalse(isValidPath("ab/cd//ef"));
46  		assertFalse(isValidPath("a/"));
47  		assertFalse(isValidPath("ab/cd/ef/"));
48  		assertFalse(isValidPath("a\u0000b"));
49  	}
50  
51  	@SuppressWarnings("unused")
52  	private static boolean isValidPath(String path) {
53  		try {
54  			new DirCacheEntry(path);
55  			return true;
56  		} catch (InvalidPathException e) {
57  			return false;
58  		}
59  	}
60  
61  	private static void checkPath(DirCacheVersion indexVersion,
62  			DirCacheEntry previous, String name) throws IOException {
63  		DirCacheEntry dce = new DirCacheEntry(name);
64  		long now = System.currentTimeMillis();
65  		long anHourAgo = now - TimeUnit.HOURS.toMillis(1);
66  		dce.setLastModified(Instant.ofEpochMilli(anHourAgo));
67  		ByteArrayOutputStream out = new ByteArrayOutputStream();
68  		dce.write(out, indexVersion, previous);
69  		byte[] raw = out.toByteArray();
70  		MessageDigest md0 = Constants.newMessageDigest();
71  		md0.update(raw);
72  		ByteArrayInputStream in = new ByteArrayInputStream(raw);
73  		MutableInteger infoAt = new MutableInteger();
74  		byte[] sharedInfo = new byte[raw.length];
75  		MessageDigest md = Constants.newMessageDigest();
76  		DirCacheEntry read = new DirCacheEntry(sharedInfo, infoAt, in, md,
77  				Instant.ofEpochMilli(now), indexVersion, previous);
78  		assertEquals("Paths of length " + name.length() + " should match", name,
79  				read.getPathString());
80  		assertEquals("Should have been fully read", -1, in.read());
81  		assertArrayEquals("Digests should match", md0.digest(),
82  				md.digest());
83  	}
84  
85  	@Test
86  	public void testLongPath() throws Exception {
87  		StringBuilder name = new StringBuilder(4094 + 16);
88  		for (int i = 0; i < 4094; i++) {
89  			name.append('a');
90  		}
91  		for (int j = 0; j < 16; j++) {
92  			checkPath(DirCacheVersion.DIRC_VERSION_EXTENDED, null,
93  					name.toString());
94  			name.append('b');
95  		}
96  	}
97  
98  	@Test
99  	public void testLongPathV4() throws Exception {
100 		StringBuilder name = new StringBuilder(4094 + 16);
101 		for (int i = 0; i < 4094; i++) {
102 			name.append('a');
103 		}
104 		DirCacheEntry previous = new DirCacheEntry(name.toString());
105 		for (int j = 0; j < 16; j++) {
106 			checkPath(DirCacheVersion.DIRC_VERSION_PATHCOMPRESS, previous,
107 					name.toString());
108 			name.append('b');
109 		}
110 	}
111 
112 	@Test
113 	public void testShortPath() throws Exception {
114 		StringBuilder name = new StringBuilder(1 + 16);
115 		name.append('a');
116 		for (int j = 0; j < 16; j++) {
117 			checkPath(DirCacheVersion.DIRC_VERSION_EXTENDED, null,
118 					name.toString());
119 			name.append('b');
120 		}
121 	}
122 
123 	@Test
124 	public void testShortPathV4() throws Exception {
125 		StringBuilder name = new StringBuilder(1 + 16);
126 		name.append('a');
127 		DirCacheEntry previous = new DirCacheEntry(name.toString());
128 		for (int j = 0; j < 16; j++) {
129 			checkPath(DirCacheVersion.DIRC_VERSION_PATHCOMPRESS, previous,
130 					name.toString());
131 			name.append('b');
132 		}
133 	}
134 
135 	@Test
136 	public void testPathV4() throws Exception {
137 		StringBuilder name = new StringBuilder();
138 		for (int i = 0; i < 20; i++) {
139 			name.append('a');
140 		}
141 		DirCacheEntry previous = new DirCacheEntry(name.toString());
142 		for (int j = 0; j < 20; j++) {
143 			name.setLength(name.length() - 1);
144 			String newName = name.toString() + "bbb";
145 			checkPath(DirCacheVersion.DIRC_VERSION_PATHCOMPRESS, previous,
146 					newName);
147 		}
148 	}
149 
150 	@SuppressWarnings("unused")
151 	@Test
152 	public void testCreate_ByStringPath() {
153 		assertEquals("a", new DirCacheEntry("a").getPathString());
154 		assertEquals("a/b", new DirCacheEntry("a/b").getPathString());
155 
156 		try {
157 			new DirCacheEntry("/a");
158 			fail("Incorrectly created DirCacheEntry");
159 		} catch (IllegalArgumentException err) {
160 			assertEquals("Invalid path: /a", err.getMessage());
161 		}
162 	}
163 
164 	@SuppressWarnings("unused")
165 	@Test
166 	public void testCreate_ByStringPathAndStage() {
167 		DirCacheEntry e;
168 
169 		e = new DirCacheEntry("a", 0);
170 		assertEquals("a", e.getPathString());
171 		assertEquals(0, e.getStage());
172 
173 		e = new DirCacheEntry("a/b", 1);
174 		assertEquals("a/b", e.getPathString());
175 		assertEquals(1, e.getStage());
176 
177 		e = new DirCacheEntry("a/c", 2);
178 		assertEquals("a/c", e.getPathString());
179 		assertEquals(2, e.getStage());
180 
181 		e = new DirCacheEntry("a/d", 3);
182 		assertEquals("a/d", e.getPathString());
183 		assertEquals(3, e.getStage());
184 
185 		try {
186 			new DirCacheEntry("/a", 1);
187 			fail("Incorrectly created DirCacheEntry");
188 		} catch (IllegalArgumentException err) {
189 			assertEquals("Invalid path: /a", err.getMessage());
190 		}
191 
192 		try {
193 			new DirCacheEntry("a", -11);
194 			fail("Incorrectly created DirCacheEntry");
195 		} catch (IllegalArgumentException err) {
196 			assertEquals("Invalid stage -11 for path a", err.getMessage());
197 		}
198 
199 		try {
200 			new DirCacheEntry("a", 4);
201 			fail("Incorrectly created DirCacheEntry");
202 		} catch (IllegalArgumentException err) {
203 			assertEquals("Invalid stage 4 for path a", err.getMessage());
204 		}
205 	}
206 
207 	@Test
208 	public void testSetFileMode() {
209 		final DirCacheEntry e = new DirCacheEntry("a");
210 
211 		assertEquals(0, e.getRawMode());
212 
213 		e.setFileMode(FileMode.REGULAR_FILE);
214 		assertSame(FileMode.REGULAR_FILE, e.getFileMode());
215 		assertEquals(FileMode.REGULAR_FILE.getBits(), e.getRawMode());
216 
217 		e.setFileMode(FileMode.EXECUTABLE_FILE);
218 		assertSame(FileMode.EXECUTABLE_FILE, e.getFileMode());
219 		assertEquals(FileMode.EXECUTABLE_FILE.getBits(), e.getRawMode());
220 
221 		e.setFileMode(FileMode.SYMLINK);
222 		assertSame(FileMode.SYMLINK, e.getFileMode());
223 		assertEquals(FileMode.SYMLINK.getBits(), e.getRawMode());
224 
225 		e.setFileMode(FileMode.GITLINK);
226 		assertSame(FileMode.GITLINK, e.getFileMode());
227 		assertEquals(FileMode.GITLINK.getBits(), e.getRawMode());
228 
229 		try {
230 			e.setFileMode(FileMode.MISSING);
231 			fail("incorrectly accepted FileMode.MISSING");
232 		} catch (IllegalArgumentException err) {
233 			assertEquals("Invalid mode 0 for path a", err.getMessage());
234 		}
235 
236 		try {
237 			e.setFileMode(FileMode.TREE);
238 			fail("incorrectly accepted FileMode.TREE");
239 		} catch (IllegalArgumentException err) {
240 			assertEquals("Invalid mode 40000 for path a", err.getMessage());
241 		}
242 	}
243 
244 	@Test
245 	public void testSetStage() {
246 		DirCacheEntry e = new DirCacheEntry("some/path", DirCacheEntry.STAGE_1);
247 		e.setAssumeValid(true);
248 		e.setCreationTime(2L);
249 		e.setFileMode(FileMode.EXECUTABLE_FILE);
250 		e.setLastModified(EPOCH.plusMillis(3L));
251 		e.setLength(100L);
252 		e.setObjectId(ObjectId
253 				.fromString("0123456789012345678901234567890123456789"));
254 		e.setUpdateNeeded(true);
255 		e.setStage(DirCacheEntry.STAGE_2);
256 
257 		assertTrue(e.isAssumeValid());
258 		assertEquals(2L, e.getCreationTime());
259 		assertEquals(
260 				ObjectId.fromString("0123456789012345678901234567890123456789"),
261 				e.getObjectId());
262 		assertEquals(FileMode.EXECUTABLE_FILE, e.getFileMode());
263 		assertEquals(EPOCH.plusMillis(3L), e.getLastModifiedInstant());
264 		assertEquals(100L, e.getLength());
265 		assertEquals(DirCacheEntry.STAGE_2, e.getStage());
266 		assertTrue(e.isUpdateNeeded());
267 		assertEquals("some/path", e.getPathString());
268 
269 		e.setStage(DirCacheEntry.STAGE_0);
270 
271 		assertTrue(e.isAssumeValid());
272 		assertEquals(2L, e.getCreationTime());
273 		assertEquals(
274 				ObjectId.fromString("0123456789012345678901234567890123456789"),
275 				e.getObjectId());
276 		assertEquals(FileMode.EXECUTABLE_FILE, e.getFileMode());
277 		assertEquals(EPOCH.plusMillis(3L), e.getLastModifiedInstant());
278 		assertEquals(100L, e.getLength());
279 		assertEquals(DirCacheEntry.STAGE_0, e.getStage());
280 		assertTrue(e.isUpdateNeeded());
281 		assertEquals("some/path", e.getPathString());
282 	}
283 
284 	@Test
285 	public void testCopyMetaDataWithStage() {
286 		copyMetaDataHelper(false);
287 	}
288 
289 	@Test
290 	public void testCopyMetaDataWithoutStage() {
291 		copyMetaDataHelper(true);
292 	}
293 
294 	private static void copyMetaDataHelper(boolean keepStage) {
295 		DirCacheEntry e = new DirCacheEntry("some/path", DirCacheEntry.STAGE_2);
296 		e.setAssumeValid(false);
297 		e.setCreationTime(2L);
298 		e.setFileMode(FileMode.EXECUTABLE_FILE);
299 		e.setLastModified(EPOCH.plusMillis(3L));
300 		e.setLength(100L);
301 		e.setObjectId(ObjectId
302 				.fromString("0123456789012345678901234567890123456789"));
303 		e.setUpdateNeeded(true);
304 
305 		DirCacheEntry f = new DirCacheEntry("someother/path",
306 				DirCacheEntry.STAGE_1);
307 		f.setAssumeValid(true);
308 		f.setCreationTime(10L);
309 		f.setFileMode(FileMode.SYMLINK);
310 		f.setLastModified(EPOCH.plusMillis(20L));
311 		f.setLength(100000000L);
312 		f.setObjectId(ObjectId
313 				.fromString("1234567890123456789012345678901234567890"));
314 		f.setUpdateNeeded(true);
315 
316 		e.copyMetaData(f, keepStage);
317 		assertTrue(e.isAssumeValid());
318 		assertEquals(10L, e.getCreationTime());
319 		assertEquals(
320 				ObjectId.fromString("1234567890123456789012345678901234567890"),
321 				e.getObjectId());
322 		assertEquals(FileMode.SYMLINK, e.getFileMode());
323 		assertEquals(EPOCH.plusMillis(20L), e.getLastModifiedInstant());
324 		assertEquals(100000000L, e.getLength());
325 		if (keepStage)
326 			assertEquals(DirCacheEntry.STAGE_2, e.getStage());
327 		else
328 			assertEquals(DirCacheEntry.STAGE_1, e.getStage());
329 		assertTrue(e.isUpdateNeeded());
330 		assertEquals("some/path", e.getPathString());
331 	}
332 }