| /* |
| * Copyright (C) 2014, Andrey Loskutov <loskutov@gmx.de> and others |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Distribution License v. 1.0 which is available at |
| * https://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| package org.eclipse.jgit.ignore; |
| |
| import static org.eclipse.jgit.ignore.internal.Strings.split; |
| 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 org.junit.Before; |
| import org.junit.Test; |
| |
| public class FastIgnoreRuleTest { |
| |
| private boolean pathMatch; |
| |
| @Before |
| public void setup() { |
| pathMatch = false; |
| } |
| |
| @Test |
| public void testSimpleCharClass() { |
| assertMatched("][a]", "]a"); |
| assertMatched("[a]", "a"); |
| assertMatched("][a]", "]a"); |
| assertMatched("[a]", "a/"); |
| assertMatched("[a]", "a/b"); |
| |
| assertMatched("[a]", "b/a"); |
| assertMatched("[a]", "b/a/"); |
| assertMatched("[a]", "b/a/b"); |
| |
| assertMatched("[a]", "/a/"); |
| assertMatched("[a]", "/a/b"); |
| |
| assertMatched("[a]", "c/a/b"); |
| assertMatched("[a]", "c/b/a"); |
| |
| assertMatched("/[a]", "a"); |
| assertMatched("/[a]", "a/"); |
| assertMatched("/[a]", "a/b"); |
| assertMatched("/[a]", "/a"); |
| assertMatched("/[a]", "/a/"); |
| assertMatched("/[a]", "/a/b"); |
| |
| assertMatched("[a]/", "a/"); |
| assertMatched("[a]/", "a/b"); |
| assertMatched("[a]/", "/a/"); |
| assertMatched("[a]/", "/a/b"); |
| |
| assertMatched("/[a]/", "a/"); |
| assertMatched("/[a]/", "a/b"); |
| assertMatched("/[a]/", "/a/"); |
| assertMatched("/[a]/", "/a/b"); |
| } |
| |
| @Test |
| public void testCharClass() { |
| assertMatched("[v-z]", "x"); |
| assertMatched("[v-z]", "x/"); |
| assertMatched("[v-z]", "x/b"); |
| |
| assertMatched("[v-z]", "b/x"); |
| assertMatched("[v-z]", "b/x/"); |
| assertMatched("[v-z]", "b/x/b"); |
| |
| assertMatched("[v-z]", "/x/"); |
| assertMatched("[v-z]", "/x/b"); |
| |
| assertMatched("[v-z]", "c/x/b"); |
| assertMatched("[v-z]", "c/b/x"); |
| |
| assertMatched("/[v-z]", "x"); |
| assertMatched("/[v-z]", "x/"); |
| assertMatched("/[v-z]", "x/b"); |
| assertMatched("/[v-z]", "/x"); |
| assertMatched("/[v-z]", "/x/"); |
| assertMatched("/[v-z]", "/x/b"); |
| |
| assertMatched("[v-z]/", "x/"); |
| assertMatched("[v-z]/", "x/b"); |
| assertMatched("[v-z]/", "/x/"); |
| assertMatched("[v-z]/", "/x/b"); |
| |
| assertMatched("/[v-z]/", "x/"); |
| assertMatched("/[v-z]/", "x/b"); |
| assertMatched("/[v-z]/", "/x/"); |
| assertMatched("/[v-z]/", "/x/b"); |
| } |
| |
| @Test |
| public void testTrailingSpaces() { |
| assertMatched("a ", "a"); |
| assertMatched("a/ ", "a/"); |
| assertMatched("a/ ", "a/b"); |
| assertMatched("a/\\ ", "a/ "); |
| assertNotMatched("a/\\ ", "a/"); |
| assertNotMatched("a/\\ ", "a/b"); |
| assertNotMatched("/ ", "a"); |
| } |
| |
| @Test |
| public void testAsteriskDot() { |
| assertMatched("*.a", ".a"); |
| assertMatched("*.a", "/.a"); |
| assertMatched("*.a", "a.a"); |
| assertMatched("*.a", "/b.a"); |
| assertMatched("*.a", "b.a"); |
| assertMatched("*.a", "/a/b.a"); |
| assertMatched("*.a", "/b/.a"); |
| } |
| |
| @Test |
| public void testAsteriskDotDoNotMatch() { |
| assertNotMatched("*.a", ".ab"); |
| assertNotMatched("*.a", "/.ab"); |
| assertNotMatched("*.a", "/b.ba"); |
| assertNotMatched("*.a", "a.ab"); |
| assertNotMatched("*.a", "/b.ab"); |
| assertNotMatched("*.a", "b.ab"); |
| assertNotMatched("*.a", "/a/b.ab"); |
| assertNotMatched("*.a", "/b/.ab"); |
| } |
| |
| @Test |
| public void testDotAsteriskMatch() { |
| assertMatched("a.*", "a."); |
| assertMatched("a.*", "a./"); |
| assertMatched("a.*", "a.b"); |
| |
| assertMatched("a.*", "b/a.b"); |
| assertMatched("a.*", "b/a.b/"); |
| assertMatched("a.*", "b/a.b/b"); |
| |
| assertMatched("a.*", "/a.b/"); |
| assertMatched("a.*", "/a.b/b"); |
| |
| assertMatched("a.*", "c/a.b/b"); |
| assertMatched("a.*", "c/b/a.b"); |
| |
| assertMatched("/a.*", "a.b"); |
| assertMatched("/a.*", "a.b/"); |
| assertMatched("/a.*", "a.b/b"); |
| assertMatched("/a.*", "/a.b"); |
| assertMatched("/a.*", "/a.b/"); |
| assertMatched("/a.*", "/a.b/b"); |
| |
| assertMatched("/a.*/b", "a.b/b"); |
| assertMatched("/a.*/b", "/a.b/b"); |
| assertMatched("/a.*/b", "/a.bc/b"); |
| assertMatched("/a.*/b", "/a./b"); |
| } |
| |
| @Test |
| public void testAsterisk() { |
| assertMatched("a*", "a"); |
| assertMatched("a*", "a/"); |
| assertMatched("a*", "ab"); |
| |
| assertMatched("a*", "b/ab"); |
| assertMatched("a*", "b/ab/"); |
| assertMatched("a*", "b/ab/b"); |
| |
| assertMatched("a*", "b/abc"); |
| assertMatched("a*", "b/abc/"); |
| assertMatched("a*", "b/abc/b"); |
| |
| assertMatched("a*", "/abc/"); |
| assertMatched("a*", "/abc/b"); |
| |
| assertMatched("a*", "c/abc/b"); |
| assertMatched("a*", "c/b/abc"); |
| |
| assertMatched("/a*", "abc"); |
| assertMatched("/a*", "abc/"); |
| assertMatched("/a*", "abc/b"); |
| assertMatched("/a*", "/abc"); |
| assertMatched("/a*", "/abc/"); |
| assertMatched("/a*", "/abc/b"); |
| |
| assertMatched("/a*/b", "abc/b"); |
| assertMatched("/a*/b", "/abc/b"); |
| assertMatched("/a*/b", "/abcd/b"); |
| assertMatched("/a*/b", "/a/b"); |
| } |
| |
| @Test |
| public void testQuestionmark() { |
| assertMatched("a?", "ab"); |
| assertMatched("a?", "ab/"); |
| |
| assertMatched("a?", "b/ab"); |
| assertMatched("a?", "b/ab/"); |
| assertMatched("a?", "b/ab/b"); |
| |
| assertMatched("a?", "/ab/"); |
| assertMatched("a?", "/ab/b"); |
| |
| assertMatched("a?", "c/ab/b"); |
| assertMatched("a?", "c/b/ab"); |
| |
| assertMatched("/a?", "ab"); |
| assertMatched("/a?", "ab/"); |
| assertMatched("/a?", "ab/b"); |
| assertMatched("/a?", "/ab"); |
| assertMatched("/a?", "/ab/"); |
| assertMatched("/a?", "/ab/b"); |
| |
| assertMatched("/a?/b", "ab/b"); |
| assertMatched("/a?/b", "/ab/b"); |
| } |
| |
| @Test |
| public void testQuestionmarkDoNotMatch() { |
| assertNotMatched("a?", "a/"); |
| assertNotMatched("a?", "abc"); |
| assertNotMatched("a?", "abc/"); |
| |
| assertNotMatched("a?", "b/abc"); |
| assertNotMatched("a?", "b/abc/"); |
| |
| assertNotMatched("a?", "/abc/"); |
| assertNotMatched("a?", "/abc/b"); |
| |
| assertNotMatched("a?", "c/abc/b"); |
| assertNotMatched("a?", "c/b/abc"); |
| |
| assertNotMatched("/a?", "abc"); |
| assertNotMatched("/a?", "abc/"); |
| assertNotMatched("/a?", "abc/b"); |
| assertNotMatched("/a?", "/abc"); |
| assertNotMatched("/a?", "/abc/"); |
| assertNotMatched("/a?", "/abc/b"); |
| |
| assertNotMatched("/a?/b", "abc/b"); |
| assertNotMatched("/a?/b", "/abc/b"); |
| assertNotMatched("/a?/b", "/a/b"); |
| } |
| |
| @Test |
| public void testSimplePatterns() { |
| assertMatched("a", "a"); |
| assertMatched("a", "a/"); |
| assertMatched("a", "a/b"); |
| |
| assertMatched("a", "b/a"); |
| assertMatched("a", "b/a/"); |
| assertMatched("a", "b/a/b"); |
| |
| assertMatched("a", "/a/"); |
| assertMatched("a", "/a/b"); |
| |
| assertMatched("a", "c/a/b"); |
| assertMatched("a", "c/b/a"); |
| |
| assertMatched("/a", "a"); |
| assertMatched("/a", "a/"); |
| assertMatched("/a", "a/b"); |
| assertMatched("/a", "/a"); |
| assertMatched("/a", "/a/"); |
| assertMatched("/a", "/a/b"); |
| |
| assertMatched("a/", "a/"); |
| assertMatched("a/", "a/b"); |
| assertMatched("a/", "/a/"); |
| assertMatched("a/", "/a/b"); |
| |
| assertMatched("/a/", "a/"); |
| assertMatched("/a/", "a/b"); |
| assertMatched("/a/", "/a/"); |
| assertMatched("/a/", "/a/b"); |
| |
| } |
| |
| @Test |
| public void testSimplePatternsDoNotMatch() { |
| assertNotMatched("ab", "a"); |
| assertNotMatched("abc", "a/"); |
| assertNotMatched("abc", "a/b"); |
| |
| assertNotMatched("a", "ab"); |
| assertNotMatched("a", "ba"); |
| assertNotMatched("a", "aa"); |
| |
| assertNotMatched("a", "b/ab"); |
| assertNotMatched("a", "b/ba"); |
| |
| assertNotMatched("a", "b/ba"); |
| assertNotMatched("a", "b/ab"); |
| |
| assertNotMatched("a", "b/ba/"); |
| assertNotMatched("a", "b/ba/b"); |
| |
| assertNotMatched("a", "/aa"); |
| assertNotMatched("a", "aa/"); |
| assertNotMatched("a", "/aa/"); |
| |
| assertNotMatched("/a", "b/a"); |
| assertNotMatched("/a", "/b/a/"); |
| |
| assertNotMatched("a/", "a"); |
| assertNotMatched("a/", "b/a"); |
| |
| assertNotMatched("/a/", "a"); |
| assertNotMatched("/a/", "/a"); |
| assertNotMatched("/a/", "b/a"); |
| } |
| |
| @Test |
| public void testSegments() { |
| assertMatched("/a/b", "a/b"); |
| assertMatched("/a/b", "/a/b"); |
| assertMatched("/a/b", "/a/b/"); |
| assertMatched("/a/b", "/a/b/c"); |
| |
| assertMatched("a/b", "a/b"); |
| assertMatched("a/b", "/a/b"); |
| assertMatched("a/b", "/a/b/"); |
| assertMatched("a/b", "/a/b/c"); |
| |
| assertMatched("a/b/", "a/b/"); |
| assertMatched("a/b/", "/a/b/"); |
| assertMatched("a/b/", "/a/b/c"); |
| } |
| |
| @Test |
| public void testSegmentsDoNotMatch() { |
| assertNotMatched("a/b", "/a/bb"); |
| assertNotMatched("a/b", "/aa/b"); |
| assertNotMatched("a/b", "a/bb"); |
| assertNotMatched("a/b", "aa/b"); |
| assertNotMatched("a/b", "c/aa/b"); |
| assertNotMatched("a/b", "c/a/bb"); |
| assertNotMatched("a/b/", "/a/b"); |
| assertNotMatched("/a/b/", "/a/b"); |
| assertNotMatched("/a/b", "c/a/b"); |
| assertNotMatched("/a/b/", "c/a/b"); |
| assertNotMatched("/a/b/", "c/a/b/"); |
| |
| // XXX why is it like this???? |
| assertNotMatched("a/b", "c/a/b"); |
| assertNotMatched("a/b", "c/a/b/"); |
| assertNotMatched("a/b", "c/a/b/c"); |
| assertNotMatched("a/b/", "c/a/b/"); |
| assertNotMatched("a/b/", "c/a/b/c"); |
| } |
| |
| @Test |
| public void testWildmatch() { |
| assertMatched("**/a/b", "a/b"); |
| assertMatched("**/a/b", "c/a/b"); |
| assertMatched("**/a/b", "c/d/a/b"); |
| assertMatched("**/**/a/b", "c/d/a/b"); |
| |
| assertMatched("/**/a/b", "a/b"); |
| assertMatched("/**/a/b", "c/a/b"); |
| assertMatched("/**/a/b", "c/d/a/b"); |
| assertMatched("/**/**/a/b", "c/d/a/b"); |
| |
| assertMatched("a/b/**", "a/b/c"); |
| assertMatched("a/b/**", "a/b/c/d/"); |
| assertMatched("a/b/**/**", "a/b/c/d"); |
| |
| assertMatched("**/a/**/b", "c/d/a/b"); |
| assertMatched("**/a/**/b", "c/d/a/e/b"); |
| assertMatched("**/**/a/**/**/b", "c/d/a/e/b"); |
| |
| assertMatched("/**/a/**/b", "c/d/a/b"); |
| assertMatched("/**/a/**/b", "c/d/a/e/b"); |
| assertMatched("/**/**/a/**/**/b", "c/d/a/e/b"); |
| |
| assertMatched("a/**/b", "a/b"); |
| assertMatched("a/**/b", "a/c/b"); |
| assertMatched("a/**/b", "a/c/d/b"); |
| assertMatched("a/**/**/b", "a/c/d/b"); |
| |
| assertMatched("a/**/b/**/c", "a/c/b/d/c"); |
| assertMatched("a/**/**/b/**/**/c", "a/c/b/d/c"); |
| |
| assertMatched("**/", "a/"); |
| assertMatched("**/", "a/b"); |
| assertMatched("**/", "a/b/c"); |
| assertMatched("**/**/", "a/"); |
| assertMatched("**/**/", "a/b"); |
| assertMatched("**/**/", "a/b/"); |
| assertMatched("**/**/", "a/b/c"); |
| assertMatched("x/**/", "x/a/"); |
| assertMatched("x/**/", "x/a/b"); |
| assertMatched("x/**/", "x/a/b/"); |
| assertMatched("**/x/", "a/x/"); |
| assertMatched("**/x/", "a/b/x/"); |
| } |
| |
| @Test |
| public void testWildmatchDoNotMatch() { |
| assertNotMatched("a/**", "a/"); |
| assertNotMatched("a/b/**", "a/b/"); |
| assertNotMatched("a/**", "a"); |
| assertNotMatched("a/b/**", "a/b"); |
| assertNotMatched("a/b/**/", "a/b"); |
| assertNotMatched("a/b/**/**", "a/b"); |
| assertNotMatched("**/a/b", "a/c/b"); |
| assertNotMatched("!/**/*.zip", "c/a/b.zip"); |
| assertNotMatched("!**/*.zip", "c/a/b.zip"); |
| assertNotMatched("a/**/b", "a/c/bb"); |
| |
| assertNotMatched("**/", "a"); |
| assertNotMatched("**/**/", "a"); |
| assertNotMatched("**/x/", "a/b/x"); |
| } |
| |
| @SuppressWarnings("unused") |
| @Test |
| public void testSimpleRules() { |
| try { |
| new FastIgnoreRule(null); |
| fail("Illegal input allowed!"); |
| } catch (IllegalArgumentException e) { |
| // expected |
| } |
| assertFalse(new FastIgnoreRule("/").isMatch("/", false)); |
| assertFalse(new FastIgnoreRule("//").isMatch("//", false)); |
| assertFalse(new FastIgnoreRule("#").isMatch("#", false)); |
| assertFalse(new FastIgnoreRule("").isMatch("", false)); |
| assertFalse(new FastIgnoreRule(" ").isMatch(" ", false)); |
| } |
| |
| @Test |
| public void testSplit() { |
| try { |
| split("/", '/').toArray(); |
| fail("should not allow single slash"); |
| } catch (IllegalStateException e) { |
| // expected |
| } |
| |
| assertArrayEquals(new String[] { "a", "b" }, split("a/b", '/') |
| .toArray()); |
| assertArrayEquals(new String[] { "a", "b/" }, split("a/b/", '/') |
| .toArray()); |
| assertArrayEquals(new String[] { "/a", "b" }, split("/a/b", '/') |
| .toArray()); |
| assertArrayEquals(new String[] { "/a", "b/" }, split("/a/b/", '/') |
| .toArray()); |
| assertArrayEquals(new String[] { "/a", "b", "c" }, split("/a/b/c", '/') |
| .toArray()); |
| assertArrayEquals(new String[] { "/a", "b", "c/" }, |
| split("/a/b/c/", '/').toArray()); |
| } |
| |
| @Test |
| public void testPathMatch() { |
| pathMatch = true; |
| assertMatched("a", "a"); |
| assertMatched("a/", "a/"); |
| assertNotMatched("a/", "a/b"); |
| |
| assertMatched("**", "a"); |
| assertMatched("**", "a/"); |
| assertMatched("**", "a/b"); |
| |
| assertNotMatched("**/", "a"); |
| assertNotMatched("**/", "a/b"); |
| assertMatched("**/", "a/"); |
| assertMatched("**/", "a/b/"); |
| |
| assertNotMatched("x/**/", "x/a"); |
| assertNotMatched("x/**/", "x/a/b"); |
| assertMatched("x/**/", "x/a/"); |
| assertMatched("x/**/", "x/y/a/"); |
| } |
| |
| @Test |
| public void testFileNameWithLineTerminator() { |
| assertMatched("a?", "a\r"); |
| assertMatched("a?", "dir/a\r"); |
| assertMatched("a?", "a\r/file"); |
| assertMatched("*a", "\ra"); |
| assertMatched("dir/*a*", "dir/\ra\r"); |
| } |
| |
| private void assertMatched(String pattern, String path) { |
| boolean match = match(pattern, path); |
| String result = path + " is " + (match ? "ignored" : "not ignored") |
| + " via '" + pattern + "' rule"; |
| if (!match) { |
| System.err.println(result); |
| } |
| assertTrue("Expected a match for: " + pattern + " with: " + path, |
| match); |
| |
| if (pattern.startsWith("!")) { |
| pattern = pattern.substring(1); |
| } else { |
| pattern = "!" + pattern; |
| } |
| match = match(pattern, path); |
| assertFalse("Expected no match for: " + pattern + " with: " + path, |
| match); |
| } |
| |
| private void assertNotMatched(String pattern, String path) { |
| boolean match = match(pattern, path); |
| String result = path + " is " + (match ? "ignored" : "not ignored") |
| + " via '" + pattern + "' rule"; |
| if (match) { |
| System.err.println(result); |
| } |
| assertFalse("Expected no match for: " + pattern + " with: " + path, |
| match); |
| |
| if (pattern.startsWith("!")) { |
| pattern = pattern.substring(1); |
| } else { |
| pattern = "!" + pattern; |
| } |
| match = match(pattern, path); |
| assertTrue("Expected a match for: " + pattern + " with: " + path, |
| match); |
| } |
| |
| /** |
| * Check for a match. If target ends with "/", match will assume that the |
| * target is meant to be a directory. |
| * |
| * @param pattern |
| * Pattern as it would appear in a .gitignore file |
| * @param target |
| * Target file path relative to repository's GIT_DIR |
| * @return Result of {@link FastIgnoreRule#isMatch(String, boolean)} |
| */ |
| private boolean match(String pattern, String target) { |
| boolean isDirectory = target.endsWith("/"); |
| FastIgnoreRule r = new FastIgnoreRule(pattern); |
| // If speed of this test is ever an issue, we can use a presetRule field |
| // to avoid recompiling a pattern each time. |
| boolean match = r.isMatch(target, isDirectory, pathMatch); |
| if (r.getNegation()) |
| match = !match; |
| return match; |
| } |
| } |