slather 2.4.6 → 2.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +5 -1
- data/.travis.yml +2 -2
- data/CHANGELOG.md +49 -0
- data/README.md +35 -1
- data/lib/slather.rb +1 -0
- data/lib/slather/command/coverage_command.rb +6 -0
- data/lib/slather/coverage_file.rb +14 -9
- data/lib/slather/coverage_service/coveralls.rb +57 -0
- data/lib/slather/coverage_service/sonarqube_xml_output.rb +61 -0
- data/lib/slather/profdata_coverage_file.rb +24 -10
- data/lib/slather/project.rb +78 -40
- data/lib/slather/version.rb +1 -1
- data/slather.gemspec +14 -14
- data/spec/fixtures/sonarqube-generic-coverage.xml +93 -0
- data/spec/slather/cocoapods_plugin_spec.rb +1 -1
- data/spec/slather/coverage_service/coveralls_spec.rb +20 -0
- data/spec/slather/coverage_service/sonarqube_xml_spec.rb +46 -0
- data/spec/slather/profdata_coverage_spec.rb +21 -0
- data/spec/slather/project_spec.rb +78 -8
- data/spec/spec_helper.rb +1 -0
- metadata +48 -38
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eeaf99ed5212bdc90e8098dfde877452af7f074ecfd41fc577d46f90b1ee65eb
|
4
|
+
data.tar.gz: 5dbd0db5223f26990c2bfd3ab02f4ed36a15d8088a0b5c393f522a785a0176cd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6802b4ef28cfb856bf68d214416d6a455de715723327dc7167600bc9cae705dacbdbffb0b8d154b2027dde6d5b2f6baec719f69a81e5293a437da1e260461e73
|
7
|
+
data.tar.gz: c6b95c762eaa1d5260da5de5910e7f429a79e45556a3b32568ba8c7c9f3ed173ed514330793c39babc767e4f021dda7fc83971750d16407d0149050286495b95
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
language: objective-c
|
2
2
|
script: bundle exec rake
|
3
|
-
osx_image:
|
3
|
+
osx_image: xcode12.2
|
4
4
|
|
5
5
|
before_install:
|
6
6
|
- curl http://curl.haxx.se/ca/cacert.pem -o /usr/local/share/cacert.pem
|
7
|
-
- gem install bundler
|
7
|
+
- gem install bundler -v "~> 2.0" --no-document
|
8
8
|
|
9
9
|
install:
|
10
10
|
- bundle install --without=documentation
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,54 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
+
## v2.6.0
|
4
|
+
|
5
|
+
* Added GitHub actions support
|
6
|
+
[martin-key](https://github.com/martin-key), [troyfontaine](https://github.com/troyfontaine)
|
7
|
+
[#468](https://github.com/SlatherOrg/slather/pull/468)
|
8
|
+
|
9
|
+
## v2.5.0
|
10
|
+
|
11
|
+
* Fixed activesupport and cocoapods dependencies
|
12
|
+
[daneov](https://github.com/daneov)
|
13
|
+
[#456](https://github.com/SlatherOrg/slather/pull/467)
|
14
|
+
|
15
|
+
* Fixed typo in documentation
|
16
|
+
[descorp](https://github.com/descorp)
|
17
|
+
[#456](https://github.com/SlatherOrg/slather/pull/463)
|
18
|
+
|
19
|
+
## v2.4.9
|
20
|
+
|
21
|
+
* Added support for Sonarqube output
|
22
|
+
[adellibovi](https://github.com/adellibovi)
|
23
|
+
[#456](https://github.com/SlatherOrg/slather/pull/456)
|
24
|
+
|
25
|
+
## v2.4.8
|
26
|
+
|
27
|
+
* Optimize performance for many binaries
|
28
|
+
[cltnschlosser](https://github.com/cltnschlosser)
|
29
|
+
[#455](https://github.com/SlatherOrg/slather/pull/455)
|
30
|
+
|
31
|
+
* Don't generate line 0 in profdata_coverage_file.rb from line with error
|
32
|
+
[tthbalazs](https://github.com/tthbalazs)
|
33
|
+
[#449](https://github.com/SlatherOrg/slather/pull/449)
|
34
|
+
|
35
|
+
* coveralls dependency update
|
36
|
+
[GRiMe2D](https://github.com/GRiMe2D)
|
37
|
+
[#448](https://github.com/SlatherOrg/slather/pull/448)
|
38
|
+
|
39
|
+
## v2.4.7
|
40
|
+
|
41
|
+
* Update dependencies
|
42
|
+
[dnedrow](https://github.com/dnedrow)
|
43
|
+
|
44
|
+
* Fixed errors when llvm-cov argument length exceeds ARG_MAX
|
45
|
+
[weibel](https://github.com/weibel)
|
46
|
+
[#414](https://github.com/SlatherOrg/slather/pull/414)
|
47
|
+
|
48
|
+
* Show "No coverage directory found." instead of "implicit conversion nil into String"
|
49
|
+
[phimage](https://github.com/phimage)
|
50
|
+
[#381](https://github.com/SlatherOrg/slather/pull/381) [#341](https://github.com/SlatherOrg/slather/issues/341)
|
51
|
+
|
3
52
|
## v2.4.6
|
4
53
|
|
5
54
|
* Fix .dSYM and .swiftmodule files filtering in find_binary_files()
|
data/README.md
CHANGED
@@ -67,6 +67,20 @@ If your configuration produces a universal binary you need to specify a specific
|
|
67
67
|
$ slather coverage -s --arch x86_64 --scheme YourXcodeSchemeName --configuration YourBuildConfigurationName path/to/project.xcodeproj
|
68
68
|
```
|
69
69
|
|
70
|
+
### For multiple modules
|
71
|
+
|
72
|
+
If you want to run some modules, but not all (like modules created by CocoaPods) you can do it like this:
|
73
|
+
|
74
|
+
```sh
|
75
|
+
$ slather coverage --binary-basename module1 --binary-basename module2 path/to/project.xcodeproj
|
76
|
+
```
|
77
|
+
You can also add it to the `.slather.yml` file as an array:
|
78
|
+
```yml
|
79
|
+
binary_basename:
|
80
|
+
- module1
|
81
|
+
- module2
|
82
|
+
```
|
83
|
+
|
70
84
|
### Setup for Xcode 5 and 6
|
71
85
|
|
72
86
|
Run this command to enable the `Generate Test Coverage` and `Instrument Program Flow` flags for your project:
|
@@ -135,7 +149,7 @@ ignore:
|
|
135
149
|
- ProjectTestsGroup/*
|
136
150
|
```
|
137
151
|
|
138
|
-
And then in your `.travis.yml` or `circle.yml`, call `slather` after a successful build:
|
152
|
+
And then in your `.travis.yml` or `circle.yml` or `github-action.yml`, call `slather` after a successful build:
|
139
153
|
|
140
154
|
```yml
|
141
155
|
# .travis.yml
|
@@ -154,6 +168,25 @@ test:
|
|
154
168
|
|
155
169
|
```
|
156
170
|
|
171
|
+
```yml
|
172
|
+
# github-action.yml
|
173
|
+
myjob:
|
174
|
+
steps:
|
175
|
+
- run: |
|
176
|
+
bundle config path vendor/bundle
|
177
|
+
bundle install --without=documentation --jobs 4 --retry 3
|
178
|
+
- name: Extract branch name
|
179
|
+
shell: bash
|
180
|
+
run: echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})"
|
181
|
+
id: get_branch
|
182
|
+
- run: bundle exec slather
|
183
|
+
env:
|
184
|
+
GIT_BRANCH: ${{ steps.get_branch.outputs.branch }}
|
185
|
+
CI_PULL_REQUEST: ${{ github.event.number }}
|
186
|
+
COVERAGE_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
187
|
+
|
188
|
+
```
|
189
|
+
|
157
190
|
#### Usage with Travis CI Pro
|
158
191
|
|
159
192
|
To use Coveralls with Travis CI Pro (for private repos), add following lines along with other settings to `.slather.yml`:
|
@@ -251,3 +284,4 @@ Please make sure to follow our general coding style and add test coverage for ne
|
|
251
284
|
* [@jhersh](https://github.com/jhersh), CircleCI support.
|
252
285
|
* [@tarbrain](https://github.com/tarbrain), Cobertura support and bugfixing.
|
253
286
|
* [@ikhsan](https://github.com/ikhsan), html support.
|
287
|
+
* [@martin-key](https://github.com/martin-key) and [@troyfontaine](https://github.com/troyfontaine), Github Actions support.
|
data/lib/slather.rb
CHANGED
@@ -12,6 +12,7 @@ require 'slather/coverage_service/simple_output'
|
|
12
12
|
require 'slather/coverage_service/html_output'
|
13
13
|
require 'slather/coverage_service/json_output'
|
14
14
|
require 'slather/coverage_service/llvm_cov_output'
|
15
|
+
require 'slather/coverage_service/sonarqube_xml_output'
|
15
16
|
require 'cfpropertylist'
|
16
17
|
|
17
18
|
module Slather
|
@@ -8,11 +8,13 @@ class CoverageCommand < Clamp::Command
|
|
8
8
|
option ["--jenkins"], :flag, "Indicate that the builds are running on Jenkins"
|
9
9
|
option ["--buildkite"], :flag, "Indicate that the builds are running on Buildkite"
|
10
10
|
option ["--teamcity"], :flag, "Indicate that the builds are running on TeamCity"
|
11
|
+
option ["--github"], :flag, "Indicate that the builds are running on Github Actions"
|
11
12
|
|
12
13
|
option ["--coveralls", "-c"], :flag, "Post coverage results to coveralls"
|
13
14
|
option ["--simple-output", "-s"], :flag, "Output coverage results to the terminal"
|
14
15
|
option ["--gutter-json", "-g"], :flag, "Output coverage results as Gutter JSON format"
|
15
16
|
option ["--cobertura-xml", "-x"], :flag, "Output coverage results as Cobertura XML format"
|
17
|
+
option ["--sonarqube-xml", "-sq"], :flag, "Output coverage results as SonarQube XML format"
|
16
18
|
option ["--llvm-cov", "-r"], :flag, "Output coverage as llvm-cov format"
|
17
19
|
option ["--json"], :flag, "Output coverage results as simple JSON"
|
18
20
|
option ["--html"], :flag, "Output coverage results as static html pages"
|
@@ -90,6 +92,8 @@ class CoverageCommand < Clamp::Command
|
|
90
92
|
project.ci_service = :buildkite
|
91
93
|
elsif teamcity?
|
92
94
|
project.ci_service = :teamcity
|
95
|
+
elsif github?
|
96
|
+
project.ci_service = :github
|
93
97
|
end
|
94
98
|
end
|
95
99
|
|
@@ -124,6 +128,8 @@ class CoverageCommand < Clamp::Command
|
|
124
128
|
project.show_html = show?
|
125
129
|
elsif json?
|
126
130
|
project.coverage_service = :json
|
131
|
+
elsif sonarqube_xml?
|
132
|
+
project.coverage_service = :sonarqube_xml
|
127
133
|
end
|
128
134
|
end
|
129
135
|
|
@@ -43,18 +43,23 @@ module Slather
|
|
43
43
|
|
44
44
|
def gcov_data
|
45
45
|
@gcov_data ||= begin
|
46
|
-
|
47
|
-
|
48
|
-
|
46
|
+
gcov_data = ""
|
47
|
+
|
48
|
+
Dir.chdir(project.project_dir) do
|
49
|
+
gcov_output = `gcov "#{source_file_pathname}" --object-directory "#{gcno_file_pathname.parent}" --branch-probabilities --branch-counts`
|
50
|
+
# Sometimes gcov makes gcov files for Cocoa Touch classes, like NSRange. Ignore and delete later.
|
51
|
+
gcov_files_created = gcov_output.scan(/creating '(.+\..+\.gcov)'/)
|
52
|
+
|
53
|
+
gcov_file_name = "./#{source_file_pathname.basename}.gcov"
|
54
|
+
if File.exists?(gcov_file_name)
|
55
|
+
gcov_data = File.new(gcov_file_name).read
|
56
|
+
else
|
57
|
+
gcov_data = ""
|
58
|
+
end
|
49
59
|
|
50
|
-
|
51
|
-
if File.exists?(gcov_file_name)
|
52
|
-
gcov_data = File.new(gcov_file_name).read
|
53
|
-
else
|
54
|
-
gcov_data = ""
|
60
|
+
gcov_files_created.each { |file| FileUtils.rm_f(file) }
|
55
61
|
end
|
56
62
|
|
57
|
-
gcov_files_created.each { |file| FileUtils.rm_f(file) }
|
58
63
|
gcov_data
|
59
64
|
end
|
60
65
|
end
|
@@ -36,6 +36,21 @@ module Slather
|
|
36
36
|
end
|
37
37
|
private :jenkins_job_id
|
38
38
|
|
39
|
+
def github_job_id
|
40
|
+
ENV['GITHUB_RUN_ID']
|
41
|
+
end
|
42
|
+
private :github_job_id
|
43
|
+
|
44
|
+
def github_pull_request
|
45
|
+
ENV['CI_PULL_REQUEST'] || ""
|
46
|
+
end
|
47
|
+
private :github_pull_request
|
48
|
+
|
49
|
+
def github_repo_name
|
50
|
+
ENV['GITHUB_REPOSITORY'] || ""
|
51
|
+
end
|
52
|
+
private :github_repo_name
|
53
|
+
|
39
54
|
def jenkins_branch_name
|
40
55
|
branch_name = ENV['GIT_BRANCH'] || ENV['BRANCH_NAME']
|
41
56
|
if branch_name.include? 'origin/'
|
@@ -51,6 +66,11 @@ module Slather
|
|
51
66
|
end
|
52
67
|
private :teamcity_branch_name
|
53
68
|
|
69
|
+
def github_branch_name
|
70
|
+
ENV['GIT_BRANCH'] || `git ls-remote --heads origin | grep $(git rev-parse HEAD) | cut -d / -f 3-`.chomp
|
71
|
+
end
|
72
|
+
private :github_branch_name
|
73
|
+
|
54
74
|
def buildkite_job_id
|
55
75
|
ENV['BUILDKITE_BUILD_NUMBER']
|
56
76
|
end
|
@@ -119,6 +139,23 @@ module Slather
|
|
119
139
|
"https://buildkite.com/" + ENV['BUILDKITE_PROJECT_SLUG'] + "/builds/" + ENV['BUILDKITE_BUILD_NUMBER'] + "#"
|
120
140
|
end
|
121
141
|
|
142
|
+
def github_git_info
|
143
|
+
{
|
144
|
+
:head => {
|
145
|
+
:id => ENV['GITHUB_SHA'],
|
146
|
+
:author_name => ENV['GITHUB_ACTOR'],
|
147
|
+
:message => (`git log --format=%s -n 1 HEAD`.chomp || "")
|
148
|
+
},
|
149
|
+
:branch => github_branch_name
|
150
|
+
}
|
151
|
+
end
|
152
|
+
private :github_git_info
|
153
|
+
|
154
|
+
def github_build_url
|
155
|
+
"https://github.com/" + ENV['GITHUB_REPOSITORY'] + "/actions/runs/" + ENV['GITHUB_RUN_ID']
|
156
|
+
end
|
157
|
+
private :github_build_url
|
158
|
+
|
122
159
|
def coveralls_coverage_data
|
123
160
|
if ci_service == :travis_ci || ci_service == :travis_pro
|
124
161
|
if travis_job_id
|
@@ -206,6 +243,26 @@ module Slather
|
|
206
243
|
else
|
207
244
|
raise StandardError, "Environment variable `TC_BUILD_NUMBER` not set. Is this running on a teamcity build?"
|
208
245
|
end
|
246
|
+
elsif ci_service == :github
|
247
|
+
|
248
|
+
if coverage_access_token.to_s.strip.length == 0
|
249
|
+
raise StandardError, "Access token is not set. Uploading coverage data for private repositories requires an access token."
|
250
|
+
end
|
251
|
+
|
252
|
+
if github_job_id
|
253
|
+
{
|
254
|
+
:service_job_id => github_job_id,
|
255
|
+
:service_name => "github",
|
256
|
+
:repo_token => coverage_access_token,
|
257
|
+
:repo_name => github_repo_name,
|
258
|
+
:source_files => coverage_files.map(&:as_json),
|
259
|
+
:service_build_url => github_build_url,
|
260
|
+
:service_pull_request => github_pull_request,
|
261
|
+
:git => github_git_info
|
262
|
+
}.to_json
|
263
|
+
else
|
264
|
+
raise StandardError, "Environment variable `GITHUB_RUN_ID` not set. Is this running on github build?"
|
265
|
+
end
|
209
266
|
else
|
210
267
|
raise StandardError, "No support for ci named #{ci_service}"
|
211
268
|
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
require 'date'
|
3
|
+
|
4
|
+
module Slather
|
5
|
+
module CoverageService
|
6
|
+
module SonarqubeXmlOutput
|
7
|
+
|
8
|
+
def coverage_file_class
|
9
|
+
if input_format == "profdata"
|
10
|
+
Slather::ProfdataCoverageFile
|
11
|
+
else
|
12
|
+
Slather::CoverageFile
|
13
|
+
end
|
14
|
+
end
|
15
|
+
private :coverage_file_class
|
16
|
+
|
17
|
+
def post
|
18
|
+
cobertura_xml_report = create_xml_report(coverage_files)
|
19
|
+
store_report(cobertura_xml_report)
|
20
|
+
end
|
21
|
+
|
22
|
+
def store_report(report)
|
23
|
+
output_file = 'sonarqube-generic-coverage.xml'
|
24
|
+
if output_directory
|
25
|
+
FileUtils.mkdir_p(output_directory)
|
26
|
+
output_file = File.join(output_directory, output_file)
|
27
|
+
end
|
28
|
+
File.write(output_file, report.to_s)
|
29
|
+
end
|
30
|
+
|
31
|
+
def create_xml_report(coverage_files)
|
32
|
+
create_empty_xml_report
|
33
|
+
coverage_node = @doc.root
|
34
|
+
coverage_node['version'] = "1"
|
35
|
+
|
36
|
+
coverage_files.each do |coverage_file|
|
37
|
+
file_node = Nokogiri::XML::Node.new "file", @doc
|
38
|
+
file_node.parent = coverage_node
|
39
|
+
file_node['path'] = coverage_file.source_file_pathname_relative_to_repo_root.to_s
|
40
|
+
coverage_file.all_lines.each do |line|
|
41
|
+
if coverage_file.coverage_for_line(line)
|
42
|
+
line_node = Nokogiri::XML::Node.new "lineToCover", @doc
|
43
|
+
line_node['lineNumber'] = coverage_file.line_number_in_line(line)
|
44
|
+
line_node['covered'] = coverage_file.coverage_for_line(line) == 0 ? "false" : "true"
|
45
|
+
line_node.parent = file_node
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
@doc.to_xml
|
50
|
+
end
|
51
|
+
|
52
|
+
def create_empty_xml_report
|
53
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
54
|
+
xml.coverage
|
55
|
+
end
|
56
|
+
@doc = builder.doc
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -18,7 +18,6 @@ module Slather
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def create_line_data
|
21
|
-
all_lines = source_code_lines
|
22
21
|
line_data = Hash.new
|
23
22
|
all_lines.each { |line| line_data[line_number_in_line(line, self.line_numbers_first)] = line }
|
24
23
|
self.line_data = line_data
|
@@ -26,14 +25,20 @@ module Slather
|
|
26
25
|
private :create_line_data
|
27
26
|
|
28
27
|
def path_on_first_line?
|
29
|
-
|
30
|
-
!path.lstrip.start_with?("1|")
|
28
|
+
!source.lstrip.start_with?("1|")
|
31
29
|
end
|
32
30
|
|
33
31
|
def source_file_pathname
|
34
32
|
@source_file_pathname ||= begin
|
35
33
|
if path_on_first_line?
|
36
|
-
|
34
|
+
end_index = self.source.index(/:?\n/)
|
35
|
+
if end_index != nil
|
36
|
+
end_index -= 1
|
37
|
+
path = self.source[0..end_index]
|
38
|
+
else
|
39
|
+
# Empty file, output just contains path
|
40
|
+
path = self.source.sub ":", ""
|
41
|
+
end
|
37
42
|
path &&= Pathname(path)
|
38
43
|
else
|
39
44
|
# llvm-cov was run with just one matching source file
|
@@ -64,7 +69,16 @@ module Slather
|
|
64
69
|
end
|
65
70
|
|
66
71
|
def source_code_lines
|
67
|
-
self.source.split("\n")[(path_on_first_line? ? 1 : 0)..-1]
|
72
|
+
lines = self.source.split("\n")[(path_on_first_line? ? 1 : 0)..-1]
|
73
|
+
ignore_error_lines(lines)
|
74
|
+
end
|
75
|
+
|
76
|
+
def ignore_error_lines(lines, line_numbers_first = self.line_numbers_first)
|
77
|
+
if line_numbers_first
|
78
|
+
lines.reject { |line| line.lstrip.start_with?('|', '--') }
|
79
|
+
else
|
80
|
+
lines
|
81
|
+
end
|
68
82
|
end
|
69
83
|
|
70
84
|
def source_data
|
@@ -72,10 +86,7 @@ module Slather
|
|
72
86
|
end
|
73
87
|
|
74
88
|
def all_lines
|
75
|
-
|
76
|
-
@all_lines = source_code_lines
|
77
|
-
end
|
78
|
-
@all_lines
|
89
|
+
@all_lines ||= source_code_lines
|
79
90
|
end
|
80
91
|
|
81
92
|
def raw_source
|
@@ -94,6 +105,9 @@ module Slather
|
|
94
105
|
|
95
106
|
def line_number_in_line(line, line_numbers_first = self.line_numbers_first)
|
96
107
|
if line_numbers_first
|
108
|
+
# Skip regex if the number is the first thing in the line
|
109
|
+
fastpath_number = line.to_i
|
110
|
+
return fastpath_number if fastpath_number != 0
|
97
111
|
line =~ /^(\s*)(\d*)/
|
98
112
|
group = $2
|
99
113
|
else
|
@@ -123,7 +137,7 @@ module Slather
|
|
123
137
|
end
|
124
138
|
|
125
139
|
def line_coverage_data
|
126
|
-
|
140
|
+
all_lines.map do |line|
|
127
141
|
coverage_for_line(line, self.line_numbers_first)
|
128
142
|
end
|
129
143
|
end
|