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,14 @@
1
+ module Assert
2
+ class ResultSet < ::Array
3
+
4
+ attr_accessor :view
5
+
6
+ def <<(result)
7
+ super
8
+ if @view && @view.respond_to?(:print_runtime_result)
9
+ @view.print_runtime_result(result)
10
+ end
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,60 @@
1
+ require 'assert/view/terminal'
2
+
3
+ module Assert
4
+ class Runner
5
+
6
+ # a Runner runs a suite of tests.
7
+
8
+ def initialize(suite, view)
9
+ raise ArgumentError if !suite.kind_of?(Suite)
10
+ @suite = suite
11
+ @view = view
12
+ end
13
+
14
+ def run(*args)
15
+ @suite.setup_blocks.each do |setup| # TODO: tests!
16
+ setup.call
17
+ end
18
+ @view.render do
19
+ benchmark { run_suite }
20
+ end
21
+ @suite.teardown_blocks.each do |teardown| # TODO: tests!
22
+ teardown.call
23
+ end
24
+ count(:failed) + count(:errored)
25
+ end
26
+
27
+ def count(type)
28
+ @suite.count(type)
29
+ end
30
+
31
+ protected
32
+
33
+ def tests_to_run
34
+ tests = @suite.tests
35
+
36
+ # order tests randomly
37
+ max = tests.size
38
+ srand
39
+ seed = srand % 0xFFFF
40
+ srand seed
41
+ tests.sort.sort_by { rand max }
42
+ tests
43
+ end
44
+
45
+ private
46
+
47
+ def benchmark
48
+ @suite.start_time = Time.now
49
+ yield if block_given?
50
+ @suite.end_time = Time.now
51
+ end
52
+
53
+ def run_suite
54
+ # TODO: parallel running
55
+ tests_to_run.each {|test| test.run(@view)}
56
+ end
57
+
58
+ end
59
+
60
+ end
@@ -0,0 +1,34 @@
1
+ require 'assert/runner'
2
+
3
+ module Assert
4
+
5
+ # a flag to know if at_exit hook has been installed already
6
+ @@at_exit_installed ||= false
7
+
8
+ class << self
9
+
10
+ # install at_exit hook (if needed) (runs at process exit)
11
+ # this ensures the test suite won't run unitl all test files are loaded
12
+ # (this is essentially a direct rip from Minitest)
13
+ def autorun
14
+ at_exit do
15
+ # don't run if there was an exception
16
+ next if $!
17
+
18
+ # the order here is important. The at_exit handler must be
19
+ # installed before anyone else gets a chance to install their
20
+ # own, that way we can be assured that our exit will be last
21
+ # to run (at_exit stacks).
22
+
23
+ exit_code = nil
24
+ at_exit { exit(false) if exit_code && exit_code != 0 }
25
+ # TODO: read options from a config for extentions??
26
+ Runner.new(self.suite, self.view).run
27
+ end unless @@at_exit_installed
28
+ @@at_exit_installed = true
29
+ end
30
+
31
+ end
32
+
33
+ end
34
+
@@ -0,0 +1,62 @@
1
+ module Assert
2
+ module Helpers
3
+
4
+ # when Assert is required it will automatically require in two helper files
5
+ # if they exist:
6
+ # * "./test/helper.rb - package-specific helpers
7
+ # * ~/.assert.rb - user-specific helpers (options, view, etc...)
8
+ # the user-specific helper file will always be required in after the
9
+ # package-specific one
10
+
11
+ class << self
12
+
13
+ # assume the test dir path is ./test and look for helpers in ./test/helper.rb
14
+ PACKAGE_TEST_DIR = "test"
15
+ PACKAGE_HELPER_FILE = "helper"
16
+ TEST_REGEX = /^#{PACKAGE_TEST_DIR}$|^#{PACKAGE_TEST_DIR}\/|\/#{PACKAGE_TEST_DIR}\/|\/#{PACKAGE_TEST_DIR}$/
17
+
18
+ USER_TEST_HELPER = "~/.assert"
19
+
20
+ def load(caller_info)
21
+ if (crp = caller_root_path(caller_info))
22
+ require_package_test_helper(crp)
23
+ end
24
+ require_user_test_helper
25
+ end
26
+
27
+ private
28
+
29
+ def require_user_test_helper
30
+ begin
31
+ require File.expand_path(USER_TEST_HELPER)
32
+ rescue LoadError => err
33
+ # do nothing
34
+ end
35
+ end
36
+
37
+ # require the package's test/helper file if it exists
38
+ def require_package_test_helper(root_path)
39
+ begin
40
+ require package_helper_file(root_path)
41
+ rescue LoadError => err
42
+ warn err.message
43
+ end
44
+ end
45
+
46
+ def package_helper_file(root_path)
47
+ File.join(root_path, PACKAGE_TEST_DIR, PACKAGE_HELPER_FILE+'.rb')
48
+ end
49
+
50
+ # this method inspects the caller info and finds the caller's root path
51
+ # this expects the caller's root path to be the parent dir of the first
52
+ # parent dir of caller named TEST_DIR
53
+ def caller_root_path(caller_info)
54
+ caller_dirname = File.expand_path(File.dirname(caller_info[0]))
55
+ if (test_dir_pos = caller_dirname.index(TEST_REGEX)) > 0
56
+ root_dir = caller_dirname[0..(test_dir_pos-1)]
57
+ end
58
+ end
59
+ end
60
+
61
+ end
62
+ end
@@ -0,0 +1,12 @@
1
+ require 'assert/context'
2
+ require 'assert/suite'
3
+
4
+ module Assert
5
+
6
+ # Setup the default global suite for collecting tests as contexts are defined
7
+ @@suite = Suite.new
8
+ class << self
9
+ def suite; @@suite; end
10
+ end
11
+
12
+ end
@@ -0,0 +1,11 @@
1
+ require 'assert/view/terminal'
2
+
3
+ module Assert
4
+
5
+ # Setup the default view to render test results (override in user or package helpers)
6
+ @@view = View::Terminal.new(self.suite, $stdout)
7
+ class << self
8
+ def view; @@view; end
9
+ end
10
+
11
+ end
@@ -0,0 +1,128 @@
1
+ require 'assert/test'
2
+
3
+ module Assert
4
+ class Suite < ::Hash
5
+
6
+ # A suite is a set of tests to run. When a test class subclasses
7
+ # the Context class, that test class is pushed to the suite.
8
+
9
+ attr_accessor :start_time, :end_time, :setup_blocks, :teardown_blocks
10
+
11
+ def run_time
12
+ (@end_time || 0) - (@start_time || 0)
13
+ end
14
+
15
+ def <<(context_klass)
16
+ # gsub off any trailing 'Test'
17
+ self[context_klass] ||= []
18
+ end
19
+
20
+ def contexts
21
+ self.keys.sort{|a,b| a.to_s <=> b.to_s}
22
+ end
23
+
24
+ def tests
25
+ prep
26
+ self.values.flatten
27
+ end
28
+
29
+ def ordered_tests(klass=nil)
30
+ prep
31
+ (klass.nil? ? self.contexts : [klass]).inject([]) do |tests, klass|
32
+ tests += (self[klass] || [])
33
+ end
34
+ end
35
+
36
+ def ordered_results(klass=nil)
37
+ ordered_tests(klass).inject([]) do |results, test|
38
+ results += test.results
39
+ end
40
+ end
41
+
42
+ def test_count(klass=nil)
43
+ prep
44
+ count_tests(klass.nil? ? self.values : [self[klass]])
45
+ end
46
+
47
+ def result_count(type=nil)
48
+ prep
49
+ count_results(self.values, type)
50
+ end
51
+
52
+ def count(thing)
53
+ case thing
54
+ when :tests
55
+ test_count
56
+ when :results
57
+ result_count
58
+ when :passed, :pass
59
+ result_count(:pass)
60
+ when :failed, :fail
61
+ result_count(:fail)
62
+ when :ignored, :ignore
63
+ result_count(:ignore)
64
+ when :skipped, :skip
65
+ result_count(:skip)
66
+ when :errored, :error
67
+ result_count(:error)
68
+ else
69
+ 0
70
+ end
71
+ end
72
+
73
+ def setup_blocks
74
+ @setup_blocks ||= []
75
+ end
76
+
77
+ def teardown_blocks
78
+ @teardown_blocks ||= []
79
+ end
80
+
81
+ # TODO: tests!
82
+ def setup(&block)
83
+ raise ArgumentError, "please provide a setup block" unless block_given?
84
+ self.setup_blocks << block
85
+ end
86
+
87
+ # TODO: tests!
88
+ def teardown(&block)
89
+ raise ArgumentError, "please provide a teardown block" unless block_given?
90
+ self.teardown_blocks << block
91
+ end
92
+
93
+ protected
94
+
95
+ def local_public_test_methods(klass)
96
+ methods = klass.public_instance_methods
97
+ while (klass.superclass)
98
+ methods -= (klass = klass.superclass).public_instance_methods
99
+ end
100
+ methods.delete_if {|method_name| method_name !~ /^test./ }
101
+ end
102
+
103
+ private
104
+
105
+ def count_tests(test_sets)
106
+ test_sets.inject(0) {|count, tests| count += tests.size}
107
+ end
108
+
109
+ def count_results(test_sets, type)
110
+ self.values.flatten.inject(0){|count, test| count += test.result_count(type) }
111
+ end
112
+
113
+ def prep
114
+ if @prepared != true
115
+ # look for local public methods starting with 'test_'and add
116
+ self.each do |context_class, tests|
117
+ local_public_test_methods(context_class).each do |meth|
118
+ tests << Test.new(meth.to_s, context_class, meth)
119
+ end
120
+ tests.uniq
121
+ end
122
+ end
123
+ @prepared = true
124
+ end
125
+
126
+ end
127
+
128
+ end
@@ -0,0 +1,90 @@
1
+ require 'assert/result'
2
+ require 'assert/result_set'
3
+
4
+ module Assert
5
+ class Test
6
+
7
+ # a Test is some code/method to run in the scope of a Context. After a
8
+ # a test runs, it should have some assertions which are its results.
9
+
10
+ attr_reader :name, :code, :context_class
11
+ attr_accessor :results
12
+
13
+ def initialize(name, context_class, code = nil, &block)
14
+ @context_class = context_class
15
+ @name = name_from_context(name)
16
+ @code = (code || block)
17
+ @results = ResultSet.new
18
+ end
19
+
20
+ def run(view=nil)
21
+ @results.view = view
22
+ begin
23
+ run_scope = @context_class.new(self)
24
+ run_setup(run_scope)
25
+ if @code.kind_of?(::Proc)
26
+ run_scope.instance_eval(&@code)
27
+ elsif run_scope.respond_to?(@code.to_s)
28
+ run_scope.send(@code.to_s)
29
+ end
30
+ rescue Result::TestSkipped => err
31
+ @results << Result::Skip.new(self.name, err)
32
+ rescue Exception => err
33
+ @results << Result::Error.new(self.name, err)
34
+ ensure
35
+ begin
36
+ run_teardown(run_scope) if run_scope
37
+ rescue Exception => teardown_err
38
+ @results << Result::Error.new(self.name, teardown_err)
39
+ end
40
+ end
41
+ @results.view = nil
42
+ @results
43
+ end
44
+
45
+ def run_setup(scope)
46
+ @context_class.all_setup_blocks.each do |setup|
47
+ scope.instance_eval(&setup)
48
+ end
49
+ end
50
+
51
+ def run_teardown(scope)
52
+ @context_class.all_teardown_blocks.each do |teardown|
53
+ scope.instance_eval(&teardown)
54
+ end
55
+ end
56
+
57
+ Assert::Result.types.each do |name, klass|
58
+ define_method "#{name}_results" do
59
+ @results.select{|r| r.kind_of?(klass) }
60
+ end
61
+ end
62
+
63
+ def result_count(type=nil)
64
+ if Assert::Result.types.include?(type)
65
+ self.send("#{type}_results").size
66
+ else
67
+ @results.size
68
+ end
69
+ end
70
+
71
+ def <=>(other_test)
72
+ self.name <=> other_test.name
73
+ end
74
+
75
+ def inspect
76
+ attributes_string = ([ :name, :context_class, :results ].collect do |attr|
77
+ "@#{attr}=#{self.send(attr).inspect}"
78
+ end).join(" ")
79
+ "#<#{self.class} #{attributes_string}>"
80
+ end
81
+
82
+ protected
83
+
84
+ def name_from_context(name)
85
+ name = name.gsub(/^test:\s+should/, "should")
86
+ [ @context_class.full_description, name ].compact.join(" ")
87
+ end
88
+
89
+ end
90
+ end
@@ -0,0 +1,3 @@
1
+ module Assert
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,54 @@
1
+ require 'assert/options'
2
+
3
+ module Assert::View
4
+
5
+ class Base
6
+ include Assert::Options
7
+
8
+ attr_reader :suite
9
+
10
+ def initialize(suite, output_io)
11
+ @suite = suite
12
+ @out = output_io
13
+ end
14
+
15
+ # override this to define how a view calls the runner and renders its results
16
+ def render(*args, &runner)
17
+ end
18
+
19
+ def print_runtime_result(result)
20
+ sym = result.to_sym
21
+ if self.respond_to?(:options)
22
+ io_print(self.options.send("#{sym}_abbrev"))
23
+ end
24
+ end
25
+
26
+ protected
27
+
28
+ def io_puts(msg, opts={})
29
+ @out.puts(io_msg(msg, opts={}))
30
+ end
31
+
32
+ def io_print(msg, opts={})
33
+ @out.print(io_msg(msg, opts={}))
34
+ end
35
+
36
+ def io_msg(msg, opts={})
37
+ if msg.kind_of?(::Symbol) && self.respond_to?(msg)
38
+ self.send(msg).to_s
39
+ else
40
+ msg.to_s
41
+ end
42
+ end
43
+
44
+ def run_time(format='%.6f')
45
+ format % @suite.run_time
46
+ end
47
+
48
+ def count(type)
49
+ @suite.count(type)
50
+ end
51
+
52
+ end
53
+
54
+ end