piggly 1.2.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +163 -0
- data/Rakefile +29 -15
- data/bin/piggly +4 -244
- data/lib/piggly.rb +19 -17
- data/lib/piggly/command.rb +9 -0
- data/lib/piggly/command/base.rb +148 -0
- data/lib/piggly/command/report.rb +162 -0
- data/lib/piggly/command/test.rb +157 -0
- data/lib/piggly/command/trace.rb +90 -0
- data/lib/piggly/command/untrace.rb +78 -0
- data/lib/piggly/compiler.rb +7 -5
- data/lib/piggly/compiler/cache_dir.rb +119 -0
- data/lib/piggly/compiler/coverage_report.rb +63 -0
- data/lib/piggly/compiler/trace_compiler.rb +105 -0
- data/lib/piggly/config.rb +47 -22
- data/lib/piggly/dumper.rb +9 -0
- data/lib/piggly/dumper/index.rb +121 -0
- data/lib/piggly/dumper/qualified_name.rb +36 -0
- data/lib/piggly/dumper/qualified_type.rb +81 -0
- data/lib/piggly/dumper/reified_procedure.rb +142 -0
- data/lib/piggly/dumper/skeleton_procedure.rb +102 -0
- data/lib/piggly/installer.rb +84 -42
- data/lib/piggly/parser.rb +43 -49
- data/lib/piggly/parser/grammar.tt +289 -313
- data/lib/piggly/parser/nodes.rb +270 -211
- data/lib/piggly/parser/traversal.rb +35 -33
- data/lib/piggly/parser/treetop_ruby19_patch.rb +1 -1
- data/lib/piggly/profile.rb +81 -60
- data/lib/piggly/reporter.rb +5 -18
- data/lib/piggly/reporter/base.rb +103 -0
- data/lib/piggly/reporter/html_dsl.rb +63 -0
- data/lib/piggly/reporter/index.rb +108 -0
- data/lib/piggly/reporter/procedure.rb +104 -0
- data/lib/piggly/reporter/resources/highlight.js +21 -0
- data/lib/piggly/reporter/{piggly.css → resources/piggly.css} +52 -12
- data/lib/piggly/reporter/{sortable.js → resources/sortable.js} +0 -0
- data/lib/piggly/tags.rb +280 -0
- data/lib/piggly/task.rb +191 -40
- data/lib/piggly/util.rb +8 -27
- data/lib/piggly/util/blankslate.rb +114 -0
- data/lib/piggly/util/cacheable.rb +19 -0
- data/lib/piggly/util/enumerable.rb +44 -0
- data/lib/piggly/util/file.rb +17 -0
- data/lib/piggly/util/process_queue.rb +96 -0
- data/lib/piggly/util/thunk.rb +39 -0
- data/lib/piggly/version.rb +8 -8
- data/spec/examples/compiler/cacheable_spec.rb +190 -0
- data/spec/examples/compiler/report_spec.rb +25 -0
- data/spec/{compiler → examples/compiler}/trace_spec.rb +7 -57
- data/spec/examples/config_spec.rb +61 -0
- data/spec/examples/dumper/index_spec.rb +197 -0
- data/spec/examples/dumper/procedure_spec.rb +116 -0
- data/spec/{grammar → examples/grammar}/expression_spec.rb +60 -60
- data/spec/{grammar → examples/grammar}/statements/assignment_spec.rb +15 -15
- data/spec/examples/grammar/statements/declaration_spec.rb +21 -0
- data/spec/{grammar → examples/grammar}/statements/exception_spec.rb +10 -10
- data/spec/{grammar → examples/grammar}/statements/if_spec.rb +47 -34
- data/spec/{grammar → examples/grammar}/statements/loop_spec.rb +5 -5
- data/spec/{grammar → examples/grammar}/statements/sql_spec.rb +11 -11
- data/spec/{grammar → examples/grammar}/tokens/comment_spec.rb +11 -11
- data/spec/{grammar → examples/grammar}/tokens/datatype_spec.rb +14 -8
- data/spec/{grammar → examples/grammar}/tokens/identifier_spec.rb +26 -10
- data/spec/{grammar → examples/grammar}/tokens/keyword_spec.rb +5 -5
- data/spec/{grammar → examples/grammar}/tokens/label_spec.rb +7 -7
- data/spec/{grammar → examples/grammar}/tokens/literal_spec.rb +1 -1
- data/spec/examples/grammar/tokens/lval_spec.rb +50 -0
- data/spec/{grammar → examples/grammar}/tokens/number_spec.rb +1 -1
- data/spec/{grammar → examples/grammar}/tokens/sqlkeywords_spec.rb +1 -1
- data/spec/{grammar → examples/grammar}/tokens/string_spec.rb +9 -9
- data/spec/{grammar → examples/grammar}/tokens/whitespace_spec.rb +1 -1
- data/spec/examples/installer_spec.rb +59 -0
- data/spec/examples/parser/nodes_spec.rb +73 -0
- data/spec/examples/parser/traversal_spec.rb +14 -0
- data/spec/examples/parser_spec.rb +115 -0
- data/spec/examples/profile_spec.rb +153 -0
- data/spec/{reporter/html_spec.rb → examples/reporter/html/dsl_spec.rb} +0 -0
- data/spec/examples/reporter/html/index_spec.rb +0 -0
- data/spec/examples/reporter/html_spec.rb +1 -0
- data/spec/examples/reporter_spec.rb +0 -0
- data/spec/{compiler → examples}/tags_spec.rb +10 -10
- data/spec/examples/task_spec.rb +0 -0
- data/spec/examples/util/cacheable_spec.rb +41 -0
- data/spec/examples/util/enumerable_spec.rb +64 -0
- data/spec/examples/util/file_spec.rb +40 -0
- data/spec/examples/util/process_queue_spec.rb +16 -0
- data/spec/examples/util/thunk_spec.rb +58 -0
- data/spec/examples/version_spec.rb +0 -0
- data/spec/issues/007_spec.rb +25 -0
- data/spec/issues/008_spec.rb +73 -0
- data/spec/issues/018_spec.rb +25 -0
- data/spec/spec_helper.rb +253 -9
- metadata +136 -93
- data/README.markdown +0 -116
- data/lib/piggly/compiler/cache.rb +0 -151
- data/lib/piggly/compiler/pretty.rb +0 -67
- data/lib/piggly/compiler/queue.rb +0 -46
- data/lib/piggly/compiler/tags.rb +0 -244
- data/lib/piggly/compiler/trace.rb +0 -91
- data/lib/piggly/filecache.rb +0 -40
- data/lib/piggly/parser/parser.rb +0 -11794
- data/lib/piggly/reporter/html.rb +0 -207
- data/spec/compiler/cache_spec.rb +0 -9
- data/spec/compiler/pretty_spec.rb +0 -9
- data/spec/compiler/queue_spec.rb +0 -3
- data/spec/compiler/rewrite_spec.rb +0 -3
- data/spec/config_spec.rb +0 -58
- data/spec/filecache_spec.rb +0 -70
- data/spec/fixtures/snippets.sql +0 -158
- data/spec/grammar/tokens/lval_spec.rb +0 -50
- data/spec/parser_spec.rb +0 -8
- data/spec/profile_spec.rb +0 -5
@@ -1,48 +1,50 @@
|
|
1
1
|
module Piggly
|
2
|
+
module Parser
|
2
3
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
4
|
+
#
|
5
|
+
# Routines for traversing a tree; assumes base class defines elements
|
6
|
+
# as a method that returns a list of child nodes
|
7
|
+
#
|
8
|
+
module Traversal
|
9
|
+
def inject(init) # :yields: NodeClass => init
|
10
|
+
if elements
|
11
|
+
elements.inject(yield(init, self)) do |state, e|
|
12
|
+
e.inject(state){|succ, n| yield(succ, n) }
|
13
|
+
end
|
14
|
+
else
|
15
|
+
yield(init, self)
|
12
16
|
end
|
13
|
-
else
|
14
|
-
yield(init, self)
|
15
17
|
end
|
16
|
-
end
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
19
|
+
def count # :yields: NodeClass => boolean
|
20
|
+
inject(0){|sum, e| yield(e) ? sum + 1 : sum }
|
21
|
+
end
|
21
22
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
23
|
+
def find # :yields: NodeClass => boolean
|
24
|
+
found = false
|
25
|
+
catch :done do
|
26
|
+
inject(nil) do |_,e|
|
27
|
+
if yield(e)
|
28
|
+
found = e
|
29
|
+
throw :done
|
30
|
+
end
|
29
31
|
end
|
30
32
|
end
|
33
|
+
found
|
31
34
|
end
|
32
|
-
found
|
33
|
-
end
|
34
35
|
|
35
|
-
|
36
|
-
|
37
|
-
|
36
|
+
def select # :yields: NodeClass => boolean
|
37
|
+
inject([]){|list,e| yield(e) ? list << e : list }
|
38
|
+
end
|
38
39
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
40
|
+
def flatten # :yields: NodeClass
|
41
|
+
if block_given?
|
42
|
+
inject([]){|list,e| list << yield(e) }
|
43
|
+
else
|
44
|
+
inject([]){|list,e| list << e }
|
45
|
+
end
|
44
46
|
end
|
45
47
|
end
|
46
|
-
end
|
47
48
|
|
49
|
+
end
|
48
50
|
end
|
data/lib/piggly/profile.rb
CHANGED
@@ -4,84 +4,105 @@ module Piggly
|
|
4
4
|
# Collection of all Tags
|
5
5
|
#
|
6
6
|
class Profile
|
7
|
-
PATTERN = /WARNING: #{Config.trace_prefix} (#{Tag::PATTERN})(?: (.))?/
|
8
7
|
|
9
|
-
|
8
|
+
def initialize
|
9
|
+
@by_id = {}
|
10
|
+
@by_cache = {}
|
11
|
+
@by_procedure = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
# Register a procedure and its list of tags
|
15
|
+
def add(procedure, tags, cache = nil)
|
16
|
+
tags.each{|t| @by_id[t.id] = t }
|
17
|
+
@by_cache[cache] = tags unless cache.nil?
|
18
|
+
@by_procedure[procedure.oid] = tags
|
19
|
+
end
|
10
20
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
21
|
+
def [](object)
|
22
|
+
case object
|
23
|
+
when String
|
24
|
+
@by_id[object] or
|
25
|
+
raise "No tag with id #{object}"
|
26
|
+
when Dumper::ReifiedProcedure,
|
27
|
+
Dumper::SkeletonProcedure
|
28
|
+
@by_procedure[object.oid] or
|
29
|
+
raise "No tags for procedure #{object.signature}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Record the execution of a coverage tag
|
34
|
+
def ping(tag_id, value=nil)
|
35
|
+
self[tag_id].ping(value)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Summarizes coverage for each type of tag (branch, block, loop)
|
39
|
+
# @return [Hash<Symbol, Hash[:count => Integer, :percent => Float]>]
|
40
|
+
def summary(procedure = nil)
|
41
|
+
tags =
|
42
|
+
if procedure
|
43
|
+
if @by_procedure.include?(procedure.oid)
|
44
|
+
@by_procedure[procedure.oid]
|
16
45
|
else
|
17
|
-
|
46
|
+
[]
|
18
47
|
end
|
48
|
+
else
|
49
|
+
@by_id.values
|
19
50
|
end
|
20
|
-
end
|
21
51
|
|
22
|
-
|
23
|
-
def add(path, tags, cache = nil)
|
24
|
-
tags.each{|t| by_id[t.id] = t }
|
25
|
-
by_file[path] = tags
|
26
|
-
by_cache[cache] = tags if cache
|
27
|
-
end
|
52
|
+
grouped = Util::Enumerable.group_by(tags){|x| x.type }
|
28
53
|
|
29
|
-
|
30
|
-
|
31
|
-
|
54
|
+
summary = Hash.new{|h,k| h[k] = Hash.new }
|
55
|
+
grouped.each do |type, ts|
|
56
|
+
summary[type][:count] = ts.size
|
57
|
+
summary[type][:percent] = Util::Enumerable.sum(ts){|x| x.to_f } / ts.size
|
32
58
|
end
|
33
59
|
|
34
|
-
|
35
|
-
|
36
|
-
@by_file ||= Hash.new
|
37
|
-
end
|
60
|
+
summary
|
61
|
+
end
|
38
62
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
63
|
+
# Resets each tag's coverage stats
|
64
|
+
def clear
|
65
|
+
@by_id.values.each{|x| x.clear }
|
66
|
+
end
|
43
67
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
else
|
49
|
-
raise "No tag with id #{tag_id}, perhaps the proc was not compiled with Piggly::Installer.trace_proc, or it has been recompiled with new tag IDs."
|
50
|
-
end
|
51
|
-
end
|
68
|
+
# Write coverage stats to the disk cache
|
69
|
+
def store
|
70
|
+
@by_cache.each{|cache, tags| cache[:tags] = tags }
|
71
|
+
end
|
52
72
|
|
53
|
-
|
54
|
-
|
55
|
-
|
73
|
+
def empty?(tags)
|
74
|
+
tags.all?{|t| t.to_f.zero? }
|
75
|
+
end
|
56
76
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
grouped = {}
|
62
|
-
end
|
63
|
-
else
|
64
|
-
grouped = map.group_by{|t| t.type }
|
65
|
-
end
|
77
|
+
# @return [String]
|
78
|
+
def difference(procedure, tags)
|
79
|
+
current = Util::Enumerable.group_by(@by_procedure[procedure.oid]){|x| x.type }
|
80
|
+
previous = Util::Enumerable.group_by(tags){|x| x.type }
|
66
81
|
|
67
|
-
|
68
|
-
|
69
|
-
summary[type][:percent] = ts.sum{|t| t.to_f } / ts.size
|
70
|
-
end
|
82
|
+
current.default = []
|
83
|
+
previous.default = []
|
71
84
|
|
72
|
-
|
73
|
-
|
85
|
+
(current.keys | previous.keys).map do |type|
|
86
|
+
pct = Util::Enumerable.sum(current[type]){|x| x.to_f } / current[type].size -
|
87
|
+
Util::Enumerable.sum(previous[type]){|x| x.to_f } / previous[type].size
|
74
88
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
end
|
89
|
+
"#{"%+0.1f" % pct}% #{type}"
|
90
|
+
end.join(", ")
|
91
|
+
end
|
79
92
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
93
|
+
# Build a notice processor function that records each tag execution
|
94
|
+
# @return [Proc]
|
95
|
+
def notice_processor(config, stderr = $stderr)
|
96
|
+
pattern = /#{config.trace_prefix} (#{Tags::AbstractTag::PATTERN})(?: (.))?/
|
84
97
|
|
98
|
+
lambda do |message|
|
99
|
+
if m = pattern.match(message)
|
100
|
+
ping(m.captures[0], m.captures[1])
|
101
|
+
else
|
102
|
+
stderr.puts(message)
|
103
|
+
end
|
104
|
+
end
|
85
105
|
end
|
86
106
|
end
|
107
|
+
|
87
108
|
end
|
data/lib/piggly/reporter.rb
CHANGED
@@ -1,21 +1,8 @@
|
|
1
1
|
module Piggly
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
# Copy each file to Config.report_root
|
9
|
-
def self.install(*files)
|
10
|
-
files.each do |file|
|
11
|
-
src = File.join(File.dirname(__FILE__), 'reporter', file)
|
12
|
-
dst = report_path(file)
|
13
|
-
|
14
|
-
File.open(dst, 'w') {|f| f.write File.read(src) }
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
2
|
+
module Reporter
|
3
|
+
autoload :HtmlDsl, "piggly/reporter/html_dsl"
|
4
|
+
autoload :Base, "piggly/reporter/base"
|
5
|
+
autoload :Index, "piggly/reporter/index"
|
6
|
+
autoload :Procedure, "piggly/reporter/procedure"
|
18
7
|
end
|
19
8
|
end
|
20
|
-
|
21
|
-
require File.join(File.dirname(__FILE__), *%w[reporter html])
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module Piggly
|
2
|
+
module Reporter
|
3
|
+
|
4
|
+
class Base
|
5
|
+
include HtmlDsl
|
6
|
+
|
7
|
+
def initialize(config)
|
8
|
+
@config = config
|
9
|
+
end
|
10
|
+
|
11
|
+
# Copy each file to @config.report_root
|
12
|
+
def install(*files)
|
13
|
+
files.each do |name|
|
14
|
+
src = File.join(File.dirname(__FILE__), name)
|
15
|
+
dst = report_path(name)
|
16
|
+
|
17
|
+
File.open(dst, "w"){|io| io.write(File.read(src)) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def report_path(file=nil, ext=nil)
|
22
|
+
unless file.nil?
|
23
|
+
# Remove the original extension from +file+ and add given extension
|
24
|
+
@config.mkpath(@config.report_root, ext ?
|
25
|
+
File.basename(file, ".*") + ext :
|
26
|
+
File.basename(file))
|
27
|
+
else
|
28
|
+
@config.mkpath(@config.report_root)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def aggregate(label, summary)
|
35
|
+
tag :p, label, :class => "summary"
|
36
|
+
tag :table, :class => "summary sortable" do
|
37
|
+
tag :tr do
|
38
|
+
tag :th, "Blocks"
|
39
|
+
tag :th, "Loops"
|
40
|
+
tag :th, "Branches"
|
41
|
+
tag :th, "Block Coverage"
|
42
|
+
tag :th, "Loop Coverage"
|
43
|
+
tag :th, "Branch Coverage"
|
44
|
+
end
|
45
|
+
|
46
|
+
tag :tr, :class => "even" do
|
47
|
+
unless summary.include?(:block) or summary.include?(:loop) or summary.include?(:branch)
|
48
|
+
# Parser couldn't parse this file
|
49
|
+
tag(:td, :class => "count") { tag :span, -1, :style => "display:none" }
|
50
|
+
tag(:td, :class => "count") { tag :span, -1, :style => "display:none" }
|
51
|
+
tag(:td, :class => "count") { tag :span, -1, :style => "display:none" }
|
52
|
+
tag(:td, :class => "pct") { tag :span, -1, :style => "display:none" }
|
53
|
+
tag(:td, :class => "pct") { tag :span, -1, :style => "display:none" }
|
54
|
+
tag(:td, :class => "pct") { tag :span, -1, :style => "display:none" }
|
55
|
+
else
|
56
|
+
tag(:td, (summary[:block][:count] || 0), :class => "count")
|
57
|
+
tag(:td, (summary[:loop][:count] || 0), :class => "count")
|
58
|
+
tag(:td, (summary[:branch][:count] || 0), :class => "count")
|
59
|
+
tag(:td, :class => "pct") { percent(summary[:block][:percent]) }
|
60
|
+
tag(:td, :class => "pct") { percent(summary[:loop][:percent]) }
|
61
|
+
tag(:td, :class => "pct") { percent(summary[:branch][:percent]) }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def percent(pct)
|
68
|
+
if pct
|
69
|
+
tag :table, :align => "center" do
|
70
|
+
tag :tr do
|
71
|
+
tag :td, "%0.2f%% " % pct, :class => "num"
|
72
|
+
|
73
|
+
style =
|
74
|
+
case pct.to_f
|
75
|
+
when 0...50; "low"
|
76
|
+
when 0...100; "mid"
|
77
|
+
else "high"
|
78
|
+
end
|
79
|
+
|
80
|
+
tag :td, :class => "graph" do
|
81
|
+
if pct
|
82
|
+
tag :table, :align => "right", :class => "graph #{style}" do
|
83
|
+
tag :tr do
|
84
|
+
tag :td, :class => "covered", :width => (pct/2.0).to_i
|
85
|
+
tag :td, :class => "uncovered", :width => ((100-pct)/2.0).to_i
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
else
|
93
|
+
tag :span, -1, :style => "display:none"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def timestamp
|
98
|
+
tag :div, "Generated by piggly #{Piggly::VERSION} at #{Time.now.strftime("%B %d, %Y %H:%M %Z")}", :class => "timestamp"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Piggly
|
2
|
+
module Reporter
|
3
|
+
|
4
|
+
#
|
5
|
+
# Markup DSL
|
6
|
+
#
|
7
|
+
module HtmlDsl
|
8
|
+
unless defined? HTML_REPLACE
|
9
|
+
HTML_REPLACE = { "&" => "&", '"' => """, ">" => ">", "<" => "<" }
|
10
|
+
HTML_PATTERN = /[&"<>]/
|
11
|
+
end
|
12
|
+
|
13
|
+
def html(output = "")
|
14
|
+
begin
|
15
|
+
@htmltag_output, htmltag_output = output, @htmltag_output
|
16
|
+
# @todo: doctype
|
17
|
+
yield
|
18
|
+
ensure
|
19
|
+
# restore
|
20
|
+
@htmltag_output = htmltag_output
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def tag(name, content = nil, attributes = {})
|
25
|
+
if content.is_a?(Hash) and attributes.empty?
|
26
|
+
content, attributes = nil, content
|
27
|
+
end
|
28
|
+
|
29
|
+
attributes = attributes.inject("") do |string, pair|
|
30
|
+
k, v = pair
|
31
|
+
string << %[ #{k}="#{v}"]
|
32
|
+
end
|
33
|
+
|
34
|
+
if content.nil?
|
35
|
+
if block_given?
|
36
|
+
@htmltag_output << "<#{name}#{attributes}>"
|
37
|
+
yield
|
38
|
+
@htmltag_output << "</#{name}>"
|
39
|
+
else
|
40
|
+
@htmltag_output << "<#{name}#{attributes}/>"
|
41
|
+
end
|
42
|
+
else
|
43
|
+
@htmltag_output << "<#{name}#{attributes}>#{content.to_s}</#{name}>"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
if "".respond_to?(:fast_xs)
|
48
|
+
def e(string)
|
49
|
+
string.fast_xs
|
50
|
+
end
|
51
|
+
elsif "".respond_to?(:to_xs)
|
52
|
+
def e(string)
|
53
|
+
string.to_xs
|
54
|
+
end
|
55
|
+
else
|
56
|
+
def e(string)
|
57
|
+
string.gsub(HTML_PATTERN) {|c| HTML_REPLACE[c] }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|