From ec14b3742103754d9022782587d0b5cf2f9fd1e3 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 25 Apr 2024 04:59:48 +0200 Subject: Merging upstream version 0.1.29. Signed-off-by: Daniel Baumann --- .../d_4b9be07fb6071cd2_test_spec_py.html | 463 --------------------- 1 file changed, 463 deletions(-) delete mode 100644 coverage-report/d_4b9be07fb6071cd2_test_spec_py.html (limited to 'coverage-report/d_4b9be07fb6071cd2_test_spec_py.html') diff --git a/coverage-report/d_4b9be07fb6071cd2_test_spec_py.html b/coverage-report/d_4b9be07fb6071cd2_test_spec_py.html deleted file mode 100644 index 4c0ed29..0000000 --- a/coverage-report/d_4b9be07fb6071cd2_test_spec_py.html +++ /dev/null @@ -1,463 +0,0 @@ - - - - - Coverage for src/debputy/plugin/api/test_api/test_spec.py: 100% - - - - - -
-
-

- Coverage for src/debputy/plugin/api/test_api/test_spec.py: - 100% -

- -

- 79 statements   - - - - -

-

- « prev     - ^ index     - » next -       - coverage.py v7.2.7, - created at 2024-04-07 12:14 +0200 -

- -
-
-
-

1import dataclasses 

-

2import os 

-

3from abc import ABCMeta 

-

4from typing import ( 

-

5 Iterable, 

-

6 Mapping, 

-

7 Callable, 

-

8 Optional, 

-

9 Union, 

-

10 List, 

-

11 Tuple, 

-

12 Set, 

-

13 Sequence, 

-

14 Generic, 

-

15 Type, 

-

16 Self, 

-

17 FrozenSet, 

-

18) 

-

19 

-

20from debian.substvars import Substvars 

-

21 

-

22from debputy import filesystem_scan 

-

23from debputy.plugin.api import ( 

-

24 VirtualPath, 

-

25 PackageProcessingContext, 

-

26 DpkgTriggerType, 

-

27 Maintscript, 

-

28) 

-

29from debputy.plugin.api.impl_types import PluginProvidedTrigger 

-

30from debputy.plugin.api.spec import DSD, ServiceUpgradeRule, PathDef 

-

31from debputy.substitution import VariableContext 

-

32 

-

33DEBPUTY_TEST_AGAINST_INSTALLED_PLUGINS = ( 

-

34 os.environ.get("DEBPUTY_TEST_PLUGIN_LOCATION", "uninstalled") == "installed" 

-

35) 

-

36 

-

37 

-

38@dataclasses.dataclass(slots=True, frozen=True) 

-

39class ADRExampleIssue: 

-

40 name: str 

-

41 example_index: int 

-

42 inconsistent_paths: Sequence[str] 

-

43 

-

44 

-

45def build_virtual_file_system( 

-

46 paths: Iterable[Union[str, PathDef]], 

-

47 read_write_fs: bool = True, 

-

48) -> VirtualPath: 

-

49 """Create a pure-virtual file system for use with metadata detectors 

-

50 

-

51 This method will generate a virtual file system a list of path names or virtual path definitions. It will 

-

52 also insert any implicit path required to make the file system connected. As an example: 

-

53 

-

54 >>> fs_root = build_virtual_file_system(['./usr/share/doc/package/copyright']) 

-

55 >>> # The file we explicitly requested is obviously there 

-

56 >>> fs_root.lookup('./usr/share/doc/package/copyright') is not None 

-

57 True 

-

58 >>> # but so is every directory up to that point 

-

59 >>> all(fs_root.lookup(d).is_dir 

-

60 ... for d in ['./usr', './usr/share', './usr/share/doc', './usr/share/doc/package'] 

-

61 ... ) 

-

62 True 

-

63 

-

64 Any string provided will be passed to `virtual_path` using all defaults for other parameters, making `str` 

-

65 arguments a nice easy shorthand if you just want a path to exist, but do not really care about it otherwise 

-

66 (or `virtual_path_def` defaults happens to work for you). 

-

67 

-

68 Here is a very small example of how to create some basic file system objects to get you started: 

-

69 

-

70 >>> from debputy.plugin.api import virtual_path_def 

-

71 >>> path_defs = [ 

-

72 ... './usr/share/doc/', # Create a directory 

-

73 ... virtual_path_def("./bin/zcat", link_target="/bin/gzip"), # Create a symlink 

-

74 ... virtual_path_def("./bin/gzip", mode=0o755), # Create a file (with a custom mode) 

-

75 ... ] 

-

76 >>> fs_root = build_virtual_file_system(path_defs) 

-

77 >>> fs_root.lookup('./usr/share/doc').is_dir 

-

78 True 

-

79 >>> fs_root.lookup('./bin/zcat').is_symlink 

-

80 True 

-

81 >>> fs_root.lookup('./bin/zcat').readlink() == '/bin/gzip' 

-

82 True 

-

83 >>> fs_root.lookup('./bin/gzip').is_file 

-

84 True 

-

85 >>> fs_root.lookup('./bin/gzip').mode == 0o755 

-

86 True 

-

87 

-

88 :param paths: An iterable any mix of path names (str) and virtual_path_def definitions 

-

89 (results from `virtual_path_def`). 

-

90 :param read_write_fs: Whether the file system is read-write (True) or read-only (False). 

-

91 Note that this is the default permission; the plugin test API may temporarily turn a 

-

92 read-write to read-only temporarily (when running a metadata detector, etc.). 

-

93 :return: The root of the generated file system 

-

94 """ 

-

95 return filesystem_scan.build_virtual_fs(paths, read_write_fs=read_write_fs) 

-

96 

-

97 

-

98@dataclasses.dataclass(slots=True, frozen=True) 

-

99class RegisteredTrigger: 

-

100 dpkg_trigger_type: DpkgTriggerType 

-

101 dpkg_trigger_target: str 

-

102 

-

103 def serialized_format(self) -> str: 

-

104 """The semantic contents of the DEBIAN/triggers file""" 

-

105 return f"{self.dpkg_trigger_type} {self.dpkg_trigger_target}" 

-

106 

-

107 @classmethod 

-

108 def from_plugin_provided_trigger( 

-

109 cls, 

-

110 plugin_provided_trigger: PluginProvidedTrigger, 

-

111 ) -> "Self": 

-

112 return cls( 

-

113 plugin_provided_trigger.dpkg_trigger_type, 

-

114 plugin_provided_trigger.dpkg_trigger_target, 

-

115 ) 

-

116 

-

117 

-

118@dataclasses.dataclass(slots=True, frozen=True) 

-

119class RegisteredMaintscript: 

-

120 """Details about a maintscript registered by a plugin""" 

-

121 

-

122 """Which maintscript is applies to (e.g., "postinst")""" 

-

123 maintscript: Maintscript 

-

124 """Which method was used to trigger the script (e.g., "on_configure")""" 

-

125 registration_method: str 

-

126 """The snippet provided by the plugin as it was provided 

-

127 

-

128 That is, no indentation/conditions/substitutions have been applied to this text 

-

129 """ 

-

130 plugin_provided_script: str 

-

131 """Whether substitutions would have been applied in a production run""" 

-

132 requested_substitution: bool 

-

133 

-

134 

-

135@dataclasses.dataclass(slots=True, frozen=True) 

-

136class DetectedService(Generic[DSD]): 

-

137 path: VirtualPath 

-

138 names: Sequence[str] 

-

139 type_of_service: str 

-

140 service_scope: str 

-

141 enable_by_default: bool 

-

142 start_by_default: bool 

-

143 default_upgrade_rule: ServiceUpgradeRule 

-

144 service_context: Optional[DSD] 

-

145 

-

146 

-

147class RegisteredPackagerProvidedFile(metaclass=ABCMeta): 

-

148 """Record of a registered packager provided file - No instantiation 

-

149 

-

150 New "mandatory" attributes may be added in minor versions, which means instantiation will break tests. 

-

151 Plugin providers should therefore not create instances of this dataclass. It is visible only to aid 

-

152 test writing by providing type-safety / auto-completion. 

-

153 """ 

-

154 

-

155 """The name stem used for generating the file""" 

-

156 stem: str 

-

157 """The recorded directory these file should be installed into""" 

-

158 installed_path: str 

-

159 """The mode that debputy will give these files when installed (unless overridden)""" 

-

160 default_mode: int 

-

161 """The default priority assigned to files unless overridden (if priories are assigned at all)""" 

-

162 default_priority: Optional[int] 

-

163 """The filename format to be used""" 

-

164 filename_format: Optional[str] 

-

165 """The formatting correcting callback""" 

-

166 post_formatting_rewrite: Optional[Callable[[str], str]] 

-

167 

-

168 def compute_dest( 

-

169 self, 

-

170 assigned_name: str, 

-

171 *, 

-

172 assigned_priority: Optional[int] = None, 

-

173 owning_package: Optional[str] = None, 

-

174 path: Optional[VirtualPath] = None, 

-

175 ) -> Tuple[str, str]: 

-

176 """Determine the basename of this packager provided file 

-

177 

-

178 This method is useful for verifying that the `installed_path` and `post_formatting_rewrite` works 

-

179 as intended. As example, some programs do not support "." in their configuration files, so you might 

-

180 have a post_formatting_rewrite à la `lambda x: x.replace(".", "_")`. Then you can test it by 

-

181 calling `assert rppf.compute_dest("python3.11")[1] == "python3_11"` to verify that if a package like 

-

182 `python3.11` were to use this packager provided file, it would still generate a supported file name. 

-

183 

-

184 For the `assigned_name` parameter, then this is normally derived from the filename. Examples for 

-

185 how to derive it: 

-

186 

-

187 * `debian/my-pkg.stem` => `my-pkg` 

-

188 * `debian/my-pkg.my-custom-name.stem` => `my-custom-name` 

-

189 

-

190 Note that all parts (`my-pkg`, `my-custom-name` and `stem`) can contain periods (".") despite 

-

191 also being a delimiter. Additionally, `my-custom-name` is not restricted to being a valid package 

-

192 name, so it can have any file-system valid character in it. 

-

193 

-

194 For the 0.01% case, where the plugin is using *both* `{name}` *and* `{owning_package}` in the 

-

195 installed_path, then you can separately *also* set the `owning_package` attribute. However, by 

-

196 default the `assigned_named` is used for both when `owning_package` is not provided. 

-

197 

-

198 :param assigned_name: The name assigned. Usually this is the name of the package containing the file. 

-

199 :param assigned_priority: Optionally a priority override for the file (if priority is supported). Must be 

-

200 omitted/None if priorities are not supported. 

-

201 :param owning_package: Optionally the name of the owning package. It is only needed for those exceedingly 

-

202 rare cases where the `installed_path` contains both `{owning_package}` (usually in addition to `{name}`). 

-

203 :param path: Special-case param, only needed for when testing a special `debputy` PPF.. 

-

204 :return: A tuple of the directory name and the basename (in that order) that combined makes up that path 

-

205 that debputy would use. 

-

206 """ 

-

207 raise NotImplementedError 

-

208 

-

209 

-

210class RegisteredMetadata: 

-

211 __slots__ = () 

-

212 

-

213 @property 

-

214 def substvars(self) -> Substvars: 

-

215 """Returns the Substvars 

-

216 

-

217 :return: The substvars in their current state. 

-

218 """ 

-

219 raise NotImplementedError 

-

220 

-

221 @property 

-

222 def triggers(self) -> List[RegisteredTrigger]: 

-

223 raise NotImplementedError 

-

224 

-

225 def maintscripts( 

-

226 self, 

-

227 *, 

-

228 maintscript: Optional[Maintscript] = None, 

-

229 ) -> List[RegisteredMaintscript]: 

-

230 """Extract the maintscript provided by the given metadata detector 

-

231 

-

232 :param maintscript: If provided, only snippet registered for the given maintscript is returned. Can be 

-

233 used to say "Give me all the 'postinst' snippets by this metadata detector", which can simplify 

-

234 verification in some cases. 

-

235 :return: A list of all matching maintscript registered by the metadata detector. If the detector has 

-

236 not been run, then the list will be empty. If the metadata detector has been run multiple times, 

-

237 then this is the aggregation of all the runs. 

-

238 """ 

-

239 raise NotImplementedError 

-

240 

-

241 

-

242class InitializedPluginUnderTest: 

-

243 def packager_provided_files(self) -> Iterable[RegisteredPackagerProvidedFile]: 

-

244 """An iterable of all packager provided files registered by the plugin under test 

-

245 

-

246 If you want a particular order, please sort the result. 

-

247 """ 

-

248 return self.packager_provided_files_by_stem().values() 

-

249 

-

250 def packager_provided_files_by_stem( 

-

251 self, 

-

252 ) -> Mapping[str, RegisteredPackagerProvidedFile]: 

-

253 """All packager provided files registered by the plugin under test grouped by name stem""" 

-

254 raise NotImplementedError 

-

255 

-

256 def run_metadata_detector( 

-

257 self, 

-

258 metadata_detector_id: str, 

-

259 fs_root: VirtualPath, 

-

260 context: Optional[PackageProcessingContext] = None, 

-

261 ) -> RegisteredMetadata: 

-

262 """Run a metadata detector (by its ID) against a given file system 

-

263 

-

264 :param metadata_detector_id: The ID of the metadata detector to run 

-

265 :param fs_root: The file system the metadata detector should see (must be the root of the file system) 

-

266 :param context: The context the metadata detector should see. If not provided, one will be mock will be 

-

267 provided to the extent possible. 

-

268 :return: The metadata registered by the metadata detector 

-

269 """ 

-

270 raise NotImplementedError 

-

271 

-

272 def run_package_processor( 

-

273 self, 

-

274 package_processor_id: str, 

-

275 fs_root: VirtualPath, 

-

276 context: Optional[PackageProcessingContext] = None, 

-

277 ) -> None: 

-

278 """Run a package processor (by its ID) against a given file system 

-

279 

-

280 Note: Dependency processors are *not* run first. 

-

281 

-

282 :param package_processor_id: The ID of the package processor to run 

-

283 :param fs_root: The file system the package processor should see (must be the root of the file system) 

-

284 :param context: The context the package processor should see. If not provided, one will be mock will be 

-

285 provided to the extent possible. 

-

286 """ 

-

287 raise NotImplementedError 

-

288 

-

289 @property 

-

290 def declared_manifest_variables(self) -> Union[Set[str], FrozenSet[str]]: 

-

291 """Extract the manifest variables declared by the plugin 

-

292 

-

293 :return: All manifest variables declared by the plugin 

-

294 """ 

-

295 raise NotImplementedError 

-

296 

-

297 def automatic_discard_rules_examples_with_issues(self) -> Sequence[ADRExampleIssue]: 

-

298 """Validate examples of the automatic discard rules 

-

299 

-

300 For any failed example, use `debputy plugin show automatic-discard-rules <name>` to see 

-

301 the failed example in full. 

-

302 

-

303 :return: If any examples have issues, this will return a non-empty sequence with an 

-

304 entry with each issue. 

-

305 """ 

-

306 raise NotImplementedError 

-

307 

-

308 def run_service_detection_and_integrations( 

-

309 self, 

-

310 service_manager: str, 

-

311 fs_root: VirtualPath, 

-

312 context: Optional[PackageProcessingContext] = None, 

-

313 *, 

-

314 service_context_type_hint: Optional[Type[DSD]] = None, 

-

315 ) -> Tuple[List[DetectedService[DSD]], RegisteredMetadata]: 

-

316 """Run the service manager's detection logic and return the results 

-

317 

-

318 This method can be used to validate the service detection and integration logic of a plugin 

-

319 for a given service manager. 

-

320 

-

321 First the service detector is run and if it finds any services, the integrator code is then 

-

322 run on those services with their default values. 

-

323 

-

324 :param service_manager: The name of the service manager as provided during the initialization 

-

325 :param fs_root: The file system the system detector should see (must be the root of 

-

326 the file system) 

-

327 :param context: The context the service detector should see. If not provided, one will be mock 

-

328 will be provided to the extent possible. 

-

329 :param service_context_type_hint: Unused; but can be used as a type hint for `mypy` (etc.) 

-

330 to align the return type. 

-

331 :return: A tuple of the list of all detected services in the provided file system and the 

-

332 metadata generated by the integrator (if any services were detected). 

-

333 """ 

-

334 raise NotImplementedError 

-

335 

-

336 def manifest_variables( 

-

337 self, 

-

338 *, 

-

339 resolution_context: Optional[VariableContext] = None, 

-

340 mocked_variables: Optional[Mapping[str, str]] = None, 

-

341 ) -> Mapping[str, str]: 

-

342 """Provide a table of the manifest variables registered by the plugin 

-

343 

-

344 Each key is a manifest variable and the value of said key is the value of the manifest 

-

345 variable. Lazy loaded variables are resolved when accessed for the first time and may 

-

346 raise exceptions if the preconditions are not correct. 

-

347 

-

348 Note this method can be called multiple times with different parameters to provide 

-

349 different contexts. Lazy loaded variables are resolved at most once per context. 

-

350 

-

351 :param resolution_context: An optional context for lazy loaded manifest variables. 

-

352 Create an instance of it via `manifest_variable_resolution_context`. 

-

353 :param mocked_variables: An optional mapping that provides values for certain manifest 

-

354 variables. This can be used if you want a certain variable to have a certain value 

-

355 for the test to be stable (or because the manifest variable you are mocking is from 

-

356 another plugin, and you do not want to deal with the implementation details of how 

-

357 it is set). Any variable that depends on the mocked variable will use the mocked 

-

358 variable in the given context. 

-

359 :return: A table of the manifest variables provided by the plugin. Note this table 

-

360 only contains manifest variables registered by the plugin. Attempting to resolve 

-

361 other variables (directly), such as mocked variables or from other plugins, will 

-

362 trigger a `KeyError`. 

-

363 """ 

-

364 raise NotImplementedError 

-
- - - -- cgit v1.2.3