diff options
author | Charley Wang <chwang@redhat.com> | 2010-07-13 00:34:15 +0200 |
---|---|---|
committer | Matthias Sohn <matthias.sohn@sap.com> | 2010-07-13 00:34:15 +0200 |
commit | b878cdcf6b4c2445553dcd1507d5c3008bf56b7b (patch) | |
tree | 878383c68bc6fccf4aec8f25416dcec27e86a931 /org.eclipse.jgit.test | |
parent | d1378e4c51ea20c96a9f4b3efb7333eadf99f141 (diff) | |
download | jgit-b878cdcf6b4c2445553dcd1507d5c3008bf56b7b.tar.gz jgit-b878cdcf6b4c2445553dcd1507d5c3008bf56b7b.zip |
Add compatibility with gitignore specifications
This patch adds ignore compatibility to jgit. It encompasses
exclude files as well as .gitignore. Uses TreeWalk and
FileTreeIterator to find nodes and parses .gitignore
files when required. The patch includes a simple cache that
can be used to save results and avoid excessive gitignore
parsing.
CQ: 4302
Bug: 303925
Change-Id: Iebd7e5bb534accca4bf00d25bbc1f561d7cad11b
Signed-off-by: Chris Aniszczyk <caniszczyk@gmail.com>
Signed-off-by: Stefan Lay <stefan.lay@sap.com>
Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
Diffstat (limited to 'org.eclipse.jgit.test')
14 files changed, 777 insertions, 0 deletions
diff --git a/org.eclipse.jgit.test/META-INF/MANIFEST.MF b/org.eclipse.jgit.test/META-INF/MANIFEST.MF index 3b82cf7d1d..3aaa8a45e8 100644 --- a/org.eclipse.jgit.test/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit.test/META-INF/MANIFEST.MF @@ -18,6 +18,7 @@ Import-Package: junit.framework;version="[3.8.2,4.0.0)", org.eclipse.jgit.errors;version="[0.9.0,0.10.0)", org.eclipse.jgit.fnmatch;version="[0.9.0,0.10.0)", org.eclipse.jgit.http.server;version="[0.9.0,0.10.0)", + org.eclipse.jgit.ignore;version="[0.9.0,0.10.0)", org.eclipse.jgit.iplog;version="[0.9.0,0.10.0)", org.eclipse.jgit.junit;version="[0.9.0,0.10.0)", org.eclipse.jgit.lib;version="[0.9.0,0.10.0)", diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/.gitignore b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/.gitignore new file mode 100644 index 0000000000..b3f6bc97fb --- /dev/null +++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/.gitignore @@ -0,0 +1 @@ +!/notignored diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/new/.gitignore b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/new/.gitignore new file mode 100644 index 0000000000..09b8574b00 --- /dev/null +++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/new/.gitignore @@ -0,0 +1 @@ +notarealfile diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/new/a/.gitignore b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/new/a/.gitignore new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/new/a/.gitignore diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/new/a/b1/.gitignore b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/new/a/b1/.gitignore new file mode 100644 index 0000000000..82b0f5d464 --- /dev/null +++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/new/a/b1/.gitignore @@ -0,0 +1 @@ +/c diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/new/a/b1/test.stp b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/new/a/b1/test.stp new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/new/a/b1/test.stp diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/new/a/b2/c/.gitignore b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/new/a/b2/c/.gitignore new file mode 100644 index 0000000000..3c6cf10b1d --- /dev/null +++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/new/a/b2/c/.gitignore @@ -0,0 +1 @@ +/notarealfile2 diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/new/a/b2/c/test.stp b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/new/a/b2/c/test.stp new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/new/a/b2/c/test.stp diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/notignored b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/notignored new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/notignored diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/src/.gitignore b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/src/.gitignore new file mode 100644 index 0000000000..b314092d16 --- /dev/null +++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/src/.gitignore @@ -0,0 +1,4 @@ +/*.st? +!/test.stp +!/a.c +/a.c diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/src/test.stp b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/src/test.stp new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/src/test.stp diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/test.stp b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/test.stp new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/excludeTest/test.stp diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreCacheTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreCacheTest.java new file mode 100644 index 0000000000..4083dcb09b --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreCacheTest.java @@ -0,0 +1,404 @@ +/* + * Copyright (C) 2010, Red Hat Inc. + * 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.ignore; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; + +import org.eclipse.jgit.lib.RepositoryTestCase; +import org.eclipse.jgit.util.JGitTestUtil; + +/** + * Tests for the ignore cache + */ +public class IgnoreCacheTest extends RepositoryTestCase { + + private File ignoreTestDir = JGitTestUtil.getTestResourceFile("excludeTest"); + private SimpleIgnoreCache cache; + private final ArrayList<File> toDelete = new ArrayList<File>(); + + //TODO: Do not use OS dependent strings to encode file paths + + public void tearDown() throws Exception { + super.tearDown(); + deleteIgnoreFiles(); + cache.clear(); + toDelete.clear(); + } + + public void setUp() throws Exception { + super.setUp(); + ignoreTestDir = JGitTestUtil.getTestResourceFile("excludeTest"); + assertTrue("Test resource directory is not a directory",ignoreTestDir.isDirectory()); + + db = createWorkRepository(); + recursiveCopy(ignoreTestDir, db.getDirectory().getParentFile()); + cache = new SimpleIgnoreCache(db); + initCache(); + } + + protected void recursiveCopy(File src, File parent) throws IOException { + for (File file : src.listFiles()) { + String rel = file.getName(); + File dst = new File(parent.toURI().resolve(rel)); + copyFileOrDirectory(file, dst); + if (file.isDirectory()) + recursiveCopy(file, dst); + } + } + + protected static void copyFileOrDirectory(File src, File dst) throws IOException { + if (src.isDirectory()) + dst.mkdir(); + else + copyFile(src, dst); + } + + public void testInitialization() { + File test = new File(db.getDirectory().getParentFile() + "/new/a/b1/test.stp"); + assertTrue("Missing file " + test.getAbsolutePath(), test.exists()); + + /* + * Every folder along the path has a .gitignore file. Therefore every + * folder should have been added and initialized + */ + boolean result = isIgnored(getRelativePath(test)); + assertFalse("Unexpected match for " + test.toString(), result); + + /* + * Check that every .gitignore along the path has been initialized + */ + File folder = test.getParentFile(); + IgnoreNode rules = null; + String fp = folder.getAbsolutePath(); + while (!folder.equals(db.getDirectory().getParentFile()) && fp.length() > 0) { + rules = cache.getRules(getRelativePath(folder)); + assertNotNull("Ignore file not initialized for " + fp, rules); + if (getRelativePath(folder).endsWith("new/a")) + //The /new/a directory has an empty ignore file + assertEquals("Ignore file not initialized for " + fp, 0, rules.getRules().size()); + else + assertEquals("Ignore file not initialized for " + fp, 1, rules.getRules().size()); + + folder = folder.getParentFile(); + fp = folder.getAbsolutePath(); + } + if (rules != null) + assertEquals(1, rules.getRules().size()); + else + fail("Base directory not initialized"); + + test = new File("/tmp/not/part/of/repo/path"); + } + + public void testRules() { + ignoreTestDir = JGitTestUtil.getTestResourceFile("excludeTest"); + assertTrue("Test resource directory is not a directory", ignoreTestDir.isDirectory()); + createExcludeFile(); + initCache(); + + File test = new File(db.getDirectory().getParentFile(), "test.stp"); + String path = test.getAbsolutePath(); + assertTrue("Could not find test file " + path, test.exists()); + + IgnoreNode baseRules = cache.getRules(""); + assertNotNull("Could not find base rules", baseRules); + + /* + * .git/info/excludes: + * /test.stp + * /notignored + * + * new/.gitignore: + * notarealfile + * + * new/a/.gitignore: + * <empty> + * + * new/a/b2/.gitignore: + * <does not exist> + * + * new/a/b1/.gitignore: + * /c + * + * new/a/b1/c/.gitignore: + * !/shouldbeignored.txt + * + * .gitignore: + * !/notignored + * /commentNotIgnored.tx#t + * /commentIgnored.txt#comment + * /commentIgnored.txt #comment + */ + boolean result = isIgnored(getRelativePath(test)); + assertEquals(3, baseRules.getRules().size()); + assertTrue(db.getDirectory().getParentFile().toURI().equals(baseRules.getBaseDir().toURI())); + //Test basic exclude file + assertTrue("Did not match file " + test.toString(), result); + //Test exclude file priority + assertNotIgnored("notignored"); + //Test that /src/test.stp is not matched by /test.stp in exclude file (Do not reinitialize) + assertNotIgnored("/src/test.stp"); + //Test file that is not mentioned -- should just return unmatched + assertNotIgnored("not/mentioned/file.txt"); + + //Test adding nonexistent node + test = new File(db.getDirectory().getParentFile(), "new/a/b2/d/test.stp"); + assertNotIgnored("new/a/b2/d/test.stp"); + assertNotIgnored("new/a/b2/d/"); + assertNotIgnored("new/a/b2/d"); + + //Test folder + test = new File(db.getDirectory().getParentFile(), "new/a/b1/c"); + assertIgnored("new/a/b1/c"); + assertIgnored("new/a/b1/c/anything.c"); + assertIgnored("new/a/b1/c/and.o"); + assertIgnored("new/a/b1/c/everything.d"); + assertIgnored("new/a/b1/c/everything.d"); + //Special case -- the normally higher priority negation in c/.gitignore is cancelled by the folder being ignored + assertIgnored("new/a/b1/c/shouldbeignored.txt"); + + //Test name-only (use non-existent folders) + assertNotIgnored("notarealfile"); + assertNotIgnored("/notarealfile"); + assertIgnored("new/notarealfile"); + assertIgnored("new/notarealfile/fake"); + assertIgnored("new/a/notarealfile"); + assertIgnored("new/a/b1/notarealfile"); + + //Test clearing node -- create empty .gitignore + createIgnoreFile(db.getDirectory().getParentFile() + "/new/a/b2/.gitignore", new String[0]); + test = new File(db.getDirectory().getParentFile(), "new/a/b2/c"); + initCache(); + baseRules = cache.getRules("new/a/b2"); + assertNotNull(baseRules); + baseRules.clear(); + assertEquals(baseRules.getRules().size(), 0); + try { + assertFalse("Node not properly cleared", baseRules.isIgnored(getRelativePath(test))); + } catch (IOException e) { + e.printStackTrace(); + fail("IO exception when testing base rules"); + } + + //Test clearing entire cache, and isEmpty + assertNotNull(cache.getRules("")); + assertFalse(cache.isEmpty()); + cache.clear(); + assertNull(cache.getRules("")); + assertTrue(cache.isEmpty()); + assertNotIgnored("/anything"); + assertNotIgnored("/new/anything"); + assertNotIgnored("/src/anything"); + } + + public void testPriorities() { + ignoreTestDir = JGitTestUtil.getTestResourceFile("excludeTest"); + assertTrue("Test resource directory is not a directory",ignoreTestDir.isDirectory()); + createExcludeFile(); + initCache(); + + File test = new File(db.getDirectory().getParentFile(), "/src/test.stp"); + assertTrue("Resource file " + test.getName() + " is missing", test.exists()); + + //Test basic exclude file + IgnoreNode node = cache.getRules("src"); + assertNotNull("Excludes file was not initialized", node); + + /* + * src/.gitignore: + * /*.st? + * !/test.stp + * !/a.c + * /a.c + * + * ./.gitignore: + * !/notignored + * + * .git/info/exclude: + * /test.stp + * /notignored + */ + assertIgnored("src/a.c"); + assertIgnored("test.stp"); + assertIgnored("src/blank.stp"); + assertNotIgnored("notignored"); + assertNotIgnored("src/test.stp"); + + assertEquals(4, node.getRules().size()); + + /* + * new/.gitignore: + * notarealfile + * + * new/a/.gitignore: + * <empty> + * + * new/a/b2/.gitignore: + * <does not exist> + * + * new/a/b2/c/.gitignore: + * /notarealfile2 + */ + assertIgnored("new/a/b2/c/notarealfile2"); + assertIgnored("new/notarealfile"); + assertIgnored("new/a/notarealfile"); + assertNotIgnored("new/a/b2/c/test.stp"); + assertNotIgnored("new/a/b2/c"); + assertNotIgnored("new/a/b2/nonexistent"); + } + + /** + * Check if a file is not matched as ignored + * @param relativePath + * Path to file, relative to db.getDirectory. Use "/" as a separator, + * this method will replace all instances of "/" with File.separator + */ + private void assertNotIgnored(String relativePath) { + File test = new File(db.getDirectory().getParentFile(), relativePath); + assertFalse("Should not match " + test.toString(), isIgnored(getRelativePath(test))); + } + + /** + * Check if a file is matched as ignored + * @param relativePath + * Path to file, relative to db.getDirectory. Use "/" as a separator, + * this method will replace all instances of "/" with File.separator. + */ + private void assertIgnored(String relativePath) { + File test = new File(db.getDirectory().getParentFile(), relativePath); + assertTrue("Failed to match " + test.toString(), isIgnored(getRelativePath(test))); + } + + /** + * Attempt to write an ignore file at the given location + * @param path + * Will create file at this path + * @param contents + * Each entry in contents will be entered on its own line + */ + private void createIgnoreFile(String path, String[] contents) { + File ignoreFile = new File(path); + ignoreFile.delete(); + ignoreFile.deleteOnExit(); //Hope to catch in the event of crash + toDelete.add(ignoreFile); //For teardown purposes + + //Jump through some hoops to create the exclude file + try { + if (!ignoreFile.createNewFile()) + fail("Could not create ignore file" + ignoreFile.getAbsolutePath()); + + BufferedWriter bw = new BufferedWriter(new FileWriter (ignoreFile)); + for (String s : contents) + bw.write(s + System.getProperty("line.separator")); + bw.flush(); + bw.close(); + } catch (IOException e1) { + e1.printStackTrace(); + fail("Could not create exclude file"); + } + } + + private void createExcludeFile() { + String[] content = new String[2]; + content[0] = "/test.stp"; + content[1] = "/notignored"; + + //We can do this because we explicitly delete parent directories later in deleteIgnoreFiles. + File parent= new File(db.getDirectory().getParentFile(), ".git/info"); + if (!parent.exists()) + parent.mkdirs(); + + createIgnoreFile(db.getDirectory().getParentFile() + "/.git/info/exclude", content); + } + + private void deleteIgnoreFiles() { + for (File f : toDelete) + f.delete(); + + //Systematically delete exclude parent dirs + File f = new File(ignoreTestDir.getAbsoluteFile(), ".git/info"); + f.delete(); + f = new File(ignoreTestDir.getAbsoluteFile(), ".git"); + f.delete(); + } + + /** + * @param path + * Filepath relative to the git directory + * @return + * Results of cache.isIgnored(path) -- true if ignored, false if + * a negation is encountered or if no rules apply + */ + private boolean isIgnored(String path) { + try { + return cache.isIgnored(path); + } catch (IOException e) { + fail("IOException when attempting to check ignored status"); + } + return false; + } + + private String getRelativePath(File file) { + String retVal = db.getDirectory().getParentFile().toURI().relativize(file.toURI()).getPath(); + if (retVal.length() == file.getAbsolutePath().length()) + fail("Not a child of the git directory"); + if (retVal.endsWith("/")) + retVal = retVal.substring(0, retVal.length() - 1); + + return retVal; + } + + private void initCache() { + try { + cache.initialize(); + } catch (IOException e) { + e.printStackTrace(); + fail("Could not initialize cache"); + } + } + +} diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreMatcherTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreMatcherTest.java new file mode 100644 index 0000000000..cacad75558 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/ignore/IgnoreMatcherTest.java @@ -0,0 +1,364 @@ +/* + * Copyright (C) 2010, Red Hat Inc. + * 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.ignore; + +import junit.framework.Assert; +import junit.framework.TestCase; + + +/** + * Tests ignore pattern matches + */ +public class IgnoreMatcherTest extends TestCase{ + + public void testBasic() { + String pattern = "/test.stp"; + assertMatched(pattern, "/test.stp"); + + pattern = "#/test.stp"; + assertNotMatched(pattern, "/test.stp"); + } + + 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"); + assertMatched(pattern, "/src/new/a.c"); + assertMatched(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"); + assertMatched(pattern, "/src/new/a.c"); + assertMatched(pattern, "/src/new/a/a.c"); + assertMatched(pattern, "/neb"); + assertNotMatched(pattern, "/src/new.c"); + } + + 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"); + assertMatched(pattern, "src/new/a.c"); + assertMatched(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"); + assertMatched(pattern, "src/new/a.c"); + assertMatched(pattern, "src/new/a/a.c"); + assertMatched(pattern, "neb"); + assertNotMatched(pattern, "src/new.c"); + } + + public void testParentDirectoryGitIgnores() { + //Contains git ignore 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"); + assertMatched(pattern, "/src/new/a.c"); + assertMatched(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/"); + assertMatched(pattern, "/src/new/a.c"); + assertMatched(pattern, "/src/new/a/a.c"); + assertNotMatched(pattern, "/src/new"); + assertNotMatched(pattern, "/src/new.c"); + + //Test directory is matched by name only + pattern = "b1"; + assertMatched(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"); + } + + public void testTrailingSlash() { + String pattern = "/src/"; + assertMatched(pattern, "/src/"); + assertMatched(pattern, "/src/new"); + assertMatched(pattern, "/src/new/a.c"); + assertMatched(pattern, "/src/a.c"); + assertNotMatched(pattern, "/src"); + assertNotMatched(pattern, "/srcA/"); + } + + 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"); + + //Test matches for name-only, applies to file name or folder name + pattern = "src"; + assertMatched(pattern, "/src/a.c"); + assertMatched(pattern, "/src/new/a.c"); + assertMatched(pattern, "/new/src/a.c"); + assertMatched(pattern, "/file/src"); + assertMatched(pattern, "/src/"); + + //Test matches for name-only, applies to file name or folder name + //With a small wildcard + pattern = "?rc"; + assertMatched(pattern, "/src/a.c"); + assertMatched(pattern, "/src/new/a.c"); + assertMatched(pattern, "/new/src/a.c"); + 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]"; + assertMatched(pattern, "/src/a.c"); + assertMatched(pattern, "/src/new/a.c"); + assertMatched(pattern, "/new/src/a.c"); + assertMatched(pattern, "/file/src"); + assertMatched(pattern, "/src/"); + assertMatched(pattern, "/srb/a.c"); + assertMatched(pattern, "/grb/new/a.c"); + assertMatched(pattern, "/new/crb/a.c"); + assertMatched(pattern, "/file/3rb"); + assertMatched(pattern, "/xrb/"); + assertMatched(pattern, "/3ra/a.c"); + assertMatched(pattern, "/5ra/new/a.c"); + assertMatched(pattern, "/new/1ra/a.c"); + assertMatched(pattern, "/file/dra"); + assertMatched(pattern, "/era/"); + assertNotMatched(pattern, "/crg"); + assertNotMatched(pattern, "/cr3"); + } + + public void testNegation() { + String pattern = "!/test.stp"; + assertMatched(pattern, "/test.stp"); + } + + public void testGetters() { + IgnoreRule r = new IgnoreRule("/pattern/"); + assertFalse(r.getNameOnly()); + assertTrue(r.dirOnly()); + assertFalse(r.getNegation()); + assertEquals(r.getPattern(), "/pattern"); + + r = new IgnoreRule("/patter?/"); + assertFalse(r.getNameOnly()); + assertTrue(r.dirOnly()); + assertFalse(r.getNegation()); + assertEquals(r.getPattern(), "/patter?"); + + r = new IgnoreRule("patt*"); + assertTrue(r.getNameOnly()); + assertFalse(r.dirOnly()); + assertFalse(r.getNegation()); + assertEquals(r.getPattern(), "patt*"); + + r = new IgnoreRule("pattern"); + assertTrue(r.getNameOnly()); + assertFalse(r.dirOnly()); + assertFalse(r.getNegation()); + assertEquals(r.getPattern(), "pattern"); + + r = new IgnoreRule("!pattern"); + assertTrue(r.getNameOnly()); + assertFalse(r.dirOnly()); + assertTrue(r.getNegation()); + assertEquals(r.getPattern(), "pattern"); + + r = new IgnoreRule("!/pattern"); + assertFalse(r.getNameOnly()); + assertFalse(r.dirOnly()); + assertTrue(r.getNegation()); + assertEquals(r.getPattern(), "/pattern"); + + r = new IgnoreRule("!/patter?"); + assertFalse(r.getNameOnly()); + assertFalse(r.dirOnly()); + assertTrue(r.getNegation()); + assertEquals(r.getPattern(), "/patter?"); + } + + /** + * 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 + */ + public void assertMatched(String pattern, String target) { + boolean value = match(pattern, target); + Assert.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 .gitignore file + * @param target + * Target file path relative to repository's GIT_DIR + */ + public void assertNotMatched(String pattern, String target) { + boolean value = match(pattern, target); + Assert.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 .gitignore file + * @param target + * Target file path relative to repository's GIT_DIR + * @return + * Result of {@link IgnoreRule#isMatch(String, boolean)} + */ + private boolean match(String pattern, String target) { + IgnoreRule r = new IgnoreRule(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("/")); + } +} |