Skip to content

Commit

Permalink
Before/After hook implementation.
Browse files Browse the repository at this point in the history
  • Loading branch information
kouno committed Jul 27, 2012
1 parent 7a89d50 commit 65282f5
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 9 deletions.
22 changes: 20 additions & 2 deletions lib/big-o/complexity_base.rb
Expand Up @@ -34,7 +34,9 @@ def initialize(options = {})
:timeout => 10,
:approximation => 0.05,
:error_pct => 0.05,
:minimum_result_set_size => 3 }
:minimum_result_set_size => 3,
:before_hook => proc {},
:after_hook => proc {} }

@options.merge!(options)
end
Expand Down Expand Up @@ -63,7 +65,7 @@ def run_simulation
begin
Timeout::timeout(@options[:timeout]) do
@options[:range].each do |n|
next if (indicator = measure(n, &@options[:fn])) == 0
next if (indicator = measurement(n)) == 0
real_complexity[n] = indicator
expected_complexity[n] = @options[:level].call(n)
end
Expand All @@ -78,6 +80,22 @@ def run_simulation
[real_complexity, expected_complexity]
end

# Measurement process.
#
# Composed of the measurement itself and executing two hooks :before_hook and :after_hook.
# Due to the structure of the measurement, the hooks are not (and should not) be part of the
# measure. In other words, any code executed within :before_hook or :after_hook will not affect
# the time/space measure of :fn.
#
# @param [Integer] iteration
# @return [Float] indicator
def measurement(n)
@options[:before_hook].call(n)
indicator = measure(n, &@options[:fn])
@options[:after_hook].call(n)
indicator
end

# Parses data from <code>#run_simulation</code>.
#
# <code>examine_result_set</code> will return true only if these conditions are met:
Expand Down
21 changes: 20 additions & 1 deletion spec/big-o/complexity_base_spec.rb
Expand Up @@ -6,7 +6,7 @@
class FunctionComplexity
include ComplexityBase

def measure
def measure(*args, &b)
1
end
end
Expand Down Expand Up @@ -55,4 +55,23 @@ def measure
@fn_complexity.options[:approximation].should == 0.1
end
end

describe 'before/after hooks' do
before :each do
@after_hook = double('lambda')
@before_hook = double('lambda')
@fn_complexity = FunctionComplexity.new({
:fn => lambda { |_| 1 },
:level => lambda { |_| 1},
:before_hook => @before_hook,
:after_hook => @after_hook })

end

it 'should be called every time we try to match a complexity level' do
@before_hook.should_receive(:call).with(kind_of(Integer)).at_most(20).times
@after_hook.should_receive(:call).with(kind_of(Integer)).at_most(20).times
@fn_complexity.process
end
end
end
9 changes: 8 additions & 1 deletion spec/big-o/space_complexity_spec.rb
Expand Up @@ -12,6 +12,14 @@
lambda { @space_complexity.process }.should raise_error(Timeout::Error)
end

it 'should ignore whatever is happening in a before/after hook' do
@space_complexity.options[:fn] = lambda { |_| simulate_memory_space(1024) }
@space_complexity.options[:before_hook] = lambda { |n| simulate_memory_space(1024 * n) }
@space_complexity.options[:after_hook] = lambda { |n| simulate_memory_space(1024 * n) }
@space_complexity.options[:approximation] = 0.2
@space_complexity.should match_complexity_level 'O(1)', lambda { |_| 1 }
end

describe '#process on complexity O(1)' do
before :each do
@space_complexity.options[:fn] = lambda { |_| simulate_memory_space(1024) }
Expand All @@ -35,7 +43,6 @@
it 'should return false if the complexity does not match (too low)' do
@space_complexity.should_not match_complexity_level 'O(1)', lambda { |_| 1 }
end

end

describe '#process on complexity O(n**2)' do
Expand Down
22 changes: 17 additions & 5 deletions spec/big-o/time_complexity_spec.rb
Expand Up @@ -6,15 +6,27 @@
@time_complexity = TimeComplexity.new
end

MIN_TIME = 0.01
AVG_TIME = 0.05

it 'should raise an exception if timeout is reached and no result was found' do
@time_complexity.options[:timeout] = 0.001
@time_complexity.options[:fn] = proc { simulate_utime_processing(1) }
lambda { @time_complexity.process }.should raise_error(Timeout::Error)
end

it 'should ignore whatever is happening in a before/after hook' do
@time_complexity.options[:fn] = lambda { |_| simulate_utime_processing(MIN_TIME) }
@time_complexity.options[:before_hook] = lambda { |n| simulate_utime_processing(MIN_TIME * n) }
@time_complexity.options[:after_hook] = lambda { |n| simulate_utime_processing(MIN_TIME * n) }
@time_complexity.options[:approximation] = 0.2
@time_complexity.should match_complexity_level 'O(1)', lambda { |_| 1 }
end


describe '#process on complexity O(1)' do
before :each do
@time_complexity.options[:fn] = lambda { |_| simulate_utime_processing(0.5) }
@time_complexity.options[:fn] = lambda { |_| simulate_utime_processing(AVG_TIME) }
end

it 'should run the function and produce a report when time threshold is hit' do
Expand All @@ -25,7 +37,7 @@

describe '#process on complexity O(n)' do
before :each do
@time_complexity.options[:fn] = lambda { |n| simulate_utime_processing(0.5 * n) }
@time_complexity.options[:fn] = lambda { |n| simulate_utime_processing(AVG_TIME * n) }
end

it 'should run the function and produce a report when time threshold is hit' do
Expand All @@ -36,7 +48,7 @@
describe '#process on complexity O(n**2)' do
before :each do
@time_complexity.options[:minimum_result_set_size] = 0
@time_complexity.options[:fn] = lambda { |n| simulate_utime_processing(0.5 * (n**2)) }
@time_complexity.options[:fn] = lambda { |n| simulate_utime_processing(AVG_TIME * (n**2)) }
end

it 'should run the function and produce a report when time threshold is hit' do
Expand All @@ -51,14 +63,14 @@

describe 'very small execution time functions (0.1 second and below)' do
it 'should still be valid in case of O(n**2)' do
@time_complexity.options[:fn] = lambda { |n| simulate_utime_processing(0.01 * n**2) }
@time_complexity.options[:fn] = lambda { |n| simulate_utime_processing(MIN_TIME * n**2) }
@time_complexity.should match_complexity_level 'O(n**2)', lambda { |n| n**2 }
@time_complexity.should_not match_complexity_level 'O(n log(n))', lambda { |n| n * Math::log(n) }
@time_complexity.should_not match_complexity_level 'O(1)', lambda { |_| 1 }
end

it 'should still be valid in case of O(n)' do
@time_complexity.options[:fn] = lambda { |n| simulate_utime_processing(0.01 * n) }
@time_complexity.options[:fn] = lambda { |n| simulate_utime_processing(MIN_TIME * n) }
@time_complexity.should match_complexity_level 'O(n)', lambda { |n| n }
@time_complexity.should_not match_complexity_level 'O(1)', lambda { |_| 1 }
end
Expand Down

0 comments on commit 65282f5

Please sign in to comment.