Skip to content

Commit 3e6b975

Browse files
committed
Swift 6
1 parent d1c0f0e commit 3e6b975

File tree

3 files changed

+131
-3
lines changed

3 files changed

+131
-3
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
name: 'Test Summary'
2+
description: 'Summarise test results and coverage'
3+
inputs:
4+
junit:
5+
description: 'path to junit xml file'
6+
required: true
7+
coverage:
8+
description: 'path to lcov.json file'
9+
required: false
10+
11+
runs:
12+
using: "composite"
13+
steps:
14+
- name: 'Summarise'
15+
run: ./.github/actions/test-summary/make-summary.swift ${{ inputs.junit }} ${{ inputs.coverage }} >> $GITHUB_STEP_SUMMARY
16+
shell: bash
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
#!/usr/bin/swift
2+
3+
import Foundation
4+
5+
let junit = CommandLine.arguments.count > 1 ? URL(filePath: CommandLine.arguments[1]) : nil
6+
let coverage = CommandLine.arguments.count > 2 ? URL(filePath: CommandLine.arguments[2]) : nil
7+
8+
guard let junit else {
9+
print("usage: ./failed-tests <junit.xml>")
10+
exit(70)
11+
}
12+
13+
let document = try XMLDocument(contentsOf: junit)
14+
let testCases = try document.nodes(forXPath: "//testcase")
15+
let failures = try document.nodes(forXPath: "//failure/..")
16+
17+
let table = makeTable(
18+
total: testCases.count,
19+
failed: failures.count,
20+
passed: testCases.count - failures.count
21+
)
22+
print(table)
23+
24+
for node in failures {
25+
guard let element = node as? XMLElement,
26+
let testClass = element.attribute(forName: "classname")?.stringValue,
27+
let testName = element.attribute(forName: "name")?.stringValue else {
28+
continue
29+
}
30+
31+
print("::warning ::Failed Test: \(testClass).\(testName)()")
32+
let messages = element
33+
.elements(forName: "failure")
34+
.compactMap { $0.attribute(forName: "message")?.stringValue }
35+
36+
print(messages.joined(separator: ". "))
37+
}
38+
39+
if let coverage {
40+
let table = try makeTable(
41+
name: coverage.lastPathComponent,
42+
coverage: JSONDecoder().decode(Coverage.self, from: Data(contentsOf: coverage))
43+
)
44+
print(table)
45+
}
46+
47+
func makeTable(
48+
total: Int,
49+
failed: Int,
50+
passed: Int,
51+
skipped: Int = 0
52+
) -> String {
53+
"""
54+
<table>
55+
<tr><td></td><td><b>Tests</b></td><td><b>Passed</b> ✅</td><td><b>Skipped</b> ⏭️</td><td><b>Failed</b> ❌</td></tr>
56+
<tr><td>\(junit.lastPathComponent)</td><td>\(total) ran</td><td>\(passed) passed</td><td>\(skipped) skipped</td><td>\(failed) failed</td></tr>
57+
</table>
58+
"""
59+
}
60+
61+
func makeTable(
62+
name: String,
63+
coverage: Coverage
64+
) -> String {
65+
"""
66+
<table>
67+
<tr><td></td><td><b>Covered</b></td><td><b>Total</b></td><td><b>Coverage</b></td></tr>
68+
<tr><td>\(name)</td><td>\(coverage.covered)</td><td>\(coverage.count)</td><td>\(coverage.percentString)</td></tr>
69+
</table>
70+
"""
71+
}
72+
73+
struct Coverage: Decodable {
74+
var count: Int
75+
var covered: Int
76+
var percent: Double
77+
78+
init(from decoder: any Decoder) throws {
79+
var unkeyed = try decoder
80+
.container(keyedBy: CodingKeys.self)
81+
.nestedUnkeyedContainer(forKey: .data)
82+
let container = try unkeyed
83+
.nestedContainer(keyedBy: CodingKeys.self)
84+
.nestedContainer(keyedBy: CodingKeys.self, forKey: .totals)
85+
.nestedContainer(keyedBy: CodingKeys.self, forKey: .lines)
86+
87+
self.count = try container.decode(Int.self, forKey: .count)
88+
self.covered = try container.decode(Int.self, forKey: .covered)
89+
self.percent = try container.decode(Double.self, forKey: .percent)
90+
}
91+
92+
enum CodingKeys: String, CodingKey {
93+
case data
94+
case totals
95+
case lines
96+
case count
97+
case covered
98+
case percent
99+
}
100+
101+
var percentString: String {
102+
let formatter = NumberFormatter()
103+
formatter.numberStyle = .percent
104+
formatter.maximumFractionDigits = 2
105+
return formatter.string(from: (percent / 100) as NSNumber)!
106+
}
107+
}

.github/workflows/build.yml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@ on:
77

88
jobs:
99
xcode_16:
10-
runs-on: macos-14
10+
runs-on: macos-15
1111
steps:
1212
- name: Checkout
1313
uses: actions/checkout@v4
1414
- name: 🔍 Xcode Select
1515
run: |
16-
XCODE_PATH=`mdfind "kMDItemCFBundleIdentifier == 'com.apple.dt.Xcode' && kMDItemVersion == '16.0'" -onlyin /Applications | head -1`
16+
XCODE_PATH=`mdfind "kMDItemCFBundleIdentifier == 'com.apple.dt.Xcode' && kMDItemVersion == '16.*'" -onlyin /Applications | head -1`
1717
echo "DEVELOPER_DIR=$XCODE_PATH/Contents/Developer" >> $GITHUB_ENV
1818
- name: Version
1919
run: swift --version
@@ -28,6 +28,11 @@ jobs:
2828
with:
2929
token: ${{ secrets.CODECOV_TOKEN }}
3030
files: ./coverage_report.lcov
31+
- name: 📄 Summary
32+
uses: ./.github/actions/test-summary
33+
with:
34+
junit: result-swift-testing.xml
35+
coverage: .build/debug/codecov/KeyValueCoder.json
3136

3237
xcode_15_4:
3338
runs-on: macos-14
@@ -85,7 +90,7 @@ jobs:
8590

8691
linux_swift_6_0:
8792
runs-on: ubuntu-latest
88-
container: swiftlang/swift:nightly-6.0-jammy
93+
container: swift:6.0
8994
steps:
9095
- name: Checkout
9196
uses: actions/checkout@v4

0 commit comments

Comments
 (0)