View Javadoc
1   /*
2    * Copyright (C) 2017 Magnus Vigerlöf (magnus.vigerlof@gmail.com)
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.treewalk.filter;
44  
45  import static org.junit.Assert.assertEquals;
46  
47  import java.io.IOException;
48  import java.util.ArrayList;
49  import java.util.Arrays;
50  import java.util.Collections;
51  import java.util.List;
52  
53  import org.eclipse.jgit.dircache.DirCache;
54  import org.eclipse.jgit.dircache.DirCacheBuilder;
55  import org.eclipse.jgit.dircache.DirCacheEntry;
56  import org.eclipse.jgit.junit.RepositoryTestCase;
57  import org.eclipse.jgit.lib.FileMode;
58  import org.eclipse.jgit.lib.ObjectId;
59  import org.eclipse.jgit.lib.ObjectInserter;
60  import org.eclipse.jgit.treewalk.TreeWalk;
61  import org.junit.Before;
62  import org.junit.Test;
63  
64  public class PathFilterLogicTest extends RepositoryTestCase {
65  
66  	private ObjectId treeId;
67  
68  	@Before
69  	public void setup() throws IOException {
70  		String[] paths = new String[] {
71  				"a.txt",
72  				"sub1.txt",
73  				"sub1/suba/a.txt",
74  				"sub1/subb/b.txt",
75  				"sub2/suba/a.txt"
76  		};
77  		treeId = createTree(paths);
78  	}
79  
80  	@Test
81  	public void testSinglePath() throws IOException {
82  		List<String> expected = Arrays.asList("sub1/suba/a.txt",
83  				"sub1/subb/b.txt");
84  
85  		TreeFilter tf = PathFilter.create("sub1");
86  		List<String> paths = getMatchingPaths(treeId, tf);
87  
88  		assertEquals(expected, paths);
89  	}
90  
91  	@Test
92  	public void testSingleSubPath() throws IOException {
93  		List<String> expected = Collections.singletonList("sub1/suba/a.txt");
94  
95  		TreeFilter tf = PathFilter.create("sub1/suba");
96  		List<String> paths = getMatchingPaths(treeId, tf);
97  
98  		assertEquals(expected, paths);
99  	}
100 
101 	@Test
102 	public void testSinglePathNegate() throws IOException {
103 		List<String> expected = Arrays.asList("a.txt", "sub1.txt",
104 				"sub2/suba/a.txt");
105 
106 		TreeFilter tf = PathFilter.create("sub1").negate();
107 		List<String> paths = getMatchingPaths(treeId, tf);
108 
109 		assertEquals(expected, paths);
110 	}
111 
112 	@Test
113 	public void testSingleSubPathNegate() throws IOException {
114 		List<String> expected = Arrays.asList("a.txt", "sub1.txt",
115 				"sub1/subb/b.txt", "sub2/suba/a.txt");
116 
117 		TreeFilter tf = PathFilter.create("sub1/suba").negate();
118 		List<String> paths = getMatchingPaths(treeId, tf);
119 
120 		assertEquals(expected, paths);
121 	}
122 
123 	@Test
124 	public void testOrMultiTwoPath() throws IOException {
125 		List<String> expected = Arrays.asList("sub1/suba/a.txt",
126 				"sub1/subb/b.txt", "sub2/suba/a.txt");
127 
128 		TreeFilter[] tf = new TreeFilter[] {PathFilter.create("sub1"),
129 				PathFilter.create("sub2")};
130 		List<String> paths = getMatchingPaths(treeId, OrTreeFilter.create(tf));
131 
132 		assertEquals(expected, paths);
133 	}
134 
135 	@Test
136 	public void testOrMultiThreePath() throws IOException {
137 		List<String> expected = Arrays.asList("sub1.txt", "sub1/suba/a.txt",
138 				"sub1/subb/b.txt", "sub2/suba/a.txt");
139 
140 		TreeFilter[] tf = new TreeFilter[] {PathFilter.create("sub1"),
141 				PathFilter.create("sub2"), PathFilter.create("sub1.txt")};
142 		List<String> paths = getMatchingPaths(treeId, OrTreeFilter.create(tf));
143 
144 		assertEquals(expected, paths);
145 	}
146 
147 	@Test
148 	public void testOrMultiTwoSubPath() throws IOException {
149 		List<String> expected = Arrays.asList("sub1/subb/b.txt",
150 				"sub2/suba/a.txt");
151 
152 		TreeFilter[] tf = new TreeFilter[] {PathFilter.create("sub1/subb"),
153 				PathFilter.create("sub2/suba")};
154 		List<String> paths = getMatchingPaths(treeId, OrTreeFilter.create(tf));
155 
156 		assertEquals(expected, paths);
157 	}
158 
159 	@Test
160 	public void testOrMultiTwoMixSubPath() throws IOException {
161 		List<String> expected = Arrays.asList("sub1/subb/b.txt",
162 				"sub2/suba/a.txt");
163 
164 		TreeFilter[] tf = new TreeFilter[] {PathFilter.create("sub1/subb"),
165 				PathFilter.create("sub2")};
166 		List<String> paths = getMatchingPaths(treeId, OrTreeFilter.create(tf));
167 
168 		assertEquals(expected, paths);
169 	}
170 
171 	@Test
172 	public void testOrMultiTwoMixSubPathNegate() throws IOException {
173 		List<String> expected = Arrays.asList("a.txt", "sub1.txt",
174 				"sub1/suba/a.txt", "sub2/suba/a.txt");
175 
176 		TreeFilter[] tf = new TreeFilter[] {PathFilter.create("sub1").negate(),
177 				PathFilter.create("sub1/suba")};
178 		List<String> paths = getMatchingPaths(treeId, OrTreeFilter.create(tf));
179 
180 		assertEquals(expected, paths);
181 	}
182 
183 	@Test
184 	public void testOrMultiThreeMixSubPathNegate() throws IOException {
185 		List<String> expected = Arrays.asList("a.txt", "sub1.txt",
186 				"sub1/suba/a.txt", "sub2/suba/a.txt");
187 
188 		TreeFilter[] tf = new TreeFilter[] {PathFilter.create("sub1").negate(),
189 				PathFilter.create("sub1/suba"), PathFilter.create("no/path")};
190 		List<String> paths = getMatchingPaths(treeId, OrTreeFilter.create(tf));
191 
192 		assertEquals(expected, paths);
193 	}
194 
195 	@Test
196 	public void testPatternParentFileMatch() throws IOException {
197 		List<String> expected = Collections.emptyList();
198 
199 		TreeFilter tf = PathFilter.create("a.txt/test/path");
200 		List<String> paths = getMatchingPaths(treeId, tf);
201 
202 		assertEquals(expected, paths);
203 	}
204 
205 	@Test
206 	public void testAndMultiPath() throws IOException {
207 		List<String> expected = Collections.emptyList();
208 
209 		TreeFilter[] tf = new TreeFilter[] {PathFilter.create("sub1"),
210 				PathFilter.create("sub2")};
211 		List<String> paths = getMatchingPaths(treeId, AndTreeFilter.create(tf));
212 
213 		assertEquals(expected, paths);
214 	}
215 
216 	@Test
217 	public void testAndMultiPathNegate() throws IOException {
218 		List<String> expected = Arrays.asList("sub1/suba/a.txt",
219 				"sub1/subb/b.txt");
220 
221 		TreeFilter[] tf = new TreeFilter[] {PathFilter.create("sub1"),
222 				PathFilter.create("sub2").negate()};
223 		List<String> paths = getMatchingPaths(treeId, AndTreeFilter.create(tf));
224 
225 		assertEquals(expected, paths);
226 	}
227 
228 	@Test
229 	public void testAndMultiSubPathDualNegate() throws IOException {
230 		List<String> expected = Arrays.asList("a.txt", "sub1.txt",
231 				"sub1/subb/b.txt");
232 
233 		TreeFilter[] tf = new TreeFilter[] {PathFilter.create("sub1/suba").negate(),
234 				PathFilter.create("sub2").negate()};
235 		List<String> paths = getMatchingPaths(treeId, AndTreeFilter.create(tf));
236 
237 		assertEquals(expected, paths);
238 	}
239 
240 	@Test
241 	public void testAndMultiSubPath() throws IOException {
242 		List<String> expected = Collections.emptyList();
243 
244 		TreeFilter[] tf = new TreeFilter[] {PathFilter.create("sub1"),
245 				PathFilter.create("sub2/suba")};
246 		List<String> paths = getMatchingPaths(treeId, AndTreeFilter.create(tf));
247 
248 		assertEquals(expected, paths);
249 	}
250 
251 	@Test
252 	public void testAndMultiSubPathNegate() throws IOException {
253 		List<String> expected = Collections.singletonList("sub1/subb/b.txt");
254 
255 		TreeFilter[] tf = new TreeFilter[] {PathFilter.create("sub1"),
256 				PathFilter.create("sub1/suba").negate()};
257 		List<String> paths = getMatchingPaths(treeId, AndTreeFilter.create(tf));
258 
259 		assertEquals(expected, paths);
260 	}
261 
262 	@Test
263 	public void testAndMultiThreeSubPathNegate() throws IOException {
264 		List<String> expected = Collections.singletonList("sub1/subb/b.txt");
265 
266 		TreeFilter[] tf = new TreeFilter[]{PathFilter.create("sub1"),
267 				PathFilter.create("sub1/suba").negate(),
268 				PathFilter.create("no/path").negate()};
269 		List<String> paths = getMatchingPaths(treeId, AndTreeFilter.create(tf));
270 
271 		assertEquals(expected, paths);
272 	}
273 
274 	@Test
275 	public void testTopAndMultiPathDualNegate() throws IOException {
276 		List<String> expected = Arrays.asList("a.txt", "sub1.txt");
277 
278 		TreeFilter[] tf = new TreeFilter[] {PathFilter.create("sub1").negate(),
279 				PathFilter.create("sub2").negate()};
280 		List<String> paths = getMatchingPathsFlat(treeId, AndTreeFilter.create(tf));
281 
282 		assertEquals(expected, paths);
283 	}
284 
285 	@Test
286 	public void testTopAndMultiSubPathDualNegate() throws IOException {
287 		List<String> expected = Arrays.asList("a.txt", "sub1.txt", "sub1");
288 
289 		// Filter on 'sub1/suba' is kind of silly for a non-recursive walk.
290 		// The result is interesting though as the 'sub1' path should be
291 		// returned, due to the fact that there may be hits once the pattern
292 		// is tested with one of the leaf paths.
293 		TreeFilter[] tf = new TreeFilter[] {PathFilter.create("sub1/suba").negate(),
294 				PathFilter.create("sub2").negate()};
295 		List<String> paths = getMatchingPathsFlat(treeId, AndTreeFilter.create(tf));
296 
297 		assertEquals(expected, paths);
298 	}
299 
300 	@Test
301 	public void testTopOrMultiPathDual() throws IOException {
302 		List<String> expected = Arrays.asList("sub1.txt", "sub2");
303 
304 		TreeFilter[] tf = new TreeFilter[] {PathFilter.create("sub1.txt"),
305 				PathFilter.create("sub2")};
306 		List<String> paths = getMatchingPathsFlat(treeId, OrTreeFilter.create(tf));
307 
308 		assertEquals(expected, paths);
309 	}
310 
311 	@Test
312 	public void testTopNotPath() throws IOException {
313 		List<String> expected = Arrays.asList("a.txt", "sub1.txt", "sub2");
314 
315 		TreeFilter tf = PathFilter.create("sub1");
316 		List<String> paths = getMatchingPathsFlat(treeId, NotTreeFilter.create(tf));
317 
318 		assertEquals(expected, paths);
319 	}
320 
321 	private List<String> getMatchingPaths(final ObjectId objId,
322 			TreeFilter tf) throws IOException {
323 		return getMatchingPaths(objId, tf, true);
324 	}
325 
326 	private List<String> getMatchingPathsFlat(final ObjectId objId,
327 			TreeFilter tf) throws IOException {
328 		return getMatchingPaths(objId, tf, false);
329 	}
330 
331 	private List<String> getMatchingPaths(final ObjectId objId,
332 			TreeFilter tf, boolean recursive) throws IOException {
333 		try (TreeWalk tw = new TreeWalk(db)) {
334 			tw.setFilter(tf);
335 			tw.setRecursive(recursive);
336 			tw.addTree(objId);
337 
338 			List<String> paths = new ArrayList<>();
339 			while (tw.next()) {
340 				paths.add(tw.getPathString());
341 			}
342 			return paths;
343 		}
344 	}
345 
346 	private ObjectId createTree(String... paths) throws IOException {
347 		final ObjectInserter odi = db.newObjectInserter();
348 		final DirCache dc = db.readDirCache();
349 		final DirCacheBuilder builder = dc.builder();
350 		for (String path : paths) {
351 			DirCacheEntry entry = createEntry(path, FileMode.REGULAR_FILE);
352 			builder.add(entry);
353 		}
354 		builder.finish();
355 		final ObjectId objId = dc.writeTree(odi);
356 		odi.flush();
357 		return objId;
358 	}
359 }
360