/* * Copyright (C) 2013, Robin Rosenberg * and other copyright owners as documented in the project's IP log. * * This program and the accompanying materials are made available * under the terms of the Eclipse Distribution License v1.0 which * accompanies this distribution, is reproduced below, and is * available at http://www.eclipse.org/org/documents/edl-v10.php * * All rights reserved. * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * - Neither the name of the Eclipse Foundation, Inc. nor the * names of its contributors may be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.eclipse.jgit.treewalk.filter; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.jgit.dircache.DirCache; import org.eclipse.jgit.dircache.DirCacheEditor; import org.eclipse.jgit.dircache.DirCacheEntry; import org.eclipse.jgit.dircache.DirCacheIterator; import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.errors.StopWalkException; import org.eclipse.jgit.lib.FileMode; import org.eclipse.jgit.lib.ObjectReader; import org.eclipse.jgit.lib.Sets; import org.eclipse.jgit.treewalk.TreeWalk; import org.junit.Before; import org.junit.Test; public class PathFilterGroupTest { private TreeFilter filter; private Map<String, TreeFilter> singles; @Before public void setup() { // @formatter:off String[] paths = new String[] { "/a", // never match "/a/b", // never match "a", "b/c", "c/d/e", "c/d/f", "d/e/f/g", "d/e/f/g.x" }; // @formatter:on filter = PathFilterGroup.createFromStrings(paths); singles = new HashMap<>(); for (String path : paths) { singles.put(path, PathFilterGroup.createFromStrings(path)); } } @Test public void testExact() throws MissingObjectException, IncorrectObjectTypeException, IOException { assertMatches(Sets.of("a"), fakeWalk("a")); assertMatches(Sets.of("b/c"), fakeWalk("b/c")); assertMatches(Sets.of("c/d/e"), fakeWalk("c/d/e")); assertMatches(Sets.of("c/d/f"), fakeWalk("c/d/f")); assertMatches(Sets.of("d/e/f/g"), fakeWalk("d/e/f/g")); assertMatches(Sets.of("d/e/f/g.x"), fakeWalk("d/e/f/g.x")); } @Test public void testNoMatchButClose() throws MissingObjectException, IncorrectObjectTypeException, IOException { assertNoMatches(fakeWalk("a+")); assertNoMatches(fakeWalk("b+/c")); assertNoMatches(fakeWalk("c+/d/e")); assertNoMatches(fakeWalk("c+/d/f")); assertNoMatches(fakeWalk("c/d.a")); assertNoMatches(fakeWalk("d+/e/f/g")); } @Test public void testJustCommonPrefixIsNotMatch() throws MissingObjectException, IncorrectObjectTypeException, IOException { assertNoMatches(fakeWalk("b/a")); assertNoMatches(fakeWalk("b/d")); assertNoMatches(fakeWalk("c/d/a")); assertNoMatches(fakeWalk("d/e/e")); assertNoMatches(fakeWalk("d/e/f/g.y")); } @Test public void testKeyIsPrefixOfFilter() throws MissingObjectException, IncorrectObjectTypeException, IOException { assertMatches(Sets.of("b/c"), fakeWalkAtSubtree("b")); assertMatches(Sets.of("c/d/e", "c/d/f"), fakeWalkAtSubtree("c/d")); assertMatches(Sets.of("c/d/e", "c/d/f"), fakeWalkAtSubtree("c")); assertMatches(Sets.of("d/e/f/g", "d/e/f/g.x"), fakeWalkAtSubtree("d/e/f")); assertMatches(Sets.of("d/e/f/g", "d/e/f/g.x"), fakeWalkAtSubtree("d/e")); assertMatches(Sets.of("d/e/f/g", "d/e/f/g.x"), fakeWalkAtSubtree("d")); assertNoMatches(fakeWalk("b")); assertNoMatches(fakeWalk("c/d")); assertNoMatches(fakeWalk("c")); assertNoMatches(fakeWalk("d/e/f")); assertNoMatches(fakeWalk("d/e")); assertNoMatches(fakeWalk("d")); } @Test public void testFilterIsPrefixOfKey() throws MissingObjectException, IncorrectObjectTypeException, IOException { assertMatches(Sets.of("a"), fakeWalk("a/b")); assertMatches(Sets.of("b/c"), fakeWalk("b/c/d")); assertMatches(Sets.of("c/d/e"), fakeWalk("c/d/e/f")); assertMatches(Sets.of("c/d/f"), fakeWalk("c/d/f/g")); assertMatches(Sets.of("d/e/f/g"), fakeWalk("d/e/f/g/h")); assertMatches(Sets.of("d/e/f/g"), fakeWalk("d/e/f/g/y")); assertMatches(Sets.of("d/e/f/g.x"), fakeWalk("d/e/f/g.x/h")); } @Test public void testLongPaths() throws MissingObjectException, IncorrectObjectTypeException, IOException { TreeFilter longPathFilter = PathFilterGroup .createFromStrings( "tst/org/eclipse/jgit/treewalk/filter/PathFilterGroupTest.java", "tst/org/eclipse/jgit/treewalk/filter/PathFilterGroupTest2.java"); assertFalse(longPathFilter .include(fakeWalk("tst/org/eclipse/jgit/treewalk/FileTreeIteratorTest.java"))); assertFalse(longPathFilter.include(fakeWalk("tst/a-other-in-same"))); assertFalse(longPathFilter.include(fakeWalk("a-nothing-in-common"))); } @Test public void testStopWalk() throws MissingObjectException, IncorrectObjectTypeException, IOException { // Obvious filter.include(fakeWalk("d/e/f/f")); // Obvious try { filter.include(fakeWalk("de")); fail("StopWalkException expected"); } catch (StopWalkException e) { // good } // less obvious due to git sorting order filter.include(fakeWalk("d-")); // less obvious due to git sorting order try { filter.include(fakeWalk("d0")); fail("StopWalkException expected"); } catch (StopWalkException e) { // good } // less obvious #2 due to git sorting order filter.include(fakeWalk("d/e/f/g/h.txt")); // listed before g/y, so can't StopWalk here filter.include(fakeWalk("d/e/f/g.y")); singles.get("d/e/f/g").include(fakeWalk("d/e/f/g.y")); // non-ascii try { filter.include(fakeWalk("\u00C0")); fail("StopWalkException expected"); } catch (StopWalkException e) { // good } } private void assertNoMatches(TreeWalk tw) throws MissingObjectException, IncorrectObjectTypeException, IOException { assertMatches(Sets.<String> of(), tw); } private void assertMatches(Set<String> expect, TreeWalk tw) throws MissingObjectException, IncorrectObjectTypeException, IOException { List<String> actual = new ArrayList<>(); for (String path : singles.keySet()) { if (includes(singles.get(path), tw)) { actual.add(path); } } String[] e = expect.toArray(new String[expect.size()]); String[] a = actual.toArray(new String[actual.size()]); Arrays.sort(e); Arrays.sort(a); assertArrayEquals(e, a); if (expect.isEmpty()) { assertFalse(includes(filter, tw)); } else { assertTrue(includes(filter, tw)); } } private static boolean includes(TreeFilter f, TreeWalk tw) throws MissingObjectException, IncorrectObjectTypeException, IOException { try { return f.include(tw); } catch (StopWalkException e) { return false; } } TreeWalk fakeWalk(final String path) throws IOException { DirCache dc = DirCache.newInCore(); DirCacheEditor dce = dc.editor(); dce.add(new DirCacheEditor.PathEdit(path) { public void apply(DirCacheEntry ent) { ent.setFileMode(FileMode.REGULAR_FILE); } }); dce.finish(); TreeWalk ret = new TreeWalk((ObjectReader) null); ret.reset(); ret.setRecursive(true); ret.addTree(new DirCacheIterator(dc)); ret.next(); return ret; } TreeWalk fakeWalkAtSubtree(final String path) throws IOException { DirCache dc = DirCache.newInCore(); DirCacheEditor dce = dc.editor(); dce.add(new DirCacheEditor.PathEdit(path + "/README") { public void apply(DirCacheEntry ent) { ent.setFileMode(FileMode.REGULAR_FILE); } }); dce.finish(); TreeWalk ret = new TreeWalk((ObjectReader) null); ret.addTree(new DirCacheIterator(dc)); ret.next(); while (!path.equals(ret.getPathString())) { if (ret.isSubtree()) { ret.enterSubtree(); } ret.next(); } return ret; } }