assert 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/.gitignore +5 -0
  2. data/Gemfile +6 -0
  3. data/Gemfile.lock +17 -0
  4. data/README.rdoc +77 -0
  5. data/Rakefile +7 -0
  6. data/assert.gemspec +21 -0
  7. data/examples/empty_test.rb +5 -0
  8. data/examples/results_test.rb +25 -0
  9. data/examples/single_test.rb +9 -0
  10. data/lib/assert.rb +8 -0
  11. data/lib/assert/assertions.rb +253 -0
  12. data/lib/assert/context.rb +196 -0
  13. data/lib/assert/options.rb +43 -0
  14. data/lib/assert/rake_tasks.rb +95 -0
  15. data/lib/assert/result.rb +164 -0
  16. data/lib/assert/result_set.rb +14 -0
  17. data/lib/assert/runner.rb +60 -0
  18. data/lib/assert/setup/autorun.rb +34 -0
  19. data/lib/assert/setup/helpers.rb +62 -0
  20. data/lib/assert/setup/suite.rb +12 -0
  21. data/lib/assert/setup/view.rb +11 -0
  22. data/lib/assert/suite.rb +128 -0
  23. data/lib/assert/test.rb +90 -0
  24. data/lib/assert/version.rb +3 -0
  25. data/lib/assert/view/base.rb +54 -0
  26. data/lib/assert/view/terminal.rb +138 -0
  27. data/test/assertions/assert_block_test.rb +39 -0
  28. data/test/assertions/assert_instance_of_test.rb +43 -0
  29. data/test/assertions/assert_kind_of_test.rb +43 -0
  30. data/test/assertions/assert_not_block_test.rb +39 -0
  31. data/test/assertions/assert_not_instance_of_test.rb +43 -0
  32. data/test/assertions/assert_not_kind_of_test.rb +43 -0
  33. data/test/assertions/assert_not_respond_to_test.rb +43 -0
  34. data/test/assertions/assert_nothing_raised_test.rb +46 -0
  35. data/test/assertions/assert_raises_test.rb +49 -0
  36. data/test/assertions/assert_respond_to_test.rb +43 -0
  37. data/test/assertions_test.rb +334 -0
  38. data/test/context/class_methods_test.rb +314 -0
  39. data/test/context_test.rb +288 -0
  40. data/test/fixtures/inherited_stuff.rb +36 -0
  41. data/test/fixtures/sample_context.rb +13 -0
  42. data/test/helper.rb +52 -0
  43. data/test/irb.rb +10 -0
  44. data/test/options_test.rb +78 -0
  45. data/test/result_set_test.rb +89 -0
  46. data/test/result_test.rb +255 -0
  47. data/test/runner_test.rb +33 -0
  48. data/test/suite_test.rb +200 -0
  49. data/test/test/running_test.rb +327 -0
  50. data/test/test_test.rb +184 -0
  51. data/test/view_test.rb +35 -0
  52. metadata +155 -0
@@ -0,0 +1,196 @@
1
+ require 'assert/suite'
2
+ require 'assert/assertions'
3
+ require 'assert/result'
4
+
5
+ module Assert
6
+ class Context
7
+ include Assert::Assertions
8
+
9
+ # a Context is a scope for tests to run in. Contexts have setup and
10
+ # teardown blocks, subjects, and descriptions. Tests are run in the
11
+ # scope of a Context instance. Therefore, a Context should have
12
+ # minimal base logic/methods/instance_vars. The instance should remain
13
+ # pure to not pollute test scopes.
14
+
15
+ # if a class subclasses Context, add it to the suite
16
+ def self.inherited(klass)
17
+ Assert.suite << klass
18
+ end
19
+
20
+ # put all logic here to keep context instances pure for running tests
21
+ class << self
22
+ attr_accessor :subject_block
23
+
24
+ def setup_once(&block)
25
+ Assert.suite.setup(&block)
26
+ end
27
+ alias_method :before_once, :setup_once
28
+
29
+ def teardown_once(&block)
30
+ Assert.suite.teardown(&block)
31
+ end
32
+ alias_method :after_once, :teardown_once
33
+
34
+ def setup(&block)
35
+ raise ArgumentError, "please provide a setup block" unless block_given?
36
+ self.setup_blocks << block
37
+ end
38
+ alias_method :before, :setup
39
+
40
+ def teardown(&block)
41
+ raise ArgumentError, "please provide a teardown block" unless block_given?
42
+ self.teardown_blocks << block
43
+ end
44
+ alias_method :after, :teardown
45
+
46
+ def setup_blocks
47
+ @setup_blocks ||= []
48
+ end
49
+
50
+ def teardown_blocks
51
+ @teardown_blocks ||= []
52
+ end
53
+
54
+ def all_setup_blocks
55
+ inherited_blocks = if superclass.respond_to?(:all_setup_blocks)
56
+ superclass.all_setup_blocks
57
+ end
58
+ (inherited_blocks || []) + self.setup_blocks
59
+ end
60
+
61
+ def all_teardown_blocks
62
+ inherited_blocks = if superclass.respond_to?(:all_teardown_blocks)
63
+ superclass.all_teardown_blocks
64
+ end
65
+ (inherited_blocks || []) + self.teardown_blocks
66
+ end
67
+
68
+ def desc(text)
69
+ raise ArgumentError, "no context description provided" if text.nil?
70
+ self.descriptions << text
71
+ end
72
+ alias_method :description, :desc
73
+
74
+ def descriptions
75
+ @descriptions ||= []
76
+ end
77
+
78
+ def full_description
79
+ inherited_description = if superclass.respond_to?(:full_description)
80
+ superclass.full_description
81
+ end
82
+ parts = [ inherited_description ].push(self.descriptions).flatten.reject do |part|
83
+ !part || part.to_s.empty?
84
+ end
85
+ parts.join(" ") if !parts.empty?
86
+ end
87
+
88
+ def subject(&block)
89
+ raise ArgumentError, "please provide a subject block" unless block_given?
90
+ self.subject_block = block
91
+ end
92
+
93
+ def subject_block
94
+ @subject_block ||= if superclass.respond_to?(:subject_block)
95
+ superclass.subject_block
96
+ end
97
+ end
98
+
99
+ def should(desc, &block)
100
+ raise ArgumentError, "please provide a test block" unless block_given?
101
+ method_name = "test: should #{desc}"
102
+ if method_defined?(method_name)
103
+ from = caller.first
104
+ puts "WARNING: should #{desc.inspect} is redefining #{method_name}!"
105
+ puts " from: #{from}"
106
+ end
107
+ define_method(method_name, &block)
108
+ end
109
+
110
+ def should_eventually(desc, &block)
111
+ should(desc){ skip }
112
+ end
113
+
114
+ end
115
+
116
+ def initialize(running_test = nil)
117
+ @__running_test__ = running_test
118
+ end
119
+
120
+ # check if the assertion is a truthy value, if so create a new pass result, otherwise
121
+ # create a new fail result with the desc and what failed msg.
122
+ # all other assertion helpers use this one in the end
123
+ def assert(assertion, fail_desc=nil, what_failed_msg=nil)
124
+ what_failed_msg ||= "Failed assert: assertion was <#{assertion.inspect}>."
125
+ msg = fail_message(fail_desc) { what_failed_msg }
126
+ assertion ? pass : fail(msg)
127
+ end
128
+
129
+ # the opposite of assert, check if the assertion is a false value, if so create a new pass
130
+ # result, otherwise create a new fail result with the desc and it's what failed msg
131
+ def assert_not(assertion, fail_desc=nil)
132
+ what_failed_msg = "Failed assert_not: assertion was <#{assertion.inspect}>."
133
+ assert(!assertion, fail_desc, what_failed_msg)
134
+ end
135
+ alias_method :refute, :assert_not
136
+
137
+ # adds a Skip result to the end of the test's results and breaks test execution
138
+ def skip(skip_msg=nil)
139
+ raise(Result::TestSkipped, skip_msg || "")
140
+ end
141
+
142
+ # adds a Pass result to the end of the test's results
143
+ # does not break test execution
144
+ def pass(pass_msg=nil)
145
+ capture_result do |test_name, backtrace|
146
+ Assert::Result::Pass.new(test_name, pass_msg, backtrace)
147
+ end
148
+ end
149
+
150
+ # adds a Fail result to the end of the test's results
151
+ # does not break test execution
152
+ def fail(fail_msg=nil)
153
+ capture_result do |test_name, backtrace|
154
+ message = (fail_message(fail_msg) { }).call
155
+ Assert::Result::Fail.new(test_name, message, backtrace)
156
+ end
157
+ end
158
+ alias_method :flunk, :fail
159
+
160
+ # adds an Ignore result to the end of the test's results
161
+ # does not break test execution
162
+ def ignore(ignore_msg=nil)
163
+ capture_result do |test_name, backtrace|
164
+ Assert::Result::Ignore.new(test_name, ignore_msg, backtrace)
165
+ end
166
+ end
167
+
168
+ def subject
169
+ if subject_block = self.class.subject_block
170
+ instance_eval(&subject_block)
171
+ end
172
+ end
173
+
174
+ def inspect
175
+ "#<#{self.class}>"
176
+ end
177
+
178
+ protected
179
+
180
+ def capture_result
181
+ if block_given?
182
+ result = yield @__running_test__.name, caller
183
+ @__running_test__.results << result
184
+ result
185
+ end
186
+ end
187
+
188
+ # Returns a Proc that will output a custom message along with the default fail message.
189
+ def fail_message(fail_desc=nil, &what_failed)
190
+ fail_desc.kind_of?(::Proc) ? fail_desc : Proc.new do
191
+ [ what_failed.call, fail_desc ].compact.join("\n")
192
+ end
193
+ end
194
+
195
+ end
196
+ end
@@ -0,0 +1,43 @@
1
+ module Assert
2
+ module Options
3
+
4
+ class Base
5
+ def method_missing(method, *args, &block)
6
+ if args.empty?
7
+ self.instance_variable_get("@#{method}")
8
+ else
9
+ value = args.size == 1 ? args.first : args
10
+ self.instance_variable_set("@#{method}", value)
11
+ if method.to_s =~ /^default_/
12
+ self.instance_variable_set("@#{$'}", value)
13
+ end
14
+ end
15
+ end
16
+
17
+ end
18
+
19
+ def self.included(receiver)
20
+ receiver.send(:class_variable_set, "@@options", Base.new)
21
+ receiver.send(:extend, ClassMethods)
22
+ receiver.send(:include, InstanceMethods)
23
+ end
24
+
25
+ module ClassMethods
26
+ def options(&block)
27
+ options = self.send(:class_variable_get, "@@options")
28
+ if block_given?
29
+ options.instance_eval(&block)
30
+ else
31
+ options
32
+ end
33
+ end
34
+ end
35
+
36
+ module InstanceMethods
37
+ def options
38
+ self.class.options
39
+ end
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,95 @@
1
+ require 'rake'
2
+ require 'rake/tasklib'
3
+
4
+ module Assert; end
5
+ module Assert::RakeTasks
6
+
7
+ FILE_SUFFIX = "_test.rb"
8
+
9
+ class TestTask < Rake::TaskLib
10
+ attr_accessor :name, :description, :test_files
11
+
12
+ # Create a testing task.
13
+ def initialize(name=:test)
14
+ @name = name
15
+ @description = "Run tests" + (@name==:test ? "" : " for #{@name}")
16
+ @test_files = []
17
+ yield self if block_given?
18
+ end
19
+
20
+ # Define the rake task to run this test suite
21
+ def to_task
22
+ desc @description
23
+ task @name do
24
+ RakeFileUtils.verbose(true) { ruby "\"#{rake_loader}\" " + file_list }
25
+ end
26
+ end
27
+
28
+ def file_list # :nodoc:
29
+ @test_files.collect{|f| "\"#{f}\""}.join(' ')
30
+ end
31
+
32
+ protected
33
+
34
+ def rake_loader # :nodoc:
35
+ find_file('rake/rake_test_loader') or
36
+ fail "unable to find rake test loader"
37
+ end
38
+
39
+ def find_file(fn) # :nodoc:
40
+ $LOAD_PATH.each do |path|
41
+ file_path = File.join(path, "#{fn}.rb")
42
+ return file_path if File.exist? file_path
43
+ end
44
+ nil
45
+ end
46
+ end
47
+
48
+ class << self
49
+ def for(test_namespace = :test)
50
+ self.irb_task(test_namespace.to_s)
51
+ self.to_tasks(test_namespace.to_s)
52
+ end
53
+
54
+ def irb_task(path)
55
+ irb_file = File.join(path, "irb.rb")
56
+ if File.exist?(irb_file)
57
+ desc "Open irb preloaded with #{irb_file}"
58
+ task :irb do
59
+ sh "irb -rubygems -r ./#{irb_file}"
60
+ end
61
+ end
62
+ end
63
+
64
+ def to_tasks(path)
65
+ suite_name = File.basename(path)
66
+
67
+ # define a rake test task for all top-level test files at this path
68
+ if !Dir.glob(File.join(path, "**/*#{FILE_SUFFIX}")).empty?
69
+ TestTask.new(suite_name.to_sym) do |t|
70
+ file_location = suite_name == path ? '' : " for #{File.join(path.split(File::SEPARATOR)[1..-1])}"
71
+ t.description = "Run all tests#{file_location}"
72
+ t.test_files = FileList["#{path}/**/*#{FILE_SUFFIX}"]
73
+ end.to_task
74
+ end
75
+
76
+ namespace suite_name.to_s do
77
+ # define rake test tasks for each top-level test file individually
78
+ Dir.glob(File.join(path, "*#{FILE_SUFFIX}")).each do |test_file|
79
+ test_name = File.basename(test_file, FILE_SUFFIX)
80
+ TestTask.new(test_name.to_sym) do |t|
81
+ t.description = "Run tests for #{[path.split(File::SEPARATOR), test_name].flatten[1..-1].join(':')}"
82
+ t.test_files = FileList[test_file]
83
+ end.to_task
84
+ end
85
+
86
+ # recursively define rake test tasks for each file
87
+ # in each top-level directory
88
+ Dir.glob(File.join(path, "*/")).each do |test_dir|
89
+ self.to_tasks(test_dir)
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ end
@@ -0,0 +1,164 @@
1
+ module Assert; end
2
+ module Assert::Result
3
+
4
+ class Base; end
5
+ class Pass < Base; end
6
+ class Fail < Base; end
7
+ class Ignore < Base; end
8
+ class FromException < Base; end
9
+ class Error < FromException; end
10
+ class Skip < FromException; end
11
+
12
+ class << self
13
+ def types
14
+ { :pass => Pass,
15
+ :fail => Fail,
16
+ :ignore => Ignore,
17
+ :skip => Skip,
18
+ :error => Error
19
+ }
20
+ end
21
+ end
22
+
23
+ class Backtrace < ::Array
24
+ # ripped from minitest...
25
+
26
+ file = File.expand_path __FILE__
27
+ # if RUBY_VERSION =~ /^1\.9/ then # bt's expanded, but __FILE__ isn't :(
28
+ # File.expand_path __FILE__
29
+ # elsif __FILE__ =~ /^[^\.]/ then # assume both relative
30
+ # require 'pathname'
31
+ # pwd = Pathname.new Dir.pwd
32
+ # pn = Pathname.new File.expand_path(__FILE__)
33
+ # relpath = pn.relative_path_from(pwd) rescue pn
34
+ # pn = File.join ".", relpath unless pn.relative?
35
+ # pn.to_s
36
+ # else # assume both are expanded
37
+ # __FILE__
38
+ # end
39
+
40
+ # './lib' in project dir, or '/usr/local/blahblah' if installed
41
+ ASSERT_DIR = File.dirname(File.dirname(file))
42
+
43
+ def initialize(value=nil)
44
+ super(value || ["No backtrace"])
45
+ end
46
+
47
+ def to_s
48
+ self.join("\n")
49
+ end
50
+
51
+ def filtered
52
+ new_bt = []
53
+
54
+ self.each do |line|
55
+ break if line.rindex ASSERT_DIR, 0
56
+ new_bt << line
57
+ end
58
+
59
+ new_bt = self.reject { |line| line.rindex ASSERT_DIR, 0 } if new_bt.empty?
60
+ new_bt = self.dup if new_bt.empty?
61
+
62
+ self.class.new(new_bt)
63
+ end
64
+ end
65
+
66
+
67
+ # Result classes...
68
+
69
+ class Base
70
+
71
+ attr_reader :test_name, :message, :backtrace
72
+
73
+ def initialize(test_name, message, backtrace=nil)
74
+ @backtrace = Backtrace.new(backtrace)
75
+ @test_name = test_name
76
+ @message = message && !message.empty? ? message : nil
77
+ end
78
+
79
+ Assert::Result.types.keys.each do |meth|
80
+ define_method("#{meth}?") { false }
81
+ end
82
+
83
+ def to_sym; nil; end
84
+
85
+ def to_s
86
+ [self.test_name, self.message, self.trace].compact.join("\n")
87
+ end
88
+
89
+ def trace
90
+ self.backtrace.filtered.first.to_s
91
+ end
92
+
93
+ def ==(other)
94
+ self.class == other.class && self.message == other.message
95
+ end
96
+
97
+ def inspect
98
+ "#<#{self.class} @message=#{self.message.inspect}>"
99
+ end
100
+
101
+ end
102
+
103
+ class Pass < Base
104
+ def pass?; true; end
105
+ def to_sym; :passed; end
106
+
107
+ def to_s
108
+ "PASS: #{super}"
109
+ end
110
+ end
111
+
112
+ class Fail < Base
113
+ def fail?; true; end
114
+ def to_sym; :failed; end
115
+
116
+ def to_s
117
+ "FAIL: #{super}"
118
+ end
119
+ end
120
+
121
+ class Ignore < Base
122
+ def ignore?; true; end
123
+ def to_sym; :ignored; end
124
+
125
+ def to_s
126
+ "IGNORE: #{super}"
127
+ end
128
+ end
129
+
130
+ # Error and Skip results are built from exceptions being raised
131
+ class FromException < Base
132
+ def initialize(test_name, exception)
133
+ super(test_name, exception.message, exception.backtrace || [])
134
+ end
135
+ end
136
+
137
+ # raised by the 'skip' context helper to break test execution
138
+ class TestSkipped < RuntimeError; end
139
+
140
+ class Skip < FromException
141
+ def skip?; true; end
142
+ def to_sym; :skipped; end
143
+
144
+ def to_s
145
+ "SKIP: #{super}"
146
+ end
147
+ end
148
+
149
+ class Error < FromException
150
+
151
+ def error?; true; end
152
+ def to_sym; :errored; end
153
+
154
+ def to_s
155
+ "ERROR: #{super}"
156
+ end
157
+
158
+ # override of the base, always show the full unfiltered backtrace for errors
159
+ def trace
160
+ self.backtrace.to_s
161
+ end
162
+ end
163
+
164
+ end