1
+ """
2
+ Custom JUnit XML writer extension for terraform-compliance
3
+ Fixes the UnboundLocalError when scenarios are skipped
4
+ """
5
+
6
+ import os
7
+ import sys
8
+ from datetime import datetime
9
+ from xml .etree import ElementTree as ET
10
+ from radish .extensions import CodeGenerator
11
+ from radish .model import ScenarioLoop , ScenarioOutline
12
+ from radish .hookregistry import after
13
+ from radish import config
14
+
15
+
16
+ class FixedJUnitXMLWriter (CodeGenerator ):
17
+ """
18
+ Fixed JUnit XML writer that properly handles skipped scenarios
19
+ """
20
+
21
+ OPTIONS = [
22
+ (
23
+ "--junit-xml=<PATH>" ,
24
+ "write JUnit XML result file after run to <PATH>" ,
25
+ )
26
+ ]
27
+
28
+ def __init__ (self ):
29
+ # Disable the default radish JUnit XML writer
30
+ self .junit_xml_path = None
31
+
32
+ def get_junit_xml_path (self ):
33
+ """Get the JUnit XML output path from configuration"""
34
+ return config ().junit_xml
35
+
36
+ @after .each_feature
37
+ def generate_junit_xml (self , feature ):
38
+ """Generate JUnit XML report for the feature"""
39
+ junit_xml_path = self .get_junit_xml_path ()
40
+ if not junit_xml_path :
41
+ return
42
+
43
+ # Create testsuite element
44
+ testsuite = ET .Element ("testsuite" )
45
+ testsuite .set ("name" , feature .sentence )
46
+ testsuite .set ("tests" , str (len (feature .scenarios )))
47
+ testsuite .set ("time" , str (feature .duration ))
48
+
49
+ # Count results
50
+ failures = 0
51
+ errors = 0
52
+ skipped = 0
53
+
54
+ for scenario in feature .scenarios :
55
+ if isinstance (scenario , (ScenarioLoop , ScenarioOutline )):
56
+ scenarios = scenario .scenarios
57
+ else :
58
+ scenarios = [scenario ]
59
+
60
+ for actual_scenario in scenarios :
61
+ # Create testcase element for each scenario
62
+ testcase = ET .SubElement (testsuite , "testcase" )
63
+ testcase .set ("classname" , feature .sentence )
64
+ testcase .set ("name" , actual_scenario .sentence )
65
+ testcase .set ("time" , str (actual_scenario .duration ))
66
+
67
+ # Handle different scenario states
68
+ if actual_scenario .state == "skipped" :
69
+ skipped += 1
70
+ skipped_elem = ET .SubElement (testcase , "skipped" )
71
+ if hasattr (actual_scenario , 'skip_reason' ):
72
+ skipped_elem .set ("message" , str (actual_scenario .skip_reason ))
73
+ elif actual_scenario .state == "failed" :
74
+ failures += 1
75
+ failure_elem = ET .SubElement (testcase , "failure" )
76
+ if hasattr (actual_scenario , 'failure_reason' ):
77
+ failure_elem .set ("message" , str (actual_scenario .failure_reason ))
78
+ if hasattr (actual_scenario , 'failure_traceback' ):
79
+ failure_elem .text = str (actual_scenario .failure_traceback )
80
+ elif actual_scenario .state == "untested" :
81
+ errors += 1
82
+ error_elem = ET .SubElement (testcase , "error" )
83
+ error_elem .set ("message" , "Scenario was not tested" )
84
+
85
+ # Update testsuite attributes
86
+ testsuite .set ("failures" , str (failures ))
87
+ testsuite .set ("errors" , str (errors ))
88
+ testsuite .set ("skipped" , str (skipped ))
89
+
90
+ # Write XML file
91
+ try :
92
+ tree = ET .ElementTree (testsuite )
93
+ # Create directory if it doesn't exist
94
+ os .makedirs (os .path .dirname (junit_xml_path ) or '.' , exist_ok = True )
95
+ tree .write (junit_xml_path , encoding = "utf-8" , xml_declaration = True )
96
+ except Exception as e :
97
+ print (f"Error writing JUnit XML: { e } " , file = sys .stderr )
98
+
99
+
100
+ # Register the extension
101
+ def load_radish_extensions ():
102
+ """Load the fixed JUnit XML writer extension"""
103
+ return [FixedJUnitXMLWriter ]
0 commit comments