-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathparser.swift
110 lines (91 loc) · 3 KB
/
parser.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
// to run: `swift parser.swift coverage.json`
import Foundation
class Parser {
func parse(_ fileName: String) {
guard let contents = readFile(fileName) else {
print("Could not parse coverage structure from \(fileName)")
return
}
let decoder = JSONDecoder()
do {
let coverage = try decoder.decode(Coverage.self, from: contents)
printCoverage(coverage)
} catch {
print("Error: \(error)")
}
}
private func readFile(_ fileName: String) -> Data? {
let currentDirectoryURL = URL(fileURLWithPath: FileManager.default.currentDirectoryPath)
guard let fileURL = URL(string: fileName, relativeTo: currentDirectoryURL) else {
print("file not found: \(currentDirectoryURL.path)/\(fileName)")
return Data()
}
do {
return try String(contentsOf: fileURL).data(using: .utf8)
} catch {
print("Error: \(error)")
return Data()
}
}
private func printCoverage(_ coverage: Coverage) {
let nonTestTargets = coverage.targets.filter { target in
return !target.name.contains(".xctest")
}
let nonPodsTargets = nonTestTargets.filter { target in
return target.files.filter { file in
return file.path.contains("/Pods/")
}.count == 0
}
let maxLength = nonPodsTargets.reduce(0) { max($0, $1.name.count) }
let title = "Total coverage"
print(String(format: "%@: %.3f%%", title.padding(toLength: maxLength, withPad: " ", startingAt: 0), coverage.lineCoverage * 100))
nonPodsTargets
.sorted { $0.lineCoverage > $1.lineCoverage }
.forEach { print(String(format: " %@: %.3f%%", $0.name.padding(toLength: maxLength, withPad: " ", startingAt: 0), $0.lineCoverage * 100)) }
}
}
struct Coverage: Coverable {
let executableLines: Int
let coveredLines: Int
let lineCoverage: Double
let targets: [Target]
}
struct Target: NamedCoverable {
let name: String
let executableLines: Int
let coveredLines: Int
let lineCoverage: Double
let buildProductPath: String
let files: [File]
}
struct File: NamedCoverable {
let name: String
let executableLines: Int
let coveredLines: Int
let lineCoverage: Double
let path: String
let functions: [Function]
}
struct Function: NamedCoverable {
let name: String
let executableLines: Int
let coveredLines: Int
let lineCoverage: Double
let lineNumber: Int
let executionCount: Int
}
protocol Coverable: Decodable {
var executableLines: Int { get }
var coveredLines: Int { get }
var lineCoverage: Double { get }
}
protocol NamedCoverable: Coverable {
var name: String { get }
}
if CommandLine.arguments.count > 1 {
let fileName = CommandLine.arguments[1]
let parser = Parser()
parser.parse(fileName)
} else {
print("Please specify a filename as a command line argument")
}