View Javadoc
1   /*
2    *  Copyright 2014 The Kuali Foundation Licensed under the
3    *	Educational Community License, Version 2.0 (the "License"); you may
4    *	not use this file except in compliance with the License. You may
5    *	obtain a copy of the License at
6    *
7    *	http://www.osedu.org/licenses/ECL-2.0
8    *
9    *	Unless required by applicable law or agreed to in writing,
10   *	software distributed under the License is distributed on an "AS IS"
11   *	BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12   *	or implied. See the License for the specific language governing
13   *	permissions and limitations under the License.
14   */
15  package org.kuali.student.svn.model;
16  
17  import java.io.IOException;
18  import java.util.Arrays;
19  import java.util.Map;
20  
21  import org.eclipse.jgit.errors.CorruptObjectException;
22  import org.eclipse.jgit.errors.IncorrectObjectTypeException;
23  import org.eclipse.jgit.errors.MissingObjectException;
24  import org.eclipse.jgit.lib.AnyObjectId;
25  import org.eclipse.jgit.lib.FileMode;
26  import org.eclipse.jgit.lib.ObjectId;
27  import org.eclipse.jgit.lib.ObjectInserter;
28  import org.eclipse.jgit.lib.ObjectReader;
29  import org.eclipse.jgit.lib.RefUpdate.Result;
30  import org.eclipse.jgit.revwalk.RevCommit;
31  import org.eclipse.jgit.revwalk.RevWalk;
32  import org.eclipse.jgit.treewalk.TreeWalk;
33  import org.eclipse.jgit.treewalk.filter.PathFilter;
34  import org.junit.Assert;
35  import org.junit.Test;
36  import org.junit.runner.RunWith;
37  import org.junit.runners.BlockJUnit4ClassRunner;
38  import org.kuali.student.git.model.DummyGitTreeNodeInitializer;
39  import org.kuali.student.git.model.ExternalModuleUtils;
40  import org.kuali.student.git.model.ref.utils.GitRefUtils;
41  import org.kuali.student.git.model.tree.GitTreeData;
42  import org.kuali.student.git.model.tree.utils.GitTreeProcessor;
43  import org.kuali.student.git.model.utils.GitTestUtils;
44  import org.slf4j.Logger;
45  import org.slf4j.LoggerFactory;
46  
47  /**
48   * @author Kuali Student Team
49   *
50   */
51  @RunWith(BlockJUnit4ClassRunner.class)
52  public class TestExternalsFusion  extends AbstractGitRespositoryTestCase {
53  
54  	private static Logger log = LoggerFactory.getLogger(TestExternalsFusion.class);
55  	
56  	/**
57  	 * 
58  	 */
59  	public TestExternalsFusion() {
60  		super ("externals-fusion");
61  	}
62  
63  	@Test
64  	public void testInverseFusion() throws IOException {
65  		
66  		/*
67  		 * In this case we have a fused commit and we want to break it into seperate commits to the originating branches.
68  		 */
69  		ObjectInserter inserter = repo.newObjectInserter();
70  		
71  		// create branch 1
72  		GitTreeData branch1 = new GitTreeData(new DummyGitTreeNodeInitializer());
73  		
74  		String branch1FilePath = "src/main/java/org/kuali/student/enrollment/test.txt";
75  		storeFile (inserter, branch1, branch1FilePath, "test");
76  		
77  		ObjectId b1Id = commit (inserter, branch1, "created branch1");
78  		
79  		Result result = createBranch(b1Id, "branch1");
80  		
81  		Assert.assertEquals(Result.NEW, result);
82  		
83  		inserter.flush();
84  		
85  		// create branch 2
86  		GitTreeData branch2 = new GitTreeData(new DummyGitTreeNodeInitializer());
87  		
88  		String branch2FilePath = branch1FilePath;
89  		
90  		storeFile (inserter, branch2, branch2FilePath, "test");
91  		
92  		ObjectId b2Id = commit (inserter, branch2, "created branch2");
93  		
94  		result = createBranch(b2Id, "branch2");
95  		
96  		Assert.assertEquals(Result.NEW, result);
97  		
98  		inserter.flush();
99  		
100 		// create aggregate
101 		GitTreeData aggregate = new GitTreeData(new DummyGitTreeNodeInitializer());
102 		
103 		String aggregate_file_path = "src/main/java/org/kuali/student/enrollment/pom.xml";
104 		storeFile (inserter, aggregate, aggregate_file_path, "pom test");
105 		
106 		ObjectId aggregateId = commit (inserter, aggregate, "created aggregate");
107 		
108 		result = createBranch(aggregateId, "aggregate");
109 		
110 		Assert.assertEquals(Result.NEW, result);
111 		
112 		inserter.flush();
113 		
114 		// a fused commit
115 		GitTreeProcessor treeProcessor = new GitTreeProcessor(repo);
116 		
117 		GitTreeData fusedAggregate = treeProcessor.extractExistingTreeDataFromCommit(aggregateId);
118 		
119 		fusedAggregate.resetDirtyFlag();
120 
121 		String fusedAPath = "branch1/src/main/resources/fusedA.txt";
122 		storeFile(inserter, fusedAggregate, fusedAPath, "fusedA content");
123 		
124 		String fusedBPath = "branch2/src/main/resources/fusedB.txt";
125 		storeFile(inserter, fusedAggregate, fusedBPath, "fusedB content");
126 		
127 		// not sure if we need to split the aggregate so don't for now.
128 		
129 		ObjectId originalAggregateId = aggregateId;
130 		
131 		aggregateId = commit (inserter, fusedAggregate, "fusion commit to the aggregate");
132 		
133 		/*
134 		 * Check that original tree ids were used in the new tree.
135 		 */
136 		checkTrees (originalAggregateId, aggregateId);
137 		
138 		result = createBranch(aggregateId, "aggregate");
139 		
140 		Assert.assertEquals(Result.FORCED, result);
141 		
142 		
143 		ObjectReader objectReader = repo.newObjectReader();
144 		RevWalk rw = new RevWalk(objectReader);
145 		
146 		TreeWalk tw = new TreeWalk (objectReader);
147 		
148 		tw.setRecursive(true);
149 		
150 		ExternalModuleInfo branch1Externals = new ExternalModuleInfo("branch1", "branch1");
151 		ExternalModuleInfo branch2Externals = new ExternalModuleInfo("branch2", "branch2");
152 		
153 		Map<String, ObjectId>results = ExternalModuleUtils.splitFusedTree(objectReader, inserter, rw, aggregateId, Arrays.asList(new ExternalModuleInfo[] {branch1Externals, branch2Externals}));
154 
155 		Assert.assertEquals(true, results.containsKey("branch1"));
156 		
157 		tw.addTree(results.get("branch1"));
158 		
159 		Assert.assertEquals(true, findPath(tw, fusedAPath.substring("branch1/".length())));
160 		Assert.assertEquals(false, findPath(tw, fusedBPath.substring("branch1/".length())));
161 		
162 		Assert.assertEquals(true, results.containsKey("branch2"));
163 		
164 		tw.reset(results.get("branch2"));
165 		
166 		Assert.assertEquals(true, findPath(tw, fusedBPath.substring("branch2/".length())));
167 		Assert.assertEquals(false, findPath(tw, fusedAPath.substring("branch2/".length())));
168 		
169 		tw.reset(results.get("remainder"));
170 
171 		Assert.assertEquals(true, findPath(tw, "src/main/java/org/kuali/student/enrollment/pom.xml"));
172 		Assert.assertEquals(false, findPath(tw, "branch1"));
173 		Assert.assertEquals(false, findPath(tw, "branch2"));
174 		
175 		rw.release();
176 		
177 		objectReader.release();
178 		
179 		inserter.release();
180 		
181 	}
182 	
183 	/*
184 	 * Check that where the trees are aligned that they share the same object id.
185 	 */
186 	private void checkTrees(ObjectId originalAggregateId, ObjectId aggregateId) throws MissingObjectException, IncorrectObjectTypeException, IOException {
187 		
188 		RevWalk rw = new RevWalk(repo);
189 		
190 		RevCommit originalAggregateCommit = rw.parseCommit(originalAggregateId);
191 		
192 		RevCommit aggregateCommit = rw.parseCommit(aggregateId);
193 		
194 		TreeWalk tw = new TreeWalk(repo);
195 		
196 		tw.addTree(originalAggregateCommit.getTree().getId());
197 		tw.addTree(aggregateCommit.getTree().getId());
198 		
199 		tw.setRecursive(false);
200 		
201 		while (tw.next()) {
202 			
203 			FileMode originalMode = tw.getFileMode(0);
204 			
205 			FileMode fileMode = tw.getFileMode(1);
206 			
207 			if (originalMode.equals(FileMode.TYPE_MISSING) || fileMode.equals(FileMode.TYPE_MISSING))
208 				continue; // skip where one side or the other does not exist.
209 			
210 			String name = tw.getNameString();
211 			
212 			ObjectId originalObjectId = tw.getObjectId(0);
213 			ObjectId currentObjectId = tw.getObjectId(1);
214 			
215 			Assert.assertTrue(originalObjectId + " is not equals to " + currentObjectId + " for " + name, originalObjectId.equals(currentObjectId));
216 			
217 		}
218 		
219 		tw.release();
220 		
221 		rw.release();
222 	}
223 
224 	@Test
225 	public void testFusion() throws IOException {
226 		
227 		ObjectInserter inserter = repo.newObjectInserter();
228 		
229 		// create branch 1
230 		GitTreeData branch1 = new GitTreeData(new DummyGitTreeNodeInitializer());
231 		
232 		String branch1FilePath = "src/main/java/org/kuali/student/enrollment/test.txt";
233 		storeFile (inserter, branch1, branch1FilePath, "test");
234 		
235 		ObjectId b1Id = commit (inserter, branch1, "created branch1");
236 		
237 		Result result = createBranch(b1Id, "branch1");
238 		
239 		Assert.assertEquals(Result.NEW, result);
240 		
241 		inserter.flush();
242 		
243 		// create branch 2
244 		GitTreeData branch2 = new GitTreeData(new DummyGitTreeNodeInitializer());
245 		
246 		String branch2FilePath = branch1FilePath;
247 		
248 		storeFile (inserter, branch2, branch2FilePath, "test");
249 		
250 		ObjectId b2Id = commit (inserter, branch2, "created branch2");
251 		
252 		result = createBranch(b2Id, "branch2");
253 		
254 		Assert.assertEquals(Result.NEW, result);
255 		
256 		inserter.flush();
257 		
258 		// create aggregate
259 		GitTreeData aggregate = new GitTreeData(new DummyGitTreeNodeInitializer());
260 		
261 		String aggregate_file_path = "src/main/java/org/kuali/student/enrollment/pom.xml";
262 		storeFile (inserter, aggregate, aggregate_file_path, "pom test");
263 		
264 		ObjectId aggregateId = commit (inserter, aggregate, "created aggregate");
265 		
266 		result = createBranch(aggregateId, "aggregate");
267 		
268 		Assert.assertEquals(Result.NEW, result);
269 		
270 		inserter.flush();
271 		
272 		ExternalModuleInfo branch1Externals = new ExternalModuleInfo("branch1", "branch1", b1Id);
273 		ExternalModuleInfo branch2Externals = new ExternalModuleInfo("branch2", "branch2", b2Id);
274 		
275 		
276 		ObjectReader objectReader = repo.newObjectReader();
277 		RevWalk rw = new RevWalk(objectReader);
278 		
279 		RevCommit aggregateCommit = rw.parseCommit(aggregateId);
280 		
281 		AnyObjectId fusedTreeId = ExternalModuleUtils.createFusedTree(objectReader, inserter, rw, aggregateCommit, Arrays.asList(new ExternalModuleInfo[] {branch1Externals, branch2Externals}));
282 		
283 		TreeWalk tw = new TreeWalk (objectReader);
284 		
285 		tw.setRecursive(true);
286 		
287 		tw.addTree(fusedTreeId);
288 		
289 		Assert.assertEquals(true, findPath (tw, "branch1/" + branch1FilePath));
290 		
291 		tw.reset(fusedTreeId);
292 		
293 		Assert.assertEquals(true, findPath (tw, "branch2/" + branch1FilePath));
294 		
295 		tw.reset(fusedTreeId);
296 		
297 		tw.setFilter(PathFilter.create(aggregate_file_path));
298 		
299 		Assert.assertEquals(true, findPath (tw, aggregate_file_path));
300 		
301 		tw.release();
302 		
303 		rw.release();
304 		
305 		objectReader.release();
306 		
307 	}
308 
309 	private boolean findPath(TreeWalk tw, String targetPath) throws MissingObjectException, IncorrectObjectTypeException, CorruptObjectException, IOException {
310 		
311 		while (tw.next()) {
312 			
313 			String candidatePath = tw.getPathString();
314 			
315 			if (candidatePath.equals(targetPath))
316 				return true;
317 		}
318 		return false;
319 	}
320 }