1515# SPDX-License-Identifier: Apache-2.0
1616# Copyright (c) OWASP Foundation. All Rights Reserved.
1717
18-
18+ import sys
1919from json import dumps as json_dumps
20- from os import getenv
21- from os .path import dirname , join
20+ from os import getenv , path
2221from pathlib import Path
2322from re import sub as re_sub
24- from sys import stderr
25- from typing import Union
23+ from typing import Any , Dict , Union
2624from unittest import TestCase
2725from xml .sax .saxutils import escape as xml_escape , quoteattr as xml_quoteattr # nosec:B406
2826
3230
3331RECREATE_SNAPSHOTS = '1' == getenv ('CDX_TEST_RECREATE_SNAPSHOTS' )
3432if RECREATE_SNAPSHOTS :
35- print ('!!! WILL RECREATE ALL SNAPSHOTS !!!' , file = stderr )
33+ print ('!!! WILL RECREATE ALL SNAPSHOTS !!!' , file = sys . stderr )
3634
3735INIT_TESTBEDS = '1' != getenv ('CDX_TEST_SKIP_INIT_TESTBEDS' )
3836if INIT_TESTBEDS :
39- print ('!!! WILL INIT TESTBEDS !!!' , file = stderr )
37+ print ('!!! WILL INIT TESTBEDS !!!' , file = sys . stderr )
4038
41- _TESTDATA_DIRECTORY = join (dirname (__file__ ), '_data' )
39+ _TESTDATA_DIRECTORY = path . join (path . dirname (__file__ ), '_data' )
4240
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' )
4543
4644UNSUPPORTED_OF_SV = (
4745 (OutputFormat .JSON , SchemaVersion .V1_1 ),
@@ -60,7 +58,7 @@ class SnapshotMixin:
6058
6159 @staticmethod
6260 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' )
6462
6563 @classmethod
6664 def writeSnapshot (cls , snapshot_name : str , data : str ) -> None : # noqa: N802
@@ -92,62 +90,121 @@ def assertEqualSnapshot(self: Union[TestCase, 'SnapshotMixin'], # noqa: N802
9290_root_file_uri_xml_attr = xml_quoteattr (_root_file_uri )[1 :- 1 ]
9391_root_file_uri_json = json_dumps (_root_file_uri )[1 :- 1 ]
9492
93+ # package is called 'cyclonedx-bom', but the tool is called 'cyclonedx-py'
94+ EXPECTED_TOOL_NAME = 'cyclonedx-py'
95+
9596
9697def make_xml_comparable (bom : str ) -> str :
9798 bom = bom .replace (_root_file_uri_xml , 'file://.../' )
9899 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
100108 ' <vendor>CycloneDX</vendor>\n '
101- ' <name>cyclonedx-py </name>\n '
109+ f ' <name>{ EXPECTED_TOOL_NAME } </name>\n '
102110 f' <version>{ __this_version } </version>' ,
103111 ' <vendor>CycloneDX</vendor>\n '
104- ' <name>cyclonedx-py </name>\n '
112+ f ' <name>{ EXPECTED_TOOL_NAME } </name>\n '
105113 ' <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[]
107133 ' <vendor>CycloneDX</vendor>\n '
108134 ' <name>cyclonedx-python-lib</name>\n '
109135 ' <version>.*?</version>' ,
110136 ' <vendor>CycloneDX</vendor>\n '
111137 ' <name>cyclonedx-python-lib</name>\n '
112138 ' <version>libVersion-testing</version>' ,
113139 bom )
114- bom = re_sub ( # replace metadata.tools.externalReferences
140+ bom = re_sub ( # replace lib-dynamics externalReferences in metadata.tools[]
115141 ' <vendor>CycloneDX</vendor>\n '
116142 ' <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>' ,
119147 ' <vendor>CycloneDX</vendor>\n '
120148 ' <name>cyclonedx-python-lib</name>\n '
121- r ' <version>\1</version>' ' \n '
149+ ' <version>\\ 1</version>\n '
122150 ' <externalReferences><!-- stripped --></externalReferences>' ,
123151 bom )
124152 return bom
125153
126154
127155def make_json_comparable (bom : str ) -> str :
128156 bom = bom .replace (_root_file_uri_json , 'file://.../' )
129- bom = bom .replace ( # replace metadata.tools.version
130- ' "name": "cyclonedx-py",\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 '
131166 ' "vendor": "CycloneDX",\n '
132167 f' "version": { json_dumps (__this_version )} ' ,
133- ' "name": "cyclonedx-py" ,\n '
168+ f ' "name": { json_dumps ( EXPECTED_TOOL_NAME ) } ,\n '
134169 ' "vendor": "CycloneDX",\n '
135170 ' "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[]
137192 ' "name": "cyclonedx-python-lib",\n '
138193 ' "vendor": "CycloneDX",\n '
139194 ' "version": ".*?"' ,
140195 ' "name": "cyclonedx-python-lib",\n '
141196 ' "vendor": "CycloneDX",\n '
142197 ' "version": "libVersion-testing"' ,
143198 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 '
146203 ' "name": "cyclonedx-python-lib",\n '
147- ' "vendor": "CycloneDX"' ,
204+ ' "vendor": "CycloneDX", \n ' ,
148205 ' "externalReferences": [ ],\n '
149206 ' "name": "cyclonedx-python-lib",\n '
150- ' "vendor": "CycloneDX"' ,
207+ ' "vendor": "CycloneDX", \n ' ,
151208 bom )
152209 return bom
153210
@@ -160,3 +217,12 @@ def make_comparable(bom: str, of: OutputFormat) -> str:
160217 raise NotImplementedError (f'unknown OutputFormat: { of !r} ' )
161218
162219# 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