# frozen_string_literal: true

describe DDMetrics::Stopwatch do
  subject(:stopwatch) { described_class.new }

  after { Timecop.return }

  it 'is zero by default' do
    expect(stopwatch.duration).to eq(0.0)
  end

  it 'records correct duration after start+stop' do
    stopwatch.start
    sleep 0.1
    stopwatch.stop

    expect(stopwatch.duration).to be_within(0.01).of(0.1)
  end

  it 'records correct duration after start+stop+start+stop' do
    stopwatch.start
    sleep 0.1
    stopwatch.stop
    sleep 0.1
    stopwatch.start
    sleep 0.1
    stopwatch.stop

    expect(stopwatch.duration).to be_within(0.02).of(0.2)
  end

  it 'records correct duration with single #run call' do
    stopwatch.run do
      sleep 0.1
    end

    expect(stopwatch.duration).to be_within(0.01).of(0.1)
  end

  it 'records correct duration with multiple #run calls' do
    stopwatch.run do
      sleep 0.1
    end
    stopwatch.run do
      sleep 0.2
    end

    expect(stopwatch.duration).to be_within(0.03).of(0.3)
  end

  it 'returns the right value from a #run call' do
    result = stopwatch.run do
      sleep 0.1
      'donkey'
    end

    expect(result).to eq('donkey')
  end

  it 'errors when stopping when not started' do
    expect { stopwatch.stop }
      .to raise_error(
        DDMetrics::Stopwatch::NotRunningError,
        'Cannot stop, because stopwatch is not running',
      )
  end

  it 'errors when starting when already started' do
    stopwatch.start
    expect { stopwatch.start }
      .to raise_error(
        DDMetrics::Stopwatch::AlreadyRunningError,
        'Cannot start, because stopwatch is already running',
      )
  end

  it 'reports running status' do
    expect(stopwatch).not_to be_running
    expect(stopwatch).to be_stopped

    stopwatch.start

    expect(stopwatch).to be_running
    expect(stopwatch).not_to be_stopped

    stopwatch.stop

    expect(stopwatch).not_to be_running
    expect(stopwatch).to be_stopped
  end

  it 'errors when getting duration while running' do
    stopwatch.start
    expect { stopwatch.duration }
      .to raise_error(
        DDMetrics::Stopwatch::StillRunningError,
        'Cannot get duration, because stopwatch is still running',
      )
  end
end
