tap-test 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+