PageRenderTime 90ms CodeModel.GetById 12ms app.highlight 73ms RepoModel.GetById 1ms app.codeStats 0ms

/components/ruby-2.1.0/test/rinda/test_rinda.rb

https://github.com/jhs/ruby-inabox
Ruby | 733 lines | 581 code | 144 blank | 8 comment | 23 complexity | 4f0143be978d272b7060c7872b959492 MD5 | raw file
  1require 'test/unit'
  2
  3require 'drb/drb'
  4require 'drb/eq'
  5require 'rinda/ring'
  6require 'rinda/tuplespace'
  7
  8require 'singleton'
  9
 10module Rinda
 11
 12class MockClock
 13  include Singleton
 14
 15  class MyTS < Rinda::TupleSpace
 16    def keeper_thread
 17      nil
 18    end
 19  end
 20
 21  def initialize
 22    @now = 2
 23    @reso = 1
 24    @ts = MyTS.new
 25    @ts.write([2, :now])
 26    @inf = 2**31 - 1
 27  end
 28
 29  def now
 30    @now.to_f
 31  end
 32
 33  def at(n)
 34    n
 35  end
 36
 37  def _forward(n=nil)
 38    now ,= @ts.take([nil, :now])
 39    @now = now + n
 40    n = @reso if n.nil?
 41    @ts.write([@now, :now])
 42  end
 43
 44  def forward(n)
 45    while n > 0
 46      _forward(@reso)
 47      n -= @reso
 48      Thread.pass
 49    end
 50  end
 51
 52  def rewind
 53    @ts.take([nil, :now])
 54    @ts.write([@inf, :now])
 55    @ts.take([nil, :now])
 56    @now = 2
 57    @ts.write([2, :now])
 58  end
 59
 60  def sleep(n=nil)
 61    now ,= @ts.read([nil, :now])
 62    @ts.read([(now + n)..@inf, :now])
 63    0
 64  end
 65end
 66
 67module Time
 68  def sleep(n)
 69    @m.sleep(n)
 70  end
 71  module_function :sleep
 72
 73  def at(n)
 74    n
 75  end
 76  module_function :at
 77
 78  def now
 79    defined?(@m) && @m ? @m.now : 2
 80  end
 81  module_function :now
 82
 83  def rewind
 84    @m.rewind
 85  end
 86  module_function :rewind
 87
 88  def forward(n)
 89    @m.forward(n)
 90  end
 91  module_function :forward
 92
 93  @m = MockClock.instance
 94end
 95
 96class TupleSpace
 97  def sleep(n)
 98    Kernel.sleep(n * 0.01)
 99  end
100end
101
102module TupleSpaceTestModule
103  def sleep(n)
104    if Thread.current == Thread.main
105      Time.forward(n)
106    else
107      Time.sleep(n)
108    end
109  end
110
111  def thread_join(th)
112    while th.alive?
113      Kernel.sleep(0.1)
114      sleep(1)
115    end
116    th.value
117  end
118
119  def test_00_tuple
120    tuple = Rinda::TupleEntry.new([1,2,3])
121    assert(!tuple.canceled?)
122    assert(!tuple.expired?)
123    assert(tuple.alive?)
124  end
125
126  def test_00_template
127    tmpl = Rinda::Template.new([1,2,3])
128    assert_equal(3, tmpl.size)
129    assert_equal(3, tmpl[2])
130    assert(tmpl.match([1,2,3]))
131    assert(!tmpl.match([1,nil,3]))
132
133    tmpl = Rinda::Template.new([/^rinda/i, nil, :hello])
134    assert_equal(3, tmpl.size)
135    assert(tmpl.match(['Rinda', 2, :hello]))
136    assert(!tmpl.match(['Rinda', 2, Symbol]))
137    assert(!tmpl.match([1, 2, :hello]))
138    assert(tmpl.match([/^rinda/i, 2, :hello]))
139
140    tmpl = Rinda::Template.new([Symbol])
141    assert_equal(1, tmpl.size)
142    assert(tmpl.match([:hello]))
143    assert(tmpl.match([Symbol]))
144    assert(!tmpl.match(['Symbol']))
145
146    tmpl = Rinda::Template.new({"message"=>String, "name"=>String})
147    assert_equal(2, tmpl.size)
148    assert(tmpl.match({"message"=>"Hello", "name"=>"Foo"}))
149    assert(!tmpl.match({"message"=>"Hello", "name"=>"Foo", "1"=>2}))
150    assert(!tmpl.match({"message"=>"Hi", "name"=>"Foo", "age"=>1}))
151    assert(!tmpl.match({"message"=>"Hello", "no_name"=>"Foo"}))
152
153    assert_raise(Rinda::InvalidHashTupleKey) do
154      Rinda::Template.new({:message=>String, "name"=>String})
155    end
156    tmpl = Rinda::Template.new({"name"=>String})
157    assert_equal(1, tmpl.size)
158    assert(tmpl.match({"name"=>"Foo"}))
159    assert(!tmpl.match({"message"=>"Hello", "name"=>"Foo"}))
160    assert(!tmpl.match({"message"=>:symbol, "name"=>"Foo", "1"=>2}))
161    assert(!tmpl.match({"message"=>"Hi", "name"=>"Foo", "age"=>1}))
162    assert(!tmpl.match({"message"=>"Hello", "no_name"=>"Foo"}))
163
164    tmpl = Rinda::Template.new({"message"=>String, "name"=>String})
165    assert_equal(2, tmpl.size)
166    assert(tmpl.match({"message"=>"Hello", "name"=>"Foo"}))
167    assert(!tmpl.match({"message"=>"Hello", "name"=>"Foo", "1"=>2}))
168    assert(!tmpl.match({"message"=>"Hi", "name"=>"Foo", "age"=>1}))
169    assert(!tmpl.match({"message"=>"Hello", "no_name"=>"Foo"}))
170
171    tmpl = Rinda::Template.new({"message"=>String})
172    assert_equal(1, tmpl.size)
173    assert(tmpl.match({"message"=>"Hello"}))
174    assert(!tmpl.match({"message"=>"Hello", "name"=>"Foo"}))
175    assert(!tmpl.match({"message"=>"Hello", "name"=>"Foo", "1"=>2}))
176    assert(!tmpl.match({"message"=>"Hi", "name"=>"Foo", "age"=>1}))
177    assert(!tmpl.match({"message"=>"Hello", "no_name"=>"Foo"}))
178
179    tmpl = Rinda::Template.new({"message"=>String, "name"=>nil})
180    assert_equal(2, tmpl.size)
181    assert(tmpl.match({"message"=>"Hello", "name"=>"Foo"}))
182    assert(!tmpl.match({"message"=>"Hello", "name"=>"Foo", "1"=>2}))
183    assert(!tmpl.match({"message"=>"Hi", "name"=>"Foo", "age"=>1}))
184    assert(!tmpl.match({"message"=>"Hello", "no_name"=>"Foo"}))
185
186    assert_raise(Rinda::InvalidHashTupleKey) do
187      @ts.write({:message=>String, "name"=>String})
188    end
189
190    @ts.write([1, 2, 3])
191    assert_equal([1, 2, 3], @ts.take([1, 2, 3]))
192
193    @ts.write({'1'=>1, '2'=>2, '3'=>3})
194    assert_equal({'1'=>1, '2'=>2, '3'=>3}, @ts.take({'1'=>1, '2'=>2, '3'=>3}))
195
196    entry = @ts.write(['1'=>1, '2'=>2, '3'=>3])
197    assert_raise(Rinda::RequestExpiredError) do
198      assert_equal({'1'=>1, '2'=>2, '3'=>3}, @ts.read({'1'=>1}, 0))
199    end
200    entry.cancel
201  end
202
203  def test_00_DRbObject
204    ro = DRbObject.new(nil, "druby://host:1234")
205    tmpl = Rinda::DRbObjectTemplate.new
206    assert(tmpl === ro)
207
208    tmpl = Rinda::DRbObjectTemplate.new("druby://host:1234")
209    assert(tmpl === ro)
210
211    tmpl = Rinda::DRbObjectTemplate.new("druby://host:12345")
212    assert(!(tmpl === ro))
213
214    tmpl = Rinda::DRbObjectTemplate.new(/^druby:\/\/host:/)
215    assert(tmpl === ro)
216
217    ro = DRbObject.new_with(12345, 1234)
218    assert(!(tmpl === ro))
219
220    ro = DRbObject.new_with("druby://foo:12345", 1234)
221    assert(!(tmpl === ro))
222
223    tmpl = Rinda::DRbObjectTemplate.new(/^druby:\/\/(foo|bar):/)
224    assert(tmpl === ro)
225
226    ro = DRbObject.new_with("druby://bar:12345", 1234)
227    assert(tmpl === ro)
228
229    ro = DRbObject.new_with("druby://baz:12345", 1234)
230    assert(!(tmpl === ro))
231  end
232
233  def test_inp_rdp
234    assert_raise(Rinda::RequestExpiredError) do
235      @ts.take([:empty], 0)
236    end
237
238    assert_raise(Rinda::RequestExpiredError) do
239      @ts.read([:empty], 0)
240    end
241  end
242
243  def test_ruby_talk_264062
244    th = Thread.new { @ts.take([:empty], 1) }
245    sleep(10)
246    assert_raise(Rinda::RequestExpiredError) do
247      thread_join(th)
248    end
249
250    th = Thread.new { @ts.read([:empty], 1) }
251    sleep(10)
252    assert_raise(Rinda::RequestExpiredError) do
253      thread_join(th)
254    end
255  end
256
257  def test_symbol_tuple
258    @ts.write([:symbol, :symbol])
259    @ts.write(['string', :string])
260    assert_equal([[:symbol, :symbol]], @ts.read_all([:symbol, nil]))
261    assert_equal([[:symbol, :symbol]], @ts.read_all([Symbol, nil]))
262    assert_equal([], @ts.read_all([:nil, nil]))
263  end
264
265  def test_core_01
266    5.times do
267      @ts.write([:req, 2])
268    end
269
270    assert_equal([[:req, 2], [:req, 2], [:req, 2], [:req, 2], [:req, 2]],
271		 @ts.read_all([nil, nil]))
272
273    taker = Thread.new(5) do |count|
274      s = 0
275      count.times do
276        tuple = @ts.take([:req, Integer])
277        assert_equal(2, tuple[1])
278        s += tuple[1]
279      end
280      @ts.write([:ans, s])
281      s
282    end
283
284    assert_equal(10, thread_join(taker))
285    assert_equal([:ans, 10], @ts.take([:ans, 10]))
286    assert_equal([], @ts.read_all([nil, nil]))
287  end
288
289  def test_core_02
290    taker = Thread.new(5) do |count|
291      s = 0
292      count.times do
293        tuple = @ts.take([:req, Integer])
294        assert_equal(2, tuple[1])
295        s += tuple[1]
296      end
297      @ts.write([:ans, s])
298      s
299    end
300
301    5.times do
302      @ts.write([:req, 2])
303    end
304
305    assert_equal(10, thread_join(taker))
306    assert_equal([:ans, 10], @ts.take([:ans, 10]))
307    assert_equal([], @ts.read_all([nil, nil]))
308  end
309
310  def test_core_03_notify
311    notify1 = @ts.notify(nil, [:req, Integer])
312    notify2 = @ts.notify(nil, {"message"=>String, "name"=>String})
313
314    5.times do
315      @ts.write([:req, 2])
316    end
317
318    5.times do
319      tuple = @ts.take([:req, Integer])
320      assert_equal(2, tuple[1])
321    end
322
323    5.times do
324      assert_equal(['write', [:req, 2]], notify1.pop)
325    end
326    5.times do
327      assert_equal(['take', [:req, 2]], notify1.pop)
328    end
329
330    @ts.write({"message"=>"first", "name"=>"3"})
331    @ts.write({"message"=>"second", "name"=>"1"})
332    @ts.write({"message"=>"third", "name"=>"0"})
333    @ts.take({"message"=>"third", "name"=>"0"})
334    @ts.take({"message"=>"first", "name"=>"3"})
335
336    assert_equal(["write", {"message"=>"first", "name"=>"3"}], notify2.pop)
337    assert_equal(["write", {"message"=>"second", "name"=>"1"}], notify2.pop)
338    assert_equal(["write", {"message"=>"third", "name"=>"0"}], notify2.pop)
339    assert_equal(["take", {"message"=>"third", "name"=>"0"}], notify2.pop)
340    assert_equal(["take", {"message"=>"first", "name"=>"3"}], notify2.pop)
341  end
342
343  def test_cancel_01
344    entry = @ts.write([:removeme, 1])
345    assert_equal([[:removeme, 1]], @ts.read_all([nil, nil]))
346    entry.cancel
347    assert_equal([], @ts.read_all([nil, nil]))
348
349    template = nil
350    taker = Thread.new do
351      @ts.take([:take, nil], 10) do |t|
352        template = t
353	Thread.new do
354	  template.cancel
355	end
356      end
357    end
358
359    sleep(2)
360
361    assert_raise(Rinda::RequestCanceledError) do
362      assert_nil(thread_join(taker))
363    end
364
365    assert(template.canceled?)
366
367    @ts.write([:take, 1])
368
369    assert_equal([[:take, 1]], @ts.read_all([nil, nil]))
370  end
371
372  def test_cancel_02
373    entry = @ts.write([:removeme, 1])
374    assert_equal([[:removeme, 1]], @ts.read_all([nil, nil]))
375    entry.cancel
376    assert_equal([], @ts.read_all([nil, nil]))
377
378    template = nil
379    reader = Thread.new do
380      @ts.read([:take, nil], 10) do |t|
381        template = t
382	Thread.new do
383	  template.cancel
384	end
385      end
386    end
387
388    sleep(2)
389
390    assert_raise(Rinda::RequestCanceledError) do
391      assert_nil(thread_join(reader))
392    end
393
394    assert(template.canceled?)
395
396    @ts.write([:take, 1])
397
398    assert_equal([[:take, 1]], @ts.read_all([nil, nil]))
399  end
400
401  class SimpleRenewer
402    def initialize(sec, n = 1)
403      @sec = sec
404      @n = n
405    end
406
407    def renew
408      return -1 if @n <= 0
409      @n -= 1
410      return @sec
411    end
412  end
413
414  def test_00_renewer
415    tuple = Rinda::TupleEntry.new([1,2,3], true)
416    assert(!tuple.canceled?)
417    assert(tuple.expired?)
418    assert(!tuple.alive?)
419
420    tuple = Rinda::TupleEntry.new([1,2,3], 1)
421    assert(!tuple.canceled?)
422    assert(!tuple.expired?)
423    assert(tuple.alive?)
424    sleep(2)
425    assert(tuple.expired?)
426    assert(!tuple.alive?)
427
428    @renewer = SimpleRenewer.new(1,2)
429    tuple = Rinda::TupleEntry.new([1,2,3], @renewer)
430    assert(!tuple.canceled?)
431    assert(!tuple.expired?)
432    assert(tuple.alive?)
433    sleep(1)
434    assert(!tuple.canceled?)
435    assert(!tuple.expired?)
436    assert(tuple.alive?)
437    sleep(2)
438    assert(tuple.expired?)
439    assert(!tuple.alive?)
440  end
441end
442
443class TupleSpaceTest < Test::Unit::TestCase
444  include TupleSpaceTestModule
445
446  def setup
447    ThreadGroup.new.add(Thread.current)
448    @ts = Rinda::TupleSpace.new(1)
449  end
450  def teardown
451    # implementation-dependent
452    @ts.instance_eval{@keeper.kill if @keeper}
453  end
454end
455
456class TupleSpaceProxyTest < Test::Unit::TestCase
457  include TupleSpaceTestModule
458
459  def setup
460    ThreadGroup.new.add(Thread.current)
461    @ts_base = Rinda::TupleSpace.new(1)
462    @ts = Rinda::TupleSpaceProxy.new(@ts_base)
463  end
464  def teardown
465    # implementation-dependent
466    @ts_base.instance_eval{@keeper.kill if @keeper}
467  end
468
469  def test_remote_array_and_hash
470    # Don't remove ary/hsh local variables.
471    # These are necessary to protect objects from GC.
472    ary = [1, 2, 3]
473    @ts.write(DRbObject.new(ary))
474    assert_equal([1, 2, 3], @ts.take([1, 2, 3], 0))
475    hsh = {'head' => 1, 'tail' => 2}
476    @ts.write(DRbObject.new(hsh))
477    assert_equal({'head' => 1, 'tail' => 2},
478                 @ts.take({'head' => 1, 'tail' => 2}, 0))
479  end
480
481  def test_take_bug_8215
482    require_relative '../ruby/envutil'
483    service = DRb.start_service(nil, @ts_base)
484
485    uri = service.uri
486
487    args = [EnvUtil.rubybin, *%W[-rdrb/drb -rdrb/eq -rrinda/ring -rrinda/tuplespace -e]]
488
489    take = spawn(*args, <<-'end;', uri)
490      uri = ARGV[0]
491      DRb.start_service
492      ro = DRbObject.new_with_uri(uri)
493      ts = Rinda::TupleSpaceProxy.new(ro)
494      th = Thread.new do
495        ts.take([:test_take, nil])
496      end
497      Kernel.sleep(0.1)
498      th.raise(Interrupt) # causes loss of the taken tuple
499      ts.write([:barrier, :continue])
500      Kernel.sleep
501    end;
502
503    @ts_base.take([:barrier, :continue])
504
505    write = spawn(*args, <<-'end;', uri)
506      uri = ARGV[0]
507      DRb.start_service
508      ro = DRbObject.new_with_uri(uri)
509      ts = Rinda::TupleSpaceProxy.new(ro)
510      ts.write([:test_take, 42])
511    end;
512
513    status = Process.wait(write)
514
515    assert_equal([[:test_take, 42]], @ts_base.read_all([:test_take, nil]),
516                 '[bug:8215] tuple lost')
517  ensure
518    signal = /mswin|mingw/ =~ RUBY_PLATFORM ? "KILL" : "TERM"
519    Process.kill(signal, write) if write && status.nil?
520    Process.kill(signal, take)  if take
521    Process.wait(write) if write && status.nil?
522    Process.wait(take)  if take
523  end
524
525  @server = DRb.primary_server || DRb.start_service
526end
527
528module RingIPv6
529  def prepare_ipv6(r)
530    begin
531      Socket.getifaddrs.each do |ifaddr|
532        next unless ifaddr.addr
533        next unless ifaddr.addr.ipv6_linklocal?
534        next if ifaddr.name[0, 2] == "lo"
535        r.multicast_interface = ifaddr.ifindex
536        return ifaddr
537      end
538    rescue NotImplementedError
539      # ifindex() function may not be implemented on Windows.
540      return if
541        Socket.ip_address_list.any? { |addrinfo| addrinfo.ipv6? }
542    end
543    skip 'IPv6 not available'
544  end
545end
546
547class TestRingServer < Test::Unit::TestCase
548
549  def setup
550    @port = Rinda::Ring_PORT
551
552    @ts = Rinda::TupleSpace.new
553    @rs = Rinda::RingServer.new(@ts, [], @port)
554  end
555  def teardown
556    # implementation-dependent
557    @ts.instance_eval{@keeper.kill if @keeper}
558    @rs.shutdown
559  end
560
561  def test_do_reply
562    called = nil
563
564    callback = proc { |ts|
565      called = ts
566    }
567
568    callback = DRb::DRbObject.new callback
569
570    @ts.write [:lookup_ring, callback]
571
572    @rs.do_reply
573
574    Thread.pass until called
575
576    assert_same @ts, called
577  end
578
579  def test_do_reply_local
580    called = nil
581
582    callback = proc { |ts|
583      called = ts
584    }
585
586    @ts.write [:lookup_ring, callback]
587
588    @rs.do_reply
589
590    Thread.pass until called
591
592    assert_same @ts, called
593  end
594
595  def test_make_socket_unicast
596    v4 = @rs.make_socket('127.0.0.1')
597
598    assert_equal('127.0.0.1', v4.local_address.ip_address)
599    assert_equal(@port,       v4.local_address.ip_port)
600  end
601
602  def test_make_socket_ipv4_multicast
603    v4mc = @rs.make_socket('239.0.0.1')
604
605    if Socket.const_defined?(:SO_REUSEPORT) then
606      assert(v4mc.getsockopt(:SOCKET, :SO_REUSEPORT).bool)
607    else
608      assert(v4mc.getsockopt(:SOCKET, :SO_REUSEADDR).bool)
609    end
610
611    assert_equal('0.0.0.0', v4mc.local_address.ip_address)
612    assert_equal(@port,     v4mc.local_address.ip_port)
613  end
614
615  def test_make_socket_ipv6_multicast
616    skip 'IPv6 not available' unless
617      Socket.ip_address_list.any? { |addrinfo| addrinfo.ipv6? }
618
619    begin
620      v6mc = @rs.make_socket('ff02::1')
621    rescue Errno::EADDRNOTAVAIL
622      return # IPv6 address for multicast not available
623    end
624
625    if Socket.const_defined?(:SO_REUSEPORT) then
626      assert v6mc.getsockopt(:SOCKET, :SO_REUSEPORT).bool
627    else
628      assert v6mc.getsockopt(:SOCKET, :SO_REUSEADDR).bool
629    end
630
631    assert_equal('::1', v6mc.local_address.ip_address)
632    assert_equal(@port, v6mc.local_address.ip_port)
633  end
634
635  def test_ring_server_ipv4_multicast
636    @rs = Rinda::RingServer.new(@ts, [['239.0.0.1', '0.0.0.0']], @port)
637    v4mc = @rs.instance_variable_get('@sockets').first
638
639    if Socket.const_defined?(:SO_REUSEPORT) then
640      assert(v4mc.getsockopt(:SOCKET, :SO_REUSEPORT).bool)
641    else
642      assert(v4mc.getsockopt(:SOCKET, :SO_REUSEADDR).bool)
643    end
644
645    assert_equal('0.0.0.0', v4mc.local_address.ip_address)
646    assert_equal(@port,     v4mc.local_address.ip_port)
647  end
648
649  def test_ring_server_ipv6_multicast
650    skip 'IPv6 not available' unless
651      Socket.ip_address_list.any? { |addrinfo| addrinfo.ipv6? }
652
653    begin
654      @rs = Rinda::RingServer.new(@ts, [['ff02::1', '::1', 0]], @port)
655    rescue Errno::EADDRNOTAVAIL
656      return # IPv6 address for multicast not available
657    end
658
659    v6mc = @rs.instance_variable_get('@sockets').first
660
661    if Socket.const_defined?(:SO_REUSEPORT) then
662      assert v6mc.getsockopt(:SOCKET, :SO_REUSEPORT).bool
663    else
664      assert v6mc.getsockopt(:SOCKET, :SO_REUSEADDR).bool
665    end
666
667    assert_equal('::1', v6mc.local_address.ip_address)
668    assert_equal(@port, v6mc.local_address.ip_port)
669  end
670
671  def test_shutdown
672    @rs.shutdown
673
674    assert_nil(@rs.do_reply, 'otherwise should hang forever')
675  end
676
677end
678
679class TestRingFinger < Test::Unit::TestCase
680  include RingIPv6
681
682  def setup
683    @rf = Rinda::RingFinger.new
684  end
685
686  def test_make_socket_unicast
687    v4 = @rf.make_socket('127.0.0.1')
688
689    assert(v4.getsockopt(:SOL_SOCKET, :SO_BROADCAST).bool)
690  end
691
692  def test_make_socket_ipv4_multicast
693    v4mc = @rf.make_socket('239.0.0.1')
694
695    assert_equal(1, v4mc.getsockopt(:IPPROTO_IP, :IP_MULTICAST_LOOP).ipv4_multicast_loop)
696    assert_equal(1, v4mc.getsockopt(:IPPROTO_IP, :IP_MULTICAST_TTL).ipv4_multicast_ttl)
697  end
698
699  def test_make_socket_ipv6_multicast
700    ifaddr = prepare_ipv6(@rf)
701    begin
702      v6mc = @rf.make_socket("ff02::1")
703    rescue Errno::EINVAL
704      # somehow Debian 6.0.7 needs ifname
705      v6mc = @rf.make_socket("ff02::1%#{ifaddr.name}")
706    end
707
708    assert_equal(1, v6mc.getsockopt(:IPPROTO_IPV6, :IPV6_MULTICAST_LOOP).int)
709    assert_equal(1, v6mc.getsockopt(:IPPROTO_IPV6, :IPV6_MULTICAST_HOPS).int)
710  end
711
712  def test_make_socket_ipv4_multicast_hops
713    @rf.multicast_hops = 2
714    v4mc = @rf.make_socket('239.0.0.1')
715    assert_equal(2, v4mc.getsockopt(:IPPROTO_IP, :IP_MULTICAST_TTL).ipv4_multicast_ttl)
716  end
717
718  def test_make_socket_ipv6_multicast_hops
719    ifaddr = prepare_ipv6(@rf)
720    @rf.multicast_hops = 2
721    begin
722      v6mc = @rf.make_socket("ff02::1")
723    rescue Errno::EINVAL
724      # somehow Debian 6.0.7 needs ifname
725      v6mc = @rf.make_socket("ff02::1%#{ifaddr.name}")
726    end
727    assert_equal(2, v6mc.getsockopt(:IPPROTO_IPV6, :IPV6_MULTICAST_HOPS).int)
728  end
729
730end
731
732end
733