PageRenderTime 16ms CodeModel.GetById 8ms app.highlight 156ms RepoModel.GetById 1ms app.codeStats 0ms

/test/test_mem_cache.rb

https://github.com/LeifWarner/memcache-client
Ruby | 1323 lines | 1021 code | 286 blank | 16 comment | 6 complexity | d4b13cf8a9e23fcb31cf3065386f0d12 MD5 | raw file
   1# encoding: utf-8
   2require 'rubygems'
   3require 'logger'
   4require 'stringio'
   5require 'test/unit'
   6$TESTING = true
   7require 'memcache'
   8
   9begin
  10  gem 'flexmock'
  11  require 'flexmock/test_unit'
  12rescue LoadError => e
  13  puts "Some tests require flexmock, please run `gem install flexmock`"
  14end
  15
  16Thread.abort_on_exception = true
  17
  18class MemCache
  19
  20  attr_writer :namespace
  21  attr_writer :autofix_keys
  22
  23end
  24
  25class FakeSocket
  26
  27  attr_reader :written, :data
  28
  29  def initialize
  30    @written = StringIO.new
  31    @data = StringIO.new
  32  end
  33
  34  def write(data)
  35    @written.write data
  36  end
  37
  38  def gets
  39    @data.gets
  40  end
  41
  42  def read(arg)
  43    @data.read arg
  44  end
  45
  46end
  47
  48class Test::Unit::TestCase
  49  def requirement(bool, msg)
  50    if bool
  51      yield
  52    else
  53      puts msg
  54      assert true
  55    end
  56  end
  57
  58  def memcached_running?
  59    TCPSocket.new('localhost', 11211) rescue false
  60  end
  61
  62  def xprofile(name, &block)
  63    a = Time.now
  64    block.call
  65    Time.now - a
  66  end
  67
  68  def profile(name, &block)
  69    require 'ruby-prof'
  70    a = Time.now
  71    result = RubyProf.profile(&block)
  72    time = Time.now - a
  73    printer = RubyProf::GraphHtmlPrinter.new(result)
  74    File.open("#{name}.html", 'w') do |f|
  75      printer.print(f, :min_percent=>1)
  76    end
  77    time
  78  end
  79
  80end
  81
  82class FakeServer
  83
  84  attr_accessor :host, :port, :socket, :weight, :multithread, :status
  85
  86  def initialize(socket = nil)
  87    @closed = false
  88    @host = 'example.com'
  89    @port = 11211
  90    @socket = socket || FakeSocket.new
  91    @weight = 1
  92    @multithread = true
  93    @status = "CONNECTED"
  94  end
  95
  96  def close
  97    # begin
  98    #   raise "Already closed"
  99    # rescue => e
 100    #   puts e.backtrace.join("\n")
 101    # end
 102    @closed = true
 103    @socket = nil
 104    @status = "NOT CONNECTED"
 105  end
 106
 107  def alive?
 108    # puts "I'm #{@closed ? 'dead' : 'alive'}"
 109    !@closed
 110  end
 111
 112end
 113
 114class TestMemCache < Test::Unit::TestCase
 115
 116  def setup
 117    @cache = MemCache.new 'localhost:1', :namespace => 'my_namespace'
 118  end
 119
 120  def test_performance
 121    requirement(memcached_running?, 'A real memcached server must be running for performance testing') do
 122
 123      cache = MemCache.new(['localhost:11211',"127.0.0.1:11211"])
 124      cache.flush_all
 125      cache.add('a', 1, 120)
 126      with = xprofile 'get' do
 127        1000.times do
 128          cache.get('a')
 129        end
 130      end
 131      puts ''
 132      puts "1000 gets with socket timeout: #{with} sec"
 133
 134      cache = MemCache.new(['localhost:11211',"127.0.0.1:11211"], :timeout => nil)
 135      cache.add('a', 1, 120)
 136      without = xprofile 'get' do
 137        1000.times do
 138          cache.get('a')
 139        end
 140      end
 141      puts "1000 gets without socket timeout: #{without} sec"
 142    end
 143  end
 144
 145  def test_consistent_hashing
 146    requirement(self.respond_to?(:flexmock), 'Flexmock is required to run this test') do
 147
 148      flexmock(MemCache::Server).new_instances.should_receive(:alive?).and_return(true)
 149
 150      # Setup a continuum of two servers
 151      @cache.servers = ['mike1', 'mike2', 'mike3']
 152
 153      keys = []
 154      1000.times do |idx|
 155        keys << idx.to_s
 156      end
 157
 158      before_continuum = keys.map {|key| @cache.get_server_for_key(key) }
 159
 160      @cache.servers = ['mike1', 'mike2', 'mike3', 'mike4']
 161
 162      after_continuum = keys.map {|key| @cache.get_server_for_key(key) }
 163
 164      same_count = before_continuum.zip(after_continuum).find_all {|a| a[0].host == a[1].host }.size
 165
 166      # With continuum, we should see about 75% of the keys map to the same server
 167      # With modulo, we would see about 25%.
 168      assert same_count > 700
 169    end
 170  end
 171
 172  def test_get_multi_with_server_failure
 173    @cache = MemCache.new 'localhost:1', :namespace => 'my_namespace', :logger => nil #Logger.new(STDOUT)
 174    s1 = FakeServer.new
 175    s2 = FakeServer.new
 176
 177    # Write two messages to the socket to test failover
 178    s1.socket.data.write "VALUE my_namespace:a 0 14\r\n\004\b\"\0170123456789\r\nEND\r\n"
 179    s1.socket.data.rewind
 180    s2.socket.data.write "bogus response\r\nbogus response\r\n"
 181    s2.socket.data.rewind
 182
 183    @cache.servers = [s1, s2]
 184
 185    assert s1.alive?
 186    assert s2.alive?
 187    # a maps to s1, the rest map to s2
 188    value = @cache.get_multi(['foo', 'bar', 'a', 'b', 'c'])
 189    assert_equal({'a'=>'0123456789'}, value)
 190    assert s1.alive?
 191    assert !s2.alive?
 192  end
 193
 194  def test_cache_get_with_failover
 195    @cache = MemCache.new 'localhost:1', :namespace => 'my_namespace', :logger => nil#Logger.new(STDOUT)
 196    s1 = FakeServer.new
 197    s2 = FakeServer.new
 198
 199    # Write two messages to the socket to test failover
 200    s1.socket.data.write "VALUE foo 0 14\r\n\004\b\"\0170123456789\r\n"
 201    s1.socket.data.rewind
 202    s2.socket.data.write "bogus response\r\nbogus response\r\n"
 203    s2.socket.data.rewind
 204
 205    @cache.instance_variable_set(:@failover, true)
 206    @cache.servers = [s1, s2]
 207
 208    assert s1.alive?
 209    assert s2.alive?
 210    @cache.get('foo')
 211    assert s1.alive?
 212    assert !s2.alive?
 213  end
 214
 215  def test_cache_get_without_failover
 216    s1 = FakeServer.new
 217    s2 = FakeServer.new
 218
 219    s1.socket.data.write "VALUE foo 0 14\r\n\004\b\"\0170123456789\r\n"
 220    s1.socket.data.rewind
 221    s2.socket.data.write "bogus response\r\nbogus response\r\n"
 222    s2.socket.data.rewind
 223
 224    @cache.instance_variable_set(:@failover, false)
 225    @cache.servers = [s1, s2]
 226
 227    assert s1.alive?
 228    assert s2.alive?
 229    e = assert_raise MemCache::MemCacheError do
 230      @cache.get('foo')
 231    end
 232    assert s1.alive?
 233    assert !s2.alive?
 234
 235    assert_equal "No servers available", e.message
 236  end
 237
 238  def test_cache_get
 239    server = util_setup_fake_server
 240
 241    assert_equal "\004\b\"\0170123456789",
 242                 @cache.cache_get(server, 'my_namespace:key')
 243
 244    assert_equal "get my_namespace:key\r\n",
 245                 server.socket.written.string
 246  end
 247
 248  def test_cache_get_EOF
 249    server = util_setup_fake_server
 250    server.socket.data.string = ''
 251
 252    e = assert_raise IndexError do
 253      @cache.cache_get server, 'my_namespace:key'
 254    end
 255
 256    assert_equal "No connection to server (NOT CONNECTED)", e.message
 257  end
 258
 259  def test_cache_get_bad_state
 260    server = FakeServer.new
 261
 262    # Write two messages to the socket to test failover
 263    server.socket.data.write "bogus response\r\nbogus response\r\n"
 264    server.socket.data.rewind
 265
 266    @cache.servers = []
 267    @cache.servers << server
 268
 269    e = assert_raise IndexError do
 270      @cache.cache_get(server, 'my_namespace:key')
 271    end
 272
 273    assert_match(/#{Regexp.quote 'No connection to server (NOT CONNECTED)'}/, e.message)
 274
 275    assert !server.alive?
 276  end
 277
 278  def test_cache_get_miss
 279    socket = FakeSocket.new
 280    socket.data.write "END\r\n"
 281    socket.data.rewind
 282    server = FakeServer.new socket
 283
 284    assert_equal nil, @cache.cache_get(server, 'my_namespace:key')
 285
 286    assert_equal "get my_namespace:key\r\n",
 287                 socket.written.string
 288  end
 289
 290  def test_cache_get_multi
 291    server = util_setup_fake_server
 292    server.socket.data.write "VALUE foo 0 7\r\n"
 293    server.socket.data.write "\004\b\"\bfoo\r\n"
 294    server.socket.data.write "VALUE bar 0 7\r\n"
 295    server.socket.data.write "\004\b\"\bbar\r\n"
 296    server.socket.data.write "END\r\n"
 297    server.socket.data.rewind
 298
 299    result = @cache.cache_get_multi server, 'foo bar baz'
 300
 301    assert_equal 2, result.length
 302    assert_equal "\004\b\"\bfoo", result['foo']
 303    assert_equal "\004\b\"\bbar", result['bar']
 304  end
 305
 306  def test_cache_get_multi_EOF
 307    server = util_setup_fake_server
 308    server.socket.data.string = ''
 309
 310    e = assert_raise IndexError do
 311      @cache.cache_get_multi server, 'my_namespace:key'
 312    end
 313
 314    assert_equal "No connection to server (NOT CONNECTED)", e.message
 315  end
 316
 317  def test_cache_get_multi_bad_state
 318    server = FakeServer.new
 319
 320    # Write two messages to the socket to test failover
 321    server.socket.data.write "bogus response\r\nbogus response\r\n"
 322    server.socket.data.rewind
 323
 324    @cache.servers = []
 325    @cache.servers << server
 326
 327    e = assert_raise IndexError do
 328      @cache.cache_get_multi server, 'my_namespace:key'
 329    end
 330
 331    assert_match(/#{Regexp.quote 'No connection to server (NOT CONNECTED)'}/, e.message)
 332
 333    assert !server.alive?
 334  end
 335
 336  def test_multithread_error
 337    server = FakeServer.new
 338    server.multithread = false
 339
 340    @cache = MemCache.new(['localhost:1'], :multithread => false)
 341
 342    server.socket.data.write "bogus response\r\nbogus response\r\n"
 343    server.socket.data.rewind
 344
 345    @cache.servers = []
 346    @cache.servers << server
 347
 348    assert_nothing_raised do
 349      @cache.set 'a', 1
 350    end
 351
 352    passed = true
 353    Thread.new do
 354      begin
 355        @cache.set 'b', 2
 356        passed = false
 357      rescue MemCache::MemCacheError => me
 358        passed = me.message =~ /multiple threads/
 359      end
 360    end
 361    assert passed
 362  end
 363
 364  def test_initialize
 365    cache = MemCache.new :namespace => 'my_namespace', :readonly => true
 366
 367    assert_equal 'my_namespace', cache.namespace
 368    assert_equal true, cache.readonly?
 369    assert_equal true, cache.servers.empty?
 370  end
 371
 372  def test_initialize_compatible
 373    cache = MemCache.new ['localhost:11211', 'localhost:11212'],
 374            :namespace => 'my_namespace', :readonly => true
 375
 376    assert_equal 'my_namespace', cache.namespace
 377    assert_equal true, cache.readonly?
 378    assert_equal false, cache.servers.empty?
 379  end
 380
 381  def test_initialize_compatible_no_hash
 382    cache = MemCache.new ['localhost:11211', 'localhost:11212']
 383
 384    assert_equal nil, cache.namespace
 385    assert_equal false, cache.readonly?
 386    assert_equal false, cache.servers.empty?
 387  end
 388
 389  def test_initialize_compatible_one_server
 390    cache = MemCache.new 'localhost:11211'
 391
 392    assert_equal nil, cache.namespace
 393    assert_equal false, cache.readonly?
 394    assert_equal false, cache.servers.empty?
 395  end
 396
 397  def test_initialize_compatible_bad_arg
 398    e = assert_raise ArgumentError do
 399      cache = MemCache.new Object.new
 400    end
 401
 402    assert_equal 'first argument must be Array, Hash or String', e.message
 403  end
 404
 405  def test_initialize_multiple_servers
 406    cache = MemCache.new %w[localhost:11211 localhost:11212],
 407                         :namespace => 'my_namespace', :readonly => true
 408
 409    assert_equal 'my_namespace', cache.namespace
 410    assert_equal true, cache.readonly?
 411    assert_equal false, cache.servers.empty?
 412    assert !cache.instance_variable_get(:@continuum).empty?
 413  end
 414
 415  def test_initialize_too_many_args
 416    assert_raises ArgumentError do
 417      MemCache.new 1, 2, 3
 418    end
 419  end
 420
 421  def test_decr
 422    server = FakeServer.new
 423    server.socket.data.write "5\r\n"
 424    server.socket.data.rewind
 425
 426    @cache.servers = []
 427    @cache.servers << server
 428
 429    value = @cache.decr 'key'
 430
 431    assert_equal "decr my_namespace:key 1\r\n",
 432                 @cache.servers.first.socket.written.string
 433
 434    assert_equal 5, value
 435  end
 436
 437  def test_decr_not_found
 438    server = FakeServer.new
 439    server.socket.data.write "NOT_FOUND\r\n"
 440    server.socket.data.rewind
 441
 442    @cache.servers = []
 443    @cache.servers << server
 444
 445    value = @cache.decr 'key'
 446
 447    assert_equal "decr my_namespace:key 1\r\n",
 448                 @cache.servers.first.socket.written.string
 449
 450    assert_equal nil, value
 451  end
 452
 453  def test_decr_space_padding
 454    server = FakeServer.new
 455    server.socket.data.write "5 \r\n"
 456    server.socket.data.rewind
 457
 458    @cache.servers = []
 459    @cache.servers << server
 460
 461    value = @cache.decr 'key'
 462
 463    assert_equal "decr my_namespace:key 1\r\n",
 464                 @cache.servers.first.socket.written.string
 465
 466    assert_equal 5, value
 467  end
 468
 469  def test_get
 470    util_setup_fake_server
 471
 472    value = @cache.get 'key'
 473
 474    assert_equal "get my_namespace:key\r\n",
 475                 @cache.servers.first.socket.written.string
 476
 477    assert_equal '0123456789', value
 478  end
 479
 480  def test_fetch_without_a_block
 481    server = FakeServer.new
 482    server.socket.data.write "END\r\n"
 483    server.socket.data.rewind
 484
 485    @cache.servers = [server]
 486
 487    flexmock(@cache).should_receive(:get).with('key', false).and_return(nil)
 488
 489    value = @cache.fetch('key', 1)
 490    assert_equal nil, value
 491  end
 492
 493  def test_fetch_miss
 494    server = FakeServer.new
 495    server.socket.data.write "END\r\n"
 496    server.socket.data.rewind
 497
 498    @cache.servers = [server]
 499
 500    flexmock(@cache).should_receive(:get).with('key', false).and_return(nil)
 501    flexmock(@cache).should_receive(:add).with('key', 'value', 1, false)
 502
 503    value = @cache.fetch('key', 1) { 'value' }
 504
 505    assert_equal 'value', value
 506  end
 507
 508  def test_fetch_hit
 509    server = FakeServer.new
 510    server.socket.data.write "END\r\n"
 511    server.socket.data.rewind
 512
 513    @cache.servers = [server]
 514
 515    flexmock(@cache).should_receive(:get).with('key', false).and_return('value')
 516    flexmock(@cache).should_receive(:add).never
 517
 518    value = @cache.fetch('key', 1) { raise 'Should not be called.' }
 519
 520    assert_equal 'value', value
 521  end
 522
 523  def test_get_bad_key
 524    util_setup_fake_server
 525    assert_raise ArgumentError do @cache.get 'k y' end
 526
 527    util_setup_fake_server
 528    assert_raise ArgumentError do @cache.get 'k' * 250 end
 529  end
 530
 531  def test_get_cache_get_IOError
 532    socket = Object.new
 533    def socket.write(arg) raise IOError, 'some io error'; end
 534    server = FakeServer.new socket
 535
 536    @cache.servers = []
 537    @cache.servers << server
 538
 539    e = assert_raise MemCache::MemCacheError do
 540      @cache.get 'my_namespace:key'
 541    end
 542
 543    assert_equal 'some io error', e.message
 544  end
 545
 546  def test_get_cache_get_SystemCallError
 547    socket = Object.new
 548    def socket.write(arg) raise SystemCallError, 'some syscall error'; end
 549    server = FakeServer.new socket
 550
 551    @cache.servers = []
 552    @cache.servers << server
 553
 554    e = assert_raise MemCache::MemCacheError do
 555      @cache.get 'my_namespace:key'
 556    end
 557
 558    assert_equal 'unknown error - some syscall error', e.message.downcase
 559  end
 560
 561  def test_get_no_connection
 562    @cache.servers = 'localhost:1'
 563    e = assert_raise MemCache::MemCacheError do
 564      @cache.get 'key'
 565    end
 566
 567    assert_match(/^No connection to server/, e.message)
 568  end
 569
 570  def test_get_no_servers
 571    @cache.servers = []
 572    e = assert_raise MemCache::MemCacheError do
 573      @cache.get 'key'
 574    end
 575
 576    assert_equal 'No active servers', e.message
 577  end
 578
 579  def test_get_multi
 580    server = FakeServer.new
 581    server.socket.data.write "VALUE my_namespace:key 0 14\r\n"
 582    server.socket.data.write "\004\b\"\0170123456789\r\n"
 583    server.socket.data.write "VALUE my_namespace:keyb 0 14\r\n"
 584    server.socket.data.write "\004\b\"\0179876543210\r\n"
 585    server.socket.data.write "END\r\n"
 586    server.socket.data.rewind
 587
 588    @cache.servers = []
 589    @cache.servers << server
 590
 591    values = @cache.get_multi 'key', 'keyb'
 592
 593    assert_equal "get my_namespace:key my_namespace:keyb\r\n",
 594                 server.socket.written.string
 595
 596    expected = { 'key' => '0123456789', 'keyb' => '9876543210' }
 597
 598    assert_equal expected.sort, values.sort
 599  end
 600
 601  def test_get_multi_raw
 602    server = FakeServer.new
 603    server.socket.data.write "VALUE my_namespace:key 0 10\r\n"
 604    server.socket.data.write "0123456789\r\n"
 605    server.socket.data.write "VALUE my_namespace:keyb 0 10\r\n"
 606    server.socket.data.write "9876543210\r\n"
 607    server.socket.data.write "END\r\n"
 608    server.socket.data.rewind
 609
 610    @cache.servers = []
 611    @cache.servers << server
 612
 613    values = @cache.get_multi 'key', 'keyb', :raw => true
 614
 615    assert_equal "get my_namespace:key my_namespace:keyb\r\n",
 616                 server.socket.written.string
 617
 618    expected = { 'key' => '0123456789', 'keyb' => '9876543210' }
 619
 620    assert_equal expected.sort, values.sort
 621  end
 622
 623  def test_get_raw
 624    server = FakeServer.new
 625    server.socket.data.write "VALUE my_namespace:key 0 10\r\n"
 626    server.socket.data.write "0123456789\r\n"
 627    server.socket.data.write "END\r\n"
 628    server.socket.data.rewind
 629
 630    @cache.servers = []
 631    @cache.servers << server
 632
 633
 634    value = @cache.get 'key', true
 635
 636    assert_equal "get my_namespace:key\r\n",
 637                 @cache.servers.first.socket.written.string
 638
 639    assert_equal '0123456789', value
 640  end
 641
 642  def test_get_server_for_key
 643    server = @cache.get_server_for_key 'key'
 644    assert_equal 'localhost', server.host
 645    assert_equal 1, server.port
 646  end
 647
 648  def test_get_server_for_key_multiple
 649    s1 = util_setup_server @cache, 'one.example.com', ''
 650    s2 = util_setup_server @cache, 'two.example.com', ''
 651    @cache.servers = [s1, s2]
 652
 653    server = @cache.get_server_for_key 'keya'
 654    assert_equal 'two.example.com', server.host
 655    server = @cache.get_server_for_key 'keyb'
 656    assert_equal 'two.example.com', server.host
 657    server = @cache.get_server_for_key 'keyc'
 658    assert_equal 'two.example.com', server.host
 659    server = @cache.get_server_for_key 'keyd'
 660    assert_equal 'one.example.com', server.host
 661  end
 662
 663  def test_get_server_for_key_no_servers
 664    @cache.servers = []
 665
 666    e = assert_raise MemCache::MemCacheError do
 667      @cache.get_server_for_key 'key'
 668    end
 669
 670    assert_equal 'No servers available', e.message
 671  end
 672
 673  def test_get_server_for_key_spaces
 674    e = assert_raise ArgumentError do
 675      @cache.get_server_for_key 'space key'
 676    end
 677    assert_equal 'illegal character in key "space key"', e.message
 678  end
 679
 680  def test_get_server_for_blank_key
 681    e = assert_raise ArgumentError do
 682      @cache.get_server_for_key ''
 683    end
 684    assert_equal 'key cannot be blank', e.message
 685  end
 686
 687  def test_get_server_for_key_length
 688    @cache.get_server_for_key 'x' * 250
 689    long_key = 'x' * 251
 690    e = assert_raise ArgumentError do
 691      @cache.get_server_for_key long_key
 692    end
 693    assert_equal "key too long #{long_key.inspect}", e.message
 694  end
 695
 696  def test_incr
 697    server = FakeServer.new
 698    server.socket.data.write "5\r\n"
 699    server.socket.data.rewind
 700
 701    @cache.servers = []
 702    @cache.servers << server
 703
 704    value = @cache.incr 'key'
 705
 706    assert_equal "incr my_namespace:key 1\r\n",
 707                 @cache.servers.first.socket.written.string
 708
 709    assert_equal 5, value
 710  end
 711
 712  def test_incr_not_found
 713    server = FakeServer.new
 714    server.socket.data.write "NOT_FOUND\r\n"
 715    server.socket.data.rewind
 716
 717    @cache.servers = []
 718    @cache.servers << server
 719
 720    value = @cache.incr 'key'
 721
 722    assert_equal "incr my_namespace:key 1\r\n",
 723                 @cache.servers.first.socket.written.string
 724
 725    assert_equal nil, value
 726  end
 727
 728  def test_incr_space_padding
 729    server = FakeServer.new
 730    server.socket.data.write "5 \r\n"
 731    server.socket.data.rewind
 732
 733    @cache.servers = []
 734    @cache.servers << server
 735
 736    value = @cache.incr 'key'
 737
 738    assert_equal "incr my_namespace:key 1\r\n",
 739                 @cache.servers.first.socket.written.string
 740
 741    assert_equal 5, value
 742  end
 743
 744  def test_make_cache_key
 745    assert_equal 'my_namespace:key', @cache.make_cache_key('key')
 746    @cache.namespace = nil
 747    assert_equal 'key', @cache.make_cache_key('key')
 748  end
 749
 750  def test_make_cache_key_without_autofix
 751    @cache.autofix_keys = false
 752
 753    key = "keys with more than two hundred and fifty characters can cause problems, because they get truncated and start colliding with each other. It's not a common occurrence, but when it happens is very hard to debug. the autofix option takes care of that for you"
 754    hash = Digest::SHA1.hexdigest(key)
 755    @cache.namespace = nil
 756    assert_equal key, @cache.make_cache_key(key)
 757  end
 758
 759  def test_make_cache_key_with_autofix
 760    @cache.autofix_keys = true
 761
 762    @cache.namespace = "my_namespace"
 763    assert_equal 'my_namespace:key', @cache.make_cache_key('key')
 764    @cache.namespace = nil
 765    assert_equal 'key', @cache.make_cache_key('key')
 766
 767    key = "keys with more than two hundred and fifty characters can cause problems, because they get truncated and start colliding with each other. It's not a common occurrence, but when it happens is very hard to debug. the autofix option takes care of that for you"
 768    hash = Digest::SHA1.hexdigest(key)
 769    @cache.namespace = "my_namespace"
 770    assert_equal "my_namespace:#{hash}-autofixed", @cache.make_cache_key(key)
 771    @cache.namespace = nil
 772    assert_equal "#{hash}-autofixed", @cache.make_cache_key(key)
 773
 774    key = "a short key with spaces"
 775    hash = Digest::SHA1.hexdigest(key)
 776    @cache.namespace = "my_namespace"
 777    assert_equal "my_namespace:#{hash}-autofixed", @cache.make_cache_key(key)
 778    @cache.namespace = nil
 779    assert_equal "#{hash}-autofixed", @cache.make_cache_key(key)
 780
 781    # namespace + separator + key > 250
 782    key = 'k' * 240
 783    hash = Digest::SHA1.hexdigest(key)
 784    @cache.namespace = 'n' * 10
 785    assert_equal "#{@cache.namespace}:#{hash}-autofixed", @cache.make_cache_key(key)
 786  end
 787
 788  def test_servers
 789    server = FakeServer.new
 790    @cache.servers = []
 791    @cache.servers << server
 792    assert_equal [server], @cache.servers
 793  end
 794
 795  def test_set
 796    server = FakeServer.new
 797    server.socket.data.write "STORED\r\n"
 798    server.socket.data.rewind
 799    @cache.servers = []
 800    @cache.servers << server
 801
 802    @cache.set 'key', 'value'
 803
 804    dumped = Marshal.dump('value')
 805    expected = "set my_namespace:key 0 0 #{dumped.length}\r\n#{dumped}\r\n"
 806    assert_equal expected, server.socket.written.string
 807  end
 808
 809  def test_set_utf8
 810    server = FakeServer.new
 811    server.socket.data.write "STORED\r\n"
 812    server.socket.data.rewind
 813    @cache.servers = []
 814    @cache.servers << server
 815
 816    @cache.set 'key', 'value 启'
 817
 818    dumped = Marshal.dump('value 启')
 819
 820    expected = "set my_namespace:key 0 0 #{dumped.bytesize}\r\n"
 821    expected << dumped
 822    expected << "\r\n"
 823    assert_equal expected, server.socket.written.string
 824  end
 825
 826  def test_set_expiry
 827    server = FakeServer.new
 828    server.socket.data.write "STORED\r\n"
 829    server.socket.data.rewind
 830    @cache.servers = []
 831    @cache.servers << server
 832
 833    @cache.set 'key', 'value', 5
 834
 835    dumped = Marshal.dump('value')
 836    expected = "set my_namespace:key 0 5 #{dumped.length}\r\n#{dumped}\r\n"
 837    assert_equal expected, server.socket.written.string
 838  end
 839
 840  def test_set_raw
 841    server = FakeServer.new
 842    server.socket.data.write "STORED\r\n"
 843    server.socket.data.rewind
 844    @cache.servers = []
 845    @cache.servers << server
 846
 847    @cache.set 'key', 'value', 0, true
 848
 849    expected = "set my_namespace:key 0 0 5\r\nvalue\r\n"
 850    assert_equal expected, server.socket.written.string
 851  end
 852
 853  def test_set_utf8_raw
 854    server = FakeServer.new
 855    server.socket.data.write "STORED\r\n"
 856    server.socket.data.rewind
 857    @cache.servers = []
 858    @cache.servers << server
 859
 860    val = 'value 启'
 861    @cache.set 'key', val, 0, true
 862
 863    expected = "set my_namespace:key 0 0 #{val.bytesize}\r\n#{val}\r\n"
 864    assert_equal expected, server.socket.written.string
 865  end
 866
 867  def test_set_readonly
 868    cache = MemCache.new :readonly => true
 869
 870    e = assert_raise MemCache::MemCacheError do
 871      cache.set 'key', 'value'
 872    end
 873
 874    assert_equal 'Update of readonly cache', e.message
 875  end
 876
 877  def test_check_size_on
 878    cache = MemCache.new :check_size => true
 879
 880    server = FakeServer.new
 881    server.socket.data.write "STORED\r\n"
 882    server.socket.data.rewind
 883
 884    cache.servers = []
 885    cache.servers << server
 886
 887    e = assert_raise MemCache::MemCacheError do
 888      cache.set 'key', 'v' * 1048577
 889    end
 890
 891    assert_equal 'Value too large, memcached can only store 1MB of data per key', e.message
 892  end
 893
 894  def test_check_size_off
 895    cache = MemCache.new :check_size => false
 896
 897    server = FakeServer.new
 898    server.socket.data.write "STORED\r\n"
 899    server.socket.data.rewind
 900
 901    cache.servers = []
 902    cache.servers << server
 903
 904    assert_nothing_raised do
 905      cache.set 'key', 'v' * 1048577
 906    end
 907  end
 908
 909  def test_set_too_big
 910    server = FakeServer.new
 911
 912    # Write two messages to the socket to test failover
 913    server.socket.data.write "SERVER_ERROR\r\nSERVER_ERROR object too large for cache\r\n"
 914    server.socket.data.rewind
 915
 916    @cache.servers = []
 917    @cache.servers << server
 918
 919    e = assert_raise MemCache::MemCacheError do
 920      @cache.set 'key', 'v'
 921    end
 922
 923    assert_match(/object too large for cache/, e.message)
 924  end
 925
 926  def test_prepend
 927    server = FakeServer.new
 928    server.socket.data.write "STORED\r\n"
 929    server.socket.data.rewind
 930    @cache.servers = []
 931    @cache.servers << server
 932
 933    @cache.prepend 'key', 'value'
 934
 935    dumped = Marshal.dump('value')
 936
 937    expected = "prepend my_namespace:key 0 0 5\r\nvalue\r\n"
 938    assert_equal expected, server.socket.written.string
 939  end
 940
 941  def test_append
 942    server = FakeServer.new
 943    server.socket.data.write "STORED\r\n"
 944    server.socket.data.rewind
 945    @cache.servers = []
 946    @cache.servers << server
 947
 948    @cache.append 'key', 'value'
 949
 950    expected = "append my_namespace:key 0 0 5\r\nvalue\r\n"
 951    assert_equal expected, server.socket.written.string
 952  end
 953
 954  def test_replace
 955    server = FakeServer.new
 956    server.socket.data.write "STORED\r\n"
 957    server.socket.data.rewind
 958    @cache.servers = []
 959    @cache.servers << server
 960
 961    @cache.replace 'key', 'value', 150
 962
 963    dumped = Marshal.dump('value')
 964
 965    expected = "replace my_namespace:key 0 150 #{dumped.length}\r\n#{dumped}\r\n"
 966    assert_equal expected, server.socket.written.string
 967  end
 968
 969  def test_add
 970    server = FakeServer.new
 971    server.socket.data.write "STORED\r\n"
 972    server.socket.data.rewind
 973    @cache.servers = []
 974    @cache.servers << server
 975
 976    @cache.add 'key', 'value'
 977
 978    dumped = Marshal.dump('value')
 979
 980    expected = "add my_namespace:key 0 0 #{dumped.length}\r\n#{dumped}\r\n"
 981    assert_equal expected, server.socket.written.string
 982  end
 983
 984  def test_add_exists
 985    server = FakeServer.new
 986    server.socket.data.write "NOT_STORED\r\n"
 987    server.socket.data.rewind
 988    @cache.servers = []
 989    @cache.servers << server
 990
 991    @cache.add 'key', 'value'
 992
 993    dumped = Marshal.dump('value')
 994    expected = "add my_namespace:key 0 0 #{dumped.length}\r\n#{dumped}\r\n"
 995    assert_equal expected, server.socket.written.string
 996  end
 997
 998  def test_add_expiry
 999    server = FakeServer.new
1000    server.socket.data.write "STORED\r\n"
1001    server.socket.data.rewind
1002    @cache.servers = []
1003    @cache.servers << server
1004
1005    @cache.add 'key', 'value', 5
1006
1007    dumped = Marshal.dump('value')
1008    expected = "add my_namespace:key 0 5 #{dumped.length}\r\n#{dumped}\r\n"
1009    assert_equal expected, server.socket.written.string
1010  end
1011
1012  def test_add_raw
1013    server = FakeServer.new
1014    server.socket.data.write "STORED\r\n"
1015    server.socket.data.rewind
1016    @cache.servers = []
1017    @cache.servers << server
1018
1019    @cache.add 'key', 'value', 0, true
1020
1021    expected = "add my_namespace:key 0 0 5\r\nvalue\r\n"
1022    assert_equal expected, server.socket.written.string
1023  end
1024
1025  def test_add_raw_int
1026    server = FakeServer.new
1027    server.socket.data.write "STORED\r\n"
1028    server.socket.data.rewind
1029    @cache.servers = []
1030    @cache.servers << server
1031
1032    @cache.add 'key', 12, 0, true
1033
1034    expected = "add my_namespace:key 0 0 2\r\n12\r\n"
1035    assert_equal expected, server.socket.written.string
1036  end
1037
1038  def test_add_readonly
1039    cache = MemCache.new :readonly => true
1040
1041    e = assert_raise MemCache::MemCacheError do
1042      cache.add 'key', 'value'
1043    end
1044
1045    assert_equal 'Update of readonly cache', e.message
1046  end
1047
1048  def test_delete
1049    server = FakeServer.new
1050    @cache.servers = []
1051    @cache.servers << server
1052
1053    @cache.delete 'key'
1054
1055    expected = "delete my_namespace:key\r\n"
1056    assert_equal expected, server.socket.written.string
1057  end
1058
1059  def test_delete_with_expiry
1060    server = FakeServer.new
1061    @cache.servers = []
1062    @cache.servers << server
1063
1064    @cache.delete 'key', 300
1065
1066    expected = "delete my_namespace:key\r\n"
1067    assert_equal expected, server.socket.written.string
1068  end
1069
1070  def test_flush_all
1071    @cache.servers = []
1072    3.times { @cache.servers << FakeServer.new }
1073
1074    @cache.flush_all
1075
1076    expected = "flush_all\r\n"
1077    @cache.servers.each do |server|
1078      assert_equal expected, server.socket.written.string
1079    end
1080  end
1081
1082  def test_flush_all_with_delay
1083    @cache.servers = []
1084    3.times { @cache.servers << FakeServer.new }
1085
1086    @cache.flush_all(10)
1087
1088    @cache.servers.each_with_index do |server, idx|
1089      expected = "flush_all #{idx*10}\r\n"
1090      assert_equal expected, server.socket.written.string
1091    end
1092  end
1093
1094  def test_flush_all_failure
1095    socket = FakeSocket.new
1096
1097    # Write two messages to the socket to test failover
1098    socket.data.write "ERROR\r\nERROR\r\n"
1099    socket.data.rewind
1100
1101    server = FakeServer.new socket
1102
1103    @cache.servers = []
1104    @cache.servers << server
1105
1106    assert_raise MemCache::MemCacheError do
1107      @cache.flush_all
1108    end
1109
1110    assert_match(/flush_all\r\n/, socket.written.string)
1111  end
1112
1113  def test_flush_all_for_real
1114    requirement(memcached_running?, 'A real memcached server must be running for testing flush_all') do
1115      cache = MemCache.new "localhost:11211", :namespace => "test_flush_all"
1116      k, v = "1234", "test"
1117      assert_nil cache.get(k)
1118      cache.set(k, v)
1119      assert_equal v, cache.get(k)
1120      cache.flush_all
1121      assert_nil cache.get(k)
1122    end
1123  end
1124
1125  def test_stats
1126    socket = FakeSocket.new
1127    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"
1128    socket.data.rewind
1129    server = FakeServer.new socket
1130    def server.host() 'localhost'; end
1131    def server.port() 11211; end
1132
1133    @cache.servers = []
1134    @cache.servers << server
1135
1136    expected = {
1137      'localhost:11211' => {
1138        'pid' => 20188, 'total_items' => 32, 'version' => '1.2.3',
1139        'rusage_user' => 1.0003, 'dummy' => 'ok'
1140      }
1141    }
1142    assert_equal expected, @cache.stats
1143
1144    assert_equal "stats\r\n", socket.written.string
1145  end
1146
1147  def test_basic_threaded_operations_should_work
1148    cache = MemCache.new :multithread => true,
1149                         :namespace => 'my_namespace',
1150                         :readonly => false
1151
1152    server = FakeServer.new
1153    server.socket.data.write "STORED\r\n"
1154    server.socket.data.rewind
1155
1156    cache.servers = []
1157    cache.servers << server
1158
1159    assert cache.multithread
1160
1161    assert_nothing_raised do
1162      cache.set "test", "test value"
1163    end
1164
1165    output = server.socket.written.string
1166    assert_match(/set my_namespace:test/, output)
1167    assert_match(/test value/, output)
1168  end
1169
1170  def test_namespace_separator
1171    cache = MemCache.new :namespace => 'ns', :namespace_separator => ''
1172
1173    server = FakeServer.new
1174    server.socket.data.write "STORED\r\n"
1175    server.socket.data.rewind
1176
1177    cache.servers = []
1178    cache.servers << server
1179
1180    assert_nothing_raised do
1181      cache.set "test", "test value"
1182    end
1183
1184    output = server.socket.written.string
1185    assert_match(/set nstest/, output)
1186    assert_match(/test value/, output)
1187  end
1188
1189  def test_basic_unthreaded_operations_should_work
1190    cache = MemCache.new :multithread => false,
1191                         :namespace => 'my_namespace',
1192                         :readonly => false
1193
1194    server = FakeServer.new
1195    server.socket.data.write "STORED\r\n"
1196    server.socket.data.rewind
1197
1198    cache.servers = []
1199    cache.servers << server
1200
1201    assert !cache.multithread
1202
1203    assert_nothing_raised do
1204      cache.set "test", "test value"
1205    end
1206
1207    output = server.socket.written.string
1208    assert_match(/set my_namespace:test/, output)
1209    assert_match(/test value/, output)
1210  end
1211
1212  def util_setup_fake_server
1213    server = FakeServer.new
1214    server.socket.data.write "VALUE my_namespace:key 0 14\r\n"
1215    server.socket.data.write "\004\b\"\0170123456789\r\n"
1216    server.socket.data.write "END\r\n"
1217    server.socket.data.rewind
1218
1219    @cache.servers = []
1220    @cache.servers << server
1221
1222    return server
1223  end
1224
1225  def util_setup_server(memcache, host, responses)
1226    server = MemCache::Server.new memcache, host
1227    server.instance_variable_set :@sock, StringIO.new(responses)
1228
1229    @cache.servers = []
1230    @cache.servers << server
1231
1232    return server
1233  end
1234
1235  def test_crazy_multithreaded_access
1236    requirement(memcached_running?, 'A real memcached server must be running for performance testing') do
1237
1238      # Use a null logger to verify logging doesn't blow up at runtime
1239      cache = MemCache.new(['localhost:11211', '127.0.0.1:11211'], :logger => Logger.new('/dev/null'))
1240      cache.flush_all
1241      assert_equal true, cache.multithread
1242      workers = []
1243
1244      cache.set('f', 'zzz')
1245      assert_equal "STORED\r\n", (cache.cas('f') do |value|
1246        value << 'z'
1247      end)
1248      assert_equal 'zzzz', cache.get('f')
1249
1250      # Have a bunch of threads perform a bunch of operations at the same time.
1251      # Verify the result of each operation to ensure the request and response
1252      # are not intermingled between threads.
1253      10.times do
1254        workers << Thread.new do
1255          100.times do
1256            cache.set('a', 9)
1257            cache.set('b', 11)
1258            cache.add('c', 10, 0, true)
1259            cache.set('d', 'a', 100, true)
1260            cache.set('e', 'x', 100, true)
1261            cache.set('f', 'zzz')
1262            cache.set('g', '1ብbሲg')
1263            cache.set('Ẽ뷽cc벼g', 'batman')
1264            assert_not_nil(cache.cas('f') do |value|
1265              value << 'z'
1266            end)
1267            cache.append('d', 'b')
1268            cache.prepend('e', 'y')
1269            assert_equal "NOT_STORED\r\n", cache.add('a', 11)
1270            assert_equal({ 'a' => 9, 'b' => 11 }, cache.get_multi(['a', 'b']))
1271            inc = cache.incr('c', 10)
1272            assert_equal 0, inc % 5
1273            assert inc > 14
1274            assert cache.decr('c', 5) > 14
1275            assert_equal 11, cache.get('b')
1276            assert_equal '1ብbሲg', cache.get('g')
1277            assert_equal 'batman', cache.get('Ẽ뷽cc벼g')
1278            d = cache.get('d', true)
1279            assert_match(/\Aab*\Z/, d)
1280            e = cache.get('e', true)
1281            assert_match(/\Ay*x\Z/, e)
1282          end
1283        end
1284      end
1285
1286      workers.each { |w| w.join }
1287      cache.flush_all
1288    end
1289  end
1290
1291  def test_large_value
1292    requirement(memcached_running?, 'A memcached server must be running for live testing') do
1293      m = MemCache.new 'localhost'
1294      value = '1234567890'*500000
1295      assert_raises MemCache::MemCacheError do
1296        m.set('large_value', value)
1297      end
1298      value = '1234567890'*50000
1299      m.set('large_value', value)
1300      assert_equal value, m.get('large_value')
1301    end
1302  end
1303
1304  def test_custom_encoding
1305    requirement(memcached_running?, 'A memcached server must be running for live testing') do
1306      key = "£"
1307      array = [180,250,184,213]
1308      value = array.pack('c*')
1309      value.force_encoding('big5')
1310      assert_equal array, value.bytes.to_a
1311
1312      m = MemCache.new 'localhost'
1313      m.set(key, value)
1314      result = m.get(key)
1315      assert_equal(value, result)
1316      assert_equal(value.encoding.name, result.encoding.name)
1317      # m.set(key, value, true)
1318      # assert_equal(value, m.get(key))
1319    end
1320  end
1321
1322end
1323