GCC Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 86.8% 79 / 0 / 91
Functions: 100.0% 3 / 0 / 3
Branches: 45.6% 82 / 0 / 180

src/G4OCCTSensitiveDetectorMapReader.cc
Line Branch Exec Source
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 // Copyright (C) 2026 G4OCCT Contributors
3
4 /// @file G4OCCTSensitiveDetectorMapReader.cc
5 /// @brief Implementation of G4OCCTSensitiveDetectorMapReader.
6
7 #include "G4OCCT/G4OCCTSensitiveDetectorMapReader.hh"
8
9 // Xerces — provided transitively by Geant4::G4gdml.
10 // G4GDMLRead.hh already includes the Xerces DOM headers; we repeat them here
11 // for clarity and to document the direct dependency.
12 #include <xercesc/dom/DOM.hpp>
13 #include <xercesc/parsers/XercesDOMParser.hpp>
14 #include <xercesc/util/PlatformUtils.hpp>
15 #include <xercesc/util/XMLString.hpp>
16
17 // Geant4
18 #include <G4Exception.hh>
19 #include <G4SDManager.hh>
20
21 namespace {
22 // Convert a Xerces XMLCh* string to G4String. Releases the transcoded buffer
23 // immediately so callers do not need to manage it.
24 32 G4String Transcode(const XMLCh* const str) {
25 32 char* buf = xercesc::XMLString::transcode(str);
26
1/2
✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
32 G4String result(buf);
27
1/2
✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
32 xercesc::XMLString::release(&buf);
28 32 return result;
29 32 }
30 } // namespace
31
32 7 G4OCCTSensitiveDetectorMap G4OCCTSensitiveDetectorMapReader::ReadFile(const G4String& path) {
33 // Xerces initialization is ref-counted; safe to call multiple times.
34 7 xercesc::XMLPlatformUtils::Initialize();
35
36 7 G4OCCTSensitiveDetectorMap result;
37
38 // Pre-parse fatal errors (SDReader003–007) are saved here and reported
39 // after the parser is destroyed and Terminate() has been called. Calling
40 // Terminate() inside the parser's {} scope and then returning through the
41 // scope exit would invoke XercesDOMParser::~XercesDOMParser() after
42 // Terminate() — a SEGFAULT if a non-aborting G4VExceptionHandler allows
43 // execution to continue past G4Exception().
44 7 G4String preFatalCode;
45 7 G4String preFatalMsg;
46
47 // IIFE: contains the XercesDOMParser so it is destroyed before Terminate().
48 // Early 'return' from the lambda exits the lambda scope cleanly, ensuring
49 // the parser destructor runs before Terminate() is called below.
50
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
14 [&]() {
51 7 xercesc::XercesDOMParser parser;
52
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 parser.setDoNamespaces(false);
53
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 parser.setDoSchema(false);
54
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 parser.setCreateEntityReferenceNodes(false);
55
56 try {
57
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 parser.parse(path.c_str());
58
0/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
7 } catch (const xercesc::XMLException& e) {
59 ✗ preFatalCode = "G4OCCT_SDReader003";
60 ✗ preFatalMsg = "XML error parsing '" + path + "': " + Transcode(e.getMessage());
61 return;
62 ✗ } catch (const xercesc::DOMException& e) {
63 ✗ preFatalCode = "G4OCCT_SDReader004";
64 ✗ preFatalMsg = "DOM error parsing '" + path + "': " + Transcode(e.getMessage());
65 return;
66 ✗ }
67
68
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 const xercesc::DOMDocument* const doc = parser.getDocument();
69
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 6 times.
7 if (!doc) {
70
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 preFatalCode = "G4OCCT_SDReader005";
71
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 preFatalMsg = "Cannot open document: " + path;
72 1 return;
73 }
74
75
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 const xercesc::DOMElement* const root = doc->getDocumentElement();
76
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (!root) {
77 ✗ preFatalCode = "G4OCCT_SDReader006";
78 ✗ preFatalMsg = "Empty document: " + path;
79 ✗ return;
80 }
81
82
2/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
6 const G4String rootTag = Transcode(root->getTagName());
83
3/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 5 times.
6 if (rootTag != "sensitive_detector_map") {
84
1/2
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
1 preFatalCode = "G4OCCT_SDReader007";
85 1 preFatalMsg =
86
4/8
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 time.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 time.
✗ Branch 7 not taken.
1 "Root element must be <sensitive_detector_map>, got <" + rootTag + "> in '" + path + "'";
87 1 return;
88 }
89
90
3/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 17 times.
✓ Branch 3 taken 5 times.
22 for (xercesc::DOMNode* node = root->getFirstChild(); node != nullptr;
91
1/2
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
17 node = node->getNextSibling()) {
92
3/4
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 6 times.
17 if (node->getNodeType() != xercesc::DOMNode::ELEMENT_NODE) {
93 11 continue;
94 }
95
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 const auto* child = dynamic_cast<const xercesc::DOMElement*>(node);
96
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (!child) {
97 ✗ continue;
98 }
99
4/8
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 6 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 6 times.
6 if (Transcode(child->getTagName()) != "volume") {
100 ✗ continue;
101 }
102
103 6 G4String volumeName;
104 6 G4String sdName;
105
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 const xercesc::DOMNamedNodeMap* attrs = child->getAttributes();
106
3/4
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 6 times.
16 for (XMLSize_t i = 0; i < attrs->getLength(); ++i) {
107
2/4
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
10 const auto* attr = dynamic_cast<const xercesc::DOMAttr*>(attrs->item(i));
108
1/2
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
10 if (!attr) {
109 ✗ continue;
110 }
111
2/4
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
10 const G4String aName = Transcode(attr->getName());
112
2/4
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
10 const G4String aValue = Transcode(attr->getValue());
113
3/4
✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 5 times.
10 if (aName == "name") {
114
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 volumeName = aValue;
115
2/4
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
10 } else if (aName == "sensDet") {
116
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 sdName = aValue;
117 5 }
118 10 }
119
120
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 5 times.
6 if (volumeName.empty()) {
121
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 G4Exception("G4OCCTSensitiveDetectorMapReader::ReadFile", "G4OCCT_SDReader000",
122 FatalException,
123
2/4
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 time.
1 ("<volume> element in '" + path + "' is missing the required 'name' attribute.")
124 1 .c_str());
125 1 continue;
126 }
127
128
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 4 times.
5 if (sdName.empty()) {
129
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 G4Exception("G4OCCTSensitiveDetectorMapReader::ReadFile", "G4OCCT_SDReader001",
130 FatalException,
131
4/8
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 time.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 time.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 time.
1 ("<volume name=\"" + volumeName + "\"> in '" + path +
132 "' is missing the required 'sensDet' attribute.")
133 1 .c_str());
134 1 continue;
135 }
136
137 4 G4VSensitiveDetector* sd =
138
2/4
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
4 G4SDManager::GetSDMpointer()->FindSensitiveDetector(sdName, /*warning=*/false);
139
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 3 times.
4 if (!sd) {
140
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
1 G4Exception("G4OCCTSensitiveDetectorMapReader::ReadFile", "G4OCCT_SDReader002",
141 FatalException,
142
4/8
✓ Branch 0 taken 1 time.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 time.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 time.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 time.
1 ("Sensitive detector '" + sdName + "' (volume pattern '" + volumeName +
143 "') not found in G4SDManager. Ensure all SDs are registered before "
144 "calling ReadFile().")
145 1 .c_str());
146 1 continue;
147 }
148
149
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 result.Add(volumeName, sd);
150 6 }
151 7 }(); // XercesDOMParser destroyed here
152
153
1/2
✓ Branch 0 taken 7 times.
✗ Branch 1 not taken.
7 xercesc::XMLPlatformUtils::Terminate();
154
155
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 5 times.
7 if (!preFatalCode.empty()) {
156
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
4 G4Exception("G4OCCTSensitiveDetectorMapReader::ReadFile", preFatalCode.c_str(), FatalException,
157 2 preFatalMsg.c_str());
158 // Normally unreachable: FatalException aborts. If a non-aborting handler
159 // is installed (e.g. G4OCCTFatalCatcher in tests), return an empty map so
160 // callers get a predictable, safe value.
161 2 return result;
162 }
163
164 5 return result;
165 7 }
166