- Implement a function aggregating commit stats by author and/or by week|month - Deletes type hints - Modify TempRemoteRepo to be able to force remote fetch even for local repository (usefull in tests)
265 lines
9.3 KiB
Python
265 lines
9.3 KiB
Python
#!/usr/bin/env python3
|
|
""" Tests for git_oh script """
|
|
|
|
import calendar
|
|
import datetime
|
|
import gc
|
|
import os
|
|
import random
|
|
import shutil
|
|
import tempfile
|
|
import unittest
|
|
|
|
import git
|
|
|
|
import git_oh
|
|
|
|
|
|
class TestGitIterations(unittest.TestCase):
|
|
""" Testing iterator on git commits """
|
|
|
|
def setUp(self):
|
|
self.repo_path = tempfile.mkdtemp(prefix="git_oh_test_")
|
|
self.repo = git.Repo.init(self.repo_path)
|
|
self.actors = [git.Actor(f"User{i:d}", f"user{i:d}@localhost")
|
|
for i in range(8)]
|
|
|
|
def tearDown(self):
|
|
shutil.rmtree(self.repo_path)
|
|
|
|
def test_metadata(self):
|
|
""" Fetch commits and their metadata in a simple git repository """
|
|
fromisoformat = datetime.datetime.fromisoformat
|
|
commit_date_fmt = "2022-12-13T23:32:2%d+0200"
|
|
|
|
commits = [
|
|
self.git_commit_mod(author=self.actors[0],
|
|
commit_date=fromisoformat(commit_date_fmt % i))
|
|
for i in range(10)]
|
|
found_commits = list(git_oh.iter_commits(self.repo))
|
|
|
|
set_a = {c.hexsha for c in commits}
|
|
set_b = {c.hexsha for c in found_commits}
|
|
|
|
self.assertEqual(set_a, set_b)
|
|
|
|
for commit in found_commits:
|
|
self.assertEqual(commit.author, self.actors[0])
|
|
self.assertLessEqual(commit.committed_datetime,
|
|
fromisoformat(commit_date_fmt % 9))
|
|
self.assertGreaterEqual(commit.committed_datetime,
|
|
fromisoformat(commit_date_fmt % 0))
|
|
|
|
def test_branches(self):
|
|
""" Fetch commits from a repo with mutliple branches """
|
|
|
|
commits = [self.git_commit_mod(author=self.actors[0])
|
|
for _ in range(10)]
|
|
|
|
self.repo.git.checkout("HEAD", b="new_branch")
|
|
|
|
commits += [self.git_commit_mod(author=self.actors[0])
|
|
for _ in range(10)]
|
|
|
|
self.repo.git.checkout("HEAD", b="another_branch")
|
|
|
|
commits += [self.git_commit_mod(author=self.actors[0])
|
|
for _ in range(10)]
|
|
|
|
self.repo.git.checkout("new_branch")
|
|
|
|
found_commits = {commit.hexsha
|
|
for commit in git_oh.iter_commits(self.repo)}
|
|
commits = {commit.hexsha for commit in commits}
|
|
|
|
self.assertEqual(commits, found_commits)
|
|
|
|
|
|
def test_temp_remote_repo(self):
|
|
""" Testing TempRemoteRepo class commit fetch __iter__ method """
|
|
commits = {self.git_commit_mod(author=self.actors[0]).hexsha
|
|
for _ in range(10)}
|
|
repo = git_oh.TempRemoteRepo(f"file://{self.repo_path:s}",
|
|
force_remote=True)
|
|
|
|
found_commits = {commit.hexsha for commit in repo}
|
|
|
|
self.assertEqual(commits, found_commits)
|
|
|
|
|
|
def test_temp_remote_repo_cleanup(self):
|
|
""" Testing TempRemoteRepo class cleanup """
|
|
self.git_commit_mod(author=self.actors[0])
|
|
repo = git_oh.TempRemoteRepo(f"file://{self.repo_path:s}",
|
|
force_remote=True)
|
|
tmppath = repo.temppath
|
|
|
|
self.assertTrue(os.path.isdir(tmppath))
|
|
|
|
# ref to commits should prevent gc to call repo.__del__()
|
|
commits = list(repo)
|
|
|
|
del repo
|
|
gc.collect()
|
|
self.assertTrue(os.path.isdir(tmppath))
|
|
|
|
del commits
|
|
gc.collect() # not more refs, gc should have called repo.__del__()
|
|
self.assertFalse(os.path.isdir(tmppath))
|
|
|
|
|
|
def test_temp_remote_repo_branches(self):
|
|
""" Testing TempRemoteRepo branch commit fetch """
|
|
commits = []
|
|
for i in range(5):
|
|
commits += [self.git_commit_mod(author=self.actors[0])
|
|
for _ in range(10)]
|
|
self.repo.git.checkout("HEAD", b=f"branch-{i:d}")
|
|
repo_url = f"file://{self.repo_path:s}"
|
|
repo = git_oh.TempRemoteRepo(repo_url, force_remote=True)
|
|
found_commits = {commit.hexsha for commit in repo}
|
|
commits = {commit.hexsha for commit in commits}
|
|
|
|
self.assertEqual(commits, found_commits)
|
|
|
|
|
|
def git_commit_mod(self, msg="new commit", filename="foo.txt",
|
|
**commit_kwargs):
|
|
""" Add a random modification to repository and commit them
|
|
Arguments :
|
|
- msg : commit message
|
|
- filename : the filename to modify/add/commit
|
|
- **commit_kwargs : see help(git.index.base.IndexFile.commit)
|
|
Returns :
|
|
- the commit instance
|
|
"""
|
|
with open(os.path.join(self.repo_path, filename), "w+",
|
|
encoding="utf-8") as repo_fp:
|
|
repo_fp.write(random.choices("abcdef")[0])
|
|
self.repo.index.add([filename])
|
|
return self.repo.index.commit(msg, **commit_kwargs)
|
|
|
|
|
|
class TestTimeUtils(unittest.TestCase):
|
|
""" Testing in_office_hours() filter and valid_day() argument validator """
|
|
|
|
def test_valid_days(self):
|
|
""" Test day argument validator/convertion """
|
|
|
|
for i, day in enumerate(calendar.day_name):
|
|
self.assertEqual(i, git_oh.valid_day(day))
|
|
for i, day in enumerate(calendar.day_abbr):
|
|
self.assertEqual(i, git_oh.valid_day(day))
|
|
for i in range(7):
|
|
with self.subTest(day=i):
|
|
self.assertEqual(i, git_oh.valid_day(str(i)))
|
|
|
|
def test_office_hours_weekend(self):
|
|
""" Test in_office_hours filtering weekend """
|
|
|
|
weekends = ([5,6], [0], [], [1,2,3,4,5,6])
|
|
|
|
daydelta = datetime.timedelta(days=1)
|
|
|
|
for weekend in weekends:
|
|
test_dt = datetime.datetime(1988,12,13,12,0,0)
|
|
|
|
for _ in range(14):
|
|
with self.subTest(weekend=weekend, moment=test_dt):
|
|
res = git_oh.in_office_hours(test_dt,
|
|
starthour=datetime.time(8,0,0),
|
|
stophour=datetime.time(20,0,0),
|
|
weekend=weekend)
|
|
expected = test_dt.weekday() not in weekend
|
|
self.assertEqual(expected, res)
|
|
test_dt -= daydelta
|
|
|
|
def test_office_hours(self):
|
|
""" Test in_office_hours filtering hours """
|
|
starthour = datetime.time(8,0)
|
|
stophour = datetime.time(20,0)
|
|
weekend = (5,6)
|
|
|
|
off_oh = [(7,59,59),(20,0,1), (20,1), (0,0), (1,0)]
|
|
in_oh = [(8,0), (20,0), (12,0)]
|
|
|
|
for time_arg in off_oh:
|
|
moment = datetime.datetime(2023, 11, 1, *time_arg)
|
|
self.assertFalse(git_oh.in_office_hours(moment,
|
|
starthour, stophour, weekend=weekend))
|
|
|
|
for time_arg in in_oh:
|
|
moment = datetime.datetime(2023, 11, 1, *time_arg)
|
|
self.assertTrue(git_oh.in_office_hours(moment,
|
|
starthour, stophour, weekend=weekend))
|
|
|
|
def test_office_hours_tz_drop(self):
|
|
""" Checks that in_office_hours do not use tzinfo to compare
|
|
moment with start & stop hours
|
|
"""
|
|
moment = datetime.datetime.fromisoformat("2023-11-01T08:00:01+0200")
|
|
self.assertTrue(git_oh.in_office_hours(
|
|
moment,
|
|
datetime.time(8,0),
|
|
datetime.time(8,1),
|
|
weekend=[]))
|
|
|
|
moment = moment.astimezone(datetime.timezone.utc)
|
|
self.assertFalse(git_oh.in_office_hours(
|
|
moment,
|
|
datetime.time(8,0),
|
|
datetime.time(8,1),
|
|
weekend=[]))
|
|
|
|
def test_office_hours_invalid_weekend(self):
|
|
""" Test in_office_hours weekend validation """
|
|
bad_weekend = ([-1,0,1], [5,6,7], [7])
|
|
for badarg in bad_weekend:
|
|
with self.assertRaises(ValueError):
|
|
git_oh.in_office_hours(datetime.datetime.now(),
|
|
weekend=badarg)
|
|
|
|
def test_office_hours_tz_warn(self):
|
|
""" in_office_hours should warn when tz aware times are given """
|
|
utc_tz = datetime.timezone.utc
|
|
some_tz = datetime.timezone(datetime.timedelta(hours=2))
|
|
tz_aware = [
|
|
(datetime.time(1,2,tzinfo=utc_tz), datetime.time(0,0)),
|
|
(datetime.time(3,4,tzinfo=some_tz), datetime.time(1,0)),
|
|
(datetime.time(4,5), datetime.time(2,0, tzinfo=utc_tz)),
|
|
(datetime.time(4,5), datetime.time(2,0, tzinfo=some_tz)),
|
|
(datetime.time(1,2,tzinfo=utc_tz),
|
|
datetime.time(0,0, tzinfo=some_tz)),
|
|
]
|
|
|
|
for bad_args in tz_aware:
|
|
with self.assertWarns(Warning):
|
|
git_oh.in_office_hours(datetime.datetime.now(), *bad_args)
|
|
|
|
class TestCli(unittest.TestCase):
|
|
""" Testing CLI arguments parsing """
|
|
|
|
def test_weekend_parser(self):
|
|
""" Testing weekend parser """
|
|
for _ in range(10):
|
|
weekend = random.choices(list(range(7)), k=random.randint(1,5))
|
|
weekend_arg = " , ".join([calendar.day_abbr[i] for i in weekend])
|
|
with self.subTest(weekend=f"-w '{weekend_arg}'"):
|
|
try:
|
|
cliargs = ["file://fake_url", "-w", weekend_arg]
|
|
args = git_oh.parse_args(cliargs)
|
|
self.assertEqual(set(weekend), set(args.weekend))
|
|
except SystemExit:
|
|
self.fail(msg="Parser fails to parse --weekend argument")
|
|
|
|
def test_nul_weekend_parser(self):
|
|
""" Testing -w NUl argument support """
|
|
try:
|
|
args = git_oh.parse_args(["file://fake_url", "-w", "NUL"])
|
|
except SystemExit:
|
|
self.fail(msg="Parser fails to parse -w NUL")
|
|
self.assertEqual(len(args.weekend), 0)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|