View Javadoc
1   /*
2    * Copyright (C) 2017, 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.internal.storage.dfs;
12  
13  import static org.eclipse.jgit.junit.JGitTestUtil.concat;
14  import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
15  import static org.eclipse.jgit.lib.Constants.encodeASCII;
16  import static org.junit.Assert.assertEquals;
17  import static org.junit.Assert.assertNull;
18  import static org.junit.Assert.assertTrue;
19  import static org.junit.Assert.fail;
20  
21  import java.io.IOException;
22  
23  import org.eclipse.jgit.internal.fsck.FsckError;
24  import org.eclipse.jgit.internal.fsck.FsckError.CorruptObject;
25  import org.eclipse.jgit.junit.TestRepository;
26  import org.eclipse.jgit.lib.Constants;
27  import org.eclipse.jgit.lib.ObjectChecker.ErrorType;
28  import org.eclipse.jgit.lib.ObjectId;
29  import org.eclipse.jgit.lib.ObjectInserter;
30  import org.eclipse.jgit.revwalk.RevCommit;
31  import org.junit.Before;
32  import org.junit.Test;
33  
34  public class DfsFsckTest {
35  	private TestRepository<InMemoryRepository> git;
36  
37  	private InMemoryRepository repo;
38  
39  	private ObjectInserter ins;
40  
41  	@Before
42  	public void setUp() throws IOException {
43  		DfsRepositoryDescription desc = new DfsRepositoryDescription("test");
44  		git = new TestRepository<>(new InMemoryRepository(desc));
45  		repo = git.getRepository();
46  		ins = repo.newObjectInserter();
47  	}
48  
49  	@Test
50  	public void testHealthyRepo() throws Exception {
51  		RevCommit commit0 = git.commit().message("0").create();
52  		RevCommit commit1 = git.commit().message("1").parent(commit0).create();
53  		git.update("master", commit1);
54  
55  		DfsFsck fsck = new DfsFsck(repo);
56  		FsckError errors = fsck.check(null);
57  
58  		assertEquals(errors.getCorruptObjects().size(), 0);
59  		assertEquals(errors.getMissingObjects().size(), 0);
60  		assertEquals(errors.getCorruptIndices().size(), 0);
61  	}
62  
63  	@Test
64  	public void testCommitWithCorruptAuthor() throws Exception {
65  		StringBuilder b = new StringBuilder();
66  		b.append("tree be9bfa841874ccc9f2ef7c48d0c76226f89b7189\n");
67  		b.append("author b <b@c> <b@c> 0 +0000\n");
68  		b.append("committer <> 0 +0000\n");
69  		byte[] data = encodeASCII(b.toString());
70  		ObjectId id = ins.insert(Constants.OBJ_COMMIT, data);
71  		ins.flush();
72  
73  		DfsFsck fsck = new DfsFsck(repo);
74  		FsckError errors = fsck.check(null);
75  
76  		assertEquals(errors.getCorruptObjects().size(), 1);
77  		CorruptObject o = errors.getCorruptObjects().iterator().next();
78  		assertTrue(o.getId().equals(id));
79  		assertEquals(o.getErrorType(), ErrorType.BAD_DATE);
80  	}
81  
82  	@Test
83  	public void testCommitWithoutTree() throws Exception {
84  		StringBuilder b = new StringBuilder();
85  		b.append("parent ");
86  		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
87  		b.append('\n');
88  		byte[] data = encodeASCII(b.toString());
89  		ObjectId id = ins.insert(Constants.OBJ_COMMIT, data);
90  		ins.flush();
91  
92  		DfsFsck fsck = new DfsFsck(repo);
93  		FsckError errors = fsck.check(null);
94  
95  		assertEquals(errors.getCorruptObjects().size(), 1);
96  		CorruptObject o = errors.getCorruptObjects().iterator().next();
97  		assertTrue(o.getId().equals(id));
98  		assertEquals(o.getErrorType(), ErrorType.MISSING_TREE);
99  	}
100 
101 	@Test
102 	public void testTagWithoutObject() throws Exception {
103 		StringBuilder b = new StringBuilder();
104 		b.append("type commit\n");
105 		b.append("tag test-tag\n");
106 		b.append("tagger A. U. Thor <author@localhost> 1 +0000\n");
107 		byte[] data = encodeASCII(b.toString());
108 		ObjectId id = ins.insert(Constants.OBJ_TAG, data);
109 		ins.flush();
110 
111 		DfsFsck fsck = new DfsFsck(repo);
112 		FsckError errors = fsck.check(null);
113 
114 		assertEquals(errors.getCorruptObjects().size(), 1);
115 		CorruptObject o = errors.getCorruptObjects().iterator().next();
116 		assertTrue(o.getId().equals(id));
117 		assertEquals(o.getErrorType(), ErrorType.MISSING_OBJECT);
118 	}
119 
120 	@Test
121 	public void testTreeWithNullSha() throws Exception {
122 		byte[] data = concat(encodeASCII("100644 A"), new byte[] { '\0' },
123 				new byte[OBJECT_ID_LENGTH]);
124 		ObjectId id = ins.insert(Constants.OBJ_TREE, data);
125 		ins.flush();
126 
127 		DfsFsck fsck = new DfsFsck(repo);
128 		FsckError errors = fsck.check(null);
129 
130 		assertEquals(errors.getCorruptObjects().size(), 1);
131 		CorruptObject o = errors.getCorruptObjects().iterator().next();
132 		assertTrue(o.getId().equals(id));
133 		assertEquals(o.getErrorType(), ErrorType.NULL_SHA1);
134 	}
135 
136 	@Test
137 	public void testMultipleInvalidObjects() throws Exception {
138 		StringBuilder b = new StringBuilder();
139 		b.append("tree ");
140 		b.append("be9bfa841874ccc9f2ef7c48d0c76226f89b7189");
141 		b.append('\n');
142 		b.append("parent ");
143 		b.append("\n");
144 		byte[] data = encodeASCII(b.toString());
145 		ObjectId id1 = ins.insert(Constants.OBJ_COMMIT, data);
146 
147 		b = new StringBuilder();
148 		b.append("100644");
149 		data = encodeASCII(b.toString());
150 		ObjectId id2 = ins.insert(Constants.OBJ_TREE, data);
151 
152 		ins.flush();
153 
154 		DfsFsck fsck = new DfsFsck(repo);
155 		FsckError errors = fsck.check(null);
156 
157 		assertEquals(errors.getCorruptObjects().size(), 2);
158 		for (CorruptObject o : errors.getCorruptObjects()) {
159 			if (o.getId().equals(id1)) {
160 				assertEquals(o.getErrorType(), ErrorType.BAD_PARENT_SHA1);
161 			} else if (o.getId().equals(id2)) {
162 				assertNull(o.getErrorType());
163 			} else {
164 				fail();
165 			}
166 		}
167 	}
168 
169 	@Test
170 	public void testValidConnectivity() throws Exception {
171 		ObjectId blobId = ins
172 				.insert(Constants.OBJ_BLOB, Constants.encode("foo"));
173 
174 		byte[] blobIdBytes = new byte[OBJECT_ID_LENGTH];
175 		blobId.copyRawTo(blobIdBytes, 0);
176 		byte[] data = concat(encodeASCII("100644 regular-file\0"), blobIdBytes);
177 		ObjectId treeId = ins.insert(Constants.OBJ_TREE, data);
178 		ins.flush();
179 
180 		RevCommit commit = git.commit().message("0").setTopLevelTree(treeId)
181 				.create();
182 
183 		git.update("master", commit);
184 
185 		DfsFsck fsck = new DfsFsck(repo);
186 		FsckError errors = fsck.check(null);
187 		assertEquals(errors.getMissingObjects().size(), 0);
188 	}
189 
190 	@Test
191 	public void testMissingObject() throws Exception {
192 		ObjectId blobId = ObjectId
193 				.fromString("19102815663d23f8b75a47e7a01965dcdc96468c");
194 		byte[] blobIdBytes = new byte[OBJECT_ID_LENGTH];
195 		blobId.copyRawTo(blobIdBytes, 0);
196 		byte[] data = concat(encodeASCII("100644 regular-file\0"), blobIdBytes);
197 		ObjectId treeId = ins.insert(Constants.OBJ_TREE, data);
198 		ins.flush();
199 
200 		RevCommit commit = git.commit().message("0").setTopLevelTree(treeId)
201 				.create();
202 
203 		git.update("master", commit);
204 
205 		DfsFsck fsck = new DfsFsck(repo);
206 		FsckError errors = fsck.check(null);
207 		assertEquals(errors.getMissingObjects().size(), 1);
208 		assertEquals(errors.getMissingObjects().iterator().next(), blobId);
209 	}
210 
211 	@Test
212 	public void testNonCommitHead() throws Exception {
213 		RevCommit commit0 = git.commit().message("0").create();
214 		StringBuilder b = new StringBuilder();
215 		b.append("object ");
216 		b.append(commit0.getName());
217 		b.append('\n');
218 		b.append("type commit\n");
219 		b.append("tag test-tag\n");
220 		b.append("tagger A. U. Thor <author@localhost> 1 +0000\n");
221 
222 		byte[] data = encodeASCII(b.toString());
223 		ObjectId tagId = ins.insert(Constants.OBJ_TAG, data);
224 		ins.flush();
225 
226 		git.update("master", tagId);
227 
228 		DfsFsck fsck = new DfsFsck(repo);
229 		FsckError errors = fsck.check(null);
230 		assertEquals(errors.getCorruptObjects().size(), 0);
231 		assertEquals(errors.getNonCommitHeads().size(), 1);
232 		assertEquals(errors.getNonCommitHeads().iterator().next(),
233 				"refs/heads/master");
234 	}
235 
236 	private ObjectId insertGitModules(String contents) throws IOException {
237 		ObjectId blobId = ins.insert(Constants.OBJ_BLOB,
238 				Constants.encode(contents));
239 
240 		byte[] blobIdBytes = new byte[OBJECT_ID_LENGTH];
241 		blobId.copyRawTo(blobIdBytes, 0);
242 		byte[] data = concat(encodeASCII("100644 .gitmodules\0"), blobIdBytes);
243 		ins.insert(Constants.OBJ_TREE, data);
244 		ins.flush();
245 
246 		return blobId;
247 	}
248 
249 	@Test
250 	public void testInvalidGitModules() throws Exception {
251 		String fakeGitmodules = new StringBuilder()
252 				.append("[submodule \"test\"]\n")
253 				.append("    path = xlib\n")
254 				.append("    url = https://example.com/repo/xlib.git\n\n")
255 				.append("[submodule \"test2\"]\n")
256 				.append("    path = zlib\n")
257 				.append("    url = -upayload.sh\n")
258 				.toString();
259 
260 		ObjectId blobId = insertGitModules(fakeGitmodules);
261 
262 		DfsFsck fsck = new DfsFsck(repo);
263 		FsckError errors = fsck.check(null);
264 		assertEquals(errors.getCorruptObjects().size(), 1);
265 
266 		CorruptObject error = errors.getCorruptObjects().iterator().next();
267 		assertEquals(error.getId(), blobId);
268 		assertEquals(error.getType(), Constants.OBJ_BLOB);
269 		assertEquals(error.getErrorType(), ErrorType.GITMODULES_URL);
270 	}
271 
272 
273 	@Test
274 	public void testValidGitModules() throws Exception {
275 		String fakeGitmodules = new StringBuilder()
276 				.append("[submodule \"test\"]\n")
277 				.append("    path = xlib\n")
278 				.append("    url = https://example.com/repo/xlib.git\n\n")
279 				.append("[submodule \"test2\"]\n")
280 				.append("    path = zlib\n")
281 				.append("    url = ok/path\n")
282 				.toString();
283 
284 		insertGitModules(fakeGitmodules);
285 
286 		DfsFsck fsck = new DfsFsck(repo);
287 		FsckError errors = fsck.check(null);
288 		assertEquals(errors.getCorruptObjects().size(), 0);
289 	}
290 
291 }