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.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/.travis.yml +2 -0
  4. data/CHANGELOG.md +70 -0
  5. data/assets/slather.css +2 -1
  6. data/lib/slather/command/coverage_command.rb +1 -1
  7. data/lib/slather/coverage_file.rb +1 -1
  8. data/lib/slather/coverage_service/coveralls.rb +73 -6
  9. data/lib/slather/coverage_service/html_output.rb +51 -2
  10. data/lib/slather/profdata_coverage_file.rb +42 -2
  11. data/lib/slather/project.rb +38 -6
  12. data/lib/slather/version.rb +1 -1
  13. data/slather.gemspec +2 -2
  14. data/spec/fixtures/FixtureFramework/FixtureFramework.h +19 -0
  15. data/spec/fixtures/FixtureFramework/FlashExperiment.swift +7 -0
  16. data/spec/fixtures/FixtureFramework/Info.plist +24 -0
  17. data/spec/fixtures/FixtureFrameworkTests/FixtureFrameworkTests.swift +34 -0
  18. data/spec/fixtures/FixtureFrameworkTests/FlashExperimentTests.swift +9 -0
  19. data/spec/fixtures/FixtureFrameworkTests/Info.plist +22 -0
  20. data/spec/fixtures/cobertura.xml +157 -37
  21. data/spec/fixtures/fixtures.xcodeproj/project.pbxproj +222 -0
  22. data/spec/fixtures/fixtures.xcodeproj/xcshareddata/xcschemes/fixtures.xcscheme +21 -5
  23. data/spec/fixtures/fixtures.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  24. data/spec/slather/coverage_service/cobertura_xml_spec.rb +1 -1
  25. data/spec/slather/coverage_service/html_output_spec.rb +2 -2
  26. data/spec/slather/coverage_service/json_spec.rb +1 -1
  27. data/spec/slather/coverage_service/llvm_cov_spec.rb +1 -1
  28. data/spec/slather/coverage_service/sonarqube_xml_spec.rb +1 -1
  29. data/spec/slather/profdata_coverage_spec.rb +16 -0
  30. data/spec/slather/project_spec.rb +10 -6
  31. metadata +23 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: eeaf99ed5212bdc90e8098dfde877452af7f074ecfd41fc577d46f90b1ee65eb
4
- data.tar.gz: 5dbd0db5223f26990c2bfd3ab02f4ed36a15d8088a0b5c393f522a785a0176cd
3
+ metadata.gz: b43954fd6f31e519f192e32ad881197d35830912e22158ca17cf333b86f97e0d
4
+ data.tar.gz: 97cf7f9b6eb50d2472aec42c3f698c3a299c38cfd8757cdb2a6e2dcad0933bc6
5
5
  SHA512:
6
- metadata.gz: 6802b4ef28cfb856bf68d214416d6a455de715723327dc7167600bc9cae705dacbdbffb0b8d154b2027dde6d5b2f6baec719f69a81e5293a437da1e260461e73
7
- data.tar.gz: c6b95c762eaa1d5260da5de5910e7f429a79e45556a3b32568ba8c7c9f3ed173ed514330793c39babc767e4f021dda7fc83971750d16407d0149050286495b95
6
+ metadata.gz: f38c40172a16f8375b56cdc873e92339f1f275cef7848bc0d9947714073dd5ff93ba11dc1957781c69316eec59a213c079067b99cb48c134aeb24c20ae2cb945
7
+ data.tar.gz: 1a098f9ab86b11d5c65cea0c309a99a709de76dfdc0f1171f0e100784522a74613cdb3fb06346f4fb1d6e27825250f01ece561ac07bea6e59a489837c7c0c654
data/.gitignore CHANGED
@@ -20,7 +20,7 @@ tmp
20
20
  *.o
21
21
  *.a
22
22
  mkmf.log
23
-
23
+ .vendor
24
24
  # Xcode
25
25
  #
26
26
  *.pbxuser
data/.travis.yml CHANGED
@@ -2,6 +2,8 @@ language: objective-c
2
2
  script: bundle exec rake
3
3
  osx_image: xcode12.2
4
4
 
5
+ cache: bundler
6
+
5
7
  before_install:
6
8
  - curl http://curl.haxx.se/ca/cacert.pem -o /usr/local/share/cacert.pem
7
9
  - gem install bundler -v "~> 2.0" --no-document
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 the which the coverage will be run", :multivalued => true
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.exists?(gcov_file_name)
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
- raise StandardError, "No support for ci named #{ci_service}"
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 { cov.code(line, :class => "objc") }
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
 
@@ -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
- Pathname(file["filename"]).realpath
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, pathnames)
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.exists?(self.build_directory)
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
- `xcrun llvm-cov #{llvm_cov_args.shelljoin} #{source_files.shelljoin}`
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"] || :travis_ci)
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.exists? xcscheme_path
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|
@@ -1,3 +1,3 @@
1
1
  module Slather
2
- VERSION = '2.6.0' unless defined?(Slather::VERSION)
2
+ VERSION = '2.7.4' unless defined?(Slather::VERSION)
3
3
  end
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.7'
32
- spec.add_dependency 'nokogiri', '~> 1.8'
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,7 @@
1
+ import Foundation
2
+
3
+ public struct FlashExperiment {
4
+ public let isAwesome = true
5
+
6
+ public init() {}
7
+ }
@@ -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
+ }
@@ -0,0 +1,9 @@
1
+ import XCTest
2
+ import FixtureFramework
3
+
4
+ class FlashExperimentTests: XCTestCase {
5
+ func testExample() throws {
6
+ let sut = FlashExperiment()
7
+ XCTAssertTrue(sut.isAwesome, "Your flash experiment isn't awesome!")
8
+ }
9
+ }