tokyo 0.0.5

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,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