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.internal.storage.pack.PackExt;
32  import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
33  import org.eclipse.jgit.junit.TestRepository;
34  import org.eclipse.jgit.lib.AbbreviatedObjectId;
35  import org.eclipse.jgit.lib.ObjectId;
36  import org.eclipse.jgit.lib.ObjectReader;
37  import org.eclipse.jgit.lib.Repository;
38  import org.eclipse.jgit.revwalk.RevBlob;
39  import org.eclipse.jgit.transport.PackedObjectInfo;
40  import org.eclipse.jgit.util.FileUtils;
41  import org.junit.After;
42  import org.junit.Before;
43  import org.junit.Test;
44  
45  public class AbbreviationTest extends LocalDiskRepositoryTestCase {
46  	private FileRepository db;
47  
48  	private ObjectReader reader;
49  
50  	private TestRepository<Repository> test;
51  
52  	@Override
53  	@Before
54  	public void setUp() throws Exception {
55  		super.setUp();
56  		db = createBareRepository();
57  		reader = db.newObjectReader();
58  		test = new TestRepository<>(db);
59  	}
60  
61  	@Override
62  	@After
63  	public void tearDown() throws Exception {
64  		if (reader != null) {
65  			reader.close();
66  		}
67  	}
68  
69  	@Test
70  	public void testAbbreviateOnEmptyRepository() throws IOException {
71  		ObjectId id = id("9d5b926ed164e8ee88d3b8b1e525d699adda01ba");
72  
73  		assertEquals(id.abbreviate(2), reader.abbreviate(id, 2));
74  		assertEquals(id.abbreviate(7), reader.abbreviate(id, 7));
75  		assertEquals(id.abbreviate(8), reader.abbreviate(id, 8));
76  		assertEquals(id.abbreviate(10), reader.abbreviate(id, 10));
77  		assertEquals(id.abbreviate(16), reader.abbreviate(id, 16));
78  
79  		assertEquals(AbbreviatedObjectId.fromObjectId(id), //
80  				reader.abbreviate(id, OBJECT_ID_STRING_LENGTH));
81  
82  		Collection<ObjectId> matches;
83  
84  		matches = reader.resolve(reader.abbreviate(id, 8));
85  		assertNotNull(matches);
86  		assertEquals(0, matches.size());
87  
88  		matches = reader.resolve(AbbreviatedObjectId.fromObjectId(id));
89  		assertNotNull(matches);
90  		assertEquals(1, matches.size());
91  		assertEquals(id, matches.iterator().next());
92  	}
93  
94  	@Test
95  	public void testAbbreviateLooseBlob() throws Exception {
96  		ObjectId id = test.blob("test");
97  
98  		assertEquals(id.abbreviate(2), reader.abbreviate(id, 2));
99  		assertEquals(id.abbreviate(7), reader.abbreviate(id, 7));
100 		assertEquals(id.abbreviate(8), reader.abbreviate(id, 8));
101 		assertEquals(id.abbreviate(10), reader.abbreviate(id, 10));
102 		assertEquals(id.abbreviate(16), reader.abbreviate(id, 16));
103 
104 		Collection<ObjectId> matches = reader.resolve(reader.abbreviate(id, 8));
105 		assertNotNull(matches);
106 		assertEquals(1, matches.size());
107 		assertEquals(id, matches.iterator().next());
108 
109 		assertEquals(id, db.resolve(reader.abbreviate(id, 8).name()));
110 	}
111 
112 	@Test
113 	public void testAbbreviatePackedBlob() throws Exception {
114 		RevBlob id = test.blob("test");
115 		test.branch("master").commit().add("test", id).child();
116 		test.packAndPrune();
117 		assertTrue(reader.has(id));
118 
119 		assertEquals(id.abbreviate(7), reader.abbreviate(id, 7));
120 		assertEquals(id.abbreviate(8), reader.abbreviate(id, 8));
121 		assertEquals(id.abbreviate(10), reader.abbreviate(id, 10));
122 		assertEquals(id.abbreviate(16), reader.abbreviate(id, 16));
123 
124 		Collection<ObjectId> matches = reader.resolve(reader.abbreviate(id, 8));
125 		assertNotNull(matches);
126 		assertEquals(1, matches.size());
127 		assertEquals(id, matches.iterator().next());
128 
129 		assertEquals(id, db.resolve(reader.abbreviate(id, 8).name()));
130 	}
131 
132 	@Test
133 	public void testAbbreviateIsActuallyUnique() throws Exception {
134 		// This test is far more difficult. We have to manually craft
135 		// an input that contains collisions at a particular prefix,
136 		// but this is computationally difficult. Instead we force an
137 		// index file to have what we want.
138 		//
139 
140 		ObjectId id = id("9d5b926ed164e8ee88d3b8b1e525d699adda01ba");
141 		byte[] idBuf = toByteArray(id);
142 		List<PackedObjectInfo> objects = new ArrayList<>();
143 		for (int i = 0; i < 256; i++) {
144 			idBuf[9] = (byte) i;
145 			objects.add(new PackedObjectInfo(ObjectId.fromRaw(idBuf)));
146 		}
147 
148 		File packDir = db.getObjectDatabase().getPackDirectory();
149 		PackFile idxFile = new PackFile(packDir, id, PackExt.INDEX);
150 		PackFile packFile = idxFile.create(PackExt.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 }