ashtonw-slather 1.8.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +45 -0
- data/.travis.yml +19 -0
- data/CHANGELOG.md +132 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +206 -0
- data/Rakefile +1 -0
- data/assets/highlight.pack.js +1 -0
- data/assets/list.min.js +1 -0
- data/assets/slather.css +316 -0
- data/bin/slather +117 -0
- data/docs/logo.jpg +0 -0
- data/lib/cocoapods_plugin.rb +10 -0
- data/lib/slather.rb +20 -0
- data/lib/slather/coverage_file.rb +195 -0
- data/lib/slather/coverage_service/cobertura_xml_output.rb +183 -0
- data/lib/slather/coverage_service/coveralls.rb +186 -0
- data/lib/slather/coverage_service/gutter_json_output.rb +50 -0
- data/lib/slather/coverage_service/hardcover.rb +61 -0
- data/lib/slather/coverage_service/html_output.rb +244 -0
- data/lib/slather/coverage_service/simple_output.rb +31 -0
- data/lib/slather/coveralls_coverage_file.rb +13 -0
- data/lib/slather/project.rb +132 -0
- data/lib/slather/version.rb +3 -0
- data/slather.gemspec +32 -0
- data/spec/fixtures/fixtures.xcodeproj/project.pbxproj +496 -0
- data/spec/fixtures/fixtures.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
- data/spec/fixtures/fixtures.xcodeproj/xcshareddata/xcschemes/fixtures.xcscheme +69 -0
- data/spec/fixtures/fixtures/Supporting Files/fixtures-Prefix.pch +9 -0
- data/spec/fixtures/fixtures/fixtures.h +16 -0
- data/spec/fixtures/fixtures/fixtures.m +23 -0
- data/spec/fixtures/fixtures/fixtures_cpp.cpp +9 -0
- data/spec/fixtures/fixtures/fixtures_cpp.h +6 -0
- data/spec/fixtures/fixtures/fixtures_m.h +5 -0
- data/spec/fixtures/fixtures/fixtures_m.m +5 -0
- data/spec/fixtures/fixtures/fixtures_mm.h +5 -0
- data/spec/fixtures/fixtures/fixtures_mm.mm +5 -0
- data/spec/fixtures/fixtures/more_files/Branches.h +15 -0
- data/spec/fixtures/fixtures/more_files/Branches.m +45 -0
- data/spec/fixtures/fixtures/more_files/Empty.h +13 -0
- data/spec/fixtures/fixtures/more_files/Empty.m +13 -0
- data/spec/fixtures/fixtures/more_files/peekaview.h +13 -0
- data/spec/fixtures/fixtures/more_files/peekaview.m +31 -0
- data/spec/fixtures/fixturesTests/BranchesTests.m +38 -0
- data/spec/fixtures/fixturesTests/Supporting Files/en.lproj/InfoPlist.strings +2 -0
- data/spec/fixtures/fixturesTests/Supporting Files/fixturesTests-Info.plist +22 -0
- data/spec/fixtures/fixturesTests/fixturesTests.m +36 -0
- data/spec/fixtures/fixturesTests/peekaviewTests.m +34 -0
- data/spec/fixtures/fixtures_html/Branches.m.html +261 -0
- data/spec/fixtures/fixtures_html/BranchesTests.m.html +228 -0
- data/spec/fixtures/fixtures_html/Empty.m.html +30 -0
- data/spec/fixtures/fixtures_html/fixtures.m.html +151 -0
- data/spec/fixtures/fixtures_html/fixturesTests.m.html +216 -0
- data/spec/fixtures/fixtures_html/fixtures_cpp.cpp.html +30 -0
- data/spec/fixtures/fixtures_html/fixtures_m.m.html +30 -0
- data/spec/fixtures/fixtures_html/fixtures_mm.mm.html +30 -0
- data/spec/fixtures/fixtures_html/index.html +134 -0
- data/spec/fixtures/fixtures_html/peekaview.m.html +190 -0
- data/spec/fixtures/fixtures_html/peekaviewTests.m.html +206 -0
- data/spec/fixtures/gutter.json +1 -0
- data/spec/slather/cocoapods_plugin_spec.rb +21 -0
- data/spec/slather/coverage_file_spec.rb +337 -0
- data/spec/slather/coverage_service/cobertura_xml_spec.rb +49 -0
- data/spec/slather/coverage_service/coveralls_spec.rb +122 -0
- data/spec/slather/coverage_service/gutter_json_spec.rb +28 -0
- data/spec/slather/coverage_service/hardcover_spec.rb +87 -0
- data/spec/slather/coverage_service/html_output_spec.rb +179 -0
- data/spec/slather/coverage_service/simple_output_spec.rb +35 -0
- data/spec/slather/fixtures.gcno +0 -0
- data/spec/slather/project_spec.rb +288 -0
- data/spec/spec_helper.rb +27 -0
- metadata +319 -0
@@ -0,0 +1,50 @@
|
|
1
|
+
module Slather
|
2
|
+
module CoverageService
|
3
|
+
module GutterJsonOutput
|
4
|
+
|
5
|
+
def coverage_file_class
|
6
|
+
Slather::CoverageFile
|
7
|
+
end
|
8
|
+
private :coverage_file_class
|
9
|
+
|
10
|
+
def post
|
11
|
+
output = { 'meta' => { 'timestamp' => DateTime.now.strftime('%Y-%m-%d %H:%M:%S.%6N') } }
|
12
|
+
symbols = {}
|
13
|
+
|
14
|
+
coverage_files.each do |coverage_file|
|
15
|
+
next unless coverage_file.gcov_data
|
16
|
+
|
17
|
+
filename = coverage_file.source_file_pathname.to_s
|
18
|
+
filename = filename.sub(Pathname.pwd.to_s, '').reverse.chomp("/").reverse
|
19
|
+
|
20
|
+
coverage_file.cleaned_gcov_data.split("\n").each do |line|
|
21
|
+
data = line.split(':')
|
22
|
+
|
23
|
+
line_number = data[1].to_i
|
24
|
+
next unless line_number > 0
|
25
|
+
|
26
|
+
coverage = data[0].strip
|
27
|
+
|
28
|
+
symbol = { 'line' => line_number,
|
29
|
+
'long_text' => '',
|
30
|
+
'short_text' => coverage }
|
31
|
+
|
32
|
+
if coverage != '-'
|
33
|
+
symbol['background_color'] = coverage.to_i > 0 ? '0x35CC4B' : '0xFC635E'
|
34
|
+
end
|
35
|
+
|
36
|
+
if symbols.has_key?(filename)
|
37
|
+
symbols[filename] << symbol
|
38
|
+
else
|
39
|
+
symbols[filename] = [ symbol ]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
output['symbols_by_file'] = symbols
|
45
|
+
File.open('.gutter.json', 'w') { |file| file.write(output.to_json) }
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Slather
|
2
|
+
module CoverageService
|
3
|
+
module Hardcover
|
4
|
+
|
5
|
+
def coverage_file_class
|
6
|
+
Slather::CoverallsCoverageFile
|
7
|
+
end
|
8
|
+
private :coverage_file_class
|
9
|
+
|
10
|
+
def jenkins_job_id
|
11
|
+
"#{ENV['JOB_NAME']}/#{ENV['BUILD_NUMBER']}"
|
12
|
+
end
|
13
|
+
private :jenkins_job_id
|
14
|
+
|
15
|
+
def hardcover_coverage_data
|
16
|
+
if ci_service == :jenkins_ci
|
17
|
+
if jenkins_job_id
|
18
|
+
{
|
19
|
+
:service_job_id => jenkins_job_id,
|
20
|
+
:service_name => "jenkins-ci",
|
21
|
+
:repo_token => Project.yml["hardcover_repo_token"],
|
22
|
+
:source_files => coverage_files.map(&:as_json)
|
23
|
+
}.to_json
|
24
|
+
else
|
25
|
+
raise StandardError, "Environment variables `BUILD_NUMBER` and `JOB_NAME` are not set. Is this running on a Jenkins build?"
|
26
|
+
end
|
27
|
+
else
|
28
|
+
raise StandardError, "No support for ci named #{ci_service}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
private :hardcover_coverage_data
|
32
|
+
|
33
|
+
def post
|
34
|
+
f = File.open('hardcover_json_file', 'w+')
|
35
|
+
begin
|
36
|
+
f.write(hardcover_coverage_data)
|
37
|
+
f.close
|
38
|
+
`curl --form json_file=@#{f.path} #{hardcover_api_jobs_path}`
|
39
|
+
rescue StandardError => e
|
40
|
+
FileUtils.rm(f)
|
41
|
+
raise e
|
42
|
+
end
|
43
|
+
FileUtils.rm(f)
|
44
|
+
end
|
45
|
+
|
46
|
+
def hardcover_api_jobs_path
|
47
|
+
"#{hardcover_base_url}/v1/jobs"
|
48
|
+
end
|
49
|
+
private :hardcover_api_jobs_path
|
50
|
+
|
51
|
+
def hardcover_base_url
|
52
|
+
url = Project.yml["hardcover_base_url"]
|
53
|
+
unless url
|
54
|
+
raise "No `hardcover_base_url` configured. Please add it to your `.slather.yml`"
|
55
|
+
end
|
56
|
+
url
|
57
|
+
end
|
58
|
+
private :hardcover_base_url
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,244 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
|
3
|
+
module Slather
|
4
|
+
module CoverageService
|
5
|
+
module HtmlOutput
|
6
|
+
|
7
|
+
def coverage_file_class
|
8
|
+
Slather::CoverageFile
|
9
|
+
end
|
10
|
+
private :coverage_file_class
|
11
|
+
|
12
|
+
def directory_path
|
13
|
+
is_path_valid = !output_directory.nil? && !output_directory.strip.eql?("")
|
14
|
+
is_path_valid ? File.expand_path(output_directory) : "html"
|
15
|
+
end
|
16
|
+
private :directory_path
|
17
|
+
|
18
|
+
def post
|
19
|
+
create_html_reports(coverage_files)
|
20
|
+
generate_reports(@docs)
|
21
|
+
|
22
|
+
index_html_path = File.join(directory_path, "index.html")
|
23
|
+
if show_html
|
24
|
+
open_coverage index_html_path
|
25
|
+
else
|
26
|
+
print_path_coverage index_html_path
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def print_path_coverage(index_html)
|
31
|
+
path = File.expand_path index_html
|
32
|
+
puts "\nTo open the html reports, use \n\nopen '#{path}'\n\nor use '--show' flag to open it automatically.\n\n"
|
33
|
+
end
|
34
|
+
|
35
|
+
def open_coverage(index_html)
|
36
|
+
path = File.expand_path index_html
|
37
|
+
`open '#{path}'` if File.exist?(path)
|
38
|
+
end
|
39
|
+
|
40
|
+
def create_html_reports(coverage_files)
|
41
|
+
create_index_html(coverage_files)
|
42
|
+
create_htmls_from_files(coverage_files)
|
43
|
+
end
|
44
|
+
|
45
|
+
def generate_reports(reports)
|
46
|
+
FileUtils.rm_rf(directory_path) if Dir.exist?(directory_path)
|
47
|
+
FileUtils.mkdir_p(directory_path)
|
48
|
+
|
49
|
+
reports.each do |name, doc|
|
50
|
+
html_file = File.join(directory_path, "#{name}.html")
|
51
|
+
File.write(html_file, doc.to_html)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def create_index_html(coverage_files)
|
56
|
+
project_name = File.basename(self.xcodeproj)
|
57
|
+
template = generate_html_template(project_name, true, false)
|
58
|
+
|
59
|
+
total_relevant_lines = 0
|
60
|
+
total_tested_lines = 0
|
61
|
+
coverage_files.each { |coverage_file|
|
62
|
+
total_tested_lines += coverage_file.num_lines_tested
|
63
|
+
total_relevant_lines += coverage_file.num_lines_testable
|
64
|
+
}
|
65
|
+
|
66
|
+
builder = Nokogiri::HTML::Builder.with(template.at('#reports')) { |cov|
|
67
|
+
cov.h2 "Files for \"#{project_name}\""
|
68
|
+
|
69
|
+
cov.h4 {
|
70
|
+
percentage = (total_tested_lines / total_relevant_lines.to_f) * 100.0
|
71
|
+
cov.span "Total Coverage : "
|
72
|
+
cov.span '%.2f%%' % percentage, :class => class_for_coverage_percentage(percentage), :id => "total_coverage"
|
73
|
+
}
|
74
|
+
|
75
|
+
cov.input(:class => "search", :placeholder => "Search")
|
76
|
+
|
77
|
+
cov.table(:class => "coverage_list", :cellspacing => 0, :cellpadding => 0) {
|
78
|
+
|
79
|
+
cov.thead {
|
80
|
+
cov.tr {
|
81
|
+
cov.th "%", :class => "col_num sort", "data-sort" => "data_percentage"
|
82
|
+
cov.th "File", :class => "sort", "data-sort" => "data_filename"
|
83
|
+
cov.th "Lines", :class => "col_percent sort", "data-sort" => "data_lines"
|
84
|
+
cov.th "Relevant", :class => "col_percent sort", "data-sort" => "data_relevant"
|
85
|
+
cov.th "Covered", :class => "col_percent sort", "data-sort" => "data_covered"
|
86
|
+
cov.th "Missed", :class => "col_percent sort", "data-sort" => "data_missed"
|
87
|
+
}
|
88
|
+
}
|
89
|
+
|
90
|
+
cov.tbody(:class => "list") {
|
91
|
+
coverage_files.each { |coverage_file|
|
92
|
+
filename = File.basename(coverage_file.source_file_pathname_relative_to_repo_root)
|
93
|
+
filename_link = "#{filename}.html"
|
94
|
+
|
95
|
+
cov.tr {
|
96
|
+
percentage = coverage_file.percentage_lines_tested
|
97
|
+
|
98
|
+
cov.td { cov.span '%.2f' % percentage, :class => "percentage #{class_for_coverage_percentage(percentage)} data_percentage" }
|
99
|
+
cov.td(:class => "data_filename") {
|
100
|
+
cov.a filename, :href => filename_link
|
101
|
+
}
|
102
|
+
cov.td "#{coverage_file.line_coverage_data.count}", :class => "data_lines"
|
103
|
+
cov.td "#{coverage_file.num_lines_testable}", :class => "data_relevant"
|
104
|
+
cov.td "#{coverage_file.num_lines_tested}", :class => "data_covered"
|
105
|
+
cov.td "#{(coverage_file.num_lines_testable - coverage_file.num_lines_tested)}", :class => "data_missed"
|
106
|
+
}
|
107
|
+
}
|
108
|
+
}
|
109
|
+
}
|
110
|
+
}
|
111
|
+
|
112
|
+
@docs = Hash.new
|
113
|
+
@docs[:index] = builder.doc
|
114
|
+
end
|
115
|
+
|
116
|
+
def create_htmls_from_files(coverage_files)
|
117
|
+
coverage_files.map { |file| create_html_from_file file }
|
118
|
+
end
|
119
|
+
|
120
|
+
def create_html_from_file(coverage_file)
|
121
|
+
filepath = coverage_file.source_file_pathname_relative_to_repo_root
|
122
|
+
filename = File.basename(filepath)
|
123
|
+
percentage = coverage_file.percentage_lines_tested
|
124
|
+
|
125
|
+
cleaned_gcov_lines = coverage_file.cleaned_gcov_data.split("\n")
|
126
|
+
is_file_empty = (cleaned_gcov_lines.count <= 0)
|
127
|
+
|
128
|
+
template = generate_html_template(filename, false, is_file_empty)
|
129
|
+
|
130
|
+
builder = Nokogiri::HTML::Builder.with(template.at('#reports')) { |cov|
|
131
|
+
cov.h2(:class => "cov_title") {
|
132
|
+
cov.span("Coverage for \"#{filename}\"" + (!is_file_empty ? " : " : ""))
|
133
|
+
cov.span("#{'%.2f' % percentage}%", :class => class_for_coverage_percentage(percentage)) unless is_file_empty
|
134
|
+
}
|
135
|
+
|
136
|
+
cov.h4("(#{coverage_file.num_lines_tested} of #{coverage_file.num_lines_testable} relevant lines covered)", :class => "cov_subtitle")
|
137
|
+
cov.h4(filepath, :class => "cov_filepath")
|
138
|
+
|
139
|
+
if is_file_empty
|
140
|
+
cov.p "¯\\_(ツ)_/¯"
|
141
|
+
next
|
142
|
+
end
|
143
|
+
|
144
|
+
cov.table(:class => "source_code") {
|
145
|
+
cleaned_gcov_lines.each do |line|
|
146
|
+
data = line.split(':', 3)
|
147
|
+
|
148
|
+
line_number = data[1].to_i
|
149
|
+
next unless line_number > 0
|
150
|
+
|
151
|
+
coverage_data = data[0].strip
|
152
|
+
line_data = [line_number, data[2], hits_for_coverage_data(coverage_data)]
|
153
|
+
classes = ["num", "src", "coverage"]
|
154
|
+
|
155
|
+
cov.tr(:class => class_for_coverage_data(coverage_data)) {
|
156
|
+
line_data.each_with_index { |line, idx|
|
157
|
+
if idx != 1
|
158
|
+
cov.td(line, :class => classes[idx])
|
159
|
+
else
|
160
|
+
cov.td(:class => classes[idx]) {
|
161
|
+
cov.pre { cov.code(line, :class => "objc") }
|
162
|
+
}
|
163
|
+
end
|
164
|
+
}
|
165
|
+
}
|
166
|
+
end
|
167
|
+
}
|
168
|
+
}
|
169
|
+
|
170
|
+
@docs[filename] = builder.doc
|
171
|
+
end
|
172
|
+
|
173
|
+
def generate_html_template(title, is_index, is_file_empty)
|
174
|
+
logo_path = File.join(gem_root_path, "docs/logo.jpg")
|
175
|
+
css_path = File.join(gem_root_path, "assets/slather.css")
|
176
|
+
highlight_js_path = File.join(gem_root_path, "assets/highlight.pack.js")
|
177
|
+
list_js_path = File.join(gem_root_path, "assets/list.min.js")
|
178
|
+
|
179
|
+
builder = Nokogiri::HTML::Builder.new do |doc|
|
180
|
+
doc.html {
|
181
|
+
doc.head {
|
182
|
+
doc.title "#{title} - Slather"
|
183
|
+
doc.link :href => css_path, :media => "all", :rel => "stylesheet"
|
184
|
+
}
|
185
|
+
doc.body {
|
186
|
+
doc.header {
|
187
|
+
doc.div(:class => "row") {
|
188
|
+
doc.a(:href => "index.html") { doc.img(:src => logo_path, :alt => "Slather logo") }
|
189
|
+
}
|
190
|
+
}
|
191
|
+
doc.div(:class => "row") { doc.div(:id => "reports") }
|
192
|
+
doc.footer {
|
193
|
+
doc.div(:class => "row") {
|
194
|
+
doc.p { doc.a("Fork me on Github", :href => "https://github.com/venmo/slather") }
|
195
|
+
doc.p("© #{Date.today.year} Slather")
|
196
|
+
}
|
197
|
+
}
|
198
|
+
|
199
|
+
if is_index
|
200
|
+
doc.script :src => list_js_path
|
201
|
+
doc.script "var reports = new List('reports', { valueNames: [ 'data_percentage', 'data_filename', 'data_lines', 'data_relevant', 'data_covered', 'data_missed' ]});"
|
202
|
+
else
|
203
|
+
unless is_file_empty
|
204
|
+
doc.script :src => highlight_js_path
|
205
|
+
doc.script "hljs.initHighlightingOnLoad();"
|
206
|
+
end
|
207
|
+
end
|
208
|
+
}
|
209
|
+
}
|
210
|
+
end
|
211
|
+
builder.doc
|
212
|
+
end
|
213
|
+
|
214
|
+
def gem_root_path
|
215
|
+
File.expand_path File.join(File.dirname(__dir__), "../..")
|
216
|
+
end
|
217
|
+
|
218
|
+
def class_for_coverage_data(coverage_data)
|
219
|
+
case coverage_data
|
220
|
+
when /\d/ then "covered"
|
221
|
+
when /#/ then "missed"
|
222
|
+
else "never"
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def hits_for_coverage_data(coverage_data)
|
227
|
+
case coverage_data
|
228
|
+
when /\d/ then (coverage_data.to_i > 0) ? "#{coverage_data}x" : ""
|
229
|
+
when /#/ then "!"
|
230
|
+
else ""
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
def class_for_coverage_percentage(percentage)
|
235
|
+
case
|
236
|
+
when percentage > 85 then "cov_high"
|
237
|
+
when percentage > 70 then "cov_medium"
|
238
|
+
else "cov_low"
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Slather
|
2
|
+
module CoverageService
|
3
|
+
module SimpleOutput
|
4
|
+
|
5
|
+
def coverage_file_class
|
6
|
+
Slather::CoverageFile
|
7
|
+
end
|
8
|
+
private :coverage_file_class
|
9
|
+
|
10
|
+
def post
|
11
|
+
total_project_lines = 0
|
12
|
+
total_project_lines_tested = 0
|
13
|
+
coverage_files.each do |coverage_file|
|
14
|
+
# ignore lines that don't count towards coverage (comments, whitespace, etc). These are nil in the array.
|
15
|
+
|
16
|
+
lines_tested = coverage_file.num_lines_tested
|
17
|
+
total_lines = coverage_file.num_lines_testable
|
18
|
+
percentage = '%.2f' % [coverage_file.percentage_lines_tested]
|
19
|
+
|
20
|
+
total_project_lines_tested += lines_tested
|
21
|
+
total_project_lines += total_lines
|
22
|
+
|
23
|
+
puts "#{coverage_file.source_file_pathname_relative_to_repo_root}: #{lines_tested} of #{total_lines} lines (#{percentage}%)"
|
24
|
+
end
|
25
|
+
total_percentage = '%.2f' % [(total_project_lines_tested / total_project_lines.to_f) * 100.0]
|
26
|
+
puts "Test Coverage: #{total_percentage}%"
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'xcodeproj'
|
3
|
+
require 'json'
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
module Xcodeproj
|
7
|
+
class Project
|
8
|
+
|
9
|
+
def slather_setup_for_coverage
|
10
|
+
build_configurations.each do |build_configuration|
|
11
|
+
build_configuration.build_settings["GCC_INSTRUMENT_PROGRAM_FLOW_ARCS"] = "YES"
|
12
|
+
build_configuration.build_settings["GCC_GENERATE_TEST_COVERAGE_FILES"] = "YES"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
module Slather
|
20
|
+
class Project < Xcodeproj::Project
|
21
|
+
|
22
|
+
attr_accessor :build_directory, :ignore_list, :ci_service, :coverage_service, :coverage_access_token, :source_directory, :output_directory, :xcodeproj, :show_html
|
23
|
+
|
24
|
+
alias_method :setup_for_coverage, :slather_setup_for_coverage
|
25
|
+
|
26
|
+
def self.open(xcodeproj)
|
27
|
+
proj = super
|
28
|
+
proj.configure_from_yml
|
29
|
+
proj.xcodeproj = xcodeproj
|
30
|
+
proj
|
31
|
+
end
|
32
|
+
|
33
|
+
def derived_data_dir
|
34
|
+
File.expand_path('~') + "/Library/Developer/Xcode/DerivedData/"
|
35
|
+
end
|
36
|
+
private :derived_data_dir
|
37
|
+
|
38
|
+
def build_directory
|
39
|
+
@build_directory || derived_data_dir
|
40
|
+
end
|
41
|
+
|
42
|
+
def coverage_files
|
43
|
+
coverage_files = Dir["#{build_directory}/**/*.gcno"].map do |file|
|
44
|
+
coverage_file = coverage_file_class.new(self, file)
|
45
|
+
# If there's no source file for this gcno, it probably belongs to another project.
|
46
|
+
coverage_file.source_file_pathname && !coverage_file.ignored? ? coverage_file : nil
|
47
|
+
end.compact
|
48
|
+
|
49
|
+
if coverage_files.empty?
|
50
|
+
raise StandardError, "No coverage files found. Are you sure your project is setup for generating coverage files? Try `slather setup your/project.xcodeproj`"
|
51
|
+
else
|
52
|
+
dedupe(coverage_files)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
private :coverage_files
|
56
|
+
|
57
|
+
def dedupe(coverage_files)
|
58
|
+
coverage_files.group_by(&:source_file_pathname).values.map { |cf_array| cf_array.max_by(&:percentage_lines_tested) }
|
59
|
+
end
|
60
|
+
private :dedupe
|
61
|
+
|
62
|
+
def self.yml_filename
|
63
|
+
'.slather.yml'
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.yml
|
67
|
+
@yml ||= File.exist?(yml_filename) ? YAML.load_file(yml_filename) : {}
|
68
|
+
end
|
69
|
+
|
70
|
+
def configure_from_yml
|
71
|
+
configure_build_directory_from_yml
|
72
|
+
configure_ignore_list_from_yml
|
73
|
+
configure_ci_service_from_yml
|
74
|
+
configure_coverage_access_token_from_yml
|
75
|
+
configure_coverage_service_from_yml
|
76
|
+
configure_source_directory_from_yml
|
77
|
+
configure_output_directory_from_yml
|
78
|
+
end
|
79
|
+
|
80
|
+
def configure_build_directory_from_yml
|
81
|
+
self.build_directory = self.class.yml["build_directory"] if self.class.yml["build_directory"] && !@build_directory
|
82
|
+
end
|
83
|
+
|
84
|
+
def configure_source_directory_from_yml
|
85
|
+
self.source_directory ||= self.class.yml["source_directory"] if self.class.yml["source_directory"]
|
86
|
+
end
|
87
|
+
|
88
|
+
def configure_output_directory_from_yml
|
89
|
+
self.output_directory ||= self.class.yml["output_directory"] if self.class.yml["output_directory"]
|
90
|
+
end
|
91
|
+
|
92
|
+
def configure_ignore_list_from_yml
|
93
|
+
self.ignore_list ||= [(self.class.yml["ignore"] || [])].flatten
|
94
|
+
end
|
95
|
+
|
96
|
+
def configure_ci_service_from_yml
|
97
|
+
self.ci_service ||= (self.class.yml["ci_service"] || :travis_ci)
|
98
|
+
end
|
99
|
+
|
100
|
+
def ci_service=(service)
|
101
|
+
@ci_service = service && service.to_sym
|
102
|
+
end
|
103
|
+
|
104
|
+
def configure_coverage_service_from_yml
|
105
|
+
self.coverage_service ||= (self.class.yml["coverage_service"] || :terminal)
|
106
|
+
end
|
107
|
+
|
108
|
+
def configure_coverage_access_token_from_yml
|
109
|
+
self.coverage_access_token ||= (ENV["COVERAGE_ACCESS_TOKEN"] || self.class.yml["coverage_access_token"] || "")
|
110
|
+
end
|
111
|
+
|
112
|
+
def coverage_service=(service)
|
113
|
+
service = service && service.to_sym
|
114
|
+
if service == :coveralls
|
115
|
+
extend(Slather::CoverageService::Coveralls)
|
116
|
+
elsif service == :hardcover
|
117
|
+
extend(Slather::CoverageService::Hardcover)
|
118
|
+
elsif service == :terminal
|
119
|
+
extend(Slather::CoverageService::SimpleOutput)
|
120
|
+
elsif service == :gutter_json
|
121
|
+
extend(Slather::CoverageService::GutterJsonOutput)
|
122
|
+
elsif service == :cobertura_xml
|
123
|
+
extend(Slather::CoverageService::CoberturaXmlOutput)
|
124
|
+
elsif service == :html
|
125
|
+
extend(Slather::CoverageService::HtmlOutput)
|
126
|
+
else
|
127
|
+
raise ArgumentError, "`#{coverage_service}` is not a valid coverage service. Try `terminal`, `coveralls`, `gutter_json`, `cobertura_xml` or `html`"
|
128
|
+
end
|
129
|
+
@coverage_service = service
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|