PageRenderTime 110ms CodeModel.GetById 2ms app.highlight 102ms RepoModel.GetById 1ms app.codeStats 0ms

/test/test_mem_cache_db.rb

https://github.com/KellyMahan/memcachedb-client
Ruby | 1011 lines | 769 code | 226 blank | 16 comment | 6 complexity | af9913d887eed5ae8844d0c4ff8f3c91 MD5 | raw file
   1# encoding: utf-8
   2require 'logger'
   3require 'stringio'
   4require 'test/unit'
   5require 'rubygems'
   6begin
   7  gem 'flexmock'
   8  require 'flexmock/test_unit'
   9rescue LoadError => e
  10  puts "Some tests require flexmock, please run `gem install flexmock`"
  11end
  12
  13Thread.abort_on_exception = true
  14$TESTING = true
  15
  16require File.dirname(__FILE__) + '/../lib/memcache' if not defined?(MemCacheDb)
  17
  18class MemCacheDb
  19
  20  attr_writer :namespace
  21
  22end
  23
  24class FakeSocketDb
  25
  26  attr_reader :written, :data
  27
  28  def initialize
  29    @written = StringIO.new
  30    @data = StringIO.new
  31  end
  32
  33  def write(data)
  34    @written.write data
  35  end
  36
  37  def gets
  38    @data.gets
  39  end
  40
  41  def read(arg)
  42    @data.read arg
  43  end
  44
  45end
  46
  47class Test::Unit::TestCase
  48  def requirement(bool, msg)
  49    if bool
  50      yield
  51    else
  52      puts msg
  53      assert true
  54    end
  55  end
  56  
  57  def memcached_running?
  58    TCPSocket.new('localhost', 21201) rescue false
  59  end
  60  
  61  def xprofile(name, &block)
  62    a = Time.now
  63    block.call
  64    Time.now - a
  65  end
  66
  67  def profile(name, &block)
  68    require 'ruby-prof'
  69    a = Time.now
  70    result = RubyProf.profile(&block)
  71    time = Time.now - a
  72    printer = RubyProf::GraphHtmlPrinter.new(result)
  73    File.open("#{name}.html", 'w') do |f|
  74      printer.print(f, :min_percent=>1)
  75    end
  76    time
  77  end
  78  
  79end
  80
  81class FakeServerDb
  82
  83  attr_accessor :host, :port, :socket, :weight, :multithread, :status
  84
  85  def initialize(socket = nil)
  86    @closed = false
  87    @host = 'example.com'
  88    @port = 21201
  89    @socket = socket || FakeSocketDb.new
  90    @weight = 1
  91    @multithread = true
  92    @status = "CONNECTED"
  93  end
  94
  95  def close
  96    # begin
  97    #   raise "Already closed"
  98    # rescue => e
  99    #   puts e.backtrace.join("\n")
 100    # end
 101    @closed = true
 102    @socket = nil
 103    @status = "NOT CONNECTED"
 104  end
 105
 106  def alive?
 107    # puts "I'm #{@closed ? 'dead' : 'alive'}"
 108    !@closed
 109  end
 110
 111end
 112
 113class TestMemCacheDb < Test::Unit::TestCase
 114
 115  def setup
 116    @cache = MemCacheDb.new 'localhost:1', :namespace => 'my_namespace'
 117  end
 118
 119  def test_performance
 120    requirement(memcached_running?, 'A real memcached server must be running for performance testing') do
 121
 122      cache = MemCacheDb.new(['localhost:21201',"#{host}:21201"])
 123      cache.flush_all
 124      cache.add('a', 1, 120)
 125      with = xprofile 'get' do
 126        1000.times do
 127          cache.get('a')
 128        end
 129      end
 130      puts ''
 131      puts "1000 gets with socket timeout: #{with} sec"
 132
 133      cache = MemCacheDb.new(['localhost:21201',"127.0.0.1:21201"], :timeout => nil)
 134      cache.add('a', 1, 120)
 135      without = xprofile 'get' do
 136        1000.times do
 137          cache.get('a')
 138        end
 139      end
 140      puts "1000 gets without socket timeout: #{without} sec"
 141
 142      assert without < with
 143    end
 144  end
 145
 146  def test_consistent_hashing
 147    requirement(self.respond_to?(:flexmock), 'Flexmock is required to run this test') do
 148
 149      flexmock(MemCacheDb::Server).new_instances.should_receive(:alive?).and_return(true)
 150
 151      # Setup a continuum of two servers
 152      @cache.servers = ['mike1', 'mike2', 'mike3']
 153
 154      keys = []
 155      1000.times do |idx|
 156        keys << idx.to_s
 157      end
 158
 159      before_continuum = keys.map {|key| @cache.get_server_for_key(key) }
 160
 161      @cache.servers = ['mike1', 'mike2', 'mike3', 'mike4']
 162
 163      after_continuum = keys.map {|key| @cache.get_server_for_key(key) }
 164
 165      same_count = before_continuum.zip(after_continuum).find_all {|a| a[0].host == a[1].host }.size
 166
 167      # With continuum, we should see about 75% of the keys map to the same server
 168      # With modulo, we would see about 25%.
 169      assert same_count > 700
 170    end
 171  end
 172  
 173  def test_get_multi_with_server_failure
 174    @cache = MemCacheDb.new 'localhost:1', :namespace => 'my_namespace', :logger => nil #Logger.new(STDOUT)
 175    s1 = FakeServerDb.new
 176    s2 = FakeServerDb.new
 177
 178    # Write two messages to the socket to test failover
 179    s1.socket.data.write "VALUE my_namespace:a 0 14\r\n\004\b\"\0170123456789\r\nEND\r\n"
 180    s1.socket.data.rewind
 181    s2.socket.data.write "bogus response\r\nbogus response\r\n"
 182    s2.socket.data.rewind
 183
 184    @cache.servers = [s1, s2]
 185
 186    assert s1.alive?
 187    assert s2.alive?
 188    # a maps to s1, the rest map to s2
 189    value = @cache.get_multi(['foo', 'bar', 'a', 'b', 'c'])
 190    assert_equal({'a'=>'0123456789'}, value)
 191    assert s1.alive?
 192    assert !s2.alive?
 193  end
 194
 195  def test_cache_get_with_failover
 196    @cache = MemCacheDb.new 'localhost:1', :namespace => 'my_namespace', :logger => nil#Logger.new(STDOUT)
 197    s1 = FakeServerDb.new
 198    s2 = FakeServerDb.new
 199
 200    # Write two messages to the socket to test failover
 201    s1.socket.data.write "VALUE foo 0 14\r\n\004\b\"\0170123456789\r\n"
 202    s1.socket.data.rewind
 203    s2.socket.data.write "bogus response\r\nbogus response\r\n"
 204    s2.socket.data.rewind
 205
 206    @cache.instance_variable_set(:@failover, true)
 207    @cache.servers = [s1, s2]
 208
 209    assert s1.alive?
 210    assert s2.alive?
 211    @cache.get('foo')
 212    assert s1.alive?
 213    assert !s2.alive?
 214  end
 215  
 216  def test_cache_get_without_failover
 217    s1 = FakeServerDb.new
 218    s2 = FakeServerDb.new
 219    
 220    s1.socket.data.write "VALUE foo 0 14\r\n\004\b\"\0170123456789\r\n"
 221    s1.socket.data.rewind
 222    s2.socket.data.write "bogus response\r\nbogus response\r\n"
 223    s2.socket.data.rewind
 224
 225    @cache.instance_variable_set(:@failover, false)
 226    @cache.servers = [s1, s2]
 227
 228    assert s1.alive?
 229    assert s2.alive?
 230    e = assert_raise MemCacheDb::MemCacheDbError do
 231      @cache.get('foo')
 232    end
 233    assert s1.alive?
 234    assert !s2.alive?
 235
 236    assert_equal "No servers available", e.message
 237  end
 238
 239  def test_cache_get
 240    server = util_setup_fake_server
 241
 242    assert_equal "\004\b\"\0170123456789",
 243                 @cache.cache_get(server, 'my_namespace:key')
 244
 245    assert_equal "get my_namespace:key\r\n",
 246                 server.socket.written.string
 247  end
 248
 249  def test_cache_get_EOF
 250    server = util_setup_fake_server
 251    server.socket.data.string = ''
 252
 253    e = assert_raise IndexError do
 254      @cache.cache_get server, 'my_namespace:key'
 255    end
 256
 257    assert_equal "No connection to server (NOT CONNECTED)", e.message
 258  end
 259
 260  def test_cache_get_bad_state
 261    server = FakeServerDb.new
 262
 263    # Write two messages to the socket to test failover
 264    server.socket.data.write "bogus response\r\nbogus response\r\n"
 265    server.socket.data.rewind
 266
 267    @cache.servers = []
 268    @cache.servers << server
 269
 270    e = assert_raise IndexError do
 271      @cache.cache_get(server, 'my_namespace:key')
 272    end
 273
 274    assert_match /#{Regexp.quote 'No connection to server (NOT CONNECTED)'}/, e.message
 275
 276    assert !server.alive?
 277  end
 278
 279  def test_cache_get_miss
 280    socket = FakeSocketDb.new
 281    socket.data.write "END\r\n"
 282    socket.data.rewind
 283    server = FakeServerDb.new socket
 284
 285    assert_equal nil, @cache.cache_get(server, 'my_namespace:key')
 286
 287    assert_equal "get my_namespace:key\r\n",
 288                 socket.written.string
 289  end
 290
 291  def test_cache_get_multi
 292    server = util_setup_fake_server
 293    server.socket.data.write "VALUE foo 0 7\r\n"
 294    server.socket.data.write "\004\b\"\bfoo\r\n"
 295    server.socket.data.write "VALUE bar 0 7\r\n"
 296    server.socket.data.write "\004\b\"\bbar\r\n"
 297    server.socket.data.write "END\r\n"
 298    server.socket.data.rewind
 299
 300    result = @cache.cache_get_multi server, 'foo bar baz'
 301
 302    assert_equal 2, result.length
 303    assert_equal "\004\b\"\bfoo", result['foo']
 304    assert_equal "\004\b\"\bbar", result['bar']
 305  end
 306
 307  def test_cache_get_multi_EOF
 308    server = util_setup_fake_server
 309    server.socket.data.string = ''
 310
 311    e = assert_raise IndexError do
 312      @cache.cache_get_multi server, 'my_namespace:key'
 313    end
 314
 315    assert_equal "No connection to server (NOT CONNECTED)", e.message
 316  end
 317
 318  def test_cache_get_multi_bad_state
 319    server = FakeServerDb.new
 320
 321    # Write two messages to the socket to test failover
 322    server.socket.data.write "bogus response\r\nbogus response\r\n"
 323    server.socket.data.rewind
 324
 325    @cache.servers = []
 326    @cache.servers << server
 327
 328    e = assert_raise IndexError do
 329      @cache.cache_get_multi server, 'my_namespace:key'
 330    end
 331
 332    assert_match /#{Regexp.quote 'No connection to server (NOT CONNECTED)'}/, e.message
 333
 334    assert !server.alive?
 335  end
 336
 337  def test_multithread_error
 338    server = FakeServer.new
 339    server.multithread = false
 340    
 341    @cache = MemCacheDb.new(['localhost:1'], :multithread => false)
 342
 343    server.socket.data.write "bogus response\r\nbogus response\r\n"
 344    server.socket.data.rewind
 345
 346    @cache.servers = []
 347    @cache.servers << server
 348
 349    assert_nothing_raised do
 350      @cache.set 'a', 1
 351    end
 352
 353    passed = true
 354    Thread.new do
 355      begin
 356        @cache.set 'b', 2
 357        passed = false
 358      rescue MemCacheDb::MemCacheError => me
 359        passed = me.message =~ /multiple threads/
 360      end
 361    end
 362    assert passed
 363  end
 364
 365  def test_initialize
 366    cache = MemCacheDb.new :namespace => 'my_namespace', :readonly => true
 367
 368    assert_equal 'my_namespace', cache.namespace
 369    assert_equal true, cache.readonly?
 370    assert_equal true, cache.servers.empty?
 371  end
 372
 373  def test_initialize_compatible
 374    cache = MemCacheDb.new ['localhost:21201', 'localhost:11212'],
 375            :namespace => 'my_namespace', :readonly => true
 376
 377    assert_equal 'my_namespace', cache.namespace
 378    assert_equal true, cache.readonly?
 379    assert_equal false, cache.servers.empty?
 380  end
 381
 382  def test_initialize_compatible_no_hash
 383    cache = MemCacheDb.new ['localhost:21201', 'localhost:11212']
 384
 385    assert_equal nil, cache.namespace
 386    assert_equal false, cache.readonly?
 387    assert_equal false, cache.servers.empty?
 388  end
 389
 390  def test_initialize_compatible_one_server
 391    cache = MemCacheDb.new 'localhost:21201'
 392
 393    assert_equal nil, cache.namespace
 394    assert_equal false, cache.readonly?
 395    assert_equal false, cache.servers.empty?
 396  end
 397
 398  def test_initialize_compatible_bad_arg
 399    e = assert_raise ArgumentError do
 400      cache = MemCacheDb.new Object.new
 401    end
 402
 403    assert_equal 'first argument must be Array, Hash or String', e.message
 404  end
 405
 406  def test_initialize_multiple_servers
 407    cache = MemCacheDb.new %w[localhost:21201 localhost:11212],
 408                         :namespace => 'my_namespace', :readonly => true
 409
 410    assert_equal 'my_namespace', cache.namespace
 411    assert_equal true, cache.readonly?
 412    assert_equal false, cache.servers.empty?
 413    assert !cache.instance_variable_get(:@continuum).empty?
 414  end
 415
 416  def test_initialize_too_many_args
 417    assert_raises ArgumentError do
 418      MemCacheDb.new 1, 2, 3
 419    end
 420  end
 421
 422  def test_decr
 423    server = FakeServerDb.new
 424    server.socket.data.write "5\r\n"
 425    server.socket.data.rewind
 426
 427    @cache.servers = []
 428    @cache.servers << server
 429
 430    value = @cache.decr 'key'
 431
 432    assert_equal "decr my_namespace:key 1\r\n",
 433                 @cache.servers.first.socket.written.string
 434
 435    assert_equal 5, value
 436  end
 437
 438  def test_decr_not_found
 439    server = FakeServerDb.new
 440    server.socket.data.write "NOT_FOUND\r\n"
 441    server.socket.data.rewind
 442
 443    @cache.servers = []
 444    @cache.servers << server
 445
 446    value = @cache.decr 'key'
 447
 448    assert_equal "decr my_namespace:key 1\r\n",
 449                 @cache.servers.first.socket.written.string
 450
 451    assert_equal nil, value
 452  end
 453
 454  def test_decr_space_padding
 455    server = FakeServerDb.new
 456    server.socket.data.write "5 \r\n"
 457    server.socket.data.rewind
 458
 459    @cache.servers = []
 460    @cache.servers << server
 461
 462    value = @cache.decr 'key'
 463
 464    assert_equal "decr my_namespace:key 1\r\n",
 465                 @cache.servers.first.socket.written.string
 466
 467    assert_equal 5, value
 468  end
 469
 470  def test_get
 471    util_setup_fake_server
 472
 473    value = @cache.get 'key'
 474
 475    assert_equal "get my_namespace:key\r\n",
 476                 @cache.servers.first.socket.written.string
 477
 478    assert_equal '0123456789', value
 479  end
 480
 481  def test_get_bad_key
 482    util_setup_fake_server
 483    assert_raise ArgumentError do @cache.get 'k y' end
 484
 485    util_setup_fake_server
 486    assert_raise ArgumentError do @cache.get 'k' * 250 end
 487  end
 488
 489  def test_get_cache_get_IOError
 490    socket = Object.new
 491    def socket.write(arg) raise IOError, 'some io error'; end
 492    server = FakeServerDb.new socket
 493
 494    @cache.servers = []
 495    @cache.servers << server
 496
 497    e = assert_raise MemCacheDb::MemCacheDbError do
 498      @cache.get 'my_namespace:key'
 499    end
 500
 501    assert_equal 'some io error', e.message
 502  end
 503
 504  def test_get_cache_get_SystemCallError
 505    socket = Object.new
 506    def socket.write(arg) raise SystemCallError, 'some syscall error'; end
 507    server = FakeServerDb.new socket
 508
 509    @cache.servers = []
 510    @cache.servers << server
 511
 512    e = assert_raise MemCacheDb::MemCacheDbError do
 513      @cache.get 'my_namespace:key'
 514    end
 515
 516    assert_equal 'unknown error - some syscall error', e.message
 517  end
 518
 519  def test_get_no_connection
 520    @cache.servers = 'localhost:1'
 521    e = assert_raise MemCacheDb::MemCacheDbError do
 522      @cache.get 'key'
 523    end
 524
 525    assert_match /^No connection to server/, e.message
 526  end
 527
 528  def test_get_no_servers
 529    @cache.servers = []
 530    e = assert_raise MemCacheDb::MemCacheDbError do
 531      @cache.get 'key'
 532    end
 533
 534    assert_equal 'No active servers', e.message
 535  end
 536
 537  def test_get_multi
 538    server = FakeServerDb.new
 539    server.socket.data.write "VALUE my_namespace:key 0 14\r\n"
 540    server.socket.data.write "\004\b\"\0170123456789\r\n"
 541    server.socket.data.write "VALUE my_namespace:keyb 0 14\r\n"
 542    server.socket.data.write "\004\b\"\0179876543210\r\n"
 543    server.socket.data.write "END\r\n"
 544    server.socket.data.rewind
 545
 546    @cache.servers = []
 547    @cache.servers << server
 548
 549    values = @cache.get_multi 'key', 'keyb'
 550
 551    assert_equal "get my_namespace:key my_namespace:keyb\r\n",
 552                 server.socket.written.string
 553
 554    expected = { 'key' => '0123456789', 'keyb' => '9876543210' }
 555
 556    assert_equal expected.sort, values.sort
 557  end
 558
 559  def test_get_raw
 560    server = FakeServerDb.new
 561    server.socket.data.write "VALUE my_namespace:key 0 10\r\n"
 562    server.socket.data.write "0123456789\r\n"
 563    server.socket.data.write "END\r\n"
 564    server.socket.data.rewind
 565
 566    @cache.servers = []
 567    @cache.servers << server
 568
 569
 570    value = @cache.get 'key', true
 571
 572    assert_equal "get my_namespace:key\r\n",
 573                 @cache.servers.first.socket.written.string
 574
 575    assert_equal '0123456789', value
 576  end
 577
 578  def test_get_server_for_key
 579    server = @cache.get_server_for_key 'key'
 580    assert_equal 'localhost', server.host
 581    assert_equal 1, server.port
 582  end
 583
 584  def test_get_server_for_key_multiple
 585    s1 = util_setup_server @cache, 'one.example.com', ''
 586    s2 = util_setup_server @cache, 'two.example.com', ''
 587    @cache.servers = [s1, s2]
 588
 589    server = @cache.get_server_for_key 'keya'
 590    assert_equal 'two.example.com', server.host
 591    server = @cache.get_server_for_key 'keyb'
 592    assert_equal 'two.example.com', server.host
 593    server = @cache.get_server_for_key 'keyc'
 594    assert_equal 'two.example.com', server.host
 595    server = @cache.get_server_for_key 'keyd'
 596    assert_equal 'one.example.com', server.host
 597  end
 598
 599  def test_get_server_for_key_no_servers
 600    @cache.servers = []
 601
 602    e = assert_raise MemCacheDb::MemCacheDbError do
 603      @cache.get_server_for_key 'key'
 604    end
 605
 606    assert_equal 'No servers available', e.message
 607  end
 608
 609  def test_get_server_for_key_spaces
 610    e = assert_raise ArgumentError do
 611      @cache.get_server_for_key 'space key'
 612    end
 613    assert_equal 'illegal character in key "space key"', e.message
 614  end
 615
 616  def test_get_server_for_key_length
 617    @cache.get_server_for_key 'x' * 250
 618    long_key = 'x' * 251
 619    e = assert_raise ArgumentError do
 620      @cache.get_server_for_key long_key
 621    end
 622    assert_equal "key too long #{long_key.inspect}", e.message
 623  end
 624
 625  def test_incr
 626    server = FakeServerDb.new
 627    server.socket.data.write "5\r\n"
 628    server.socket.data.rewind
 629
 630    @cache.servers = []
 631    @cache.servers << server
 632
 633    value = @cache.incr 'key'
 634
 635    assert_equal "incr my_namespace:key 1\r\n",
 636                 @cache.servers.first.socket.written.string
 637
 638    assert_equal 5, value
 639  end
 640
 641  def test_incr_not_found
 642    server = FakeServerDb.new
 643    server.socket.data.write "NOT_FOUND\r\n"
 644    server.socket.data.rewind
 645
 646    @cache.servers = []
 647    @cache.servers << server
 648
 649    value = @cache.incr 'key'
 650
 651    assert_equal "incr my_namespace:key 1\r\n",
 652                 @cache.servers.first.socket.written.string
 653
 654    assert_equal nil, value
 655  end
 656
 657  def test_incr_space_padding
 658    server = FakeServerDb.new
 659    server.socket.data.write "5 \r\n"
 660    server.socket.data.rewind
 661
 662    @cache.servers = []
 663    @cache.servers << server
 664
 665    value = @cache.incr 'key'
 666
 667    assert_equal "incr my_namespace:key 1\r\n",
 668                 @cache.servers.first.socket.written.string
 669
 670    assert_equal 5, value
 671  end
 672
 673  def test_make_cache_key
 674    assert_equal 'my_namespace:key', @cache.make_cache_key('key')
 675    @cache.namespace = nil
 676    assert_equal 'key', @cache.make_cache_key('key')
 677  end
 678
 679  def test_servers
 680    server = FakeServerDb.new
 681    @cache.servers = []
 682    @cache.servers << server
 683    assert_equal [server], @cache.servers
 684  end
 685
 686  def test_set
 687    server = FakeServerDb.new
 688    server.socket.data.write "STORED\r\n"
 689    server.socket.data.rewind
 690    @cache.servers = []
 691    @cache.servers << server
 692
 693    @cache.set 'key', 'value'
 694
 695    dumped = Marshal.dump('value')
 696    expected = "set my_namespace:key 0 0 #{dumped.length}\r\n#{dumped}\r\n"
 697#    expected = "set my_namespace:key 0 0 9\r\n\004\b\"\nvalue\r\n"
 698    assert_equal expected, server.socket.written.string
 699  end
 700
 701  def test_set_expiry
 702    server = FakeServerDb.new
 703    server.socket.data.write "STORED\r\n"
 704    server.socket.data.rewind
 705    @cache.servers = []
 706    @cache.servers << server
 707
 708    @cache.set 'key', 'value', 5
 709
 710    dumped = Marshal.dump('value')
 711    expected = "set my_namespace:key 0 5 #{dumped.length}\r\n#{dumped}\r\n"
 712    assert_equal expected, server.socket.written.string
 713  end
 714
 715  def test_set_raw
 716    server = FakeServerDb.new
 717    server.socket.data.write "STORED\r\n"
 718    server.socket.data.rewind
 719    @cache.servers = []
 720    @cache.servers << server
 721
 722    @cache.set 'key', 'value', 0, true
 723
 724    expected = "set my_namespace:key 0 0 5\r\nvalue\r\n"
 725    assert_equal expected, server.socket.written.string
 726  end
 727
 728  def test_set_readonly
 729    cache = MemCacheDb.new :readonly => true
 730
 731    e = assert_raise MemCacheDb::MemCacheDbError do
 732      cache.set 'key', 'value'
 733    end
 734
 735    assert_equal 'Update of readonly cache', e.message
 736  end
 737
 738  def test_set_too_big
 739    server = FakeServerDb.new
 740
 741    # Write two messages to the socket to test failover
 742    server.socket.data.write "SERVER_ERROR\r\nSERVER_ERROR object too large for cache\r\n"
 743    server.socket.data.rewind
 744
 745    @cache.servers = []
 746    @cache.servers << server
 747
 748    e = assert_raise MemCacheDb::MemCacheDbError do
 749      @cache.set 'key', 'v'
 750    end
 751
 752    assert_match /object too large for cache/, e.message
 753  end
 754
 755  def test_add
 756    server = FakeServerDb.new
 757    server.socket.data.write "STORED\r\n"
 758    server.socket.data.rewind
 759    @cache.servers = []
 760    @cache.servers << server
 761
 762    @cache.add 'key', 'value'
 763    
 764    dumped = Marshal.dump('value')
 765
 766    expected = "add my_namespace:key 0 0 #{dumped.length}\r\n#{dumped}\r\n"
 767    assert_equal expected, server.socket.written.string
 768  end
 769
 770  def test_add_exists
 771    server = FakeServerDb.new
 772    server.socket.data.write "NOT_STORED\r\n"
 773    server.socket.data.rewind
 774    @cache.servers = []
 775    @cache.servers << server
 776
 777    @cache.add 'key', 'value'
 778
 779    dumped = Marshal.dump('value')
 780    expected = "add my_namespace:key 0 0 #{dumped.length}\r\n#{dumped}\r\n"
 781    assert_equal expected, server.socket.written.string
 782  end
 783
 784  def test_add_expiry
 785    server = FakeServerDb.new
 786    server.socket.data.write "STORED\r\n"
 787    server.socket.data.rewind
 788    @cache.servers = []
 789    @cache.servers << server
 790
 791    @cache.add 'key', 'value', 5
 792
 793    dumped = Marshal.dump('value')
 794    expected = "add my_namespace:key 0 5 #{dumped.length}\r\n#{dumped}\r\n"
 795    assert_equal expected, server.socket.written.string
 796  end
 797
 798  def test_add_raw
 799    server = FakeServerDb.new
 800    server.socket.data.write "STORED\r\n"
 801    server.socket.data.rewind
 802    @cache.servers = []
 803    @cache.servers << server
 804
 805    @cache.add 'key', 'value', 0, true
 806
 807    expected = "add my_namespace:key 0 0 5\r\nvalue\r\n"
 808    assert_equal expected, server.socket.written.string
 809  end
 810
 811  def test_add_raw_int
 812    server = FakeServerDb.new
 813    server.socket.data.write "STORED\r\n"
 814    server.socket.data.rewind
 815    @cache.servers = []
 816    @cache.servers << server
 817
 818    @cache.add 'key', 12, 0, true
 819
 820    expected = "add my_namespace:key 0 0 2\r\n12\r\n"
 821    assert_equal expected, server.socket.written.string
 822  end
 823
 824  def test_add_readonly
 825    cache = MemCacheDb.new :readonly => true
 826
 827    e = assert_raise MemCacheDb::MemCacheDbError do
 828      cache.add 'key', 'value'
 829    end
 830
 831    assert_equal 'Update of readonly cache', e.message
 832  end
 833
 834  def test_delete
 835    server = FakeServerDb.new
 836    @cache.servers = []
 837    @cache.servers << server
 838    
 839    @cache.delete 'key'
 840    
 841    expected = "delete my_namespace:key 0\r\n"
 842    assert_equal expected, server.socket.written.string
 843  end
 844
 845  def test_delete_with_expiry
 846    server = FakeServerDb.new
 847    @cache.servers = []
 848    @cache.servers << server
 849    
 850    @cache.delete 'key', 300
 851    
 852    expected = "delete my_namespace:key 300\r\n"
 853    assert_equal expected, server.socket.written.string
 854  end
 855
 856  def test_flush_all
 857    @cache.servers = []
 858    3.times { @cache.servers << FakeServerDb.new }
 859
 860    @cache.flush_all
 861
 862    expected = "flush_all\r\n"
 863    @cache.servers.each do |server|
 864      assert_equal expected, server.socket.written.string
 865    end
 866  end
 867
 868  def test_flush_all_failure
 869    socket = FakeSocketDb.new
 870
 871    # Write two messages to the socket to test failover
 872    socket.data.write "ERROR\r\nERROR\r\n"
 873    socket.data.rewind
 874
 875    server = FakeServerDb.new socket
 876
 877    @cache.servers = []
 878    @cache.servers << server
 879
 880    assert_raise MemCacheDb::MemCacheDbError do
 881      @cache.flush_all
 882    end
 883
 884    assert_match /flush_all\r\n/, socket.written.string
 885  end
 886
 887  def test_stats
 888    socket = FakeSocketDb.new
 889    socket.data.write "STAT pid 20188\r\nSTAT total_items 32\r\nSTAT version 1.2.3\r\nSTAT rusage_user 1:300\r\nSTAT dummy ok\r\nEND\r\n"
 890    socket.data.rewind
 891    server = FakeServerDb.new socket
 892    def server.host() 'localhost'; end
 893    def server.port() 21201; end
 894
 895    @cache.servers = []
 896    @cache.servers << server
 897
 898    expected = {
 899      'localhost:21201' => {
 900        'pid' => 20188, 'total_items' => 32, 'version' => '1.2.3',
 901        'rusage_user' => 1.0003, 'dummy' => 'ok'
 902      }
 903    }
 904    assert_equal expected, @cache.stats
 905
 906    assert_equal "stats\r\n", socket.written.string
 907  end
 908
 909  def test_basic_threaded_operations_should_work
 910    cache = MemCacheDb.new :multithread => true,
 911                         :namespace => 'my_namespace',
 912                         :readonly => false
 913
 914    server = FakeServerDb.new
 915    server.socket.data.write "STORED\r\n"
 916    server.socket.data.rewind
 917
 918    cache.servers = []
 919    cache.servers << server
 920
 921    assert cache.multithread
 922
 923    assert_nothing_raised do
 924      cache.set "test", "test value"
 925    end
 926
 927    output = server.socket.written.string
 928    assert_match /set my_namespace:test/, output
 929    assert_match /test value/, output
 930  end
 931
 932  def test_basic_unthreaded_operations_should_work
 933    cache = MemCacheDb.new :multithread => false,
 934                         :namespace => 'my_namespace',
 935                         :readonly => false
 936
 937    server = FakeServerDb.new
 938    server.socket.data.write "STORED\r\n"
 939    server.socket.data.rewind
 940
 941    cache.servers = []
 942    cache.servers << server
 943
 944    assert !cache.multithread
 945
 946    assert_nothing_raised do
 947      cache.set "test", "test value"
 948    end
 949
 950    output = server.socket.written.string
 951    assert_match /set my_namespace:test/, output
 952    assert_match /test value/, output
 953  end
 954
 955  def util_setup_fake_server
 956    server = FakeServerDb.new
 957    server.socket.data.write "VALUE my_namespace:key 0 14\r\n"
 958    server.socket.data.write "\004\b\"\0170123456789\r\n"
 959    server.socket.data.write "END\r\n"
 960    server.socket.data.rewind
 961
 962    @cache.servers = []
 963    @cache.servers << server
 964
 965    return server
 966  end
 967
 968  def util_setup_server(memcache, host, responses)
 969    server = MemCacheDb::Server.new memcache, host
 970    server.instance_variable_set :@sock, StringIO.new(responses)
 971
 972    @cache.servers = []
 973    @cache.servers << server
 974
 975    return server
 976  end
 977
 978  def test_crazy_multithreaded_access
 979    requirement(memcached_running?, 'A real memcached server must be running for performance testing') do
 980
 981      cache = MemCacheDb.new(['localhost:21201', '127.0.0.1:21201'])
 982      cache.flush_all
 983      workers = []
 984
 985      # Have a bunch of threads perform a bunch of operations at the same time.
 986      # Verify the result of each operation to ensure the request and response
 987      # are not intermingled between threads.
 988      10.times do
 989        workers << Thread.new do
 990          100.times do
 991            cache.set('a', 9)
 992            cache.set('b', 11)
 993            cache.add('c', 10, 0, true)
 994            assert_equal "NOT_STORED\r\n", cache.add('a', 11)
 995            assert_equal({ 'a' => 9, 'b' => 11 }, cache.get_multi(['a', 'b']))
 996            inc = cache.incr('c', 10)
 997            assert_equal 0, inc % 5
 998            assert inc > 14
 999            assert cache.decr('c', 5) > 14
1000            assert_equal 11, cache.get('b')
1001          end
1002        end
1003      end
1004
1005      workers.each { |w| w.join }
1006      cache.flush_all
1007    end
1008  end
1009
1010end
1011