| /* | |
| * Copyright (C) 2010, Red Hat Inc. 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.attributes; | |
| import static org.junit.Assert.assertEquals; | |
| import static org.junit.Assert.assertFalse; | |
| import static org.junit.Assert.assertNotNull; | |
| import static org.junit.Assert.assertTrue; | |
| import org.junit.Test; | |
| /** | |
| * Tests git attributes pattern matches | |
| */ | |
| public class AttributesMatcherTest { | |
| @Test | |
| public void testBasic() { | |
| String pattern = "/test.stp"; | |
| assertMatched(pattern, "/test.stp"); | |
| pattern = "#/test.stp"; | |
| assertNotMatched(pattern, "/test.stp"); | |
| } | |
| @Test | |
| public void testFileNameWildcards() { | |
| //Test basic * and ? for any pattern + any character | |
| String pattern = "*.st?"; | |
| assertMatched(pattern, "/test.stp"); | |
| assertMatched(pattern, "/anothertest.stg"); | |
| assertMatched(pattern, "/anothertest.st0"); | |
| assertNotMatched(pattern, "/anothertest.sta1"); | |
| //Check that asterisk does not expand to "/" | |
| assertNotMatched(pattern, "/another/test.sta1"); | |
| //Same as above, with a leading slash to ensure that doesn't cause problems | |
| pattern = "/*.st?"; | |
| assertMatched(pattern, "/test.stp"); | |
| assertMatched(pattern, "/anothertest.stg"); | |
| assertMatched(pattern, "/anothertest.st0"); | |
| assertNotMatched(pattern, "/anothertest.sta1"); | |
| //Check that asterisk does not expand to "/" | |
| assertNotMatched(pattern, "/another/test.sta1"); | |
| //Test for numbers | |
| pattern = "*.sta[0-5]"; | |
| assertMatched(pattern, "/test.sta5"); | |
| assertMatched(pattern, "/test.sta4"); | |
| assertMatched(pattern, "/test.sta3"); | |
| assertMatched(pattern, "/test.sta2"); | |
| assertMatched(pattern, "/test.sta1"); | |
| assertMatched(pattern, "/test.sta0"); | |
| assertMatched(pattern, "/anothertest.sta2"); | |
| assertNotMatched(pattern, "test.stag"); | |
| assertNotMatched(pattern, "test.sta6"); | |
| //Test for letters | |
| pattern = "/[tv]est.sta[a-d]"; | |
| assertMatched(pattern, "/test.staa"); | |
| assertMatched(pattern, "/test.stab"); | |
| assertMatched(pattern, "/test.stac"); | |
| assertMatched(pattern, "/test.stad"); | |
| assertMatched(pattern, "/vest.stac"); | |
| assertNotMatched(pattern, "test.stae"); | |
| assertNotMatched(pattern, "test.sta9"); | |
| //Test child directory/file is matched | |
| pattern = "/src/ne?"; | |
| assertMatched(pattern, "/src/new/"); | |
| assertMatched(pattern, "/src/new"); | |
| assertNotMatched(pattern, "/src/new/a.c"); | |
| assertNotMatched(pattern, "/src/new/a/a.c"); | |
| assertNotMatched(pattern, "/src/new.c"); | |
| //Test name-only fnmatcher matches | |
| pattern = "ne?"; | |
| assertMatched(pattern, "/src/new/"); | |
| assertMatched(pattern, "/src/new"); | |
| assertNotMatched(pattern, "/src/new/a.c"); | |
| assertNotMatched(pattern, "/src/new/a/a.c"); | |
| assertMatched(pattern, "/neb"); | |
| assertNotMatched(pattern, "/src/new.c"); | |
| } | |
| @Test | |
| public void testTargetWithoutLeadingSlash() { | |
| //Test basic * and ? for any pattern + any character | |
| String pattern = "/*.st?"; | |
| assertMatched(pattern, "test.stp"); | |
| assertMatched(pattern, "anothertest.stg"); | |
| assertMatched(pattern, "anothertest.st0"); | |
| assertNotMatched(pattern, "anothertest.sta1"); | |
| //Check that asterisk does not expand to "" | |
| assertNotMatched(pattern, "another/test.sta1"); | |
| //Same as above, with a leading slash to ensure that doesn't cause problems | |
| pattern = "/*.st?"; | |
| assertMatched(pattern, "test.stp"); | |
| assertMatched(pattern, "anothertest.stg"); | |
| assertMatched(pattern, "anothertest.st0"); | |
| assertNotMatched(pattern, "anothertest.sta1"); | |
| //Check that asterisk does not expand to "" | |
| assertNotMatched(pattern, "another/test.sta1"); | |
| //Test for numbers | |
| pattern = "/*.sta[0-5]"; | |
| assertMatched(pattern, "test.sta5"); | |
| assertMatched(pattern, "test.sta4"); | |
| assertMatched(pattern, "test.sta3"); | |
| assertMatched(pattern, "test.sta2"); | |
| assertMatched(pattern, "test.sta1"); | |
| assertMatched(pattern, "test.sta0"); | |
| assertMatched(pattern, "anothertest.sta2"); | |
| assertNotMatched(pattern, "test.stag"); | |
| assertNotMatched(pattern, "test.sta6"); | |
| //Test for letters | |
| pattern = "/[tv]est.sta[a-d]"; | |
| assertMatched(pattern, "test.staa"); | |
| assertMatched(pattern, "test.stab"); | |
| assertMatched(pattern, "test.stac"); | |
| assertMatched(pattern, "test.stad"); | |
| assertMatched(pattern, "vest.stac"); | |
| assertNotMatched(pattern, "test.stae"); | |
| assertNotMatched(pattern, "test.sta9"); | |
| //Test child directory/file is matched | |
| pattern = "/src/ne?"; | |
| assertMatched(pattern, "src/new/"); | |
| assertMatched(pattern, "src/new"); | |
| assertNotMatched(pattern, "src/new/a.c"); | |
| assertNotMatched(pattern, "src/new/a/a.c"); | |
| assertNotMatched(pattern, "src/new.c"); | |
| //Test name-only fnmatcher matches | |
| pattern = "ne?"; | |
| assertMatched(pattern, "src/new/"); | |
| assertMatched(pattern, "src/new"); | |
| assertNotMatched(pattern, "src/new/a.c"); | |
| assertNotMatched(pattern, "src/new/a/a.c"); | |
| assertMatched(pattern, "neb"); | |
| assertNotMatched(pattern, "src/new.c"); | |
| } | |
| @Test | |
| public void testParentDirectoryGitAttributes() { | |
| //Contains git attribute patterns such as might be seen in a parent directory | |
| //Test for wildcards | |
| String pattern = "/*/*.c"; | |
| assertMatched(pattern, "/file/a.c"); | |
| assertMatched(pattern, "/src/a.c"); | |
| assertNotMatched(pattern, "/src/new/a.c"); | |
| //Test child directory/file is matched | |
| pattern = "/src/new"; | |
| assertMatched(pattern, "/src/new/"); | |
| assertMatched(pattern, "/src/new"); | |
| assertNotMatched(pattern, "/src/new/a.c"); | |
| assertNotMatched(pattern, "/src/new/a/a.c"); | |
| assertNotMatched(pattern, "/src/new.c"); | |
| //Test child directory is matched, slash after name | |
| pattern = "/src/new/"; | |
| assertMatched(pattern, "/src/new/"); | |
| assertNotMatched(pattern, "/src/new/a.c"); | |
| assertNotMatched(pattern, "/src/new/a/a.c"); | |
| assertNotMatched(pattern, "/src/new"); | |
| assertNotMatched(pattern, "/src/new.c"); | |
| //Test directory is matched by name only | |
| pattern = "b1"; | |
| assertNotMatched(pattern, "/src/new/a/b1/a.c"); | |
| assertNotMatched(pattern, "/src/new/a/b2/file.c"); | |
| assertNotMatched(pattern, "/src/new/a/bb1/file.c"); | |
| assertNotMatched(pattern, "/src/new/a/file.c"); | |
| assertNotMatched(pattern, "/src/new/a/bb1"); | |
| assertMatched(pattern, "/src/new/a/b1"); | |
| } | |
| @Test | |
| public void testTrailingSlash() { | |
| String pattern = "/src/"; | |
| assertMatched(pattern, "/src/"); | |
| assertNotMatched(pattern, "/src/new"); | |
| assertNotMatched(pattern, "/src/new/a.c"); | |
| assertNotMatched(pattern, "/src/a.c"); | |
| assertNotMatched(pattern, "/src"); | |
| assertNotMatched(pattern, "/srcA/"); | |
| pattern = "src/"; | |
| assertMatched(pattern, "src/"); | |
| assertMatched(pattern, "/src/"); | |
| assertNotMatched(pattern, "src"); | |
| assertNotMatched(pattern, "/src/new"); | |
| assertNotMatched(pattern, "/src/new/a.c"); | |
| assertNotMatched(pattern, "/src/a.c"); | |
| assertNotMatched(pattern, "foo/src/a.c"); | |
| assertNotMatched(pattern, "foo/src/bar/a.c"); | |
| assertNotMatched(pattern, "foo/src/bar/src"); | |
| assertMatched(pattern, "foo/src/"); | |
| assertMatched(pattern, "foo/src/bar/src/"); | |
| } | |
| @Test | |
| public void testNameOnlyMatches() { | |
| /* | |
| * Name-only matches do not contain any path separators | |
| */ | |
| //Test matches for file extension | |
| String pattern = "*.stp"; | |
| assertMatched(pattern, "/test.stp"); | |
| assertMatched(pattern, "/src/test.stp"); | |
| assertNotMatched(pattern, "/test.stp1"); | |
| assertNotMatched(pattern, "/test.astp"); | |
| assertNotMatched(pattern, "test.stp/foo.bar"); | |
| assertMatched(pattern, "test.stp"); | |
| assertMatched(pattern, "test.stp/"); | |
| assertMatched(pattern, "test.stp/test.stp"); | |
| //Test matches for name-only, applies to file name or folder name | |
| pattern = "src"; | |
| assertMatched(pattern, "/src"); | |
| assertMatched(pattern, "/src/"); | |
| assertNotMatched(pattern, "/src/a.c"); | |
| assertNotMatched(pattern, "/src/new/a.c"); | |
| assertNotMatched(pattern, "/new/src/a.c"); | |
| assertMatched(pattern, "/file/src"); | |
| //Test matches for name-only, applies only to folder names | |
| pattern = "src/"; | |
| assertNotMatched(pattern, "/src/a.c"); | |
| assertNotMatched(pattern, "/src/new/a.c"); | |
| assertNotMatched(pattern, "/new/src/a.c"); | |
| assertNotMatched(pattern, "/src"); | |
| assertNotMatched(pattern, "/file/src"); | |
| assertMatched(pattern, "/file/src/"); | |
| //Test matches for name-only, applies to file name or folder name | |
| //With a small wildcard | |
| pattern = "?rc"; | |
| assertNotMatched(pattern, "/src/a.c"); | |
| assertNotMatched(pattern, "/src/new/a.c"); | |
| assertNotMatched(pattern, "/new/src/a.c"); | |
| assertMatched(pattern, "/new/src/"); | |
| assertMatched(pattern, "/file/src"); | |
| assertMatched(pattern, "/src/"); | |
| //Test matches for name-only, applies to file name or folder name | |
| //With a small wildcard | |
| pattern = "?r[a-c]"; | |
| assertNotMatched(pattern, "/src/a.c"); | |
| assertNotMatched(pattern, "/src/new/a.c"); | |
| assertNotMatched(pattern, "/new/src/a.c"); | |
| assertMatched(pattern, "/file/src"); | |
| assertMatched(pattern, "/src/"); | |
| assertNotMatched(pattern, "/srb/a.c"); | |
| assertNotMatched(pattern, "/grb/new/a.c"); | |
| assertNotMatched(pattern, "/new/crb/a.c"); | |
| assertMatched(pattern, "/file/3rb"); | |
| assertMatched(pattern, "/xrb/"); | |
| assertNotMatched(pattern, "/3ra/a.c"); | |
| assertNotMatched(pattern, "/5ra/new/a.c"); | |
| assertNotMatched(pattern, "/new/1ra/a.c"); | |
| assertNotMatched(pattern, "/new/1ra/a.c/"); | |
| assertMatched(pattern, "/file/dra"); | |
| assertMatched(pattern, "/file/dra/"); | |
| assertMatched(pattern, "/era/"); | |
| assertNotMatched(pattern, "/crg"); | |
| assertNotMatched(pattern, "/cr3"); | |
| } | |
| @Test | |
| public void testGetters() { | |
| AttributesRule r = new AttributesRule("/pattern/", ""); | |
| assertFalse(r.isNameOnly()); | |
| assertTrue(r.isDirOnly()); | |
| assertNotNull(r.getAttributes()); | |
| assertTrue(r.getAttributes().isEmpty()); | |
| assertEquals(r.getPattern(), "/pattern"); | |
| r = new AttributesRule("/patter?/", ""); | |
| assertFalse(r.isNameOnly()); | |
| assertTrue(r.isDirOnly()); | |
| assertNotNull(r.getAttributes()); | |
| assertTrue(r.getAttributes().isEmpty()); | |
| assertEquals(r.getPattern(), "/patter?"); | |
| r = new AttributesRule("patt*", ""); | |
| assertTrue(r.isNameOnly()); | |
| assertFalse(r.isDirOnly()); | |
| assertNotNull(r.getAttributes()); | |
| assertTrue(r.getAttributes().isEmpty()); | |
| assertEquals(r.getPattern(), "patt*"); | |
| r = new AttributesRule("pattern", "attribute1"); | |
| assertTrue(r.isNameOnly()); | |
| assertFalse(r.isDirOnly()); | |
| assertNotNull(r.getAttributes()); | |
| assertFalse(r.getAttributes().isEmpty()); | |
| assertEquals(r.getAttributes().size(), 1); | |
| assertEquals(r.getPattern(), "pattern"); | |
| r = new AttributesRule("pattern", "attribute1 -attribute2"); | |
| assertTrue(r.isNameOnly()); | |
| assertFalse(r.isDirOnly()); | |
| assertNotNull(r.getAttributes()); | |
| assertEquals(r.getAttributes().size(), 2); | |
| assertEquals(r.getPattern(), "pattern"); | |
| r = new AttributesRule("pattern", "attribute1 \t-attribute2 \t"); | |
| assertTrue(r.isNameOnly()); | |
| assertFalse(r.isDirOnly()); | |
| assertNotNull(r.getAttributes()); | |
| assertEquals(r.getAttributes().size(), 2); | |
| assertEquals(r.getPattern(), "pattern"); | |
| r = new AttributesRule("pattern", "attribute1\t-attribute2\t"); | |
| assertTrue(r.isNameOnly()); | |
| assertFalse(r.isDirOnly()); | |
| assertNotNull(r.getAttributes()); | |
| assertEquals(r.getAttributes().size(), 2); | |
| assertEquals(r.getPattern(), "pattern"); | |
| r = new AttributesRule("pattern", "attribute1\t -attribute2\t "); | |
| assertTrue(r.isNameOnly()); | |
| assertFalse(r.isDirOnly()); | |
| assertNotNull(r.getAttributes()); | |
| assertEquals(r.getAttributes().size(), 2); | |
| assertEquals(r.getPattern(), "pattern"); | |
| r = new AttributesRule("pattern", | |
| "attribute1 -attribute2 attribute3=value "); | |
| assertTrue(r.isNameOnly()); | |
| assertFalse(r.isDirOnly()); | |
| assertNotNull(r.getAttributes()); | |
| assertEquals(r.getAttributes().size(), 3); | |
| assertEquals(r.getPattern(), "pattern"); | |
| assertEquals(r.getAttributes().get(0).toString(), "attribute1"); | |
| assertEquals(r.getAttributes().get(1).toString(), "-attribute2"); | |
| assertEquals(r.getAttributes().get(2).toString(), "attribute3=value"); | |
| } | |
| @Test | |
| public void testBracketsInGroup() { | |
| //combinations of brackets in brackets, escaped and not | |
| String[] patterns = new String[]{"[[\\]]", "[\\[\\]]"}; | |
| for (String pattern : patterns) { | |
| assertNotMatched(pattern, ""); | |
| assertNotMatched(pattern, "[]"); | |
| assertNotMatched(pattern, "]["); | |
| assertNotMatched(pattern, "[\\[]"); | |
| assertNotMatched(pattern, "[[]"); | |
| assertNotMatched(pattern, "[[]]"); | |
| assertNotMatched(pattern, "[\\[\\]]"); | |
| assertMatched(pattern, "["); | |
| assertMatched(pattern, "]"); | |
| } | |
| patterns = new String[]{"[[]]", "[\\[]]"}; | |
| for (String pattern : patterns) { | |
| assertNotMatched(pattern, ""); | |
| assertMatched(pattern, "[]"); | |
| assertNotMatched(pattern, "]["); | |
| assertNotMatched(pattern, "[\\[]"); | |
| assertNotMatched(pattern, "[[]"); | |
| assertNotMatched(pattern, "[[]]"); | |
| assertNotMatched(pattern, "[\\[\\]]"); | |
| assertNotMatched(pattern, "["); | |
| assertNotMatched(pattern, "]"); | |
| } | |
| } | |
| @Test | |
| public void testFileNameWithLineTerminator() { | |
| assertMatched("a?", "a\r"); | |
| assertMatched("a?", "dir/a\r"); | |
| assertMatched("*a", "\ra"); | |
| assertMatched("dir/*a*", "dir/\ra\r"); | |
| } | |
| /** | |
| * 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 .gitattributes file | |
| * @param target | |
| * Target file path relative to repository's GIT_DIR | |
| */ | |
| private void assertMatched(String pattern, String target) { | |
| boolean value = match(pattern, target); | |
| assertTrue("Expected a match for: " + pattern + " with: " + target, | |
| value); | |
| } | |
| /** | |
| * 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 .gitattributes file | |
| * @param target | |
| * Target file path relative to repository's GIT_DIR | |
| */ | |
| private void assertNotMatched(String pattern, String target) { | |
| boolean value = match(pattern, target); | |
| assertFalse("Expected no match for: " + pattern + " with: " + target, | |
| value); | |
| } | |
| /** | |
| * 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 .gitattributes file | |
| * @param target | |
| * Target file path relative to repository's GIT_DIR | |
| * @return Result of {@link AttributesRule#isMatch(String, boolean)} | |
| */ | |
| private static boolean match(String pattern, String target) { | |
| AttributesRule r = new AttributesRule(pattern, ""); | |
| //If speed of this test is ever an issue, we can use a presetRule field | |
| //to avoid recompiling a pattern each time. | |
| return r.isMatch(target, target.endsWith("/")); | |
| } | |
| } |