Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 55 additions & 26 deletions tests.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,31 @@
from typing import Optional
from shutil import which
from unittest.mock import patch, mock_open
from pathlib import Path
from tempfile import TemporaryDirectory

from gitignore_parser import parse_gitignore

from unittest import TestCase, main
from unittest import TestCase, main, skipUnless

import tempfile
from subprocess import run


class Test(TestCase):
def test_simple(self):
matches = _parse_gitignore_string(
matches = self._parse_gitignore_string(
'__pycache__/\n'
'*.py[cod]',
fake_base_dir='/home/michael'
)
self.assertFalse(matches('/home/michael/main.py'))
self.assertTrue(matches('/home/michael/main.pyc'))
self.assertTrue(matches('/home/michael/dir/main.pyc'))
self.assertTrue(matches('/home/michael/__pycache__'))
self.assertTrue(matches('/home/michael/__pycache__/'))

def test_incomplete_filename(self):
matches = _parse_gitignore_string('o.py', fake_base_dir='/home/michael')
matches = self._parse_gitignore_string('o.py', fake_base_dir='/home/michael')
self.assertTrue(matches('/home/michael/o.py'))
self.assertFalse(matches('/home/michael/foo.py'))
self.assertFalse(matches('/home/michael/o.pyc'))
Expand All @@ -29,7 +34,7 @@ def test_incomplete_filename(self):
self.assertFalse(matches('/home/michael/dir/o.pyc'))

def test_wildcard(self):
matches = _parse_gitignore_string(
matches = self._parse_gitignore_string(
'hello.*',
fake_base_dir='/home/michael'
)
Expand All @@ -41,7 +46,7 @@ def test_wildcard(self):
self.assertFalse(matches('/home/michael/helloX'))

def test_anchored_wildcard(self):
matches = _parse_gitignore_string(
matches = self._parse_gitignore_string(
'/hello.*',
fake_base_dir='/home/michael'
)
Expand All @@ -50,7 +55,7 @@ def test_anchored_wildcard(self):
self.assertFalse(matches('/home/michael/a/hello.java'))

def test_trailingspaces(self):
matches = _parse_gitignore_string(
matches = self._parse_gitignore_string(
'ignoretrailingspace \n'
'notignoredspace\\ \n'
'partiallyignoredspace\\ \n'
Expand All @@ -73,7 +78,7 @@ def test_trailingspaces(self):
self.assertFalse(matches('/home/michael/notignoredmultiplespace'))

def test_comment(self):
matches = _parse_gitignore_string(
matches = self._parse_gitignore_string(
'somematch\n'
'#realcomment\n'
'othermatch\n'
Expand All @@ -87,7 +92,7 @@ def test_comment(self):

def test_ignore_directory(self):
matches = \
_parse_gitignore_string('.venv/', fake_base_dir='/home/michael')
self._parse_gitignore_string('.venv/', fake_base_dir='/home/michael')
self.assertTrue(matches('/home/michael/.venv'))
self.assertTrue(matches('/home/michael/.venv/folder'))
self.assertTrue(matches('/home/michael/.venv/file.txt'))
Expand All @@ -96,13 +101,13 @@ def test_ignore_directory(self):

def test_ignore_directory_asterisk(self):
matches = \
_parse_gitignore_string('.venv/*', fake_base_dir='/home/michael')
self._parse_gitignore_string('.venv/*', fake_base_dir='/home/michael')
self.assertFalse(matches('/home/michael/.venv'))
self.assertTrue(matches('/home/michael/.venv/folder'))
self.assertTrue(matches('/home/michael/.venv/file.txt'))

def test_negation(self):
matches = _parse_gitignore_string(
matches = self._parse_gitignore_string(
'''
*.ignore
!keep.ignore
Expand All @@ -114,15 +119,15 @@ def test_negation(self):
self.assertTrue(matches('/home/michael/waste.ignore'))

def test_literal_exclamation_mark(self):
matches = _parse_gitignore_string(
matches = self._parse_gitignore_string(
'\\!ignore_me!', fake_base_dir='/home/michael'
)
self.assertTrue(matches('/home/michael/!ignore_me!'))
self.assertFalse(matches('/home/michael/ignore_me!'))
self.assertFalse(matches('/home/michael/ignore_me'))

def test_double_asterisks(self):
matches = _parse_gitignore_string(
matches = self._parse_gitignore_string(
'foo/**/Bar', fake_base_dir='/home/michael'
)
self.assertTrue(matches('/home/michael/foo/hello/Bar'))
Expand All @@ -132,7 +137,7 @@ def test_double_asterisks(self):

def test_double_asterisk_without_slashes_handled_like_single_asterisk(self):
matches = \
_parse_gitignore_string('a/b**c/d', fake_base_dir='/home/michael')
self._parse_gitignore_string('a/b**c/d', fake_base_dir='/home/michael')
self.assertTrue(matches('/home/michael/a/bc/d'))
self.assertTrue(matches('/home/michael/a/bXc/d'))
self.assertTrue(matches('/home/michael/a/bbc/d'))
Expand All @@ -144,16 +149,16 @@ def test_double_asterisk_without_slashes_handled_like_single_asterisk(self):

def test_more_asterisks_handled_like_single_asterisk(self):
matches = \
_parse_gitignore_string('***a/b', fake_base_dir='/home/michael')
self._parse_gitignore_string('***a/b', fake_base_dir='/home/michael')
self.assertTrue(matches('/home/michael/XYZa/b'))
self.assertFalse(matches('/home/michael/foo/a/b'))
matches = \
_parse_gitignore_string('a/b***', fake_base_dir='/home/michael')
self._parse_gitignore_string('a/b***', fake_base_dir='/home/michael')
self.assertTrue(matches('/home/michael/a/bXYZ'))
self.assertFalse(matches('/home/michael/a/b/foo'))

def test_directory_only_negation(self):
matches = _parse_gitignore_string('''
matches = self._parse_gitignore_string('''
data/**
!data/**/
!.gitkeep
Expand All @@ -171,20 +176,20 @@ def test_directory_only_negation(self):
)

def test_single_asterisk(self):
matches = _parse_gitignore_string('*', fake_base_dir='/home/michael')
matches = self._parse_gitignore_string('*', fake_base_dir='/home/michael')
self.assertTrue(matches('/home/michael/file.txt'))
self.assertTrue(matches('/home/michael/directory'))
self.assertTrue(matches('/home/michael/directory-trailing/'))

def test_supports_path_type_argument(self):
matches = _parse_gitignore_string(
matches = self._parse_gitignore_string(
'file1\n!file2', fake_base_dir='/home/michael'
)
self.assertTrue(matches(Path('/home/michael/file1')))
self.assertFalse(matches(Path('/home/michael/file2')))

def test_slash_in_range_does_not_match_dirs(self):
matches = _parse_gitignore_string(
matches = self._parse_gitignore_string(
'abc[X-Z/]def', fake_base_dir='/home/michael'
)
self.assertFalse(matches('/home/michael/abcdef'))
Expand All @@ -198,7 +203,7 @@ def test_symlink_to_another_directory(self):
with TemporaryDirectory() as project_dir:
with TemporaryDirectory() as another_dir:
matches = \
_parse_gitignore_string('link', fake_base_dir=project_dir)
self._parse_gitignore_string('link', fake_base_dir=project_dir)

# Create a symlink to another directory.
link = Path(project_dir, 'link')
Expand All @@ -218,14 +223,38 @@ def test_symlink_to_symlink_directory(self):
link.symlink_to(project_dir)
file = Path(link, 'file.txt')
matches = \
_parse_gitignore_string('file.txt', fake_base_dir=str(link))
self._parse_gitignore_string('file.txt', fake_base_dir=str(link))
self.assertTrue(matches(file))

def _parse_gitignore_string(self, data: str, fake_base_dir: Optional[str] = None):
with patch('builtins.open', mock_open(read_data=data)):
success = parse_gitignore(f'{fake_base_dir}/.gitignore', fake_base_dir)
return success


@skipUnless(which("git"), "Git not installed")
class TestAgainstGit(Test):
"""Testcase running tests against `git check-ignore`.

This is not to test if git is right, this is to validate that all
tests from the Test class are valid according to git.
"""
def setUp(self):
self.tmpdir = tempfile.TemporaryDirectory()
run(["git", "init"], cwd=self.tmpdir.name)

def tearDown(self):
self.tmpdir.cleanup()

def _parse_gitignore_string(self, data: str, fake_base_dir: Optional[str] = None):
(Path(self.tmpdir.name) / ".gitignore").write_text(data, encoding="UTF-8")

def matcher(path):
path = str(path).replace(fake_base_dir + "/", "")
return run(["git", "check-ignore", path], cwd=self.tmpdir.name, check=False).returncode == 0

return matcher

def _parse_gitignore_string(data: str, fake_base_dir: str = None):
with patch('builtins.open', mock_open(read_data=data)):
success = parse_gitignore(f'{fake_base_dir}/.gitignore', fake_base_dir)
return success

if __name__ == '__main__':
main()