elasticsearch-extensions 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +119 -1
- data/Rakefile +4 -6
- data/elasticsearch-extensions.gemspec +1 -0
- data/lib/elasticsearch/extensions/test/cluster.rb +251 -0
- data/lib/elasticsearch/extensions/test/profiling.rb +98 -0
- data/lib/elasticsearch/extensions/test/startup_shutdown.rb +54 -0
- data/lib/elasticsearch/extensions/version.rb +1 -1
- metadata +21 -2
data/README.md
CHANGED
@@ -19,7 +19,125 @@ Display a table with the output of the `_analyze` API:
|
|
19
19
|
require 'elasticsearch/extensions/ansi'
|
20
20
|
puts Elasticsearch::Client.new.indices.analyze(text: 'Quick Brown Fox Jumped').to_ansi
|
21
21
|
|
22
|
-
[Full documentation](http://rubydoc.info/gems/elasticsearch-extensions/Elasticsearch/Extensions/ANSI)
|
22
|
+
[Full documentation](http://rubydoc.info/gems/elasticsearch-extensions/Elasticsearch/Extensions/ANSI)
|
23
|
+
|
24
|
+
### Test::Cluster
|
25
|
+
|
26
|
+
Allows to programatically start and stop an Elasticsearch cluster suitable for isolating tests.
|
27
|
+
|
28
|
+
HTTP service is running on ports `9250-*` by default, and the cluster runs in-memory only.
|
29
|
+
|
30
|
+
Start and stop the default cluster:
|
31
|
+
|
32
|
+
require 'elasticsearch/extensions/test/cluster'
|
33
|
+
|
34
|
+
Elasticsearch::Extensions::Test::Cluster.start
|
35
|
+
Elasticsearch::Extensions::Test::Cluster.stop
|
36
|
+
|
37
|
+
Start the cluster on specific port, with a specific Elasticsearch version, number of nodes and cluster name:
|
38
|
+
|
39
|
+
require 'elasticsearch/extensions/test/cluster'
|
40
|
+
|
41
|
+
Elasticsearch::Extensions::Test::Cluster.start \
|
42
|
+
cluster_name: "my-testing-cluster",
|
43
|
+
command: "/usr/local/Cellar/elasticsearch/1.0.0.Beta2/bin/elasticsearch",
|
44
|
+
nodes: 3,
|
45
|
+
port: 9350
|
46
|
+
|
47
|
+
# Starting 3 Elasticsearch nodes.....................
|
48
|
+
# --------------------------------------------------------------------------------
|
49
|
+
# Cluster: my-testing-cluster
|
50
|
+
# Status: green
|
51
|
+
# Nodes: 3
|
52
|
+
# - node-1 | version: 1.0.0.Beta2, pid: 54469
|
53
|
+
# + node-2 | version: 1.0.0.Beta2, pid: 54470
|
54
|
+
# - node-3 | version: 1.0.0.Beta2, pid: 54468
|
55
|
+
# => true
|
56
|
+
|
57
|
+
|
58
|
+
Stop this cluster:
|
59
|
+
|
60
|
+
require 'elasticsearch/extensions/test/cluster'
|
61
|
+
|
62
|
+
Elasticsearch::Extensions::Test::Cluster.stop \
|
63
|
+
command: "/usr/local/Cellar/elasticsearch/1.0.0.Beta2/bin/elasticsearch",
|
64
|
+
port: 9350
|
65
|
+
|
66
|
+
# Stopping Elasticsearch nodes... stopped PID 54469. stopped PID 54470. stopped PID 54468.
|
67
|
+
# # => [54469, 54470, 54468]
|
68
|
+
|
69
|
+
[Full documentation](http://rubydoc.info/gems/elasticsearch-extensions/Elasticsearch/Extensions/Test/Cluster)
|
70
|
+
|
71
|
+
### Test::StartupShutdown
|
72
|
+
|
73
|
+
Allows to register `startup` and `shutdown` hooks for Test::Unit, similarly to RSpec's `before(:all)`,
|
74
|
+
compatible with the [Test::Unit 2](https://github.com/test-unit/test-unit/blob/master/lib/test/unit/testcase.rb) syntax.
|
75
|
+
|
76
|
+
The extension is useful for e.g. starting the testing Elasticsearch cluster before the test suite is executed,
|
77
|
+
and stopping it afterwards.
|
78
|
+
|
79
|
+
** IMPORTANT NOTE ** You have to register the handler for `shutdown` hook before requiring 'test/unit':
|
80
|
+
|
81
|
+
# File: test_helper.rb
|
82
|
+
at_exit { MyTest.__run_at_exit_hooks }
|
83
|
+
require 'test/unit'
|
84
|
+
|
85
|
+
Example of handler registration:
|
86
|
+
|
87
|
+
class MyTest < Test::Unit::TestCase
|
88
|
+
extend Elasticsearch::Extensions::Test::StartupShutdown
|
89
|
+
|
90
|
+
startup { puts "Suite starting up..." }
|
91
|
+
shutdown { puts "Suite shutting down..." }
|
92
|
+
end
|
93
|
+
|
94
|
+
[Full documentation](http://rubydoc.info/gems/elasticsearch-extensions/Elasticsearch/Extensions/Test/StartupShutdown)
|
95
|
+
|
96
|
+
Examples in the Elasticsearch gem test suite: [1](https://github.com/elasticsearch/elasticsearch-ruby/blob/master/elasticsearch-transport/test/integration/client_test.rb#L4-L6), [2](https://github.com/elasticsearch/elasticsearch-ruby/blob/master/elasticsearch-transport/test/test_helper.rb#L44)
|
97
|
+
|
98
|
+
### Test::Profiling
|
99
|
+
|
100
|
+
Allows to define and execute profiling tests within [Shoulda](https://github.com/thoughtbot/shoulda) contexts.
|
101
|
+
Measures operations and reports statistics, including code profile.
|
102
|
+
|
103
|
+
Let's define a simple profiling test in a `profiling_test.rb` file:
|
104
|
+
|
105
|
+
require 'test/unit'
|
106
|
+
require 'shoulda/context'
|
107
|
+
require 'elasticsearch/extensions/test/profiling'
|
108
|
+
|
109
|
+
class ProfilingTest < Test::Unit::TestCase
|
110
|
+
extend Elasticsearch::Extensions::Test::Profiling
|
111
|
+
|
112
|
+
context "Mathematics" do
|
113
|
+
measure "divide numbers", count: 10_000 do
|
114
|
+
assert_nothing_raised { 1/2 }
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
Let's run it:
|
121
|
+
|
122
|
+
$ QUIET=y ruby profiling_test.rb
|
123
|
+
|
124
|
+
...
|
125
|
+
ProfilingTest
|
126
|
+
|
127
|
+
-------------------------------------------------------------------------------
|
128
|
+
Context: Mathematics should divide numbers (10000x)
|
129
|
+
mean: 0.03ms | avg: 0.03ms | max: 0.14ms
|
130
|
+
-------------------------------------------------------------------------------
|
131
|
+
PASS (0:00:00.490) test: Mathematics should divide numbers (10000x).
|
132
|
+
...
|
133
|
+
|
134
|
+
When using the `QUIET` option, only the statistics on operation throughput are printed.
|
135
|
+
When omitted, the full code profile by [RubyProf](https://github.com/ruby-prof/ruby-prof) is printed.
|
136
|
+
|
137
|
+
[Full documentation](http://rubydoc.info/gems/elasticsearch-extensions/Elasticsearch/Extensions/Test/StartupShutdown)
|
138
|
+
|
139
|
+
[Example in the Elasticsearch gem](https://github.com/elasticsearch/elasticsearch-ruby/blob/master/elasticsearch-transport/test/profile/client_benchmark_test.rb)
|
140
|
+
|
23
141
|
|
24
142
|
## Installation
|
25
143
|
|
data/Rakefile
CHANGED
@@ -49,17 +49,15 @@ namespace :test do
|
|
49
49
|
desc "Start Elasticsearch nodes for tests"
|
50
50
|
task :start do
|
51
51
|
$LOAD_PATH << File.expand_path('../lib', __FILE__) << File.expand_path('../test', __FILE__)
|
52
|
-
require 'elasticsearch/
|
53
|
-
|
54
|
-
Elasticsearch::TestCluster.start
|
52
|
+
require 'elasticsearch/extensions/test/cluster'
|
53
|
+
Elasticsearch::Extensions::Test::Cluster.start
|
55
54
|
end
|
56
55
|
|
57
56
|
desc "Stop Elasticsearch nodes for tests"
|
58
57
|
task :stop do
|
59
58
|
$LOAD_PATH << File.expand_path('../lib', __FILE__) << File.expand_path('../test', __FILE__)
|
60
|
-
require 'elasticsearch/
|
61
|
-
|
62
|
-
Elasticsearch::TestCluster.stop
|
59
|
+
require 'elasticsearch/extensions/test/cluster'
|
60
|
+
Elasticsearch::Extensions::Test::Cluster.stop
|
63
61
|
end
|
64
62
|
end
|
65
63
|
end
|
@@ -0,0 +1,251 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
require 'net/http'
|
3
|
+
require 'uri'
|
4
|
+
require 'json'
|
5
|
+
require 'ansi'
|
6
|
+
|
7
|
+
STDOUT.sync = true
|
8
|
+
|
9
|
+
class String
|
10
|
+
|
11
|
+
# Redefine the ANSI method: do not print ANSI when not running in the terminal
|
12
|
+
#
|
13
|
+
def ansi(*args)
|
14
|
+
STDOUT.tty? ? ANSI.ansi(self, *args) : self
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module Elasticsearch
|
19
|
+
module Extensions
|
20
|
+
module Test
|
21
|
+
|
22
|
+
# A convenience Ruby class for starting and stopping a separate testing in-memory cluster,
|
23
|
+
# to not depend on -- and not mess up -- <localhost:9200>.
|
24
|
+
#
|
25
|
+
# @example Start a cluster with default configuration
|
26
|
+
# require 'elasticsearch/extensions/test/cluster'
|
27
|
+
# Elasticsearch::Extensions::Test::Cluster.start
|
28
|
+
#
|
29
|
+
# @see Cluster#start Cluster.start
|
30
|
+
# @see Cluster#stop Cluster.stop
|
31
|
+
#
|
32
|
+
module Cluster
|
33
|
+
|
34
|
+
# Starts a cluster
|
35
|
+
#
|
36
|
+
# Launches the specified number of nodes in test-suitable configuration by default
|
37
|
+
# and prints information about the cluster -- unless this specific cluster is running already.
|
38
|
+
#
|
39
|
+
# Use the {Cluster#stop Cluster.stop} command with the same arguments to stop this cluster.
|
40
|
+
#
|
41
|
+
# @option arguments [String] :command Elasticsearch command (default: `elasticsearch`).
|
42
|
+
# @option arguments [Integer] :nodes Number of desired nodes (default: 2).
|
43
|
+
# @option arguments [String] :cluster_name Cluster name (default: `elasticsearch_test`).
|
44
|
+
# @option arguments [String] :port Starting port number; will be auto-incremented (default: 9250).
|
45
|
+
# @option arguments [Integer] :timeout Timeout when starting the cluster (default: 30).
|
46
|
+
#
|
47
|
+
# You can also use environment variables to set these options.
|
48
|
+
#
|
49
|
+
# @example Start a cluster with default configuration (2 nodes, in-memory, etc)
|
50
|
+
# Elasticsearch::Extensions::Test::Cluster.start
|
51
|
+
#
|
52
|
+
# @example Start a cluster with a custom configuration
|
53
|
+
# Elasticsearch::Extensions::Test::Cluster.start \
|
54
|
+
# cluster_name: 'my-cluster',
|
55
|
+
# nodes: 3,
|
56
|
+
# node_name: 'my-node',
|
57
|
+
# port: 9350
|
58
|
+
#
|
59
|
+
# @example Start a cluster with a different Elasticsearch version
|
60
|
+
# Elasticsearch::Extensions::Test::Cluster.start \
|
61
|
+
# command: "/usr/local/Cellar/elasticsearch/1.0.0.Beta2/bin/elasticsearch"
|
62
|
+
#
|
63
|
+
# @return Boolean
|
64
|
+
# @see Cluster#stop Cluster.stop
|
65
|
+
#
|
66
|
+
def start(arguments={})
|
67
|
+
@@number_of_nodes = (ENV['TEST_CLUSTER_NODES'] || arguments[:nodes] || 2).to_i
|
68
|
+
|
69
|
+
arguments[:command] ||= ENV['TEST_CLUSTER_COMMAND'] || 'elasticsearch'
|
70
|
+
arguments[:port] ||= (ENV['TEST_CLUSTER_PORT'] || 9250).to_i
|
71
|
+
arguments[:cluster_name] ||= ENV['TEST_CLUSTER_NAME'] || 'elasticsearch_test'
|
72
|
+
arguments[:node_name] ||= 'node'
|
73
|
+
arguments[:timeout] ||= 30
|
74
|
+
|
75
|
+
if running? :on => arguments[:port], :as => arguments[:cluster_name]
|
76
|
+
print "[!] Elasticsearch cluster already running".ansi(:red)
|
77
|
+
wait_for_green(arguments[:port], arguments[:timeout])
|
78
|
+
return false
|
79
|
+
end
|
80
|
+
|
81
|
+
print "Starting ".ansi(:faint) +
|
82
|
+
@@number_of_nodes.to_s.ansi(:bold, :faint) +
|
83
|
+
" Elasticsearch nodes..".ansi(:faint)
|
84
|
+
|
85
|
+
@@number_of_nodes.times do |n|
|
86
|
+
n += 1
|
87
|
+
pidfile = File.expand_path("tmp/elasticsearch-#{n}.pid", Dir.pwd)
|
88
|
+
pid = Process.spawn <<-COMMAND
|
89
|
+
#{arguments[:command]} \
|
90
|
+
-D es.foreground=yes \
|
91
|
+
-D es.cluster.name=#{arguments[:cluster_name]} \
|
92
|
+
-D es.node.name=#{arguments[:node_name]}-#{n} \
|
93
|
+
-D es.http.port=#{arguments[:port].to_i + (n-1)} \
|
94
|
+
-D es.gateway.type=none \
|
95
|
+
-D es.index.store.type=memory \
|
96
|
+
-D es.network.host=0.0.0.0 \
|
97
|
+
-D es.discovery.zen.ping.multicast.enabled=true \
|
98
|
+
-D es.pidfile=#{pidfile} \
|
99
|
+
> /dev/null 2>&1
|
100
|
+
COMMAND
|
101
|
+
Process.detach pid
|
102
|
+
end
|
103
|
+
|
104
|
+
wait_for_green(arguments[:port], arguments[:timeout])
|
105
|
+
return true
|
106
|
+
end
|
107
|
+
|
108
|
+
# Stop the cluster.
|
109
|
+
#
|
110
|
+
# Fetches the PID numbers from "Nodes Info" API and terminates matching nodes.
|
111
|
+
#
|
112
|
+
# @example Stop the default cluster
|
113
|
+
# Elasticsearch::Extensions::Test::Cluster.stop
|
114
|
+
#
|
115
|
+
# @example Stop the cluster reachable on specific port
|
116
|
+
# Elasticsearch::Extensions::Test::Cluster.stop port: 9350
|
117
|
+
#
|
118
|
+
# @return Boolean
|
119
|
+
# @see Cluster#start Cluster.start
|
120
|
+
#
|
121
|
+
def stop(arguments={})
|
122
|
+
arguments[:port] ||= (ENV['TEST_CLUSTER_PORT'] || 9250).to_i
|
123
|
+
|
124
|
+
nodes = JSON.parse(Net::HTTP.get(URI("http://localhost:#{arguments[:port]}/_cluster/nodes/?process"))) rescue nil
|
125
|
+
|
126
|
+
return false if nodes.nil? or nodes.empty?
|
127
|
+
|
128
|
+
pids = nodes['nodes'].map { |id, info| info['process']['id'] }
|
129
|
+
|
130
|
+
unless pids.empty?
|
131
|
+
print "Stopping Elasticsearch nodes... ".ansi(:faint)
|
132
|
+
pids.each_with_index do |pid, i|
|
133
|
+
begin
|
134
|
+
print "stopped PID #{pid}. ".ansi(:green) if Process.kill 'KILL', pid
|
135
|
+
rescue Exception => e
|
136
|
+
print "[#{e.class}] PID #{pid} not found. ".ansi(:red)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
puts
|
140
|
+
else
|
141
|
+
false
|
142
|
+
end
|
143
|
+
|
144
|
+
return pids
|
145
|
+
end
|
146
|
+
|
147
|
+
# Returns true when a specific test node is running within the cluster.
|
148
|
+
#
|
149
|
+
# @option arguments [Integer] :on The port on which the node is running.
|
150
|
+
# @option arguments [String] :as The cluster name.
|
151
|
+
#
|
152
|
+
# @return Boolean
|
153
|
+
#
|
154
|
+
def running?(arguments={})
|
155
|
+
port = arguments[:on] || (ENV['TEST_CLUSTER_PORT'] || 9250).to_i
|
156
|
+
cluster_name = arguments[:as] || ENV['TEST_CLUSTER_NAME'] || 'elasticsearch_test'
|
157
|
+
|
158
|
+
if cluster_health = Timeout::timeout(0.25) { __get_cluster_health(port) } rescue nil
|
159
|
+
return cluster_health['cluster_name'] == cluster_name && \
|
160
|
+
cluster_health['number_of_nodes'] == @@number_of_nodes
|
161
|
+
end
|
162
|
+
return false
|
163
|
+
end
|
164
|
+
|
165
|
+
# Waits until the cluster is green and prints information
|
166
|
+
#
|
167
|
+
# @example Print the information about the default cluster
|
168
|
+
# Elasticsearch::Extensions::Test::Cluster.wait_for_green
|
169
|
+
#
|
170
|
+
# @param (see #__wait_for_status)
|
171
|
+
#
|
172
|
+
# @return Boolean
|
173
|
+
#
|
174
|
+
def wait_for_green(port=9250, timeout=60)
|
175
|
+
__wait_for_status('green', port, timeout)
|
176
|
+
end
|
177
|
+
|
178
|
+
# Blocks the process and waits for the cluster to be in a "green" state.
|
179
|
+
#
|
180
|
+
# Prints information about the cluster on STDOUT if the cluster is available.
|
181
|
+
#
|
182
|
+
# @param status [String] The status to wait for (yellow, green)
|
183
|
+
# @param port [Integer] The port on which the cluster is reachable
|
184
|
+
# @param timeout [Integer] The explicit timeout for the operation
|
185
|
+
#
|
186
|
+
# @api private
|
187
|
+
#
|
188
|
+
# @return Boolean
|
189
|
+
#
|
190
|
+
def __wait_for_status(status='green', port=9250, timeout=30)
|
191
|
+
uri = URI("http://localhost:#{port}/_cluster/health?wait_for_status=#{status}")
|
192
|
+
|
193
|
+
Timeout::timeout(timeout) do
|
194
|
+
loop do
|
195
|
+
response = begin
|
196
|
+
JSON.parse(Net::HTTP.get(uri))
|
197
|
+
rescue Exception => e
|
198
|
+
puts e.inspect if ENV['DEBUG']
|
199
|
+
nil
|
200
|
+
end
|
201
|
+
|
202
|
+
if response && response['status'] == status && ( @@number_of_nodes.nil? || @@number_of_nodes == response['number_of_nodes'].to_i )
|
203
|
+
__print_cluster_info(port) and break
|
204
|
+
end
|
205
|
+
|
206
|
+
print '.'.ansi(:faint)
|
207
|
+
sleep 1
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
return true
|
212
|
+
end
|
213
|
+
|
214
|
+
# Print information about the cluster on STDOUT
|
215
|
+
#
|
216
|
+
# @api private
|
217
|
+
#
|
218
|
+
def __print_cluster_info(port)
|
219
|
+
health = JSON.parse(Net::HTTP.get(URI("http://localhost:#{port}/_cluster/health")))
|
220
|
+
nodes = JSON.parse(Net::HTTP.get(URI("http://localhost:#{port}/_cluster/nodes/?process")))
|
221
|
+
master = JSON.parse(Net::HTTP.get(URI("http://localhost:#{port}/_cluster/state")))['master_node']
|
222
|
+
|
223
|
+
puts "\n",
|
224
|
+
('-'*80).ansi(:faint),
|
225
|
+
'Cluster: '.ljust(20).ansi(:faint) + health['cluster_name'].to_s.ansi(:faint),
|
226
|
+
'Status: '.ljust(20).ansi(:faint) + health['status'].to_s.ansi(:faint),
|
227
|
+
'Nodes: '.ljust(20).ansi(:faint) + health['number_of_nodes'].to_s.ansi(:faint)
|
228
|
+
|
229
|
+
nodes['nodes'].each do |id, info|
|
230
|
+
m = id == master ? '+' : '-'
|
231
|
+
puts ''.ljust(20) +
|
232
|
+
"#{m} #{info['name'].ansi(:bold)} | version: #{info['version']}, pid: #{info['process']['id']}".ansi(:faint)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
# Tries to load cluster health information
|
237
|
+
#
|
238
|
+
# @api private
|
239
|
+
#
|
240
|
+
def __get_cluster_health(port=9250)
|
241
|
+
uri = URI("http://localhost:#{port}/_cluster/health")
|
242
|
+
if response = Net::HTTP.get(uri) rescue nil
|
243
|
+
return JSON.parse(response)
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
extend self
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'ruby-prof'
|
2
|
+
require 'benchmark'
|
3
|
+
require 'ansi'
|
4
|
+
|
5
|
+
module Elasticsearch
|
6
|
+
module Extensions
|
7
|
+
module Test
|
8
|
+
|
9
|
+
# Allows to define and execute profiling tests within [Shoulda](https://github.com/thoughtbot/shoulda) contexts.
|
10
|
+
#
|
11
|
+
# Measures operations and reports statistics, including code profile.
|
12
|
+
#
|
13
|
+
# Uses the "benchmark" standard library and the "ruby-prof" gem.
|
14
|
+
#
|
15
|
+
# File: profiling_test.rb
|
16
|
+
#
|
17
|
+
# require 'test/unit'
|
18
|
+
# require 'shoulda/context'
|
19
|
+
# require 'elasticsearch/extensions/test/profiling'
|
20
|
+
#
|
21
|
+
# class ProfilingTest < Test::Unit::TestCase
|
22
|
+
# extend Elasticsearch::Extensions::Test::Profiling
|
23
|
+
#
|
24
|
+
# context "Mathematics" do
|
25
|
+
# measure "divide numbers", count: 10_000 do
|
26
|
+
# assert_nothing_raised { 1/2 }
|
27
|
+
# end
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# $ QUIET=y ruby profiling_test.rb
|
33
|
+
#
|
34
|
+
# ...
|
35
|
+
# ProfilingTest
|
36
|
+
#
|
37
|
+
# -------------------------------------------------------------------------------
|
38
|
+
# Context: Mathematics should divide numbers (10000x)
|
39
|
+
# mean: 0.03ms | avg: 0.03ms | max: 0.14ms
|
40
|
+
# -------------------------------------------------------------------------------
|
41
|
+
# PASS (0:00:00.490) test: Mathematics should divide numbers (10000x).
|
42
|
+
# ...
|
43
|
+
#
|
44
|
+
module Profiling
|
45
|
+
|
46
|
+
# Profiles the passed block of code.
|
47
|
+
#
|
48
|
+
# measure "divide numbers", count: 10_000 do
|
49
|
+
# assert_nothing_raised { 1/2 }
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# @todo Try to make progress bar not to interfere with tests
|
53
|
+
#
|
54
|
+
def measure(name, options={}, &block)
|
55
|
+
___ = '-'*ANSI::Terminal.terminal_width
|
56
|
+
test_name = self.name.split('::').last
|
57
|
+
context_name = self.context(nil) {}.first.parent.name
|
58
|
+
count = Integer(ENV['COUNT'] || options[:count] || 1_000)
|
59
|
+
ticks = []
|
60
|
+
# progress = ANSI::Progressbar.new("#{name} (#{count}x)", count)
|
61
|
+
|
62
|
+
should "#{name} (#{count}x)" do
|
63
|
+
RubyProf.start
|
64
|
+
|
65
|
+
count.times do
|
66
|
+
ticks << Benchmark.realtime { self.instance_eval(&block) }
|
67
|
+
# RubyProf.pause
|
68
|
+
# progress.inc
|
69
|
+
# RubyProf.resume
|
70
|
+
end
|
71
|
+
|
72
|
+
result = RubyProf.stop
|
73
|
+
# progress.finish
|
74
|
+
|
75
|
+
total = result.threads.reduce(0) { |total,info| total += info.total_time; total }
|
76
|
+
mean = (ticks.sort[(ticks.size/2).round-1])*1000
|
77
|
+
avg = (ticks.inject {|sum,el| sum += el; sum}.to_f/ticks.size)*1000
|
78
|
+
max = ticks.max*1000
|
79
|
+
|
80
|
+
|
81
|
+
result.eliminate_methods!([/Integer#times|Benchmark.realtime|ANSI::Code#.*|ANSI::ProgressBar#.*/])
|
82
|
+
printer = RubyProf::FlatPrinter.new(result)
|
83
|
+
# printer = RubyProf::GraphPrinter.new(result)
|
84
|
+
|
85
|
+
puts "\n",
|
86
|
+
___,
|
87
|
+
'Context: ' + ANSI.bold(context_name) + ' should ' + ANSI.bold(name) + " (#{count}x)",
|
88
|
+
"mean: #{sprintf('%.2f', mean)}ms | " +
|
89
|
+
"avg: #{sprintf('%.2f', avg)}ms | " +
|
90
|
+
"max: #{sprintf('%.2f', max)}ms",
|
91
|
+
___
|
92
|
+
printer.print(STDOUT, {}) unless ENV['QUIET'] || options[:quiet]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Elasticsearch
|
2
|
+
module Extensions
|
3
|
+
module Test
|
4
|
+
# Startup/shutdown support for test suites
|
5
|
+
#
|
6
|
+
# Example:
|
7
|
+
#
|
8
|
+
# class MyTest < Test::Unit::TestCase
|
9
|
+
# extend Elasticsearch::Extensions::Test::StartupShutdown
|
10
|
+
#
|
11
|
+
# startup { puts "Suite starting up..." }
|
12
|
+
# shutdown { puts "Suite shutting down..." }
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# *** IMPORTANT NOTE: **********************************************************
|
16
|
+
#
|
17
|
+
# You have to register the handler for shutdown before requiring 'test/unit':
|
18
|
+
#
|
19
|
+
# # File: test_helper.rb
|
20
|
+
# at_exit { MyTest.__run_at_exit_hooks }
|
21
|
+
# require 'test/unit'
|
22
|
+
#
|
23
|
+
# The API follows Test::Unit 2.0
|
24
|
+
# <https://github.com/test-unit/test-unit/blob/master/lib/test/unit/testcase.rb>
|
25
|
+
#
|
26
|
+
module StartupShutdown
|
27
|
+
@@started = false
|
28
|
+
@@shutdown_blocks ||= []
|
29
|
+
|
30
|
+
def startup &block
|
31
|
+
return if started?
|
32
|
+
@@started = true
|
33
|
+
yield block if block_given?
|
34
|
+
end
|
35
|
+
|
36
|
+
def shutdown &block
|
37
|
+
@@shutdown_blocks << block if block_given?
|
38
|
+
end
|
39
|
+
|
40
|
+
def started?
|
41
|
+
!! @@started
|
42
|
+
end
|
43
|
+
|
44
|
+
def __run_at_exit_hooks
|
45
|
+
return unless started?
|
46
|
+
STDERR.puts ANSI.faint("Running at_exit hooks...")
|
47
|
+
puts ANSI.faint('-'*80)
|
48
|
+
@@shutdown_blocks.each { |b| b.call }
|
49
|
+
puts ANSI.faint('-'*80)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: elasticsearch-extensions
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-12-
|
12
|
+
date: 2013-12-09 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: elasticsearch
|
@@ -43,6 +43,22 @@ dependencies:
|
|
43
43
|
- - ! '>='
|
44
44
|
- !ruby/object:Gem::Version
|
45
45
|
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: ruby-prof
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
46
62
|
- !ruby/object:Gem::Dependency
|
47
63
|
name: bundler
|
48
64
|
requirement: !ruby/object:Gem::Requirement
|
@@ -238,6 +254,9 @@ files:
|
|
238
254
|
- lib/elasticsearch/extensions/ansi/actions.rb
|
239
255
|
- lib/elasticsearch/extensions/ansi/helpers.rb
|
240
256
|
- lib/elasticsearch/extensions/ansi/response.rb
|
257
|
+
- lib/elasticsearch/extensions/test/cluster.rb
|
258
|
+
- lib/elasticsearch/extensions/test/profiling.rb
|
259
|
+
- lib/elasticsearch/extensions/test/startup_shutdown.rb
|
241
260
|
- lib/elasticsearch/extensions/version.rb
|
242
261
|
- test/ansi/unit/ansi_test.rb
|
243
262
|
- test/test_helper.rb
|