fraggle 0.4.0 → 1.0.0pre

Sign up to get free protection for your applications and to get access to all the features.
@@ -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