View Javadoc
1   /*
2    * Copyright (C) 2010, 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.file;
12  
13  import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
14  import static org.eclipse.jgit.lib.Constants.OBJECT_ID_STRING_LENGTH;
15  import static org.junit.Assert.assertEquals;
16  import static org.junit.Assert.assertNotNull;
17  import static org.junit.Assert.assertTrue;
18  import static org.junit.Assert.fail;
19  
20  import java.io.BufferedOutputStream;
21  import java.io.ByteArrayOutputStream;
22  import java.io.File;
23  import java.io.FileOutputStream;
24  import java.io.IOException;
25  import java.io.OutputStream;
26  import java.util.ArrayList;
27  import java.util.Collection;
28  import java.util.List;
29  
30  import org.eclipse.jgit.errors.AmbiguousObjectException;
31  import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
32  import org.eclipse.jgit.junit.TestRepository;
33  import org.eclipse.jgit.lib.AbbreviatedObjectId;
34  import org.eclipse.jgit.lib.ObjectId;
35  import org.eclipse.jgit.lib.ObjectReader;
36  import org.eclipse.jgit.lib.Repository;
37  import org.eclipse.jgit.revwalk.RevBlob;
38  import org.eclipse.jgit.transport.PackedObjectInfo;
39  import org.eclipse.jgit.util.FileUtils;
40  import org.junit.After;
41  import org.junit.Before;
42  import org.junit.Test;
43  
44  public class AbbreviationTest extends LocalDiskRepositoryTestCase {
45  	private FileRepository db;
46  
47  	private ObjectReader reader;
48  
49  	private TestRepository<Repository> test;
50  
51  	@Override
52  	@Before
53  	public void setUp() throws Exception {
54  		super.setUp();
55  		db = createBareRepository();
56  		reader = db.newObjectReader();
57  		test = new TestRepository<>(db);
58  	}
59  
60  	@Override
61  	@After
62  	public void tearDown() throws Exception {
63  		if (reader != null) {
64  			reader.close();
65  		}
66  	}
67  
68  	@Test
69  	public void testAbbreviateOnEmptyRepository() throws IOException {
70  		ObjectId id = id("9d5b926ed164e8ee88d3b8b1e525d699adda01ba");
71  
72  		assertEquals(id.abbreviate(2), reader.abbreviate(id, 2));
73  		assertEquals(id.abbreviate(7), reader.abbreviate(id, 7));
74  		assertEquals(id.abbreviate(8), reader.abbreviate(id, 8));
75  		assertEquals(id.abbreviate(10), reader.abbreviate(id, 10));
76  		assertEquals(id.abbreviate(16), reader.abbreviate(id, 16));
77  
78  		assertEquals(AbbreviatedObjectId.fromObjectId(id), //
79  				reader.abbreviate(id, OBJECT_ID_STRING_LENGTH));
80  
81  		Collection<ObjectId> matches;
82  
83  		matches = reader.resolve(reader.abbreviate(id, 8));
84  		assertNotNull(matches);
85  		assertEquals(0, matches.size());
86  
87  		matches = reader.resolve(AbbreviatedObjectId.fromObjectId(id));
88  		assertNotNull(matches);
89  		assertEquals(1, matches.size());
90  		assertEquals(id, matches.iterator().next());
91  	}
92  
93  	@Test
94  	public void testAbbreviateLooseBlob() throws Exception {
95  		ObjectId id = test.blob("test");
96  
97  		assertEquals(id.abbreviate(2), reader.abbreviate(id, 2));
98  		assertEquals(id.abbreviate(7), reader.abbreviate(id, 7));
99  		assertEquals(id.abbreviate(8), reader.abbreviate(id, 8));
100 		assertEquals(id.abbreviate(10), reader.abbreviate(id, 10));
101 		assertEquals(id.abbreviate(16), reader.abbreviate(id, 16));
102 
103 		Collection<ObjectId> matches = reader.resolve(reader.abbreviate(id, 8));
104 		assertNotNull(matches);
105 		assertEquals(1, matches.size());
106 		assertEquals(id, matches.iterator().next());
107 
108 		assertEquals(id, db.resolve(reader.abbreviate(id, 8).name()));
109 	}
110 
111 	@Test
112 	public void testAbbreviatePackedBlob() throws Exception {
113 		RevBlob id = test.blob("test");
114 		test.branch("master").commit().add("test", id).child();
115 		test.packAndPrune();
116 		assertTrue(reader.has(id));
117 
118 		assertEquals(id.abbreviate(7), reader.abbreviate(id, 7));
119 		assertEquals(id.abbreviate(8), reader.abbreviate(id, 8));
120 		assertEquals(id.abbreviate(10), reader.abbreviate(id, 10));
121 		assertEquals(id.abbreviate(16), reader.abbreviate(id, 16));
122 
123 		Collection<ObjectId> matches = reader.resolve(reader.abbreviate(id, 8));
124 		assertNotNull(matches);
125 		assertEquals(1, matches.size());
126 		assertEquals(id, matches.iterator().next());
127 
128 		assertEquals(id, db.resolve(reader.abbreviate(id, 8).name()));
129 	}
130 
131 	@Test
132 	public void testAbbreviateIsActuallyUnique() throws Exception {
133 		// This test is far more difficult. We have to manually craft
134 		// an input that contains collisions at a particular prefix,
135 		// but this is computationally difficult. Instead we force an
136 		// index file to have what we want.
137 		//
138 
139 		ObjectId id = id("9d5b926ed164e8ee88d3b8b1e525d699adda01ba");
140 		byte[] idBuf = toByteArray(id);
141 		List<PackedObjectInfo> objects = new ArrayList<>();
142 		for (int i = 0; i < 256; i++) {
143 			idBuf[9] = (byte) i;
144 			objects.add(new PackedObjectInfo(ObjectId.fromRaw(idBuf)));
145 		}
146 
147 		String packName = "pack-" + id.name();
148 		File packDir = db.getObjectDatabase().getPackDirectory();
149 		File idxFile = new File(packDir, packName + ".idx");
150 		File packFile = new File(packDir, packName + ".pack");
151 		FileUtils.mkdir(packDir, true);
152 		try (OutputStream dst = new BufferedOutputStream(
153 				new FileOutputStream(idxFile))) {
154 			PackIndexWriter writer = new PackIndexWriterV2(dst);
155 			writer.write(objects, new byte[OBJECT_ID_LENGTH]);
156 		}
157 
158 		try (FileOutputStream unused = new FileOutputStream(packFile)) {
159 			// unused
160 		}
161 
162 		assertEquals(id.abbreviate(20), reader.abbreviate(id, 2));
163 
164 		AbbreviatedObjectId abbrev8 = id.abbreviate(8);
165 		Collection<ObjectId> matches = reader.resolve(abbrev8);
166 		assertNotNull(matches);
167 		assertEquals(objects.size(), matches.size());
168 		for (PackedObjectInfo info : objects)
169 			assertTrue("contains " + info.name(), matches.contains(info));
170 
171 		try {
172 			db.resolve(abbrev8.name());
173 			fail("did not throw AmbiguousObjectException");
174 		} catch (AmbiguousObjectException err) {
175 			assertEquals(abbrev8, err.getAbbreviatedObjectId());
176 			matches = err.getCandidates();
177 			assertNotNull(matches);
178 			assertEquals(objects.size(), matches.size());
179 			for (PackedObjectInfo info : objects)
180 				assertTrue("contains " + info.name(), matches.contains(info));
181 		}
182 
183 		assertEquals(id, db.resolve(id.abbreviate(20).name()));
184 	}
185 
186 	private static ObjectId id(String name) {
187 		return ObjectId.fromString(name);
188 	}
189 
190 	private static byte[] toByteArray(ObjectId id) throws IOException {
191 		ByteArrayOutputStream buf = new ByteArrayOutputStream(OBJECT_ID_LENGTH);
192 		id.copyRawTo(buf);
193 		return buf.toByteArray();
194 	}
195 }