View Javadoc
1   /*
2    * Copyright (C) 2011, GitHub Inc.
3    * and other copyright owners as documented in the project's IP log.
4    *
5    * This program and the accompanying materials are made available
6    * under the terms of the Eclipse Distribution License v1.0 which
7    * accompanies this distribution, is reproduced below, and is
8    * available at http://www.eclipse.org/org/documents/edl-v10.php
9    *
10   * All rights reserved.
11   *
12   * Redistribution and use in source and binary forms, with or
13   * without modification, are permitted provided that the following
14   * conditions are met:
15   *
16   * - Redistributions of source code must retain the above copyright
17   *   notice, this list of conditions and the following disclaimer.
18   *
19   * - Redistributions in binary form must reproduce the above
20   *   copyright notice, this list of conditions and the following
21   *   disclaimer in the documentation and/or other materials provided
22   *   with the distribution.
23   *
24   * - Neither the name of the Eclipse Foundation, Inc. nor the
25   *   names of its contributors may be used to endorse or promote
26   *   products derived from this software without specific prior
27   *   written permission.
28   *
29   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42   */
43  package org.eclipse.jgit.submodule;
44  
45  import static org.junit.Assert.assertEquals;
46  import static org.junit.Assert.assertNotNull;
47  import static org.junit.Assert.assertTrue;
48  import static org.junit.Assert.fail;
49  
50  import java.io.File;
51  import java.text.MessageFormat;
52  
53  import org.eclipse.jgit.api.Git;
54  import org.eclipse.jgit.api.Status;
55  import org.eclipse.jgit.api.SubmoduleAddCommand;
56  import org.eclipse.jgit.api.errors.GitAPIException;
57  import org.eclipse.jgit.api.errors.JGitInternalException;
58  import org.eclipse.jgit.dircache.DirCache;
59  import org.eclipse.jgit.dircache.DirCacheEditor;
60  import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit;
61  import org.eclipse.jgit.dircache.DirCacheEntry;
62  import org.eclipse.jgit.internal.JGitText;
63  import org.eclipse.jgit.junit.RepositoryTestCase;
64  import org.eclipse.jgit.lib.ConfigConstants;
65  import org.eclipse.jgit.lib.Constants;
66  import org.eclipse.jgit.lib.FileMode;
67  import org.eclipse.jgit.lib.ObjectId;
68  import org.eclipse.jgit.lib.Repository;
69  import org.eclipse.jgit.revwalk.RevCommit;
70  import org.eclipse.jgit.storage.file.FileBasedConfig;
71  import org.junit.Test;
72  
73  /**
74   * Unit tests of {@link org.eclipse.jgit.api.SubmoduleAddCommand}
75   */
76  public class SubmoduleAddTest extends RepositoryTestCase {
77  
78  	@Test
79  	public void commandWithNullPath() throws GitAPIException {
80  		try {
81  			new SubmoduleAddCommand(db).setURI("uri").call().close();
82  			fail("Exception not thrown");
83  		} catch (IllegalArgumentException e) {
84  			assertEquals(JGitText.get().pathNotConfigured, e.getMessage());
85  		}
86  	}
87  
88  	@Test
89  	public void commandWithEmptyPath() throws GitAPIException {
90  		try {
91  			new SubmoduleAddCommand(db).setPath("").setURI("uri").call()
92  					.close();
93  			fail("Exception not thrown");
94  		} catch (IllegalArgumentException e) {
95  			assertEquals(JGitText.get().pathNotConfigured, e.getMessage());
96  		}
97  	}
98  
99  	@Test
100 	public void commandWithNullUri() throws GitAPIException {
101 		try {
102 			new SubmoduleAddCommand(db).setPath("sub").call().close();
103 			fail("Exception not thrown");
104 		} catch (IllegalArgumentException e) {
105 			assertEquals(JGitText.get().uriNotConfigured, e.getMessage());
106 		}
107 	}
108 
109 	@Test
110 	public void commandWithEmptyUri() throws GitAPIException {
111 		try {
112 			new SubmoduleAddCommand(db).setPath("sub").setURI("").call()
113 					.close();
114 			fail("Exception not thrown");
115 		} catch (IllegalArgumentException e) {
116 			assertEquals(JGitText.get().uriNotConfigured, e.getMessage());
117 		}
118 	}
119 
120 	@Test
121 	public void addSubmodule() throws Exception {
122 		try (Git git = new Git(db)) {
123 			writeTrashFile("file.txt", "content");
124 			git.add().addFilepattern("file.txt").call();
125 			RevCommit commit = git.commit().setMessage("create file").call();
126 
127 			SubmoduleAddCommand command = new SubmoduleAddCommand(db);
128 			String path = "sub";
129 			command.setPath(path);
130 			String uri = db.getDirectory().toURI().toString();
131 			command.setURI(uri);
132 			ObjectId subCommit;
133 			try (Repository repo = command.call()) {
134 				assertNotNull(repo);
135 				subCommit = repo.resolve(Constants.HEAD);
136 			}
137 
138 			SubmoduleWalk generator = SubmoduleWalk.forIndex(db);
139 			generator.loadModulesConfig();
140 			assertTrue(generator.next());
141 			assertEquals(path, generator.getModuleName());
142 			assertEquals(path, generator.getPath());
143 			assertEquals(commit, generator.getObjectId());
144 			assertEquals(uri, generator.getModulesUrl());
145 			assertEquals(path, generator.getModulesPath());
146 			assertEquals(uri, generator.getConfigUrl());
147 			try (Repository subModRepo = generator.getRepository()) {
148 				assertNotNull(subModRepo);
149 				assertEquals(subCommit, commit);
150 			}
151 
152 			Status status = Git.wrap(db).status().call();
153 			assertTrue(status.getAdded().contains(Constants.DOT_GIT_MODULES));
154 			assertTrue(status.getAdded().contains(path));
155 		}
156 	}
157 
158 	@Test
159 	public void addSubmoduleWithName() throws Exception {
160 		try (Git git = new Git(db)) {
161 			writeTrashFile("file.txt", "content");
162 			git.add().addFilepattern("file.txt").call();
163 			RevCommit commit = git.commit().setMessage("create file").call();
164 
165 			SubmoduleAddCommand command = new SubmoduleAddCommand(db);
166 			String name = "testsub";
167 			command.setName(name);
168 			String path = "sub";
169 			command.setPath(path);
170 			String uri = db.getDirectory().toURI().toString();
171 			command.setURI(uri);
172 			ObjectId subCommit;
173 			try (Repository repo = command.call()) {
174 				assertNotNull(repo);
175 				subCommit = repo.resolve(Constants.HEAD);
176 			}
177 
178 			SubmoduleWalk generator = SubmoduleWalk.forIndex(db);
179 			generator.loadModulesConfig();
180 			assertTrue(generator.next());
181 			assertEquals(name, generator.getModuleName());
182 			assertEquals(path, generator.getPath());
183 			assertEquals(commit, generator.getObjectId());
184 			assertEquals(uri, generator.getModulesUrl());
185 			assertEquals(path, generator.getModulesPath());
186 			assertEquals(uri, generator.getConfigUrl());
187 			try (Repository subModRepo = generator.getRepository()) {
188 				assertNotNull(subModRepo);
189 				assertEquals(subCommit, commit);
190 			}
191 
192 			Status status = Git.wrap(db).status().call();
193 			assertTrue(status.getAdded().contains(Constants.DOT_GIT_MODULES));
194 			assertTrue(status.getAdded().contains(path));
195 		}
196 	}
197 
198 	@Test
199 	public void addExistentSubmodule() throws Exception {
200 		final ObjectId id = ObjectId
201 				.fromString("abcd1234abcd1234abcd1234abcd1234abcd1234");
202 		final String path = "sub";
203 		DirCache cache = db.lockDirCache();
204 		DirCacheEditor editor = cache.editor();
205 		editor.add(new PathEdit(path) {
206 
207 			@Override
208 			public void apply(DirCacheEntry ent) {
209 				ent.setFileMode(FileMode.GITLINK);
210 				ent.setObjectId(id);
211 			}
212 		});
213 		editor.commit();
214 
215 		SubmoduleAddCommand command = new SubmoduleAddCommand(db);
216 		command.setPath(path);
217 		command.setURI("git://server/repo.git");
218 		try {
219 			command.call().close();
220 			fail("Exception not thrown");
221 		} catch (JGitInternalException e) {
222 			assertEquals(
223 					MessageFormat.format(JGitText.get().submoduleExists, path),
224 					e.getMessage());
225 		}
226 	}
227 
228 	@Test
229 	public void addSubmoduleWithInvalidPath() throws Exception {
230 		SubmoduleAddCommand command = new SubmoduleAddCommand(db);
231 		command.setPath("-invalid-path");
232 		command.setName("sub");
233 		command.setURI("http://example.com/repo/x.git");
234 		try {
235 			command.call().close();
236 			fail("Exception not thrown");
237 		} catch (IllegalArgumentException e) {
238 			assertEquals("Invalid submodule path '-invalid-path'",
239 					e.getMessage());
240 		}
241 	}
242 
243 	@Test
244 	public void addSubmoduleWithInvalidUri() throws Exception {
245 		SubmoduleAddCommand command = new SubmoduleAddCommand(db);
246 		command.setPath("valid-path");
247 		command.setURI("-upstream");
248 		try {
249 			command.call().close();
250 			fail("Exception not thrown");
251 		} catch (IllegalArgumentException e) {
252 			assertEquals("Invalid submodule URL '-upstream'", e.getMessage());
253 		}
254 	}
255 
256 	@Test
257 	public void addSubmoduleWithRelativeUri() throws Exception {
258 		try (Git git = new Git(db)) {
259 			writeTrashFile("file.txt", "content");
260 			git.add().addFilepattern("file.txt").call();
261 			RevCommit commit = git.commit().setMessage("create file").call();
262 
263 			SubmoduleAddCommand command = new SubmoduleAddCommand(db);
264 			String path = "sub";
265 			String uri = "./.git";
266 			command.setPath(path);
267 			command.setURI(uri);
268 			Repository repo = command.call();
269 			assertNotNull(repo);
270 			addRepoToClose(repo);
271 
272 			SubmoduleWalk generator = SubmoduleWalk.forIndex(db);
273 			assertTrue(generator.next());
274 			assertEquals(path, generator.getPath());
275 			assertEquals(commit, generator.getObjectId());
276 			assertEquals(uri, generator.getModulesUrl());
277 			assertEquals(path, generator.getModulesPath());
278 			String fullUri = db.getDirectory().getAbsolutePath();
279 			if (File.separatorChar == '\\') {
280 				fullUri = fullUri.replace('\\', '/');
281 			}
282 			assertEquals(fullUri, generator.getConfigUrl());
283 			try (Repository subModRepo = generator.getRepository()) {
284 				assertNotNull(subModRepo);
285 				assertEquals(fullUri,
286 						subModRepo.getConfig().getString(
287 								ConfigConstants.CONFIG_REMOTE_SECTION,
288 								Constants.DEFAULT_REMOTE_NAME,
289 								ConfigConstants.CONFIG_KEY_URL));
290 			}
291 			assertEquals(commit, repo.resolve(Constants.HEAD));
292 
293 			Status status = Git.wrap(db).status().call();
294 			assertTrue(status.getAdded().contains(Constants.DOT_GIT_MODULES));
295 			assertTrue(status.getAdded().contains(path));
296 		}
297 	}
298 
299 	@Test
300 	public void addSubmoduleWithExistingSubmoduleDefined() throws Exception {
301 		String path1 = "sub1";
302 		String url1 = "git://server/repo1.git";
303 		String path2 = "sub2";
304 
305 		FileBasedConfig modulesConfig = new FileBasedConfig(new File(
306 				db.getWorkTree(), Constants.DOT_GIT_MODULES), db.getFS());
307 		modulesConfig.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION,
308 				path1, ConfigConstants.CONFIG_KEY_PATH, path1);
309 		modulesConfig.setString(ConfigConstants.CONFIG_SUBMODULE_SECTION,
310 				path1, ConfigConstants.CONFIG_KEY_URL, url1);
311 		modulesConfig.save();
312 
313 		try (Git git = new Git(db)) {
314 			writeTrashFile("file.txt", "content");
315 			git.add().addFilepattern("file.txt").call();
316 			assertNotNull(git.commit().setMessage("create file").call());
317 
318 			SubmoduleAddCommand command = new SubmoduleAddCommand(db);
319 			command.setPath(path2);
320 			String url2 = db.getDirectory().toURI().toString();
321 			command.setURI(url2);
322 			Repository r = command.call();
323 			assertNotNull(r);
324 			addRepoToClose(r);
325 
326 			modulesConfig.load();
327 			assertEquals(path1, modulesConfig.getString(
328 					ConfigConstants.CONFIG_SUBMODULE_SECTION, path1,
329 					ConfigConstants.CONFIG_KEY_PATH));
330 			assertEquals(url1, modulesConfig.getString(
331 					ConfigConstants.CONFIG_SUBMODULE_SECTION, path1,
332 					ConfigConstants.CONFIG_KEY_URL));
333 			assertEquals(path2, modulesConfig.getString(
334 					ConfigConstants.CONFIG_SUBMODULE_SECTION, path2,
335 					ConfigConstants.CONFIG_KEY_PATH));
336 			assertEquals(url2, modulesConfig.getString(
337 					ConfigConstants.CONFIG_SUBMODULE_SECTION, path2,
338 					ConfigConstants.CONFIG_KEY_URL));
339 		}
340 	}
341 
342 	@Test
343 	public void denySubmoduleWithDotDot() throws Exception {
344 		SubmoduleAddCommand command = new SubmoduleAddCommand(db);
345 		command.setName("dir/../");
346 		command.setPath("sub");
347 		command.setURI(db.getDirectory().toURI().toString());
348 		try {
349 			command.call();
350 			fail();
351 		} catch (IllegalArgumentException e) {
352 			// Expected
353 		}
354 	}
355 }