15
15
# SPDX-License-Identifier: Apache-2.0
16
16
# Copyright (c) OWASP Foundation. All Rights Reserved.
17
17
18
-
18
+ import sys
19
19
from json import dumps as json_dumps
20
- from os import getenv
21
- from os .path import dirname , join
20
+ from os import getenv , path
22
21
from pathlib import Path
23
22
from re import sub as re_sub
24
- from sys import stderr
25
- from typing import Union
23
+ from typing import Any , Dict , Union
26
24
from unittest import TestCase
27
25
from xml .sax .saxutils import escape as xml_escape , quoteattr as xml_quoteattr # nosec:B406
28
26
32
30
33
31
RECREATE_SNAPSHOTS = '1' == getenv ('CDX_TEST_RECREATE_SNAPSHOTS' )
34
32
if RECREATE_SNAPSHOTS :
35
- print ('!!! WILL RECREATE ALL SNAPSHOTS !!!' , file = stderr )
33
+ print ('!!! WILL RECREATE ALL SNAPSHOTS !!!' , file = sys . stderr )
36
34
37
35
INIT_TESTBEDS = '1' != getenv ('CDX_TEST_SKIP_INIT_TESTBEDS' )
38
36
if INIT_TESTBEDS :
39
- print ('!!! WILL INIT TESTBEDS !!!' , file = stderr )
37
+ print ('!!! WILL INIT TESTBEDS !!!' , file = sys . stderr )
40
38
41
- _TESTDATA_DIRECTORY = join (dirname (__file__ ), '_data' )
39
+ _TESTDATA_DIRECTORY = path . join (path . dirname (__file__ ), '_data' )
42
40
43
- INFILES_DIRECTORY = join (_TESTDATA_DIRECTORY , 'infiles' )
44
- SNAPSHOTS_DIRECTORY = join (_TESTDATA_DIRECTORY , 'snapshots' )
41
+ INFILES_DIRECTORY = path . join (_TESTDATA_DIRECTORY , 'infiles' )
42
+ SNAPSHOTS_DIRECTORY = path . join (_TESTDATA_DIRECTORY , 'snapshots' )
45
43
46
44
UNSUPPORTED_OF_SV = (
47
45
(OutputFormat .JSON , SchemaVersion .V1_1 ),
@@ -60,7 +58,7 @@ class SnapshotMixin:
60
58
61
59
@staticmethod
62
60
def getSnapshotFile (snapshot_name : str ) -> str : # noqa: N802
63
- return join (SNAPSHOTS_DIRECTORY , f'{ snapshot_name } .bin' )
61
+ return path . join (SNAPSHOTS_DIRECTORY , f'{ snapshot_name } .bin' )
64
62
65
63
@classmethod
66
64
def writeSnapshot (cls , snapshot_name : str , data : str ) -> None : # noqa: N802
@@ -92,62 +90,121 @@ def assertEqualSnapshot(self: Union[TestCase, 'SnapshotMixin'], # noqa: N802
92
90
_root_file_uri_xml_attr = xml_quoteattr (_root_file_uri )[1 :- 1 ]
93
91
_root_file_uri_json = json_dumps (_root_file_uri )[1 :- 1 ]
94
92
93
+ # package is called 'cyclonedx-bom', but the tool is called 'cyclonedx-py'
94
+ EXPECTED_TOOL_NAME = 'cyclonedx-py'
95
+
95
96
96
97
def make_xml_comparable (bom : str ) -> str :
97
98
bom = bom .replace (_root_file_uri_xml , 'file://.../' )
98
99
bom = bom .replace (_root_file_uri_xml_attr , 'file://.../' )
99
- bom = bom .replace ( # replace metadata.tools.version
100
+ bom = bom .replace ( # replace this version in metadata.tools.components
101
+ ' <group>CycloneDX</group>\n '
102
+ f' <name>{ EXPECTED_TOOL_NAME } </name>\n '
103
+ f' <version>{ __this_version } </version>' ,
104
+ ' <group>CycloneDX</group>\n '
105
+ f' <name>{ EXPECTED_TOOL_NAME } </name>\n '
106
+ ' <version>thisVersion-testing</version>' )
107
+ bom = bom .replace ( # replace this version in metadata.tools
100
108
' <vendor>CycloneDX</vendor>\n '
101
- ' <name>cyclonedx-bom </name>\n '
109
+ f ' <name>{ EXPECTED_TOOL_NAME } </name>\n '
102
110
f' <version>{ __this_version } </version>' ,
103
111
' <vendor>CycloneDX</vendor>\n '
104
- ' <name>cyclonedx-bom </name>\n '
112
+ f ' <name>{ EXPECTED_TOOL_NAME } </name>\n '
105
113
' <version>thisVersion-testing</version>' )
106
- bom = re_sub ( # replace metadata.tools.version
114
+ bom = re_sub ( # replace lib-dynamics in metadata.tools.components
115
+ ' <group>CycloneDX</group>\n '
116
+ ' <name>cyclonedx-python-lib</name>\n '
117
+ ' <version>.*?</version>\n '
118
+ ' <description>.*?</description>\n '
119
+ ' <licenses>\n '
120
+ '(?: .*?\n )*'
121
+ ' </licenses>\n '
122
+ ' <externalReferences>\n '
123
+ '(?: .*?\n )*'
124
+ ' </externalReferences>' ,
125
+ ' <group>CycloneDX</group>\n '
126
+ ' <name>cyclonedx-python-lib</name>\n '
127
+ ' <version>libVersion-testing</version>\n '
128
+ ' <description><!-- stripped --></description>\n '
129
+ ' <licenses><!-- stripped --></licenses>\n '
130
+ ' <externalReferences><!-- stripped --></externalReferences>' ,
131
+ bom )
132
+ bom = re_sub ( # replace lib-dynamics version in metadata.tools[]
107
133
' <vendor>CycloneDX</vendor>\n '
108
134
' <name>cyclonedx-python-lib</name>\n '
109
135
' <version>.*?</version>' ,
110
136
' <vendor>CycloneDX</vendor>\n '
111
137
' <name>cyclonedx-python-lib</name>\n '
112
138
' <version>libVersion-testing</version>' ,
113
139
bom )
114
- bom = re_sub ( # replace metadata.tools.externalReferences
140
+ bom = re_sub ( # replace lib-dynamics externalReferences in metadata.tools[]
115
141
' <vendor>CycloneDX</vendor>\n '
116
142
' <name>cyclonedx-python-lib</name>\n '
117
- r' <version>(.*?)</version>\n'
118
- r' <externalReferences>[\s\S]*?</externalReferences>' ,
143
+ ' <version>(.*?)</version>\n '
144
+ ' <externalReferences>\n '
145
+ '(?: .*?\n )*'
146
+ ' </externalReferences>' ,
119
147
' <vendor>CycloneDX</vendor>\n '
120
148
' <name>cyclonedx-python-lib</name>\n '
121
- r ' <version>\1</version>' ' \n '
149
+ ' <version>\\ 1</version>\n '
122
150
' <externalReferences><!-- stripped --></externalReferences>' ,
123
151
bom )
124
152
return bom
125
153
126
154
127
155
def make_json_comparable (bom : str ) -> str :
128
156
bom = bom .replace (_root_file_uri_json , 'file://.../' )
129
- bom = bom .replace ( # replace metadata.tools.version
130
- ' "name": "cyclonedx-bom",\n '
157
+ bom = bom .replace ( # replace this version in metadata.tools.components[]
158
+ f' "name": { json_dumps (EXPECTED_TOOL_NAME )} ,\n '
159
+ ' "type": "application",\n '
160
+ f' "version": { json_dumps (__this_version )} ' ,
161
+ f' "name": { json_dumps (EXPECTED_TOOL_NAME )} ,\n '
162
+ ' "type": "application",\n '
163
+ ' "version": "thisVersion-testing"' )
164
+ bom = bom .replace ( # replace this version in metadata.tools[]
165
+ f' "name": { json_dumps (EXPECTED_TOOL_NAME )} ,\n '
131
166
' "vendor": "CycloneDX",\n '
132
167
f' "version": { json_dumps (__this_version )} ' ,
133
- ' "name": "cyclonedx-bom" ,\n '
168
+ f ' "name": { json_dumps ( EXPECTED_TOOL_NAME ) } ,\n '
134
169
' "vendor": "CycloneDX",\n '
135
170
' "version": "thisVersion-testing"' )
136
- bom = re_sub ( # replace metadata.tools.version
171
+ bom = re_sub ( # replace lib-dynamics in metadata.tools.components[]
172
+ ' "description": ".*?",\n '
173
+ ' "externalReferences": \\ [\n '
174
+ '(?: .*?\n )*'
175
+ ' \\ ],\n '
176
+ ' "group": "CycloneDX",\n '
177
+ ' "licenses": \\ [\n '
178
+ '(?: .*?\n )*'
179
+ ' \\ ],\n '
180
+ ' "name": "cyclonedx-python-lib",\n '
181
+ ' "type": "library",\n '
182
+ ' "version": ".*?"' ,
183
+ ' "description": "stripped",\n '
184
+ ' "externalReferences": [ ],\n '
185
+ ' "group": "CycloneDX",\n '
186
+ ' "licenses": [ ],\n '
187
+ ' "name": "cyclonedx-python-lib",\n '
188
+ ' "type": "library",\n '
189
+ ' "version": "libVersion-testing"' ,
190
+ bom )
191
+ bom = re_sub ( # replace lib-dynamics version in metadata.tools[]
137
192
' "name": "cyclonedx-python-lib",\n '
138
193
' "vendor": "CycloneDX",\n '
139
194
' "version": ".*?"' ,
140
195
' "name": "cyclonedx-python-lib",\n '
141
196
' "vendor": "CycloneDX",\n '
142
197
' "version": "libVersion-testing"' ,
143
198
bom )
144
- bom = re_sub ( # replace metadata.tools.externalReferences
145
- r' "externalReferences": \[[\s\S]*?\],\n'
199
+ bom = re_sub ( # replace lib-dynamics externalReferences in metadata.tools[]
200
+ ' "externalReferences": \\ [\n '
201
+ '(?: .*?\n )*'
202
+ ' \\ ],\n '
146
203
' "name": "cyclonedx-python-lib",\n '
147
- ' "vendor": "CycloneDX"' ,
204
+ ' "vendor": "CycloneDX", \n ' ,
148
205
' "externalReferences": [ ],\n '
149
206
' "name": "cyclonedx-python-lib",\n '
150
- ' "vendor": "CycloneDX"' ,
207
+ ' "vendor": "CycloneDX", \n ' ,
151
208
bom )
152
209
return bom
153
210
@@ -160,3 +217,12 @@ def make_comparable(bom: str, of: OutputFormat) -> str:
160
217
raise NotImplementedError (f'unknown OutputFormat: { of !r} ' )
161
218
162
219
# endregion reproducible test results
220
+
221
+
222
+ def load_pyproject () -> Dict [str , Any ]:
223
+ if sys .version_info >= (3 , 11 ):
224
+ from tomllib import load as toml_load
225
+ else :
226
+ from tomli import load as toml_load
227
+ with open (path .join (path .dirname (__file__ ), '..' , 'pyproject.toml' ), 'rb' ) as f :
228
+ return toml_load (f )
0 commit comments