1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 package org.eclipse.jgit.transport;
47
48 import static java.nio.charset.StandardCharsets.UTF_8;
49 import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
50 import static org.junit.Assert.assertEquals;
51 import static org.junit.Assert.assertNotNull;
52 import static org.junit.Assert.assertNull;
53 import static org.junit.Assert.assertTrue;
54 import static org.junit.Assert.fail;
55
56 import java.io.ByteArrayInputStream;
57 import java.io.ByteArrayOutputStream;
58 import java.io.FileNotFoundException;
59 import java.io.IOException;
60 import java.net.URISyntaxException;
61 import java.util.Collections;
62 import java.util.Set;
63
64 import org.eclipse.jgit.errors.MissingBundlePrerequisiteException;
65 import org.eclipse.jgit.errors.MissingObjectException;
66 import org.eclipse.jgit.errors.NotSupportedException;
67 import org.eclipse.jgit.errors.TransportException;
68 import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
69 import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
70 import org.eclipse.jgit.lib.Constants;
71 import org.eclipse.jgit.lib.NullProgressMonitor;
72 import org.eclipse.jgit.lib.ObjectId;
73 import org.eclipse.jgit.lib.ObjectInserter;
74 import org.eclipse.jgit.lib.ObjectReader;
75 import org.eclipse.jgit.lib.Ref;
76 import org.eclipse.jgit.lib.Repository;
77 import org.eclipse.jgit.revwalk.RevCommit;
78 import org.eclipse.jgit.revwalk.RevWalk;
79 import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
80 import org.junit.Test;
81
82 public class BundleWriterTest extends SampleDataRepositoryTestCase {
83
84 @Test
85 public void testWriteSingleRef() throws Exception {
86
87 final byte[] bundle = makeBundle("refs/heads/firstcommit",
88 "42e4e7c5e507e113ebbb7801b16b52cf867b7ce1", null);
89
90
91
92 Repository newRepo = createBareRepository();
93 FetchResult fetchResult = fetchFromBundle(newRepo, bundle);
94 Ref advertisedRef = fetchResult
95 .getAdvertisedRef("refs/heads/firstcommit");
96
97
98 assertEquals("42e4e7c5e507e113ebbb7801b16b52cf867b7ce1", advertisedRef
99 .getObjectId().name());
100
101 assertEquals("42e4e7c5e507e113ebbb7801b16b52cf867b7ce1", newRepo
102 .resolve("refs/heads/firstcommit").name());
103 }
104
105 @Test
106 public void testWriteHEAD() throws Exception {
107 byte[] bundle = makeBundle("HEAD",
108 "42e4e7c5e507e113ebbb7801b16b52cf867b7ce1", null);
109
110 Repository newRepo = createBareRepository();
111 FetchResult fetchResult = fetchFromBundle(newRepo, bundle);
112 Ref advertisedRef = fetchResult.getAdvertisedRef("HEAD");
113
114 assertEquals("42e4e7c5e507e113ebbb7801b16b52cf867b7ce1", advertisedRef
115 .getObjectId().name());
116 }
117
118 @Test
119 public void testIncrementalBundle() throws Exception {
120 byte[] bundle;
121
122
123 bundle = makeBundle("refs/heads/aa", db.resolve("a").name(), null);
124
125
126
127
128 Repository newRepo = createBareRepository();
129 FetchResult fetchResult = fetchFromBundle(newRepo, bundle);
130 Ref advertisedRef = fetchResult.getAdvertisedRef("refs/heads/aa");
131
132 assertEquals(db.resolve("a").name(), advertisedRef.getObjectId().name());
133 assertEquals(db.resolve("a").name(), newRepo.resolve("refs/heads/aa")
134 .name());
135 assertNull(newRepo.resolve("refs/heads/a"));
136
137
138 try (RevWalk rw = new RevWalk(db)) {
139 bundle = makeBundle("refs/heads/cc", db.resolve("c").name(),
140 rw.parseCommit(db.resolve("a").toObjectId()));
141 fetchResult = fetchFromBundle(newRepo, bundle);
142 advertisedRef = fetchResult.getAdvertisedRef("refs/heads/cc");
143 assertEquals(db.resolve("c").name(), advertisedRef.getObjectId().name());
144 assertEquals(db.resolve("c").name(), newRepo.resolve("refs/heads/cc")
145 .name());
146 assertNull(newRepo.resolve("refs/heads/c"));
147 assertNull(newRepo.resolve("refs/heads/a"));
148
149 try {
150
151 Repository newRepo2 = createBareRepository();
152 fetchResult = fetchFromBundle(newRepo2, bundle);
153 fail("We should not be able to fetch from bundle with prerequisites that are not fulfilled");
154 } catch (MissingBundlePrerequisiteException e) {
155 assertTrue(e.getMessage()
156 .indexOf(db.resolve("refs/heads/a").name()) >= 0);
157 }
158 }
159 }
160
161 @Test
162 public void testAbortWrite() throws Exception {
163 boolean caught = false;
164 try {
165 makeBundleWithCallback(
166 "refs/heads/aa", db.resolve("a").name(), null, false);
167 } catch (WriteAbortedException e) {
168 caught = true;
169 }
170 assertTrue(caught);
171 }
172
173 @Test
174 public void testCustomObjectReader() throws Exception {
175 String refName = "refs/heads/blob";
176 String data = "unflushed data";
177 ObjectId id;
178 ByteArrayOutputStream out = new ByteArrayOutputStream();
179 try (Repository repo = new InMemoryRepository(
180 new DfsRepositoryDescription("repo"));
181 ObjectInserter ins = repo.newObjectInserter();
182 ObjectReader or = ins.newReader()) {
183 id = ins.insert(OBJ_BLOB, Constants.encode(data));
184 BundleWriter bw = new BundleWriter(or);
185 bw.include(refName, id);
186 bw.writeBundle(NullProgressMonitor.INSTANCE, out);
187 assertNull(repo.exactRef(refName));
188 try {
189 repo.open(id, OBJ_BLOB);
190 fail("We should not be able to open the unflushed blob");
191 } catch (MissingObjectException e) {
192
193 }
194 }
195
196 try (Repository repo = new InMemoryRepository(
197 new DfsRepositoryDescription("copy"))) {
198 fetchFromBundle(repo, out.toByteArray());
199 Ref ref = repo.exactRef(refName);
200 assertNotNull(ref);
201 assertEquals(id, ref.getObjectId());
202 assertEquals(data, new String(repo.open(id, OBJ_BLOB).getBytes(), UTF_8));
203 }
204 }
205
206 private static FetchResult fetchFromBundle(final Repository newRepo,
207 final byte[] bundle) throws URISyntaxException,
208 NotSupportedException, TransportException {
209 final URIish uri = new URIish("in-memory://");
210 final ByteArrayInputStream in = new ByteArrayInputStream(bundle);
211 final RefSpec rs = new RefSpec("refs/heads/*:refs/heads/*");
212 final Set<RefSpec> refs = Collections.singleton(rs);
213 try (TransportBundleStream transport = new TransportBundleStream(
214 newRepo, uri, in)) {
215 return transport.fetch(NullProgressMonitor.INSTANCE, refs);
216 }
217 }
218
219 private byte[] makeBundle(final String name,
220 final String anObjectToInclude, final RevCommit assume)
221 throws FileNotFoundException, IOException {
222 return makeBundleWithCallback(name, anObjectToInclude, assume, true);
223 }
224
225 private byte[] makeBundleWithCallback(final String name,
226 final String anObjectToInclude, final RevCommit assume,
227 boolean value)
228 throws FileNotFoundException, IOException {
229 final BundleWriter bw;
230
231 bw = new BundleWriter(db);
232 bw.setObjectCountCallback(new NaiveObjectCountCallback(value));
233 bw.include(name, ObjectId.fromString(anObjectToInclude));
234 if (assume != null)
235 bw.assume(assume);
236 final ByteArrayOutputStream out = new ByteArrayOutputStream();
237 bw.writeBundle(NullProgressMonitor.INSTANCE, out);
238 return out.toByteArray();
239 }
240
241 private static class NaiveObjectCountCallback
242 implements ObjectCountCallback {
243 private final boolean value;
244
245 NaiveObjectCountCallback(boolean value) {
246 this.value = value;
247 }
248
249 @Override
250 public void setObjectCount(long unused) throws WriteAbortedException {
251 if (!value)
252 throw new WriteAbortedException();
253 }
254 }
255
256 }