slather 2.4.6 → 2.6.0
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 +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
|