tryouts 0.4.0

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.
@@ -0,0 +1,25 @@
1
+
2
+ class Hash
3
+
4
+ # A depth-first look to find the deepest point in the Hash.
5
+ # The top level Hash is counted in the total so the final
6
+ # number is the depth of its children + 1. An example:
7
+ #
8
+ # ahash = { :level1 => { :level2 => {} } }
9
+ # ahash.deepest_point # => 3
10
+ #
11
+ def deepest_point(h=self, steps=0)
12
+ if h.is_a?(Hash)
13
+ steps += 1
14
+ h.each_pair do |n,possible_h|
15
+ ret = deepest_point(possible_h, steps)
16
+ steps = ret if steps < ret
17
+ end
18
+ else
19
+ return 0
20
+ end
21
+ steps
22
+ end
23
+
24
+ end
25
+
@@ -0,0 +1,2 @@
1
+
2
+ require "tryouts/mixins/hash"
@@ -0,0 +1,171 @@
1
+
2
+ class Tryouts
3
+
4
+ # = Tryout
5
+ #
6
+ # A Tryout is a set of drills (each drill is a test).
7
+ #
8
+ class Tryout
9
+
10
+ # The name of this tryout
11
+ attr_reader :name
12
+
13
+ # A Hash of Dream objects for this Tryout. The keys are drill names.
14
+ attr_accessor :dreams
15
+
16
+ # An Array of Drill objects
17
+ attr_reader :drills
18
+
19
+ # A default value for Drill.dtype
20
+ attr_reader :dtype
21
+
22
+ # For drill type :cli, this is the name of the command to test. It
23
+ # should be a valid method available to a Rye::Box object.
24
+ # For drill type :api, this attribute is ignored.
25
+ attr_reader :command
26
+
27
+ # A block to executed one time before starting the drills
28
+ attr_reader :setup
29
+
30
+ # A block to executed one time before starting the drills
31
+ attr_reader :clean
32
+
33
+ @@valid_dtypes = [:cli, :api]
34
+
35
+ # All :api Drills are run within this context (not used for :cli).
36
+ # Each Drill is executed in a new instance of this class. That means
37
+ # instance variables are not carried through, but class variables are.
38
+ # The before and after blocks are also run in this context.
39
+ class DrillContext; end
40
+
41
+ def initialize(name, dtype, command=nil, *args)
42
+ raise "Must supply command for dtype :cli" if dtype == :cli && command.nil?
43
+ raise "#{dtype} is not a valid drill type" if !@@valid_dtypes.member?(dtype)
44
+ @name, @dtype, @command = name, dtype, command
45
+ @drills = []
46
+ @dreams = {}
47
+ end
48
+
49
+ ## --------------------------------------- EXTERNAL API -----
50
+
51
+ # Populate this Tryout from a block. The block should contain calls to
52
+ # the external DSL methods: dream, drill, xdrill
53
+ def from_block(b=nil, &inline)
54
+ runtime = b.nil? ? inline : b
55
+ instance_eval &runtime
56
+ end
57
+
58
+ # Execute all Drill objects
59
+ def run
60
+ update_drills! # Ensure all drills have all known dreams
61
+ DrillContext.class_eval &setup if setup.is_a?(Proc)
62
+ puts Tryouts::TRYOUT_MSG % @name
63
+ @drills.each do |drill|
64
+ drill.run(DrillContext.new) # Returns true or false
65
+ end
66
+ DrillContext.class_eval &clean if clean.is_a?(Proc)
67
+ end
68
+
69
+ # Prints error output. If there are no errors, it prints nothing.
70
+ def report
71
+ return true if success?
72
+ failed = @drills.select { |d| !d.success? }
73
+ failed.each_with_index do |drill,index|
74
+
75
+ puts '%sERROR %2d/%-2d in "%s"' % [$/, index+1, failed.size, drill.name]
76
+
77
+ if drill.dream
78
+ puts '%24s: %s (expected %s)' % ["response code", drill.reality.rcode, drill.dream.rcode]
79
+ puts '%24s: %s' % ["expected output", drill.dream.output.inspect]
80
+ puts '%24s: %s' % ["actual output", drill.reality.output.inspect]
81
+ if drill.reality.emsg || (drill.reality.emsg != drill.dream.emsg)
82
+ puts '%24s: %s' % ["expected error msg", drill.dream.emsg.inspect]
83
+ puts '%24s: %s' % ["actual error msg", drill.reality.emsg.inspect]
84
+ end
85
+ else
86
+ puts '%24s' % ["[nodream]"]
87
+ if drill.reality.rcode > 0
88
+ puts '%24s: %s' % ["rcode", drill.reality.rcode.inspect]
89
+ puts '%24s: %s' % ["msg", drill.reality.emsg.inspect]
90
+ end
91
+ end
92
+
93
+ if drill.reality.rcode > 0
94
+ puts '%24s: ' % ["backtrace"]
95
+ puts drill.reality.backtrace, $/
96
+ end
97
+ end
98
+ false
99
+ end
100
+
101
+ # Did every Tryout finish successfully?
102
+ def success?
103
+ return @success unless @success.nil?
104
+ # Returns true only when every Tryout result returns true
105
+ @success = !(@drills.collect { |r| r.success? }.member?(false))
106
+ end
107
+
108
+ # Add a Drill object to the list for this Tryout. If there is a dream
109
+ # defined with the same name as the Drill, that dream will be given to
110
+ # the Drill before its added to the list.
111
+ def add_drill(d)
112
+ d.add_dream @dreams[d.name] if !@dreams.nil? && @dreams.has_key?(d.name)
113
+ drills << d if d.is_a?(Tryouts::Drill)
114
+ d
115
+ end
116
+
117
+ # Goes through the list of Drill objects (@drills) and gives each
118
+ # one its associated Dream object (if available).
119
+ #
120
+ # This method is called before Tryout#run, but is only necessary in
121
+ # the case where dreams where loaded after the drills were defined.
122
+ def update_drills!
123
+ return if @dreams.nil?
124
+ @drills.each do |drill|
125
+ next unless @dreams.has_key?(drill.name)
126
+ drill.add_dream @dreams[drill.name]
127
+ end
128
+ end
129
+
130
+ ## --------------------------------------- EXTERNAL DSL -----
131
+
132
+ # A block to executed one time _before_ starting the drills
133
+ def setup(&block)
134
+ return @setup unless block
135
+ @setup = block
136
+ end
137
+
138
+ # A block to executed one time _after_ the drills
139
+ def clean(&block)
140
+ return @clean unless block
141
+ @clean = block
142
+ end
143
+
144
+ # +name+ of the Drill associated to this Dream
145
+ # +output+ A String or Array of expected output. A Dream object will be created using this value (optional)
146
+ # +definition+ is a block which will be run on an instance of Dream
147
+ #
148
+ # NOTE: This method is DSL-only. It's not intended to be used in OO syntax.
149
+ def dream(name, output=nil, format=nil, rcode=0, emsg=nil, &definition)
150
+ if output.nil?
151
+ dobj = Tryouts::Drill::Dream.from_block definition
152
+ else
153
+ dobj = Tryouts::Drill::Dream.new(output)
154
+ dobj.format, dobj.rcode, dobj.emsg = format, rcode, emsg
155
+ end
156
+ @dreams[name] = dobj
157
+ dobj
158
+ end
159
+
160
+ # Create and add a Drill object to the list for this Tryout
161
+ # +name+ is the name of the drill.
162
+ # +args+ is sent directly to the Drill class. The values are specific on the Sergeant.
163
+ def drill(name, *args, &definition)
164
+ args.unshift(@command) if @dtype == :cli
165
+ drill = Tryouts::Drill.new(name, @dtype, *args, &definition)
166
+ add_drill drill
167
+ end
168
+ def xdrill(*args, &b); end # ignore calls to xdrill
169
+
170
+
171
+ end; end
data/lib/tryouts.rb ADDED
@@ -0,0 +1,386 @@
1
+
2
+ require 'rubygems'
3
+ require 'ostruct'
4
+ require 'rye'
5
+ require 'yaml'
6
+ begin; require 'json'; rescue LoadError; end # json may not be installed
7
+
8
+ GYMNASIUM_HOME = File.join(Dir.pwd, 'tryouts')
9
+ GYMNASIUM_GLOB = File.join(GYMNASIUM_HOME, '**', '*_tryouts.rb')
10
+
11
+
12
+ # = Tryouts
13
+ #
14
+ # This class has three purposes:
15
+ # * It represents the Tryouts object which is a group of Tryout objects.
16
+ # * The tryouts and dreams DSLs are executed within its namespace. In general the
17
+ # class methods are the handlers for the DSL syntax (some instance getter methods
18
+ # are modified to support DSL syntax by acting like setters when given arguments)
19
+ # * It stores all known instances of Tryouts objects in a class variable @@instances.
20
+ #
21
+ # ==== Are you ready to run some drills?
22
+ #
23
+ # May all your dreams come true!
24
+ #
25
+ class Tryouts
26
+ # = Exception
27
+ # A generic exception which all other Tryouts exceptions inherit from.
28
+ class Exception < RuntimeError; end
29
+ # = BadDreams
30
+ # Raised when there is a problem loading or parsing a Tryouts::Drill::Dream object
31
+ class BadDreams < Exception; end
32
+
33
+ VERSION = "0.4.0"
34
+
35
+ require 'tryouts/mixins'
36
+ require 'tryouts/tryout'
37
+ require 'tryouts/drill'
38
+
39
+ require 'tryouts/orderedhash'
40
+ HASH_TYPE = (RUBY_VERSION =~ /1.9/) ? ::Hash : Tryouts::OrderedHash
41
+
42
+ TRYOUT_MSG = "\n %s "
43
+ DRILL_MSG = ' %30s: '
44
+ DRILL_ERR = ' %s: '
45
+
46
+ # An Array of +_tryouts.rb+ file paths that have been loaded.
47
+ @@loaded_files = []
48
+ # An Hash of Tryouts instances stored under the name of the Tryouts subclass.
49
+ @@instances = HASH_TYPE.new
50
+
51
+ # The name of this group of Tryout objects
52
+ attr_accessor :group
53
+ # A Symbol representing the default drill type. One of: :cli, :api
54
+ attr_accessor :dtype
55
+ # An Array of file paths which populated this instance of Tryouts
56
+ attr_accessor :paths
57
+ # A Hash of dreams for all tryouts in this class. The keys should
58
+ # match the names of each tryout. The values are hashes will drill
59
+ # names as keys and response
60
+ attr_accessor :dreams
61
+ # An Array of Tryout objects
62
+ attr_accessor :tryouts
63
+ # A Symbol representing the command taking part in the tryouts. For @dtype :cli only.
64
+ attr_accessor :command
65
+ # A Symbol representing the name of the library taking part in the tryouts. For @dtype :api only.
66
+ attr_accessor :library
67
+ # The name of the most recent dreams group (see self.dream)
68
+ attr_accessor :dream_pointer
69
+
70
+ # Returns +@@instances+
71
+ def self.instances; @@instances; end
72
+
73
+ def initialize(group=nil)
74
+ @group = group || "Default Group"
75
+ @tryouts = HASH_TYPE.new
76
+ @paths = []
77
+ @command = nil
78
+ @dreams = HASH_TYPE.new
79
+ @dream_pointer = nil
80
+ end
81
+
82
+ # Populate this Tryouts from a block. The block should contain calls to
83
+ # the external DSL methods: tryout, command, dreams
84
+ def from_block(b, &inline)
85
+ instance_eval &b
86
+ end
87
+
88
+ # Execute Tryout#report for each Tryout in +@tryouts+
89
+ def report
90
+ successes = []
91
+ @tryouts.each_pair { |n,to| successes << to.report }
92
+ puts $/, "All your dreams came true" unless successes.member?(false)
93
+ end
94
+
95
+ # Execute Tryout#run for each Tryout in +@tryouts+
96
+ def run; @tryouts.each_pair { |n,to| to.run }; end
97
+
98
+ # Add a shell command to Rye::Cmd and save the command name
99
+ # in @@commands so it can be used as the default for drills
100
+ def command(name=nil, path=nil)
101
+ return @command if name.nil?
102
+ @command = name.to_sym
103
+ @dtype = :cli
104
+ Rye::Cmd.module_eval do
105
+ define_method(name) do |*args|
106
+ cmd(path || name, *args)
107
+ end
108
+ end
109
+ @command
110
+ end
111
+ # Calls Tryouts#command on the current instance of Tryouts
112
+ #
113
+ # NOTE: this is a standalone DSL-syntax method.
114
+ def self.command(*args)
115
+ @@instances.last.command(*args)
116
+ end
117
+
118
+
119
+ # Require +name+. If +path+ is supplied, it will "require path".
120
+ # * +name+ The name of the library in question (required). Stored as a Symbol to +@library+.
121
+ # * +path+ Add a path to the front of $LOAD_PATH (optional). Use this if you want to load
122
+ # a specific copy of the library. Otherwise, it loads from the system path.
123
+ def library(name=nil, path=nil)
124
+ return @library if name.nil?
125
+ @library = name.to_sym
126
+ @dtype = :api
127
+ $LOAD_PATH.unshift path unless path.nil?
128
+ require @library.to_s
129
+ end
130
+ # Calls Tryouts#library on the current instance of Tryouts
131
+ #
132
+ # NOTE: this is a standalone DSL-syntax method.
133
+ def self.library(*args)
134
+ @@instances.last.library(*args)
135
+ end
136
+
137
+ def group(name=nil)
138
+ return @group if name.nil?
139
+ @group = name unless name.nil?
140
+ # Preload dreams if possible
141
+ dfile = self.class.find_dreams_file(GYMNASIUM_HOME, @group)
142
+ self.load_dreams_file(dfile) if dfile
143
+ @group
144
+ end
145
+ # Raises a Tryouts::Exception. +group+ is not support in the standalone syntax
146
+ # because the group name is taken from the name of the class. See inherited.
147
+ #
148
+ # NOTE: this is a standalone DSL-syntax method.
149
+ def self.group(*args)
150
+ raise "Group is already set: #{@@instances.last.group}"
151
+ end
152
+
153
+ # Create a new Tryout object and add it to the list for this Tryouts class.
154
+ # * +name+ is the name of the Tryout
155
+ # * +type+ is the default drill type for the Tryout. One of: :cli, :api
156
+ # * +command+ when type is :cli, this is the name of the Rye::Box method that we're testing. Otherwise ignored.
157
+ # * +b+ is a block definition for the Tryout. See Tryout#from_block
158
+ #
159
+ # NOTE: This is a DSL-only method and is not intended for OO use.
160
+ def tryout(name, dtype=nil, command=nil, &block)
161
+ return if name.nil?
162
+ dtype ||= @dtype
163
+ command ||= @command if dtype == :cli
164
+ to = find_tryout(name, dtype)
165
+ if to.nil?
166
+ to = Tryouts::Tryout.new(name, dtype, command)
167
+ @tryouts[name] = to
168
+ end
169
+ # Populate the dreams if they've already been loaded
170
+ to.dreams = @dreams[name] if @dreams.has_key?(name)
171
+ # Process the rest of the DSL
172
+ to.from_block block if block
173
+ to
174
+ end
175
+ # Calls Tryouts#tryout on the current instance of Tryouts
176
+ #
177
+ # NOTE: this is a standalone DSL-syntax method.
178
+ def self.tryout(*args, &block)
179
+ @@instances.last.tryout(*args, &block)
180
+ end
181
+
182
+ # Find matching Tryout objects by +name+ and filter by
183
+ # +dtype+ if specified. Returns a Tryout object or nil.
184
+ def find_tryout(name, dtype=nil)
185
+ by_name = @tryouts.values.select { |t| t.name == name }
186
+ by_name = by_name.select { |t| t.dtype == dtype } if dtype
187
+ by_name.first # by_name is an Array. We just want the Object.
188
+ end
189
+
190
+ # This method does nothing. It provides a quick way to disable a tryout.
191
+ #
192
+ # NOTE: This is a DSL-only method and is not intended for OO use.
193
+ def xtryout(*args, &block); end
194
+ # This method does nothing. It provides a quick way to disable a tryout.
195
+ #
196
+ # NOTE: this is a standalone DSL-syntax method.
197
+ def self.xtryout(*args, &block); end
198
+
199
+ # Load dreams from a file or directory or if a block is given
200
+ # it's processed
201
+ # Raises a Tryouts::BadDreams exception when something goes awry.
202
+ #
203
+ # This method is used in two ways:
204
+ # * In the dreams file DSL
205
+ # * As a getter method on a Tryouts object
206
+ def dreams(tryout_name=nil, &definition)
207
+ return @dreams unless tryout_name
208
+
209
+ #
210
+ # dreams "path/2/dreams"
211
+ # OR
212
+ # dreams "path/2/file_of_dreams.rb"
213
+ #
214
+ if File.exists?(tryout_name)
215
+ dfile = tryout_name
216
+ # If we're given a directory we'll build the filename using the class name
217
+ if File.directory?(tryout_name)
218
+ dfile = self.class.find_dreams_file(tryout_name, @group)
219
+ end
220
+ raise BadDreams, "Cannot find dreams file (#{tryout_name})" unless dfile
221
+ @dreams = load_dreams_file( dfile) || {}
222
+
223
+ #
224
+ # dreams "Tryout Name" do
225
+ # dream "drill name" ...
226
+ # end
227
+ #
228
+ elsif tryout_name.kind_of?(String) && definition
229
+ to = find_tryout(tryout_name, @dtype)
230
+
231
+ if to.nil?
232
+ @dream_pointer = tryout_name # Used in Tryouts.dream
233
+ @dreams[ @dream_pointer ] ||= {}
234
+ definition.call
235
+ else
236
+ to.from_block &definition
237
+ end
238
+ else
239
+ raise BadDreams, tryout_name
240
+ end
241
+ @dreams
242
+ end
243
+ # Without arguments, returns a Hash of all known dreams.
244
+ # With arguments, it calls Tryouts#dreams on the current instance of Tryouts.
245
+ #
246
+ # NOTE: this is a standalone DSL-syntax method.
247
+ def self.dreams(*args, &block)
248
+ if args.empty? && block.nil?
249
+ dreams = {}
250
+ @@instances.each_pair do |name,inst|
251
+ dreams[name] = inst.dreams
252
+ end
253
+ return dreams
254
+ else
255
+ # Call the Tryouts#dreams instance method
256
+ @@instances.last.dreams(*args, &block)
257
+ end
258
+ end
259
+
260
+ # +name+ of the Drill associated to this Dream
261
+ # +output+ A String or Array of expected output. A Dream object will be created using this value (optional)
262
+ # +definition+ is a block which will be run on an instance of Dream
263
+ #
264
+ # This method is different than Tryout#dream because this one stores
265
+ # dreams inside an instance variable of the current Tryouts object.
266
+ # This allows for the situation where the dreams block appears before
267
+ # the tryout block. See Tryouts#tryout
268
+ #
269
+ # NOTE: This method is DSL-only. It's not intended to be used in OO syntax.
270
+ def dream(name, output=nil, format=nil, rcode=0, emsg=nil, &definition)
271
+ to = find_tryout(@dream_pointer, @dtype)
272
+ if to.nil?
273
+ if output.nil?
274
+ dobj = Tryouts::Drill::Dream.from_block definition
275
+ else
276
+ dobj = Tryouts::Drill::Dream.new(output)
277
+ dobj.format, dobj.rcode, dobj.emsg = format, rcode, emsg
278
+ end
279
+ @dreams[@dream_pointer][name] = dobj
280
+ else
281
+ # Let the Tryout object process the dream DSL.
282
+ # We'll get here if the dream is placed after
283
+ # the drill with the same name in the same block.
284
+ to.dream name, output, format, rcode, emsg, &definition
285
+ end
286
+ end
287
+ # Calls Tryouts#dream on the current instance of Tryouts
288
+ #
289
+ # NOTE: this is a standalone DSL-syntax method.
290
+ def self.dream(*args, &block)
291
+ @@instances.last.dream(*args, &block)
292
+ end
293
+
294
+ # Populate @@dreams with the content of the file +dpath+.
295
+ #
296
+ # NOTE: this is an OO syntax method
297
+ def load_dreams_file(dpath)
298
+ type = File.extname dpath
299
+ if type == ".yaml" || type == ".yml"
300
+ @dreams = YAML.load_file dpath
301
+ elsif type == ".json" || type == ".js"
302
+ @dreams = JSON.load_file dpath
303
+ elsif type == ".rb"
304
+ @dreams = instance_eval File.read(dpath)
305
+ else
306
+ raise BadDreams, "Unknown kind of dream: #{dpath}"
307
+ end
308
+ @dreams
309
+ end
310
+
311
+ # Parse a +_tryouts.rb+ file. See Tryouts::CLI::Run for an example.
312
+ #
313
+ # NOTE: this is an OO syntax method
314
+ def self.parse_file(fpath)
315
+ raise "No such file: #{fpath}" unless File.exists?(fpath)
316
+ file_content = File.read(fpath)
317
+ to = Tryouts.new
318
+ to.instance_eval file_content, fpath
319
+ if @@instances.has_key? to.group
320
+ to = @@instances[to.group]
321
+ to.instance_eval file_content, fpath
322
+ end
323
+ to.paths << fpath
324
+ @@instances[to.group] = to
325
+ end
326
+
327
+ # Run all Tryout objects in +@tryouts+
328
+ #
329
+ # NOTE: this is an OO syntax method
330
+ def self.run
331
+ @@instances.each_pair do |group, inst|
332
+ inst.tryouts.each_pair do |name,to|
333
+ to.run
334
+ to.report
335
+ STDOUT.flush
336
+ end
337
+ end
338
+ end
339
+
340
+ # Called when a new class inherits from Tryouts. This creates a new instance
341
+ # of Tryouts, sets group to the name of the new class, and adds the instance
342
+ # to +@@instances+.
343
+ #
344
+ # NOTE: this is a standalone DSL-syntax method.
345
+ def self.inherited(klass)
346
+ to = @@instances[ klass ]
347
+ to ||= Tryouts.new
348
+ to.paths << __FILE__
349
+ to.group = klass
350
+ @@instances[to.group] = to
351
+ end
352
+
353
+
354
+ ##---
355
+ ## Is this wacky syntax useful for anything?
356
+ ## t2 :set .
357
+ ## run = "poop"
358
+ ## def self.t2(*args)
359
+ ## OpenStruct.new
360
+ ## end
361
+ ##+++
362
+
363
+ # Find a dreams file in the directory +dir+ based on the current group name.
364
+ # The expected filename format is: groupname_dreams.ext where "groupname" is
365
+ # the lowercase name of the Tryouts group (spaces removed) and "ext" is one
366
+ # of: yaml, js, json, rb.
367
+ #
368
+ # e.g.
369
+ # Tryouts.find_dreams_file "dirpath" # => dirpath/tryouts_dreams.rb
370
+ #
371
+ def self.find_dreams_file(dir, group=nil)
372
+ dpath = nil
373
+ group ||= @@instances.last.group
374
+ group = group.to_s.downcase.tr(' ', '')
375
+ [:rb, :yaml].each do |ext|
376
+ tmp = File.join(dir, "#{group}_dreams.#{ext}")
377
+ if File.exists?(tmp)
378
+ dpath = tmp
379
+ break
380
+ end
381
+ end
382
+ dpath
383
+ end
384
+
385
+
386
+ end
@@ -0,0 +1,19 @@
1
+
2
+ dreams "Common Usage" do
3
+ dream "No args" do
4
+ output inline(%Q{
5
+ Date: 2009-02-16
6
+ Owners: greg, rupaul, telly, prince kinko
7
+ Players: d-bam, alberta, birds, condor man
8
+ })
9
+ end
10
+ dream "YAML Output" do
11
+ format :yaml
12
+ output ({
13
+ "Date" => "2009-02-16",
14
+ "Players" => ["d-bam", "alberta", "birds", "condor man"],
15
+ "Owners" => ["greg", "rupaul", "telly", "prince kinko"]
16
+ })
17
+ end
18
+ end
19
+
@@ -0,0 +1,10 @@
1
+ common usage:
2
+ yaml output:
3
+ :format: :yaml
4
+ :rcode: 0
5
+ no args:
6
+ :rcode: 0
7
+ :output:
8
+ - " Date: 2009-02-16\n"
9
+ - " Players: d-bam, alberta, birds, condor man\n"
10
+ - " Coaches: greg|rupaul|telly|prince kinko\n"
@@ -0,0 +1,26 @@
1
+
2
+ TRYOUTS_HOME = File.expand_path(File.join(File.dirname(__FILE__), ".."))
3
+ MOCKOUT_PATH = File.join(TRYOUTS_HOME, "bin", "mockout")
4
+
5
+ group "mockout cli"
6
+ command :mockout, MOCKOUT_PATH
7
+
8
+ tryout "Common Usage" do
9
+ drill "No Command"
10
+ drill "No args", :info
11
+ drill "YAML Output", :f, :yaml, :info
12
+ drill "JSON Output", :f, :json, :info
13
+ end
14
+
15
+ tryout "inline dream that passes", :cli, :mockout do
16
+ output = ["we expect mockout to", "echo these lines back"]
17
+
18
+ # $ bin/mockout sergeant -e "we expect mockout to" "echo these lines back"
19
+ drill "echo arguments", :info, :e, output[0], output[1]
20
+ dream "echo arguments", output
21
+ end
22
+
23
+ tryout "inline dream that fails", :cli, :mockout do
24
+ dream "echo arguments", "The dream does"
25
+ drill "echo arguments", :info, :e, "not match reality"
26
+ end