slather 1.4.0 → 1.5.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6570b6eb98ca404460677120ce2bff49446d5047
4
- data.tar.gz: a387ce407d1748ba2fcb6cc57261c601f53900fb
3
+ metadata.gz: 46f9e4fda1716e9fef3ef6cf621c7008d1b3f0ed
4
+ data.tar.gz: 4f34b0648dcd51da3480ba7bc138069790096345
5
5
  SHA512:
6
- metadata.gz: bd10828959766f25526d693082585fb5a08295c79e3f1c8e48181abb3c3235e85f043c45a2fdb8cef4e33f3feaf1b39054a8df4ef64d4a78567f85b8c0989e61
7
- data.tar.gz: 3226f015ea7a407d33112f275d231ef3935f8e96d1d9724ab93b577ed2b049d21e2d24765d80188a7826508b62ce403a22248071c54d0d3bb0e087545dc79d4d
6
+ metadata.gz: 6a5ab9bfa9dc93b84d94eccaf2f2d6313ab98efbc46e60d3df0bd10244d77e5c48994f83e4e8430d349a80b9a8219cdfb3942e8ee4fe81ba0cc98be60847dd11
7
+ data.tar.gz: 41d88beeca785be4edd61526e455ed2a6265078536332b1ba1adee3f4e1a813b129f833871a554950c26d6c9b292f775da835f6ee0b121b5c1ec4b86606ac0df
data/README.md CHANGED
@@ -72,6 +72,26 @@ install: bundle install --without=documentation --path ../travis_bundle_dir
72
72
  after_success: slather
73
73
  ```
74
74
 
75
+ ### Cobertura
76
+
77
+ To create a Cobertura XML report set `cobertura_xml` as coverage service inside your `.slather.yml`:
78
+
79
+ ```yml
80
+ # .slather.yml
81
+
82
+ coverage_service: cobertura_xml
83
+ xcodeproj: path/to/project.xcodeproj
84
+ ignore:
85
+ - ExamplePodCode/*
86
+ - ProjectTestsGroup/*
87
+ ```
88
+
89
+ Or use the command line options `--cobertura-xml` or `-x`:
90
+
91
+ ```sh
92
+ $ slather coverage -x
93
+ ```
94
+
75
95
  ### Coverage for code included via CocoaPods
76
96
 
77
97
  If you're trying to compute the coverage of code that has been included via
@@ -86,7 +106,7 @@ source_directory: Pods/AFNetworking
86
106
 
87
107
  ### Custom Build Directory
88
108
 
89
- Slather will look for the test coverage files in `DerivedData` by default. If you send build output to a custom location, like [this](https://github.com/erikdoe/ocmock/blob/master/Tools/travis.sh#L12), then you should also set the `build_directory` property in `.slather.yml`
109
+ Slather will look for the test coverage files in `DerivedData` by default. If you send build output to a custom location, like [this](https://github.com/erikdoe/ocmock/blob/7f4d22b38eedf1bb9a12ab1591ac0a5d436db61a/Tools/travis.sh#L12), then you should also set the `build_directory` property in `.slather.yml`
90
110
 
91
111
  ## Contributing
92
112
 
data/bin/slather CHANGED
@@ -16,6 +16,7 @@ Clamp do
16
16
  option ["--coveralls", "-c"], :flag, "Post coverage results to coveralls"
17
17
  option ["--simple-output", "-s"], :flag, "Output coverage results to the terminal"
18
18
  option ["--gutter-json", "-g"], :flag, "Output coverage results as Gutter JSON format"
19
+ option ["--cobertura-xml", "-x"], :flag, "Output coverage results as Cobertura XML format"
19
20
 
20
21
  option ["--build-directory", "-b"], "BUILD_DIRECTORY", "The directory where gcno files will be written to. Defaults to derived data."
21
22
  option ["--source-directory"], "SOURCE_DIRECTORY", "The directory where your source files are located."
@@ -75,6 +76,8 @@ Clamp do
75
76
  project.coverage_service = :terminal
76
77
  elsif gutter_json?
77
78
  project.coverage_service = :gutter_json
79
+ elsif cobertura_xml?
80
+ project.coverage_service = :cobertura_xml
78
81
  end
79
82
  end
80
83
 
data/lib/slather.rb CHANGED
@@ -2,6 +2,7 @@ require 'slather/version'
2
2
  require 'slather/project'
3
3
  require 'slather/coverage_file'
4
4
  require 'slather/coveralls_coverage_file'
5
+ require 'slather/coverage_service/cobertura_xml_output'
5
6
  require 'slather/coverage_service/coveralls'
6
7
  require 'slather/coverage_service/gutter_json_output'
7
8
  require 'slather/coverage_service/simple_output'
@@ -38,7 +38,7 @@ module Slather
38
38
 
39
39
  def gcov_data
40
40
  @gcov_data ||= begin
41
- gcov_output = `gcov "#{source_file_pathname}" --object-directory "#{gcno_file_pathname.parent}"`
41
+ gcov_output = `gcov "#{source_file_pathname}" --object-directory "#{gcno_file_pathname.parent}" --branch-probabilities --branch-counts`
42
42
  # Sometimes gcov makes gcov files for Cocoa Touch classes, like NSRange. Ignore and delete later.
43
43
  gcov_files_created = gcov_output.scan(/creating '(.+\..+\.gcov)'/)
44
44
 
@@ -48,16 +48,15 @@ module Slather
48
48
  end
49
49
 
50
50
  gcov_files_created.each { |file| FileUtils.rm_f(file) }
51
-
52
51
  gcov_data
53
52
  end
54
53
  end
55
54
 
56
- def coverage_data
57
- if gcov_data
58
- first_line_start = gcov_data =~ /^\s+(-|#+|[0-9+]):\s+1:/
55
+ def line_coverage_data
56
+ if cleaned_gcov_data
57
+ first_line_start = cleaned_gcov_data =~ /^\s+(-|#+|[0-9+]):\s+1:/
59
58
 
60
- gcov_data[first_line_start..-1].split("\n").map do |line|
59
+ cleaned_gcov_data[first_line_start..-1].split("\n").map do |line|
61
60
  coverage_for_line(line)
62
61
  end
63
62
  else
@@ -65,6 +64,11 @@ module Slather
65
64
  end
66
65
  end
67
66
 
67
+ def cleaned_gcov_data
68
+ data = gcov_data.gsub(/^function(.*) called [0-9]+ returned [0-9]+% blocks executed(.*)$\r?\n/, '')
69
+ data.gsub(/^branch(.*)$\r?\n/, '')
70
+ end
71
+
68
72
  def coverage_for_line(line)
69
73
  line =~ /^(.+?):/
70
74
 
@@ -80,11 +84,15 @@ module Slather
80
84
  end
81
85
 
82
86
  def num_lines_tested
83
- coverage_data.compact.select { |cd| cd > 0 }.count
87
+ line_coverage_data.compact.select { |cd| cd > 0 }.count
84
88
  end
85
89
 
86
90
  def num_lines_testable
87
- coverage_data.compact.count
91
+ line_coverage_data.compact.count
92
+ end
93
+
94
+ def rate_lines_tested
95
+ (num_lines_tested / num_lines_testable.to_f)
88
96
  end
89
97
 
90
98
  def percentage_lines_tested
@@ -95,6 +103,74 @@ module Slather
95
103
  end
96
104
  end
97
105
 
106
+ def branch_coverage_data
107
+ @branch_coverage_data ||= begin
108
+ branch_coverage_data = Hash.new
109
+
110
+ @gcov_data.scan(/(^(\s+(-|#+|[0-9]+):\s+[1-9]+:(.*)$\r?\n)(^branch\s+[0-9]+\s+[a-zA-Z0-9]+\s+[a-zA-Z0-9]+$\r?\n)+)+/) {|data|
111
+ lines = data[0].split("\n")
112
+ line_number = lines[0].split(':')[1].strip.to_i
113
+ branch_coverage_data[line_number] = lines[1..-1].map do |line|
114
+ if line.split(' ')[2].strip == "never"
115
+ 0
116
+ else
117
+ line.split(' ')[3].strip.to_i
118
+ end
119
+ end
120
+ }
121
+ branch_coverage_data
122
+ end
123
+ end
124
+
125
+ def branch_coverage_data_for_statement_on_line(line_number)
126
+ branch_coverage_data[line_number]
127
+ end
128
+
129
+ def num_branches_for_statement_on_line(line_number)
130
+ branch_coverage_data_for_statement_on_line(line_number).length
131
+ end
132
+
133
+ def num_branch_hits_for_statement_on_line(line_number)
134
+ branch_coverage_data_for_statement_on_line(line_number).count { |hit_count| hit_count > 0 }
135
+ end
136
+
137
+ def rate_branch_coverage_for_statement_on_line(line_number)
138
+ branch_data = branch_coverage_data_for_statement_on_line(line_number)
139
+ if branch_data == nil
140
+ 0.0
141
+ else
142
+ (num_branch_hits_for_statement_on_line(line_number) / branch_data.length.to_f)
143
+ end
144
+ end
145
+
146
+ def percentage_branch_coverage_for_statement_on_line(line_number)
147
+ rate_branch_coverage_for_statement_on_line(line_number) * 100
148
+ end
149
+
150
+ def num_branches_testable
151
+ branch_coverage_data.keys.reduce(0) do |sum, line_number|
152
+ sum += num_branches_for_statement_on_line(line_number)
153
+ end
154
+ end
155
+
156
+ def num_branches_tested
157
+ branch_coverage_data.keys.reduce(0) do |sum, line_number|
158
+ sum += num_branch_hits_for_statement_on_line(line_number)
159
+ end
160
+ end
161
+
162
+ def rate_branches_tested
163
+ if (num_branches_testable == 0)
164
+ 0.0
165
+ else
166
+ (num_branches_tested / num_branches_testable.to_f)
167
+ end
168
+ end
169
+
170
+ def source_file_basename
171
+ File.basename(source_file_pathname, '.m')
172
+ end
173
+
98
174
  def ignored?
99
175
  project.ignore_list.any? do |ignore|
100
176
  File.fnmatch(ignore, source_file_pathname_relative_to_repo_root)
@@ -0,0 +1,167 @@
1
+ require 'nokogiri'
2
+
3
+ module Slather
4
+ module CoverageService
5
+ module CoberturaXmlOutput
6
+
7
+ def coverage_file_class
8
+ Slather::CoverageFile
9
+ end
10
+ private :coverage_file_class
11
+
12
+ def post
13
+ cobertura_xml_report = create_xml_report(coverage_files)
14
+ File.open('cobertura.xml', 'w') { |file|
15
+ file.write(cobertura_xml_report.to_s)
16
+ }
17
+ end
18
+
19
+ def grouped_coverage_files
20
+ groups = Hash.new
21
+ coverage_files.each do |coverage_file|
22
+ path = File.dirname(coverage_file.source_file_pathname_relative_to_repo_root)
23
+ if groups[path] == nil
24
+ groups[path] = Array.new
25
+ end
26
+ groups[path].push(coverage_file)
27
+ end
28
+ groups
29
+ end
30
+
31
+ def create_xml_report(coverage_files)
32
+ total_project_lines = 0
33
+ total_project_lines_tested = 0
34
+ total_project_line_rate = 0.0
35
+ total_project_branches = 0
36
+ total_project_branches_tested = 0
37
+
38
+ create_empty_xml_report
39
+ coverage_node = @doc.root
40
+ source_node = @doc.at_css "source"
41
+ source_node.content = Pathname.pwd.to_s
42
+ packages_node = @doc.at_css "packages"
43
+
44
+ # group files by path
45
+ grouped_coverage_files.each do |path , package_coverage_files|
46
+ package_node = Nokogiri::XML::Node.new "package", @doc
47
+ package_node.parent = packages_node
48
+ classes_node = Nokogiri::XML::Node.new "classes", @doc
49
+ classes_node.parent = package_node
50
+ package_node['name'] = path.gsub(/\//, '.')
51
+
52
+ total_package_lines = 0
53
+ total_package_lines_tested = 0
54
+ total_package_lines_rate = 0.0
55
+ total_package_branches = 0
56
+ total_package_branches_tested = 0
57
+
58
+ package_coverage_files.each do |package_coverage_file|
59
+ next unless package_coverage_file.gcov_data
60
+ class_node = create_class_node(package_coverage_file)
61
+ class_node.parent = classes_node
62
+ total_package_lines += package_coverage_file.num_lines_testable
63
+ total_package_lines_tested += package_coverage_file.num_lines_tested
64
+ total_package_branches += package_coverage_file.num_branches_testable
65
+ total_package_branches_tested += package_coverage_file.num_branches_tested
66
+ end
67
+
68
+ total_package_line_rate = '%.16f' % (total_package_lines_tested / total_package_lines.to_f)
69
+ total_package_branch_rate = '%.16f' % (total_package_branches_tested / total_package_branches.to_f)
70
+
71
+ package_node['line-rate'] = total_package_line_rate
72
+ package_node['branch-rate'] = total_package_branch_rate
73
+ package_node['complexity'] = '0.0'
74
+
75
+ total_project_lines += total_package_lines
76
+ total_project_lines_tested += total_package_lines_tested
77
+ total_project_branches += total_package_branches
78
+ total_project_branches_tested += total_package_branches_tested
79
+ end
80
+
81
+ total_project_line_rate = '%.16f' % (total_project_lines_tested / total_project_lines.to_f)
82
+ total_project_branch_rate = '%.16f' % (total_project_branches_tested / total_project_branches.to_f)
83
+
84
+ coverage_node['line-rate'] = total_project_line_rate
85
+ coverage_node['branch-rate'] = total_project_branch_rate
86
+ coverage_node['lines-covered'] = total_project_lines_tested
87
+ coverage_node['lines-valid'] = total_project_lines
88
+ coverage_node['branches-covered'] = total_project_branches_tested
89
+ coverage_node['branches-valid'] = total_project_branches
90
+ coverage_node['complexity'] = "0.0"
91
+ coverage_node['timestamp'] = DateTime.now.strftime('%s')
92
+ coverage_node['version'] = "Slather #{Slather::VERSION}"
93
+ @doc.to_xml
94
+ end
95
+
96
+ def create_class_node(coverage_file)
97
+ filename = coverage_file.source_file_basename
98
+ filepath = coverage_file.source_file_pathname_relative_to_repo_root.to_s
99
+
100
+ class_node = Nokogiri::XML::Node.new "class", @doc
101
+ class_node['name'] = filename
102
+ class_node['filename'] = filepath
103
+ class_node['line-rate'] = '%.16f' % coverage_file.rate_lines_tested
104
+ class_node['branch-rate'] = '1.0'
105
+
106
+ methods_node = Nokogiri::XML::Node.new "methods", @doc
107
+ methods_node.parent = class_node
108
+ lines_node = Nokogiri::XML::Node.new "lines", @doc
109
+ lines_node.parent = class_node
110
+
111
+ coverage_file.cleaned_gcov_data.split("\n").each do |line|
112
+ line_segments = line.split(':')
113
+ if coverage_file.coverage_for_line(line)
114
+ line_number = line_segments[1].strip.to_i
115
+ line_node = create_line_node(line, coverage_file)
116
+ line_node.parent = lines_node
117
+ end
118
+ end
119
+ class_node['branch-rate'] = '%.16f' % [coverage_file.rate_branches_tested]
120
+ class_node['complexity'] = '0.0'
121
+ class_node
122
+ end
123
+
124
+ def create_line_node(line, coverage_file)
125
+ line_number = line.split(':')[1].strip.to_i
126
+ line_node = Nokogiri::XML::Node.new "line", @doc
127
+ line_node['number'] = line_number
128
+ line_node['branch'] = "false"
129
+ line_node['hits'] = coverage_file.coverage_for_line(line)
130
+
131
+ if coverage_file.branch_coverage_data_for_statement_on_line(line_number)
132
+ line_node['branch'] = "true"
133
+ conditions_node = Nokogiri::XML::Node.new "conditions", @doc
134
+ conditions_node.parent = line_node
135
+ condition_node = Nokogiri::XML::Node.new "condition", @doc
136
+ condition_node.parent = conditions_node
137
+ condition_node['number'] = "0"
138
+ condition_node['type'] = "jump"
139
+ branches_testable = coverage_file.num_branches_for_statement_on_line(line_number)
140
+ branch_hits = coverage_file.num_branch_hits_for_statement_on_line(line_number)
141
+ condition_coverage = coverage_file.percentage_branch_coverage_for_statement_on_line(line_number)
142
+ condition_node['coverage'] = "#{condition_coverage.to_i}%"
143
+ line_node['condition-coverage'] = "#{condition_coverage.to_i}% (#{branch_hits}/#{branches_testable})"
144
+ end
145
+ line_node
146
+ end
147
+
148
+ def create_empty_xml_report
149
+ builder = Nokogiri::XML::Builder.new do |xml|
150
+ xml.doc.create_internal_subset(
151
+ 'coverage',
152
+ nil,
153
+ "http://cobertura.sourceforge.net/xml/coverage-04.dtd"
154
+ )
155
+ xml.coverage do
156
+ xml.sources do
157
+ xml.source
158
+ end
159
+ xml.packages
160
+ end
161
+ end
162
+ @doc = builder.doc
163
+ end
164
+
165
+ end
166
+ end
167
+ end
@@ -17,7 +17,7 @@ module Slather
17
17
  filename = coverage_file.source_file_pathname.to_s
18
18
  filename = filename.sub(Pathname.pwd.to_s, '')[1..-1]
19
19
 
20
- coverage_file.gcov_data.split("\n").each do |line|
20
+ coverage_file.cleaned_gcov_data.split("\n").each do |line|
21
21
  data = line.split(':')
22
22
 
23
23
  line_number = data[1].to_i
@@ -5,7 +5,7 @@ module Slather
5
5
  {
6
6
  :name => source_file_pathname_relative_to_repo_root.to_s,
7
7
  :source => source_data,
8
- :coverage => coverage_data
8
+ :coverage => line_coverage_data
9
9
  }
10
10
  end
11
11
 
@@ -106,8 +106,10 @@ module Slather
106
106
  extend(Slather::CoverageService::SimpleOutput)
107
107
  elsif service == :gutter_json
108
108
  extend(Slather::CoverageService::GutterJsonOutput)
109
+ elsif service == :cobertura_xml
110
+ extend(Slather::CoverageService::CoberturaXmlOutput)
109
111
  else
110
- raise ArgumentError, "`#{coverage_service}` is not a valid coverage service. Try `terminal` or `coveralls`"
112
+ raise ArgumentError, "`#{coverage_service}` is not a valid coverage service. Try `terminal`, `coveralls`, `gutter_json` or `cobertura_xml`"
111
113
  end
112
114
  @coverage_service = service
113
115
  end
@@ -1,3 +1,3 @@
1
1
  module Slather
2
- VERSION = "1.4.0"
2
+ VERSION = "1.5.0"
3
3
  end
data/slather.gemspec CHANGED
@@ -26,4 +26,5 @@ Gem::Specification.new do |spec|
26
26
 
27
27
  spec.add_dependency "clamp", "~> 0.6"
28
28
  spec.add_dependency "xcodeproj", "~> 0.19.2"
29
+ spec.add_dependency "nokogiri", "~> 1.6.3"
29
30
  end
@@ -0,0 +1,128 @@
1
+ <?xml version="1.0"?>
2
+ <!DOCTYPE coverage SYSTEM "http://cobertura.sourceforge.net/xml/coverage-04.dtd">
3
+ <coverage line-rate="0.6603773584905660" branch-rate="0.1190476190476190" lines-covered="35" lines-valid="53" branches-covered="5" branches-valid="42" complexity="0.0" timestamp="" version="">
4
+ <sources>
5
+ <source></source>
6
+ </sources>
7
+ <packages>
8
+ <package name="spec.fixtures.fixtures.more_files" line-rate="0.3846153846153846" branch-rate="0.3333333333333333" complexity="0.0">
9
+ <classes>
10
+ <class name="Branches" filename="spec/fixtures/fixtures/more_files/Branches.m" line-rate="0.5000000000000000" branch-rate="0.4000000000000000" complexity="0.0">
11
+ <methods/>
12
+ <lines>
13
+ <line number="13" branch="false" hits="2"/>
14
+ <line number="15" branch="true" hits="2" condition-coverage="100% (2/2)">
15
+ <conditions>
16
+ <condition number="0" type="jump" coverage="100%"/>
17
+ </conditions>
18
+ </line>
19
+ <line number="16" branch="false" hits="1"/>
20
+ <line number="18" branch="true" hits="1" condition-coverage="50% (1/2)">
21
+ <conditions>
22
+ <condition number="0" type="jump" coverage="50%"/>
23
+ </conditions>
24
+ </line>
25
+ <line number="19" branch="false" hits="0"/>
26
+ <line number="20" branch="false" hits="0"/>
27
+ <line number="21" branch="false" hits="1"/>
28
+ <line number="22" branch="false" hits="1"/>
29
+ <line number="25" branch="false" hits="2"/>
30
+ <line number="26" branch="true" hits="2" condition-coverage="50% (1/2)">
31
+ <conditions>
32
+ <condition number="0" type="jump" coverage="50%"/>
33
+ </conditions>
34
+ </line>
35
+ <line number="27" branch="false" hits="2"/>
36
+ <line number="29" branch="true" hits="0" condition-coverage="0% (0/4)">
37
+ <conditions>
38
+ <condition number="0" type="jump" coverage="0%"/>
39
+ </conditions>
40
+ </line>
41
+ <line number="31" branch="false" hits="0"/>
42
+ <line number="32" branch="false" hits="0"/>
43
+ <line number="35" branch="false" hits="0"/>
44
+ <line number="36" branch="false" hits="0"/>
45
+ <line number="38" branch="false" hits="0"/>
46
+ <line number="39" branch="false" hits="0"/>
47
+ <line number="41" branch="false" hits="0"/>
48
+ <line number="43" branch="false" hits="2"/>
49
+ </lines>
50
+ </class>
51
+ <class name="peekaview" filename="spec/fixtures/fixtures/more_files/peekaview.m" line-rate="0.0000000000000000" branch-rate="0.0000000000000000" complexity="0.0">
52
+ <methods/>
53
+ <lines>
54
+ <line number="13" branch="false" hits="0"/>
55
+ <line number="15" branch="false" hits="0"/>
56
+ <line number="16" branch="true" hits="0" condition-coverage="0% (0/2)">
57
+ <conditions>
58
+ <condition number="0" type="jump" coverage="0%"/>
59
+ </conditions>
60
+ </line>
61
+ <line number="18" branch="false" hits="0"/>
62
+ <line number="19" branch="false" hits="0"/>
63
+ <line number="20" branch="false" hits="0"/>
64
+ </lines>
65
+ </class>
66
+ </classes>
67
+ </package>
68
+ <package name="spec.fixtures.fixtures" line-rate="0.5000000000000000" branch-rate="NaN" complexity="0.0">
69
+ <classes>
70
+ <class name="fixtures" filename="spec/fixtures/fixtures/fixtures.m" line-rate="0.5000000000000000" branch-rate="0.0000000000000000" complexity="0.0">
71
+ <methods/>
72
+ <lines>
73
+ <line number="15" branch="false" hits="1"/>
74
+ <line number="16" branch="false" hits="1"/>
75
+ <line number="20" branch="false" hits="0"/>
76
+ <line number="21" branch="false" hits="0"/>
77
+ </lines>
78
+ </class>
79
+ </classes>
80
+ </package>
81
+ <package name="spec.fixtures.fixturesTests" line-rate="1.0000000000000000" branch-rate="0.0333333333333333" complexity="0.0">
82
+ <classes>
83
+ <class name="BranchesTests" filename="spec/fixtures/fixturesTests/BranchesTests.m" line-rate="1.0000000000000000" branch-rate="0.0000000000000000" complexity="0.0">
84
+ <methods/>
85
+ <lines>
86
+ <line number="19" branch="false" hits="2"/>
87
+ <line number="21" branch="false" hits="2"/>
88
+ <line number="25" branch="false" hits="2"/>
89
+ <line number="26" branch="false" hits="2"/>
90
+ <line number="29" branch="false" hits="1"/>
91
+ <line number="30" branch="false" hits="1"/>
92
+ <line number="31" branch="false" hits="1"/>
93
+ <line number="34" branch="false" hits="1"/>
94
+ <line number="35" branch="false" hits="1"/>
95
+ <line number="36" branch="false" hits="1"/>
96
+ </lines>
97
+ </class>
98
+ <class name="fixturesTests" filename="spec/fixtures/fixturesTests/fixturesTests.m" line-rate="1.0000000000000000" branch-rate="0.0000000000000000" complexity="0.0">
99
+ <methods/>
100
+ <lines>
101
+ <line number="20" branch="false" hits="1"/>
102
+ <line number="22" branch="false" hits="1"/>
103
+ <line number="27" branch="false" hits="1"/>
104
+ <line number="28" branch="false" hits="1"/>
105
+ <line number="32" branch="false" hits="1"/>
106
+ <line number="33" branch="false" hits="1"/>
107
+ <line number="34" branch="false" hits="1"/>
108
+ </lines>
109
+ </class>
110
+ <class name="peekaviewTests" filename="spec/fixtures/fixturesTests/peekaviewTests.m" line-rate="1.0000000000000000" branch-rate="0.0333333333333333" complexity="0.0">
111
+ <methods/>
112
+ <lines>
113
+ <line number="19" branch="false" hits="1"/>
114
+ <line number="21" branch="false" hits="1"/>
115
+ <line number="26" branch="false" hits="1"/>
116
+ <line number="27" branch="false" hits="1"/>
117
+ <line number="31" branch="true" hits="2" condition-coverage="3% (1/30)">
118
+ <conditions>
119
+ <condition number="0" type="jump" coverage="3%"/>
120
+ </conditions>
121
+ </line>
122
+ <line number="32" branch="false" hits="1"/>
123
+ </lines>
124
+ </class>
125
+ </classes>
126
+ </package>
127
+ </packages>
128
+ </coverage>