tap-test 0.1.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,208 @@
1
+ if RUBY_PLATFORM =~ /mswin32/
2
+ begin
3
+ require 'rubygems'
4
+ require 'win32/open3'
5
+ rescue(LoadError)
6
+ puts %q{
7
+ Tap:Test::ShellTest requires the win32-open3 gem on Windows.
8
+ Use this command and try again:
9
+
10
+ % gem install win32-open3
11
+
12
+ }
13
+ raise
14
+ end
15
+ else
16
+ require 'open3'
17
+ end
18
+
19
+ require 'tap/test/shell_test/class_methods'
20
+
21
+ module Tap
22
+ module Test
23
+
24
+ # A module for testing shell scripts.
25
+ #
26
+ # require 'test/unit'
27
+ # class ShellTestSample < Test::Unit::TestCase
28
+ # include Tap::Test::ShellTest
29
+ #
30
+ # # these are the default sh_test options used
31
+ # # in tests like test_sh_command_alias
32
+ # self.sh_test_options = {
33
+ # :cmd_pattern => '% inspect_argv',
34
+ # :cmd => 'ruby -e "puts ARGV.inspect"'
35
+ # }
36
+ #
37
+ # def test_echo
38
+ # assert_equal "goodnight moon", sh("echo goodnight moon").strip
39
+ # end
40
+ #
41
+ # def test_echo_using_sh_test
42
+ # sh_test %q{
43
+ # echo goodnight moon
44
+ # goodnight moon
45
+ # }
46
+ # end
47
+ #
48
+ # def test_sh_command_alias
49
+ # sh_test("% inspect_env") do |output|
50
+ # assert output !~ /NEW_ENV_VAR/
51
+ # end
52
+ #
53
+ # sh_test("NEW_ENV_VAR=blue % inspect_env") do |output|
54
+ # assert output =~ /NEW_ENV_VAR=blue/
55
+ # end
56
+ # end
57
+ # end
58
+ #
59
+ module ShellTest
60
+
61
+ def self.included(base) # :nodoc:
62
+ super
63
+ base.extend ShellTest::ClassMethods
64
+ end
65
+
66
+ # Sets up the ShellTest module. Be sure to call super if you override
67
+ # setup in an including module.
68
+ def setup
69
+ super
70
+ @shell_test_notification = false
71
+ end
72
+
73
+ # Returns true if the ENV variable 'VERBOSE' is true. When verbose,
74
+ # ShellTest prints the expanded commands of sh_test to $stdout.
75
+ def verbose?
76
+ ENV['VERBOSE'] == 'true'
77
+ end
78
+
79
+ # Returns true if the ENV variable 'QUIET' is true. When quiet,
80
+ # ShellTest does not print any extra information to $stdout.
81
+ def quiet?
82
+ ENV['QUIET'] == 'true'
83
+ end
84
+
85
+ # Sets the specified ENV variables for the duration of the block.
86
+ # If replace is true, current ENV variables are replaced; otherwise
87
+ # the new env variables are simply added to the existing set.
88
+ def with_env(env={}, replace=false)
89
+ current_env = {}
90
+ ENV.each_pair do |key, value|
91
+ current_env[key] = value
92
+ end
93
+
94
+ begin
95
+ ENV.clear if replace
96
+ env.each_pair do |key, value|
97
+ ENV[key] = value
98
+ end if env
99
+
100
+ yield
101
+
102
+ ensure
103
+ ENV.clear
104
+ current_env.each_pair do |key, value|
105
+ ENV[key] = value
106
+ end
107
+ end
108
+ end
109
+
110
+ # Executes the command using IO.popen and returns the stdout content.
111
+ #
112
+ # ==== Note
113
+ # On Windows this method requires the {win32-popen3}[http://rubyforge.org/projects/win32utils]
114
+ # utility. If it is not available, it will have to be installed:
115
+ #
116
+ # % gem install win32-open3
117
+ #
118
+ def sh(cmd)
119
+ Open3.popen3(cmd) do |i,o,s|
120
+ yield(i,o,s) if block_given?
121
+ return o.read
122
+ end
123
+ end
124
+
125
+ # Peforms a shell test. Shell tests execute the command and yield the
126
+ # $stdout result to the block for validation. The command is executed
127
+ # through sh, ie using IO.popen.
128
+ #
129
+ # Options provided to sh_test are merged with the sh_test_options set
130
+ # for the class.
131
+ #
132
+ # ==== Command Aliases
133
+ #
134
+ # The options allow specification of a command pattern that gets
135
+ # replaced with a command alias. Only the first instance of the command
136
+ # pattern is replaced. In addition, shell tests allow the expected result
137
+ # to be specified inline with the command. Used together, these allow
138
+ # multiple tests of a complex command to be specified easily:
139
+ #
140
+ # opts = {
141
+ # :cmd_pattern => '% argv_inspect',
142
+ # :cmd => 'ruby -e "puts ARGV.inspect"'
143
+ # }
144
+ #
145
+ # sh_test %Q{
146
+ # % argv_inspect goodnight moon
147
+ # ["goodnight", "moon"]
148
+ # }, opts
149
+ #
150
+ # sh_test %Q{
151
+ # % argv_inspect hello world
152
+ # ["hello", "world"]
153
+ # }, opts
154
+ #
155
+ # ==== ENV variables
156
+ #
157
+ # Options may specify a hash of env variables that will be set in the
158
+ # subprocess.
159
+ #
160
+ # sh_test %Q{
161
+ # ruby -e "puts ENV['SAMPLE']"
162
+ # value
163
+ # }, :env => {'SAMPLE' => 'value'}
164
+ #
165
+ # Note it is better to specify env variables in this way rather than
166
+ # through the command trick 'VAR=value cmd ...', as that syntax does
167
+ # not work on Windows. As a point of interest, see
168
+ # http://gist.github.com/107363 for a demonstration of ENV
169
+ # variables being inherited by subprocesses.
170
+ #
171
+ def sh_test(cmd, options={})
172
+ options = sh_test_options.merge(options)
173
+
174
+ unless quiet? || @shell_test_notification
175
+ @shell_test_notification = true
176
+ puts
177
+ puts method_name
178
+ end
179
+
180
+ cmd, expected = cmd.lstrip.split(/\r?\n/, 2)
181
+ original_cmd = cmd
182
+
183
+ if cmd_pattern = options[:cmd_pattern]
184
+ cmd = cmd.sub(cmd_pattern, options[:cmd].to_s)
185
+ end
186
+
187
+ start = Time.now
188
+ result = with_env(options[:env], options[:replace_env]) do
189
+ sh(cmd)
190
+ end
191
+ finish = Time.now
192
+
193
+ elapsed = "%.3f" % [finish-start]
194
+ puts " (#{elapsed}s) #{verbose? ? cmd : original_cmd}" unless quiet?
195
+
196
+ assert_equal(expected, result, cmd) if expected
197
+ yield(result) if block_given?
198
+ result
199
+ end
200
+
201
+ # Returns a hash of the default sh_test options. See
202
+ # ShellTest::ClassMethods#sh_test_options.
203
+ def sh_test_options
204
+ self.class.sh_test_options
205
+ end
206
+ end
207
+ end
208
+ end
@@ -0,0 +1,97 @@
1
+ module Tap
2
+ module Test
3
+ module SubsetTest
4
+
5
+ # Class methods associated with SubsetTest.
6
+ module ClassMethods
7
+
8
+ # Passes conditions to subclass
9
+ def inherited(child) # :nodoc:
10
+ super
11
+ dup = {}
12
+ conditions.each_pair {|key, value| dup[key] = value.dup }
13
+ child.instance_variable_set(:@conditions, dup)
14
+ end
15
+
16
+ # Initialize conditions.
17
+ def self.extended(base) # :nodoc:
18
+ base.instance_variable_set(:@conditions, {})
19
+ end
20
+
21
+ # A hash of [name, [msg, condition_block]] pairs defined by condition.
22
+ attr_reader :conditions
23
+
24
+ # Defines a condition block and associated message.
25
+ # Raises an error if no condition block is given.
26
+ def condition(name, msg=nil, &block)
27
+ raise ArgumentError, "no condition block given" unless block_given?
28
+ conditions[name] = [msg, block]
29
+ end
30
+
31
+ # Returns true if the all blocks for the specified conditions return true.
32
+ #
33
+ # condition(:is_true) { true }
34
+ # condition(:is_false) { false }
35
+ # satisfied?(:is_true) # => true
36
+ # satisfied?(:is_true, :is_false) # => false
37
+ #
38
+ # Yields the name and message for each unsatisfied condition to the
39
+ # block, if given.
40
+ def satisfied?(*names) # :yields: name-of-unsatisfied-condition, msg
41
+ unsatisfied = unsatisfied_conditions(*names)
42
+
43
+ unsatisfied.each do |name|
44
+ yield(name, condition[name][0])
45
+ end if block_given?
46
+
47
+ unsatisfied.empty?
48
+ end
49
+
50
+ # Returns an array of the unsatified conditions. Raises
51
+ # an error if a condition has not been defined.
52
+ #
53
+ # condition(:is_true) { true }
54
+ # condition(:is_false) { false }
55
+ # unsatisfied_conditions(:is_true, :is_false) # => [:is_false]
56
+ #
57
+ def unsatisfied_conditions(*condition_names)
58
+ condition_names = conditions.keys if condition_names.empty?
59
+ unsatified = []
60
+ condition_names.each do |name|
61
+ unless condition = conditions[name]
62
+ raise ArgumentError, "Unknown condition: #{name}"
63
+ end
64
+
65
+ unsatified << name unless condition.last.call
66
+ end
67
+ unsatified
68
+ end
69
+
70
+ # Returns true if RUBY_PLATFORM matches one of the specfied
71
+ # platforms. Use the prefix 'non_' to specify any plaform
72
+ # except the specified platform (ex: 'non_mswin'). Returns
73
+ # true if no platforms are specified.
74
+ #
75
+ # Some common platforms:
76
+ # mswin Windows
77
+ # darwin Mac
78
+ def match_platform?(*platforms)
79
+ platforms.each do |platform|
80
+ platform.to_s =~ /^(non_)?(.*)/
81
+
82
+ non = true if $1
83
+ match_platform = !RUBY_PLATFORM.index($2).nil?
84
+ return false unless (non && !match_platform) || (!non && match_platform)
85
+ end
86
+
87
+ true
88
+ end
89
+
90
+ # Returns true if type or 'ALL' is specified as 'true' in ENV.
91
+ def run_subset?(type)
92
+ ENV[type] == "true" || ENV['ALL'] == "true" ? true : false
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,245 @@
1
+ require 'benchmark'
2
+ require 'tap/test/subset_test/class_methods'
3
+
4
+ module Tap
5
+ module Test
6
+
7
+ # SubsetTest provides methods to conditionally run tests.
8
+ #
9
+ # require 'tap/test'
10
+ #
11
+ # class Test::Unit::TestCase
12
+ # # only true if running on windows
13
+ # condition(:windows) { match_platform?('mswin') }
14
+ #
15
+ # # only true if running on anything but windows
16
+ # condition(:non_windows) { match_platform?('non_mswin') }
17
+ # end
18
+ #
19
+ # Here the WindowsOnlyTest will only run on a Windows platform. Conditions
20
+ # like these may be targeted at specific tests when only some tests need
21
+ # to be skipped.
22
+ #
23
+ # class RunOnlyAFewTest < Test::Unit::TestCase
24
+ # include SubsetTest
25
+ #
26
+ # def test_runs_all_the_time
27
+ # assert true
28
+ # end
29
+ #
30
+ # def test_runs_only_if_non_windows_condition_is_true
31
+ # condition_test(:non_windows) { assert true }
32
+ # end
33
+ # end
34
+ #
35
+ # def test_runs_only_when_ENV_variable_EXTENDED_is_true
36
+ # extended_test { assert true }
37
+ # end
38
+ #
39
+ # def test_runs_only_when_ENV_variable_BENCHMARK_is_true
40
+ # benchmark_test do |x|
41
+ # x.report("init speed") { 10000.times { Object.new } }
42
+ # end
43
+ # end
44
+ #
45
+ # def test_runs_only_when_ENV_variable_CUSTOM_is_true
46
+ # subset_test('CUSTOM') { assert true }
47
+ # end
48
+ # end
49
+ #
50
+ # In the example, the ENV variables EXTENDED, BENCHMARK, and CUSTOM act as
51
+ # flags to run specific tests. If you're running your test using Rake, ENV
52
+ # variables can be set from the command line like so:
53
+ #
54
+ # % EXTENDED=true rake test
55
+ # % BENCHMARK=true rake test
56
+ #
57
+ # Since tap and rap can run rake tasks as well, these are equivalent:
58
+ #
59
+ # % EXTENDED=true tap run test
60
+ # % BENCHMARK=true rap test
61
+ #
62
+ # To run all tests that get switched using an ENV variable, set ALL=true.
63
+ #
64
+ # % ALL=true rap test
65
+ #
66
+ # See SubsetTest::ClassMethods for more information.
67
+ module SubsetTest
68
+
69
+ def self.included(base) # :nodoc:
70
+ super
71
+ base.extend SubsetTest::ClassMethods
72
+ end
73
+
74
+ # Returns true if the specified conditions are satisfied.
75
+ def satisfied?(*condition_names)
76
+ self.class.satisfied?(*condition_names)
77
+ end
78
+
79
+ # Returns true if the subset type (ex 'BENCHMARK') or 'ALL' is
80
+ # specified in ENV.
81
+ def run_subset?(type)
82
+ self.class.run_subset?(type)
83
+ end
84
+
85
+ # Returns true if the input string matches the regexp specified in
86
+ # ENV[type]. Returns the default value if ENV['ALL'] is 'true', and the
87
+ # default if type is not specified in ENV.
88
+ def match_regexp?(type, str, default=true)
89
+ return true if ENV["ALL"] == 'true'
90
+ return default unless value = ENV[type]
91
+
92
+ str =~ Regexp.new(value) ? true : false
93
+ end
94
+
95
+ # Platform-specific test. Useful for specifying test that should only
96
+ # be run on a subset of platforms. Prints ' ' if the test is not run.
97
+ #
98
+ # def test_only_on_windows
99
+ # platform_test('mswin') { ... }
100
+ # end
101
+ #
102
+ # See SubsetTestClass#match_platform? for matching details.
103
+ def platform_test(*platforms)
104
+ if self.class.match_platform?(*platforms)
105
+ yield
106
+ else
107
+ print ' '
108
+ end
109
+ end
110
+
111
+ # Conditonal test. Only runs if the specified conditions are satisfied.
112
+ # If no conditons are explicitly set, condition_test only runs if ALL
113
+ # conditions for the test are satisfied.
114
+ #
115
+ # condition(:is_true) { true }
116
+ # condition(:is_false) { false }
117
+ #
118
+ # def test_only_if_true_is_satisfied
119
+ # condition_test(:is_true) { # runs }
120
+ # end
121
+ #
122
+ # def test_only_if_all_conditions_are_satisfied
123
+ # condition_test { # does not run }
124
+ # end
125
+ #
126
+ # See SubsetTestClass#condition for more details.
127
+ def condition_test(*condition_names)
128
+ if self.class.unsatisfied_conditions(*condition_names).empty?
129
+ yield
130
+ else
131
+ print ' '
132
+ end
133
+ end
134
+
135
+ # Defines a subset test. The provided block will only run if:
136
+ # - the subset type is specified in ENV
137
+ # - the subset 'ALL' is specified in ENV
138
+ # - the test method name matches the regexp provided in the
139
+ # <TYPE>_TEST ENV variable
140
+ #
141
+ # Otherwise the block will be skipped and +skip+ will be printed in the
142
+ # test output. By default skip is the first letter of +type+.
143
+ #
144
+ # For example, with these methods:
145
+ #
146
+ # def test_one
147
+ # subset_test('CUSTOM') { assert true }
148
+ # end
149
+ #
150
+ # def test_two
151
+ # subset_test('CUSTOM') { assert true }
152
+ # end
153
+ #
154
+ # Condition Tests that get run
155
+ # ENV['ALL']=true test_one, test_two
156
+ # ENV['CUSTOM']=true test_one, test_two
157
+ # ENV['CUSTOM_TEST']=test_ test_one, test_two
158
+ # ENV['CUSTOM_TEST']=test_one test_one
159
+ # ENV['CUSTOM']=nil no tests get run
160
+ #
161
+ # If you're running your tests with rake (or rap), ENV variables may be
162
+ # set from the command line.
163
+ #
164
+ # # all tests
165
+ # % rap test all=true
166
+ #
167
+ # # custom subset tests
168
+ # % rap test custom=true
169
+ #
170
+ # # just test_one (it is the only test
171
+ # # matching the '.est_on.' pattern)
172
+ # % rap test custom_test=.est_on.
173
+ #
174
+ def subset_test(type, skip=type[0..0].downcase)
175
+ type = type.upcase
176
+ if run_subset?(type) || ENV[type]
177
+ if match_regexp?("#{type}_TEST", name.to_s)
178
+ yield
179
+ else
180
+ print skip
181
+ end
182
+ else
183
+ print skip
184
+ end
185
+ end
186
+
187
+ # Declares a subset_test for the ENV variable 'EXTENDED'.
188
+ # Prints 'x' if the test is not run.
189
+ #
190
+ # def test_some_long_process
191
+ # extended_test { ... }
192
+ # end
193
+ def extended_test(&block)
194
+ subset_test("EXTENDED", "x", &block)
195
+ end
196
+
197
+ # Declares a subset_test for the ENV variable 'BENCHMARK'. If run,
198
+ # benchmark_test sets up benchmarking using the Benchmark.bm method
199
+ # using the input length and block. Prints 'b' if the test is not run.
200
+ #
201
+ # include Benchmark
202
+ # def test_speed
203
+ # benchmark_test(10) {|x| ... }
204
+ # end
205
+ def benchmark_test(length=10, &block)
206
+ subset_test("BENCHMARK") do
207
+ puts
208
+ puts name
209
+ Benchmark.bm(length, &block)
210
+ end
211
+ end
212
+
213
+ # Declares a subset_test for the ENV variable 'PROMPT'. When run, prompts
214
+ # the user for each input specified in array. Inputs will then be passed
215
+ # as a hash to the block. Prints 'p' unless run.
216
+ #
217
+ # def test_requiring_inputs
218
+ # prompt_test(:a, :b, :c) {|a, b, c| ... }
219
+ # end
220
+ #
221
+ # If run, the command line prompt will be like the following:
222
+ #
223
+ # test_requiring_inputs: Enter values or 'skip'
224
+ # a: avalue
225
+ # b: bvalue
226
+ # c: cvalue
227
+ #
228
+ # The block recieves ['avalue', 'bvalue', 'cvalue'].
229
+ def prompt_test(*keys, &block)
230
+ subset_test("PROMPT", "p") do
231
+ puts "\n#{name} -- Enter values or 'skip'."
232
+
233
+ values = keys.collect do |key|
234
+ print "#{key}: "
235
+ value = gets.strip
236
+ flunk "skipped test" if value =~ /skip/i
237
+ value
238
+ end
239
+
240
+ yield(*values)
241
+ end
242
+ end
243
+ end
244
+ end
245
+ end
@@ -0,0 +1,22 @@
1
+ module Tap
2
+ module Test
3
+
4
+ # Simply sets up and tears down Tap::App.instance so that tests that
5
+ # instantiate classes will not inadvertently smush over into one another.
6
+ module TapTest
7
+
8
+ # The test specific app
9
+ attr_reader :app
10
+
11
+ def setup
12
+ super
13
+ @app = Tap::App.instance = Tap::App.new(:debug => true, :quiet => true)
14
+ end
15
+
16
+ def teardown
17
+ Tap::App.instance = nil
18
+ super
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,83 @@
1
+ require 'test/unit'
2
+ require 'tap/test'
3
+
4
+ if Object.const_defined?(:MiniTest)
5
+
6
+ ################################
7
+ # MiniTest shims (ruby 1.9)
8
+ ################################
9
+
10
+ # Tap-Test adds a class skip_test method to TestCase to allow a full class
11
+ # to be skipped (for instance due to a failed Tap::Test::SubsetTest
12
+ # condition). The method is not required by any Tap-Test module.
13
+ class Test::Unit::TestCase
14
+ class << self
15
+ # Causes a test suite to be skipped. If a message is given, it will
16
+ # print and notify the user the test suite has been skipped.
17
+ def skip_test(msg=nil)
18
+ @@test_suites.delete(self)
19
+ puts "Skipping #{self}#{msg.empty? ? '' : ': ' + msg}"
20
+ end
21
+ end
22
+ end
23
+
24
+ # MiniTest renames method_name as name. For backwards compatibility
25
+ # (and for Tap::Test::FileTest) it must be added back.
26
+ class MiniTest::Unit::TestCase
27
+ def method_name
28
+ name
29
+ end
30
+ end
31
+
32
+ MiniTest::Unit::TestCase.extend Tap::Test
33
+
34
+ else
35
+
36
+ ################################
37
+ # Test::Unit shims (< ruby 1.9)
38
+ ################################
39
+ # :stopdoc:
40
+ # Implementing skip_test in the original Test::Unit is considerably more
41
+ # tricky than in the Mini::Test Test::Unit.
42
+ class Test::Unit::TestCase
43
+ class << self
44
+ alias tap_original_test_case_inherited inherited
45
+
46
+ def inherited(child)
47
+ super
48
+ tap_original_test_case_inherited(child)
49
+ child.instance_variable_set(:@skip_messages, [])
50
+ child.instance_variable_set(:@run_test_suite, true)
51
+ end
52
+
53
+ # Causes a test suite to be skipped. If a message is given, it will
54
+ # print and notify the user the test suite has been skipped.
55
+ def skip_test(msg=nil)
56
+ @run_test_suite = false
57
+ @skip_messages << msg
58
+ end
59
+
60
+ alias :original_suite :suite
61
+
62
+ # Modifies the default suite method to skip the suit unless
63
+ # run_test_suite is true. If the test is skipped, the skip_messages
64
+ # will be printed along with the default 'Skipping <Test>' message.
65
+ def suite # :nodoc:
66
+ if @run_test_suite
67
+ original_suite
68
+ else
69
+ skip_message = @skip_messages.compact.join(', ')
70
+ puts "Skipping #{name}#{skip_message.empty? ? '' : ': ' + skip_message}"
71
+
72
+ # return an empty test suite of the appropriate name
73
+ Test::Unit::TestSuite.new(name)
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ Test::Unit::TestCase.extend Tap::Test
80
+ # :startdoc:
81
+ end
82
+
83
+