piggly 1.2.0 → 1.2.1
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.
- data/README.markdown +40 -8
- data/bin/piggly +5 -2
- data/lib/piggly/parser/grammar.tt +2 -2
- data/lib/piggly/parser/parser.rb +33 -22
- data/lib/piggly/task.rb +2 -2
- data/lib/piggly/version.rb +2 -2
- metadata +2 -2
data/README.markdown
CHANGED
@@ -1,29 +1,54 @@
|
|
1
|
-
# Piggly - PostgreSQL PL/pgSQL stored
|
1
|
+
# Piggly - PostgreSQL PL/pgSQL stored-procedure code coverage for Ruby
|
2
|
+
|
3
|
+
## Purpose
|
4
|
+
|
5
|
+
PL/pgSQL doesn't have much in the way of developer tools, and writing automated tests for
|
6
|
+
stored procedures can go much better when you know what you haven't tested. Code coverage
|
7
|
+
allows you to see which parts of your code haven't been executed.
|
2
8
|
|
3
9
|
## What's Piggly?
|
4
|
-
|
5
|
-
|
10
|
+
|
11
|
+
Piggly is a tool written in Ruby to track code coverage of PostgreSQL's PL/pgSQL stored
|
12
|
+
procedures. It reports on code coverage to help you identify untested parts of your code. You
|
13
|
+
write tests in Ruby against your stored procedures and run them with piggly.
|
14
|
+
|
15
|
+
## How Does It Work?
|
16
|
+
|
17
|
+
Piggly tracks the execution of PostgreSQL's PL/pgSQL stored procedures by recompiling
|
18
|
+
the stored procedure with instrumentation code that uses RAISE WARNING to notify the
|
19
|
+
client of an execution event (e.g., a branch condition evaluating to true or false). It records
|
20
|
+
these events and generates prettified source code that is annotated with coverage details.
|
6
21
|
|
7
22
|
## Features
|
23
|
+
|
8
24
|
* Branch, block, and loop coverage analysis
|
9
25
|
* Instrumenting source-to-source compiler
|
10
26
|
* Low test execution overhead
|
11
27
|
* Reduced compilation times by use of disk caching
|
12
|
-
* Readable and easily
|
13
|
-
*
|
28
|
+
* Readable and easily-navigable reports (see example/piggly/reports/index.html)
|
29
|
+
* Possible to aggregate coverage across multiple runs
|
14
30
|
* Test::Unit and RSpec compatible
|
15
31
|
|
16
32
|
## Limitations
|
33
|
+
|
17
34
|
* Cannot parse aggregate definitions (but helper functions are fine)
|
18
35
|
* Cannot parse nested dollar-quoted strings, eg $A$ ... $B$ ... $B$ ... $A$
|
19
36
|
* Report generation is resource intensive and slow
|
37
|
+
* SQL statements are not instrumented, so their branches (COALESCE, WHERE-clauses, etc) aren't tracked
|
38
|
+
* Not all PL/pgSQL grammar is currently supported, but this is easily addressed
|
20
39
|
|
21
40
|
## Requirements
|
41
|
+
|
22
42
|
* [Treetop] [2]
|
23
|
-
* Stored procedures stored on the filesystem, defined
|
43
|
+
* Stored procedures stored on the filesystem, defined as `CREATE OR REPLACE FUNCTION`
|
24
44
|
* The [ruby-pg driver] [3], and for the time being, ActiveRecord (some workaround should be possible)
|
25
45
|
|
46
|
+
## How to Install
|
47
|
+
|
48
|
+
$ gem install piggly
|
49
|
+
|
26
50
|
## Usage
|
51
|
+
|
27
52
|
Assume your stored procedures are in proc/, and the tests that should be exercising your
|
28
53
|
stored procedures are in spec/.
|
29
54
|
|
@@ -57,16 +82,17 @@ stored procedures are in spec/.
|
|
57
82
|
-rw-r--r-- 1 kputnam kputnam 1.3K 2010-04-19 14:25 piggly/reports/index.html
|
58
83
|
|
59
84
|
Note the compilation can be slow on the first run, but on subsequent runs it shouldn't need
|
60
|
-
to
|
85
|
+
to compile everything again. If a file is added or changed (based on mtime), it will be recompiled.
|
61
86
|
|
62
87
|
Piggly can also be run from Rake, with a task like:
|
88
|
+
require 'piggly/task'
|
63
89
|
|
64
90
|
namespace :spec do
|
65
91
|
Piggly::Task.new(:piggly => 'db:test:prepare') do |t|
|
66
92
|
t.libs.push 'spec'
|
67
93
|
|
68
94
|
t.test_files = FileList['spec/**/*_spec.rb']
|
69
|
-
t.proc_files = FileList['
|
95
|
+
t.proc_files = FileList['proc/*.sql']
|
70
96
|
|
71
97
|
# this can be used if piggly is frozen in a Rails application
|
72
98
|
t.libs.concat Dir['vendor/gems/*/lib/'].sort.reverse
|
@@ -76,9 +102,15 @@ Piggly can also be run from Rake, with a task like:
|
|
76
102
|
|
77
103
|
$ rake spec:piggly
|
78
104
|
|
105
|
+
## Bugs & Issues
|
106
|
+
|
107
|
+
Please report any issues on the [github tracker] [4]
|
108
|
+
|
79
109
|
## Author
|
110
|
+
|
80
111
|
* Kyle Putnam <putnam.kyle@gmail.com>
|
81
112
|
|
82
113
|
[1]: http://github.com/relevance/rcov/
|
83
114
|
[2]: http://github.com/nathansobo/treetop
|
84
115
|
[3]: http://bitbucket.org/ged/ruby-pg/
|
116
|
+
[4]: http://github.com/kputnam/piggly/issues
|
data/bin/piggly
CHANGED
@@ -26,6 +26,7 @@ module Piggly
|
|
26
26
|
uninstall_procs(sources)
|
27
27
|
create_index(sources)
|
28
28
|
create_reports(sources)
|
29
|
+
exit! 0 # avoid running tests again
|
29
30
|
end
|
30
31
|
end
|
31
32
|
|
@@ -172,10 +173,12 @@ module Piggly
|
|
172
173
|
end
|
173
174
|
|
174
175
|
def execute_tests
|
175
|
-
if defined?
|
176
|
+
if defined? Test::Unit::AutoRunner
|
177
|
+
Test::Unit::AutoRunner.run
|
178
|
+
elsif defined? Spec::Runner
|
176
179
|
Spec::Runner.run
|
177
180
|
else
|
178
|
-
Test::Unit
|
181
|
+
raise "Neither RSpec nor Test::Unit were detected"
|
179
182
|
end
|
180
183
|
end
|
181
184
|
|
@@ -13,7 +13,7 @@ grammar Piggly
|
|
13
13
|
procedureHeader
|
14
14
|
name:tIdentifier tSpace?
|
15
15
|
parameters:parameterList tSpace
|
16
|
-
procedureReturn
|
16
|
+
procedureReturn
|
17
17
|
return:tType tSpace kwAS tSpace
|
18
18
|
procedureBody
|
19
19
|
procedureFooter <Procedure>
|
@@ -24,7 +24,7 @@ grammar Piggly
|
|
24
24
|
end
|
25
25
|
|
26
26
|
rule procedureReturn
|
27
|
-
'returns' tSpace 'setof'? <TextNode>
|
27
|
+
'returns' tSpace ( 'setof' tSpace )? <TextNode>
|
28
28
|
end
|
29
29
|
|
30
30
|
rule procedureBody
|
data/lib/piggly/parser/parser.rb
CHANGED
@@ -61,32 +61,28 @@ module Piggly
|
|
61
61
|
elements[5]
|
62
62
|
end
|
63
63
|
|
64
|
-
def
|
64
|
+
def return
|
65
65
|
elements[6]
|
66
66
|
end
|
67
67
|
|
68
|
-
def
|
68
|
+
def tSpace2
|
69
69
|
elements[7]
|
70
70
|
end
|
71
71
|
|
72
|
-
def
|
72
|
+
def kwAS
|
73
73
|
elements[8]
|
74
74
|
end
|
75
75
|
|
76
|
-
def
|
76
|
+
def tSpace3
|
77
77
|
elements[9]
|
78
78
|
end
|
79
79
|
|
80
|
-
def tSpace4
|
81
|
-
elements[10]
|
82
|
-
end
|
83
|
-
|
84
80
|
def procedureBody
|
85
|
-
elements[
|
81
|
+
elements[10]
|
86
82
|
end
|
87
83
|
|
88
84
|
def procedureFooter
|
89
|
-
elements[
|
85
|
+
elements[11]
|
90
86
|
end
|
91
87
|
end
|
92
88
|
|
@@ -125,27 +121,23 @@ module Piggly
|
|
125
121
|
r7 = _nt_procedureReturn
|
126
122
|
s0 << r7
|
127
123
|
if r7
|
128
|
-
r8 =
|
124
|
+
r8 = _nt_tType
|
129
125
|
s0 << r8
|
130
126
|
if r8
|
131
|
-
r9 =
|
127
|
+
r9 = _nt_tSpace
|
132
128
|
s0 << r9
|
133
129
|
if r9
|
134
|
-
r10 =
|
130
|
+
r10 = _nt_kwAS
|
135
131
|
s0 << r10
|
136
132
|
if r10
|
137
|
-
r11 =
|
133
|
+
r11 = _nt_tSpace
|
138
134
|
s0 << r11
|
139
135
|
if r11
|
140
|
-
r12 =
|
136
|
+
r12 = _nt_procedureBody
|
141
137
|
s0 << r12
|
142
138
|
if r12
|
143
|
-
r13 =
|
139
|
+
r13 = _nt_procedureFooter
|
144
140
|
s0 << r13
|
145
|
-
if r13
|
146
|
-
r14 = _nt_procedureFooter
|
147
|
-
s0 << r14
|
148
|
-
end
|
149
141
|
end
|
150
142
|
end
|
151
143
|
end
|
@@ -280,6 +272,12 @@ module Piggly
|
|
280
272
|
def tSpace
|
281
273
|
elements[1]
|
282
274
|
end
|
275
|
+
end
|
276
|
+
|
277
|
+
module ProcedureReturn1
|
278
|
+
def tSpace
|
279
|
+
elements[1]
|
280
|
+
end
|
283
281
|
|
284
282
|
end
|
285
283
|
|
@@ -307,11 +305,24 @@ module Piggly
|
|
307
305
|
r2 = _nt_tSpace
|
308
306
|
s0 << r2
|
309
307
|
if r2
|
308
|
+
i4, s4 = index, []
|
310
309
|
if has_terminal?('setof', false, index)
|
311
|
-
|
310
|
+
r5 = instantiate_node(SyntaxNode,input, index...(index + 5))
|
312
311
|
@index += 5
|
313
312
|
else
|
314
313
|
terminal_parse_failure('setof')
|
314
|
+
r5 = nil
|
315
|
+
end
|
316
|
+
s4 << r5
|
317
|
+
if r5
|
318
|
+
r6 = _nt_tSpace
|
319
|
+
s4 << r6
|
320
|
+
end
|
321
|
+
if s4.last
|
322
|
+
r4 = instantiate_node(SyntaxNode,input, i4...index, s4)
|
323
|
+
r4.extend(ProcedureReturn0)
|
324
|
+
else
|
325
|
+
@index = i4
|
315
326
|
r4 = nil
|
316
327
|
end
|
317
328
|
if r4
|
@@ -324,7 +335,7 @@ module Piggly
|
|
324
335
|
end
|
325
336
|
if s0.last
|
326
337
|
r0 = instantiate_node(TextNode,input, i0...index, s0)
|
327
|
-
r0.extend(
|
338
|
+
r0.extend(ProcedureReturn1)
|
328
339
|
else
|
329
340
|
@index = i0
|
330
341
|
r0 = nil
|
data/lib/piggly/task.rb
CHANGED
@@ -23,7 +23,7 @@ module Piggly
|
|
23
23
|
@test_files = []
|
24
24
|
@proc_files = []
|
25
25
|
@ruby_opts = []
|
26
|
-
@
|
26
|
+
@report_dir = 'piggly/report'
|
27
27
|
@cache_root = 'piggly/cache'
|
28
28
|
@piggly_path = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'bin', 'piggly'))
|
29
29
|
@piggly_opts = ''
|
@@ -47,7 +47,7 @@ module Piggly
|
|
47
47
|
|
48
48
|
ruby opts.join(' ') + ' ' +
|
49
49
|
@piggly_opts + ' ' +
|
50
|
-
%{-o #{quote @
|
50
|
+
%{-o #{quote @report_dir} } +
|
51
51
|
%{-c #{quote @cache_root} } +
|
52
52
|
proc_files.map{|s| %[-s "#{s}" ] }.join +
|
53
53
|
test_files.map{|f| quote(f) }.join(' ')
|
data/lib/piggly/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: piggly
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.
|
4
|
+
version: 1.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kyle Putnam
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-04-
|
12
|
+
date: 2010-04-22 00:00:00 +00:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|