View Javadoc
1   /*
2    * Copyright (C) 2013, Robin Rosenberg
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  
44  package org.eclipse.jgit.treewalk.filter;
45  
46  import static org.junit.Assert.assertArrayEquals;
47  import static org.junit.Assert.assertFalse;
48  import static org.junit.Assert.assertTrue;
49  import static org.junit.Assert.fail;
50  
51  import java.io.IOException;
52  import java.util.ArrayList;
53  import java.util.Arrays;
54  import java.util.HashMap;
55  import java.util.List;
56  import java.util.Map;
57  import java.util.Set;
58  
59  import org.eclipse.jgit.dircache.DirCache;
60  import org.eclipse.jgit.dircache.DirCacheEditor;
61  import org.eclipse.jgit.dircache.DirCacheEntry;
62  import org.eclipse.jgit.dircache.DirCacheIterator;
63  import org.eclipse.jgit.errors.IncorrectObjectTypeException;
64  import org.eclipse.jgit.errors.MissingObjectException;
65  import org.eclipse.jgit.errors.StopWalkException;
66  import org.eclipse.jgit.lib.FileMode;
67  import org.eclipse.jgit.lib.ObjectReader;
68  import org.eclipse.jgit.lib.Sets;
69  import org.eclipse.jgit.treewalk.TreeWalk;
70  import org.junit.Before;
71  import org.junit.Test;
72  
73  public class PathFilterGroupTest {
74  
75  	private TreeFilter filter;
76  
77  	private Map<String, TreeFilter> singles;
78  
79  	@Before
80  	public void setup() {
81  		// @formatter:off
82  		String[] paths = new String[] {
83  				"/a", // never match
84  				"/a/b", // never match
85  				"a",
86  				"b/c",
87  				"c/d/e",
88  				"c/d/f",
89  				"d/e/f/g",
90  				"d/e/f/g.x"
91  				};
92  		// @formatter:on
93  		filter = PathFilterGroup.createFromStrings(paths);
94  		singles = new HashMap<>();
95  		for (String path : paths) {
96  			singles.put(path, PathFilterGroup.createFromStrings(path));
97  		}
98  	}
99  
100 	@Test
101 	public void testExact() throws MissingObjectException,
102 			IncorrectObjectTypeException, IOException {
103 		assertMatches(Sets.of("a"), fakeWalk("a"));
104 		assertMatches(Sets.of("b/c"), fakeWalk("b/c"));
105 		assertMatches(Sets.of("c/d/e"), fakeWalk("c/d/e"));
106 		assertMatches(Sets.of("c/d/f"), fakeWalk("c/d/f"));
107 		assertMatches(Sets.of("d/e/f/g"), fakeWalk("d/e/f/g"));
108 		assertMatches(Sets.of("d/e/f/g.x"), fakeWalk("d/e/f/g.x"));
109 	}
110 
111 	@Test
112 	public void testNoMatchButClose() throws MissingObjectException,
113 			IncorrectObjectTypeException, IOException {
114 		assertNoMatches(fakeWalk("a+"));
115 		assertNoMatches(fakeWalk("b+/c"));
116 		assertNoMatches(fakeWalk("c+/d/e"));
117 		assertNoMatches(fakeWalk("c+/d/f"));
118 		assertNoMatches(fakeWalk("c/d.a"));
119 		assertNoMatches(fakeWalk("d+/e/f/g"));
120 	}
121 
122 	@Test
123 	public void testJustCommonPrefixIsNotMatch() throws MissingObjectException,
124 			IncorrectObjectTypeException, IOException {
125 		assertNoMatches(fakeWalk("b/a"));
126 		assertNoMatches(fakeWalk("b/d"));
127 		assertNoMatches(fakeWalk("c/d/a"));
128 		assertNoMatches(fakeWalk("d/e/e"));
129 		assertNoMatches(fakeWalk("d/e/f/g.y"));
130 	}
131 
132 	@Test
133 	public void testKeyIsPrefixOfFilter() throws MissingObjectException,
134 			IncorrectObjectTypeException, IOException {
135 		assertMatches(Sets.of("b/c"), fakeWalkAtSubtree("b"));
136 		assertMatches(Sets.of("c/d/e", "c/d/f"), fakeWalkAtSubtree("c/d"));
137 		assertMatches(Sets.of("c/d/e", "c/d/f"), fakeWalkAtSubtree("c"));
138 		assertMatches(Sets.of("d/e/f/g", "d/e/f/g.x"),
139 				fakeWalkAtSubtree("d/e/f"));
140 		assertMatches(Sets.of("d/e/f/g", "d/e/f/g.x"),
141 				fakeWalkAtSubtree("d/e"));
142 		assertMatches(Sets.of("d/e/f/g", "d/e/f/g.x"), fakeWalkAtSubtree("d"));
143 
144 		assertNoMatches(fakeWalk("b"));
145 		assertNoMatches(fakeWalk("c/d"));
146 		assertNoMatches(fakeWalk("c"));
147 		assertNoMatches(fakeWalk("d/e/f"));
148 		assertNoMatches(fakeWalk("d/e"));
149 		assertNoMatches(fakeWalk("d"));
150 
151 	}
152 
153 	@Test
154 	public void testFilterIsPrefixOfKey() throws MissingObjectException,
155 			IncorrectObjectTypeException, IOException {
156 		assertMatches(Sets.of("a"), fakeWalk("a/b"));
157 		assertMatches(Sets.of("b/c"), fakeWalk("b/c/d"));
158 		assertMatches(Sets.of("c/d/e"), fakeWalk("c/d/e/f"));
159 		assertMatches(Sets.of("c/d/f"), fakeWalk("c/d/f/g"));
160 		assertMatches(Sets.of("d/e/f/g"), fakeWalk("d/e/f/g/h"));
161 		assertMatches(Sets.of("d/e/f/g"), fakeWalk("d/e/f/g/y"));
162 		assertMatches(Sets.of("d/e/f/g.x"), fakeWalk("d/e/f/g.x/h"));
163 	}
164 
165 	@Test
166 	public void testLongPaths() throws MissingObjectException,
167 			IncorrectObjectTypeException, IOException {
168 		TreeFilter longPathFilter = PathFilterGroup
169 				.createFromStrings(
170 						"tst/org/eclipse/jgit/treewalk/filter/PathFilterGroupTest.java",
171 						"tst/org/eclipse/jgit/treewalk/filter/PathFilterGroupTest2.java");
172 		assertFalse(longPathFilter
173 				.include(fakeWalk("tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java")));
174 		assertFalse(longPathFilter.include(fakeWalk("tst/a-other-in-same")));
175 		assertFalse(longPathFilter.include(fakeWalk("a-nothing-in-common")));
176 	}
177 
178 	@Test
179 	public void testStopWalk() throws MissingObjectException,
180 			IncorrectObjectTypeException, IOException {
181 		// Obvious
182 		filter.include(fakeWalk("d/e/f/f"));
183 
184 		// Obvious
185 		try {
186 			filter.include(fakeWalk("de"));
187 			fail("StopWalkException expected");
188 		} catch (StopWalkException e) {
189 			// good
190 		}
191 
192 		// less obvious due to git sorting order
193 		filter.include(fakeWalk("d-"));
194 
195 		// less obvious due to git sorting order
196 		try {
197 			filter.include(fakeWalk("d0"));
198 			fail("StopWalkException expected");
199 		} catch (StopWalkException e) {
200 			// good
201 		}
202 
203 		// less obvious #2 due to git sorting order
204 		filter.include(fakeWalk("d/e/f/g/h.txt"));
205 
206 		// listed before g/y, so can't StopWalk here
207 		filter.include(fakeWalk("d/e/f/g.y"));
208 		singles.get("d/e/f/g").include(fakeWalk("d/e/f/g.y"));
209 
210 		// non-ascii
211 		try {
212 			filter.include(fakeWalk("\u00C0"));
213 			fail("StopWalkException expected");
214 		} catch (StopWalkException e) {
215 			// good
216 		}
217 	}
218 
219 	private void assertNoMatches(TreeWalk tw) throws MissingObjectException,
220 			IncorrectObjectTypeException, IOException {
221 		assertMatches(Sets.<String> of(), tw);
222 	}
223 
224 	private void assertMatches(Set<String> expect, TreeWalk tw)
225 			throws MissingObjectException, IncorrectObjectTypeException,
226 			IOException {
227 		List<String> actual = new ArrayList<>();
228 		for (String path : singles.keySet()) {
229 			if (includes(singles.get(path), tw)) {
230 				actual.add(path);
231 			}
232 		}
233 
234 		String[] e = expect.toArray(new String[expect.size()]);
235 		String[] a = actual.toArray(new String[actual.size()]);
236 		Arrays.sort(e);
237 		Arrays.sort(a);
238 		assertArrayEquals(e, a);
239 
240 		if (expect.isEmpty()) {
241 			assertFalse(includes(filter, tw));
242 		} else {
243 			assertTrue(includes(filter, tw));
244 		}
245 	}
246 
247 	private static boolean includes(TreeFilter f, TreeWalk tw)
248 			throws MissingObjectException, IncorrectObjectTypeException,
249 			IOException {
250 		try {
251 			return f.include(tw);
252 		} catch (StopWalkException e) {
253 			return false;
254 		}
255 	}
256 
257 	TreeWalk fakeWalk(final String path) throws IOException {
258 		DirCache dc = DirCache.newInCore();
259 		DirCacheEditor dce = dc.editor();
260 		dce.add(new DirCacheEditor.PathEdit(path) {
261 
262 			@Override
263 			public void apply(DirCacheEntry ent) {
264 				ent.setFileMode(FileMode.REGULAR_FILE);
265 			}
266 		});
267 		dce.finish();
268 
269 		TreeWalk ret = new TreeWalk((ObjectReader) null);
270 		ret.reset();
271 		ret.setRecursive(true);
272 		ret.addTree(new DirCacheIterator(dc));
273 		ret.next();
274 		return ret;
275 	}
276 
277 	TreeWalk fakeWalkAtSubtree(final String path) throws IOException {
278 		DirCache dc = DirCache.newInCore();
279 		DirCacheEditor dce = dc.editor();
280 		dce.add(new DirCacheEditor.PathEdit(path + "/README") {
281 			@Override
282 			public void apply(DirCacheEntry ent) {
283 				ent.setFileMode(FileMode.REGULAR_FILE);
284 			}
285 		});
286 		dce.finish();
287 
288 		TreeWalk ret = new TreeWalk((ObjectReader) null);
289 		ret.addTree(new DirCacheIterator(dc));
290 		ret.next();
291 		while (!path.equals(ret.getPathString())) {
292 			if (ret.isSubtree()) {
293 				ret.enterSubtree();
294 			}
295 			ret.next();
296 		}
297 		return ret;
298 	}
299 }