View Javadoc
1   /*
2    * Copyright (C) 2013, Robin Rosenberg and others
3    *
4    * This program and the accompanying materials are made available under the
5    * terms of the Eclipse Distribution License v. 1.0 which is available at
6    * https://www.eclipse.org/org/documents/edl-v10.php.
7    *
8    * SPDX-License-Identifier: BSD-3-Clause
9    */
10  
11  package org.eclipse.jgit.treewalk.filter;
12  
13  import static org.junit.Assert.assertArrayEquals;
14  import static org.junit.Assert.assertFalse;
15  import static org.junit.Assert.assertTrue;
16  import static org.junit.Assert.fail;
17  
18  import java.io.IOException;
19  import java.util.ArrayList;
20  import java.util.Arrays;
21  import java.util.HashMap;
22  import java.util.List;
23  import java.util.Map;
24  import java.util.Set;
25  
26  import org.eclipse.jgit.dircache.DirCache;
27  import org.eclipse.jgit.dircache.DirCacheEditor;
28  import org.eclipse.jgit.dircache.DirCacheEntry;
29  import org.eclipse.jgit.dircache.DirCacheIterator;
30  import org.eclipse.jgit.errors.IncorrectObjectTypeException;
31  import org.eclipse.jgit.errors.MissingObjectException;
32  import org.eclipse.jgit.errors.StopWalkException;
33  import org.eclipse.jgit.lib.FileMode;
34  import org.eclipse.jgit.lib.ObjectReader;
35  import org.eclipse.jgit.lib.Sets;
36  import org.eclipse.jgit.treewalk.TreeWalk;
37  import org.junit.Before;
38  import org.junit.Test;
39  
40  public class PathFilterGroupTest {
41  
42  	private TreeFilter filter;
43  
44  	private Map<String, TreeFilter> singles;
45  
46  	@Before
47  	public void setup() {
48  		// @formatter:off
49  		String[] paths = new String[] {
50  				"/a", // never match
51  				"/a/b", // never match
52  				"a",
53  				"b/c",
54  				"c/d/e",
55  				"c/d/f",
56  				"d/e/f/g",
57  				"d/e/f/g.x"
58  				};
59  		// @formatter:on
60  		filter = PathFilterGroup.createFromStrings(paths);
61  		singles = new HashMap<>();
62  		for (String path : paths) {
63  			singles.put(path, PathFilterGroup.createFromStrings(path));
64  		}
65  	}
66  
67  	@Test
68  	public void testExact() throws MissingObjectException,
69  			IncorrectObjectTypeException, IOException {
70  		assertMatches(Sets.of("a"), fakeWalk("a"));
71  		assertMatches(Sets.of("b/c"), fakeWalk("b/c"));
72  		assertMatches(Sets.of("c/d/e"), fakeWalk("c/d/e"));
73  		assertMatches(Sets.of("c/d/f"), fakeWalk("c/d/f"));
74  		assertMatches(Sets.of("d/e/f/g"), fakeWalk("d/e/f/g"));
75  		assertMatches(Sets.of("d/e/f/g.x"), fakeWalk("d/e/f/g.x"));
76  	}
77  
78  	@Test
79  	public void testNoMatchButClose() throws MissingObjectException,
80  			IncorrectObjectTypeException, IOException {
81  		assertNoMatches(fakeWalk("a+"));
82  		assertNoMatches(fakeWalk("b+/c"));
83  		assertNoMatches(fakeWalk("c+/d/e"));
84  		assertNoMatches(fakeWalk("c+/d/f"));
85  		assertNoMatches(fakeWalk("c/d.a"));
86  		assertNoMatches(fakeWalk("d+/e/f/g"));
87  	}
88  
89  	@Test
90  	public void testJustCommonPrefixIsNotMatch() throws MissingObjectException,
91  			IncorrectObjectTypeException, IOException {
92  		assertNoMatches(fakeWalk("b/a"));
93  		assertNoMatches(fakeWalk("b/d"));
94  		assertNoMatches(fakeWalk("c/d/a"));
95  		assertNoMatches(fakeWalk("d/e/e"));
96  		assertNoMatches(fakeWalk("d/e/f/g.y"));
97  	}
98  
99  	@Test
100 	public void testKeyIsPrefixOfFilter() throws MissingObjectException,
101 			IncorrectObjectTypeException, IOException {
102 		assertMatches(Sets.of("b/c"), fakeWalkAtSubtree("b"));
103 		assertMatches(Sets.of("c/d/e", "c/d/f"), fakeWalkAtSubtree("c/d"));
104 		assertMatches(Sets.of("c/d/e", "c/d/f"), fakeWalkAtSubtree("c"));
105 		assertMatches(Sets.of("d/e/f/g", "d/e/f/g.x"),
106 				fakeWalkAtSubtree("d/e/f"));
107 		assertMatches(Sets.of("d/e/f/g", "d/e/f/g.x"),
108 				fakeWalkAtSubtree("d/e"));
109 		assertMatches(Sets.of("d/e/f/g", "d/e/f/g.x"), fakeWalkAtSubtree("d"));
110 
111 		assertNoMatches(fakeWalk("b"));
112 		assertNoMatches(fakeWalk("c/d"));
113 		assertNoMatches(fakeWalk("c"));
114 		assertNoMatches(fakeWalk("d/e/f"));
115 		assertNoMatches(fakeWalk("d/e"));
116 		assertNoMatches(fakeWalk("d"));
117 
118 	}
119 
120 	@Test
121 	public void testFilterIsPrefixOfKey() throws MissingObjectException,
122 			IncorrectObjectTypeException, IOException {
123 		assertMatches(Sets.of("a"), fakeWalk("a/b"));
124 		assertMatches(Sets.of("b/c"), fakeWalk("b/c/d"));
125 		assertMatches(Sets.of("c/d/e"), fakeWalk("c/d/e/f"));
126 		assertMatches(Sets.of("c/d/f"), fakeWalk("c/d/f/g"));
127 		assertMatches(Sets.of("d/e/f/g"), fakeWalk("d/e/f/g/h"));
128 		assertMatches(Sets.of("d/e/f/g"), fakeWalk("d/e/f/g/y"));
129 		assertMatches(Sets.of("d/e/f/g.x"), fakeWalk("d/e/f/g.x/h"));
130 	}
131 
132 	@Test
133 	public void testLongPaths() throws MissingObjectException,
134 			IncorrectObjectTypeException, IOException {
135 		TreeFilter longPathFilter = PathFilterGroup
136 				.createFromStrings(
137 						"tst/org/eclipse/jgit/treewalk/filter/PathFilterGroupTest.java",
138 						"tst/org/eclipse/jgit/treewalk/filter/PathFilterGroupTest2.java");
139 		assertFalse(longPathFilter
140 				.include(fakeWalk("tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java")));
141 		assertFalse(longPathFilter.include(fakeWalk("tst/a-other-in-same")));
142 		assertFalse(longPathFilter.include(fakeWalk("a-nothing-in-common")));
143 	}
144 
145 	@Test
146 	public void testStopWalk() throws MissingObjectException,
147 			IncorrectObjectTypeException, IOException {
148 		// Obvious
149 		filter.include(fakeWalk("d/e/f/f"));
150 
151 		// Obvious
152 		try {
153 			filter.include(fakeWalk("de"));
154 			fail("StopWalkException expected");
155 		} catch (StopWalkException e) {
156 			// good
157 		}
158 
159 		// less obvious due to git sorting order
160 		filter.include(fakeWalk("d-"));
161 
162 		// less obvious due to git sorting order
163 		try {
164 			filter.include(fakeWalk("d0"));
165 			fail("StopWalkException expected");
166 		} catch (StopWalkException e) {
167 			// good
168 		}
169 
170 		// less obvious #2 due to git sorting order
171 		filter.include(fakeWalk("d/e/f/g/h.txt"));
172 
173 		// listed before g/y, so can't StopWalk here
174 		filter.include(fakeWalk("d/e/f/g.y"));
175 		singles.get("d/e/f/g").include(fakeWalk("d/e/f/g.y"));
176 
177 		// non-ascii
178 		try {
179 			filter.include(fakeWalk("\u00C0"));
180 			fail("StopWalkException expected");
181 		} catch (StopWalkException e) {
182 			// good
183 		}
184 	}
185 
186 	private void assertNoMatches(TreeWalk tw) throws MissingObjectException,
187 			IncorrectObjectTypeException, IOException {
188 		assertMatches(Sets.<String> of(), tw);
189 	}
190 
191 	private void assertMatches(Set<String> expect, TreeWalk tw)
192 			throws MissingObjectException, IncorrectObjectTypeException,
193 			IOException {
194 		List<String> actual = new ArrayList<>();
195 		for (String path : singles.keySet()) {
196 			if (includes(singles.get(path), tw)) {
197 				actual.add(path);
198 			}
199 		}
200 
201 		String[] e = expect.toArray(new String[0]);
202 		String[] a = actual.toArray(new String[0]);
203 		Arrays.sort(e);
204 		Arrays.sort(a);
205 		assertArrayEquals(e, a);
206 
207 		if (expect.isEmpty()) {
208 			assertFalse(includes(filter, tw));
209 		} else {
210 			assertTrue(includes(filter, tw));
211 		}
212 	}
213 
214 	private static boolean includes(TreeFilter f, TreeWalk tw)
215 			throws MissingObjectException, IncorrectObjectTypeException,
216 			IOException {
217 		try {
218 			return f.include(tw);
219 		} catch (StopWalkException e) {
220 			return false;
221 		}
222 	}
223 
224 	TreeWalk fakeWalk(String path) throws IOException {
225 		DirCache dc = DirCache.newInCore();
226 		DirCacheEditor dce = dc.editor();
227 		dce.add(new DirCacheEditor.PathEdit(path) {
228 
229 			@Override
230 			public void apply(DirCacheEntry ent) {
231 				ent.setFileMode(FileMode.REGULAR_FILE);
232 			}
233 		});
234 		dce.finish();
235 
236 		TreeWalk ret = new TreeWalk((ObjectReader) null);
237 		ret.reset();
238 		ret.setRecursive(true);
239 		ret.addTree(new DirCacheIterator(dc));
240 		ret.next();
241 		return ret;
242 	}
243 
244 	TreeWalk fakeWalkAtSubtree(String path) throws IOException {
245 		DirCache dc = DirCache.newInCore();
246 		DirCacheEditor dce = dc.editor();
247 		dce.add(new DirCacheEditor.PathEdit(path + "/README") {
248 			@Override
249 			public void apply(DirCacheEntry ent) {
250 				ent.setFileMode(FileMode.REGULAR_FILE);
251 			}
252 		});
253 		dce.finish();
254 
255 		TreeWalk ret = new TreeWalk((ObjectReader) null);
256 		ret.addTree(new DirCacheIterator(dc));
257 		ret.next();
258 		while (!path.equals(ret.getPathString())) {
259 			if (ret.isSubtree()) {
260 				ret.enterSubtree();
261 			}
262 			ret.next();
263 		}
264 		return ret;
265 	}
266 }