<commit_before>require 'spec_helper'

describe Redis::Lock do
  let(:key) {"key"}
  let(:lock) {Redis::Lock.new(@redis, key)}

  before(:all) do
    @redis = Redis.new
  end

  before(:each) do
    @redis.flushdb
  end

  after(:each) do
    @redis.flushdb
  end

  after(:all) do
    @redis.quit
  end

  context "#lock" do
    it "should set an appropriate key in redis" do
      lock.lock
      @redis.get("lock:#{key}").should_not be_nil
    end

    context "when a block is provided" do
      it "locks before yielding and releases after" do
        lock.lock do |l|
          l.should == lock
          @redis.get("lock:#{key}").should_not be_nil
        end
        @redis.get("lock:#{key}").should be_nil
      end
    end

    context "when acquire_timeout is provided" do
      it "times out after the given timeout with an appropriate error" do
        other_lock = Redis::Lock.new(@redis, key)
        time = Time.now
        lock.lock
        begin
          other_lock.lock(2)
          fail()
        rescue => e
          (Time.now - time).should be_within(0.1).of(2.0)
          e.should be_a(Redis::Lock::AcquireLockTimeOut)
        end
      end
    end

    context "when initialized with auto_release_time" do
      it "sets the redis key with an appropriate expiration" do
        other_lock = Redis::Lock.new(@redis, key, :auto_release_time => 7)
        @redis.should_receive(:set).with("lock:#{key}", an_instance_of(String), :nx => true, :ex => 7).and_return(true)
        other_lock.lock
      end
    end

    context "when initialized with base_sleep" do
      it "retries with exponential back off starting at base_sleep millis" do
        other_lock = Redis::Lock.new(@redis, key, :base_sleep => 25)
        other_lock.should_receive(:sleep).with(0.025).ordered
        other_lock.should_receive(:sleep).with(0.05).ordered
        other_lock.should_receive(:sleep).with(0.1).ordered
        other_lock.should_receive(:sleep).with(0.2).ordered
        other_lock.should_receive(:sleep) do |sleep_time|
          sleep_time.should == 0.4
          lock.unlock
        end.ordered
        lock.lock
        other_lock.lock
      end
    end
  end

  context "#unlock" do
    it "should delete an appropriate key from redis" do
      lock.lock
      lock.unlock
      @redis.get("lock:#{key}").should be_nil
    end

    it "should not delete a lock held by another instance" do
      other_lock = Redis::Lock.new(@redis, key, :auto_release_time => 1)
      other_lock.lock
      sleep(1.1)
      lock.lock
      other_lock.unlock
      @redis.get("lock:#{key}").should_not be_nil
    end

    context "when the instance has not been locked" do
      it "is a no op" do
        @redis.should_not_receive(:eval)
        lock.unlock<commit_msg>test fix<commit_after>require 'spec_helper'

describe Redis::Lock do
  let(:key) {"key"}
  let(:lock) {Redis::Lock.new(@redis, key)}

  before(:all) do
    @redis = Redis.new
  end

  before(:each) do
    @redis.flushdb
  end

  after(:each) do
    @redis.flushdb
  end

  after(:all) do
    @redis.quit
  end

  context "#lock" do
    it "should set an appropriate key in redis" do
      lock.lock
      @redis.get("lock:#{key}").should_not be_nil
    end

    context "when a block is provided" do
      it "locks before yielding and releases after" do
        lock.lock do |l|
          l.should == lock
          @redis.get("lock:#{key}").should_not be_nil
        end
        @redis.get("lock:#{key}").should be_nil
      end
    end

    context "when acquire_timeout is provided" do
      it "times out after the given timeout with an appropriate error" do
        other_lock = Redis::Lock.new(@redis, key)
        time = Time.now
        lock.lock
        begin
          other_lock.lock(2)
          fail()
        rescue => e
          (Time.now - time).should be_within(0.1).of(2.0)
          e.should be_a(Redis::Lock::AcquireLockTimeOut)
        end
      end
    end

    context "when initialized with auto_release_time" do
      it "sets the redis key with an appropriate expiration" do
        other_lock = Redis::Lock.new(@redis, key, :auto_release_time => 7)
        @redis.should_receive(:set).with("lock:#{key}", an_instance_of(String), :nx => true, :ex => 7).and_return(true)
        other_lock.lock
      end
    end

    context "when initialized with base_sleep" do
      it "retries with exponential back off starting at base_sleep millis" do
        other_lock = Redis::Lock.new(@redis, key, :base_sleep => 25)
        other_lock.should_receive(:sleep).with(0.025).ordered
        other_lock.should_receive(:sleep).with(0.05).ordered
        other_lock.should_receive(:sleep).with(0.1).ordered
        other_lock.should_receive(:sleep).with(0.2).ordered
        other_lock.should_receive(:sleep) do |sleep_time|
          sleep_time.should == 0.4
          lock.unlock
        end.ordered
        lock.lock
        other_lock.lock
      end
    end
  end

  context "#unlock" do
    it "should delete an appropriate key from redis" do
      lock.lock
      lock.unlock
      @redis.get("lock:#{key}").should be_nil
    end

    it "should not delete a lock held by another instance" do
      other_lock = Redis::Lock.new(@redis, key, :auto_release_time => 1)
      other_lock.lock
      sleep(1.1)
      lock.lock
      other_lock.unlock(true)
      @redis.get("lock:#{key}").should_not be_nil
    end

    context "when the instance has not been locked" do
      it "is a no op" do
        @redis.should_not_receive(:eval)
        lock.unlock
{'fraction_non_alphanumeric': 0.11121141000180539, 'fraction_numerical': 0.009026900162484202, 'mean_word_length': 2.4071340713407134, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4191894', 'n_tokens_mistral': 1837, 'n_tokens_neox': 1768, 'n_words': 464}