1require 'test/unit' 2 3require 'drb/drb' 4require 'drb/eq' 5require 'rinda/tuplespace' 6 7require 'singleton' 8 9module Rinda 10 11class MockClock 12 include Singleton 13 14 class MyTS < Rinda::TupleSpace 15 def keeper_thread 16 nil 17 end 18 end 19 20 def initialize 21 @now = 2 22 @reso = 1 23 @ts = MyTS.new 24 @ts.write([2, :now]) 25 @inf = 2**31 - 1 26 end 27 28 def now 29 @now.to_f 30 end 31 32 def at(n) 33 n 34 end 35 36 def _forward(n=nil) 37 now ,= @ts.take([nil, :now]) 38 @now = now + n 39 n = @reso if n.nil? 40 @ts.write([@now, :now]) 41 end 42 43 def forward(n) 44 while n > 0 45 _forward(@reso) 46 n -= @reso 47 Thread.pass 48 end 49 end 50 51 def rewind 52 now ,= @ts.take([nil, :now]) 53 @ts.write([@inf, :now]) 54 @ts.take([nil, :now]) 55 @now = 2 56 @ts.write([2, :now]) 57 end 58 59 def sleep(n=nil) 60 now ,= @ts.read([nil, :now]) 61 @ts.read([(now + n)..@inf, :now]) 62 0 63 end 64end 65 66module Time 67 def sleep(n) 68 @m.sleep(n) 69 end 70 module_function :sleep 71 72 def at(n) 73 n 74 end 75 module_function :at 76 77 def now 78 @m ? @m.now : 2 79 end 80 module_function :now 81 82 def rewind 83 @m.rewind 84 end 85 module_function :rewind 86 87 def forward(n) 88 @m.forward(n) 89 end 90 module_function :forward 91 92 @m = MockClock.instance 93end 94 95class TupleSpace 96 def sleep(n) 97 Kernel.sleep(n * 0.01) 98 end 99end 100 101module TupleSpaceTestModule 102 def sleep(n) 103 if Thread.current == Thread.main 104 Time.forward(n) 105 else 106 Time.sleep(n) 107 end 108 end 109 110 def thread_join(th) 111 while th.alive? 112 Kernel.sleep(0.1) 113 sleep(1) 114 end 115 th.value 116 end 117 118 def test_00_tuple 119 tuple = Rinda::TupleEntry.new([1,2,3]) 120 assert(!tuple.canceled?) 121 assert(!tuple.expired?) 122 assert(tuple.alive?) 123 end 124 125 def test_00_template 126 tmpl = Rinda::Template.new([1,2,3]) 127 assert_equal(3, tmpl.size) 128 assert_equal(3, tmpl[2]) 129 assert(tmpl.match([1,2,3])) 130 assert(!tmpl.match([1,nil,3])) 131 132 tmpl = Rinda::Template.new([/^rinda/i, nil, :hello]) 133 assert_equal(3, tmpl.size) 134 assert(tmpl.match(['Rinda', 2, :hello])) 135 assert(!tmpl.match(['Rinda', 2, Symbol])) 136 assert(!tmpl.match([1, 2, :hello])) 137 assert(tmpl.match([/^rinda/i, 2, :hello])) 138 139 tmpl = Rinda::Template.new([Symbol]) 140 assert_equal(1, tmpl.size) 141 assert(tmpl.match([:hello])) 142 assert(tmpl.match([Symbol])) 143 assert(!tmpl.match(['Symbol'])) 144 145 tmpl = Rinda::Template.new({"message"=>String, "name"=>String}) 146 assert_equal(2, tmpl.size) 147 assert(tmpl.match({"message"=>"Hello", "name"=>"Foo"})) 148 assert(!tmpl.match({"message"=>"Hello", "name"=>"Foo", "1"=>2})) 149 assert(!tmpl.match({"message"=>"Hi", "name"=>"Foo", "age"=>1})) 150 assert(!tmpl.match({"message"=>"Hello", "no_name"=>"Foo"})) 151 152 assert_raise(Rinda::InvalidHashTupleKey) do 153 tmpl = Rinda::Template.new({:message=>String, "name"=>String}) 154 end 155 tmpl = Rinda::Template.new({"name"=>String}) 156 assert_equal(1, tmpl.size) 157 assert(tmpl.match({"name"=>"Foo"})) 158 assert(!tmpl.match({"message"=>"Hello", "name"=>"Foo"})) 159 assert(!tmpl.match({"message"=>:symbol, "name"=>"Foo", "1"=>2})) 160 assert(!tmpl.match({"message"=>"Hi", "name"=>"Foo", "age"=>1})) 161 assert(!tmpl.match({"message"=>"Hello", "no_name"=>"Foo"})) 162 163 tmpl = Rinda::Template.new({"message"=>String, "name"=>String}) 164 assert_equal(2, tmpl.size) 165 assert(tmpl.match({"message"=>"Hello", "name"=>"Foo"})) 166 assert(!tmpl.match({"message"=>"Hello", "name"=>"Foo", "1"=>2})) 167 assert(!tmpl.match({"message"=>"Hi", "name"=>"Foo", "age"=>1})) 168 assert(!tmpl.match({"message"=>"Hello", "no_name"=>"Foo"})) 169 170 tmpl = Rinda::Template.new({"message"=>String}) 171 assert_equal(1, tmpl.size) 172 assert(tmpl.match({"message"=>"Hello"})) 173 assert(!tmpl.match({"message"=>"Hello", "name"=>"Foo"})) 174 assert(!tmpl.match({"message"=>"Hello", "name"=>"Foo", "1"=>2})) 175 assert(!tmpl.match({"message"=>"Hi", "name"=>"Foo", "age"=>1})) 176 assert(!tmpl.match({"message"=>"Hello", "no_name"=>"Foo"})) 177 178 tmpl = Rinda::Template.new({"message"=>String, "name"=>nil}) 179 assert_equal(2, tmpl.size) 180 assert(tmpl.match({"message"=>"Hello", "name"=>"Foo"})) 181 assert(!tmpl.match({"message"=>"Hello", "name"=>"Foo", "1"=>2})) 182 assert(!tmpl.match({"message"=>"Hi", "name"=>"Foo", "age"=>1})) 183 assert(!tmpl.match({"message"=>"Hello", "no_name"=>"Foo"})) 184 185 assert_raise(Rinda::InvalidHashTupleKey) do 186 @ts.write({:message=>String, "name"=>String}) 187 end 188 189 @ts.write([1, 2, 3]) 190 assert_equal([1, 2, 3], @ts.take([1, 2, 3])) 191 192 @ts.write({'1'=>1, '2'=>2, '3'=>3}) 193 assert_equal({'1'=>1, '2'=>2, '3'=>3}, @ts.take({'1'=>1, '2'=>2, '3'=>3})) 194 195 entry = @ts.write(['1'=>1, '2'=>2, '3'=>3]) 196 assert_raise(Rinda::RequestExpiredError) do 197 assert_equal({'1'=>1, '2'=>2, '3'=>3}, @ts.read({'1'=>1}, 0)) 198 end 199 entry.cancel 200 end 201 202 def test_00_DRbObject 203 ro = DRbObject.new(nil, "druby://host:1234") 204 tmpl = Rinda::DRbObjectTemplate.new 205 assert(tmpl === ro) 206 207 tmpl = Rinda::DRbObjectTemplate.new("druby://host:1234") 208 assert(tmpl === ro) 209 210 tmpl = Rinda::DRbObjectTemplate.new("druby://host:12345") 211 assert(!(tmpl === ro)) 212 213 tmpl = Rinda::DRbObjectTemplate.new(/^druby:\/\/host:/) 214 assert(tmpl === ro) 215 216 ro = DRbObject.new_with(12345, 1234) 217 assert(!(tmpl === ro)) 218 219 ro = DRbObject.new_with("druby://foo:12345", 1234) 220 assert(!(tmpl === ro)) 221 222 tmpl = Rinda::DRbObjectTemplate.new(/^druby:\/\/(foo|bar):/) 223 assert(tmpl === ro) 224 225 ro = DRbObject.new_with("druby://bar:12345", 1234) 226 assert(tmpl === ro) 227 228 ro = DRbObject.new_with("druby://baz:12345", 1234) 229 assert(!(tmpl === ro)) 230 end 231 232 def test_inp_rdp 233 assert_raise(Rinda::RequestExpiredError) do 234 @ts.take([:empty], 0) 235 end 236 237 assert_raise(Rinda::RequestExpiredError) do 238 @ts.read([:empty], 0) 239 end 240 end 241 242 def test_ruby_talk_264062 243 th = Thread.new { @ts.take([:empty], 1) } 244 sleep(10) 245 assert_raise(Rinda::RequestExpiredError) do 246 thread_join(th) 247 end 248 249 th = Thread.new { @ts.read([:empty], 1) } 250 sleep(10) 251 assert_raise(Rinda::RequestExpiredError) do 252 thread_join(th) 253 end 254 end 255 256 def test_symbol_tuple 257 @ts.write([:symbol, :symbol]) 258 @ts.write(['string', :string]) 259 assert_equal([[:symbol, :symbol]], @ts.read_all([:symbol, nil])) 260 assert_equal([[:symbol, :symbol]], @ts.read_all([Symbol, nil])) 261 assert_equal([], @ts.read_all([:nil, nil])) 262 end 263 264 def test_core_01 265 5.times do |n| 266 @ts.write([:req, 2]) 267 end 268 269 assert_equal([[:req, 2], [:req, 2], [:req, 2], [:req, 2], [:req, 2]], 270 @ts.read_all([nil, nil])) 271 272 taker = Thread.new(5) do |count| 273 s = 0 274 count.times do 275 tuple = @ts.take([:req, Integer]) 276 assert_equal(2, tuple[1]) 277 s += tuple[1] 278 end 279 @ts.write([:ans, s]) 280 s 281 end 282 283 assert_equal(10, thread_join(taker)) 284 assert_equal([:ans, 10], @ts.take([:ans, 10])) 285 assert_equal([], @ts.read_all([nil, nil])) 286 end 287 288 def test_core_02 289 taker = Thread.new(5) do |count| 290 s = 0 291 count.times do 292 tuple = @ts.take([:req, Integer]) 293 assert_equal(2, tuple[1]) 294 s += tuple[1] 295 end 296 @ts.write([:ans, s]) 297 s 298 end 299 300 5.times do |n| 301 @ts.write([:req, 2]) 302 end 303 304 assert_equal(10, thread_join(taker)) 305 assert_equal([:ans, 10], @ts.take([:ans, 10])) 306 assert_equal([], @ts.read_all([nil, nil])) 307 end 308 309 def test_core_03_notify 310 notify1 = @ts.notify(nil, [:req, Integer]) 311 notify2 = @ts.notify(nil, {"message"=>String, "name"=>String}) 312 313 5.times do |n| 314 @ts.write([:req, 2]) 315 end 316 317 5.times do 318 tuple = @ts.take([:req, Integer]) 319 assert_equal(2, tuple[1]) 320 end 321 322 5.times do 323 assert_equal(['write', [:req, 2]], notify1.pop) 324 end 325 5.times do 326 assert_equal(['take', [:req, 2]], notify1.pop) 327 end 328 329 @ts.write({"message"=>"first", "name"=>"3"}) 330 @ts.write({"message"=>"second", "name"=>"1"}) 331 @ts.write({"message"=>"third", "name"=>"0"}) 332 @ts.take({"message"=>"third", "name"=>"0"}) 333 @ts.take({"message"=>"first", "name"=>"3"}) 334 335 assert_equal(["write", {"message"=>"first", "name"=>"3"}], notify2.pop) 336 assert_equal(["write", {"message"=>"second", "name"=>"1"}], notify2.pop) 337 assert_equal(["write", {"message"=>"third", "name"=>"0"}], notify2.pop) 338 assert_equal(["take", {"message"=>"third", "name"=>"0"}], notify2.pop) 339 assert_equal(["take", {"message"=>"first", "name"=>"3"}], notify2.pop) 340 end 341 342 def test_cancel_01 343 entry = @ts.write([:removeme, 1]) 344 assert_equal([[:removeme, 1]], @ts.read_all([nil, nil])) 345 entry.cancel 346 assert_equal([], @ts.read_all([nil, nil])) 347 348 template = nil 349 taker = Thread.new do 350 @ts.take([:take, nil], 10) do |t| 351 template = t 352 Thread.new do 353 template.cancel 354 end 355 end 356 end 357 358 sleep(2) 359 360 assert_raise(Rinda::RequestCanceledError) do 361 assert_nil(thread_join(taker)) 362 end 363 364 assert(template.canceled?) 365 366 @ts.write([:take, 1]) 367 368 assert_equal([[:take, 1]], @ts.read_all([nil, nil])) 369 end 370 371 def test_cancel_02 372 entry = @ts.write([:removeme, 1]) 373 assert_equal([[:removeme, 1]], @ts.read_all([nil, nil])) 374 entry.cancel 375 assert_equal([], @ts.read_all([nil, nil])) 376 377 template = nil 378 reader = Thread.new do 379 @ts.read([:take, nil], 10) do |t| 380 template = t 381 Thread.new do 382 template.cancel 383 end 384 end 385 end 386 387 sleep(2) 388 389 assert_raise(Rinda::RequestCanceledError) do 390 assert_nil(thread_join(reader)) 391 end 392 393 assert(template.canceled?) 394 395 @ts.write([:take, 1]) 396 397 assert_equal([[:take, 1]], @ts.read_all([nil, nil])) 398 end 399 400 class SimpleRenewer 401 def initialize(sec, n = 1) 402 @sec = sec 403 @n = n 404 end 405 406 def renew 407 return -1 if @n <= 0 408 @n -= 1 409 return @sec 410 end 411 end 412 413 def test_00_renewer 414 tuple = Rinda::TupleEntry.new([1,2,3], true) 415 assert(!tuple.canceled?) 416 assert(tuple.expired?) 417 assert(!tuple.alive?) 418 419 tuple = Rinda::TupleEntry.new([1,2,3], 1) 420 assert(!tuple.canceled?) 421 assert(!tuple.expired?) 422 assert(tuple.alive?) 423 sleep(2) 424 assert(tuple.expired?) 425 assert(!tuple.alive?) 426 427 @renewer = SimpleRenewer.new(1,2) 428 tuple = Rinda::TupleEntry.new([1,2,3], @renewer) 429 assert(!tuple.canceled?) 430 assert(!tuple.expired?) 431 assert(tuple.alive?) 432 sleep(1) 433 assert(!tuple.canceled?) 434 assert(!tuple.expired?) 435 assert(tuple.alive?) 436 sleep(2) 437 assert(tuple.expired?) 438 assert(!tuple.alive?) 439 end 440end 441 442class TupleSpaceTest < Test::Unit::TestCase 443 include TupleSpaceTestModule 444 445 def setup 446 ThreadGroup.new.add(Thread.current) 447 @ts = Rinda::TupleSpace.new(1) 448 end 449 def teardown 450 # implementation-dependent 451 @ts.instance_eval{@keeper.kill if @keeper} 452 end 453end 454 455class TupleSpaceProxyTest < Test::Unit::TestCase 456 include TupleSpaceTestModule 457 458 def setup 459 ThreadGroup.new.add(Thread.current) 460 @ts_base = Rinda::TupleSpace.new(1) 461 @ts = Rinda::TupleSpaceProxy.new(@ts_base) 462 end 463 def teardown 464 # implementation-dependent 465 @ts_base.instance_eval{@keeper.kill if @keeper} 466 end 467 468 def test_remote_array_and_hash 469 # Don't remove ary/hsh local variables. 470 # These are necessary to protect objects from GC. 471 ary = [1, 2, 3] 472 @ts.write(DRbObject.new(ary)) 473 assert_equal([1, 2, 3], @ts.take([1, 2, 3], 0)) 474 hsh = {'head' => 1, 'tail' => 2} 475 @ts.write(DRbObject.new(hsh)) 476 assert_equal({'head' => 1, 'tail' => 2}, 477 @ts.take({'head' => 1, 'tail' => 2}, 0)) 478 end 479 480 @server = DRb.primary_server || DRb.start_service 481end 482 483end 484 485