Module: Elasticsearch::Extensions::Test::Profiling

Defined in:
lib/elasticsearch/extensions/test/profiling.rb

Overview

Allows to define and execute profiling tests within [Shoulda](github.com/thoughtbot/shoulda) contexts.

Measures operations and reports statistics, including code profile.

Uses the “benchmark” standard library and the “ruby-prof” gem.

File: profiling_test.rb

require 'test/unit'
require 'shoulda/context'
require 'elasticsearch/extensions/test/profiling'

class ProfilingTest < Test::Unit::TestCase
  extend Elasticsearch::Extensions::Test::Profiling

  context "Mathematics" do
    measure "divide numbers", count: 10_000 do
      assert_nothing_raised { 1/2 }
    end
  end

end

$ QUIET=y ruby profiling_test.rb

...
ProfilingTest

-------------------------------------------------------------------------------
Context: Mathematics should divide numbers (10000x)
mean: 0.03ms | avg: 0.03ms | max: 0.14ms
-------------------------------------------------------------------------------
     PASS (0:00:00.490) test: Mathematics should divide numbers (10000x).
...

Instance Method Summary collapse

Instance Method Details

#measure(name, options = {}, &block) ⇒ Object

TODO:

Try to make progress bar not to interfere with tests

Profiles the passed block of code.

measure "divide numbers", count: 10_000 do
 assert_nothing_raised { 1/2 }
end


71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/elasticsearch/extensions/test/profiling.rb', line 71

def measure(name, options={}, &block)
  ___          = '-'*ANSI::Terminal.terminal_width
  test_name    = name
  suite_name   = self.name.split('::').last
  context_name = self.context(nil) {}.first.parent.name
  count        = Integer(ENV['COUNT'] || options[:count] || 1_000)
  ticks        = []
  progress     = ANSI::Progressbar.new(suite_name, count) unless ENV['QUIET'] || options[:quiet]

  should "#{test_name} (#{count}x)" do
    RubyProf.start

    begin
      count.times do
        ticks << Benchmark.realtime { self.instance_eval(&block) }

        if progress
          RubyProf.pause
          progress.inc
          RubyProf.resume
        end
      end
    ensure
      result = RubyProf.stop
      progress.finish if progress
    end

    total = result.threads.reduce(0) { |t,info| t += info.total_time; t }
    mean  = (ticks.sort[(ticks.size/2).round-1])*1000
    avg   = (ticks.inject {|sum,el| sum += el; sum}.to_f/ticks.size)*1000
    min   = ticks.min*1000
    max   = ticks.max*1000


    result.eliminate_methods!([/Integer#times|Benchmark.realtime|ANSI::Code#.*|ANSI::ProgressBar#.*/])
    printer = RubyProf::FlatPrinter.new(result)
    # printer = RubyProf::GraphPrinter.new(result)

    puts "\n",
         ___,
         "#{suite_name}: " + ANSI.bold(context_name) + ' should ' + ANSI.bold(name) + " (#{count}x)",
         "total: #{sprintf('%.2f', total)}s | " +
         "mean: #{sprintf('%.2f', mean)}ms | " +
         "avg: #{sprintf('%.2f',  avg)}ms | " +
         "min: #{sprintf('%.2f',  min)}ms | " +
         "max: #{sprintf('%.2f',  max)}ms",
         ___
    printer.print(STDOUT, {}) unless ENV['QUIET'] || options[:quiet]
  end
end