cond 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/spec/common.rb ADDED
@@ -0,0 +1,49 @@
1
+ $LOAD_PATH.unshift File.dirname(__FILE__) + "/../lib"
2
+ $LOAD_PATH.unshift File.dirname(__FILE__) + "/../support"
3
+
4
+ # darn rspec warnings
5
+ $VERBOSE = false
6
+ begin
7
+ require 'spec'
8
+ rescue LoadError
9
+ require 'rubygems'
10
+ require 'spec'
11
+ end
12
+
13
+ # NOTE: In jruby this must come after require 'rubygems'
14
+ require 'cond'
15
+
16
+ require 'pathname'
17
+ require 'stringio'
18
+
19
+ def pipe_to_ruby(code)
20
+ require 'quix/ruby'
21
+ IO.popen(%{"#{Quix::Ruby::EXECUTABLE}"}, "r+") { |pipe|
22
+ pipe.puts code
23
+ pipe.flush
24
+ pipe.close_write
25
+ pipe.read
26
+ }
27
+ end
28
+
29
+ def capture(input_string)
30
+ previous = [
31
+ $stdout, $stdin, Cond.defaults.stream_out, Cond.defaults.stream_in
32
+ ]
33
+ begin
34
+ StringIO.open("", "r+") { |output|
35
+ StringIO.open(input_string) { |input|
36
+ Cond.defaults.stream_out = output
37
+ Cond.defaults.stream_in = input
38
+ $stdout = output
39
+ $stdin = input
40
+ yield
41
+ output.rewind
42
+ output.read
43
+ }
44
+ }
45
+ ensure
46
+ $stdout, $stdin, Cond.defaults.stream_out, Cond.defaults.stream_in =
47
+ previous
48
+ end
49
+ end
@@ -0,0 +1,55 @@
1
+ require File.dirname(__FILE__) + "/common"
2
+
3
+ include Cond
4
+
5
+ describe "error reporting" do
6
+ it "should raise NoRestartError when restart is not found" do
7
+ lambda {
8
+ invoke_restart(:zzz)
9
+ }.should raise_error(Cond::NoRestartError)
10
+ end
11
+
12
+ it "should raise ContextError when restart called outside " +
13
+ "restartable block" do
14
+ lambda {
15
+ restart :zzz do
16
+ end
17
+ }.should raise_error(Cond::ContextError)
18
+ end
19
+
20
+ it "should raise ContextError when restart called inside handling block" do
21
+ lambda {
22
+ handling do
23
+ restart :zzz do
24
+ end
25
+ end
26
+ }.should raise_error(Cond::ContextError)
27
+ end
28
+
29
+ it "should raise ContextError when handle called outside handling block" do
30
+ lambda {
31
+ handle RuntimeError do
32
+ end
33
+ }.should raise_error(Cond::ContextError)
34
+ end
35
+
36
+ it "should raise ContextError when restart called inside handling block" do
37
+ lambda {
38
+ handling do
39
+ restart :zzz do
40
+ end
41
+ end
42
+ }.should raise_error(Cond::ContextError)
43
+ end
44
+
45
+ [:leave, :again].each { |keyword|
46
+ desc =
47
+ "should raise ContextError when #{keyword} called outside " +
48
+ "restartable or handling block"
49
+ it desc do
50
+ lambda {
51
+ send keyword
52
+ }.should raise_error(Cond::ContextError)
53
+ end
54
+ }
55
+ end
@@ -0,0 +1,88 @@
1
+ require File.dirname(__FILE__) + "/common"
2
+
3
+ include Cond
4
+
5
+ [:handling, :restartable].each { |keyword|
6
+ describe "arguments to 'leave' have semantics of 'return'" do
7
+ it "should be passed to the #{keyword} block result (none)" do
8
+ send(keyword) do
9
+ leave
10
+ end.should == nil
11
+ end
12
+ it "should be passed to the #{keyword} block result (single)" do
13
+ send(keyword) do
14
+ leave 3
15
+ end.should == 3
16
+ end
17
+ it "should be passed to the #{keyword} block result (multiple)" do
18
+ send(keyword) do
19
+ leave 4, 5
20
+ end.should == [4, 5]
21
+ end
22
+ it "should be passed to the #{keyword} block result (single array)" do
23
+ send(keyword) do
24
+ leave([6, 7])
25
+ end.should == [6, 7]
26
+ end
27
+ end
28
+ }
29
+
30
+ [:handling, :restartable].each { |keyword|
31
+ describe "arguments to 'again' have semantics of 'return'" do
32
+ before :each do
33
+ @memo = []
34
+ end
35
+ it "should be passed to the #{keyword} block args (none)" do
36
+ send(keyword) do |*args|
37
+ @memo.push :visit
38
+ if @memo.size == 2
39
+ args.should == []
40
+ again
41
+ elsif @memo.size == 3
42
+ args.should == []
43
+ leave
44
+ end
45
+ again
46
+ end
47
+ end
48
+ it "should be passed to the #{keyword} block args (single)" do
49
+ send(keyword) do |*args|
50
+ @memo.push :visit
51
+ if @memo.size == 2
52
+ args.should == [3]
53
+ again
54
+ elsif @memo.size == 3
55
+ args.should == []
56
+ leave
57
+ end
58
+ again 3
59
+ end
60
+ end
61
+ it "should be passed to the #{keyword} block args (multiple)" do
62
+ send(keyword) do |*args|
63
+ @memo.push :visit
64
+ if @memo.size == 2
65
+ args.should == [4, 5]
66
+ again
67
+ elsif @memo.size == 3
68
+ args.should == []
69
+ leave
70
+ end
71
+ again 4, 5
72
+ end
73
+ end
74
+ it "should be passed to the #{keyword} block args (single array)" do
75
+ send(keyword) do |*args|
76
+ @memo.push :visit
77
+ if @memo.size == 2
78
+ args.should == [6, 7]
79
+ again
80
+ elsif @memo.size == 3
81
+ args.should == []
82
+ leave
83
+ end
84
+ again [6, 7]
85
+ end
86
+ end
87
+ end
88
+ }
@@ -0,0 +1,86 @@
1
+ require File.dirname(__FILE__) + "/common"
2
+
3
+ class AnimalError < StandardError ; end
4
+ class BirdError < AnimalError ; end
5
+ class SparrowError < BirdError ; end
6
+ class DogError < AnimalError ; end
7
+ class RawError < Exception ; end
8
+
9
+ RANDOM_ERRORS = [
10
+ Exception,
11
+ RuntimeError,
12
+ AnimalError,
13
+ BirdError,
14
+ SparrowError,
15
+ DogError,
16
+ RawError,
17
+ ]
18
+
19
+ describe "handler matching system" do
20
+ before :all do
21
+ @memo = []
22
+ @handlers = RANDOM_ERRORS.inject(Hash.new) { |acc, ex|
23
+ acc.merge!(ex => lambda { |t| @memo.push ex })
24
+ }
25
+ end
26
+
27
+ it "should find an exact match when it is the only option" do
28
+ @memo.clear
29
+ Cond.with_handlers(DogError => @handlers[DogError]) {
30
+ raise DogError
31
+ }
32
+ @memo.should == [DogError]
33
+ end
34
+
35
+ it "should find an exact match among other matches" do
36
+ @memo.clear
37
+ Cond.with_handlers(@handlers) {
38
+ raise DogError
39
+ }
40
+ @memo.should == [DogError]
41
+ end
42
+
43
+ it "should find a match given an Exception instance" do
44
+ @memo.clear
45
+ Cond.with_handlers(@handlers) {
46
+ raise DogError.new
47
+ }
48
+ @memo.should == [DogError]
49
+ end
50
+
51
+ it "should not find non-matches" do
52
+ lambda {
53
+ Cond.with_handlers(BirdError => lambda { |e| }) {
54
+ raise DogError
55
+ }
56
+ }.should raise_error(DogError)
57
+ end
58
+
59
+ it "should find a related match" do
60
+ @memo.clear
61
+ Cond.with_handlers(BirdError => @handlers[BirdError]) {
62
+ raise SparrowError
63
+ }
64
+ @memo.should == [BirdError]
65
+ end
66
+
67
+ it "should find the closest related match" do
68
+ @memo.clear
69
+ handlers = {
70
+ BirdError => @handlers[BirdError],
71
+ AnimalError => @handlers[AnimalError],
72
+ }
73
+ Cond.with_handlers(handlers) {
74
+ raise SparrowError
75
+ }
76
+ @memo.should == [BirdError]
77
+ end
78
+
79
+ it "should work with the catch-all handler" do
80
+ @memo.clear
81
+ Cond.with_handlers(@handlers) {
82
+ raise SyntaxError
83
+ }
84
+ @memo.should == [Exception]
85
+ end
86
+ end
@@ -0,0 +1,69 @@
1
+ require File.dirname(__FILE__) + "/common"
2
+
3
+ describe "raise replacement" do
4
+ it "should raise" do
5
+ lambda {
6
+ raise NameError
7
+ }.should raise_error(NameError)
8
+ end
9
+
10
+ it "should raise RuntimeError for 'raise \"string\"'" do
11
+ lambda {
12
+ raise "The mass of men lead lives of quiet desperation. --Thoreau"
13
+ }.should raise_error(RuntimeError)
14
+ end
15
+
16
+ it "should accept the three-argument form" do
17
+ begin
18
+ raise RuntimeError, "msg", ["zz"]
19
+ rescue => e
20
+ [e.class, e.message, e.backtrace].should == [RuntimeError, "msg", ["zz"]]
21
+ end
22
+ end
23
+
24
+ it "should raise TypeError if the third arg is not array of strings" do
25
+ ex = nil
26
+ begin
27
+ raise RuntimeError, "zz", Hash.new
28
+ rescue Exception => ex
29
+ ex = ex
30
+ end
31
+ ex.class.should == TypeError
32
+ end
33
+
34
+ it "should raise TypeError for random junk arguments" do
35
+ lambda {
36
+ raise "a", "b"
37
+ }.should raise_error(TypeError)
38
+ lambda {
39
+ raise 1, "b"
40
+ }.should raise_error(TypeError)
41
+ lambda {
42
+ raise RuntimeError, "msg", 33
43
+ }.should raise_error(TypeError)
44
+ end
45
+
46
+ it "should accept a non-Exception non-String which responds to #exception" do
47
+ klass = Class.new {
48
+ def exception
49
+ RuntimeError.new("my exception")
50
+ end
51
+ }
52
+ begin
53
+ raise klass.new
54
+ rescue => e
55
+ e.message.should == "my exception"
56
+ end
57
+ end
58
+
59
+ it "should raise TypeError if the argument is not a String, not " +
60
+ "an Exception, and does not respond to #exception" do
61
+ lambda {
62
+ raise 27
63
+ }.should raise_error(TypeError)
64
+ end
65
+
66
+ it "should be aliased to 'fail'" do
67
+ Kernel.instance_method(:raise).should == Kernel.instance_method(:fail)
68
+ end
69
+ end
@@ -0,0 +1,101 @@
1
+ require File.dirname(__FILE__) + "/common"
2
+
3
+ class ReraiseExampleError < Exception
4
+ end
5
+
6
+ include Cond
7
+
8
+ describe "re-raise" do
9
+ it "should work work with no arguments" do
10
+ lambda {
11
+ handling do
12
+ handle ReraiseExampleError do
13
+ raise
14
+ end
15
+ raise ReraiseExampleError
16
+ end
17
+ }.should raise_error(ReraiseExampleError)
18
+ end
19
+
20
+ describe "with arguments" do
21
+ before :all do
22
+ @memo = []
23
+ @func = lambda {
24
+ begin
25
+ handling do
26
+ handle ReraiseExampleError do
27
+ raise "---test"
28
+ end
29
+ raise ReraiseExampleError
30
+ end
31
+ rescue Exception => e
32
+ @memo.push e
33
+ raise
34
+ end
35
+ }
36
+ end
37
+
38
+ it "should raise the new exception" do
39
+ @func.should raise_error(RuntimeError)
40
+ end
41
+
42
+ it "should preserve 'message'" do
43
+ @memo.first.message.should == "---test"
44
+ end
45
+ end
46
+
47
+ describe "nested" do
48
+ before :all do
49
+ @memo = []
50
+ @func = lambda {
51
+ handling do
52
+ handle ReraiseExampleError do
53
+ @memo.push :outer
54
+ end
55
+ handling do
56
+ handle ReraiseExampleError do
57
+ @memo.push :inner
58
+ raise
59
+ end
60
+ raise ReraiseExampleError
61
+ end
62
+ end
63
+ }
64
+ end
65
+
66
+ it "should transfer to handlers earlier in the stack" do
67
+ @func.should_not raise_error
68
+ end
69
+
70
+ it "should be ordered inside to outside" do
71
+ @memo.should == [:inner, :outer]
72
+ end
73
+ end
74
+
75
+ describe "nested with empty inner blocks" do
76
+ before :all do
77
+ @memo = []
78
+ @func = lambda {
79
+ handling do
80
+ handle ReraiseExampleError do
81
+ @memo.push :outer
82
+ raise
83
+ end
84
+ handling do
85
+ handling do
86
+ raise ReraiseExampleError
87
+ end
88
+ end
89
+ end
90
+ }
91
+ end
92
+
93
+ it "should transfer to handlers earlier in the stack" do
94
+ @func.should raise_error(ReraiseExampleError)
95
+ end
96
+
97
+ it "should call the re-raising handler once" do
98
+ @memo.should == [:outer]
99
+ end
100
+ end
101
+ end