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.
- checksums.yaml +7 -0
- data/README.md +170 -0
- data/Rakefile +33 -0
- data/bin/piggly +8 -0
- data/lib/piggly/command/base.rb +148 -0
- data/lib/piggly/command/report.rb +162 -0
- data/lib/piggly/command/trace.rb +90 -0
- data/lib/piggly/command/untrace.rb +78 -0
- data/lib/piggly/command.rb +8 -0
- 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 +117 -0
- data/lib/piggly/compiler.rb +7 -0
- data/lib/piggly/config.rb +80 -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 +141 -0
- data/lib/piggly/dumper/reified_procedure.rb +172 -0
- data/lib/piggly/dumper/skeleton_procedure.rb +112 -0
- data/lib/piggly/dumper.rb +9 -0
- data/lib/piggly/installer.rb +137 -0
- data/lib/piggly/parser/grammar.tt +748 -0
- data/lib/piggly/parser/nodes.rb +378 -0
- data/lib/piggly/parser/traversal.rb +50 -0
- data/lib/piggly/parser/treetop_ruby19_patch.rb +21 -0
- data/lib/piggly/parser.rb +69 -0
- data/lib/piggly/profile.rb +108 -0
- data/lib/piggly/reporter/base.rb +106 -0
- data/lib/piggly/reporter/html_dsl.rb +63 -0
- data/lib/piggly/reporter/index.rb +114 -0
- data/lib/piggly/reporter/procedure.rb +129 -0
- data/lib/piggly/reporter/resources/highlight.js +38 -0
- data/lib/piggly/reporter/resources/piggly.css +515 -0
- data/lib/piggly/reporter/resources/sortable.js +493 -0
- data/lib/piggly/reporter.rb +8 -0
- data/lib/piggly/tags.rb +280 -0
- data/lib/piggly/task.rb +215 -0
- 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/util.rb +9 -0
- data/lib/piggly/version.rb +15 -0
- data/lib/piggly.rb +20 -0
- data/spec/examples/compiler/cacheable_spec.rb +190 -0
- data/spec/examples/compiler/report_spec.rb +25 -0
- data/spec/examples/compiler/trace_spec.rb +123 -0
- data/spec/examples/config_spec.rb +63 -0
- data/spec/examples/dumper/index_spec.rb +199 -0
- data/spec/examples/dumper/procedure_spec.rb +116 -0
- data/spec/examples/grammar/expression_spec.rb +302 -0
- data/spec/examples/grammar/statements/assignment_spec.rb +70 -0
- data/spec/examples/grammar/statements/declaration_spec.rb +21 -0
- data/spec/examples/grammar/statements/exception_spec.rb +78 -0
- data/spec/examples/grammar/statements/if_spec.rb +191 -0
- data/spec/examples/grammar/statements/loop_spec.rb +41 -0
- data/spec/examples/grammar/statements/sql_spec.rb +71 -0
- data/spec/examples/grammar/tokens/comment_spec.rb +58 -0
- data/spec/examples/grammar/tokens/datatype_spec.rb +58 -0
- data/spec/examples/grammar/tokens/identifier_spec.rb +74 -0
- data/spec/examples/grammar/tokens/keyword_spec.rb +44 -0
- data/spec/examples/grammar/tokens/label_spec.rb +40 -0
- data/spec/examples/grammar/tokens/literal_spec.rb +30 -0
- data/spec/examples/grammar/tokens/lval_spec.rb +50 -0
- data/spec/examples/grammar/tokens/number_spec.rb +34 -0
- data/spec/examples/grammar/tokens/sqlkeywords_spec.rb +45 -0
- data/spec/examples/grammar/tokens/string_spec.rb +54 -0
- data/spec/examples/grammar/tokens/whitespace_spec.rb +40 -0
- 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 +118 -0
- data/spec/examples/profile_spec.rb +153 -0
- data/spec/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/examples/tags_spec.rb +285 -0
- 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 +59 -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/issues/028_spec.rb +48 -0
- data/spec/issues/032_spec.rb +98 -0
- data/spec/issues/036_spec.rb +41 -0
- data/spec/spec_helper.rb +312 -0
- data/spec/spec_suite.rb +5 -0
- metadata +162 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
module Piggly
|
|
2
|
+
module Util
|
|
3
|
+
|
|
4
|
+
#
|
|
5
|
+
# Executes blocks in parallel subprocesses
|
|
6
|
+
#
|
|
7
|
+
class ProcessQueue
|
|
8
|
+
|
|
9
|
+
def self.concurrent=(count)
|
|
10
|
+
@concurrent = count
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.concurrent
|
|
14
|
+
@concurrent || 1
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def initialize(concurrent = self.class.concurrent)
|
|
18
|
+
@concurrent, @items = concurrent, []
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def concurrent=(value)
|
|
22
|
+
@concurrent = value
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def size
|
|
26
|
+
@items.size
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def queue(&block)
|
|
30
|
+
@items << block
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
alias add queue
|
|
34
|
+
|
|
35
|
+
def execute
|
|
36
|
+
# Test if fork is supported
|
|
37
|
+
forkable =
|
|
38
|
+
begin
|
|
39
|
+
Process.wait(Process.fork { exit! 60 })
|
|
40
|
+
raise unless $?.exitstatus.to_i == 60
|
|
41
|
+
true
|
|
42
|
+
rescue Exception
|
|
43
|
+
false
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
if forkable
|
|
47
|
+
concurrently
|
|
48
|
+
else
|
|
49
|
+
serially
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
protected
|
|
54
|
+
|
|
55
|
+
def serially
|
|
56
|
+
$stderr.puts "ProcessQueue running serially"
|
|
57
|
+
|
|
58
|
+
while block = @items.shift
|
|
59
|
+
block.call
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def concurrently
|
|
64
|
+
$stderr.puts "ProcessQueue running concurrently"
|
|
65
|
+
active = 0
|
|
66
|
+
|
|
67
|
+
# enable enterprise ruby feature
|
|
68
|
+
GC.copy_on_write_friendly = true if GC.respond_to?(:copy_on_write_friendly=)
|
|
69
|
+
|
|
70
|
+
while block = @items.shift
|
|
71
|
+
if active >= @concurrent
|
|
72
|
+
pid = Process.wait
|
|
73
|
+
active -= 1
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# use exit! to avoid auto-running any test suites
|
|
77
|
+
Process.fork do
|
|
78
|
+
begin
|
|
79
|
+
block.call
|
|
80
|
+
exit! 0
|
|
81
|
+
rescue Exception
|
|
82
|
+
$stderr.puts $!
|
|
83
|
+
$stderr.puts "\t" + $!.backtrace.join("\n\t")
|
|
84
|
+
exit! 1
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
active += 1
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
Process.waitall
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module Piggly
|
|
2
|
+
module Util
|
|
3
|
+
|
|
4
|
+
# @todo: Ruby 1.9 BasicObject
|
|
5
|
+
begin
|
|
6
|
+
BlankSlate
|
|
7
|
+
rescue NameError
|
|
8
|
+
require "piggly/util/blankslate"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
#
|
|
12
|
+
# Wraps a computation and delays its evaluation until
|
|
13
|
+
# a message is sent to it. Computation can be forced by
|
|
14
|
+
# calling `force!`
|
|
15
|
+
#
|
|
16
|
+
class Thunk < BlankSlate
|
|
17
|
+
def initialize(&block)
|
|
18
|
+
@block = block
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def force!
|
|
22
|
+
unless @block.nil?
|
|
23
|
+
@value = @block.call
|
|
24
|
+
@block = nil
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
@value
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def thunk?
|
|
31
|
+
true
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def method_missing(name, *args, &block)
|
|
35
|
+
force!.send(name, *args, &block)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
data/lib/piggly/util.rb
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
module Piggly
|
|
2
|
+
module Util
|
|
3
|
+
autoload :ProcessQueue, "piggly/util/process_queue"
|
|
4
|
+
autoload :Thunk, "piggly/util/thunk"
|
|
5
|
+
autoload :Cacheable, "piggly/util/cacheable"
|
|
6
|
+
autoload :Enumerable, "piggly/util/enumerable"
|
|
7
|
+
autoload :File, "piggly/util/file"
|
|
8
|
+
end
|
|
9
|
+
end
|
data/lib/piggly.rb
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require "erb"
|
|
2
|
+
require "yaml"
|
|
3
|
+
require "optparse"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
require "digest/md5"
|
|
6
|
+
require "set"
|
|
7
|
+
|
|
8
|
+
module Piggly
|
|
9
|
+
autoload :VERSION, "piggly/version"
|
|
10
|
+
autoload :Config, "piggly/config"
|
|
11
|
+
autoload :Command, "piggly/command"
|
|
12
|
+
autoload :Compiler, "piggly/compiler"
|
|
13
|
+
autoload :Dumper, "piggly/dumper"
|
|
14
|
+
autoload :Parser, "piggly/parser"
|
|
15
|
+
autoload :Profile, "piggly/profile"
|
|
16
|
+
autoload :Installer, "piggly/installer"
|
|
17
|
+
autoload :Reporter, "piggly/reporter"
|
|
18
|
+
autoload :Tags, "piggly/tags"
|
|
19
|
+
autoload :Util, "piggly/util"
|
|
20
|
+
end
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module Piggly
|
|
4
|
+
|
|
5
|
+
=begin
|
|
6
|
+
describe Util::Cacheable do
|
|
7
|
+
before do
|
|
8
|
+
@compiler = Class.new { include Piggly::Util::Cacheable }
|
|
9
|
+
@compiler.stub(:name).and_return('TestCompiler')
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
describe "stale?" do
|
|
13
|
+
it "compares cache_path with source path and cache_sources" do
|
|
14
|
+
@compiler.stub(:cache_sources).
|
|
15
|
+
and_return(%w(parser.rb grammar.tt nodes.rb))
|
|
16
|
+
|
|
17
|
+
@compiler.should_receive(:cache_path).
|
|
18
|
+
with('source.sql').
|
|
19
|
+
and_return('source.cache')
|
|
20
|
+
|
|
21
|
+
Util::File.should_receive(:stale?).
|
|
22
|
+
with('source.cache', 'source.sql', 'parser.rb', 'grammar.tt', 'nodes.rb')
|
|
23
|
+
|
|
24
|
+
@compiler.stale?('source.sql')
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
describe "cache" do
|
|
29
|
+
before do
|
|
30
|
+
@procedure = mock('procedure')
|
|
31
|
+
@procedure.stub(:source_path).and_return('source path')
|
|
32
|
+
@procedure.stub(:source).and_return('SOURCE CODE')
|
|
33
|
+
@procedure.stub(:name).and_return('f')
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
context "when cache is stale" do
|
|
37
|
+
before do
|
|
38
|
+
@compiler.should_receive(:stale?).
|
|
39
|
+
and_return(true)
|
|
40
|
+
|
|
41
|
+
File.should_receive(:read).
|
|
42
|
+
with(@procedure.source_path).
|
|
43
|
+
and_return(@procedure.source)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "parses the procedure source" do
|
|
47
|
+
@compiler.stub(:compile).
|
|
48
|
+
and_return(mock('result').as_null_object)
|
|
49
|
+
Compiler::Cacheable::CacheDirectory.stub(:lookup).
|
|
50
|
+
and_return(mock('cache').as_null_object)
|
|
51
|
+
|
|
52
|
+
Parser.should_receive(:parse).
|
|
53
|
+
with(@procedure.source)
|
|
54
|
+
|
|
55
|
+
@compiler.cache(@procedure)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it "passes the parse tree and transient arguments to the compiler" do
|
|
59
|
+
tree = mock('parse tree').as_null_object
|
|
60
|
+
args = %w(a b c)
|
|
61
|
+
block = lambda{|a,b| b }
|
|
62
|
+
|
|
63
|
+
Parser.stub(:parse).and_return(tree)
|
|
64
|
+
Compiler::Cacheable::CacheDirectory.stub(:lookup).
|
|
65
|
+
and_return(mock('cache').as_null_object)
|
|
66
|
+
|
|
67
|
+
# calling cache method below should pass the parse tree plus any
|
|
68
|
+
# arguments given to cache along to the abstract 'compile' method
|
|
69
|
+
@compiler.should_receive(:compile).
|
|
70
|
+
with(tree, *args.push(block)).
|
|
71
|
+
and_return(mock('result').as_null_object)
|
|
72
|
+
|
|
73
|
+
@compiler.cache(@procedure, *args, &block)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
it "updates the cache with the results from the compiler" do
|
|
77
|
+
cache = mock('cache')
|
|
78
|
+
result = mock('result')
|
|
79
|
+
|
|
80
|
+
Parser.stub(:parse).
|
|
81
|
+
and_return(mock('parse tree').as_null_object)
|
|
82
|
+
@compiler.should_receive(:compile).
|
|
83
|
+
# with parse tree
|
|
84
|
+
and_return(result)
|
|
85
|
+
|
|
86
|
+
Compiler::Cacheable::CacheDirectory.should_receive(:lookup).
|
|
87
|
+
and_return(cache)
|
|
88
|
+
cache.should_receive(:replace).
|
|
89
|
+
with(result)
|
|
90
|
+
|
|
91
|
+
@compiler.cache(@procedure)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
it "returns the cache object" do
|
|
95
|
+
Parser.stub(:parse).
|
|
96
|
+
and_return(mock('parse tree').as_null_object)
|
|
97
|
+
@compiler.should_receive(:compile).
|
|
98
|
+
and_return(mock('result'))
|
|
99
|
+
|
|
100
|
+
cache = mock('cache')
|
|
101
|
+
cache.stub(:replace)
|
|
102
|
+
|
|
103
|
+
Compiler::Cacheable::CacheDirectory.should_receive(:lookup).
|
|
104
|
+
and_return(cache)
|
|
105
|
+
|
|
106
|
+
@compiler.cache(@procedure).should == cache
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
context "when cache is fresh" do
|
|
111
|
+
before do
|
|
112
|
+
@compiler.should_receive(:stale?).
|
|
113
|
+
and_return(false)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
it "returns the cached results from disk" do
|
|
117
|
+
cache = mock('cache')
|
|
118
|
+
|
|
119
|
+
Compiler::Cacheable::CacheDirectory.should_receive(:lookup).
|
|
120
|
+
and_return(cache)
|
|
121
|
+
|
|
122
|
+
@compiler.cache(@procedure).should == cache
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
describe Compiler::Cacheable::CacheDirectory do
|
|
129
|
+
before do
|
|
130
|
+
@cache = Compiler::Cacheable::CacheDirectory.new('directory-path')
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
describe "[]=" do
|
|
134
|
+
it "stores the new entry" do
|
|
135
|
+
@cache.stub(:write)
|
|
136
|
+
@cache[:foo] = 'data'
|
|
137
|
+
@cache[:foo].should == 'data'
|
|
138
|
+
@cache['foo'].should == 'data'
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
it "writes through to disk" do
|
|
142
|
+
@cache.should_receive(:write).
|
|
143
|
+
with('foo' => 'data')
|
|
144
|
+
@cache['foo'] = 'data'
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
describe "update" do
|
|
149
|
+
it "stores new entries" do
|
|
150
|
+
@cache.stub(:write)
|
|
151
|
+
@cache.update(:abc => 'abacus', :xyz => 'xylophone')
|
|
152
|
+
@cache[:abc].should == 'abacus'
|
|
153
|
+
@cache[:xyz].should == 'xylophone'
|
|
154
|
+
@cache['abc'].should == 'abacus'
|
|
155
|
+
@cache['xyz'].should == 'xylophone'
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
it "stores updated entries"
|
|
159
|
+
it "writes through to disk"
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
describe "replace" do
|
|
163
|
+
it "stores new entries"
|
|
164
|
+
it "stores updated entries"
|
|
165
|
+
it "removes previous entries"
|
|
166
|
+
it "writes through to disk"
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
describe "clear" do
|
|
170
|
+
it "removes all entries"
|
|
171
|
+
it "writes through to disk"
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
describe "[]" do
|
|
175
|
+
context "when entry is not already in memory" do
|
|
176
|
+
it "reads the entry from disk"
|
|
177
|
+
it "stores the entry in memory"
|
|
178
|
+
it "returns the associated value"
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
context "when entry is already in memory" do
|
|
182
|
+
it "does not read the entry from disk"
|
|
183
|
+
it "returns the associated value"
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
end
|
|
188
|
+
=end
|
|
189
|
+
|
|
190
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module Piggly
|
|
4
|
+
|
|
5
|
+
describe Compiler::CoverageReport do
|
|
6
|
+
describe "compile" do
|
|
7
|
+
context "when trace cache is stale" do
|
|
8
|
+
it "does not request the trace compiler output"
|
|
9
|
+
it "returns nil"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
context "when trace cache is fresh" do
|
|
13
|
+
it "recurses the children of non-terminal node"
|
|
14
|
+
it "does not recurse terminal nodes"
|
|
15
|
+
|
|
16
|
+
it "marks tagged terminal nodes"
|
|
17
|
+
it "does not mark untagged terminal nodes"
|
|
18
|
+
|
|
19
|
+
it "marks tagged non-terminal nodes"
|
|
20
|
+
it "does not mark untagged non-terminal nodes"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
end
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'ostruct'
|
|
3
|
+
|
|
4
|
+
module Piggly
|
|
5
|
+
|
|
6
|
+
=begin
|
|
7
|
+
describe Compiler::Trace, "with terminal root node" do
|
|
8
|
+
before do
|
|
9
|
+
@tree = N.terminal('code')
|
|
10
|
+
@compiler = Compiler::Trace.new('file.sql')
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it "compiles to original terminal" do
|
|
14
|
+
code, _ = @compiler.compile(@tree)
|
|
15
|
+
code.should eql('code')
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "compiles to single block sequence" do
|
|
19
|
+
code, data = @compiler.compile(@tree)
|
|
20
|
+
blocks = data[:blocks].flatten
|
|
21
|
+
|
|
22
|
+
blocks.size.should eql(1)
|
|
23
|
+
blocks.first.should eql([:root, 0..code.size - 1])
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
describe Compiler::Trace, "with regular root node" do
|
|
28
|
+
before do
|
|
29
|
+
@tree = N.node N.terminal('statement'),
|
|
30
|
+
N.terminal('statement'),
|
|
31
|
+
N.node(N.terminal('statement'),
|
|
32
|
+
N.terminal('statement')),
|
|
33
|
+
N.terminal('statement'),
|
|
34
|
+
N.terminal('statement')
|
|
35
|
+
@compiler = Compiler::Trace.new('file.sql')
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it "compiles" do
|
|
39
|
+
code, _ = @compiler.compile(@tree)
|
|
40
|
+
code.should eql('statement' * 6)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "flattens" do
|
|
44
|
+
code, data = @compiler.compile(@tree)
|
|
45
|
+
blocks = data[:blocks].flatten
|
|
46
|
+
blocks.size.should eql(1)
|
|
47
|
+
|
|
48
|
+
# compiled code has no added instrumentation
|
|
49
|
+
blocks.first.should eql([:root, 0..code.size - 1])
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
describe Compiler::Trace, "root node contains branches" do
|
|
54
|
+
before do
|
|
55
|
+
@tree = N.node N.node(N.terminal('statement-1;'), N.space),
|
|
56
|
+
N.branch(N.node(N.terminal('if-1'), N.space),
|
|
57
|
+
N.node(N.terminal('condition'), N.space),
|
|
58
|
+
N.node(N.terminal('then'), N.space),
|
|
59
|
+
N.node(N.terminal('consequence'), N.space),
|
|
60
|
+
N.node(N.terminal('end if;'), N.space)),
|
|
61
|
+
N.node(N.terminal('statement-2;'), N.space),
|
|
62
|
+
N.branch(N.node(N.terminal('if-2'), N.space),
|
|
63
|
+
N.node(N.terminal('condition'), N.space),
|
|
64
|
+
N.node(N.terminal('then'), N.space),
|
|
65
|
+
N.node(N.terminal('consequence'), N.space),
|
|
66
|
+
N.terminal('end if;'))
|
|
67
|
+
@tree.interval # force computation
|
|
68
|
+
@compiler = Compiler::Trace.new('file.sql')
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it "flattens" do
|
|
72
|
+
code, data = @compiler.compile(@tree)
|
|
73
|
+
blocks = data[:blocks].flatten
|
|
74
|
+
|
|
75
|
+
first = blocks.select{|id, interval| id == 1 }.map{|e| e.last }
|
|
76
|
+
first.size.should eql(1)
|
|
77
|
+
'consequence'.size.should eql(first[0].end - first[0].begin)
|
|
78
|
+
|
|
79
|
+
second = blocks.select{|id, interval| id == 2 }.map{|e| e.last }
|
|
80
|
+
second.size.should eql(1)
|
|
81
|
+
'consequence'.size.should eql(second[0].end - second[0].begin)
|
|
82
|
+
|
|
83
|
+
roots = blocks.select{|id, interval| id == :root }.map{|e| e.last }
|
|
84
|
+
roots.size.should eql(3) # root <consequence> root <consequence> root
|
|
85
|
+
|
|
86
|
+
# compiled code size = source code size + instrumentation code size
|
|
87
|
+
roots.first.begin.should eql(0)
|
|
88
|
+
roots.last.end.should eql(code.size - ("raise notice 'PIGGLY-file.sql-n';\n".size * 2) - 1)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
it "compiles" do
|
|
92
|
+
code, _ = @compiler.compile(@tree)
|
|
93
|
+
code.should eql(%w[statement-1;
|
|
94
|
+
if-1 condition then
|
|
95
|
+
raise notice 'PIGGLY-file.sql-1';\n
|
|
96
|
+
consequence
|
|
97
|
+
end if;
|
|
98
|
+
statement-2;
|
|
99
|
+
if-2 condition then
|
|
100
|
+
raise notice 'PIGGLY-file.sql-2';\n
|
|
101
|
+
consequence
|
|
102
|
+
end if;].join(' ').gsub('\n ', "\n"))
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it "prepends Config.trace_prefix to raise statements" do
|
|
106
|
+
Config.trace_prefix = 'PREFIX'
|
|
107
|
+
|
|
108
|
+
code, _ = @compiler.compile(@tree)
|
|
109
|
+
code.should eql(%w[statement-1;
|
|
110
|
+
if-1 condition then
|
|
111
|
+
raise notice 'PREFIX-file.sql-1';\n
|
|
112
|
+
consequence
|
|
113
|
+
end if;
|
|
114
|
+
statement-2;
|
|
115
|
+
if-2 condition then
|
|
116
|
+
raise notice 'PREFIX-file.sql-2';\n
|
|
117
|
+
consequence
|
|
118
|
+
end if;].join(' ').gsub('\n ', "\n"))
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
=end
|
|
122
|
+
|
|
123
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module Piggly
|
|
4
|
+
|
|
5
|
+
describe Config do
|
|
6
|
+
before do
|
|
7
|
+
@config = Config.new
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it "has class accessors and mutators" do
|
|
11
|
+
expect(@config).to respond_to(:cache_root)
|
|
12
|
+
expect(@config).to respond_to(:cache_root=)
|
|
13
|
+
expect(@config).to respond_to(:report_root)
|
|
14
|
+
expect(@config).to respond_to(:report_root=)
|
|
15
|
+
expect(@config).to respond_to(:trace_prefix)
|
|
16
|
+
expect(@config).to respond_to(:trace_prefix=)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "has default values" do
|
|
20
|
+
# @config.cache_root = nil
|
|
21
|
+
expect(@config.cache_root).not_to be_nil
|
|
22
|
+
expect(@config.cache_root).to match(/cache$/)
|
|
23
|
+
|
|
24
|
+
# @config.report_root = nil
|
|
25
|
+
expect(@config.report_root).not_to be_nil
|
|
26
|
+
expect(@config.report_root).to match(/reports$/)
|
|
27
|
+
|
|
28
|
+
# @config.trace_prefix = nil
|
|
29
|
+
expect(@config.trace_prefix).not_to be_nil
|
|
30
|
+
expect(@config.trace_prefix).to eq('PIGGLY')
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
describe "path" do
|
|
34
|
+
it "doesn't reparent absolute paths" do
|
|
35
|
+
expect(@config.path('/tmp', '/usr/bin/ps')).to eq('/usr/bin/ps')
|
|
36
|
+
expect(@config.path('A:/data/tmp', 'C:/USER/tmp')).to eq('C:/USER/tmp')
|
|
37
|
+
expect(@config.path('/tmp/data', '../data.txt')).to eq('../data.txt')
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "reparents relative paths" do
|
|
41
|
+
expect(@config.path('/tmp', 'note.txt')).to eq('/tmp/note.txt')
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "doesn't require path parameter" do
|
|
45
|
+
expect(@config.path('/tmp')).to eq('/tmp')
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
describe "mkpath" do
|
|
50
|
+
it "creates root if doesn't exist" do
|
|
51
|
+
expect(FileUtils).to receive(:makedirs).with('x/y').once.and_return(true)
|
|
52
|
+
@config.mkpath('x/y', 'z')
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it "throws an error on path components that exist as files" do
|
|
56
|
+
skip "Test requires /etc/passwd to exist (Unix-specific)" if RUBY_PLATFORM =~ /mingw|mswin|windows/i
|
|
57
|
+
expect { @config.mkpath('/etc/passwd/file') }.to raise_error(Errno::EEXIST)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
end
|
|
63
|
+
|