View Javadoc
1   /*
2    * Copyright (C) 2008-2009, Google Inc.
3    * Copyright (C) 2008, Mike Ralphson <mike@abacus.co.uk>
4    * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>
5    * and other copyright owners as documented in the project's IP log.
6    *
7    * This program and the accompanying materials are made available
8    * under the terms of the Eclipse Distribution License v1.0 which
9    * accompanies this distribution, is reproduced below, and is
10   * available at http://www.eclipse.org/org/documents/edl-v10.php
11   *
12   * All rights reserved.
13   *
14   * Redistribution and use in source and binary forms, with or
15   * without modification, are permitted provided that the following
16   * conditions are met:
17   *
18   * - Redistributions of source code must retain the above copyright
19   *   notice, this list of conditions and the following disclaimer.
20   *
21   * - Redistributions in binary form must reproduce the above
22   *   copyright notice, this list of conditions and the following
23   *   disclaimer in the documentation and/or other materials provided
24   *   with the distribution.
25   *
26   * - Neither the name of the Eclipse Foundation, Inc. nor the
27   *   names of its contributors may be used to endorse or promote
28   *   products derived from this software without specific prior
29   *   written permission.
30   *
31   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
32   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
33   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
34   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
36   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
37   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
38   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
39   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
40   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
41   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
42   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
43   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44   */
45  
46  package org.eclipse.jgit.transport;
47  
48  import static org.junit.Assert.assertEquals;
49  import static org.junit.Assert.assertNull;
50  import static org.junit.Assert.assertTrue;
51  import static org.junit.Assert.fail;
52  
53  import java.io.ByteArrayInputStream;
54  import java.io.ByteArrayOutputStream;
55  import java.io.FileNotFoundException;
56  import java.io.IOException;
57  import java.net.URISyntaxException;
58  import java.util.Collections;
59  import java.util.Set;
60  
61  import org.eclipse.jgit.errors.MissingBundlePrerequisiteException;
62  import org.eclipse.jgit.errors.NotSupportedException;
63  import org.eclipse.jgit.errors.TransportException;
64  import org.eclipse.jgit.lib.NullProgressMonitor;
65  import org.eclipse.jgit.lib.ObjectId;
66  import org.eclipse.jgit.lib.Ref;
67  import org.eclipse.jgit.lib.Repository;
68  import org.eclipse.jgit.revwalk.RevCommit;
69  import org.eclipse.jgit.revwalk.RevWalk;
70  import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
71  import org.junit.Test;
72  
73  public class BundleWriterTest extends SampleDataRepositoryTestCase {
74  
75  	@Test
76  	public void testWriteSingleRef() throws Exception {
77  		// Create a tiny bundle, (well one of) the first commits only
78  		final byte[] bundle = makeBundle("refs/heads/firstcommit",
79  				"42e4e7c5e507e113ebbb7801b16b52cf867b7ce1", null);
80  
81  		// Then we clone a new repo from that bundle and do a simple test. This
82  		// makes sure we could read the bundle we created.
83  		Repository newRepo = createBareRepository();
84  		FetchResult fetchResult = fetchFromBundle(newRepo, bundle);
85  		Ref advertisedRef = fetchResult
86  				.getAdvertisedRef("refs/heads/firstcommit");
87  
88  		// We expect first commit to appear by id
89  		assertEquals("42e4e7c5e507e113ebbb7801b16b52cf867b7ce1", advertisedRef
90  				.getObjectId().name());
91  		// ..and by name as the bundle created a new ref
92  		assertEquals("42e4e7c5e507e113ebbb7801b16b52cf867b7ce1", newRepo
93  				.resolve("refs/heads/firstcommit").name());
94  	}
95  
96  	@Test
97  	public void testWriteHEAD() throws Exception {
98  		byte[] bundle = makeBundle("HEAD",
99  				"42e4e7c5e507e113ebbb7801b16b52cf867b7ce1", null);
100 
101 		Repository newRepo = createBareRepository();
102 		FetchResult fetchResult = fetchFromBundle(newRepo, bundle);
103 		Ref advertisedRef = fetchResult.getAdvertisedRef("HEAD");
104 
105 		assertEquals("42e4e7c5e507e113ebbb7801b16b52cf867b7ce1", advertisedRef
106 				.getObjectId().name());
107 	}
108 
109 	@Test
110 	public void testIncrementalBundle() throws Exception {
111 		byte[] bundle;
112 
113 		// Create a small bundle, an early commit
114 		bundle = makeBundle("refs/heads/aa", db.resolve("a").name(), null);
115 
116 		// Then we clone a new repo from that bundle and do a simple test. This
117 		// makes sure
118 		// we could read the bundle we created.
119 		Repository newRepo = createBareRepository();
120 		FetchResult fetchResult = fetchFromBundle(newRepo, bundle);
121 		Ref advertisedRef = fetchResult.getAdvertisedRef("refs/heads/aa");
122 
123 		assertEquals(db.resolve("a").name(), advertisedRef.getObjectId().name());
124 		assertEquals(db.resolve("a").name(), newRepo.resolve("refs/heads/aa")
125 				.name());
126 		assertNull(newRepo.resolve("refs/heads/a"));
127 
128 		// Next an incremental bundle
129 		bundle = makeBundle("refs/heads/cc", db.resolve("c").name(),
130 				new RevWalk(db).parseCommit(db.resolve("a").toObjectId()));
131 		fetchResult = fetchFromBundle(newRepo, bundle);
132 		advertisedRef = fetchResult.getAdvertisedRef("refs/heads/cc");
133 		assertEquals(db.resolve("c").name(), advertisedRef.getObjectId().name());
134 		assertEquals(db.resolve("c").name(), newRepo.resolve("refs/heads/cc")
135 				.name());
136 		assertNull(newRepo.resolve("refs/heads/c"));
137 		assertNull(newRepo.resolve("refs/heads/a")); // still unknown
138 
139 		try {
140 			// Check that we actually needed the first bundle
141 			Repository newRepo2 = createBareRepository();
142 			fetchResult = fetchFromBundle(newRepo2, bundle);
143 			fail("We should not be able to fetch from bundle with prerequisites that are not fulfilled");
144 		} catch (MissingBundlePrerequisiteException e) {
145 			assertTrue(e.getMessage()
146 					.indexOf(db.resolve("refs/heads/a").name()) >= 0);
147 		}
148 	}
149 
150 	private static FetchResult fetchFromBundle(final Repository newRepo,
151 			final byte[] bundle) throws URISyntaxException,
152 			NotSupportedException, TransportException {
153 		final URIish uri = new URIish("in-memory://");
154 		final ByteArrayInputStream in = new ByteArrayInputStream(bundle);
155 		final RefSpec rs = new RefSpec("refs/heads/*:refs/heads/*");
156 		final Set<RefSpec> refs = Collections.singleton(rs);
157 		return new TransportBundleStream(newRepo, uri, in).fetch(
158 				NullProgressMonitor.INSTANCE, refs);
159 	}
160 
161 	private byte[] makeBundle(final String name,
162 			final String anObjectToInclude, final RevCommit assume)
163 			throws FileNotFoundException, IOException {
164 		final BundleWriter bw;
165 
166 		bw = new BundleWriter(db);
167 		bw.include(name, ObjectId.fromString(anObjectToInclude));
168 		if (assume != null)
169 			bw.assume(assume);
170 		final ByteArrayOutputStream out = new ByteArrayOutputStream();
171 		bw.writeBundle(NullProgressMonitor.INSTANCE, out);
172 		return out.toByteArray();
173 	}
174 
175 }