@@ -11,31 +11,37 @@ public extension Xccov.Converters {
1111 enum CoberturaXml { }
1212}
1313
14- public extension Xccov . Converters . CoberturaXml {
14+ extension XMLNode {
15+ static func nodeAttribute( withName name: String , stringValue value: String ) -> XMLNode {
16+ guard let attribute = XMLNode . attribute ( withName: name, stringValue: value) as? XMLNode else {
17+ return XMLNode ( )
18+ }
1519
20+ return attribute
21+ }
22+ }
23+
24+ public extension Xccov . Converters . CoberturaXml {
1625 static func convert( coverageReport: CoverageReport ) -> Result < String , Xccov . Error > {
1726 Self . convert ( coverageReport: coverageReport,
1827 timeStamp: Date ( ) . timeIntervalSince1970,
1928 currentDirectoryPath: FileManager . default. currentDirectoryPath)
2029 }
2130
31+ //swiftlint:disable:next function_body_length
2232 static func convert( coverageReport: CoverageReport ,
2333 timeStamp: TimeInterval = Date ( ) . timeIntervalSince1970,
2434 currentDirectoryPath: String = FileManager . default. currentDirectoryPath) -> Result < String , Xccov . Error > {
25- let dtd = try ! XMLDTD ( contentsOf: URL ( string: " http://cobertura.sourceforge.net/xml/coverage-04.dtd " ) !)
35+ guard
36+ let dtdUrl = URL ( string: " http://cobertura.sourceforge.net/xml/coverage-04.dtd " ) ,
37+ let dtd = try ? XMLDTD ( contentsOf: dtdUrl) else {
38+ return . failure( . conversionFailed( " DTD could not be constructed " ) )
39+ }
40+
2641 dtd. name = " coverage "
2742 dtd. systemID = " http://cobertura.sourceforge.net/xml/coverage-04.dtd "
2843
29- let rootElement = XMLElement ( name: " coverage " )
30- rootElement. addAttribute ( XMLNode . attribute ( withName: " line-rate " , stringValue: " \( coverageReport. lineCoverage) " ) as! XMLNode )
31- rootElement. addAttribute ( XMLNode . attribute ( withName: " branch-rate " , stringValue: " 1.0 " ) as! XMLNode )
32- rootElement. addAttribute ( XMLNode . attribute ( withName: " lines-covered " , stringValue: " \( coverageReport. coveredLines) " ) as! XMLNode )
33- rootElement. addAttribute ( XMLNode . attribute ( withName: " lines-valid " , stringValue: " \( coverageReport. executableLines) " ) as! XMLNode )
34- rootElement. addAttribute ( XMLNode . attribute ( withName: " timestamp " , stringValue: " \( timeStamp) " ) as! XMLNode )
35- rootElement. addAttribute ( XMLNode . attribute ( withName: " version " , stringValue: " diff_coverage 0.1 " ) as! XMLNode )
36- rootElement. addAttribute ( XMLNode . attribute ( withName: " complexity " , stringValue: " 0.0 " ) as! XMLNode )
37- rootElement. addAttribute ( XMLNode . attribute ( withName: " branches-valid " , stringValue: " 1.0 " ) as! XMLNode )
38- rootElement. addAttribute ( XMLNode . attribute ( withName: " branches-covered " , stringValue: " 1.0 " ) as! XMLNode )
44+ let rootElement = Self . makeRootElement ( coverageReport: coverageReport, timeStamp: timeStamp)
3945
4046 let doc = XMLDocument ( rootElement: rootElement)
4147 doc. version = " 1.0 "
@@ -48,7 +54,7 @@ public extension Xccov.Converters.CoberturaXml {
4854
4955 let packagesElement = XMLElement ( name: " packages " )
5056 rootElement. addChild ( packagesElement)
51-
57+
5258 // Sort files to avoid duplicated packages
5359 let allFiles = coverageReport. targets. flatMap { $0. files } . sorted { $0. path > $1. path }
5460
@@ -71,30 +77,32 @@ public extension Xccov.Converters.CoberturaXml {
7177
7278 currentPackage = packageName
7379 if isNewPackage {
74- currentPackageElement. addAttribute ( XMLNode . attribute ( withName: " name " , stringValue: packageName) as! XMLNode )
75- currentPackageElement. addAttribute ( XMLNode . attribute ( withName: " line-rate " , stringValue: " \( fileCoverageReport. lineCoverage) " ) as! XMLNode )
76- currentPackageElement. addAttribute ( XMLNode . attribute ( withName: " branch-rate " , stringValue: " 1.0 " ) as! XMLNode )
77- currentPackageElement. addAttribute ( XMLNode . attribute ( withName: " complexity " , stringValue: " 0.0 " ) as! XMLNode )
80+ currentPackageElement. addAttribute ( XMLNode . nodeAttribute ( withName: " name " , stringValue: packageName) )
81+ currentPackageElement. addAttribute ( XMLNode . nodeAttribute ( withName: " line-rate " , stringValue: " \( fileCoverageReport. lineCoverage) " ) )
82+ currentPackageElement. addAttribute ( XMLNode . nodeAttribute ( withName: " branch-rate " , stringValue: " 1.0 " ) )
83+ currentPackageElement. addAttribute ( XMLNode . nodeAttribute ( withName: " complexity " , stringValue: " 0.0 " ) )
7884 }
7985
8086 let classElement = XMLElement ( name: " class " )
81- classElement. addAttribute ( XMLNode . attribute ( withName: " name " , stringValue: " \( packageName) . \( ( fileCoverageReport. name as NSString ) . deletingPathExtension) " ) as! XMLNode )
82- classElement. addAttribute ( XMLNode . attribute ( withName: " filename " , stringValue: " \( filePath) " ) as! XMLNode )
83- classElement. addAttribute ( XMLNode . attribute ( withName: " line-rate " , stringValue: " \( fileCoverageReport. lineCoverage) " ) as! XMLNode )
84- classElement. addAttribute ( XMLNode . attribute ( withName: " branch-rate " , stringValue: " 1.0 " ) as! XMLNode )
85- classElement. addAttribute ( XMLNode . attribute ( withName: " complexity " , stringValue: " 0.0 " ) as! XMLNode )
87+ classElement. addAttribute ( XMLNode . nodeAttribute ( withName: " name " ,
88+ stringValue: " \( packageName) . \( ( fileCoverageReport. name as NSString ) . deletingPathExtension) " ) )
89+ classElement. addAttribute ( XMLNode . nodeAttribute ( withName: " filename " , stringValue: " \( filePath) " ) )
90+ classElement. addAttribute ( XMLNode . nodeAttribute ( withName: " line-rate " , stringValue: " \( fileCoverageReport. lineCoverage) " ) )
91+ classElement. addAttribute ( XMLNode . nodeAttribute ( withName: " branch-rate " , stringValue: " 1.0 " ) )
92+ classElement. addAttribute ( XMLNode . nodeAttribute ( withName: " complexity " , stringValue: " 0.0 " ) )
8693 currentPackageElement. addChild ( classElement)
8794
8895 let linesElement = XMLElement ( name: " lines " )
8996 classElement. addChild ( linesElement)
9097
9198 for functionCoverageReport in fileCoverageReport. functions {
9299 for index in 0 ..< functionCoverageReport. executableLines {
93- // Function coverage report won't be 100% reliable without parsing it by file (would need to use xccov view --file filePath currentDirectory + Build/Logs/Test/*.xccovarchive)
100+ // Function coverage report won't be 100% reliable without parsing it by file
101+ // (would need to use xccov view --file filePath currentDirectory + Build/Logs/Test/*.xccovarchive)
94102 let lineElement = XMLElement ( kind: . element, options: . nodeCompactEmptyElement)
95103 lineElement. name = " line "
96- lineElement. addAttribute ( XMLNode . attribute ( withName: " number " , stringValue: " \( functionCoverageReport. lineNumber + index) " ) as! XMLNode )
97- lineElement. addAttribute ( XMLNode . attribute ( withName: " branch " , stringValue: " false " ) as! XMLNode )
104+ lineElement. addAttribute ( XMLNode . nodeAttribute ( withName: " number " , stringValue: " \( functionCoverageReport. lineNumber + index) " ) )
105+ lineElement. addAttribute ( XMLNode . nodeAttribute ( withName: " branch " , stringValue: " false " ) )
98106
99107 let lineHits : Int
100108 if index < functionCoverageReport. coveredLines {
@@ -103,12 +111,27 @@ public extension Xccov.Converters.CoberturaXml {
103111 lineHits = 0
104112 }
105113
106- lineElement. addAttribute ( XMLNode . attribute ( withName: " hits " , stringValue: " \( lineHits) " ) as! XMLNode )
114+ lineElement. addAttribute ( XMLNode . nodeAttribute ( withName: " hits " , stringValue: " \( lineHits) " ) )
107115 linesElement. addChild ( lineElement)
108116 }
109117 }
110118 }
111119
112120 return . success( doc. xmlString ( options: [ . nodePrettyPrint] ) )
113121 }
122+
123+ static func makeRootElement( coverageReport: CoverageReport , timeStamp: TimeInterval ) -> XMLElement {
124+ let rootElement = XMLElement ( name: " coverage " )
125+ rootElement. addAttribute ( XMLNode . nodeAttribute ( withName: " line-rate " , stringValue: " \( coverageReport. lineCoverage) " ) )
126+ rootElement. addAttribute ( XMLNode . nodeAttribute ( withName: " branch-rate " , stringValue: " 1.0 " ) )
127+ rootElement. addAttribute ( XMLNode . nodeAttribute ( withName: " lines-covered " , stringValue: " \( coverageReport. coveredLines) " ) )
128+ rootElement. addAttribute ( XMLNode . nodeAttribute ( withName: " lines-valid " , stringValue: " \( coverageReport. executableLines) " ) )
129+ rootElement. addAttribute ( XMLNode . nodeAttribute ( withName: " timestamp " , stringValue: " \( timeStamp) " ) )
130+ rootElement. addAttribute ( XMLNode . nodeAttribute ( withName: " version " , stringValue: " diff_coverage 0.1 " ) )
131+ rootElement. addAttribute ( XMLNode . nodeAttribute ( withName: " complexity " , stringValue: " 0.0 " ) )
132+ rootElement. addAttribute ( XMLNode . nodeAttribute ( withName: " branches-valid " , stringValue: " 1.0 " ) )
133+ rootElement. addAttribute ( XMLNode . nodeAttribute ( withName: " branches-covered " , stringValue: " 1.0 " ) )
134+
135+ return rootElement
136+ }
114137}
0 commit comments