Skip to content

Commit 507ddaf

Browse files
Merge pull request #238 from ably/code-coverage
[ECO-5252] Generate a code coverage report in CI
2 parents cc690c3 + 04d8565 commit 507ddaf

File tree

3 files changed

+71
-1
lines changed

3 files changed

+71
-1
lines changed

.github/workflows/check.yaml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,41 @@ jobs:
138138
- name: Run tests
139139
run: swift run BuildTool test-library --platform ${{ matrix.platform }}
140140

141+
code-coverage:
142+
name: Generate code coverage (Xcode ${{ matrix.tooling.xcodeVersion }})
143+
runs-on: macos-15
144+
needs: generate-matrices
145+
strategy:
146+
fail-fast: false
147+
matrix: ${{ fromJson(needs.generate-matrices.outputs.matrix).withoutPlatform }}
148+
149+
steps:
150+
- uses: actions/checkout@v4
151+
with:
152+
submodules: true
153+
- uses: maxim-lobanov/setup-xcode@v1
154+
with:
155+
xcode-version: ${{ matrix.tooling.xcodeVersion }}
156+
157+
- run: swift run BuildTool generate-code-coverage --result-bundle-path CodeCoverage.xcresult
158+
159+
# Generate a Markdown report of the code coverage information and add it to the workflow run.
160+
#
161+
# This tool is the best option that I could find after a brief look at the options. There are a few things that I wish it could do:
162+
#
163+
# - post a message on the pull request, like they do on Kotlin
164+
# - offer more fine-grained configuration about which data to include in the report (I only care about code coverage, not test results, and I don't care about code coverage of the AblyChatTests target)
165+
#
166+
# but it'll do for now (we can always fork or have another look for tooling later).
167+
- uses: slidoapp/xcresulttool@v3.1.0
168+
with:
169+
path: CodeCoverage.xcresult
170+
# This title will be used for the sidebar item that this job adds to GitHub results page for this workflow
171+
title: Code coverage results
172+
# Turning off as much non-code-coverage information as it lets me
173+
show-passed-tests: false
174+
if: success() || failure()
175+
141176
build-release-configuration-xcode:
142177
name: Xcode, `release` configuration, ${{matrix.platform}} (Xcode ${{ matrix.tooling.xcodeVersion }})
143178
runs-on: macos-15
@@ -247,6 +282,7 @@ jobs:
247282
- build-release-configuration-xcode
248283
- check-example-app
249284
- check-documentation
285+
- code-coverage
250286

251287
steps:
252288
- name: No-op

Sources/BuildTool/BuildTool.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ struct BuildTool: AsyncParsableCommand {
1616
Lint.self,
1717
SpecCoverage.self,
1818
BuildDocumentation.self,
19+
GenerateCodeCoverage.self,
1920
]
2021
)
2122
}
@@ -71,6 +72,31 @@ struct TestLibrary: AsyncParsableCommand {
7172
}
7273
}
7374

75+
@available(macOS 14, *)
76+
struct GenerateCodeCoverage: AsyncParsableCommand {
77+
static let configuration = CommandConfiguration(
78+
abstract: "Generate code coverage for the AblyChat library",
79+
discussion: "Runs the unit tests and outputs a .xcresult bundle containing code coverage information"
80+
)
81+
82+
@Option(help: "Pathname of where to output the .xcresult bundle.")
83+
var resultBundlePath: String
84+
85+
mutating func run() async throws {
86+
let platform = Platform.macOS
87+
let destinationSpecifier = try await platform.resolve()
88+
let scheme = "AblyChat"
89+
90+
try await XcodeRunner.runXcodebuild(
91+
action: "test",
92+
scheme: scheme,
93+
destination: destinationSpecifier,
94+
testPlan: "UnitTests",
95+
resultBundlePath: resultBundlePath
96+
)
97+
}
98+
}
99+
74100
@available(macOS 14, *)
75101
struct BuildExampleApp: AsyncParsableCommand {
76102
static let configuration = CommandConfiguration(abstract: "Build the AblyChatExample example app")

Sources/BuildTool/XcodeRunner.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import Foundation
22

33
@available(macOS 14, *)
44
enum XcodeRunner {
5-
static func runXcodebuild(action: String?, configuration: Configuration? = nil, scheme: String, destination: DestinationSpecifier) async throws {
5+
static func runXcodebuild(action: String?, configuration: Configuration? = nil, scheme: String, destination: DestinationSpecifier, testPlan: String? = nil, resultBundlePath: String? = nil) async throws {
66
var arguments: [String] = []
77

88
if let action {
@@ -16,6 +16,14 @@ enum XcodeRunner {
1616
arguments.append(contentsOf: ["-scheme", scheme])
1717
arguments.append(contentsOf: ["-destination", destination.xcodebuildArgument])
1818

19+
if let testPlan {
20+
arguments.append(contentsOf: ["-testPlan", testPlan])
21+
}
22+
23+
if let resultBundlePath {
24+
arguments.append(contentsOf: ["-resultBundlePath", resultBundlePath])
25+
}
26+
1927
/*
2028
Note: I was previously passing SWIFT_TREAT_WARNINGS_AS_ERRORS=YES here, but am no longer able to do so, for the following reasons:
2129

0 commit comments

Comments
 (0)