@@ -14,12 +14,6 @@ class TestCaseLink:
1414class TestCase :
1515 @classmethod
1616 def create_minimal (cls , title : str ) -> 'TestCase' :
17- """
18- Create a new minimal TestCase instance with just a title.
19- The generated XML will include only the required namespaces, type, and title.
20- :param title: Title of the new Test Case
21- :return: A minimal TestCase object
22- """
2317 namespaces = {
2418 'rdf' : 'http://www.w3.org/1999/02/22-rdf-syntax-ns#' ,
2519 'dcterms' : 'http://purl.org/dc/terms/' ,
@@ -55,7 +49,6 @@ def create_minimal(cls, title: str) -> 'TestCase':
5549 namespaces = namespaces
5650 )
5751
58- # Add to elements for proper serialization
5952 tc .elements .append ((
6053 '{http://purl.org/dc/terms/}title' ,
6154 {'{http://www.w3.org/2001/XMLSchema#}datatype' : 'http://www.w3.org/2001/XMLSchema#string' },
@@ -87,49 +80,28 @@ def create_minimal(cls, title: str) -> 'TestCase':
8780 links : List [TestCaseLink ] = field (default_factory = list )
8881 namespaces : Dict [str , str ] = field (default_factory = dict )
8982 elements : List [Tuple [str , Dict [str , str ], Optional [str ]]] = field (default_factory = list )
90-
91-
83+ extra_descriptions : Dict [str , List [Tuple [str , Dict [str , str ], Optional [str ]]]] = field (default_factory = dict )
9284
9385 def add_link (self , predicate : str , target : str , title : Optional [str ] = None ):
94- """
95- Add a generic link to the test case.
96- :param predicate: Predicate URI of the link
97- :param target: Target URI of the linked resource
98- :param title: Optional title describing the link
99- """
10086 self .links .append (TestCaseLink (predicate = predicate , target = target , title = title ))
10187
10288 def add_validatesRequirementLink (self , target : str , title : Optional [str ] = None ):
103- """
104- Add a validatesRequirement link to the test case and include a corresponding RDF property.
105- :param target: The target requirement URI
106- :param title: Optional title for the link
107- """
10889 self .links .append (TestCaseLink (
10990 subject = self .uri ,
11091 predicate = "http://open-services.net/ns/qm#validatesRequirement" ,
11192 target = target ,
11293 title = title
11394 ))
114- # Add corresponding property to the TestCase element list
11595 tag = '{' + self .namespaces .get ('oslc_qm' , 'http://open-services.net/ns/qm#' ) + '}validatesRequirement'
11696 attrib = {'{' + self .namespaces ['rdf' ] + '}resource' : target }
11797 self .elements .append ((tag , attrib , None ))
11898
11999 def delete_link (self , target : str ) -> bool :
120- """
121- Delete a generic link based on its target URI.
122- :param target: The target URI to remove
123- :return: True if a link was removed, False otherwise
124- """
125100 initial_length = len (self .links )
126101 self .links = [link for link in self .links if link .target != target ]
127102 return len (self .links ) < initial_length
128103
129104 def delete_validatesRequirementLink (self , target : str ) -> bool :
130- """
131- Delete validatesRequirement links matching the target URL from both the links list and the elements list.
132- """
133105 initial_links = len (self .links )
134106 self .links = [link for link in self .links if not (
135107 link .predicate == "http://open-services.net/ns/qm#validatesRequirement" and link .target == target )
@@ -146,22 +118,42 @@ def delete_validatesRequirementLink(self, target: str) -> bool:
146118
147119 @staticmethod
148120 def from_etree (etree : ET ._ElementTree ) -> 'TestCase' :
149- """
150- Parse a TestCase instance from an RDF XML ElementTree.
151- :param etree: lxml.etree ElementTree representing the RDF
152- :return: TestCase instance with parsed data
153- """
154121 root = etree .getroot ()
155122 namespaces = {k if k is not None else '' : v for k , v in root .nsmap .items ()}
156123 ns = namespaces .copy ()
157124
158- main_elem = root .find (".//rdf:Description[@rdf:about]" , ns )
125+ # Find all rdf:Description elements with rdf:about
126+ about_elements = root .findall (".//rdf:Description[@rdf:about]" , ns )
127+
128+ # Identify the main test case element (without '#' in rdf:about)
129+ main_elem = None
130+ for elem in about_elements :
131+ uri = elem .attrib .get (f'{{{ ns ["rdf" ]} }}about' )
132+ #print(uri)
133+ if uri and 'TestCase' in uri and '#' not in uri :
134+ main_elem = elem
135+ break
136+
159137 if main_elem is None :
160- raise ValueError ("No rdf:Description with rdf:about found" )
138+ raise ValueError ("No main rdf:Description with rdf:about (without '#') found" )
161139
162140 uri = main_elem .attrib [f'{{{ ns ["rdf" ]} }}about' ]
163141 testcase = TestCase (uri = uri , namespaces = namespaces )
164142
143+
144+
145+ # def from_etree(etree: ET._ElementTree) -> 'TestCase':
146+ # root = etree.getroot()
147+ # namespaces = {k if k is not None else '': v for k, v in root.nsmap.items()}
148+ # ns = namespaces.copy()
149+
150+ # main_elem = root.find(".//rdf:Description[@rdf:about]", ns)
151+ # if main_elem is None:
152+ # raise ValueError("No rdf:Description with rdf:about found")
153+
154+ # uri = main_elem.attrib[f'{{{ns["rdf"]}}}about']
155+ # testcase = TestCase(uri=uri, namespaces=namespaces)
156+
165157 for elem in main_elem :
166158 tag = elem .tag
167159 text = elem .text .strip () if elem .text else ""
@@ -214,13 +206,21 @@ def from_etree(etree: ET._ElementTree) -> 'TestCase':
214206 title = title_elem .text if title_elem is not None else None
215207 ))
216208
209+ for desc in root .findall (".//rdf:Description[@rdf:about]" , ns ):
210+ about = desc .attrib .get (f'{{{ ns ["rdf" ]} }}about' )
211+ if about == testcase .uri :
212+ continue
213+ elems = []
214+ for elem in desc :
215+ tag = elem .tag
216+ text = elem .text .strip () if elem .text else ""
217+ attrib = dict (elem .attrib )
218+ elems .append ((tag , attrib , text ))
219+ testcase .extra_descriptions [about ] = elems
220+
217221 return testcase
218222
219223 def to_etree (self ) -> ET ._ElementTree :
220- """
221- Serialize the TestCase instance to an RDF XML ElementTree.
222- :return: lxml.etree ElementTree
223- """
224224 NSMAP = self .namespaces or {'rdf' : "http://www.w3.org/1999/02/22-rdf-syntax-ns#" }
225225 rdf = ET .Element (ET .QName (NSMAP ['rdf' ], 'RDF' ), nsmap = NSMAP )
226226 if self .uri != "" :
@@ -230,7 +230,6 @@ def to_etree(self) -> ET._ElementTree:
230230 else :
231231 desc = ET .SubElement (rdf , ET .QName (NSMAP ['rdf' ], 'Description' ))
232232
233- # Emit known fields first
234233 def add (tag_ns : str , tag : str , text = None , attrib = None ):
235234 el = ET .SubElement (desc , ET .QName (NSMAP [tag_ns ], tag ), attrib or {})
236235 if text :
@@ -273,7 +272,7 @@ def add(tag_ns: str, tag: str, text=None, attrib=None):
273272 for tag , attrib , text in self .elements :
274273 short_tag = ET .QName (tag ).localname
275274 if short_tag in known_tags :
276- continue # already added from field values
275+ continue
277276 el = ET .SubElement (desc , ET .QName (tag ), {
278277 ET .QName (k ) if isinstance (k , str ) and ':' in k else k : v for k , v in attrib .items ()
279278 })
@@ -300,17 +299,18 @@ def add(tag_ns: str, tag: str, text=None, attrib=None):
300299 if link .title :
301300 ET .SubElement (stmt , ET .QName (NSMAP ['dcterms' ], 'title' )).text = link .title
302301
302+ for about , elems in self .extra_descriptions .items ():
303+ desc = ET .SubElement (rdf , ET .QName (NSMAP ['rdf' ], 'Description' ), {
304+ ET .QName (NSMAP ['rdf' ], 'about' ): about
305+ })
306+ for tag , attrib , text in elems :
307+ el = ET .SubElement (desc , ET .QName (tag ), attrib )
308+ if text :
309+ el .text = text
310+
303311 return ET .ElementTree (rdf )
304312
305313 def is_xml_equal (self , other : 'TestCase' ) -> bool :
306- """
307- Compare two TestCase instances for XML structural equality.
308- :param other: Another TestCase instance
309- :return: True if both XML trees are semantically identical
310- """
311- """
312- Compare two TestCase instances for XML structural equality.
313- """
314314 def clean (xml : ET ._ElementTree ) -> bytes :
315315 return ET .tostring (xml .getroot (), encoding = 'utf-8' , method = 'c14n' )
316316
0 commit comments