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,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
@@ -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
@@ -0,0 +1,15 @@
1
+ module Piggly
2
+ module VERSION
3
+ MAJOR = 2
4
+ MINOR = 3
5
+ TINY = 3
6
+
7
+ RELEASE_DATE = "2025-12-12"
8
+ end
9
+
10
+ class << VERSION
11
+ def to_s
12
+ [VERSION::MAJOR, VERSION::MINOR, VERSION::TINY].join(".")
13
+ end
14
+ end
15
+ 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
+