-
Notifications
You must be signed in to change notification settings - Fork 27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Coverage Converter update #25
Coverage Converter update #25
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A couple thoughts for cleaning things up, but this is looking great to me. I'll approve, but let's wait for @a7ex's feedback as well. 💯
} | ||
|
||
// Subrange information struct | ||
struct Subrange: Codable { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since all we're doing here is decoding JSON, you could change this (and the ones below) to use Decodable
instead of Codable
.
struct Subrange: Codable { | |
struct Subrange: Decodable { |
// Custom encoding method to handle the top-level dictionary | ||
func encode(to encoder: Encoder) throws { | ||
var container = encoder.container(keyedBy: CodingKeys.self) | ||
for (key, value) in files { | ||
guard let codingKey = CodingKeys(stringValue: key) else { | ||
continue | ||
} | ||
try container.encode(value, forKey: codingKey) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you switch to only using Decodable
, you can then remove this custom encoding method.
// Custom encoding method to handle the top-level dictionary | |
func encode(to encoder: Encoder) throws { | |
var container = encoder.container(keyedBy: CodingKeys.self) | |
for (key, value) in files { | |
guard let codingKey = CodingKeys(stringValue: key) else { | |
continue | |
} | |
try container.encode(value, forKey: codingKey) | |
} | |
} |
let lineNum = lineData.line | ||
var covered = lineData.executionCount | ||
if covered != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we're not doing anything if covered
== nil
, could we switch this to a guard
statement? It'd eliminate the need for force unwrapping covered
below as well.
let lineNum = lineData.line | |
var covered = lineData.executionCount | |
if covered != nil { | |
let lineNum = lineData.line | |
guard var covered = lineData.executionCount else { | |
... |
That looks very good to me as well. A change I should have done in the first place. Please give me one more day to review. I am pretty loaded with work "at work" at the moment. Will come back to you on this tomorrow |
Hi @maxwell-legrand could you fix the tests on your branch, so the action runs green? I can not commit the fixed tests to your repo. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks again for the great work, I was postponing that work. which you now did for quite some weeks, since I learned, that xcccv tool now can give us the coverage with only one call.
arguments.append("--json") | ||
arguments.append(resultFile.url.path) | ||
let coverageData = try Shell.execute(program: "/usr/bin/xcrun", with: arguments) | ||
let resultsString = String(decoding: coverageData, as: UTF8.self) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to convert coverageData to String and then back to Data.
Instead we can use
guard let coverageJson = try? JSONDecoder().decode(FileCoverage.self, from: coverageData) else
...
right away. Right?
Skip line 122 to 127
let fileInfoSemaphore = DispatchSemaphore(value: 1) | ||
var fileInfo: [FileInfo] = [] | ||
|
||
// since we need to invoke xccov for each file, it takes pretty much time | ||
// so we invoke it in parallel on 8 threads, that speeds up things considerably | ||
let queue = OperationQueue() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that xcccv tool doesn't need to get called repeatedly we do not need to spread the for loop over different threads. In fact I testest and without spawning to threads it ran slightly faster.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey there, sorry for the delay here, haven't been at my computer much this weekend. Is there a way to toggle this to be a CLI flag? I imagine that for a very large results file it might be faster to work serially on a machine with not a lot of CPUs but on a machine with 8+ cpus I image it might eek out better performance? I can try and work on a PR to add that functionality if you'd be alright with that
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was just saving the CPU for other parallel tasks. I will try some larger xcresult bundles and measure the difference with the PR I just created and with your version. Let's see, it it is worth it to block more than one core for this. And let's take this discussion to the new PR. I invited you as collaborator and once you accept I can add you as reviewer to the PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good to me, I'm not all too familiar with Swift's parallelism and my best frame of reference is Go's goroutines (greenthreads)
This change was brought about by a necessity to optimize how the coverage for each file was generated. On my machine, running the tool for a large xcresult file would take a long time to complete (roughly 2-2.5 minutes) and would cause a significant draw of CPU as multiple instances of xccov were being run.
I've attempted to improve the performance by instead making one call to xccov and generating a JSON report for the whole xcresult file and then iterate over the keys in parallel. This brings the time for running the tool against the same file from 2 minutes to 4 seconds.
This is my first attempt at writing real Swift, so feel free to tear this apart with feedback! Appreciate all the amazing work that has gone into this project! Thanks in advance!