slather 1.4.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
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>