duvet 0.3.3 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +1 -1
- data/README.md +26 -9
- data/Rakefile +13 -2
- data/lib/duvet.rb +101 -43
- data/lib/duvet/cov.rb +58 -65
- data/lib/duvet/covs.rb +25 -37
- data/lib/duvet/version.rb +2 -2
- data/spec/duvet/cov_spec.rb +91 -0
- data/spec/duvet/covs_spec.rb +57 -0
- data/spec/duvet_spec.rb +81 -0
- data/{test → spec}/helper.rb +2 -8
- data/templates/css/styles.css +129 -0
- data/templates/html/file.erb +21 -22
- data/templates/html/index.erb +14 -15
- metadata +19 -44
- data/lib/duvet/core_ext.rb +0 -17
- data/test/test_core_ext.rb +0 -13
- data/test/test_cov.rb +0 -76
- data/test/test_covs.rb +0 -67
- data/test/test_duvet.rb +0 -17
data/lib/duvet/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
module Duvet
|
2
|
-
VERSION = '0.
|
3
|
-
end
|
2
|
+
VERSION = '0.4.0'
|
3
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require_relative '../helper'
|
2
|
+
|
3
|
+
describe Duvet::Cov do
|
4
|
+
|
5
|
+
let(:lines) { [5, 2, nil, 2, 4, 3, 0, 0, 0, nil] }
|
6
|
+
subject { Duvet::Cov.new('lib/duvet.rb', lines) }
|
7
|
+
|
8
|
+
describe '#path' do
|
9
|
+
it 'returns the cleaned path' do
|
10
|
+
cov = Duvet::Cov.new(Dir.pwd + '/test.rb', [])
|
11
|
+
cov.path.must_equal Pathname.new('test.rb')
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#lines' do
|
16
|
+
it 'returns the passed coverage data' do
|
17
|
+
subject.lines.must_equal lines
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#code_lines' do
|
22
|
+
it 'returns lines which can be exectued' do
|
23
|
+
subject.code_lines.must_equal [5, 2, 2, 4, 3, 0, 0, 0]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe '#ran_lines' do
|
28
|
+
it 'returns lines which were executed' do
|
29
|
+
subject.ran_lines.must_equal [5, 2, 2, 4, 3]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
describe '#total_coverage' do
|
35
|
+
it 'returns the ratio of lines which were executed' do
|
36
|
+
subject.total_coverage.must_equal 0.5
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe '#code_coverage' do
|
41
|
+
it 'returns the ratio of executable lines which were executed' do
|
42
|
+
subject.code_coverage.must_equal 0.625
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '#percent' do
|
47
|
+
it 'returns a number as a 4-digit (ie. ??.??) percentage' do
|
48
|
+
subject.percent(0.123456789).must_equal "12.35%"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '#report' do
|
53
|
+
it 'generates a simple textual report of coverage' do
|
54
|
+
subject.report.must_equal <<EOS
|
55
|
+
lib/duvet.rb
|
56
|
+
total: 50.00%
|
57
|
+
code: 62.50%
|
58
|
+
EOS
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '#data' do
|
63
|
+
it 'contains the necessary template data' do
|
64
|
+
subject.path.stubs(:readlines).returns("code and code and code")
|
65
|
+
|
66
|
+
subject.data.must_equal file: {
|
67
|
+
path: 'lib/duvet.rb',
|
68
|
+
url: 'lib/duvet.html',
|
69
|
+
root: '../',
|
70
|
+
source: 'code and code and code',
|
71
|
+
},
|
72
|
+
lines: {
|
73
|
+
total: 10,
|
74
|
+
code: 8,
|
75
|
+
ran: 5
|
76
|
+
},
|
77
|
+
coverage: {
|
78
|
+
code: '62.50%',
|
79
|
+
total: '50.00%',
|
80
|
+
lines: lines
|
81
|
+
}
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe '#write' do
|
86
|
+
it 'writes the file' do
|
87
|
+
Duvet.expects(:write).with(subject.data, 'html/file.erb')
|
88
|
+
subject.write
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require_relative '../helper'
|
2
|
+
|
3
|
+
describe Duvet::Covs do
|
4
|
+
|
5
|
+
let(:files) {
|
6
|
+
{
|
7
|
+
'lib/duvet.rb' => [3, 2, 1, 0, nil],
|
8
|
+
'lib/duvet/cov.rb' => [nil, nil, 0, 1, 2]
|
9
|
+
}
|
10
|
+
}
|
11
|
+
|
12
|
+
subject { Duvet::Covs.from_data files }
|
13
|
+
|
14
|
+
describe '.from_data' do
|
15
|
+
it 'converts data to Cov instances' do
|
16
|
+
subject.each do |cov|
|
17
|
+
cov.must_be_kind_of Duvet::Cov
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#report' do
|
23
|
+
it 'creates a report' do
|
24
|
+
subject.report.must_equal <<EOS
|
25
|
+
lib/duvet.rb
|
26
|
+
total: 60.00%
|
27
|
+
code: 75.00%
|
28
|
+
|
29
|
+
lib/duvet/cov.rb
|
30
|
+
total: 40.00%
|
31
|
+
code: 66.67%
|
32
|
+
|
33
|
+
EOS
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe '#data' do
|
38
|
+
it 'populates the data' do
|
39
|
+
subject.data.must_include :files
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '#write' do
|
44
|
+
it 'writes the index and individual covs' do
|
45
|
+
subject.each {|i| i.expects(:write) }
|
46
|
+
Duvet.expects(:write).with(subject.data, 'html/index.erb')
|
47
|
+
|
48
|
+
subject.write
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'warns if no coverage to write' do
|
52
|
+
empty = Duvet::Covs.new
|
53
|
+
-> { empty.write }.must_output nil, "No files to create coverage for.\n"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
data/spec/duvet_spec.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
require_relative 'helper'
|
2
|
+
|
3
|
+
describe Duvet do
|
4
|
+
|
5
|
+
before { Coverage.stubs(:start) }
|
6
|
+
subject { Duvet.dup }
|
7
|
+
|
8
|
+
describe '#start' do
|
9
|
+
it 'makes sure :filter is a regexp' do
|
10
|
+
subject.start :filter => 'str'
|
11
|
+
subject.opts[:filter].must_be_kind_of Regexp
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'starts Coverage' do
|
15
|
+
Coverage.expects(:start)
|
16
|
+
subject.start
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#result' do
|
21
|
+
it 'filters the results' do
|
22
|
+
Coverage.expects(:result).returns({'a/b.c' => [], 'a/e.f' => [], 'g/h.i' => []})
|
23
|
+
subject.start :filter => 'a/e'
|
24
|
+
|
25
|
+
subject.result.map(&:path).map(&:to_s).must_equal ['a/e.f']
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'returns a Covs instance' do
|
29
|
+
Coverage.stubs(:result).returns({'a/b.c' => []})
|
30
|
+
subject.result.must_be_kind_of Duvet::Covs
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe '#format' do
|
35
|
+
it 'renders a file from the data and template given' do
|
36
|
+
Pathname.any_instance.stubs(:read).returns("The magic number is <%= a %>")
|
37
|
+
r = subject.format({a: 1}, 'template.erb')
|
38
|
+
r.must_equal "The magic number is 1"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#write' do
|
43
|
+
it 'formats then writes a file' do
|
44
|
+
formatted = Object.new
|
45
|
+
data = {file: {url: 'somewhere/file.html'}}
|
46
|
+
subject.expects(:format).with(data, 'template').returns(formatted)
|
47
|
+
subject.expects(:write_file).with(formatted, 'somewhere/file.html')
|
48
|
+
|
49
|
+
subject.write data, 'template'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe '#write_file' do
|
54
|
+
it 'writes a file' do
|
55
|
+
File.expects(:open).with(Pathname.new('cov/somewhere/file.html'), 'w')
|
56
|
+
subject.write_file 'text', 'somewhere/file.html'
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe '#write_resources' do
|
61
|
+
it 'writes the resources' do
|
62
|
+
subject.stubs(:write_file)
|
63
|
+
4.times { subject.expects(:write_file) }
|
64
|
+
|
65
|
+
subject.write_resources
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe '#finish' do
|
70
|
+
it 'makes the directory, writes the results and resources' do
|
71
|
+
FileUtils.expects(:mkdir_p).with(subject.opts[:dir])
|
72
|
+
obj = mock()
|
73
|
+
subject.expects(:result).returns(obj)
|
74
|
+
obj.expects(:write)
|
75
|
+
subject.expects(:write_resources)
|
76
|
+
|
77
|
+
subject.finish
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
data/{test → spec}/helper.rb
RENAMED
@@ -7,13 +7,7 @@ Coverage.start
|
|
7
7
|
require_relative '../lib/duvet'
|
8
8
|
Duvet.start :filter => 'lib/duvet'
|
9
9
|
|
10
|
-
require 'minitest/
|
10
|
+
require 'minitest/autorun'
|
11
11
|
require 'minitest/pride'
|
12
12
|
|
13
|
-
require '
|
14
|
-
class MiniTest::Unit::TestCase
|
15
|
-
include RR::Adapters::TestUnit
|
16
|
-
end
|
17
|
-
|
18
|
-
MiniTest::Unit.autorun
|
19
|
-
|
13
|
+
require 'mocha'
|
@@ -0,0 +1,129 @@
|
|
1
|
+
@charset "UTF-8";
|
2
|
+
html {
|
3
|
+
margin: 0;
|
4
|
+
padding: 0;
|
5
|
+
width: 100%; }
|
6
|
+
|
7
|
+
body {
|
8
|
+
background: #f4f2ed;
|
9
|
+
font: 12px/1.3em Helvetica;
|
10
|
+
margin: 8px; }
|
11
|
+
|
12
|
+
a {
|
13
|
+
text-decoration: none; }
|
14
|
+
|
15
|
+
header {
|
16
|
+
margin: 2em 0.5em;
|
17
|
+
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); }
|
18
|
+
header h1, header h1 a {
|
19
|
+
color: #333333; }
|
20
|
+
header h2 {
|
21
|
+
color: #999999;
|
22
|
+
font-size: 16px; }
|
23
|
+
|
24
|
+
#filter {
|
25
|
+
position: absolute;
|
26
|
+
top: 20px;
|
27
|
+
right: 8px;
|
28
|
+
border: 1px solid #787878;
|
29
|
+
background: #f0f0f0;
|
30
|
+
-webkit-border-radius: 3px;
|
31
|
+
-webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1), 0 1px 0 white;
|
32
|
+
-webkit-transition: all 0.2s;
|
33
|
+
padding: 4px;
|
34
|
+
width: 180px; }
|
35
|
+
#filter:focus {
|
36
|
+
outline: none;
|
37
|
+
background: #f4f4f4;
|
38
|
+
border: 1px solid #646464; }
|
39
|
+
|
40
|
+
.table-wrapper {
|
41
|
+
min-width: 700px; }
|
42
|
+
|
43
|
+
table {
|
44
|
+
border: 1px solid grey;
|
45
|
+
background: #e0dedb;
|
46
|
+
border-collapse: collapse;
|
47
|
+
width: 100%;
|
48
|
+
margin-bottom: 1em;
|
49
|
+
-webkit-box-shadow: 0 1px 0 white; }
|
50
|
+
|
51
|
+
table.source, pre, code {
|
52
|
+
font: 11px/1.2em Menlo; }
|
53
|
+
|
54
|
+
td pre {
|
55
|
+
margin: 0; }
|
56
|
+
|
57
|
+
.summary {
|
58
|
+
margin-bottom: 1em;
|
59
|
+
background: #e0dedb; }
|
60
|
+
.summary tbody .name, .summary tbody .name a {
|
61
|
+
color: black;
|
62
|
+
font-weight: bold; }
|
63
|
+
.summary tbody tr:hover {
|
64
|
+
background: #c8c5c0; }
|
65
|
+
.summary thead {
|
66
|
+
background: #333333;
|
67
|
+
color: #cccccc; }
|
68
|
+
.summary thead th {
|
69
|
+
cursor: pointer; }
|
70
|
+
.summary tr {
|
71
|
+
text-align: left; }
|
72
|
+
.summary th, .summary td {
|
73
|
+
padding: 0.5em; }
|
74
|
+
.summary .header::after {
|
75
|
+
font-size: 10px;
|
76
|
+
margin-left: 10px; }
|
77
|
+
.summary .descending::after {
|
78
|
+
content: "▼"; }
|
79
|
+
.summary .ascending::after {
|
80
|
+
content: "▲"; }
|
81
|
+
.summary .lines, .summary .loc, .summary .ran, .summary .cov, .summary .code {
|
82
|
+
width: 100px; }
|
83
|
+
|
84
|
+
.totals {
|
85
|
+
border-top: 1px solid grey;
|
86
|
+
background: #c8c5c0;
|
87
|
+
font-weight: bold; }
|
88
|
+
|
89
|
+
.source tr pre {
|
90
|
+
margin-left: 3px; }
|
91
|
+
.source tr .count {
|
92
|
+
background: white;
|
93
|
+
-webkit-border-radius: 4px;
|
94
|
+
-webkit-border-top-left-radius: 0px;
|
95
|
+
-webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
|
96
|
+
border: 1px solid black;
|
97
|
+
display: block;
|
98
|
+
padding: 3px;
|
99
|
+
position: absolute;
|
100
|
+
margin-top: -1.3em;
|
101
|
+
right: 15px;
|
102
|
+
opacity: 0; }
|
103
|
+
.source tr .no {
|
104
|
+
color: #cccccc;
|
105
|
+
background: #333333;
|
106
|
+
padding: 0 0 0 3px;
|
107
|
+
width: 25px; }
|
108
|
+
.source tr:hover .count {
|
109
|
+
opacity: 1; }
|
110
|
+
.source tr:hover .no {
|
111
|
+
color: white; }
|
112
|
+
.source tr.excluded {
|
113
|
+
background: #e0dedb; }
|
114
|
+
.source tr.excluded:hover {
|
115
|
+
background: #c8c5c0; }
|
116
|
+
.source tr.unran {
|
117
|
+
background: #ce8b8c; }
|
118
|
+
.source tr.unran:hover {
|
119
|
+
background: #bf6768; }
|
120
|
+
.source tr.ran {
|
121
|
+
background: #bed2be; }
|
122
|
+
.source tr.ran:hover {
|
123
|
+
background: #a0bda0; }
|
124
|
+
|
125
|
+
footer {
|
126
|
+
color: rgba(0, 0, 0, 0.6);
|
127
|
+
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); }
|
128
|
+
footer a {
|
129
|
+
color: black; }
|
data/templates/html/file.erb
CHANGED
@@ -2,19 +2,19 @@
|
|
2
2
|
<html lang="en">
|
3
3
|
<head>
|
4
4
|
<meta charset="utf-8" />
|
5
|
-
<title><%= file
|
6
|
-
<link rel="stylesheet" href="styles.css" type="text/css" />
|
7
|
-
<script type="text/javascript" src="jquery.js"></script>
|
8
|
-
<script type="text/javascript" src="main.js"></script>
|
9
|
-
<script type="text/javascript" src="plugins.js"></script>
|
5
|
+
<title><%= file.path %></title>
|
6
|
+
<link rel="stylesheet" href="<%= file.root %>styles.css" type="text/css" />
|
7
|
+
<script type="text/javascript" src="<%= file.root %>jquery.js"></script>
|
8
|
+
<script type="text/javascript" src="<%= file.root %>main.js"></script>
|
9
|
+
<script type="text/javascript" src="<%= file.root %>plugins.js"></script>
|
10
10
|
</head>
|
11
|
-
|
11
|
+
|
12
12
|
<body>
|
13
13
|
<header>
|
14
|
-
<h1><a href="index.html">Coverage</a></h1>
|
15
|
-
<h2><%= file
|
14
|
+
<h1><a href="<%= file.root %>index.html">Coverage</a></h1>
|
15
|
+
<h2><%= file.path %></h2>
|
16
16
|
</header>
|
17
|
-
|
17
|
+
|
18
18
|
<div class="table-wrapper">
|
19
19
|
<table class="summary" border="none">
|
20
20
|
<thead>
|
@@ -29,26 +29,26 @@
|
|
29
29
|
</thead>
|
30
30
|
<tbody>
|
31
31
|
<tr>
|
32
|
-
<td class="name"><%= file
|
33
|
-
<td><%=
|
34
|
-
<td><%=
|
35
|
-
<td><%=
|
36
|
-
<td><%= coverage
|
37
|
-
<td><%= coverage
|
32
|
+
<td class="name"><%= file.path %></td>
|
33
|
+
<td><%= lines.total %></td>
|
34
|
+
<td><%= lines.code %></td>
|
35
|
+
<td><%= lines.ran %></td>
|
36
|
+
<td><%= coverage.total %></td>
|
37
|
+
<td><%= coverage.code %></td>
|
38
38
|
</tr>
|
39
39
|
</tbody>
|
40
40
|
</table>
|
41
41
|
</div>
|
42
|
-
|
42
|
+
|
43
43
|
<div class="table-wrapper">
|
44
44
|
<table class="source" border="none">
|
45
|
-
<% file
|
45
|
+
<% file.source.zip(coverage.lines).each_with_index do |(line, count), i| %>
|
46
46
|
<% if count.nil? %>
|
47
47
|
<tr class="excluded">
|
48
48
|
<td class="no"><%= i %></td>
|
49
49
|
<td><pre><code><%= line %></code></pre></td>
|
50
50
|
</tr>
|
51
|
-
|
51
|
+
|
52
52
|
<% else %>
|
53
53
|
<tr class="<%= count.zero? ? "unran" : "ran" %>">
|
54
54
|
<td class="no"><%= i %></td>
|
@@ -58,14 +58,13 @@
|
|
58
58
|
<% end %>
|
59
59
|
</table>
|
60
60
|
</div>
|
61
|
-
|
62
|
-
|
61
|
+
|
62
|
+
|
63
63
|
<footer>
|
64
64
|
<span>
|
65
|
-
Generated at <%= time %> with
|
65
|
+
Generated at <%= time %> with
|
66
66
|
<a href="http://github.com/hawx/duvet"><%= name %> <%= version %></a>
|
67
67
|
</span>
|
68
68
|
</footer>
|
69
69
|
</body>
|
70
70
|
</html>
|
71
|
-
|