tokyo 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,34 @@
1
+ module Tokyo
2
+
3
+ def refute_thrown_as_expected object, expected_symbol, block = nil
4
+ f = refute_thrown(object, expected_symbol)
5
+ return f if f
6
+
7
+ if expected_symbol
8
+ f = refute_expected_symbol_thrown(object, expected_symbol)
9
+ return f if f
10
+ end
11
+ nil
12
+ end
13
+
14
+ def refute_thrown object, should_throw = false
15
+ if should_throw
16
+ return [
17
+ 'Expected a symbol to be thrown at %s' % object[:caller]
18
+ ] unless object[:thrown]
19
+ else
20
+ return [
21
+ 'Not expected a symbol to be thrown at %s' % object[:caller]
22
+ ] if object[:thrown]
23
+ end
24
+ nil
25
+ end
26
+
27
+ def refute_expected_symbol_thrown object, expected_symbol
28
+ return [
29
+ 'Not expected :%s to be thrown' % expected_symbol,
30
+ 'at %s' % object[:caller]
31
+ ] if expected_symbol == object[:thrown]
32
+ nil
33
+ end
34
+ end
data/lib/tokyo/util.rb ADDED
@@ -0,0 +1,91 @@
1
+ module Tokyo
2
+
3
+ def call_block block
4
+ {returned: block.call, caller: relative_source_location(block)}.freeze
5
+ rescue UncaughtThrowError => e
6
+ {raised: e, thrown: extract_thrown_symbol(e), caller: relative_source_location(block)}.freeze
7
+ rescue Exception => e
8
+ {raised: e, caller: relative_source_location(block)}.freeze
9
+ end
10
+
11
+ # extract thrown symbol from given exception
12
+ #
13
+ # @param exception
14
+ #
15
+ def extract_thrown_symbol exception
16
+ return unless exception.is_a?(Exception)
17
+ return unless s = exception.message.scan(/uncaught throw\W+(\w+)/).flatten[0]
18
+ s.to_sym
19
+ end
20
+
21
+ def identity_string type, label, block
22
+ '%s %s (%s:%s)' % [
23
+ blue(type),
24
+ label.inspect,
25
+ *relative_source_location(block)
26
+ ]
27
+ end
28
+
29
+ def relative_source_location block
30
+ return unless block
31
+ [
32
+ relative_location(block.source_location[0]),
33
+ block.source_location[1]
34
+ ]
35
+ end
36
+
37
+ def relative_location line
38
+ line.sub(/\A#{pwd}\/+/, '')
39
+ end
40
+
41
+ def pretty_backtrace e
42
+ Array(e.backtrace).map {|l| relative_location(l)}
43
+ end
44
+
45
+ def readline caller
46
+ file, line = caller_to_source_location(caller)
47
+ return unless file && line
48
+ lines = ((@__readlinecache__ ||= {})[file] ||= File.readlines(file))
49
+ return unless line = lines[line.to_i - 1]
50
+ line.sub(/(do|\{)\Z/, '').strip
51
+ end
52
+
53
+ def caller_to_source_location caller
54
+ file, line = caller.split(/:(\d+):in.+/)
55
+ [relative_location(file), line]
56
+ end
57
+
58
+ def find_files pattern_or_files
59
+ return pattern_or_files if pattern_or_files.is_a?(Array)
60
+ Dir[pwd(pattern_or_files)]
61
+ end
62
+
63
+ def load_file file
64
+ augment_load_path(file)
65
+ require(file)
66
+ end
67
+
68
+ def augment_load_path file
69
+ # adding ./
70
+ $:.unshift(pwd) unless $:.include?(pwd)
71
+
72
+ # adding ./lib/
73
+ lib = pwd('lib')
74
+ unless $:.include?(lib)
75
+ $:.unshift(lib) if File.directory?(lib)
76
+ end
77
+
78
+ # adding file's dirname
79
+ dir = File.dirname(file)
80
+ $:.unshift(dir) unless $:.include?(dir)
81
+ end
82
+
83
+ def pwd *args
84
+ File.join(Dir.pwd, *args.map!(&:to_s))
85
+ end
86
+ end
87
+
88
+ require 'tokyo/util/assert_raise'
89
+ require 'tokyo/util/refute_raise'
90
+ require 'tokyo/util/assert_throw'
91
+ require 'tokyo/util/refute_throw'
data/lib/tokyo.rb ADDED
@@ -0,0 +1,121 @@
1
+ require 'tty-progressbar'
2
+ require 'tty-screen'
3
+ require 'pastel'
4
+ require 'json'
5
+
6
+ module Tokyo
7
+ extend self
8
+
9
+ DEFAULT_PATTERN = '{spec,test}/**/{*_spec.rb,*_test.rb}'.freeze
10
+ GLOBAL_SETUPS = []
11
+
12
+ GenericFailure = Struct.new(:reason, :caller)
13
+ AssertionFailure = Struct.new(:object, :arguments, :caller)
14
+
15
+ Skip = Struct.new(:reason, :caller)
16
+
17
+ INDENT = ' '.freeze
18
+ PASTEL = Pastel.new
19
+ %w[
20
+ red
21
+ bright_red
22
+ green
23
+ blue
24
+ cyan
25
+ magenta
26
+ bold
27
+ underline
28
+ ].each {|m| define_method(m) {|*a| PASTEL.__send__(m, *a)}}
29
+
30
+ def units
31
+ @units ||= []
32
+ end
33
+
34
+ def assertions
35
+ @assertions ||= {}
36
+ end
37
+
38
+ def total_assertions
39
+ @total_assertions ||= []
40
+ end
41
+
42
+ def skips
43
+ @skips ||= []
44
+ end
45
+
46
+ def define_spec label, block
47
+ define_unit_class(:spec, label, block, [].freeze)
48
+ end
49
+
50
+ def define_and_register_a_spec label, block
51
+ units << define_spec(label, block)
52
+ end
53
+
54
+ def define_context label, block, parent
55
+ define_unit_class(:context, label, block, [*parent.__ancestors__, parent].freeze)
56
+ end
57
+
58
+ def define_and_register_a_context label, block, parent
59
+ units << define_context(label, block, parent)
60
+ end
61
+
62
+ # define a class that will hold contexts and tests
63
+ #
64
+ # @param [String, Symbol] type
65
+ # @param [String, Symbol] label
66
+ # @param [Proc] block
67
+ # @param [Array] ancestors
68
+ # @return [Unit]
69
+ #
70
+ def define_unit_class type, label, block, ancestors
71
+ identity = identity_string(type, label, block).freeze
72
+ Class.new ancestors.last || Unit do
73
+ define_singleton_method(:__ancestors__) {ancestors}
74
+ define_singleton_method(:__identity__) {identity}
75
+ Tokyo::GLOBAL_SETUPS.each {|b| class_exec(&b)}
76
+ # execute given block only after global setups executed and all utility methods defined
77
+ result = catch(:__tokyo_skip__) {class_exec(&block)}
78
+ Tokyo.skips << result if result.is_a?(Skip)
79
+ end
80
+ end
81
+
82
+ # define a module that when included will execute the given block on base
83
+ #
84
+ # @param [Proc] block
85
+ # @return [Module]
86
+ #
87
+ def define_unit_module block
88
+ block || raise(ArgumentError, 'missing block')
89
+ Module.new do
90
+ # any spec/context that will include this module will "inherit" it's logic
91
+ #
92
+ # @example
93
+ # EnumeratorSpec = spec 'Enumerator tests' do
94
+ # # some tests here
95
+ # end
96
+ #
97
+ # spec Array do
98
+ # include EnumeratorSpec
99
+ # end
100
+ #
101
+ # spec Hash do
102
+ # include EnumeratorSpec
103
+ # end
104
+ #
105
+ define_singleton_method(:included) {|b| b.class_exec(&block)}
106
+ end
107
+ end
108
+
109
+ # stop any code and report a failure
110
+ def fail reason, caller
111
+ throw(:__tokyo_status__, GenericFailure.new(Array(reason), caller))
112
+ end
113
+ end
114
+
115
+ require 'tokyo/core_ext'
116
+ require 'tokyo/pretty_print'
117
+ require 'tokyo/expectations'
118
+ require 'tokyo/util'
119
+ require 'tokyo/unit'
120
+ require 'tokyo/assert'
121
+ require 'tokyo/run'
@@ -0,0 +1,98 @@
1
+ describe :assert do
2
+
3
+ it 'pass ==' do
4
+ x = mock(:==, :x)
5
+ x == :x
6
+ x.verify
7
+ end
8
+
9
+ it 'pass ===' do
10
+ x = mock(:===, :x)
11
+ x === :x
12
+ x.verify
13
+ end
14
+
15
+ it 'pass !=' do
16
+ x = mock(:!=, :x)
17
+ x != :x
18
+ x.verify
19
+ end
20
+
21
+ it 'pass >' do
22
+ x = mock(:>, :x)
23
+ x > :x
24
+ x.verify
25
+ end
26
+
27
+ it 'pass >=' do
28
+ x = mock(:>=, :x)
29
+ x >= :x
30
+ x.verify
31
+ end
32
+
33
+ it 'pass <' do
34
+ x = mock(:<, :x)
35
+ x < :x
36
+ x.verify
37
+ end
38
+
39
+ it 'pass <=' do
40
+ x = mock(:<=, :x)
41
+ x <= :x
42
+ x.verify
43
+ end
44
+
45
+ it 'pass eql?' do
46
+ x = mock(:eql?, :x)
47
+ x.eql? :x
48
+ x.verify
49
+ end
50
+
51
+ it 'pass equal?' do
52
+ x = mock(:equal?, :x)
53
+ x.equal? :x
54
+ x.verify
55
+ end
56
+
57
+ it 'pass =~' do
58
+ x = mock(:=~, :x)
59
+ x =~ :x
60
+ x.verify
61
+ end
62
+
63
+ it 'pass match' do
64
+ x = mock(:match, :x)
65
+ x.match :x
66
+ x.verify
67
+ end
68
+
69
+ it 'pass any?' do
70
+ m = mock(:any?)
71
+ m.any?
72
+ m.verify
73
+ end
74
+
75
+ it 'pass all?' do
76
+ x = mock(:all?, :x)
77
+ x.all? :x
78
+ x.verify
79
+ end
80
+
81
+ it 'pass start_with?' do
82
+ x = mock(:start_with?, :x)
83
+ x.start_with? :x
84
+ x.verify
85
+ end
86
+
87
+ it 'pass end_with?' do
88
+ x = mock(:end_with?, :x)
89
+ x.end_with? :x
90
+ x.verify
91
+ end
92
+
93
+ it 'pass respond_to?' do
94
+ x = mock(:respond_to?, :__id__, true)
95
+ x.respond_to? :__id__, true
96
+ x.verify
97
+ end
98
+ end
@@ -0,0 +1,52 @@
1
+ describe :context_inheritance_test do
2
+
3
+ it 'inherits from parent spec' do
4
+ x = nil
5
+ spec rand do
6
+ define_method(:set_x) {x = true}
7
+ context rand do
8
+ allocate.send(:set_x)
9
+ end
10
+ end
11
+ assert_equal true, x
12
+ end
13
+
14
+ it 'inherits from parent context' do
15
+ x = nil
16
+ spec rand do
17
+ context rand do
18
+ define_method(:set_x) {x = true}
19
+ context rand do
20
+ allocate.send(:set_x)
21
+ end
22
+ end
23
+ end
24
+ assert_equal true, x
25
+ end
26
+
27
+ it 'inherits from parent spec through parent context' do
28
+ x = nil
29
+ spec rand do
30
+ define_method(:set_x) {x = true}
31
+ context rand do
32
+ context rand do
33
+ allocate.send(:set_x)
34
+ end
35
+ end
36
+ end
37
+ assert_equal true, x
38
+ end
39
+
40
+ it 'does not inherit tests' do
41
+ spec_tests, context_tests = nil
42
+ spec rand do
43
+ test(:x) {}
44
+ context rand do
45
+ context_tests = tests.size
46
+ end
47
+ spec_tests = tests.size
48
+ end
49
+ assert_equal 1, spec_tests
50
+ assert_equal 0, context_tests
51
+ end
52
+ end
@@ -0,0 +1,35 @@
1
+ describe :hooks do
2
+
3
+ it 'calls before and after hooks' do
4
+ called = 0
5
+ spec rand do
6
+ before {called += 1}
7
+ after {called += 1}
8
+ test(:x) {}
9
+ run(:x)
10
+ end
11
+ assert_equal 2, called
12
+ end
13
+
14
+ it 'calls around hooks' do
15
+ called = false
16
+ spec rand do
17
+ around {|test| test.call}
18
+ test(:x) {called = true}
19
+ run(:x)
20
+ end
21
+ assert_equal true, called
22
+ end
23
+
24
+ it 'inherits hooks' do
25
+ spec_hooks, context_hooks = nil
26
+ spec rand do
27
+ before {}
28
+ context rand do
29
+ context_hooks = hooks
30
+ end
31
+ spec_hooks = hooks
32
+ end
33
+ assert_equal context_hooks, spec_hooks
34
+ end
35
+ end
@@ -0,0 +1,66 @@
1
+ describe :raise do
2
+
3
+ it 'should pass if any exception raised' do
4
+ this = self
5
+ spec rand do
6
+ test(:test) {assert {x}.raise}
7
+ this.assert_equal :__tokyo_passed__, run(:test)
8
+ end
9
+ end
10
+
11
+ it 'should fail if nothing raised' do
12
+ this = self
13
+ spec rand do
14
+ test(:test) {assert {}.raise}
15
+ this.assert_equal Tokyo::GenericFailure, run(:test).class
16
+ end
17
+ end
18
+
19
+ it 'should pass if type matching' do
20
+ this = self
21
+ spec rand do
22
+ test(:test) {assert {x}.raise(NameError)}
23
+ this.assert_equal :__tokyo_passed__, run(:test)
24
+ end
25
+ end
26
+
27
+ it 'should fail if wrong exception raised' do
28
+ this = self
29
+ spec rand do
30
+ test(:test) {assert {}.raise(ArgumentError)}
31
+ this.assert_equal Tokyo::GenericFailure, run(:test).class
32
+ end
33
+ end
34
+
35
+ it 'should pass if both type and message matches' do
36
+ this = self
37
+ spec rand do
38
+ test(:test) {assert {x}.raise NameError, /undefined local variable or method/}
39
+ this.assert_equal :__tokyo_passed__, run(:test)
40
+ end
41
+ end
42
+
43
+ it 'should fail if message does not match' do
44
+ this = self
45
+ spec rand do
46
+ test(:test) {assert {x}.raise(NameError, /blah/)}
47
+ this.assert_equal Tokyo::GenericFailure, run(:test).class
48
+ end
49
+ end
50
+
51
+ it 'should pass if given block validates the raised error' do
52
+ this = self
53
+ spec rand do
54
+ test(:test) {assert {x}.raise {|e| e.class == NameError && e.message =~ /undefined local variable or method/}}
55
+ this.assert_equal :__tokyo_passed__, run(:test)
56
+ end
57
+ end
58
+
59
+ it 'should fail if given block invalidates raised error' do
60
+ this = self
61
+ spec rand do
62
+ test(:test) {assert {x}.raise {false}}
63
+ this.assert_equal Tokyo::GenericFailure, run(:test).class
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,83 @@
1
+ describe :receive_and_raise do
2
+
3
+ it 'should pass when received message raises as expected' do
4
+ this = self
5
+ spec rand do
6
+ test :test do
7
+ expect(x = 'x').to_receive(:y).and_raise(NoMethodError)
8
+ x.y
9
+ end
10
+ this.assert_equal :__tokyo_passed__, run(:test)
11
+ end
12
+ end
13
+
14
+ it 'should pass when received message raises whatever error' do
15
+ this = self
16
+ spec rand do
17
+ test :test do
18
+ expect(x = 'x').to_receive(:y).and_raise
19
+ x.y
20
+ end
21
+ this.assert_equal :__tokyo_passed__, run(:test)
22
+ end
23
+ end
24
+
25
+ it 'should fail when received message does not raise' do
26
+ this = self
27
+ spec rand do
28
+ test :test do
29
+ expect(x = 'x').to_receive(:to_s).and_raise
30
+ x.to_s
31
+ end
32
+ this.assert_match /Expected a exception to be raised/, run(:test).reason*' '
33
+ end
34
+ end
35
+
36
+ it 'should fail when received message raises a unexpected error type' do
37
+ this = self
38
+ spec rand do
39
+ test :test do
40
+ expect(x = 'x').to_receive(:y).and_raise(NameError)
41
+ x.y
42
+ end
43
+ this.assert_match /Expected a NameError to be raised/, run(:test).reason*' '
44
+ end
45
+ end
46
+
47
+ it 'should fail when error raised by received message is of expected type but error message does not match' do
48
+ this = self
49
+ spec rand do
50
+ test :test do
51
+ expect(x = 'x').to_receive(:y).and_raise(NoMethodError, /blah/)
52
+ x.y
53
+ end
54
+ this.assert_match /to match "blah"/, run(:test).reason*' '
55
+ end
56
+ end
57
+
58
+ it 'should pass if received message raises a error that match by type and message' do
59
+ this = self
60
+ spec rand do
61
+ test :test do
62
+ expect(x = 'x').to_receive(:y).and_raise(NoMethodError, /undefined method `y' for "x":String/)
63
+ x.y
64
+ end
65
+ this.assert_equal :__tokyo_passed__, run(:test)
66
+ end
67
+ end
68
+
69
+ it 'should pass if error raised by received message are validated by block' do
70
+ this, t, m = self, nil, nil
71
+ spec rand do
72
+ test :test do
73
+ expect(x = 'x').to_receive(:y).and_raise {|e|
74
+ t, m = e.class, e.message
75
+ }
76
+ x.y
77
+ end
78
+ run(:test)
79
+ this.assert_equal NoMethodError, t
80
+ this.assert_match /undefined method .y. for "x":String/, m
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,46 @@
1
+ describe :receive_and_return do
2
+
3
+ it 'should pass when expected message returns expected value' do
4
+ this = self
5
+ spec rand do
6
+ test :test do
7
+ expect(x = 'x').to_receive(:+).with('y').and_return('xy')
8
+ x + 'y'
9
+ end
10
+ this.assert_equal :__tokyo_passed__, run(:test)
11
+ end
12
+ end
13
+
14
+ it 'should fail when expected message returns wrong value' do
15
+ this = self
16
+ spec rand do
17
+ test :test do
18
+ expect(x = 'x').to_receive(:to_s).and_return(:y)
19
+ x.to_s
20
+ end
21
+ this.assert_match /Looks like :to_s message never returned expected value/, run(:test).reason*' '
22
+ end
23
+ end
24
+
25
+ it 'should pass when block validates returned value' do
26
+ this = self
27
+ spec rand do
28
+ test :test do
29
+ expect(x = 'x').to_receive(:+).with('y').and_return {|v| v == 'xy'}
30
+ x + 'y'
31
+ end
32
+ this.assert_equal :__tokyo_passed__, run(:test)
33
+ end
34
+ end
35
+
36
+ it 'should fail when block does not validate returned value' do
37
+ this = self
38
+ spec rand do
39
+ test :test do
40
+ expect(x = 'x').to_receive(:+).with('y').and_return {false}
41
+ x + 'y'
42
+ end
43
+ this.assert_match /Looks like :\+ message never returned expected value/, run(:test).reason*' '
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,74 @@
1
+ describe :receive_and_throw do
2
+
3
+ it 'should pass when received message throws whatever' do
4
+ this = self
5
+ spec rand do
6
+ test :test do
7
+ x = Class.new {define_singleton_method(:y) {throw :z}}
8
+ expect(x).to_receive(:y).and_throw
9
+ x.y
10
+ end
11
+ this.assert_equal :__tokyo_passed__, run(:test)
12
+ end
13
+ end
14
+
15
+ it 'should pass when received message throws expected symbol' do
16
+ this = self
17
+ spec rand do
18
+ test :test do
19
+ x = Class.new {define_singleton_method(:y) {throw :z}}
20
+ expect(x).to_receive(:y).and_throw(:z)
21
+ x.y
22
+ end
23
+ this.assert_equal :__tokyo_passed__, run(:test)
24
+ end
25
+ end
26
+
27
+ it 'should fail when received message throws nothing' do
28
+ this = self
29
+ spec rand do
30
+ test :test do
31
+ x = Class.new {define_singleton_method(:y) {}}
32
+ expect(x).to_receive(:y).and_throw
33
+ x.y
34
+ end
35
+ this.assert_match /Expected a symbol to be thrown/, run(:test).reason*' '
36
+ end
37
+ end
38
+
39
+ it 'should fail when received message throws wrong symbol' do
40
+ this = self
41
+ spec rand do
42
+ test :test do
43
+ x = Class.new {define_singleton_method(:y) {throw :z}}
44
+ expect(x).to_receive(:y).and_throw(:a)
45
+ x.y
46
+ end
47
+ this.assert_match /Expected :a to be thrown/, run(:test).reason*' '
48
+ end
49
+ end
50
+
51
+ it 'should pass when given block validates thrown symbol' do
52
+ this = self
53
+ spec rand do
54
+ test :test do
55
+ x = Class.new {define_singleton_method(:y) {throw :z}}
56
+ expect(x).to_receive(:y).and_throw {|s| s == :z}
57
+ x.y
58
+ end
59
+ this.assert_equal :__tokyo_passed__, run(:test)
60
+ end
61
+ end
62
+
63
+ it 'should fail when block does not validate thrown symbol' do
64
+ this = self
65
+ spec rand do
66
+ test :test do
67
+ x = Class.new {define_singleton_method(:y) {throw :z}}
68
+ expect(x).to_receive(:y).and_throw {false}
69
+ x.y
70
+ end
71
+ this.assert_match /Looks like wrong or no symbol thrown/, run(:test).reason*' '
72
+ end
73
+ end
74
+ end