fraggle 0.4.0 → 1.0.0pre

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,139 @@
1
+ require 'fraggle/request'
2
+ require 'fraggle/response'
3
+
4
+ module Fraggle
5
+
6
+ module Connection
7
+
8
+ # Base class for all Connection errors
9
+ class Error < StandardError
10
+ attr_accessor :req
11
+
12
+ def initialize(req, msg=nil)
13
+ @req = req
14
+ super(msg)
15
+ end
16
+ end
17
+
18
+ # Raised when a request is invalid
19
+ class SendError < Error
20
+ end
21
+
22
+
23
+ attr_reader :last_received, :addr
24
+
25
+ def initialize(addr)
26
+ @addr, @cb = addr, {}
27
+ end
28
+
29
+ def receive_data(data)
30
+ @last_received = Time.now
31
+
32
+ (@buf ||= "") << data
33
+
34
+ while @buf.length > 0
35
+ if @len && @buf.length >= @len
36
+ bytes = @buf.slice!(0, @len)
37
+ @len = nil
38
+ res = Response.decode(bytes)
39
+ receive_response(res)
40
+ elsif @buf.length >= 4
41
+ bytes = @buf.slice!(0, 4)
42
+ @len = bytes.unpack("N")[0]
43
+ else
44
+ break
45
+ end
46
+ end
47
+ end
48
+
49
+ # The default receive_response
50
+ def receive_response(res)
51
+ req = @cb[res.tag]
52
+
53
+ if ! req
54
+ return
55
+ end
56
+
57
+ if ! res.ok?
58
+ @cb.delete(req.tag)
59
+ req.emit(:error, res)
60
+ return
61
+ end
62
+
63
+ if res.done?
64
+ @cb.delete(req.tag)
65
+ req.emit(:done)
66
+ end
67
+
68
+ if res.valid?
69
+ req.emit(:valid, res)
70
+ end
71
+ end
72
+
73
+ def send_request(req)
74
+ if req.tag
75
+ raise SendError, "Already sent #{req.inspect}"
76
+ end
77
+
78
+ if err?
79
+ next_tick { req.emit(:error, nil) }
80
+ return req
81
+ end
82
+
83
+ req.tag = 0
84
+ while @cb.has_key?(req.tag)
85
+ req.tag += 1
86
+
87
+ req.tag %= 2**31
88
+ end
89
+
90
+ req.cn = self
91
+
92
+ @cb[req.tag] = req
93
+
94
+ data = req.encode
95
+ head = [data.length].pack("N")
96
+
97
+ send_data("#{head}#{data}")
98
+
99
+ req
100
+ end
101
+
102
+ def post_init
103
+ last = 0
104
+ rev = Request.new :verb => Request::Verb::REV
105
+
106
+ rev.valid do |e|
107
+ if e.rev <= last
108
+ close_connection
109
+ else
110
+ timer(5) { send_request(rev.dup) }
111
+ last = e.rev
112
+ end
113
+ end
114
+
115
+ send_request(rev.dup)
116
+ end
117
+
118
+ def unbind
119
+ @err = true
120
+ @cb.values.each do |req|
121
+ req.emit(:error, nil)
122
+ end
123
+ end
124
+
125
+ def err?
126
+ !!@err
127
+ end
128
+
129
+ def timer(n, &blk)
130
+ EM.add_timer(n, &blk)
131
+ end
132
+
133
+ def next_tick(&blk)
134
+ EM.next_tick(&blk)
135
+ end
136
+
137
+ end
138
+
139
+ end
@@ -0,0 +1,62 @@
1
+ ## Generated from msg.proto for proto
2
+ require "beefcake"
3
+
4
+ module Fraggle
5
+
6
+ class Request
7
+ include Beefcake::Message
8
+
9
+ module Verb
10
+ CHECKIN = 0
11
+ GET = 1
12
+ SET = 2
13
+ DEL = 3
14
+ ESET = 4
15
+ REV = 5
16
+ NOP = 7
17
+ WATCH = 8
18
+ WALK = 9
19
+ CANCEL = 10
20
+ GETDIR = 14
21
+ STAT = 16
22
+ end
23
+
24
+ required :tag, :int32, 1
25
+ required :verb, Request::Verb, 2
26
+ optional :path, :string, 4
27
+ optional :value, :bytes, 5
28
+ optional :other_tag, :int32, 6
29
+ optional :offset, :int32, 7
30
+ optional :limit, :int32, 8
31
+ optional :rev, :int64, 9
32
+
33
+ end
34
+
35
+ class Response
36
+ include Beefcake::Message
37
+
38
+ module Err
39
+ OTHER = 127
40
+ TAG_IN_USE = 1
41
+ UNKNOWN_VERB = 2
42
+ REDIRECT = 3
43
+ TOO_LATE = 4
44
+ REV_MISMATCH = 5
45
+ BAD_PATH = 6
46
+ MISSING_ARG = 7
47
+ NOTDIR = 20
48
+ ISDIR = 21
49
+ NOENT = 22
50
+ end
51
+
52
+ required :tag, :int32, 1
53
+ required :flags, :int32, 2
54
+ optional :rev, :int64, 3
55
+ optional :path, :string, 5
56
+ optional :value, :bytes, 6
57
+ optional :len, :int32, 8
58
+ optional :err_code, Response::Err, 100
59
+ optional :err_detail, :string, 101
60
+
61
+ end
62
+ end
@@ -1,15 +1,42 @@
1
- require 'fraggle/msg'
2
- require 'fraggle/emitter'
3
-
4
- ##
5
- # An extension to Request in msg.rb. I want to keep these seperated so when
6
- # future versions of Beefcake can generate code, we don't have to manually add
7
- # this back in for each generation.
1
+ require 'fraggle/msg.pb'
8
2
 
9
3
  module Fraggle
10
-
11
4
  class Request
12
- include Emitter
13
- end
14
5
 
6
+ attr_accessor :cn
7
+ attr_reader :cb
8
+
9
+ def initialize(attrs={})
10
+ super(attrs)
11
+ @cb = Hash.new(Proc.new{})
12
+ end
13
+
14
+ def valid(&blk)
15
+ @cb[:valid] = blk
16
+ self
17
+ end
18
+
19
+ def done(&blk)
20
+ @cb[:done] = blk
21
+ self
22
+ end
23
+
24
+ def error(&blk)
25
+ @cb[:error] = blk
26
+ self
27
+ end
28
+
29
+ def emit(name, *args)
30
+ @cb[name].call(*args)
31
+ end
32
+
33
+ def cancel
34
+ @can ||= Request.new(:verb => Verb::CANCEL, :other_tag => self.tag)
35
+ req = cn.send_request(@can)
36
+ req.done do
37
+ emit(:done)
38
+ end
39
+ end
40
+
41
+ end
15
42
  end
@@ -1,49 +1,28 @@
1
- require 'fraggle/msg'
2
-
3
- ##
4
- # An extension to Response in msg.rb. I want to keep these seperated so when
5
- # future versions of Beefcake can generate code, we don't have to manually add
6
- # this back in for each generation.
1
+ require 'fraggle/msg.pb'
7
2
 
8
3
  module Fraggle
9
-
10
4
  class Response
11
5
 
12
- module Flag
13
- VALID = 1
14
- DONE = 2
15
- SET = 4
16
- DEL = 8
17
- end
18
-
19
-
20
- Missing = 0
21
- Clobber = -1
22
- Dir = -2
23
- Nop = -3
6
+ VALID = 1
7
+ DONE = 2
24
8
 
25
- Refused = -1
9
+ def valid?
10
+ return false if !flags
11
+ (flags & VALID) > 0
12
+ end
26
13
 
27
- # CAS
28
- def missing? ; rev == Missing ; end
29
- def dir? ; rev == Dir ; end
30
- def dummy? ; rev == Nop ; end
14
+ def done?
15
+ return false if !flags
16
+ (flags & DONE) > 0
17
+ end
31
18
 
32
- def del? ; (flags & Flag::Del) > 0 ; end
33
- def set? ; (flags & Flag::Set) > 0 ; end
19
+ def ok?
20
+ err_code.nil?
21
+ end
34
22
 
35
- # ERR
36
- def ok? ; err_code != 0 ; end
37
- def other? ; err_code == Err::OTHER ; end
38
- def unknown_verb? ; err_code == Err::UNKNOWN_VERB ; end
39
- def redirect? ; err_code == Err::REDIRECT ; end
40
- def too_late? ; err_code == Err::TOO_LATE ; end
41
- def mismatch? ; err_code == Err::CAS_MISMATCH ; end
42
- def not_dir? ; err_code == Err::NOT_DIR ; end
43
- def is_dir? ; err_code == Err::ISDIR ; end
23
+ def redirect?
24
+ err_code == Err::REDIRECT
25
+ end
44
26
 
45
- # Custom
46
- def disconnected? ; err_code == Errno::ECONNREFUSED::Errno ; end
47
27
  end
48
-
49
28
  end
@@ -0,0 +1,3 @@
1
+ module Fraggle
2
+ VERSION = "1.0.0pre"
3
+ end
@@ -1,239 +1,259 @@
1
+ require File.dirname(__FILE__)+"/helper"
1
2
  require 'fraggle/client'
2
- require 'fraggle/response'
3
- require 'fraggle/test'
4
3
 
5
4
  class FraggleClientTest < Test::Unit::TestCase
6
- include Fraggle::Test
7
5
 
8
- attr_reader :c, :blk
6
+ attr_reader :c, :addrs, :blk, :called
9
7
 
10
8
  def setup
11
- @c = TestClient.new(["127.0.0.1:8046"])
12
- @blk = Blk.new
13
- end
9
+ addr = "127.0.0.1:0"
10
+ cn = TestConn.new(addr)
14
11
 
15
- def test_send_recv
16
- req = c.send(Fraggle::Request.new).valid(&blk)
12
+ @addrs = ["1.1.1.1:1", "2.2.2.2:2", "3.3.3.3:3"]
13
+ @c = Fraggle::Client.new(cn, @addrs)
17
14
 
18
- assert_sent req.tag
19
- assert_recv reply(req.tag)
15
+ def @c.reconnect(addr)
16
+ @cn = TestConn.new(addr)
17
+ end
20
18
  end
21
19
 
22
- def test_valid
23
- req = c.send(Fraggle::Request.new).valid(&blk)
20
+ def test_send_valid_done
21
+ req, log = request(V::NOP)
22
+ req = c.send(req)
24
23
 
25
- reply(req.tag)
26
- reply(req.tag)
27
- reply(req.tag)
24
+ res = Fraggle::Response.new :tag => req.tag, :value => "ing", :flags => F::VALID|F::DONE
25
+ c.cn.receive_response(res)
28
26
 
29
- assert_equal 3, blk.length
27
+ assert_equal [res], log.valid
28
+ assert_equal [req], log.done
29
+ assert_equal [], log.error
30
30
  end
31
31
 
32
- def test_done
33
- req = c.send(Fraggle::Request.new)
34
- req.done(&blk)
35
-
36
- reply!(req.tag)
37
-
38
- assert_equal 1, blk.length
32
+ def test_send_error
33
+ req, log = request(V::NOP)
34
+ req = c.send(req)
39
35
 
40
- req.valid(&blk)
36
+ res = Fraggle::Response.new :tag => req.tag, :err_code => E::OTHER, :flags => F::VALID|F::DONE
37
+ c.cn.receive_response(res)
41
38
 
42
- reply(req.tag)
43
- reply(req.tag)
44
- reply(req.tag)
45
-
46
- assert_equal 1, blk.length
39
+ assert_equal [], log.valid
40
+ assert_equal [], log.done
41
+ assert_equal [res], log.error
47
42
  end
48
43
 
49
- def test_error
50
- req = c.send(Fraggle::Request.new)
51
- req.error(&blk)
44
+ def test_reconnect_without_pending_requests
45
+ exp = @addrs.dup
52
46
 
53
- reply(req.tag, :err_code => E::OTHER, :err_detail => "boom!")
54
- reply(req.tag, :err_code => E::OTHER, :err_detail => "boom!")
47
+ # Disconnect from 127.0.0.1:0
48
+ c.cn.close_connection
55
49
 
56
- assert_equal 1, blk.length
57
- end
50
+ # Send a request to invoke reconnect
51
+ req, log = request(V::NOP)
52
+ req = c.send(req)
58
53
 
59
- def test_tagging
60
- t = Fraggle::Client::MinTag
54
+ # Fake reactor turn (only available in tests)
55
+ c.cn.tick!
61
56
 
62
- assert_equal t+0, c.noop.tag
63
- assert_equal t+1, c.noop.tag
64
- assert_equal t+2, c.noop.tag
65
- assert_equal t+3, c.noop.tag
66
- assert_equal t+4, c.noop.tag
67
- end
57
+ assert exp.include?(c.cn.addr), "#{c.cn.addr.inspect} not in #{exp.inspect}"
68
58
 
69
- def test_no_tag_if_tag
70
- req = Fraggle::Request.new :tag => 99
71
- c.send(req)
72
- assert_sent 99
59
+ # If the client can handle an error, it should not mention it to the user.
60
+ assert_equal [], log.error
73
61
  end
74
62
 
75
- # CHECKIN cas, path => cas
76
- def test_checkin
77
- req = c.checkin("abc123", 123).valid(&blk)
63
+ def test_reconnect_with_pending_request
64
+ exp = @addrs.dup
78
65
 
79
- assert_sent(req.tag, :verb => V::CHECKIN, :path => "abc123", :rev => 123)
80
- assert_recv(reply(req.tag, :rev => 123))
81
- end
66
+ # Send a request to invoke reconnect
67
+ req, log = request(V::NOP)
68
+ req = c.send(req)
82
69
 
83
- # GET path, id => cas, value
84
- def test_get
85
- req = c.get(0, "/ping").valid(&blk)
70
+ # Disconnect from 127.0.0.1:0
71
+ c.cn.close_connection
86
72
 
87
- assert_sent req.tag, :rev => 0, :verb => V::GET, :path => "/ping"
88
- assert_recv reply(req.tag, :cas => 123, :value => "pong")
89
- end
73
+ # Fake reactor turn (only available in tests)
74
+ c.cn.tick!
90
75
 
91
- # STAT path, id => cas, len
92
- def test_stat
93
- req = c.stat(0, "/ping").valid(&blk)
76
+ assert exp.include?(c.cn.addr), "#{c.cn.addr.inspect} not in #{exp.inspect}"
94
77
 
95
- assert_sent req.tag, :rev => 0, :verb => V::STAT, :path => "/ping"
96
- assert_recv reply(req.tag, :cas => 123, :len => 4)
78
+ # If the client can handle an error, it should not mention it to the user.
79
+ assert_equal [], log.error
97
80
  end
98
81
 
99
- # GETDIR id, path, offset, limit => {cas, value}+
100
- def test_getdir
101
- req = c.getdir(0, "/test", nil, nil).valid(&blk)
102
-
103
- assert_sent req.tag, :rev => 0, :verb => V::GETDIR, :path => "/test"
104
- assert_recv reply(req.tag, :rev => 123, :value => "a")
82
+ # retry
83
+ def test_resend_pending_requests
84
+ req, log = request(V::GET, :path => "/foo")
85
+ req = c.resend(req)
105
86
 
106
- req = c.getdir(0, "/test", 1, 2).valid(&blk)
87
+ c.cn.close_connection
107
88
 
108
- assert_sent req.tag, :rev => 0, :verb => V::GETDIR, :path => "/test", :offset => 1, :limit => 2
109
- assert_recv reply(req.tag, :cas => 123, :value => "b")
89
+ assert_equal [req], c.cn.sent
110
90
  end
111
91
 
112
- # SET cas, path, value => cas
113
- def test_set
114
- req = c.set("/foo", "bar", 123).valid(&blk)
92
+ def test_manage_offset
93
+ req, log = request(V::WALK, :path => "/foo/*", :offset => 3)
94
+ req = c.resend(req)
115
95
 
116
- assert_sent(req.tag, :verb => V::SET, :rev => 123, :path => "/foo", :value => "bar")
117
- assert_recv(reply(req.tag, :rev => 123))
118
- end
96
+ res = Fraggle::Response.new :tag => req.tag, :flags => F::VALID
97
+ c.cn.receive_response(res)
119
98
 
120
- # DEL cas, path => {}
121
- def test_del
122
- req = c.del("/foo", 123).valid(&blk)
99
+ c.cn.close_connection
123
100
 
124
- assert_sent(req.tag, :verb => V::DEL, :rev => 123, :path => "/foo")
125
- assert_recv(reply(req.tag))
101
+ exp, _ = request(V::WALK, :tag => req.tag, :path => "/foo/*", :offset => 4)
102
+ assert_equal [exp], c.cn.sent
126
103
  end
127
104
 
128
- # WALK path, id => {cas, path, value}+
129
- def test_walk
130
- req = c.walk(nil, "/foo/*").valid(&blk)
131
-
132
- assert_respond_to req, :cancel
105
+ def test_manage_limit
106
+ req, log = request(V::WALK, :path => "/foo/*", :limit => 4)
107
+ req = c.resend(req)
133
108
 
134
- assert_sent(req.tag, :verb => V::WALK, :path => "/foo/*")
135
- assert_recv(reply(req.tag, :rev => 123, :path => "/foo/a", :value => "1"))
136
- assert_recv(reply(req.tag, :rev => 456, :path => "/foo/b", :value => "2"))
137
- assert_recv(reply(req.tag, :rev => 789, :path => "/foo/c", :value => "3"))
138
- end
109
+ res = Fraggle::Response.new :tag => req.tag, :flags => F::VALID
110
+ c.cn.receive_response(res)
139
111
 
140
- # WATCH path => {cas, path, value}+
141
- def test_watch
142
- req = c.watch(123, "/foo/*").valid(&blk)
112
+ c.cn.close_connection
143
113
 
144
- assert_respond_to req, :cancel
114
+ exp, _ = request(V::WALK, :tag => req.tag, :path => "/foo/*", :limit => 3)
115
+ assert_equal [exp], c.cn.sent
116
+ end
145
117
 
146
- assert_sent(req.tag, :rev => 123, :verb => V::WATCH, :path => "/foo/*")
147
- assert_recv(reply(req.tag, :cas => 123, :path => "/foo/a", :value => "1"))
148
- assert_recv(reply(req.tag, :cas => 456, :path => "/foo/b", :value => "2"))
149
- assert_recv(reply(req.tag, :cas => 789, :path => "/foo/c", :value => "3"))
118
+ def test_manage_rev
119
+ req, log = request(V::WALK, :path => "/foo/*", :rev => 4)
120
+ req = c.resend(req)
121
+
122
+ # nil rev
123
+ res = Fraggle::Response.new :tag => req.tag, :flags => F::VALID
124
+ c.cn.receive_response(res)
125
+ assert_equal 4, req.rev
126
+
127
+ # equal to rev
128
+ res = Fraggle::Response.new :tag => req.tag, :rev => 4, :flags => F::VALID
129
+ c.cn.receive_response(res)
130
+ assert_equal 4, req.rev
131
+
132
+ # less than rev
133
+ res = Fraggle::Response.new :tag => req.tag, :rev => 3, :flags => F::VALID
134
+ c.cn.receive_response(res)
135
+ assert_equal 4, req.rev
136
+
137
+ # greater than rev
138
+ # NOTE: This will never happen in life on a WALK, this is purely a
139
+ # test.
140
+ res = Fraggle::Response.new :tag => req.tag, :rev => 5, :flags => F::VALID
141
+ c.cn.receive_response(res)
142
+ assert_equal 5, req.rev
143
+
144
+ # force retry
145
+ c.cn.close_connection
146
+
147
+ exp, _ = request(V::WALK, :tag => req.tag, :rev => 5, :path => "/foo/*")
148
+ assert_equal [exp], c.cn.sent
150
149
  end
151
150
 
152
- # NOOP {} => {}
153
- def test_noop
154
- req = c.noop.valid(&blk)
151
+ def test_redirect
152
+ req, log = request(V::SET, :rev => 0, :path => "/foo", :value => "bar")
153
+ req = c.send(req)
155
154
 
156
- assert_sent(req.tag, :verb => V::NOOP)
157
- assert_recv(reply(req.tag))
158
- end
155
+ res = Fraggle::Response.new(
156
+ :tag => req.tag,
157
+ :err_code => E::REDIRECT,
158
+ :err_detail => "9.9.9.9:9",
159
+ :flags => F::VALID|F::DONE
160
+ )
159
161
 
160
- # CANCEL id => {}
161
- def test_cancel
162
- nop = c.noop.valid(&blk)
163
- req = c.__cancel__(nop).valid(&blk)
162
+ c.cn.receive_response(res)
164
163
 
165
- assert_sent(req.tag, :verb => V::CANCEL, :id => nop.tag)
166
- assert_recv(reply(req.tag))
164
+ assert_equal "1.1.1.1:1", c.cn.addr
167
165
  end
168
166
 
169
- def test_cancelable
170
- can = c.cancelable(Fraggle::Request.new(:tag => 123))
171
167
 
172
- assert ! can.canceled?
168
+ ###
169
+ # Sugar
173
170
 
174
- req = can.cancel
171
+ def last_sent
172
+ c.cn.sent.last
173
+ end
175
174
 
176
- assert can.canceled?
177
- assert_equal 1, c.length
178
- assert_sent req.tag, :verb => V::CANCEL, :id => can.tag
175
+ def assert_verb(exp, name, *args)
176
+ called = false
177
+ blk = Proc.new { called = true }
178
+ req = c.__send__(name, *args, &blk)
179
+ exp[:tag] = req.tag
180
+ assert_equal exp, last_sent.to_hash
179
181
 
180
- # A few more for good measure
181
- can.cancel
182
- can.cancel
182
+ c.cn.receive_response(reply(req.tag))
183
+ assert called
184
+ end
183
185
 
184
- # Ensure we haven't called cancel on the same tag more than once.
185
- assert_equal 1, c.length
186
+ def test_set
187
+ exp = {
188
+ :verb => V::SET,
189
+ :rev => 0,
190
+ :path => "/foo",
191
+ :value => "bar"
192
+ }
193
+
194
+ assert_verb exp, :set, 0, "/foo", "bar"
186
195
  end
187
196
 
188
- def test_cancel_does_not_prematurely_remove_callback
189
- x = c.watch(123, "/foo/*").valid(&blk)
190
- y = x.cancel
197
+ def test_get
198
+ exp = {
199
+ :verb => V::GET,
200
+ :rev => 0,
201
+ :path => "/foo"
202
+ }
191
203
 
192
- assert_not_equal x.object_id, y.object_id
193
- assert_not_equal x.tag, y.tag
204
+ assert_verb exp, :get, 0, "/foo"
194
205
  end
195
206
 
196
- def test_cancel_discards_further_replies
197
- x = c.watch(123, "/foo/*").valid(&blk)
198
- x.cancel
199
-
200
- reply!(x.tag)
207
+ def test_del
208
+ exp = {
209
+ :verb => V::DEL,
210
+ :rev => 0,
211
+ :path => "/foo"
212
+ }
201
213
 
202
- # The cancel happened before the reply was received by the reactor. Any
203
- # remaining data from the server should be discarded.
204
- assert_equal 0, blk.length
214
+ assert_verb exp, :del, 0, "/foo"
205
215
  end
206
216
 
207
- def test_tag_pending_cancel_is_not_useable
208
- x = c.watch(123, "/foo/*").valid(&blk)
209
- y = x.cancel
210
-
211
- # Force a reset of tag so that `send` will attempt
212
- # to reuse the pending cancels tag.
213
- c.instance_eval { @tag = x.tag }
217
+ def test_getdir
218
+ exp = {
219
+ :verb => V::GETDIR,
220
+ :rev => 0,
221
+ :path => "/foo",
222
+ :offset => 0,
223
+ :limit => 5
224
+ }
225
+
226
+ assert_verb exp, :getdir, 0, "/foo", 0, 5
227
+ end
214
228
 
215
- z = c.noop
229
+ def test_rev
230
+ exp = {
231
+ :verb => V::REV
232
+ }
216
233
 
217
- assert_not_equal x.tag, z.tag
218
- assert_not_equal y.tag, z.tag
234
+ assert_verb exp, :rev
219
235
  end
220
236
 
221
- def test_reuse_canceled_tag
222
- x = c.watch(123, "/foo/*").valid(&blk)
223
- y = x.cancel
224
-
225
- reply!(y.tag)
237
+ def test_walk
238
+ exp = {
239
+ :verb => V::WALK,
240
+ :rev => 0,
241
+ :path => "/foo/*",
242
+ :offset => 0,
243
+ :limit => 5
244
+ }
245
+
246
+ assert_verb exp, :walk, 0, "/foo/*", 0, 5
247
+ end
226
248
 
227
- z = c.noop
249
+ def test_watch
250
+ exp = {
251
+ :verb => V::WATCH,
252
+ :rev => 0,
253
+ :path => "/foo/*"
254
+ }
228
255
 
229
- assert_equal x.tag, z.tag
256
+ assert_verb exp, :watch, 0, "/foo/*"
230
257
  end
231
258
 
232
- # These are planned for future doozer versions
233
- #
234
- # ESET cas, path => {}
235
- # GETDIR path => {cas, value}+
236
- # MONITOR path => {cas, path, value}+
237
- # SYNCPATH path => cas, value
238
-
239
259
  end