delano-tryouts 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES.txt CHANGED
@@ -1,5 +1,20 @@
1
1
  TRYOUTS, CHANGES
2
2
 
3
+
4
+ #### 0.6.0 (2009-06-24) ###############################
5
+
6
+ NOTE: External dreams files are no longer supported.
7
+ NOTE: command testing (:cli) is disabled.
8
+
9
+ * CHANGE: Removed all mention of external dreams files.
10
+ * CHANGE: dream method is not available inside drill block.
11
+ * CHANGE: rcode, emsg, backtrace renamed to ecode, error, trace
12
+ * ADDED: dream is now available above drill blocks.
13
+ * ADDED: Better verbose and debugging output
14
+ * ADDED: Cleaner test output
15
+ * ADDED: drill success is now solely based on output.
16
+
17
+
3
18
  #### 0.5.1 (2009-06-13) ###############################
4
19
 
5
20
  * ADDED: dream method is now available inside drill block
data/README.rdoc CHANGED
@@ -1,7 +1,8 @@
1
- = Tryouts - v0.5 BETA
1
+ = Tryouts - v0.6 BETA
2
2
 
3
3
  Tryouts is a high-level testing library for your command-line applications and Ruby codes.
4
4
 
5
+ NOTE: The DSL syntax has changed significantly from 0.5 and command-line app tests are currently disabled.
5
6
 
6
7
  == Terminology
7
8
 
@@ -11,41 +12,6 @@ Tryouts is a bit different than other testing libraries. Test definitions are or
11
12
  * Drill: a test.
12
13
  * Dream: the expected outcome of a drill.
13
14
 
14
-
15
- == Testing a command-line application (a CLI)
16
-
17
- Tryouts tests command-line applications by comparing expected output with the actual output. Let's say we have an executable called mockout and we want to test the following commands:
18
-
19
- $ bin/executable
20
- $ bin/executable -f yaml
21
-
22
- The tryout definition would look like this:
23
-
24
- command :executable, "path/2/executable"
25
-
26
- tryout "Common Usage" do
27
- drill "No Command"
28
- drill "YAML Output", :f, 'yaml'
29
- end
30
-
31
- And the expected output would be defined like this:
32
-
33
- dream "No Command" do
34
- output inline(%Q{
35
- Date: 2009-02-16
36
- Players: d-bam, alberta, birds, condor man
37
- Owners: greg, rupaul, telly, prince kinko
38
- })
39
- end
40
- dream "YAML Output" do
41
- format :yaml
42
- output ({
43
- "Date" => "2009-02-16",
44
- "Players" => ["d-bam", "alberta", "birds", "condor man"],
45
- "Owners" => ["greg", "rupaul", "telly", "prince kinko"]
46
- })
47
- end
48
-
49
15
  == Testing Ruby codes (an API)
50
16
 
51
17
  Tryouts employs the same approach for testing Ruby codes. The return value of the drill block is compared to the expectation defined by the dream. Here is an example of including dreams inside the tryout definition.
@@ -53,17 +19,22 @@ Tryouts employs the same approach for testing Ruby codes. The return value of th
53
19
  library :caesars, LIBRARY_PATH
54
20
 
55
21
  tryout "Common Usage" do
56
- dream "Some Maths", 3
57
- drill "Some Maths" do
22
+ dream 3
23
+ drill "Check the return value" do
58
24
  12 / 4
59
25
  end
60
26
 
61
- dream "A Block", Proc, :class # Test the class type instead of the value
62
- drill "A Block" do
27
+ dream :class, :Proc
28
+ drill "Check the return value class" do
63
29
  Proc.new do
64
30
  :anything
65
31
  end
66
32
  end
33
+
34
+ dream :exception, NameError
35
+ drill "A test can pass based on the exception" do
36
+ raise NameError
37
+ end
67
38
  end
68
39
 
69
40
 
@@ -75,23 +46,9 @@ This library is very new (est. 2009-05-19) and has not been vetted by the scruti
75
46
  * Unexpected errors.
76
47
 
77
48
 
78
- == 3 Ways to define tryouts
79
-
80
- There are three ways to define an instance of this class:
81
- * In +_tryouts.rb+ files using the DSL syntax. One file per Tryouts object.
82
- * See: http://github.com/delano/tryouts/blob/master/tryouts/mockoutcli_tryouts.rb
83
- * In standalone ruby files using a hybrid DSL/OO syntax. Supports multiple
84
- Tryouts objects per file.
85
- * See: http://github.com/delano/tryouts/blob/master/tryouts/standalone_test.rb
86
- * With regular object-oriented syntax.
87
- * See: http://tryouts.rubyforge.org/
88
-
89
49
  == On Threads
90
50
 
91
- Tryouts does some funky stuff to make it simple to write tests. This "funky
92
- stuff" means that this library is *not thread-safe at definition-time*. However, once
93
- all tryouts files are parsed (or in OO-syntax, once all objects are created), this
94
- class should be *thread-safe at drill-time*.
51
+ Tryouts does some funky stuff to make it simple to write tests. This "funky stuff" means that this library is *not thread-safe at definition-time*. However, once all tryouts files are parsed (or in OO-syntax, once all objects are created), this class should be *thread-safe at drill-time*.
95
52
 
96
53
  == More Info
97
54
 
@@ -101,7 +58,7 @@ class should be *thread-safe at drill-time*.
101
58
 
102
59
  == Thanks
103
60
 
104
- * Everyone at Utrecht.rb
61
+ * Everyone at Utrecht.rb and Amsterdam.rb
105
62
 
106
63
  == Credits
107
64
 
data/bin/sergeant CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  TRYOUTS_HOME = File.expand_path(File.join(File.dirname(__FILE__), '..'))
4
4
 
5
- local_libs = %w{tryouts net-scp amazon-ec2 aws-s3 caesars drydock rye storable sysinfo annoy}
6
- local_libs.each { |dir| $:.unshift File.join(TRYOUTS_HOME, '..', dir, 'lib') }
5
+ #local_libs = %w{tryouts net-scp amazon-ec2 aws-s3 caesars drydock rye storable sysinfo annoy}
6
+ #local_libs.each { |dir| $:.unshift File.join(TRYOUTS_HOME, '..', dir, 'lib') }
7
7
 
8
8
  require 'drydock'
9
9
  require 'tryouts'
@@ -15,7 +15,7 @@ require 'tryouts/cli'
15
15
  module TryoutsCLI
16
16
  extend Drydock
17
17
 
18
- debug :on
18
+ debug :off
19
19
  default :run, :with_args
20
20
 
21
21
  global :q, :quiet, "Decrease output"
data/lib/tryouts.rb CHANGED
@@ -4,7 +4,7 @@ require 'rye'
4
4
  require 'yaml'
5
5
  begin; require 'json'; rescue LoadError; end # json may not be installed
6
6
 
7
- GYMNASIUM_HOME = File.join(Dir.pwd, 'tryouts')
7
+ GYMNASIUM_HOME = File.join(Dir.pwd, 'tryouts') ## also check try (for rye)
8
8
  GYMNASIUM_GLOB = File.join(GYMNASIUM_HOME, '**', '*_tryouts.rb')
9
9
 
10
10
 
@@ -29,7 +29,7 @@ class Tryouts
29
29
  # Raised when there is a problem loading or parsing a Tryouts::Drill::Dream object
30
30
  class BadDreams < Exception; end
31
31
 
32
- VERSION = "0.5.1"
32
+ VERSION = "0.6.0"
33
33
 
34
34
  require 'tryouts/mixins'
35
35
  require 'tryouts/tryout'
@@ -49,6 +49,17 @@ class Tryouts
49
49
  # An instance of SysInfo
50
50
  @@sysinfo = SysInfo.new
51
51
 
52
+ @@debug = false
53
+ @@verbose = 0
54
+
55
+ def self.debug?; @@debug; end
56
+ def self.enable_debug; @@debug = true; end
57
+ def self.disable_debug; @@debug = false; end
58
+
59
+ def self.verbose; @@verbose; end
60
+ def self.verbose=(v); @@verbose = (v == true) ? 1 : v; end
61
+
62
+
52
63
  # Returns +@@instances+
53
64
  def self.instances; @@instances; end
54
65
  # Returns +@@sysinfo+
@@ -60,26 +71,18 @@ class Tryouts
60
71
  attr_accessor :dtype
61
72
  # An Array of file paths which populated this instance of Tryouts
62
73
  attr_accessor :paths
63
- # A Hash of dreams for all tryouts in this class. The keys should
64
- # match the names of each tryout. The values are hashes will drill
65
- # names as keys and response
66
- attr_accessor :dreams
67
74
  # An Array of Tryout objects
68
75
  attr_accessor :tryouts
69
76
  # A Symbol representing the command taking part in the tryouts. For @dtype :cli only.
70
77
  attr_accessor :command
71
78
  # A Symbol representing the name of the library taking part in the tryouts. For @dtype :api only.
72
79
  attr_accessor :library
73
- # The name of the most recent dreams group (see self.dream)
74
- attr_accessor :dream_pointer
75
80
 
76
81
  def initialize(group=nil)
77
82
  @group = group || "Default Group"
78
83
  @tryouts = HASH_TYPE.new
79
84
  @paths = []
80
85
  @command = nil
81
- @dreams = HASH_TYPE.new
82
- @dream_pointer = nil
83
86
  end
84
87
 
85
88
  # Populate this Tryouts from a block. The block should contain calls to
@@ -101,6 +104,7 @@ class Tryouts
101
104
  # Add a shell command to Rye::Cmd and save the command name
102
105
  # in @@commands so it can be used as the default for drills
103
106
  def command(name=nil, path=nil)
107
+ raise "command testing is temporarily disabled"
104
108
  return @command if name.nil?
105
109
  @command = name.to_sym
106
110
  @dtype = :cli
@@ -139,9 +143,6 @@ class Tryouts
139
143
  def group(name=nil)
140
144
  return @group if name.nil?
141
145
  @group = name unless name.nil?
142
- # Preload dreams if possible
143
- dfile = self.class.find_dreams_file(GYMNASIUM_HOME, @group)
144
- self.load_dreams_file(dfile) if dfile
145
146
  @group
146
147
  end
147
148
  # Raises a Tryouts::Exception. +group+ is not support in the standalone syntax
@@ -171,8 +172,7 @@ class Tryouts
171
172
  to = Tryouts::Tryout.new(name, dtype, command)
172
173
  @tryouts[name] = to
173
174
  end
174
- # Populate the dreams if they've already been loaded
175
- to.dreams = @dreams[name] if @dreams.has_key?(name)
175
+
176
176
  # Process the rest of the DSL
177
177
  to.from_block block if block
178
178
  to
@@ -201,67 +201,6 @@ class Tryouts
201
201
  # NOTE: this is a standalone DSL-syntax method.
202
202
  def self.xtryout(*args, &block); end
203
203
 
204
- # Load dreams from a file or directory or if a block is given
205
- # it's processed
206
- # Raises a Tryouts::BadDreams exception when something goes awry.
207
- #
208
- # This method is used in two ways:
209
- # * In the dreams file DSL
210
- # * As a getter method on a Tryouts object
211
- def dreams(tryout_name=nil, &definition)
212
- return @dreams unless tryout_name
213
-
214
- #
215
- # dreams "path/2/dreams"
216
- # OR
217
- # dreams "path/2/file_of_dreams.rb"
218
- #
219
- if File.exists?(tryout_name)
220
- dfile = tryout_name
221
- # If we're given a directory we'll build the filename using the class name
222
- if File.directory?(tryout_name)
223
- dfile = self.class.find_dreams_file(tryout_name, @group)
224
- end
225
- raise BadDreams, "Cannot find dreams file (#{tryout_name})" unless dfile
226
- @dreams = load_dreams_file( dfile) || {}
227
-
228
- #
229
- # dreams "Tryout Name" do
230
- # dream "drill name" ...
231
- # end
232
- #
233
- elsif tryout_name.kind_of?(String) && definition
234
- to = find_tryout(tryout_name, @dtype)
235
-
236
- if to.nil?
237
- @dream_pointer = tryout_name # Used in Tryouts.dream
238
- @dreams[ @dream_pointer ] ||= {}
239
- definition.call
240
- else
241
- to.from_block &definition
242
- end
243
- else
244
- raise BadDreams, tryout_name
245
- end
246
- @dreams
247
- end
248
- # Without arguments, returns a Hash of all known dreams.
249
- # With arguments, it calls Tryouts#dreams on the current instance of Tryouts.
250
- #
251
- # NOTE: this is a standalone DSL-syntax method.
252
- def self.dreams(*args, &block)
253
- if args.empty? && block.nil?
254
- dreams = {}
255
- @@instances.each_pair do |name,inst|
256
- dreams[name] = inst.dreams
257
- end
258
- return dreams
259
- else
260
- # Call the Tryouts#dreams instance method
261
- @@instances.last.dreams(*args, &block)
262
- end
263
- end
264
-
265
204
  # Returns +@tryouts+.
266
205
  #
267
206
  # Also acts as a stub for Tryouts#tryout in case someone
@@ -275,66 +214,6 @@ class Tryouts
275
214
  tryout(args, &block)
276
215
  end
277
216
 
278
- # +name+ of the Drill associated to this Dream
279
- # +output+ A String or Array of expected output. A Dream object will be created using this value (optional)
280
- # +definition+ is a block which will be run on an instance of Dream
281
- #
282
- # This method is different than Tryout#dream because this one stores
283
- # dreams inside an instance variable of the current Tryouts object.
284
- # This allows for the situation where the dreams block appears before
285
- # the tryout block. See Tryouts#tryout
286
- #
287
- # NOTE: This method is DSL-only. It's not intended to be used in OO syntax.
288
- def dream(name, output=nil, format=nil, rcode=0, emsg=nil, &definition)
289
- to = find_tryout(@dream_pointer, @dtype)
290
- if to.nil?
291
- if output.nil?
292
- dobj = Tryouts::Drill::Dream.from_block definition
293
- else
294
- dobj = Tryouts::Drill::Dream.new(output)
295
- dobj.format, dobj.rcode, dobj.emsg = format, rcode, emsg
296
- end
297
- @dreams[@dream_pointer][name] = dobj
298
- else
299
- # Let the Tryout object process the dream DSL.
300
- # We'll get here if the dream is placed after
301
- # the drill with the same name in the same block.
302
- to.dream name, output, format, rcode, emsg, &definition
303
- end
304
- end
305
- # Calls Tryouts#dream on the current instance of Tryouts
306
- #
307
- # NOTE: this is a standalone DSL-syntax method.
308
- def self.dream(*args, &block)
309
- @@instances.last.dream(*args, &block)
310
- end
311
-
312
- # This method does nothing. It provides a quick way to disable a dream.
313
- #
314
- # NOTE: This is a DSL-only method and is not intended for OO use.
315
- def xdream(*args, &block); end
316
- # This method does nothing. It provides a quick way to disable a dream.
317
- #
318
- # NOTE: this is a standalone DSL-syntax method.
319
- def self.xdream(*args, &block); end
320
-
321
- # Populate @@dreams with the content of the file +dpath+.
322
- #
323
- # NOTE: this is an OO syntax method
324
- def load_dreams_file(dpath)
325
- type = File.extname dpath
326
- if type == ".yaml" || type == ".yml"
327
- @dreams = YAML.load_file dpath
328
- elsif type == ".json" || type == ".js"
329
- @dreams = JSON.load_file dpath
330
- elsif type == ".rb"
331
- @dreams = instance_eval File.read(dpath)
332
- else
333
- raise BadDreams, "Unknown kind of dream: #{dpath}"
334
- end
335
- @dreams
336
- end
337
-
338
217
  # Parse a +_tryouts.rb+ file. See Tryouts::CLI::Run for an example.
339
218
  #
340
219
  # NOTE: this is an OO syntax method
@@ -387,27 +266,5 @@ class Tryouts
387
266
  ## end
388
267
  ##+++
389
268
 
390
- # Find a dreams file in the directory +dir+ based on the current group name.
391
- # The expected filename format is: groupname_dreams.ext where "groupname" is
392
- # the lowercase name of the Tryouts group (spaces removed) and "ext" is one
393
- # of: yaml, js, json, rb.
394
- #
395
- # e.g.
396
- # Tryouts.find_dreams_file "dirpath" # => dirpath/tryouts_dreams.rb
397
- #
398
- def self.find_dreams_file(dir, group=nil)
399
- dpath = nil
400
- group ||= @@instances.last.group
401
- group = group.to_s.downcase.tr(' ', '')
402
- [:rb, :yaml].each do |ext|
403
- tmp = File.join(dir, "#{group}_dreams.#{ext}")
404
- if File.exists?(tmp)
405
- dpath = tmp
406
- break
407
- end
408
- end
409
- dpath
410
- end
411
-
412
269
 
413
270
  end
@@ -14,7 +14,7 @@ class Run < Drydock::Command
14
14
  # Display the dreams from all known tryouts
15
15
  def dreams
16
16
  load_available_tryouts_files
17
- if @global.verbose > 0
17
+ if Tryouts.verbose > 0
18
18
  puts Tryouts.dreams.to_yaml
19
19
  else
20
20
  Tryouts.dreams.each_pair do |n,dreams|
@@ -32,16 +32,18 @@ class Run < Drydock::Command
32
32
  # $ sergeant run [path/2/tryouts]
33
33
  # Executes all tryouts that can be found from the current working directory.
34
34
  def run
35
- if @global.verbose > 0
36
- puts "#{Tryouts.sysinfo.to_s} (#{RUBY_VERSION})"
37
- end
35
+
36
+ Tryouts.enable_debug if Drydock.debug?
37
+ Tryouts.verbose = @global.verbose
38
+
39
+ puts "#{Tryouts.sysinfo.to_s} (#{RUBY_VERSION})" if Tryouts.verbose > 0
38
40
 
39
41
  load_available_tryouts_files
40
42
 
41
43
  passed, failed = 0, 0
42
44
  Tryouts.instances.each_pair do |group,tryouts_inst|
43
45
  puts '', ' %-60s'.att(:reverse) % group
44
- puts " #{tryouts_inst.paths.join("\n ")}" if @global.verbose > 0
46
+ puts " #{tryouts_inst.paths.join("\n ")}" if Tryouts.verbose > 0
45
47
  tryouts_inst.tryouts.each_pair do |name,to|
46
48
  to.run
47
49
  to.report
@@ -52,14 +54,14 @@ class Run < Drydock::Command
52
54
  end
53
55
  unless @global.quiet
54
56
  if (passed == 0 && failed == 0)
55
- puts DEV if @global.verbose > 4
57
+ puts DEV if Tryouts.verbose > 4
56
58
  msg = " You didn't even try to acheive your dreams :[ "
57
59
  elsif failed == 0
58
- puts PUG if @global.verbose > 4
60
+ puts PUG if Tryouts.verbose > 4
59
61
  msg = " All %s dreams came true ".color(:green)
60
62
  msg = msg % [passed+failed]
61
63
  else
62
- puts BUG if @global.verbose > 4
64
+ puts BUG if Tryouts.verbose > 4
63
65
  score = (passed.to_f / (passed.to_f+failed.to_f)) * 100
64
66
  msg = " %s of %s dreams came true (%d%%) ".color(:red)
65
67
  msg = msg % [passed, passed+failed, score.to_i]
@@ -74,7 +76,7 @@ class Run < Drydock::Command
74
76
  load_available_tryouts_files
75
77
  Tryouts.instances.each_pair do |n,tryouts_inst|
76
78
  puts n
77
- if @global.verbose > 0
79
+ if Tryouts.verbose > 0
78
80
  puts " #{tryouts_inst.paths.join("\n ")}"
79
81
  end
80
82
  tryouts_inst.tryouts.each_pair do |t2,tryout|
@@ -104,7 +106,7 @@ private
104
106
  end
105
107
  end
106
108
  @tryouts_files.uniq! # Don't load the same file twice
107
- @tryouts_files.each { |f| puts "LOADING: #{f}"} if @global.verbose > 0
109
+ @tryouts_files.each { |f| puts "LOADING: #{f}"} if Tryouts.verbose > 1
108
110
  @tryouts_files.each { |file| Tryouts.parse_file file }
109
111
  end
110
112
  end
data/lib/tryouts/drill.rb CHANGED
@@ -7,7 +7,8 @@ class Tryouts
7
7
  # This class represents a drill. A drill is single test.
8
8
  #
9
9
  class Drill
10
-
10
+
11
+ require 'tryouts/drill/context'
11
12
  require 'tryouts/drill/response'
12
13
  require 'tryouts/drill/sergeant/cli'
13
14
  require 'tryouts/drill/sergeant/api'
@@ -28,22 +29,22 @@ class Tryouts
28
29
  # A Reality object (the actual output of the test)
29
30
  attr_reader :reality
30
31
 
31
- def initialize(name, dtype, *drill_args, &drill)
32
+ def initialize(name, dtype, *opts, &drill)
32
33
  @name, @dtype, @drill = name, dtype, drill
33
- @sergeant = hire_sergeant *drill_args
34
+ @sergeant = hire_sergeant opts
34
35
  # For CLI drills, a block takes precedence over inline args.
35
36
  # A block will contain multiple shell commands (see Rye::Box#batch)
36
37
  drill_args = [] if dtype == :cli && drill.is_a?(Proc)
37
38
  @reality = Tryouts::Drill::Reality.new
38
39
  end
39
40
 
40
- def hire_sergeant(*drill_args)
41
+ def hire_sergeant(opts={})
41
42
  if @dtype == :cli
42
- Tryouts::Drill::Sergeant::CLI.new(*drill_args)
43
+ Tryouts::Drill::Sergeant::CLI.new(*opts)
43
44
  elsif @dtype == :api
44
- Tryouts::Drill::Sergeant::API.new(drill_args.first)
45
+ Tryouts::Drill::Sergeant::API.new(opts.first) # should be a hash
45
46
  else
46
- raise NoSergeant, "What is #{@dtype}?"
47
+ raise NoSergeant, "Weird drill sergeant: #{@dtype}"
47
48
  end
48
49
  end
49
50
 
@@ -53,15 +54,6 @@ class Tryouts
53
54
  @reality = @sergeant.run @drill, context
54
55
  # Store the stash from the drill block
55
56
  @reality.stash = context.stash if context.respond_to? :stash
56
- # Create or overwrite an existing dream if onw was defined in the block
57
- if context.respond_to?(:dream) && context.has_dream?
58
- @dream = Tryouts::Drill::Dream.new
59
- @dream.output = context.dream
60
- @dream.format = context.format unless context.format.nil?
61
- @dream.rcode = context.rcode unless context.rcode.nil?
62
- @dream.emsg = context.emsg unless context.emsg.nil?
63
- end
64
-
65
57
  # If the drill block returned true we assume success if there's no dream
66
58
  if @dream.nil? && @reality.output == true
67
59
  @dream = Tryouts::Drill::Dream.new
@@ -69,45 +61,31 @@ class Tryouts
69
61
  end
70
62
  process_reality
71
63
  rescue => ex
72
- @reality.rcode = -2
73
- @reality.emsg, @reality.backtrace = ex.message, ex.backtrace
64
+ @reality.ecode, @reality.etype = -2, ex.class
65
+ @reality.error, @reality.trace = ex.message, ex.backtrace
74
66
  end
75
- note = @dream ? discrepency.join(', ') : 'nodream'
76
- puts self.success? ? "PASS".color(:green) : "FAIL (#{note})".color(:red)
77
67
  self.success?
78
68
  end
79
69
 
80
70
  def success?
81
- return false if @dream.nil? || @reality.nil?
82
71
  @dream == @reality
83
72
  end
84
73
 
85
- def discrepency
86
- diffs = []
87
- if @dream
88
- diffs << "rcode" if @dream.rcode != @reality.rcode
89
- diffs << "output" if !@dream.compare_output(@reality)
90
- diffs << "emsg" if @dream.emsg != @reality.emsg
91
- end
92
- diffs
93
- end
94
74
 
95
75
  def add_dream(d)
96
- @dream = d if d.is_a?(Tryouts::Drill::Dream)
76
+ @dream = d
97
77
  end
98
78
 
99
79
  private
80
+
100
81
  # Use the :format provided in the dream to convert the output from reality
101
82
  def process_reality
102
- @reality.normalize!
103
- return unless @dream && @dream.format
104
- if @dream.format == :to_yaml
105
- @reality.output = YAML.load(@reality.output.join("\n"))
106
- elsif @dream.format == :to_json
107
- @reality.output = JSON.load(@reality.output.join("\n"))
83
+ case @dream.format
84
+ when :class
85
+ @reality.output = @reality.output.class
86
+ when :exception
87
+ @reality.output = @reality.etype
108
88
  end
109
-
110
- #p [:process, @name, @dream.format, @reality.output]
111
89
  end
112
90
 
113
91
  end; end
@@ -0,0 +1,32 @@
1
+
2
+ class Tryouts
3
+ class Tryout
4
+
5
+ # All :api Drills are run within this context (not used for :cli).
6
+ # Each Drill is executed in a new instance of this class. That means
7
+ # instance variables are not carried through, but class variables are.
8
+ # The before and after blocks are also run in this context.
9
+ class DrillContext
10
+ # An ordered Hash of stashed objects.
11
+ attr_writer :stash
12
+
13
+ def initialize; @stash = Tryouts::HASH_TYPE.new; @has_dream = false; end
14
+
15
+ # Set to to true by DrillContext#dream
16
+ def has_dream?; @has_dream; end
17
+
18
+ # If called with no arguments, returns +@stash+.
19
+ # If called with arguments, it will add a new value to the +@stash+
20
+ # and return the new value. e.g.
21
+ #
22
+ # stash :name, 'some value' # => 'some value'
23
+ #
24
+ def stash(*args)
25
+ return @stash if args.empty?
26
+ @stash[args[0]] = args[1]
27
+ args[1]
28
+ end
29
+
30
+ end
31
+ end
32
+ end
@@ -5,66 +5,23 @@ class Tryouts::Drill
5
5
  # A generic base class for Dream and Reality
6
6
  #
7
7
  class Response
8
- attr_accessor :output, :format, :rcode, :emsg, :backtrace
9
- def initialize(output=nil, format=nil, rcode=0)
10
- @output, @format, @rcode = output, format, (rcode || 0)
11
- @output = nil if @output.nil?
12
- normalize!
8
+ attr_accessor :output, :format
9
+ def initialize(output=nil, format=nil)
10
+ @output, @format = output, format
13
11
  end
14
12
 
15
- def ==(other)
16
- return false if other.nil?
17
- @rcode == other.rcode &&
18
- @emsg == other.emsg &&
19
- compare_output(other)
20
- end
21
-
22
- def rcode(val=nil); @rcode = val unless val.nil?; normalize!; @rcode; end
23
- def output(val=nil); @output = val unless val.nil?; normalize!; @output; end
24
- def emsg(val=nil); @emsg = val unless val.nil?; normalize!; @emsg; end
25
- def format(val=nil); @format = val unless val.nil?; normalize!; @format; end
26
-
27
- def output=(val); @output = val; normalize!; @output; end
28
- def rcode=(val); @rcode = val; normalize!; @rcode; end
29
- def format=(val); @format = val; normalize!; @format; end
30
- def emsg=(val); @emsg = val; normalize!; @emsg; end
13
+ def format(val=nil); @format = val.to_sym unless val.nil?; @format; end
14
+ def format=(val); @format = val.to_sym; @format; end
31
15
 
32
- # Enforce the following restrictions on the data fields:
33
- # * +rcode+ is an Integer
34
- # * +format+ is a Symbol
35
- # This method is called automatically any time a field is updated.
36
- def normalize!
37
- @rcode = @rcode.to_i if @rcode.is_a?(String)
38
- @format = @format.to_sym if @format.is_a?(String)
39
- end
40
- def compare_output(other)
41
- #
42
- # The dream is always on the left (Does your dream match reality?)
43
- # This check is important so we can support both:
44
- # @dream == @reality
45
- # AND
46
- # @reality == @dream
47
- #
48
- if self.is_a? Tryouts::Drill::Dream
49
- dream, reality = self, other
50
- elsif self.is_a? Tryouts::Drill::Reality
51
- dream, reality = other, self
52
- else
53
- # If self isn't a Dream or a Reality, then we have a problem
54
- return false
55
- end
16
+ def Response.compare(dream, reality)
17
+ return false if reality.nil?
56
18
 
57
- # The matching statement will be the return value.
58
- if dream.format.nil? || (dream.class == reality.class)
59
- dream.output == reality.output
60
- elsif reality.respond_to? dream.format
61
- reality.output.send(dream.format) == dream.output
62
- else
63
- false
64
- end
19
+ ## I don't think this check is necessary or useful
20
+ ##return false unless reality.error.nil? && reality.trace.nil?
21
+ return true if reality.output == true and dream.nil?
22
+ reality.output == dream.output
65
23
 
66
24
  end
67
-
68
25
  end
69
26
 
70
27
 
@@ -82,18 +39,20 @@ class Tryouts::Drill
82
39
 
83
40
  def from_block(definition)
84
41
  instance_eval &definition
85
- self.normalize!
86
42
  self
87
43
  end
88
44
 
89
- # Takes a String +val+ and splits the lines into an Array. Each line
90
- # has
45
+ # Takes a String +val+ and splits the lines into an Array.
91
46
  def inline(val=nil)
92
47
  lines = (val.split($/) || [])
93
48
  lines.shift if lines.first.strip == ""
94
49
  lines.pop if lines.last.strip == ""
95
50
  lines
96
51
  end
52
+
53
+ def ==(reality)
54
+ Response.compare(self, reality)
55
+ end
97
56
  end
98
57
 
99
58
  # = Reality
@@ -103,10 +62,17 @@ class Tryouts::Drill
103
62
  class Reality < Tryouts::Drill::Response
104
63
  # An ordered hash taken from the DrillContext that created this Reality.
105
64
  attr_accessor :stash
65
+ attr_accessor :error
66
+ attr_accessor :trace
67
+ attr_accessor :ecode
68
+ attr_accessor :etype
106
69
  def initialize
107
- @rcode = 0
108
70
  @stash = Tryouts::HASH_TYPE.new
109
71
  end
72
+
73
+ def ==(dream)
74
+ Response.compare(dream, self)
75
+ end
110
76
  end
111
77
 
112
78
  end
@@ -9,30 +9,30 @@ class Tryouts; class Drill; module Sergeant
9
9
  #
10
10
  class API
11
11
 
12
- # +return_value+ specify a return value. This will be
12
+ attr_reader :opts
13
+
14
+ # +opts+ is a Hash with the following optional keys:
15
+ #
16
+ # * +:output+ specify a return value. This will be
13
17
  # used if no block is specified for the drill.
14
- def initialize(return_value=nil)
15
- @return_value = return_value
18
+ def initialize(opts={})
19
+ @opts = opts
16
20
  end
17
21
 
18
22
  def run(block, context, &inline)
19
-
20
23
  # A Proc object takes precedence over an inline block.
21
24
  runtime = (block.nil? ? inline : block)
22
25
  response = Tryouts::Drill::Reality.new
23
26
  if runtime.nil?
24
- response.output = @return_value
27
+ response.output = @opts[:output]
25
28
  else
26
29
  begin
27
- unless runtime.nil?
28
- ret = context.instance_eval &runtime
29
- response.output, response.rcode = ret, 0
30
- end
31
- rescue => ex
32
- response.rcode = 1
33
- response.output = ret
34
- response.emsg = ex.message
35
- response.backtrace = ex.backtrace
30
+ response.output = context.instance_eval &runtime
31
+ rescue => e
32
+ puts e.message, e.backtrace if Tryouts.debug? && Tryouts.verbose > 2
33
+ response.etype = e.class
34
+ response.error = e.message
35
+ response.trace = e.backtrace
36
36
  end
37
37
  end
38
38
  response
@@ -25,21 +25,30 @@ class Tryouts; class Drill; module Sergeant
25
25
  response = Tryouts::Drill::Reality.new
26
26
  begin
27
27
  if runtime.nil?
28
- ret = @rbox.send *rbox_args
28
+ ret = @rbox.send *@rbox_args
29
29
  else
30
30
  ret = @rbox.instance_eval &runtime
31
31
  end
32
- response.rcode = ret.exit_code
33
- response.output = ret.stdout.size == 1 ? ret.stdout.first : Array.new(ret.stdout) # Cast the Rye::Rap object
34
- response.emsg = ret.stderr unless ret.stderr.empty?
32
+ response.ecode = ret.exit_code
33
+ if ret.stdout.size == 1
34
+ response.output = ret.stdout.first
35
+ else
36
+ response.output = Array.new(ret.stdout) # Cast the Rye::Rap object
37
+ end
38
+ response.error = ret.stderr unless ret.stderr.empty?
35
39
  rescue Rye::CommandNotFound => ex
36
- response.rcode = -2
37
- response.emsg = "[#{@rbox.host}] Command not found: #{ex.message}"
38
- response.backtrace = ex.backtrace
40
+ puts ex.message, ex.backtrace if Tryouts.debug? && Tryouts.verbose > 2
41
+ response.etype = ex.class
42
+ response.ecode = ex.exit_code
43
+ response.error = "[#{@rbox.host}] Command not found: #{ex.message}"
44
+ response.trace = ex.backtrace
39
45
  rescue Rye::CommandError => ex
40
- response.rcode = ex.exit_code
46
+ puts ex.message, ex.backtrace if Tryouts.debug? && Tryouts.verbose > 2
47
+ response.etype = ex.class
48
+ response.ecode = ex.exit_code
41
49
  response.output = ex.stdout
42
- response.emsg = ex.stderr
50
+ response.error = ex.stderr.join $/
51
+ response.trace = ex.backtrace
43
52
  end
44
53
  response
45
54
  end
@@ -26,74 +26,15 @@ class Tryouts
26
26
  # For drill type :api, this attribute is ignored.
27
27
  attr_reader :command
28
28
  # A Hash of Dream objects for this Tryout. The keys are drill names.
29
- attr_accessor :dreams
29
+ attr_reader :dream_catcher
30
30
 
31
31
  @@valid_dtypes = [:cli, :api]
32
-
33
- # All :api Drills are run within this context (not used for :cli).
34
- # Each Drill is executed in a new instance of this class. That means
35
- # instance variables are not carried through, but class variables are.
36
- # The before and after blocks are also run in this context.
37
- class DrillContext
38
- # An ordered Hash of stashed objects.
39
- attr_writer :stash
40
- # A value used as the dream output that will overwrite a predefined dream
41
- attr_writer :dream
42
- attr_writer :format
43
- attr_writer :rcode
44
- attr_writer :emsg
45
- attr_writer :output
46
-
47
- def initialize; @stash = Tryouts::HASH_TYPE.new; @has_dream = false; end
48
-
49
- # Set to to true by DrillContext#dream
50
- def has_dream?; @has_dream; end
51
-
52
- # If called with no arguments, returns +@stash+.
53
- # If called with arguments, it will add a new value to the +@stash+
54
- # and return the new value. e.g.
55
- #
56
- # stash :name, 'some value' # => 'some value'
57
- #
58
- def stash(*args)
59
- return @stash if args.empty?
60
- @stash[args[0]] = args[1]
61
- args[1]
62
- end
63
-
64
- # If called with no arguments, returns +@dream+.
65
- # If called with one argument, it will overwrite +@dream+ with the
66
- # first element. If called with two arguments, it will check if
67
- # the second argument is a Symbol or Fixnum. If it's a Symbol it
68
- # will assume it's +@format+. If it's a Fixnum, it will assume
69
- # it's +@rcode+. If there's a there's a third argument and it's a
70
- # Fixnum, it's assumed to be +@rcode+. In all cases, this method
71
- # returns the value of +@dream+. e.g.
72
- #
73
- # dream 'some value' # => 'some value'
74
- # dream :val1, :class, 1 # => :val1
75
- #
76
- def dream(*args)
77
- return @dream if args.empty?
78
- @has_dream = true
79
- @dream = args.shift
80
- @format = args.shift if args.first.is_a? Symbol
81
- @rcode = args.shift if args.first.is_a? Fixnum
82
- @emsg = args.shift if args.first.is_a? String
83
- end
84
-
85
- def output(*args); return @output if args.empty?; @output = args.first; end
86
- def format(*args); return @format if args.empty?; @format = args.first; end
87
- def rcode(*args); return @rcode if args.empty?; @rcode = args.first; end
88
- def emsg(*args); return @emsg if args.empty?; @emsg = args.first; end
89
-
90
- end
91
32
 
92
33
  def initialize(name, dtype, command=nil, *args)
93
34
  raise "Must supply command for dtype :cli" if dtype == :cli && command.nil?
94
35
  raise "#{dtype} is not a valid drill type" if !@@valid_dtypes.member?(dtype)
95
36
  @name, @dtype, @command = name, dtype, command
96
- @drills, @dreams = [], {}
37
+ @drills, @dream_catcher = [], []
97
38
  @passed, @failed = 0, 0
98
39
  end
99
40
 
@@ -108,13 +49,17 @@ class Tryouts
108
49
 
109
50
  # Execute all Drill objects
110
51
  def run
111
- update_drills! # Ensure all drills have all known dreams
112
52
  DrillContext.module_eval &setup if setup.is_a?(Proc)
113
53
  puts Tryouts::TRYOUT_MSG.bright % @name
114
54
  @drills.each do |drill|
115
- drill.run(DrillContext.new) # Returns true or false
116
- drill.reality.stash.each_pair do |n,v|
117
- puts '%14s: %s' % [n,v.inspect]
55
+ drill.run DrillContext.new
56
+ note = @dream ? '' : '(nodream)'
57
+ puts drill.success? ? "PASS".color(:green) : "FAIL #{note}".color(:red)
58
+ puts " #{drill.reality.output.inspect}" if Tryouts.verbose > 0
59
+ if Tryouts.verbose > 1
60
+ drill.reality.stash.each_pair do |n,v|
61
+ puts '%14s: %s' % [n,v.inspect]
62
+ end
118
63
  end
119
64
  drill.success? ? @passed += 1 : @failed += 1
120
65
  end
@@ -126,25 +71,23 @@ class Tryouts
126
71
  return true if success?
127
72
  failed = @drills.select { |d| !d.success? }
128
73
  failed.each_with_index do |drill,index|
74
+ dream, reality = drill.dream, drill.reality
129
75
  title = ' %-59s' % %Q{ERROR #{index+1}/#{failed.size} "#{drill.name}"}
130
76
  puts $/, ' ' << title.color(:red).att(:reverse)
131
77
 
132
- if drill.dream
133
- puts '%24s: %s (expected %s)' % ["response code", drill.reality.rcode, drill.dream.rcode]
134
- puts '%24s: %s' % ["expected output", drill.dream.output.inspect]
135
- puts '%24s: %s' % ["actual output", drill.reality.output.inspect]
136
- if drill.reality.emsg || (drill.reality.emsg != drill.dream.emsg)
137
- puts '%24s: %s' % ["expected error msg", drill.dream.emsg.inspect]
138
- puts '%24s: %s' % ["actual error msg", drill.reality.emsg.inspect]
78
+ if dream
79
+ puts '%12s: %s' % [ "expected", dream.output.inspect]
80
+ puts '%12s: %s' % ["returned", reality.output.inspect]
81
+ unless reality.error.nil?
82
+ puts '%12s: %s' % ["error", reality.error.inspect]
139
83
  end
140
-
141
- if drill.reality.rcode > 0
142
- puts '%24s: ' % ["backtrace"]
143
- puts drill.reality.backtrace, $/
84
+ unless reality.trace.nil?
85
+ puts '%12s: %s' % ["trace", reality.trace.join($/ + ' '*14)]
86
+ puts
144
87
  end
145
88
  else
146
- puts '%24s: %s' % ["expected output", "[nodream]"]
147
- puts '%24s: %s' % ["actual output", drill.reality.output.inspect]
89
+ puts '%12s: %s' % ["expected", "[nodream]"]
90
+ puts '%12s: %s' % ["returned", reality.output.inspect]
148
91
  end
149
92
 
150
93
  end
@@ -158,26 +101,15 @@ class Tryouts
158
101
  @success = !(@drills.collect { |r| r.success? }.member?(false))
159
102
  end
160
103
 
161
- # Add a Drill object to the list for this Tryout. If there is a dream
162
- # defined with the same name as the Drill, that dream will be given to
163
- # the Drill before its added to the list.
104
+ # Add a Drill object to the list for this Tryout. If there is one or
105
+ # more dreams in +@dream_catcher+, it will be added to drill +d+.
164
106
  def add_drill(d)
165
- d.add_dream @dreams[d.name] if !@dreams.nil? && @dreams.has_key?(d.name)
166
- drills << d if d.is_a?(Tryouts::Drill)
167
- d
168
- end
169
-
170
- # Goes through the list of Drill objects (@drills) and gives each
171
- # one its associated Dream object (if available).
172
- #
173
- # This method is called before Tryout#run, but is only necessary in
174
- # the case where dreams where loaded after the drills were defined.
175
- def update_drills!
176
- return if @dreams.nil?
177
- @drills.each do |drill|
178
- next unless @dreams.has_key?(drill.name)
179
- drill.add_dream @dreams[drill.name]
107
+ unless @dream_catcher.empty?
108
+ d.add_dream @dream_catcher.first
109
+ @dream_catcher.clear
180
110
  end
111
+ drills << d
112
+ d
181
113
  end
182
114
 
183
115
  ## --------------------------------------- EXTERNAL DSL -----
@@ -199,28 +131,30 @@ class Tryouts
199
131
  # +args+ is sent directly to the Drill class. The values are specific on the Sergeant.
200
132
  def drill(dname, *args, &definition)
201
133
  raise "Empty drill name (#{@name})" if dname.nil? || dname.empty?
202
- args.unshift(@command) if @dtype == :cli
203
- drill = Tryouts::Drill.new(dname, @dtype, *args, &definition)
204
- add_drill drill
134
+ if definition.nil?
135
+ drill = Tryouts::Drill.new(dname, @dtype, :output => args.first)
136
+ else
137
+ drill = Tryouts::Drill.new(dname, @dtype, args.first, &definition)
138
+ end
139
+ self.add_drill drill
205
140
  end
206
141
  # A quick way to comment out a drill
207
142
  def xdrill(*args, &b); end # ignore calls to xdrill
208
143
 
209
- # +name+ of the Drill associated to this Dream
210
- # +output+ A String or Array of expected output. A Dream object will be created using this value (optional)
211
- # +definition+ is a block which will be run on an instance of Dream
144
+
212
145
  #
213
146
  # NOTE: This method is DSL-only. It's not intended to be used in OO syntax.
214
- def dream(dname, output=nil, format=nil, rcode=0, emsg=nil, &definition)
215
- raise "Empty dream name (#{@name})" if dname.nil? || dname.empty?
216
- if output.nil?
217
- raise "No output or block for '#{dname}' (#{@name})" if definition.nil?
147
+ def dream(*args, &definition)
148
+ if args.empty?
218
149
  dobj = Tryouts::Drill::Dream.from_block definition
219
150
  else
220
- dobj = Tryouts::Drill::Dream.new(output)
221
- dobj.format, dobj.rcode, dobj.emsg = format, rcode, emsg
151
+ if args.size == 1
152
+ dobj = Tryouts::Drill::Dream.new(args.shift) # dream 'OUTPUT'
153
+ else
154
+ dobj = Tryouts::Drill::Dream.new(*args.reverse) # dream :form, 'OUTPUT'
155
+ end
222
156
  end
223
- @dreams[dname] = dobj
157
+ @dream_catcher << dobj
224
158
  dobj
225
159
  end
226
160
  # A quick way to comment out a dream
data/tryouts.gemspec ADDED
@@ -0,0 +1,75 @@
1
+ @spec = Gem::Specification.new do |s|
2
+ s.name = "tryouts"
3
+ s.rubyforge_project = "tryouts"
4
+ s.version = "0.6.0"
5
+ s.summary = "Tryouts are high-level tests for your Ruby code. May all your dreams come true!"
6
+ s.description = s.summary
7
+ s.author = "Delano Mandelbaum"
8
+ s.email = "tryouts@solutious.com"
9
+ s.homepage = "http://github.com/delano/tryouts"
10
+
11
+ # = EXECUTABLES =
12
+ # The list of executables in your project (if any). Don't include the path,
13
+ # just the base filename.
14
+ s.executables = %w[sergeant]
15
+
16
+ # Directories to extract rdocs from
17
+ s.require_paths = %w[lib]
18
+
19
+ # Specific files to include rdocs from
20
+ s.extra_rdoc_files = %w[README.rdoc LICENSE.txt]
21
+
22
+ # Update --main to reflect the default page to display
23
+ s.rdoc_options = ["--line-numbers", "--title", "Tryouts: #{s.summary}", "--main", "README.rdoc"]
24
+
25
+ # = DEPENDENCIES =
26
+ # Add all gem dependencies
27
+ s.add_dependency 'drydock', '>= 0.6.5'
28
+ s.add_dependency 'rye', '>= 0.8.2'
29
+ s.add_dependency 'sysinfo', '>= 0.5.1'
30
+
31
+ # = MANIFEST =
32
+ # The complete list of files to be included in the release. When GitHub packages your gem,
33
+ # it doesn't allow you to run any command that accesses the filesystem. You will get an
34
+ # error. You can ask your VCS for the list of versioned files:
35
+ # git ls-files
36
+ # svn list -R
37
+ s.files = %w(
38
+ CHANGES.txt
39
+ LICENSE.txt
40
+ README.rdoc
41
+ Rakefile
42
+ bin/mockout
43
+ bin/sergeant
44
+ lib/tryouts.rb
45
+ lib/tryouts/cli.rb
46
+ lib/tryouts/cli/run.rb
47
+ lib/tryouts/drill.rb
48
+ lib/tryouts/drill/context.rb
49
+ lib/tryouts/drill/response.rb
50
+ lib/tryouts/drill/sergeant/api.rb
51
+ lib/tryouts/drill/sergeant/cli.rb
52
+ lib/tryouts/mixins.rb
53
+ lib/tryouts/mixins/hash.rb
54
+ lib/tryouts/orderedhash.rb
55
+ lib/tryouts/tryout.rb
56
+ tryouts.gemspec
57
+ )
58
+
59
+ s.has_rdoc = true
60
+ s.rubygems_version = '1.3.0'
61
+
62
+ if s.respond_to? :specification_version then
63
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
64
+ s.specification_version = 2
65
+
66
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
67
+ s.add_runtime_dependency(%q<RedCloth>, [">= 4.0.4"])
68
+ else
69
+ s.add_dependency(%q<RedCloth>, [">= 4.0.4"])
70
+ end
71
+ else
72
+ s.add_dependency(%q<RedCloth>, [">= 4.0.4"])
73
+ end
74
+
75
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: delano-tryouts
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Delano Mandelbaum
@@ -30,7 +30,7 @@ dependencies:
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 0.6.6
33
+ version: 0.8.2
34
34
  version:
35
35
  - !ruby/object:Gem::Dependency
36
36
  name: sysinfo
@@ -72,6 +72,7 @@ files:
72
72
  - lib/tryouts/cli.rb
73
73
  - lib/tryouts/cli/run.rb
74
74
  - lib/tryouts/drill.rb
75
+ - lib/tryouts/drill/context.rb
75
76
  - lib/tryouts/drill/response.rb
76
77
  - lib/tryouts/drill/sergeant/api.rb
77
78
  - lib/tryouts/drill/sergeant/cli.rb
@@ -79,6 +80,7 @@ files:
79
80
  - lib/tryouts/mixins/hash.rb
80
81
  - lib/tryouts/orderedhash.rb
81
82
  - lib/tryouts/tryout.rb
83
+ - tryouts.gemspec
82
84
  has_rdoc: true
83
85
  homepage: http://github.com/delano/tryouts
84
86
  post_install_message: