Git Office Hours : le test pour Entr'ouvert
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

test.py 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. #!/usr/bin/env python3
  2. """ Tests for git_oh script """
  3. import calendar
  4. import datetime
  5. import gc
  6. import os
  7. import random
  8. import shutil
  9. import tempfile
  10. import unittest
  11. import git
  12. import git_oh
  13. class TestGitIterations(unittest.TestCase):
  14. """ Testing iterator on git commits """
  15. def setUp(self):
  16. self.repo_path = tempfile.mkdtemp(prefix="git_oh_test_")
  17. self.repo = git.Repo.init(self.repo_path)
  18. self.actors = [git.Actor(f"User{i:d}", f"user{i:d}@localhost")
  19. for i in range(8)]
  20. def tearDown(self):
  21. shutil.rmtree(self.repo_path)
  22. def test_metadata(self):
  23. """ Fetch commits and their metadata in a simple git repository """
  24. fromisoformat = datetime.datetime.fromisoformat
  25. commit_date_fmt = "2022-12-13T23:32:2%d+0200"
  26. commits = [
  27. self.git_commit_mod(author=self.actors[0],
  28. commit_date=fromisoformat(commit_date_fmt % i))
  29. for i in range(10)]
  30. found_commits = list(git_oh.iter_commits(self.repo))
  31. set_a = {c.hexsha for c in commits}
  32. set_b = {c.hexsha for c in found_commits}
  33. self.assertEqual(set_a, set_b)
  34. for commit in found_commits:
  35. self.assertEqual(commit.author, self.actors[0])
  36. self.assertLessEqual(commit.committed_datetime,
  37. fromisoformat(commit_date_fmt % 9))
  38. self.assertGreaterEqual(commit.committed_datetime,
  39. fromisoformat(commit_date_fmt % 0))
  40. def test_branches(self):
  41. """ Fetch commits from a repo with mutliple branches """
  42. commits = [self.git_commit_mod(author=self.actors[0])
  43. for _ in range(10)]
  44. self.repo.git.checkout("HEAD", b="new_branch")
  45. commits += [self.git_commit_mod(author=self.actors[0])
  46. for _ in range(10)]
  47. self.repo.git.checkout("HEAD", b="another_branch")
  48. commits += [self.git_commit_mod(author=self.actors[0])
  49. for _ in range(10)]
  50. self.repo.git.checkout("new_branch")
  51. found_commits = {commit.hexsha
  52. for commit in git_oh.iter_commits(self.repo)}
  53. commits = {commit.hexsha for commit in commits}
  54. self.assertEqual(commits, found_commits)
  55. def test_temp_remote_repo(self):
  56. """ Testing TempRemoteRepo class commit fetch __iter__ method """
  57. commits = {self.git_commit_mod(author=self.actors[0]).hexsha
  58. for _ in range(10)}
  59. repo = git_oh.TempRemoteRepo(f"file://{self.repo_path:s}",
  60. force_remote=True)
  61. found_commits = {commit.hexsha for commit in repo}
  62. self.assertEqual(commits, found_commits)
  63. def test_temp_remote_repo_cleanup(self):
  64. """ Testing TempRemoteRepo class cleanup """
  65. self.git_commit_mod(author=self.actors[0])
  66. repo = git_oh.TempRemoteRepo(f"file://{self.repo_path:s}",
  67. force_remote=True)
  68. tmppath = repo.temppath
  69. self.assertTrue(os.path.isdir(tmppath))
  70. # ref to commits should prevent gc to call repo.__del__()
  71. commits = list(repo)
  72. del repo
  73. gc.collect()
  74. self.assertTrue(os.path.isdir(tmppath))
  75. del commits
  76. gc.collect() # not more refs, gc should have called repo.__del__()
  77. self.assertFalse(os.path.isdir(tmppath))
  78. def test_temp_remote_repo_branches(self):
  79. """ Testing TempRemoteRepo branch commit fetch """
  80. commits = []
  81. for i in range(5):
  82. commits += [self.git_commit_mod(author=self.actors[0])
  83. for _ in range(10)]
  84. self.repo.git.checkout("HEAD", b=f"branch-{i:d}")
  85. repo_url = f"file://{self.repo_path:s}"
  86. repo = git_oh.TempRemoteRepo(repo_url, force_remote=True)
  87. found_commits = {commit.hexsha for commit in repo}
  88. commits = {commit.hexsha for commit in commits}
  89. self.assertEqual(commits, found_commits)
  90. def git_commit_mod(self, msg="new commit", filename="foo.txt",
  91. **commit_kwargs):
  92. """ Add a random modification to repository and commit them
  93. Arguments :
  94. - msg : commit message
  95. - filename : the filename to modify/add/commit
  96. - **commit_kwargs : see help(git.index.base.IndexFile.commit)
  97. Returns :
  98. - the commit instance
  99. """
  100. with open(os.path.join(self.repo_path, filename), "w+",
  101. encoding="utf-8") as repo_fp:
  102. repo_fp.write(random.choices("abcdef")[0])
  103. self.repo.index.add([filename])
  104. return self.repo.index.commit(msg, **commit_kwargs)
  105. class TestTimeUtils(unittest.TestCase):
  106. """ Testing in_office_hours() filter and valid_day() argument validator """
  107. def test_valid_days(self):
  108. """ Test day argument validator/convertion """
  109. for i, day in enumerate(calendar.day_name):
  110. self.assertEqual(i, git_oh.valid_day(day))
  111. for i, day in enumerate(calendar.day_abbr):
  112. self.assertEqual(i, git_oh.valid_day(day))
  113. for i in range(7):
  114. with self.subTest(day=i):
  115. self.assertEqual(i, git_oh.valid_day(str(i)))
  116. def test_office_hours_weekend(self):
  117. """ Test in_office_hours filtering weekend """
  118. weekends = ([5,6], [0], [], [1,2,3,4,5,6])
  119. daydelta = datetime.timedelta(days=1)
  120. for weekend in weekends:
  121. test_dt = datetime.datetime(1988,12,13,12,0,0)
  122. for _ in range(14):
  123. with self.subTest(weekend=weekend, moment=test_dt):
  124. res = git_oh.in_office_hours(test_dt,
  125. starthour=datetime.time(8,0,0),
  126. stophour=datetime.time(20,0,0),
  127. weekend=weekend)
  128. expected = test_dt.weekday() not in weekend
  129. self.assertEqual(expected, res)
  130. test_dt -= daydelta
  131. def test_office_hours(self):
  132. """ Test in_office_hours filtering hours """
  133. starthour = datetime.time(8,0)
  134. stophour = datetime.time(20,0)
  135. weekend = (5,6)
  136. off_oh = [(7,59,59),(20,0,1), (20,1), (0,0), (1,0)]
  137. in_oh = [(8,0), (20,0), (12,0)]
  138. for time_arg in off_oh:
  139. moment = datetime.datetime(2023, 11, 1, *time_arg)
  140. self.assertFalse(git_oh.in_office_hours(moment,
  141. starthour, stophour, weekend=weekend))
  142. for time_arg in in_oh:
  143. moment = datetime.datetime(2023, 11, 1, *time_arg)
  144. self.assertTrue(git_oh.in_office_hours(moment,
  145. starthour, stophour, weekend=weekend))
  146. def test_office_hours_midnight(self):
  147. """ Test in_office_hours special case for daystop at midnight """
  148. starthour = datetime.time(12,0)
  149. stophour = datetime.time(0,0)
  150. moment = datetime.datetime.fromisoformat("2023-11-01T09:00+0200")
  151. self.assertFalse(git_oh.in_office_hours(moment, starthour, stophour))
  152. moment = datetime.datetime.fromisoformat("2023-11-01T13:00+0200")
  153. self.assertTrue(git_oh.in_office_hours(moment, starthour, stophour))
  154. def test_office_hours_nightwork(self):
  155. """ Testing in_office_hours when daystop < daystart """
  156. starthour = datetime.time(20,0)
  157. stophour = datetime.time(8,0)
  158. moment = datetime.datetime.fromisoformat("2023-11-01T00:00+0200")
  159. self.assertTrue(git_oh.in_office_hours(moment, starthour, stophour))
  160. moment = datetime.datetime.fromisoformat("2023-11-01T05:00+0200")
  161. self.assertTrue(git_oh.in_office_hours(moment, starthour, stophour))
  162. moment = datetime.datetime.fromisoformat("2023-11-01T23:00+0200")
  163. self.assertTrue(git_oh.in_office_hours(moment, starthour, stophour))
  164. moment = datetime.datetime.fromisoformat("2023-11-01T12:00+0200")
  165. self.assertFalse(git_oh.in_office_hours(moment, starthour, stophour))
  166. def test_office_hours_tz_drop(self):
  167. """ Checks that in_office_hours do not use tzinfo to compare
  168. moment with start & stop hours
  169. """
  170. moment = datetime.datetime.fromisoformat("2023-11-01T08:00:01+0200")
  171. self.assertTrue(git_oh.in_office_hours(
  172. moment,
  173. datetime.time(8,0),
  174. datetime.time(8,1),
  175. weekend=[]))
  176. moment = moment.astimezone(datetime.timezone.utc)
  177. self.assertFalse(git_oh.in_office_hours(
  178. moment,
  179. datetime.time(8,0),
  180. datetime.time(8,1),
  181. weekend=[]))
  182. def test_office_hours_invalid_weekend(self):
  183. """ Test in_office_hours weekend validation """
  184. bad_weekend = ([-1,0,1], [5,6,7], [7])
  185. for badarg in bad_weekend:
  186. with self.assertRaises(ValueError):
  187. git_oh.in_office_hours(datetime.datetime.now(),
  188. weekend=badarg)
  189. def test_office_hours_tz_warn(self):
  190. """ in_office_hours should warn when tz aware times are given """
  191. utc_tz = datetime.timezone.utc
  192. some_tz = datetime.timezone(datetime.timedelta(hours=2))
  193. tz_aware = [
  194. (datetime.time(1,2,tzinfo=utc_tz), datetime.time(0,0)),
  195. (datetime.time(3,4,tzinfo=some_tz), datetime.time(1,0)),
  196. (datetime.time(4,5), datetime.time(2,0, tzinfo=utc_tz)),
  197. (datetime.time(4,5), datetime.time(2,0, tzinfo=some_tz)),
  198. (datetime.time(1,2,tzinfo=utc_tz),
  199. datetime.time(0,0, tzinfo=some_tz)),
  200. ]
  201. for bad_args in tz_aware:
  202. with self.assertWarns(Warning):
  203. git_oh.in_office_hours(datetime.datetime.now(), *bad_args)
  204. class TestCli(unittest.TestCase):
  205. """ Testing CLI arguments parsing """
  206. def test_weekend_parser(self):
  207. """ Testing weekend parser """
  208. for _ in range(10):
  209. weekend = random.choices(list(range(7)), k=random.randint(1,5))
  210. weekend_arg = " , ".join([calendar.day_abbr[i] for i in weekend])
  211. with self.subTest(weekend=f"-w '{weekend_arg}'"):
  212. try:
  213. cliargs = ["file://fake_url", "-w", weekend_arg]
  214. args = git_oh.parse_args(cliargs)
  215. self.assertEqual(set(weekend), set(args.weekend))
  216. except SystemExit:
  217. self.fail(msg="Parser fails to parse --weekend argument")
  218. def test_nul_weekend_parser(self):
  219. """ Testing -w NUl argument support """
  220. try:
  221. args = git_oh.parse_args(["file://fake_url", "-w", "NUL"])
  222. except SystemExit:
  223. self.fail(msg="Parser fails to parse -w NUL")
  224. self.assertEqual(len(args.weekend), 0)
  225. if __name__ == "__main__":
  226. unittest.main()