piggly-nsd 2.3.3

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.
Files changed (96) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +170 -0
  3. data/Rakefile +33 -0
  4. data/bin/piggly +8 -0
  5. data/lib/piggly/command/base.rb +148 -0
  6. data/lib/piggly/command/report.rb +162 -0
  7. data/lib/piggly/command/trace.rb +90 -0
  8. data/lib/piggly/command/untrace.rb +78 -0
  9. data/lib/piggly/command.rb +8 -0
  10. data/lib/piggly/compiler/cache_dir.rb +119 -0
  11. data/lib/piggly/compiler/coverage_report.rb +63 -0
  12. data/lib/piggly/compiler/trace_compiler.rb +117 -0
  13. data/lib/piggly/compiler.rb +7 -0
  14. data/lib/piggly/config.rb +80 -0
  15. data/lib/piggly/dumper/index.rb +121 -0
  16. data/lib/piggly/dumper/qualified_name.rb +36 -0
  17. data/lib/piggly/dumper/qualified_type.rb +141 -0
  18. data/lib/piggly/dumper/reified_procedure.rb +172 -0
  19. data/lib/piggly/dumper/skeleton_procedure.rb +112 -0
  20. data/lib/piggly/dumper.rb +9 -0
  21. data/lib/piggly/installer.rb +137 -0
  22. data/lib/piggly/parser/grammar.tt +748 -0
  23. data/lib/piggly/parser/nodes.rb +378 -0
  24. data/lib/piggly/parser/traversal.rb +50 -0
  25. data/lib/piggly/parser/treetop_ruby19_patch.rb +21 -0
  26. data/lib/piggly/parser.rb +69 -0
  27. data/lib/piggly/profile.rb +108 -0
  28. data/lib/piggly/reporter/base.rb +106 -0
  29. data/lib/piggly/reporter/html_dsl.rb +63 -0
  30. data/lib/piggly/reporter/index.rb +114 -0
  31. data/lib/piggly/reporter/procedure.rb +129 -0
  32. data/lib/piggly/reporter/resources/highlight.js +38 -0
  33. data/lib/piggly/reporter/resources/piggly.css +515 -0
  34. data/lib/piggly/reporter/resources/sortable.js +493 -0
  35. data/lib/piggly/reporter.rb +8 -0
  36. data/lib/piggly/tags.rb +280 -0
  37. data/lib/piggly/task.rb +215 -0
  38. data/lib/piggly/util/blankslate.rb +114 -0
  39. data/lib/piggly/util/cacheable.rb +19 -0
  40. data/lib/piggly/util/enumerable.rb +44 -0
  41. data/lib/piggly/util/file.rb +17 -0
  42. data/lib/piggly/util/process_queue.rb +96 -0
  43. data/lib/piggly/util/thunk.rb +39 -0
  44. data/lib/piggly/util.rb +9 -0
  45. data/lib/piggly/version.rb +15 -0
  46. data/lib/piggly.rb +20 -0
  47. data/spec/examples/compiler/cacheable_spec.rb +190 -0
  48. data/spec/examples/compiler/report_spec.rb +25 -0
  49. data/spec/examples/compiler/trace_spec.rb +123 -0
  50. data/spec/examples/config_spec.rb +63 -0
  51. data/spec/examples/dumper/index_spec.rb +199 -0
  52. data/spec/examples/dumper/procedure_spec.rb +116 -0
  53. data/spec/examples/grammar/expression_spec.rb +302 -0
  54. data/spec/examples/grammar/statements/assignment_spec.rb +70 -0
  55. data/spec/examples/grammar/statements/declaration_spec.rb +21 -0
  56. data/spec/examples/grammar/statements/exception_spec.rb +78 -0
  57. data/spec/examples/grammar/statements/if_spec.rb +191 -0
  58. data/spec/examples/grammar/statements/loop_spec.rb +41 -0
  59. data/spec/examples/grammar/statements/sql_spec.rb +71 -0
  60. data/spec/examples/grammar/tokens/comment_spec.rb +58 -0
  61. data/spec/examples/grammar/tokens/datatype_spec.rb +58 -0
  62. data/spec/examples/grammar/tokens/identifier_spec.rb +74 -0
  63. data/spec/examples/grammar/tokens/keyword_spec.rb +44 -0
  64. data/spec/examples/grammar/tokens/label_spec.rb +40 -0
  65. data/spec/examples/grammar/tokens/literal_spec.rb +30 -0
  66. data/spec/examples/grammar/tokens/lval_spec.rb +50 -0
  67. data/spec/examples/grammar/tokens/number_spec.rb +34 -0
  68. data/spec/examples/grammar/tokens/sqlkeywords_spec.rb +45 -0
  69. data/spec/examples/grammar/tokens/string_spec.rb +54 -0
  70. data/spec/examples/grammar/tokens/whitespace_spec.rb +40 -0
  71. data/spec/examples/installer_spec.rb +59 -0
  72. data/spec/examples/parser/nodes_spec.rb +73 -0
  73. data/spec/examples/parser/traversal_spec.rb +14 -0
  74. data/spec/examples/parser_spec.rb +118 -0
  75. data/spec/examples/profile_spec.rb +153 -0
  76. data/spec/examples/reporter/html/dsl_spec.rb +0 -0
  77. data/spec/examples/reporter/html/index_spec.rb +0 -0
  78. data/spec/examples/reporter/html_spec.rb +1 -0
  79. data/spec/examples/reporter_spec.rb +0 -0
  80. data/spec/examples/tags_spec.rb +285 -0
  81. data/spec/examples/task_spec.rb +0 -0
  82. data/spec/examples/util/cacheable_spec.rb +41 -0
  83. data/spec/examples/util/enumerable_spec.rb +64 -0
  84. data/spec/examples/util/file_spec.rb +40 -0
  85. data/spec/examples/util/process_queue_spec.rb +16 -0
  86. data/spec/examples/util/thunk_spec.rb +59 -0
  87. data/spec/examples/version_spec.rb +0 -0
  88. data/spec/issues/007_spec.rb +25 -0
  89. data/spec/issues/008_spec.rb +73 -0
  90. data/spec/issues/018_spec.rb +25 -0
  91. data/spec/issues/028_spec.rb +48 -0
  92. data/spec/issues/032_spec.rb +98 -0
  93. data/spec/issues/036_spec.rb +41 -0
  94. data/spec/spec_helper.rb +312 -0
  95. data/spec/spec_suite.rb +5 -0
  96. metadata +162 -0
@@ -0,0 +1,106 @@
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" 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 }
50
+ tag(:td, :class => "count") { tag :span, -1 }
51
+ tag(:td, :class => "count") { tag :span, -1 }
52
+ tag(:td, :class => "pct") { tag :span, -1 }
53
+ tag(:td, :class => "pct") { tag :span, -1 }
54
+ tag(:td, :class => "pct") { tag :span, -1 }
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; "zero"
76
+ when 0...30; "low"
77
+ when 0...60; "mid"
78
+ when 0...99; "high"
79
+ else "full"
80
+ end
81
+
82
+ tag :td, :class => "graph" do
83
+ if pct
84
+ tag :table, :align => "right", :class => "graph #{style}" do
85
+ tag :tr do
86
+ covered_width = (pct/2.0).round
87
+ tag :td, :class => "covered", :width => covered_width
88
+ tag :td, :class => "uncovered", :width => (50 - covered_width)
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
95
+ else
96
+ tag :span, -1, :style => "display:none"
97
+ end
98
+ end
99
+
100
+ def timestamp
101
+ tag :div, "Generated by piggly #{Piggly::VERSION} at #{Time.now.strftime("%B %d, %Y %H:%M %Z")}", :class => "timestamp"
102
+ end
103
+ end
104
+
105
+ end
106
+ 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 = { "&" => "&amp;", '"' => "&quot;", ">" => "&gt;", "<" => "&lt;" }
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
@@ -0,0 +1,114 @@
1
+ module Piggly
2
+ module Reporter
3
+
4
+ class Index < Base
5
+
6
+ def initialize(config, profile)
7
+ @config, @profile = config, profile
8
+ end
9
+
10
+ def report(procedures, index)
11
+ io = File.open("#{report_path}/index.html", "w")
12
+
13
+ html(io) do
14
+ tag :html do
15
+ tag :head do
16
+ tag :title, "Piggly PL/pgSQL Code Coverage"
17
+ tag :meta, :charset => "utf-8"
18
+ tag :link, :rel => "stylesheet", :type => "text/css", :href => "piggly.css"
19
+ tag :script, "<!-- -->", :type => "text/javascript", :src => "sortable.js"
20
+ end
21
+
22
+ tag :body do
23
+ aggregate("PL/pgSQL Coverage Summary", @profile.summary)
24
+ table(procedures.sort_by{|p| index.label(p) }, index)
25
+ timestamp
26
+ end
27
+ end
28
+ end
29
+ ensure
30
+ io.close
31
+ end
32
+
33
+ private
34
+
35
+ def table(procedures, index)
36
+ tag :div, :class => "table-wrapper" do
37
+ tag :table, :class => "summary sortable" do
38
+ tag :tr do
39
+ tag :th, "Procedure"
40
+ tag :th, "Blocks"
41
+ tag :th, "Loops"
42
+ tag :th, "Branches"
43
+ tag :th, "Block Coverage"
44
+ tag :th, "Loop Coverage"
45
+ tag :th, "Branch Coverage"
46
+ end
47
+
48
+ procedures.each_with_index do |procedure, k|
49
+ summary = @profile.summary(procedure)
50
+ row = k.modulo(2) == 0 ? "even" : "odd"
51
+ label = index.label(procedure)
52
+
53
+ tag :tr, :class => row do
54
+ unless summary.include?(:block) or summary.include?(:loop) or summary.include?(:branch)
55
+ # Parser couldn't parse this file
56
+ tag :td, label, :class => "file fail"
57
+ tag(:td, :class => "count") { tag :span, -1 }
58
+ tag(:td, :class => "count") { tag :span, -1 }
59
+ tag(:td, :class => "count") { tag :span, -1 }
60
+ tag(:td, :class => "pct") { tag :span, -1 }
61
+ tag(:td, :class => "pct") { tag :span, -1 }
62
+ tag(:td, :class => "pct") { tag :span, -1 }
63
+ else
64
+ tag(:td, :class => "file") { tag :a, label, :href => procedure.identifier + ".html" }
65
+ tag :td, (summary[:block][:count] || 0), :class => "count"
66
+ tag :td, (summary[:loop][:count] || 0), :class => "count"
67
+ tag :td, (summary[:branch][:count] || 0), :class => "count"
68
+ tag(:td, :class => "pct") { percent(summary[:block][:percent]) }
69
+ tag(:td, :class => "pct") { percent(summary[:loop][:percent]) }
70
+ tag(:td, :class => "pct") { percent(summary[:branch][:percent]) }
71
+ end
72
+ end
73
+
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ def percent(pct)
80
+ if pct
81
+ tag :table, :align => "center" do
82
+ tag :tr do
83
+ tag :td, "%0.2f%%&nbsp;" % pct, :class => "num"
84
+
85
+ style =
86
+ case pct.to_f
87
+ when 0; "zero"
88
+ when 0...30; "low"
89
+ when 0...60; "mid"
90
+ when 0...99; "high"
91
+ else "full"
92
+ end
93
+
94
+ tag :td, :class => "graph" do
95
+ if pct
96
+ tag :table, :align => "right", :class => "graph #{style}" do
97
+ tag :tr do
98
+ covered_width = (pct/2.0).round
99
+ tag :td, :class => "covered", :width => covered_width
100
+ tag :td, :class => "uncovered", :width => (50 - covered_width)
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
107
+ else
108
+ tag :span, -1, :style => "display:none"
109
+ end
110
+ end
111
+ end
112
+
113
+ end
114
+ end
@@ -0,0 +1,129 @@
1
+ module Piggly
2
+ module Reporter
3
+
4
+ class Procedure < Base
5
+
6
+ def initialize(config, profile)
7
+ @config, @profile = config, profile
8
+ end
9
+
10
+ def report(procedure)
11
+ io = File.open(report_path(procedure.source_path(@config), ".html"), "w")
12
+
13
+ begin
14
+ compiler = Compiler::CoverageReport.new(@config)
15
+ data = compiler.compile(procedure, @profile)
16
+
17
+ html(io) do
18
+ tag :html, :xmlns => "http://www.w3.org/1999/xhtml" do
19
+ tag :head do
20
+ tag :title, "Code Coverage: #{procedure.name}"
21
+ tag :meta, :charset => "utf-8"
22
+ tag :link, :rel => "stylesheet", :type => "text/css", :href => "piggly.css"
23
+ tag :script, "<!-- -->", :type => "text/javascript", :src => "highlight.js"
24
+ end
25
+
26
+ tag :body do
27
+ tag :div, :class => "header" do
28
+ aggregate(procedure.name, @profile.summary(procedure))
29
+ end
30
+
31
+ tag :div, :class => "container" do
32
+ tag :div, :class => "listing" do
33
+ tag :table do
34
+ tag :tr do
35
+ tag :td, "&nbsp;", :class => "signature"
36
+ tag :td, signature(procedure), :class => "signature"
37
+ end
38
+
39
+ tag :tr do
40
+ tag :td, data[:lines].to_a.map{|n| %[<a href="#L#{n}" id="L#{n}">#{n}</a>] }.join("\n"), :class => "lines"
41
+ tag :td, data[:html], :class => "code"
42
+ end
43
+ end
44
+ end
45
+
46
+ toc(@profile[procedure])
47
+ end
48
+
49
+ tag :a, "Return to index", :href => "index.html", :class => "return"
50
+
51
+ timestamp
52
+
53
+ end
54
+ end
55
+ end
56
+ ensure
57
+ io.close
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ def signature(procedure)
64
+ keyword = procedure.prokind == "p" ? "procedure" : "function"
65
+ string = "<span class='tK'>create function</span> <b><span class='tI'>#{procedure.name}</span></b>"
66
+
67
+ if procedure.arg_names.size <= 1
68
+ string << " ( "
69
+ separator = ", "
70
+ spacer = " "
71
+ else
72
+ string << "\n\t( "
73
+ separator = ",\n\t "
74
+ spacer = "\t"
75
+ end
76
+
77
+ arguments = procedure.arg_types.zip(procedure.arg_modes, procedure.arg_names).map do |atype, amode, aname|
78
+ amode &&= "<span class='tK'>#{amode.downcase}</span>#{spacer}"
79
+ aname &&= "<span class='tI'>#{aname}</span>#{spacer}"
80
+ "#{amode}#{aname}<span class='tD'>#{atype}</span>"
81
+ end.join(separator)
82
+
83
+ string << arguments << " )"
84
+
85
+ if procedure.prokind != "p"
86
+ string << "\n<span class='tK'>returns#{procedure.setof ? ' setof' : ''}</span>"
87
+
88
+ if procedure.type.table?
89
+ fields = procedure.type.types.zip(procedure.type.names).map do |rtype, rname|
90
+ rname = "<span class='tI'>#{rname}</span>\t"
91
+ rtype = "<span class='tD'>#{rtype}</span>"
92
+ "#{rname}#{rtype}"
93
+ end.join(",\n\t")
94
+
95
+ string << " <span class='tK'>table</span> (\n\t" << fields << " )"
96
+ else
97
+ string << " <span class='tD'>#{procedure.type.shorten}</span>"
98
+ end
99
+ end
100
+
101
+ string << "\n <span class='tK'>language #{procedure.language}</span>"
102
+ string << "\n <span class='tK'>security definer</span>" if procedure.secdef
103
+ string << "\n <span class='tK'>strict</span>" if procedure.strict
104
+ string << "\n <span class='tK'>#{procedure.volatility.downcase}</span>" if procedure.prokind != "p"
105
+
106
+ string
107
+ end
108
+
109
+ def toc(tags)
110
+ todo = tags.reject{|t| t.complete? }
111
+
112
+ tag :div, :class => 'toc' do
113
+ tag :strong, 'Notes'
114
+
115
+ tag :ol do
116
+ todo.each do |t|
117
+ tag(:li, :class => t.type) do
118
+ tag :a, t.description, :href => "#T#{t.id}",
119
+ :onMouseOver => "highlight('T#{t.id}')"
120
+ end
121
+ end
122
+ end unless todo.empty?
123
+ end
124
+ end
125
+
126
+ end
127
+
128
+ end
129
+ end
@@ -0,0 +1,38 @@
1
+
2
+ var lastId = null;
3
+ var lastBg = null;
4
+
5
+ function highlight(id) {
6
+ var el = document.getElementById(id);
7
+ if (el) {
8
+ if (lastId)
9
+ unhighlight(lastId, lastBg);
10
+
11
+ lastId = id;
12
+ lastBg = el.style.backgroundColor;
13
+
14
+ el.style.backgroundColor = 'yellow';
15
+ }
16
+ }
17
+
18
+ function unhighlight(id, bg) {
19
+ var el = document.getElementById(id);
20
+ if (el) { el.style.backgroundColor = bg; }
21
+ }
22
+
23
+ document.addEventListener('DOMContentLoaded', function() {
24
+ var toc = document.getElementsByClassName('toc');
25
+ var lis = document.getElementsByClassName('listing');
26
+ if (toc.length != 1 || lis.length != 1)
27
+ return;
28
+
29
+ window.onscroll = function() {
30
+ if (lis[0].getBoundingClientRect().y <= 0) {
31
+ toc[0].classList.add('toc-fixed');
32
+ lis[0].classList.add('listing-fixed');
33
+ } else {
34
+ toc[0].classList.remove('toc-fixed');
35
+ lis[0].classList.remove('listing-fixed');
36
+ }
37
+ }
38
+ });