slather 2.6.0 → 2.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/.travis.yml +2 -0
- data/CHANGELOG.md +70 -0
- data/assets/slather.css +2 -1
- data/lib/slather/command/coverage_command.rb +1 -1
- data/lib/slather/coverage_file.rb +1 -1
- data/lib/slather/coverage_service/coveralls.rb +73 -6
- data/lib/slather/coverage_service/html_output.rb +51 -2
- data/lib/slather/profdata_coverage_file.rb +42 -2
- data/lib/slather/project.rb +38 -6
- data/lib/slather/version.rb +1 -1
- data/slather.gemspec +2 -2
- data/spec/fixtures/FixtureFramework/FixtureFramework.h +19 -0
- data/spec/fixtures/FixtureFramework/FlashExperiment.swift +7 -0
- data/spec/fixtures/FixtureFramework/Info.plist +24 -0
- data/spec/fixtures/FixtureFrameworkTests/FixtureFrameworkTests.swift +34 -0
- data/spec/fixtures/FixtureFrameworkTests/FlashExperimentTests.swift +9 -0
- data/spec/fixtures/FixtureFrameworkTests/Info.plist +22 -0
- data/spec/fixtures/cobertura.xml +157 -37
- data/spec/fixtures/fixtures.xcodeproj/project.pbxproj +222 -0
- data/spec/fixtures/fixtures.xcodeproj/xcshareddata/xcschemes/fixtures.xcscheme +21 -5
- data/spec/fixtures/fixtures.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- data/spec/slather/coverage_service/cobertura_xml_spec.rb +1 -1
- data/spec/slather/coverage_service/html_output_spec.rb +2 -2
- data/spec/slather/coverage_service/json_spec.rb +1 -1
- data/spec/slather/coverage_service/llvm_cov_spec.rb +1 -1
- data/spec/slather/coverage_service/sonarqube_xml_spec.rb +1 -1
- data/spec/slather/profdata_coverage_spec.rb +16 -0
- data/spec/slather/project_spec.rb +10 -6
- metadata +23 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b43954fd6f31e519f192e32ad881197d35830912e22158ca17cf333b86f97e0d
|
4
|
+
data.tar.gz: 97cf7f9b6eb50d2472aec42c3f698c3a299c38cfd8757cdb2a6e2dcad0933bc6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f38c40172a16f8375b56cdc873e92339f1f275cef7848bc0d9947714073dd5ff93ba11dc1957781c69316eec59a213c079067b99cb48c134aeb24c20ae2cb945
|
7
|
+
data.tar.gz: 1a098f9ab86b11d5c65cea0c309a99a709de76dfdc0f1171f0e100784522a74613cdb3fb06346f4fb1d6e27825250f01ece561ac07bea6e59a489837c7c0c654
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,75 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
+
## v2.7.4
|
4
|
+
|
5
|
+
* Support Ruby 3.2.0
|
6
|
+
[crazymanish](https://github.com/crazymanish)
|
7
|
+
[#532](https://github.com/SlatherOrg/slather/pull/532)
|
8
|
+
|
9
|
+
## v2.7.3
|
10
|
+
|
11
|
+
* Support Coveralls parallel runs
|
12
|
+
[paulz](https://github.com/paulz)
|
13
|
+
[#523](https://github.com/SlatherOrg/slather/pull/523)
|
14
|
+
|
15
|
+
* Update nokogiri version
|
16
|
+
[anil291987](https://github.com/anil291987)
|
17
|
+
[#518](https://github.com/SlatherOrg/slather/pull/518)
|
18
|
+
[#524](https://github.com/SlatherOrg/slather/pull/524)
|
19
|
+
|
20
|
+
## v2.7.2
|
21
|
+
|
22
|
+
* Update xcodeproj version
|
23
|
+
[adamyanalunas](https://github.com/adamyanalunas)
|
24
|
+
[#502](https://github.com/SlatherOrg/slather/pull/502)
|
25
|
+
|
26
|
+
* Update nokogiri version
|
27
|
+
[jwelton](https://github.com/jwelton)
|
28
|
+
[#503](https://github.com/SlatherOrg/slather/pull/503)
|
29
|
+
|
30
|
+
* Support alternate CI systems in coveralls output
|
31
|
+
[fermoyadrop](https://github.com/fermoyadrop)
|
32
|
+
[#504](https://github.com/SlatherOrg/slather/pull/504)
|
33
|
+
|
34
|
+
* Add Bitrise support to coveralls output
|
35
|
+
[fermoyadrop](https://github.com/fermoyadrop)
|
36
|
+
[#504](https://github.com/SlatherOrg/slather/pull/505)
|
37
|
+
|
38
|
+
## v2.7.1
|
39
|
+
|
40
|
+
* Support generating coverage for framework targets
|
41
|
+
[onato](https://github.com/onato)
|
42
|
+
[#482](https://github.com/SlatherOrg/slather/pull/482)
|
43
|
+
|
44
|
+
* Show number of lines in HTML report
|
45
|
+
[SiemianHS](https://github.com/SiemianHS)
|
46
|
+
[#494](https://github.com/SlatherOrg/slather/pull/494)
|
47
|
+
|
48
|
+
* Fixed issues with HTML report generation
|
49
|
+
[fchiba](https://github.com/fchiba)
|
50
|
+
[#483](https://github.com/SlatherOrg/slather/pull/483)
|
51
|
+
[#484](https://github.com/SlatherOrg/slather/pull/484)
|
52
|
+
|
53
|
+
* Don't fail if a source file doesn't exist
|
54
|
+
[chillpop](https://github.com/chillpop)
|
55
|
+
[#492](https://github.com/SlatherOrg/slather/pull/492)
|
56
|
+
|
57
|
+
## v2.7.0
|
58
|
+
|
59
|
+
* Add Branch Coverage data for ProfData coverage files
|
60
|
+
[hborawski](https://github.com/hborawski)
|
61
|
+
[#477](https://github.com/SlatherOrg/slather/pull/477)
|
62
|
+
|
63
|
+
* Fixed 'Argument list too long' when running 'xcrun llvm-cov'
|
64
|
+
[samuelsainz](https://github.com/samuelsainz)
|
65
|
+
[#476](https://github.com/SlatherOrg/slather/pull/476)
|
66
|
+
|
67
|
+
## v2.6.1
|
68
|
+
|
69
|
+
* Update nokogiri to 1.11
|
70
|
+
[ashin-omg](https://github.com/ashin-omg)
|
71
|
+
[#473](https://github.com/SlatherOrg/slather/pull/473)
|
72
|
+
|
3
73
|
## v2.6.0
|
4
74
|
|
5
75
|
* Added GitHub actions support
|
data/assets/slather.css
CHANGED
@@ -74,6 +74,7 @@ table.source_code {
|
|
74
74
|
table.source_code td {
|
75
75
|
padding-bottom: 0.3em;
|
76
76
|
}
|
77
|
+
code.missed { background-color: rgba(255, 73, 76, 0.3); }
|
77
78
|
table.source_code tr.missed td { background-color: rgba(248, 103, 105, 0.2); }
|
78
79
|
table.source_code tr.covered td { background-color: rgba(103, 207, 124, 0.2); }
|
79
80
|
table.source_code td.num {
|
@@ -124,7 +125,7 @@ footer p, footer a {
|
|
124
125
|
Syntax Highlighting using highlight.js (https://highlightjs.org)
|
125
126
|
------------------------------------------------------------- */
|
126
127
|
.hljs {
|
127
|
-
display: block;
|
128
|
+
display: inline-block;
|
128
129
|
overflow-x: auto;
|
129
130
|
-webkit-text-size-adjust: none;
|
130
131
|
}
|
@@ -30,7 +30,7 @@ class CoverageCommand < Clamp::Command
|
|
30
30
|
option ["--scheme"], "SCHEME", "The scheme for which the coverage was generated"
|
31
31
|
option ["--configuration"], "CONFIGURATION", "The configuration for test that the project was set"
|
32
32
|
option ["--workspace"], "WORKSPACE", "The workspace that the project was built in"
|
33
|
-
option ["--binary-file"], "BINARY_FILE", "The binary file against
|
33
|
+
option ["--binary-file"], "BINARY_FILE", "The binary file against which the coverage will be run", :multivalued => true
|
34
34
|
option ["--binary-basename"], "BINARY_BASENAME", "Basename of the file against which the coverage will be run", :multivalued => true
|
35
35
|
option ["--arch"], "ARCH", "Architecture to use from universal binaries"
|
36
36
|
option ["--source-files"], "SOURCE_FILES", "A Dir.glob compatible pattern used to limit the lookup to specific source files. Ignored in gcov mode.", :multivalued => true
|
@@ -51,7 +51,7 @@ module Slather
|
|
51
51
|
gcov_files_created = gcov_output.scan(/creating '(.+\..+\.gcov)'/)
|
52
52
|
|
53
53
|
gcov_file_name = "./#{source_file_pathname.basename}.gcov"
|
54
|
-
if File.
|
54
|
+
if File.exist?(gcov_file_name)
|
55
55
|
gcov_data = File.new(gcov_file_name).read
|
56
56
|
else
|
57
57
|
gcov_data = ""
|
@@ -41,6 +41,16 @@ module Slather
|
|
41
41
|
end
|
42
42
|
private :github_job_id
|
43
43
|
|
44
|
+
def bitrise_job_id
|
45
|
+
ENV['BITRISE_BUILD_NUMBER']
|
46
|
+
end
|
47
|
+
private :bitrise_job_id
|
48
|
+
|
49
|
+
def bitrise_pull_request
|
50
|
+
ENV['BITRISE_PULL_REQUEST']
|
51
|
+
end
|
52
|
+
private :bitrise_pull_request
|
53
|
+
|
44
54
|
def github_pull_request
|
45
55
|
ENV['CI_PULL_REQUEST'] || ""
|
46
56
|
end
|
@@ -71,6 +81,11 @@ module Slather
|
|
71
81
|
end
|
72
82
|
private :github_branch_name
|
73
83
|
|
84
|
+
def bitrise_branch_name
|
85
|
+
ENV['BITRISE_GIT_BRANCH'] || `git ls-remote --heads origin | grep $(git rev-parse HEAD) | cut -d / -f 3-`.chomp
|
86
|
+
end
|
87
|
+
private :bitrise_branch_name
|
88
|
+
|
74
89
|
def buildkite_job_id
|
75
90
|
ENV['BUILDKITE_BUILD_NUMBER']
|
76
91
|
end
|
@@ -151,16 +166,39 @@ module Slather
|
|
151
166
|
end
|
152
167
|
private :github_git_info
|
153
168
|
|
169
|
+
def bitrise_git_info
|
170
|
+
{
|
171
|
+
:head => {
|
172
|
+
:id => ENV['BITRISE_GIT_COMMIT'],
|
173
|
+
:committer_name => (ENV['GIT_CLONE_COMMIT_AUTHOR_NAME'] || `git log --format=%an -n 1 HEAD`.chomp || ""),
|
174
|
+
:committer_email => (ENV['GIT_CLONE_COMMIT_AUTHOR_EMAIL'] || `git log --format=%ae -n 1 HEAD`.chomp || ""),
|
175
|
+
:message => (ENV['BITRISE_GIT_MESSAGE'] || `git log --format=%s -n 1 HEAD`.chomp || "")
|
176
|
+
},
|
177
|
+
:branch => bitrise_branch_name
|
178
|
+
}
|
179
|
+
end
|
180
|
+
private :bitrise_git_info
|
181
|
+
|
154
182
|
def github_build_url
|
155
183
|
"https://github.com/" + ENV['GITHUB_REPOSITORY'] + "/actions/runs/" + ENV['GITHUB_RUN_ID']
|
156
184
|
end
|
157
185
|
private :github_build_url
|
158
186
|
|
187
|
+
def is_parallel
|
188
|
+
ENV['IS_PARALLEL'] != nil
|
189
|
+
end
|
190
|
+
private :is_parallel
|
191
|
+
|
192
|
+
def github_job_name
|
193
|
+
ENV['GITHUB_JOB']
|
194
|
+
end
|
195
|
+
private :github_job_name
|
196
|
+
|
159
197
|
def coveralls_coverage_data
|
160
198
|
if ci_service == :travis_ci || ci_service == :travis_pro
|
161
199
|
if travis_job_id
|
162
200
|
if ci_service == :travis_ci
|
163
|
-
|
201
|
+
|
164
202
|
if coverage_access_token.to_s.strip.length > 0
|
165
203
|
raise StandardError, "Access token is set. Uploading coverage data for public repositories doesn't require an access token."
|
166
204
|
end
|
@@ -170,7 +208,7 @@ module Slather
|
|
170
208
|
:service_name => "travis-ci",
|
171
209
|
:source_files => coverage_files.map(&:as_json)
|
172
210
|
}.to_json
|
173
|
-
elsif ci_service == :travis_pro
|
211
|
+
elsif ci_service == :travis_pro
|
174
212
|
|
175
213
|
if coverage_access_token.to_s.strip.length == 0
|
176
214
|
raise StandardError, "Access token is not set. Uploading coverage data for private repositories requires an access token."
|
@@ -258,13 +296,42 @@ module Slather
|
|
258
296
|
:source_files => coverage_files.map(&:as_json),
|
259
297
|
:service_build_url => github_build_url,
|
260
298
|
:service_pull_request => github_pull_request,
|
261
|
-
:git => github_git_info
|
299
|
+
:git => github_git_info,
|
300
|
+
:parallel => is_parallel,
|
301
|
+
:flag_name => github_job_name
|
262
302
|
}.to_json
|
263
303
|
else
|
264
304
|
raise StandardError, "Environment variable `GITHUB_RUN_ID` not set. Is this running on github build?"
|
265
305
|
end
|
306
|
+
elsif ci_service == :bitrise
|
307
|
+
{
|
308
|
+
:service_job_id => bitrise_job_id,
|
309
|
+
:service_name => 'bitrise',
|
310
|
+
:repo_token => coverage_access_token,
|
311
|
+
:source_files => coverage_files.map(&:as_json),
|
312
|
+
:service_pull_request => bitrise_pull_request,
|
313
|
+
:service_branch => bitrise_branch_name,
|
314
|
+
:git => bitrise_git_info
|
315
|
+
}.to_json
|
266
316
|
else
|
267
|
-
|
317
|
+
{
|
318
|
+
:service_job_id => ENV['CI_BUILD_NUMBER'],
|
319
|
+
:service_name => ENV['CI_NAME'] || ci_service,
|
320
|
+
:repo_token => coverage_access_token,
|
321
|
+
:source_files => coverage_files.map(&:as_json),
|
322
|
+
:service_build_url => ENV['CI_BUILD_URL'],
|
323
|
+
:service_pull_request => ENV['CI_PULL_REQUEST'],
|
324
|
+
:service_branch => ENV['CI_BRANCH'],
|
325
|
+
:git => {
|
326
|
+
:head => {
|
327
|
+
:id => ENV['CI_COMMIT'],
|
328
|
+
:committer_name => (`git log --format=%an -n 1 HEAD`.chomp || ""),
|
329
|
+
:committer_email => (`git log --format=%ae -n 1 HEAD`.chomp || ""),
|
330
|
+
:message => (`git log --format=%s -n 1 HEAD`.chomp || "")
|
331
|
+
},
|
332
|
+
:branch => ENV['CI_BRANCH']
|
333
|
+
}
|
334
|
+
}.to_json
|
268
335
|
end
|
269
336
|
end
|
270
337
|
private :coveralls_coverage_data
|
@@ -277,8 +344,8 @@ module Slather
|
|
277
344
|
|
278
345
|
curl_result = `curl -s --form json_file=@#{f.path} #{coveralls_api_jobs_path}`
|
279
346
|
|
280
|
-
if curl_result.is_a? String
|
281
|
-
curl_result_json = JSON.parse(curl_result)
|
347
|
+
if curl_result.is_a? String
|
348
|
+
curl_result_json = JSON.parse(curl_result)
|
282
349
|
|
283
350
|
if curl_result_json["error"]
|
284
351
|
error_message = curl_result_json["message"]
|
@@ -70,9 +70,14 @@ module Slather
|
|
70
70
|
|
71
71
|
total_relevant_lines = 0
|
72
72
|
total_tested_lines = 0
|
73
|
+
total_relevant_branches = 0
|
74
|
+
total_branches_tested = 0
|
73
75
|
coverage_files.each { |coverage_file|
|
74
76
|
total_tested_lines += coverage_file.num_lines_tested
|
75
77
|
total_relevant_lines += coverage_file.num_lines_testable
|
78
|
+
|
79
|
+
total_relevant_branches += coverage_file.num_branches_testable
|
80
|
+
total_branches_tested += coverage_file.num_branches_tested
|
76
81
|
}
|
77
82
|
|
78
83
|
builder = Nokogiri::HTML::Builder.with(template.at('#reports')) { |cov|
|
@@ -82,6 +87,22 @@ module Slather
|
|
82
87
|
percentage = (total_tested_lines / total_relevant_lines.to_f) * 100.0
|
83
88
|
cov.span "Total Coverage : "
|
84
89
|
cov.span decimal_f(percentage) + '%', :class => class_for_coverage_percentage(percentage), :id => "total_coverage"
|
90
|
+
cov.span " ("
|
91
|
+
cov.span total_tested_lines, :id => "total_tested_lines"
|
92
|
+
cov.span " of "
|
93
|
+
cov.span total_relevant_lines, :id => "total_relevant_lines"
|
94
|
+
cov.span " lines)"
|
95
|
+
}
|
96
|
+
|
97
|
+
cov.h4 {
|
98
|
+
percentage = (total_branches_tested / total_relevant_branches.to_f) * 100.0
|
99
|
+
cov.span "Total Branch Coverage : "
|
100
|
+
cov.span decimal_f(percentage) + '%', :class => class_for_coverage_percentage(percentage), :id => "total_coverage"
|
101
|
+
cov.span " ("
|
102
|
+
cov.span total_branches_tested, :id => "total_branches_tested"
|
103
|
+
cov.span " of "
|
104
|
+
cov.span total_relevant_branches, :id => "total_relevant_branches"
|
105
|
+
cov.span " lines)"
|
85
106
|
}
|
86
107
|
|
87
108
|
cov.input(:class => "search", :placeholder => "Search")
|
@@ -133,6 +154,7 @@ module Slather
|
|
133
154
|
filepath = coverage_file.source_file_pathname_relative_to_repo_root
|
134
155
|
filename = File.basename(filepath)
|
135
156
|
percentage = coverage_file.percentage_lines_tested
|
157
|
+
branch_percentage = coverage_file.rate_branches_tested * 100
|
136
158
|
|
137
159
|
cleaned_gcov_lines = coverage_file.cleaned_gcov_data.split("\n")
|
138
160
|
is_file_empty = (cleaned_gcov_lines.count <= 0)
|
@@ -142,7 +164,10 @@ module Slather
|
|
142
164
|
builder = Nokogiri::HTML::Builder.with(template.at('#reports')) { |cov|
|
143
165
|
cov.h2(:class => "cov_title") {
|
144
166
|
cov.span("Coverage for \"#{filename}\"" + (!is_file_empty ? " : " : ""))
|
167
|
+
cov.span("Lines: ") unless is_file_empty
|
145
168
|
cov.span("#{decimal_f(percentage)}%", :class => class_for_coverage_percentage(percentage)) unless is_file_empty
|
169
|
+
cov.span(" Branches: ") unless is_file_empty
|
170
|
+
cov.span("#{decimal_f(branch_percentage)}%", :class => class_for_coverage_percentage(branch_percentage)) unless is_file_empty
|
146
171
|
}
|
147
172
|
|
148
173
|
cov.h4("(#{coverage_file.num_lines_tested} of #{coverage_file.num_lines_testable} relevant lines covered)", :class => "cov_subtitle")
|
@@ -157,8 +182,9 @@ module Slather
|
|
157
182
|
|
158
183
|
cov.table(:class => "source_code") {
|
159
184
|
cleaned_gcov_lines.each do |line|
|
160
|
-
|
161
185
|
line_number = coverage_file.line_number_in_line(line)
|
186
|
+
missed_regions = coverage_file.branch_region_data[line_number]
|
187
|
+
hits = coverage_file.coverage_for_line(line)
|
162
188
|
next unless line_number > 0
|
163
189
|
|
164
190
|
line_source = line.split(line_number_separator, 3)[2]
|
@@ -171,7 +197,30 @@ module Slather
|
|
171
197
|
cov.td(line, :class => classes[idx])
|
172
198
|
else
|
173
199
|
cov.td(:class => classes[idx]) {
|
174
|
-
cov.pre {
|
200
|
+
cov.pre {
|
201
|
+
# If the line has coverage and missed regions, split up
|
202
|
+
# the line to show regions that weren't covered
|
203
|
+
if missed_regions != nil && hits != nil && hits > 0
|
204
|
+
regions = missed_regions.map do |region|
|
205
|
+
region_start, region_length = region
|
206
|
+
if region_length != nil
|
207
|
+
line[region_start, region_length]
|
208
|
+
else
|
209
|
+
line[region_start, line.length - region_start]
|
210
|
+
end
|
211
|
+
end
|
212
|
+
current_line = line
|
213
|
+
regions.each do |region|
|
214
|
+
covered, remainder = current_line.split(region, 2)
|
215
|
+
cov.code(covered, :class => "objc")
|
216
|
+
cov.code(region, :class => "objc missed")
|
217
|
+
current_line = remainder
|
218
|
+
end
|
219
|
+
cov.code(current_line, :class => "objc")
|
220
|
+
else
|
221
|
+
cov.code(line, :class => "objc")
|
222
|
+
end
|
223
|
+
}
|
175
224
|
}
|
176
225
|
end
|
177
226
|
}
|
@@ -8,7 +8,7 @@ module Slather
|
|
8
8
|
include CoverageInfo
|
9
9
|
include CoverallsCoverage
|
10
10
|
|
11
|
-
attr_accessor :project, :source, :line_numbers_first, :line_data
|
11
|
+
attr_accessor :project, :source, :segments, :line_numbers_first, :line_data
|
12
12
|
|
13
13
|
def initialize(project, source, line_numbers_first)
|
14
14
|
self.project = project
|
@@ -188,7 +188,47 @@ module Slather
|
|
188
188
|
|
189
189
|
def branch_coverage_data
|
190
190
|
@branch_coverage_data ||= begin
|
191
|
-
Hash.new
|
191
|
+
branch_coverage_data = Hash.new
|
192
|
+
|
193
|
+
self.segments.each do |segment|
|
194
|
+
line, col, hits, has_count, *rest = segment
|
195
|
+
next if !has_count
|
196
|
+
if branch_coverage_data.key?(line)
|
197
|
+
branch_coverage_data[line] = branch_coverage_data[line] + [hits]
|
198
|
+
else
|
199
|
+
branch_coverage_data[line] = [hits]
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
branch_coverage_data
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def branch_region_data
|
208
|
+
@branch_region_data ||= begin
|
209
|
+
branch_region_data = Hash.new
|
210
|
+
region_start = nil
|
211
|
+
current_line = 0
|
212
|
+
@segments ||= []
|
213
|
+
@segments.each do |segment|
|
214
|
+
line, col, hits, has_count, *rest = segment
|
215
|
+
# Make column 0 based index
|
216
|
+
col = col - 1
|
217
|
+
if hits == 0 && has_count
|
218
|
+
current_line = line
|
219
|
+
region_start = col
|
220
|
+
elsif region_start != nil && hits > 0 && has_count
|
221
|
+
# if the region wrapped to a new line before ending, put nil to indicate it didnt end on this line
|
222
|
+
region_end = line == current_line ? col - region_start : nil
|
223
|
+
if branch_region_data.key?(current_line)
|
224
|
+
branch_region_data[current_line] << [region_start, region_end]
|
225
|
+
else
|
226
|
+
branch_region_data[current_line] = [[region_start, region_end]]
|
227
|
+
end
|
228
|
+
region_start = nil
|
229
|
+
end
|
230
|
+
end
|
231
|
+
branch_region_data
|
192
232
|
end
|
193
233
|
end
|
194
234
|
|
data/lib/slather/project.rb
CHANGED
@@ -135,7 +135,12 @@ module Slather
|
|
135
135
|
coverage_json = JSON.parse(coverage_json_string)
|
136
136
|
coverage_json["data"].reduce([]) do |result, chunk|
|
137
137
|
result.concat(chunk["files"].map do |file|
|
138
|
-
|
138
|
+
filename = file["filename"]
|
139
|
+
path = Pathname(filename)
|
140
|
+
# Don't crash if the file doesn't exist on disk.
|
141
|
+
# This may happen for autogenerated files that have been deleted.
|
142
|
+
filename = path.exist? ? path.realpath : filename
|
143
|
+
{"filename" => filename, "segments" => file["segments"]}
|
139
144
|
end)
|
140
145
|
end
|
141
146
|
end
|
@@ -162,13 +167,24 @@ module Slather
|
|
162
167
|
end
|
163
168
|
private :create_coverage_files_for_binary
|
164
169
|
|
165
|
-
def create_coverage_files(binary_path,
|
170
|
+
def create_coverage_files(binary_path, path_objects)
|
166
171
|
line_numbers_first = Gem::Version.new(self.llvm_version) >= Gem::Version.new('8.1.0')
|
172
|
+
# get just file names from the path objects
|
173
|
+
pathnames = path_objects.map { |path_obj| path_obj["filename"] }.compact
|
174
|
+
# Map of path name => segment array
|
175
|
+
paths_to_segments = path_objects.reduce(Hash.new) do |hash, path_obj|
|
176
|
+
hash[path_obj["filename"]] = path_obj["segments"]
|
177
|
+
hash
|
178
|
+
end
|
167
179
|
files = create_profdata(binary_path, pathnames)
|
168
180
|
files.map do |source|
|
169
181
|
coverage_file = coverage_file_class.new(self, source, line_numbers_first)
|
170
182
|
# If a single source file is used, the resulting output does not contain the file name.
|
171
183
|
coverage_file.source_file_pathname = pathnames.first if pathnames.count == 1
|
184
|
+
# if there is segment data for the given path, add it to the coverage_file
|
185
|
+
if paths_to_segments.key?(coverage_file.source_file_pathname)
|
186
|
+
coverage_file.segments = paths_to_segments[coverage_file.source_file_pathname]
|
187
|
+
end
|
172
188
|
!coverage_file.ignored? ? coverage_file : nil
|
173
189
|
end.compact
|
174
190
|
end
|
@@ -192,7 +208,7 @@ module Slather
|
|
192
208
|
|
193
209
|
def profdata_coverage_dir
|
194
210
|
@profdata_coverage_dir ||= begin
|
195
|
-
raise StandardError, "The specified build directory (#{self.build_directory}) does not exist" unless File.
|
211
|
+
raise StandardError, "The specified build directory (#{self.build_directory}) does not exist" unless File.exist?(self.build_directory)
|
196
212
|
dir = nil
|
197
213
|
if self.scheme
|
198
214
|
dir = Dir[File.join(build_directory,"/**/CodeCoverage/#{self.scheme}")].first
|
@@ -277,7 +293,10 @@ module Slather
|
|
277
293
|
if self.arch
|
278
294
|
llvm_cov_args << "--arch" << self.arch
|
279
295
|
end
|
280
|
-
|
296
|
+
|
297
|
+
# POSIX systems have an ARG_MAX for the maximum total length of the command line, so the command may fail with an error message of "Argument list too long".
|
298
|
+
# Using the xargs command we can break the list of source_files into sublists small enough to be acceptable.
|
299
|
+
`printf '%s\\0' #{source_files.shelljoin} | xargs -0 xcrun llvm-cov #{llvm_cov_args.shelljoin}`
|
281
300
|
end
|
282
301
|
private :unsafe_profdata_llvm_cov_output
|
283
302
|
|
@@ -356,7 +375,7 @@ module Slather
|
|
356
375
|
end
|
357
376
|
|
358
377
|
def configure_ci_service
|
359
|
-
self.ci_service ||= (self.class.yml["ci_service"] || :
|
378
|
+
self.ci_service ||= (ENV["CI_SERVICE"] || self.class.yml["ci_service"] || :other)
|
360
379
|
end
|
361
380
|
|
362
381
|
def configure_input_format
|
@@ -484,7 +503,7 @@ module Slather
|
|
484
503
|
end
|
485
504
|
end
|
486
505
|
|
487
|
-
raise StandardError, "No scheme named '#{self.scheme}' found in #{self.path}" unless File.
|
506
|
+
raise StandardError, "No scheme named '#{self.scheme}' found in #{self.path}" unless File.exist? xcscheme_path
|
488
507
|
|
489
508
|
xcscheme = Xcodeproj::XCScheme.new(xcscheme_path)
|
490
509
|
|
@@ -568,6 +587,19 @@ module Slather
|
|
568
587
|
def find_buildable_names(xcscheme)
|
569
588
|
found_buildable_names = []
|
570
589
|
|
590
|
+
# enumerate code coverage targets
|
591
|
+
begin
|
592
|
+
code_coverage_targets = xcscheme.test_action.xml_element.elements['CodeCoverageTargets']
|
593
|
+
targets = code_coverage_targets.map do |node|
|
594
|
+
Xcodeproj::XCScheme::BuildableReference.new(node) if node.is_a?(REXML::Element)
|
595
|
+
end.compact
|
596
|
+
buildable_names = targets.each do |target|
|
597
|
+
found_buildable_names.push(target.buildable_name)
|
598
|
+
end
|
599
|
+
rescue
|
600
|
+
# just in case if there are no entries in the test action
|
601
|
+
end
|
602
|
+
|
571
603
|
# enumerate build action entries
|
572
604
|
begin
|
573
605
|
xcscheme.build_action.entries.each do |entry|
|
data/lib/slather/version.rb
CHANGED
data/slather.gemspec
CHANGED
@@ -28,8 +28,8 @@ Gem::Specification.new do |spec|
|
|
28
28
|
spec.add_development_dependency 'equivalent-xml', '~> 0.6'
|
29
29
|
|
30
30
|
spec.add_dependency 'clamp', '~> 1.3'
|
31
|
-
spec.add_dependency 'xcodeproj', '~> 1.
|
32
|
-
spec.add_dependency 'nokogiri', '
|
31
|
+
spec.add_dependency 'xcodeproj', '~> 1.21'
|
32
|
+
spec.add_dependency 'nokogiri', '>= 1.13.9'
|
33
33
|
spec.add_dependency 'CFPropertyList', '>= 2.2', '< 4'
|
34
34
|
|
35
35
|
spec.add_runtime_dependency 'activesupport'
|
@@ -0,0 +1,19 @@
|
|
1
|
+
//
|
2
|
+
// FixtureFramework.h
|
3
|
+
// FixtureFramework
|
4
|
+
//
|
5
|
+
// Created by Stephen Williams on 11/03/21.
|
6
|
+
// Copyright © 2021 marklarr. All rights reserved.
|
7
|
+
//
|
8
|
+
|
9
|
+
#import <Foundation/Foundation.h>
|
10
|
+
|
11
|
+
//! Project version number for FixtureFramework.
|
12
|
+
FOUNDATION_EXPORT double FixtureFrameworkVersionNumber;
|
13
|
+
|
14
|
+
//! Project version string for FixtureFramework.
|
15
|
+
FOUNDATION_EXPORT const unsigned char FixtureFrameworkVersionString[];
|
16
|
+
|
17
|
+
// In this header, you should import all the public headers of your framework using statements like #import <FixtureFramework/PublicHeader.h>
|
18
|
+
|
19
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
3
|
+
<plist version="1.0">
|
4
|
+
<dict>
|
5
|
+
<key>CFBundleDevelopmentRegion</key>
|
6
|
+
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
7
|
+
<key>CFBundleExecutable</key>
|
8
|
+
<string>$(EXECUTABLE_NAME)</string>
|
9
|
+
<key>CFBundleIdentifier</key>
|
10
|
+
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
11
|
+
<key>CFBundleInfoDictionaryVersion</key>
|
12
|
+
<string>6.0</string>
|
13
|
+
<key>CFBundleName</key>
|
14
|
+
<string>$(PRODUCT_NAME)</string>
|
15
|
+
<key>CFBundlePackageType</key>
|
16
|
+
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
17
|
+
<key>CFBundleShortVersionString</key>
|
18
|
+
<string>1.0</string>
|
19
|
+
<key>CFBundleVersion</key>
|
20
|
+
<string>$(CURRENT_PROJECT_VERSION)</string>
|
21
|
+
<key>NSHumanReadableCopyright</key>
|
22
|
+
<string>Copyright © 2021 marklarr. All rights reserved.</string>
|
23
|
+
</dict>
|
24
|
+
</plist>
|
@@ -0,0 +1,34 @@
|
|
1
|
+
//
|
2
|
+
// FixtureFrameworkTests.swift
|
3
|
+
// FixtureFrameworkTests
|
4
|
+
//
|
5
|
+
// Created by Stephen Williams on 11/03/21.
|
6
|
+
// Copyright © 2021 marklarr. All rights reserved.
|
7
|
+
//
|
8
|
+
|
9
|
+
import XCTest
|
10
|
+
@testable import FixtureFramework
|
11
|
+
|
12
|
+
class FixtureFrameworkTests: XCTestCase {
|
13
|
+
|
14
|
+
override func setUpWithError() throws {
|
15
|
+
// Put setup code here. This method is called before the invocation of each test method in the class.
|
16
|
+
}
|
17
|
+
|
18
|
+
override func tearDownWithError() throws {
|
19
|
+
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
20
|
+
}
|
21
|
+
|
22
|
+
func testExample() throws {
|
23
|
+
// This is an example of a functional test case.
|
24
|
+
// Use XCTAssert and related functions to verify your tests produce the correct results.
|
25
|
+
}
|
26
|
+
|
27
|
+
func testPerformanceExample() throws {
|
28
|
+
// This is an example of a performance test case.
|
29
|
+
self.measure {
|
30
|
+
// Put the code you want to measure the time of here.
|
31
|
+
}
|
32
|
+
}
|
33
|
+
|
34
|
+
}
|