cond 0.2.0

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