Coverage for tests/helpers.py: 100.00%

42 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-04-02 00:26 +0200

1"""Helpers for writing tests.""" 

2 

3from __future__ import annotations 

4 

5import os 

6import random 

7import shutil 

8import subprocess 

9import uuid 

10from contextlib import contextmanager 

11from typing import TYPE_CHECKING, Iterator 

12 

13if TYPE_CHECKING: 

14 from pathlib import Path 

15 

16 

17class GitRepo: 

18 """Test utility class to initalize and work with a Git repository.""" 

19 

20 _git_exec = shutil.which("git") or "git" 

21 

22 def __init__(self, repo: Path) -> None: 

23 """Initialization the Git repository wrapper. 

24 

25 Initializes a new Git repository under the given path. 

26 

27 Parameters: 

28 repo: Path to the git repository. 

29 """ 

30 self.path = repo 

31 self.path.mkdir(parents=True, exist_ok=True) 

32 self.git("init", "-b", "main") 

33 self.git("config", "user.name", "dummy") 

34 self.git("config", "user.email", "dummy@example.com") 

35 self.git("remote", "add", "origin", "git@github.com:example/example") 

36 self.first_hash = self.commit("chore: Initial repository creation") 

37 

38 def git(self, *args: str) -> str: 

39 """Run a Git command in the repository. 

40 

41 Parameters: 

42 *args: Arguments passed to the Git command. 

43 

44 Returns: 

45 The output of the command. 

46 """ 

47 return subprocess.check_output( 

48 [self._git_exec, "-C", str(self.path), *args], # noqa: S603 

49 text=True, 

50 ) 

51 

52 def commit(self, message: str) -> str: 

53 """Create, add and commit a new file into the Git repository. 

54 

55 Parameters: 

56 message: The commit message. 

57 

58 Returns: 

59 The Git commit hash. 

60 """ 

61 with self.path.joinpath(str(uuid.uuid4())).open("w") as fh: 

62 fh.write(str(random.randint(0, 1))) # noqa: S311 

63 self.git("add", "-A") 

64 self.git("commit", "-m", message) 

65 return self.git("rev-parse", "HEAD").rstrip() 

66 

67 def tag(self, tagname: str) -> None: 

68 """Create a new tag in the GIt repository. 

69 

70 Parameters: 

71 tagname: The name of the new tag. 

72 """ 

73 self.git("tag", tagname) 

74 

75 def branch(self, branchname: str) -> None: 

76 """Create a new branch in the Git repository. 

77 

78 Parameters: 

79 branchname: The name of the new branch. 

80 """ 

81 self.git("branch", branchname) 

82 

83 def checkout(self, branchname: str) -> None: 

84 """Checkout a branch. 

85 

86 Parameters: 

87 branchname: The name of the branch. 

88 """ 

89 self.git("checkout", branchname) 

90 

91 def merge(self, branchname: str) -> str: 

92 """Merge a branch into the current branch, creating a new merge commit. 

93 

94 Parameters: 

95 branchname: The name of the branch to merge. 

96 

97 Returns: 

98 The Git commit hash of the merge commit. 

99 """ 

100 self.git("merge", "--no-ff", "--commit", "-m", f"merge: Merge branch '{branchname}'", branchname) 

101 return self.git("rev-parse", "HEAD").rstrip() 

102 

103 @contextmanager 

104 def enter(self) -> Iterator[None]: 

105 """Context manager to enter the repository directory.""" 

106 old_cwd = os.getcwd() 

107 os.chdir(self.path) 

108 try: 

109 yield 

110 finally: 

111 os.chdir(old_cwd)