Coverage for glotter/testinfo.py: 100%

74 statements  

« prev     ^ index     » next       coverage.py v7.6.12, created at 2025-04-12 02:25 +0000

1import yaml 

2from jinja2 import Environment, BaseLoader 

3 

4from glotter.project import NamingScheme 

5from glotter.settings import Settings 

6 

7 

8class ContainerInfo: 

9 """Configuration for a container to run for a directory""" 

10 

11 def __init__(self, image, tag, cmd, build=None): 

12 """ 

13 Initialize a ContainerInfo 

14 

15 :param image: the image to run 

16 :param tag: the tag of the image to run 

17 :param cmd: the command to run the source inside the container 

18 :param build: an optional command to run to build the source before running the command 

19 """ 

20 self._image = image 

21 self._cmd = cmd 

22 self._tag = tag 

23 self._build = build 

24 

25 @property 

26 def image(self): 

27 """Returns the image to run""" 

28 return self._image 

29 

30 @property 

31 def cmd(self): 

32 """Returns the command to run the source inside the container""" 

33 return self._cmd 

34 

35 @property 

36 def tag(self): 

37 """Returns the tag of the image to run""" 

38 return self._tag 

39 

40 @property 

41 def build(self): 

42 """Returns the command to build the source before running it inside the container""" 

43 return self._build 

44 

45 @classmethod 

46 def from_dict(cls, dictionary): 

47 """ 

48 Create a ContainerInfo from a dictionary 

49 

50 :param dictionary: the dictionary representing ContainerInfo 

51 :return: a new ContainerInfo 

52 """ 

53 image = dictionary["image"] 

54 tag = dictionary["tag"] 

55 cmd = dictionary["cmd"] 

56 build = dictionary["build"] if "build" in dictionary else None 

57 return ContainerInfo(image=image, tag=tag, cmd=cmd, build=build) 

58 

59 def __eq__(self, other): 

60 return ( 

61 self.image == other.image 

62 and self.cmd == other.cmd 

63 and self.tag == other.tag 

64 and self.build == other.build 

65 ) 

66 

67 

68class FolderInfo: 

69 """Metadata about sources in a directory""" 

70 

71 def __init__(self, extension, naming): 

72 """ 

73 Initialize a FolderInfo 

74 

75 :param extension: the file extension that is considered as source 

76 :param naming: the naming scheme for files in the directory 

77 """ 

78 self._extension = extension 

79 try: 

80 self._naming = NamingScheme[naming] 

81 except KeyError as e: 

82 raise KeyError(f'Unknown naming scheme: "{naming}"') from e 

83 

84 @property 

85 def extension(self): 

86 """Returns the extension for sources in the directory""" 

87 return self._extension 

88 

89 @property 

90 def naming(self): 

91 """Returns the naming scheme for the directory""" 

92 return self._naming 

93 

94 def get_project_mappings(self, include_extension=False): 

95 """ 

96 Uses the naming scheme to generate the expected source names in the directory 

97 and create a mapping from ProjectType to source name 

98 

99 :param include_extension: whether to include the extension in the source name 

100 :return: a dict where the key is a ProjectType and the value is the source name 

101 """ 

102 extension = self.extension if include_extension else "" 

103 return { 

104 project_type: f"{project.get_project_name_by_scheme(self.naming)}{extension}" 

105 for project_type, project in Settings().projects.items() 

106 } 

107 

108 def __eq__(self, other): 

109 return self.extension == other.extension and self.naming == other.naming 

110 

111 @classmethod 

112 def from_dict(cls, dictionary): 

113 """ 

114 Create a FileInfo from a dictionary 

115 

116 :param dictionary: the dictionary representing FileInfo 

117 :return: a new FileInfo 

118 """ 

119 return FolderInfo(dictionary["extension"], dictionary["naming"]) 

120 

121 

122class TestInfo: 

123 """an object representation of a testinfo file""" 

124 

125 __test__ = False # Indicate this is not a test 

126 

127 def __init__(self, container_info, file_info): 

128 """ 

129 Initialize a TestInfo object 

130 

131 :param container_info: ContainerInfo object 

132 :param file_info: FileInfo object 

133 """ 

134 self._container_info = container_info 

135 self._file_info = file_info 

136 

137 @property 

138 def container_info(self): 

139 """Return container info section""" 

140 return self._container_info 

141 

142 @property 

143 def file_info(self): 

144 """Return file info section""" 

145 return self._file_info 

146 

147 @classmethod 

148 def from_dict(cls, dictionary): 

149 """ 

150 Create a TestInfo from a dictionary 

151 

152 :param dictionary: the dictionary representing TestInfo 

153 :return: a new TestInfo 

154 """ 

155 return TestInfo( 

156 container_info=ContainerInfo.from_dict(dictionary["container"]), 

157 file_info=FolderInfo.from_dict(dictionary["folder"]), 

158 ) 

159 

160 @classmethod 

161 def from_string(cls, string, source): 

162 """ 

163 Create a TestInfo from a string. Modify the string using Jinja2 templating. Then parse it as yaml 

164 

165 :param string: contents of a testinfo file 

166 :param source: a source object to use for jinja2 template parsing 

167 :return: a new TestInfo 

168 """ 

169 template = Environment(loader=BaseLoader).from_string(string) 

170 template_string = template.render(source=source) 

171 info_yaml = yaml.safe_load(template_string) 

172 return cls.from_dict(info_yaml) 

173 

174 def __eq__(self, other): 

175 return ( 

176 self.container_info == other.container_info 

177 and self.file_info == other.file_info 

178 )