PageRenderTime 145ms CodeModel.GetById 20ms app.highlight 118ms RepoModel.GetById 1ms app.codeStats 0ms

/test/test_mem_cache.rb

https://github.com/jsl/memcache-client
Ruby | 1170 lines | 891 code | 263 blank | 16 comment | 6 complexity | 0385f2389b137ee6c3489b3cb90aaa60 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?(MemCache)
  17
  18class MemCache
  19
  20  attr_writer :namespace
  21
  22end
  23
  24class FakeSocket
  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', 11211) 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 FakeServer
  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 = 11211
  89    @socket = socket || FakeSocket.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 TestMemCache < Test::Unit::TestCase
 114
 115  def setup
 116    @cache = MemCache.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 = MemCache.new(['localhost:11211',"127.0.0.1:11211"])
 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 = MemCache.new(['localhost:11211',"127.0.0.1:11211"], :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    end
 142  end
 143
 144  def test_consistent_hashing
 145    requirement(self.respond_to?(:flexmock), 'Flexmock is required to run this test') do
 146
 147      flexmock(MemCache::Server).new_instances.should_receive(:alive?).and_return(true)
 148
 149      # Setup a continuum of two servers
 150      @cache.servers = ['mike1', 'mike2', 'mike3']
 151
 152      keys = []
 153      1000.times do |idx|
 154        keys << idx.to_s
 155      end
 156
 157      before_continuum = keys.map {|key| @cache.get_server_for_key(key) }
 158
 159      @cache.servers = ['mike1', 'mike2', 'mike3', 'mike4']
 160
 161      after_continuum = keys.map {|key| @cache.get_server_for_key(key) }
 162
 163      same_count = before_continuum.zip(after_continuum).find_all {|a| a[0].host == a[1].host }.size
 164
 165      # With continuum, we should see about 75% of the keys map to the same server
 166      # With modulo, we would see about 25%.
 167      assert same_count > 700
 168    end
 169  end
 170  
 171  def test_get_multi_with_server_failure
 172    @cache = MemCache.new 'localhost:1', :namespace => 'my_namespace', :logger => nil #Logger.new(STDOUT)
 173    s1 = FakeServer.new
 174    s2 = FakeServer.new
 175
 176    # Write two messages to the socket to test failover
 177    s1.socket.data.write "VALUE my_namespace:a 0 14\r\n\004\b\"\0170123456789\r\nEND\r\n"
 178    s1.socket.data.rewind
 179    s2.socket.data.write "bogus response\r\nbogus response\r\n"
 180    s2.socket.data.rewind
 181
 182    @cache.servers = [s1, s2]
 183
 184    assert s1.alive?
 185    assert s2.alive?
 186    # a maps to s1, the rest map to s2
 187    value = @cache.get_multi(['foo', 'bar', 'a', 'b', 'c'])
 188    assert_equal({'a'=>'0123456789'}, value)
 189    assert s1.alive?
 190    assert !s2.alive?
 191  end
 192
 193  def test_cache_get_with_failover
 194    @cache = MemCache.new 'localhost:1', :namespace => 'my_namespace', :logger => nil#Logger.new(STDOUT)
 195    s1 = FakeServer.new
 196    s2 = FakeServer.new
 197
 198    # Write two messages to the socket to test failover
 199    s1.socket.data.write "VALUE foo 0 14\r\n\004\b\"\0170123456789\r\n"
 200    s1.socket.data.rewind
 201    s2.socket.data.write "bogus response\r\nbogus response\r\n"
 202    s2.socket.data.rewind
 203
 204    @cache.instance_variable_set(:@failover, true)
 205    @cache.servers = [s1, s2]
 206
 207    assert s1.alive?
 208    assert s2.alive?
 209    @cache.get('foo')
 210    assert s1.alive?
 211    assert !s2.alive?
 212  end
 213  
 214  def test_cache_get_without_failover
 215    s1 = FakeServer.new
 216    s2 = FakeServer.new
 217    
 218    s1.socket.data.write "VALUE foo 0 14\r\n\004\b\"\0170123456789\r\n"
 219    s1.socket.data.rewind
 220    s2.socket.data.write "bogus response\r\nbogus response\r\n"
 221    s2.socket.data.rewind
 222
 223    @cache.instance_variable_set(:@failover, false)
 224    @cache.servers = [s1, s2]
 225
 226    assert s1.alive?
 227    assert s2.alive?
 228    e = assert_raise MemCache::MemCacheError do
 229      @cache.get('foo')
 230    end
 231    assert s1.alive?
 232    assert !s2.alive?
 233
 234    assert_equal "No servers available", e.message
 235  end
 236
 237  def test_cache_get
 238    server = util_setup_fake_server
 239
 240    assert_equal "\004\b\"\0170123456789",
 241                 @cache.cache_get(server, 'my_namespace:key')
 242
 243    assert_equal "get my_namespace:key\r\n",
 244                 server.socket.written.string
 245  end
 246
 247  def test_cache_get_EOF
 248    server = util_setup_fake_server
 249    server.socket.data.string = ''
 250
 251    e = assert_raise IndexError do
 252      @cache.cache_get server, 'my_namespace:key'
 253    end
 254
 255    assert_equal "No connection to server (NOT CONNECTED)", e.message
 256  end
 257
 258  def test_cache_get_bad_state
 259    server = FakeServer.new
 260
 261    # Write two messages to the socket to test failover
 262    server.socket.data.write "bogus response\r\nbogus response\r\n"
 263    server.socket.data.rewind
 264
 265    @cache.servers = []
 266    @cache.servers << server
 267
 268    e = assert_raise IndexError do
 269      @cache.cache_get(server, 'my_namespace:key')
 270    end
 271
 272    assert_match /#{Regexp.quote 'No connection to server (NOT CONNECTED)'}/, e.message
 273
 274    assert !server.alive?
 275  end
 276
 277  def test_cache_get_miss
 278    socket = FakeSocket.new
 279    socket.data.write "END\r\n"
 280    socket.data.rewind
 281    server = FakeServer.new socket
 282
 283    assert_equal nil, @cache.cache_get(server, 'my_namespace:key')
 284
 285    assert_equal "get my_namespace:key\r\n",
 286                 socket.written.string
 287  end
 288
 289  def test_cache_get_multi
 290    server = util_setup_fake_server
 291    server.socket.data.write "VALUE foo 0 7\r\n"
 292    server.socket.data.write "\004\b\"\bfoo\r\n"
 293    server.socket.data.write "VALUE bar 0 7\r\n"
 294    server.socket.data.write "\004\b\"\bbar\r\n"
 295    server.socket.data.write "END\r\n"
 296    server.socket.data.rewind
 297
 298    result = @cache.cache_get_multi server, 'foo bar baz'
 299
 300    assert_equal 2, result.length
 301    assert_equal "\004\b\"\bfoo", result['foo']
 302    assert_equal "\004\b\"\bbar", result['bar']
 303  end
 304
 305  def test_cache_get_multi_EOF
 306    server = util_setup_fake_server
 307    server.socket.data.string = ''
 308
 309    e = assert_raise IndexError do
 310      @cache.cache_get_multi server, 'my_namespace:key'
 311    end
 312
 313    assert_equal "No connection to server (NOT CONNECTED)", e.message
 314  end
 315
 316  def test_cache_get_multi_bad_state
 317    server = FakeServer.new
 318
 319    # Write two messages to the socket to test failover
 320    server.socket.data.write "bogus response\r\nbogus response\r\n"
 321    server.socket.data.rewind
 322
 323    @cache.servers = []
 324    @cache.servers << server
 325
 326    e = assert_raise IndexError do
 327      @cache.cache_get_multi server, 'my_namespace:key'
 328    end
 329
 330    assert_match /#{Regexp.quote 'No connection to server (NOT CONNECTED)'}/, e.message
 331
 332    assert !server.alive?
 333  end
 334
 335  def test_multithread_error
 336    server = FakeServer.new
 337    server.multithread = false
 338    
 339    @cache = MemCache.new(['localhost:1'], :multithread => false)
 340
 341    server.socket.data.write "bogus response\r\nbogus response\r\n"
 342    server.socket.data.rewind
 343
 344    @cache.servers = []
 345    @cache.servers << server
 346
 347    assert_nothing_raised do
 348      @cache.set 'a', 1
 349    end
 350
 351    passed = true
 352    Thread.new do
 353      begin
 354        @cache.set 'b', 2
 355        passed = false
 356      rescue MemCache::MemCacheError => me
 357        passed = me.message =~ /multiple threads/
 358      end
 359    end
 360    assert passed
 361  end
 362
 363  def test_initialize
 364    cache = MemCache.new :namespace => 'my_namespace', :readonly => true
 365
 366    assert_equal 'my_namespace', cache.namespace
 367    assert_equal true, cache.readonly?
 368    assert_equal true, cache.servers.empty?
 369  end
 370
 371  def test_initialize_compatible
 372    cache = MemCache.new ['localhost:11211', 'localhost:11212'],
 373            :namespace => 'my_namespace', :readonly => true
 374
 375    assert_equal 'my_namespace', cache.namespace
 376    assert_equal true, cache.readonly?
 377    assert_equal false, cache.servers.empty?
 378  end
 379
 380  def test_initialize_compatible_no_hash
 381    cache = MemCache.new ['localhost:11211', 'localhost:11212']
 382
 383    assert_equal nil, cache.namespace
 384    assert_equal false, cache.readonly?
 385    assert_equal false, cache.servers.empty?
 386  end
 387
 388  def test_initialize_compatible_one_server
 389    cache = MemCache.new 'localhost:11211'
 390
 391    assert_equal nil, cache.namespace
 392    assert_equal false, cache.readonly?
 393    assert_equal false, cache.servers.empty?
 394  end
 395
 396  def test_initialize_compatible_bad_arg
 397    e = assert_raise ArgumentError do
 398      cache = MemCache.new Object.new
 399    end
 400
 401    assert_equal 'first argument must be Array, Hash or String', e.message
 402  end
 403
 404  def test_initialize_multiple_servers
 405    cache = MemCache.new %w[localhost:11211 localhost:11212],
 406                         :namespace => 'my_namespace', :readonly => true
 407
 408    assert_equal 'my_namespace', cache.namespace
 409    assert_equal true, cache.readonly?
 410    assert_equal false, cache.servers.empty?
 411    assert !cache.instance_variable_get(:@continuum).empty?
 412  end
 413
 414  def test_initialize_too_many_args
 415    assert_raises ArgumentError do
 416      MemCache.new 1, 2, 3
 417    end
 418  end
 419
 420  def test_decr
 421    server = FakeServer.new
 422    server.socket.data.write "5\r\n"
 423    server.socket.data.rewind
 424
 425    @cache.servers = []
 426    @cache.servers << server
 427
 428    value = @cache.decr 'key'
 429
 430    assert_equal "decr my_namespace:key 1\r\n",
 431                 @cache.servers.first.socket.written.string
 432
 433    assert_equal 5, value
 434  end
 435
 436  def test_decr_not_found
 437    server = FakeServer.new
 438    server.socket.data.write "NOT_FOUND\r\n"
 439    server.socket.data.rewind
 440
 441    @cache.servers = []
 442    @cache.servers << server
 443
 444    value = @cache.decr 'key'
 445
 446    assert_equal "decr my_namespace:key 1\r\n",
 447                 @cache.servers.first.socket.written.string
 448
 449    assert_equal nil, value
 450  end
 451
 452  def test_decr_space_padding
 453    server = FakeServer.new
 454    server.socket.data.write "5 \r\n"
 455    server.socket.data.rewind
 456
 457    @cache.servers = []
 458    @cache.servers << server
 459
 460    value = @cache.decr 'key'
 461
 462    assert_equal "decr my_namespace:key 1\r\n",
 463                 @cache.servers.first.socket.written.string
 464
 465    assert_equal 5, value
 466  end
 467
 468  def test_get
 469    util_setup_fake_server
 470
 471    value = @cache.get 'key'
 472
 473    assert_equal "get my_namespace:key\r\n",
 474                 @cache.servers.first.socket.written.string
 475
 476    assert_equal '0123456789', value
 477  end
 478
 479  def test_fetch_without_a_block
 480    server = FakeServer.new
 481    server.socket.data.write "END\r\n"
 482    server.socket.data.rewind
 483
 484    @cache.servers = [server]
 485
 486    flexmock(@cache).should_receive(:get).with('key', false).and_return(nil)
 487
 488    value = @cache.fetch('key', 1)
 489    assert_equal nil, value
 490  end
 491  
 492  def test_fetch_miss
 493    server = FakeServer.new
 494    server.socket.data.write "END\r\n"
 495    server.socket.data.rewind
 496
 497    @cache.servers = [server]
 498
 499    flexmock(@cache).should_receive(:get).with('key', false).and_return(nil)
 500    flexmock(@cache).should_receive(:add).with('key', 'value', 1, false)
 501
 502    value = @cache.fetch('key', 1) { 'value' }
 503
 504    assert_equal 'value', value
 505  end
 506
 507  def test_fetch_hit
 508    server = FakeServer.new
 509    server.socket.data.write "END\r\n"
 510    server.socket.data.rewind
 511
 512    @cache.servers = [server]
 513
 514    flexmock(@cache).should_receive(:get).with('key', false).and_return('value')
 515    flexmock(@cache).should_receive(:add).never
 516
 517    value = @cache.fetch('key', 1) { raise 'Should not be called.' }
 518
 519    assert_equal 'value', value
 520  end
 521
 522  def test_get_bad_key
 523    util_setup_fake_server
 524    assert_raise ArgumentError do @cache.get 'k y' end
 525
 526    util_setup_fake_server
 527    assert_raise ArgumentError do @cache.get 'k' * 250 end
 528  end
 529
 530  def test_get_cache_get_IOError
 531    socket = Object.new
 532    def socket.write(arg) raise IOError, 'some io error'; end
 533    server = FakeServer.new socket
 534
 535    @cache.servers = []
 536    @cache.servers << server
 537
 538    e = assert_raise MemCache::MemCacheError do
 539      @cache.get 'my_namespace:key'
 540    end
 541
 542    assert_equal 'some io error', e.message
 543  end
 544
 545  def test_get_cache_get_SystemCallError
 546    socket = Object.new
 547    def socket.write(arg) raise SystemCallError, 'some syscall error'; end
 548    server = FakeServer.new socket
 549
 550    @cache.servers = []
 551    @cache.servers << server
 552
 553    e = assert_raise MemCache::MemCacheError do
 554      @cache.get 'my_namespace:key'
 555    end
 556
 557    assert_equal 'unknown error - some syscall error', e.message
 558  end
 559
 560  def test_get_no_connection
 561    @cache.servers = 'localhost:1'
 562    e = assert_raise MemCache::MemCacheError do
 563      @cache.get 'key'
 564    end
 565
 566    assert_match /^No connection to server/, e.message
 567  end
 568
 569  def test_get_no_servers
 570    @cache.servers = []
 571    e = assert_raise MemCache::MemCacheError do
 572      @cache.get 'key'
 573    end
 574
 575    assert_equal 'No active servers', e.message
 576  end
 577
 578  def test_get_multi
 579    server = FakeServer.new
 580    server.socket.data.write "VALUE my_namespace:key 0 14\r\n"
 581    server.socket.data.write "\004\b\"\0170123456789\r\n"
 582    server.socket.data.write "VALUE my_namespace:keyb 0 14\r\n"
 583    server.socket.data.write "\004\b\"\0179876543210\r\n"
 584    server.socket.data.write "END\r\n"
 585    server.socket.data.rewind
 586
 587    @cache.servers = []
 588    @cache.servers << server
 589
 590    values = @cache.get_multi 'key', 'keyb'
 591
 592    assert_equal "get my_namespace:key my_namespace:keyb\r\n",
 593                 server.socket.written.string
 594
 595    expected = { 'key' => '0123456789', 'keyb' => '9876543210' }
 596
 597    assert_equal expected.sort, values.sort
 598  end
 599
 600  def test_get_raw
 601    server = FakeServer.new
 602    server.socket.data.write "VALUE my_namespace:key 0 10\r\n"
 603    server.socket.data.write "0123456789\r\n"
 604    server.socket.data.write "END\r\n"
 605    server.socket.data.rewind
 606
 607    @cache.servers = []
 608    @cache.servers << server
 609
 610
 611    value = @cache.get 'key', true
 612
 613    assert_equal "get my_namespace:key\r\n",
 614                 @cache.servers.first.socket.written.string
 615
 616    assert_equal '0123456789', value
 617  end
 618
 619  def test_get_server_for_key
 620    server = @cache.get_server_for_key 'key'
 621    assert_equal 'localhost', server.host
 622    assert_equal 1, server.port
 623  end
 624
 625  def test_get_server_for_key_multiple
 626    s1 = util_setup_server @cache, 'one.example.com', ''
 627    s2 = util_setup_server @cache, 'two.example.com', ''
 628    @cache.servers = [s1, s2]
 629
 630    server = @cache.get_server_for_key 'keya'
 631    assert_equal 'two.example.com', server.host
 632    server = @cache.get_server_for_key 'keyb'
 633    assert_equal 'two.example.com', server.host
 634    server = @cache.get_server_for_key 'keyc'
 635    assert_equal 'two.example.com', server.host
 636    server = @cache.get_server_for_key 'keyd'
 637    assert_equal 'one.example.com', server.host
 638  end
 639
 640  def test_get_server_for_key_no_servers
 641    @cache.servers = []
 642
 643    e = assert_raise MemCache::MemCacheError do
 644      @cache.get_server_for_key 'key'
 645    end
 646
 647    assert_equal 'No servers available', e.message
 648  end
 649
 650  def test_get_server_for_key_spaces
 651    e = assert_raise ArgumentError do
 652      @cache.get_server_for_key 'space key'
 653    end
 654    assert_equal 'illegal character in key "space key"', e.message
 655  end
 656
 657  def test_get_server_for_key_length
 658    @cache.get_server_for_key 'x' * 250
 659    long_key = 'x' * 251
 660    e = assert_raise ArgumentError do
 661      @cache.get_server_for_key long_key
 662    end
 663    assert_equal "key too long #{long_key.inspect}", e.message
 664  end
 665
 666  def test_incr
 667    server = FakeServer.new
 668    server.socket.data.write "5\r\n"
 669    server.socket.data.rewind
 670
 671    @cache.servers = []
 672    @cache.servers << server
 673
 674    value = @cache.incr 'key'
 675
 676    assert_equal "incr my_namespace:key 1\r\n",
 677                 @cache.servers.first.socket.written.string
 678
 679    assert_equal 5, value
 680  end
 681
 682  def test_incr_not_found
 683    server = FakeServer.new
 684    server.socket.data.write "NOT_FOUND\r\n"
 685    server.socket.data.rewind
 686
 687    @cache.servers = []
 688    @cache.servers << server
 689
 690    value = @cache.incr 'key'
 691
 692    assert_equal "incr my_namespace:key 1\r\n",
 693                 @cache.servers.first.socket.written.string
 694
 695    assert_equal nil, value
 696  end
 697
 698  def test_incr_space_padding
 699    server = FakeServer.new
 700    server.socket.data.write "5 \r\n"
 701    server.socket.data.rewind
 702
 703    @cache.servers = []
 704    @cache.servers << server
 705
 706    value = @cache.incr 'key'
 707
 708    assert_equal "incr my_namespace:key 1\r\n",
 709                 @cache.servers.first.socket.written.string
 710
 711    assert_equal 5, value
 712  end
 713
 714  def test_make_cache_key
 715    assert_equal 'my_namespace:key', @cache.make_cache_key('key')
 716    @cache.namespace = nil
 717    assert_equal 'key', @cache.make_cache_key('key')
 718  end
 719
 720  def test_servers
 721    server = FakeServer.new
 722    @cache.servers = []
 723    @cache.servers << server
 724    assert_equal [server], @cache.servers
 725  end
 726
 727  def test_set
 728    server = FakeServer.new
 729    server.socket.data.write "STORED\r\n"
 730    server.socket.data.rewind
 731    @cache.servers = []
 732    @cache.servers << server
 733
 734    @cache.set 'key', 'value'
 735
 736    dumped = Marshal.dump('value')
 737    expected = "set my_namespace:key 0 0 #{dumped.length}\r\n#{dumped}\r\n"
 738#    expected = "set my_namespace:key 0 0 9\r\n\004\b\"\nvalue\r\n"
 739    assert_equal expected, server.socket.written.string
 740  end
 741
 742  def test_set_expiry
 743    server = FakeServer.new
 744    server.socket.data.write "STORED\r\n"
 745    server.socket.data.rewind
 746    @cache.servers = []
 747    @cache.servers << server
 748
 749    @cache.set 'key', 'value', 5
 750
 751    dumped = Marshal.dump('value')
 752    expected = "set my_namespace:key 0 5 #{dumped.length}\r\n#{dumped}\r\n"
 753    assert_equal expected, server.socket.written.string
 754  end
 755
 756  def test_set_raw
 757    server = FakeServer.new
 758    server.socket.data.write "STORED\r\n"
 759    server.socket.data.rewind
 760    @cache.servers = []
 761    @cache.servers << server
 762
 763    @cache.set 'key', 'value', 0, true
 764
 765    expected = "set my_namespace:key 0 0 5\r\nvalue\r\n"
 766    assert_equal expected, server.socket.written.string
 767  end
 768
 769  def test_set_readonly
 770    cache = MemCache.new :readonly => true
 771
 772    e = assert_raise MemCache::MemCacheError do
 773      cache.set 'key', 'value'
 774    end
 775
 776    assert_equal 'Update of readonly cache', e.message
 777  end
 778
 779  def test_check_size_on
 780    cache = MemCache.new :check_size => true
 781
 782    server = FakeServer.new
 783    server.socket.data.write "STORED\r\n"
 784    server.socket.data.rewind
 785
 786    cache.servers = []
 787    cache.servers << server
 788
 789    e = assert_raise MemCache::MemCacheError do
 790      cache.set 'key', 'v' * 1048577
 791    end
 792
 793    assert_equal 'Value too large, memcached can only store 1MB of data per key', e.message
 794  end
 795
 796  def test_check_size_off
 797    cache = MemCache.new :check_size => false
 798
 799    server = FakeServer.new
 800    server.socket.data.write "STORED\r\n"
 801    server.socket.data.rewind
 802
 803    cache.servers = []
 804    cache.servers << server
 805
 806    assert_nothing_raised do
 807      cache.set 'key', 'v' * 1048577
 808    end
 809  end
 810
 811  def test_set_too_big
 812    server = FakeServer.new
 813
 814    # Write two messages to the socket to test failover
 815    server.socket.data.write "SERVER_ERROR\r\nSERVER_ERROR object too large for cache\r\n"
 816    server.socket.data.rewind
 817
 818    @cache.servers = []
 819    @cache.servers << server
 820
 821    e = assert_raise MemCache::MemCacheError do
 822      @cache.set 'key', 'v'
 823    end
 824
 825    assert_match /object too large for cache/, e.message
 826  end
 827
 828  def test_prepend
 829    server = FakeServer.new
 830    server.socket.data.write "STORED\r\n"
 831    server.socket.data.rewind
 832    @cache.servers = []
 833    @cache.servers << server
 834
 835    @cache.prepend 'key', 'value'
 836    
 837    dumped = Marshal.dump('value')
 838
 839    expected = "prepend my_namespace:key 0 0 5\r\nvalue\r\n"
 840    assert_equal expected, server.socket.written.string
 841  end
 842
 843  def test_append
 844    server = FakeServer.new
 845    server.socket.data.write "STORED\r\n"
 846    server.socket.data.rewind
 847    @cache.servers = []
 848    @cache.servers << server
 849
 850    @cache.append 'key', 'value'
 851    
 852    expected = "append my_namespace:key 0 0 5\r\nvalue\r\n"
 853    assert_equal expected, server.socket.written.string
 854  end
 855
 856  def test_replace
 857    server = FakeServer.new
 858    server.socket.data.write "STORED\r\n"
 859    server.socket.data.rewind
 860    @cache.servers = []
 861    @cache.servers << server
 862
 863    @cache.replace 'key', 'value', 150
 864    
 865    dumped = Marshal.dump('value')
 866
 867    expected = "replace my_namespace:key 0 150 #{dumped.length}\r\n#{dumped}\r\n"
 868    assert_equal expected, server.socket.written.string
 869  end
 870
 871  def test_add
 872    server = FakeServer.new
 873    server.socket.data.write "STORED\r\n"
 874    server.socket.data.rewind
 875    @cache.servers = []
 876    @cache.servers << server
 877
 878    @cache.add 'key', 'value'
 879    
 880    dumped = Marshal.dump('value')
 881
 882    expected = "add my_namespace:key 0 0 #{dumped.length}\r\n#{dumped}\r\n"
 883    assert_equal expected, server.socket.written.string
 884  end
 885
 886  def test_add_exists
 887    server = FakeServer.new
 888    server.socket.data.write "NOT_STORED\r\n"
 889    server.socket.data.rewind
 890    @cache.servers = []
 891    @cache.servers << server
 892
 893    @cache.add 'key', 'value'
 894
 895    dumped = Marshal.dump('value')
 896    expected = "add my_namespace:key 0 0 #{dumped.length}\r\n#{dumped}\r\n"
 897    assert_equal expected, server.socket.written.string
 898  end
 899
 900  def test_add_expiry
 901    server = FakeServer.new
 902    server.socket.data.write "STORED\r\n"
 903    server.socket.data.rewind
 904    @cache.servers = []
 905    @cache.servers << server
 906
 907    @cache.add 'key', 'value', 5
 908
 909    dumped = Marshal.dump('value')
 910    expected = "add my_namespace:key 0 5 #{dumped.length}\r\n#{dumped}\r\n"
 911    assert_equal expected, server.socket.written.string
 912  end
 913
 914  def test_add_raw
 915    server = FakeServer.new
 916    server.socket.data.write "STORED\r\n"
 917    server.socket.data.rewind
 918    @cache.servers = []
 919    @cache.servers << server
 920
 921    @cache.add 'key', 'value', 0, true
 922
 923    expected = "add my_namespace:key 0 0 5\r\nvalue\r\n"
 924    assert_equal expected, server.socket.written.string
 925  end
 926
 927  def test_add_raw_int
 928    server = FakeServer.new
 929    server.socket.data.write "STORED\r\n"
 930    server.socket.data.rewind
 931    @cache.servers = []
 932    @cache.servers << server
 933
 934    @cache.add 'key', 12, 0, true
 935
 936    expected = "add my_namespace:key 0 0 2\r\n12\r\n"
 937    assert_equal expected, server.socket.written.string
 938  end
 939
 940  def test_add_readonly
 941    cache = MemCache.new :readonly => true
 942
 943    e = assert_raise MemCache::MemCacheError do
 944      cache.add 'key', 'value'
 945    end
 946
 947    assert_equal 'Update of readonly cache', e.message
 948  end
 949
 950  def test_delete
 951    server = FakeServer.new
 952    @cache.servers = []
 953    @cache.servers << server
 954    
 955    @cache.delete 'key'
 956    
 957    expected = "delete my_namespace:key 0\r\n"
 958    assert_equal expected, server.socket.written.string
 959  end
 960
 961  def test_delete_with_expiry
 962    server = FakeServer.new
 963    @cache.servers = []
 964    @cache.servers << server
 965    
 966    @cache.delete 'key', 300
 967    
 968    expected = "delete my_namespace:key 300\r\n"
 969    assert_equal expected, server.socket.written.string
 970  end
 971
 972  def test_flush_all
 973    @cache.servers = []
 974    3.times { @cache.servers << FakeServer.new }
 975
 976    @cache.flush_all
 977
 978    expected = "flush_all\r\n"
 979    @cache.servers.each do |server|
 980      assert_equal expected, server.socket.written.string
 981    end
 982  end
 983
 984  def test_flush_all_with_delay
 985    @cache.servers = []
 986    3.times { @cache.servers << FakeServer.new }
 987
 988    @cache.flush_all(10)
 989
 990    @cache.servers.each_with_index do |server, idx|
 991      expected = "flush_all #{idx*10}\r\n"
 992      assert_equal expected, server.socket.written.string
 993    end
 994  end
 995
 996  def test_flush_all_failure
 997    socket = FakeSocket.new
 998
 999    # Write two messages to the socket to test failover
1000    socket.data.write "ERROR\r\nERROR\r\n"
1001    socket.data.rewind
1002
1003    server = FakeServer.new socket
1004
1005    @cache.servers = []
1006    @cache.servers << server
1007
1008    assert_raise MemCache::MemCacheError do
1009      @cache.flush_all
1010    end
1011
1012    assert_match /flush_all\r\n/, socket.written.string
1013  end
1014  
1015  def test_flush_all_for_real
1016    requirement(memcached_running?, 'A real memcached server must be running for testing flush_all') do
1017      cache = MemCache.new "localhost:11211", :namespace => "test_flush_all"
1018      k, v = "1234", "test"
1019      assert_nil cache.get(k)
1020      cache.set(k, v)
1021      assert_equal v, cache.get(k)
1022      cache.flush_all
1023      assert_nil cache.get(k)
1024    end
1025  end
1026
1027  def test_stats
1028    socket = FakeSocket.new
1029    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"
1030    socket.data.rewind
1031    server = FakeServer.new socket
1032    def server.host() 'localhost'; end
1033    def server.port() 11211; end
1034
1035    @cache.servers = []
1036    @cache.servers << server
1037
1038    expected = {
1039      'localhost:11211' => {
1040        'pid' => 20188, 'total_items' => 32, 'version' => '1.2.3',
1041        'rusage_user' => 1.0003, 'dummy' => 'ok'
1042      }
1043    }
1044    assert_equal expected, @cache.stats
1045
1046    assert_equal "stats\r\n", socket.written.string
1047  end
1048
1049  def test_basic_threaded_operations_should_work
1050    cache = MemCache.new :multithread => true,
1051                         :namespace => 'my_namespace',
1052                         :readonly => false
1053
1054    server = FakeServer.new
1055    server.socket.data.write "STORED\r\n"
1056    server.socket.data.rewind
1057
1058    cache.servers = []
1059    cache.servers << server
1060
1061    assert cache.multithread
1062
1063    assert_nothing_raised do
1064      cache.set "test", "test value"
1065    end
1066
1067    output = server.socket.written.string
1068    assert_match /set my_namespace:test/, output
1069    assert_match /test value/, output
1070  end
1071
1072  def test_basic_unthreaded_operations_should_work
1073    cache = MemCache.new :multithread => false,
1074                         :namespace => 'my_namespace',
1075                         :readonly => false
1076
1077    server = FakeServer.new
1078    server.socket.data.write "STORED\r\n"
1079    server.socket.data.rewind
1080
1081    cache.servers = []
1082    cache.servers << server
1083
1084    assert !cache.multithread
1085
1086    assert_nothing_raised do
1087      cache.set "test", "test value"
1088    end
1089
1090    output = server.socket.written.string
1091    assert_match /set my_namespace:test/, output
1092    assert_match /test value/, output
1093  end
1094
1095  def util_setup_fake_server
1096    server = FakeServer.new
1097    server.socket.data.write "VALUE my_namespace:key 0 14\r\n"
1098    server.socket.data.write "\004\b\"\0170123456789\r\n"
1099    server.socket.data.write "END\r\n"
1100    server.socket.data.rewind
1101
1102    @cache.servers = []
1103    @cache.servers << server
1104
1105    return server
1106  end
1107
1108  def util_setup_server(memcache, host, responses)
1109    server = MemCache::Server.new memcache, host
1110    server.instance_variable_set :@sock, StringIO.new(responses)
1111
1112    @cache.servers = []
1113    @cache.servers << server
1114
1115    return server
1116  end
1117
1118  def test_crazy_multithreaded_access
1119    requirement(memcached_running?, 'A real memcached server must be running for performance testing') do
1120
1121      # Use a null logger to verify logging doesn't blow up at runtime
1122      cache = MemCache.new(['localhost:11211', '127.0.0.1:11211'], :logger => Logger.new('/dev/null'))
1123      cache.flush_all
1124      workers = []
1125
1126      cache.set('f', 'zzz')
1127      assert_equal "STORED\r\n", (cache.cas('f') do |value|
1128        value << 'z'
1129      end)
1130      assert_equal 'zzzz', cache.get('f')
1131
1132      # Have a bunch of threads perform a bunch of operations at the same time.
1133      # Verify the result of each operation to ensure the request and response
1134      # are not intermingled between threads.
1135      10.times do
1136        workers << Thread.new do
1137          100.times do
1138            cache.set('a', 9)
1139            cache.set('b', 11)
1140            cache.add('c', 10, 0, true)
1141            cache.set('d', 'a', 100, true)
1142            cache.set('e', 'x', 100, true)
1143            cache.set('f', 'zzz')
1144            assert_not_nil(cache.cas('f') do |value|
1145              value << 'z'
1146            end)
1147            cache.append('d', 'b')
1148            cache.prepend('e', 'y')
1149            assert_equal "NOT_STORED\r\n", cache.add('a', 11)
1150            assert_equal({ 'a' => 9, 'b' => 11 }, cache.get_multi(['a', 'b']))
1151            inc = cache.incr('c', 10)
1152            assert_equal 0, inc % 5
1153            assert inc > 14
1154            assert cache.decr('c', 5) > 14
1155            assert_equal 11, cache.get('b')
1156            d = cache.get('d', true)
1157            assert_match /\Aab*\Z/, d
1158            e = cache.get('e', true)
1159            assert_match /\Ay*x\Z/, e
1160          end
1161        end
1162      end
1163
1164      workers.each { |w| w.join }
1165      cache.flush_all
1166    end
1167  end
1168
1169end
1170