grntest 1.1.1 → 1.1.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9681ce146649a19fe0099c41cac952e0c53c964c
4
- data.tar.gz: accc76ff8605c28f26a07014082523f7d30134ec
3
+ metadata.gz: 7da2ff4d46c711c2bc7bf534ed0478fc4bb9a510
4
+ data.tar.gz: 7734142d71d8b3d0375e9275eb168fac6df23932
5
5
  SHA512:
6
- metadata.gz: 210cd594bdfb36425deb5d28084acbf798bfd765021c002f7c00fb54d0ee64c7f69a335f269f9f4ec5229e8930692cccecabc74929004e24a66e9d7813f7615e
7
- data.tar.gz: 51151bee659689a783ee6eed2f9c8d8fc48e1a71245cb9ec15c8cd8a9cb48a9c3bbff2b276e402a9d1abec46f740a6760d9d95daeb39b74d9152b6aae87f805f
6
+ metadata.gz: d17ae23c5d7342c7aae95d96bf2c8e271661860232b4766d7397250c1276b7984d1be6b2d415f30639b5a1f2a0b4be8705c6202973bd626afdc4b38bdce63994
7
+ data.tar.gz: e35c94ee423590de22f75f33406f6728ce6ff016d08001aac49645dd1b8d7f74c7d68d0ac14e0396209a20e4efc27284c62910b0a0516602668d0f4ee9602050
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # README
2
2
 
3
+ [![Gem Version](https://badge.fury.io/rb/grntest.svg)](http://badge.fury.io/rb/grntest)
4
+
3
5
  ## Name
4
6
 
5
7
  grntest
@@ -215,6 +217,8 @@ Here are available `NAME` s:
215
217
  * `long-timeout`
216
218
  * `on-error`
217
219
  * `omit`
220
+ * `add-important-log-levels`
221
+ * `remove-important-log-levels`
218
222
 
219
223
  `ARGUMENTS...` are depends on directive. A directive doesn't require
220
224
  any arguments but a directive requires arguments.
@@ -429,6 +433,83 @@ Example:
429
433
  new_excelent_command
430
434
  ```
431
435
 
436
+ #### `add-important-log-levels`
437
+
438
+ Usage:
439
+
440
+ ```
441
+ #@add-important-log-levels LEVEL_1 LEVEL_2 ...
442
+ ```
443
+
444
+ Add `LEVEL_N` to important log level list. grntest outputs log
445
+ messages when their log level is included in important log level
446
+ list.
447
+
448
+ The default important log level list has the following log levels:
449
+
450
+ * `emergency`
451
+ * `alert`
452
+ * `critical`
453
+ * `error`
454
+ * `warning`
455
+ * `notice`
456
+
457
+ If you want to test log messages for `info`, `debug` or `dump` log
458
+ levels, you need to add the log level to important log level list.
459
+
460
+ Example:
461
+
462
+ ```
463
+ # Enable logs for debug level
464
+ log_level --level debug
465
+ # Collect debug level log messages
466
+ #@add-important-log-levels debug
467
+ log_put --level debug --message "This is a message"
468
+ # The following messages is collected by grntest
469
+ #|d| This is a message
470
+ ```
471
+
472
+ #### `remove-important-log-levels`
473
+
474
+ Usage:
475
+
476
+ ```
477
+ #@remove-important-log-levels LEVEL_1 LEVEL_2 ...
478
+ ```
479
+
480
+ Remove `LEVEL_N` from important log level list. grntest outputs log
481
+ messages when their log level is included in important log level
482
+ list.
483
+
484
+ The default important log level list has the following log levels:
485
+
486
+ * `emergency`
487
+ * `alert`
488
+ * `critical`
489
+ * `error`
490
+ * `warning`
491
+ * `notice`
492
+
493
+ You can remove them from important log level list. You can only remove
494
+ log levels added by `add-important-log-levels`.
495
+
496
+ Example:
497
+
498
+ ```
499
+ # Enable logs for debug level
500
+ log_level --level debug
501
+ # Collect debug level log messages
502
+ #@add-important-log-levels debug
503
+ log_put --level debug --message "This is a message"
504
+ # The following messages is collected by grntest
505
+ #|d| This is a message
506
+
507
+ # Disable collecting debug level log messages
508
+ #@remove-important-log-levels debug
509
+ log_put --level debug --message "This is a message"
510
+ # No message is collected by grntest
511
+ ```
512
+
432
513
  ## Options
433
514
 
434
515
  Grntest has many options. You don't need to specify many of them
@@ -554,7 +635,7 @@ has many test scripts and uses many useful features. They will help you.
554
635
 
555
636
  ## Dependencies
556
637
 
557
- * Ruby 1.9.3
638
+ * Ruby
558
639
  * msgpack gem
559
640
 
560
641
  ## Mailing list
data/doc/text/news.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # News
2
2
 
3
+ ## 1.1.2: 2015-07-08
4
+
5
+ ### Improvements
6
+
7
+ * Improve HTTP server related error case handlings.
8
+ * Support Valgrind.
9
+ * Added `--stop-on-failure` that stops testing when one test is failed.
10
+ * Support JSONP.
11
+ * Add `add-important-log-levels` directive.
12
+ * Add `remove-important-log-levels` directive.
13
+
3
14
  ## 1.1.1: 2015-02-03
4
15
 
5
16
  ### Improvements
@@ -32,14 +32,15 @@ module Grntest
32
32
  end
33
33
 
34
34
  attr_reader :context
35
- def initialize(context=nil)
35
+ def initialize(context)
36
36
  @loading = false
37
37
  @pending_command = ""
38
38
  @pending_load_command = nil
39
39
  @current_command_name = nil
40
40
  @output_type = nil
41
41
  @long_timeout = default_long_timeout
42
- @context = context || ExecutionContext.new
42
+ @context = context
43
+ @custom_important_log_levels = []
43
44
  end
44
45
 
45
46
  def execute(script_path)
@@ -191,6 +192,24 @@ module Grntest
191
192
  @context.omit
192
193
  end
193
194
 
195
+ def execute_directive_add_important_log_levels(line, content, options)
196
+ log_levels = options.collect do |log_level|
197
+ normalize_log_level(log_level)
198
+ end
199
+ @custom_important_log_levels |= log_levels
200
+ end
201
+
202
+ def execute_directive_remove_important_log_levels(line, content, options)
203
+ log_levels = options.collect do |log_level|
204
+ normalize_log_level(log_level)
205
+ end
206
+ @custom_important_log_levels -= log_levels
207
+ end
208
+
209
+ def normalize_log_level(level)
210
+ level[0]
211
+ end
212
+
194
213
  def execute_directive(line, content)
195
214
  command, *options = Shellwords.split(content)
196
215
  case command
@@ -210,6 +229,10 @@ module Grntest
210
229
  execute_directive_on_error(line, content, options)
211
230
  when "omit"
212
231
  execute_directive_omit(line, content, options)
232
+ when "add-important-log-levels"
233
+ execute_directive_add_important_log_levels(line, content, options)
234
+ when "remove-important-log-levels"
235
+ execute_directive_remove_important_log_levels(line, content, options)
213
236
  else
214
237
  log_input(line)
215
238
  log_error("#|e| unknown directive: <#{command}>")
@@ -296,7 +319,8 @@ module Grntest
296
319
  end
297
320
 
298
321
  def important_log_level?(log_level)
299
- ["E", "A", "C", "e", "w"].include?(log_level)
322
+ ["E", "A", "C", "e", "w"].include?(log_level) or
323
+ @custom_important_log_levels.include?(log_level)
300
324
  end
301
325
 
302
326
  def backtrace_log_message?(message)
@@ -22,11 +22,11 @@ require "grntest/executors/base-executor"
22
22
  module Grntest
23
23
  module Executors
24
24
  class HTTPExecutor < BaseExecutor
25
- def initialize(host, port, context=nil)
25
+ def initialize(host, port, context, options={})
26
26
  super(context)
27
27
  @host = host
28
28
  @port = port
29
- @read_timeout = 3
29
+ @read_timeout = options[:read_timeout] || 3
30
30
  end
31
31
 
32
32
  def send_command(command)
@@ -44,13 +44,16 @@ module Grntest
44
44
  rescue Error
45
45
  n_retried += 1
46
46
  sleep(0.1)
47
- retry if n_retried < 50
47
+ retry if n_retried < 100
48
48
  raise
49
49
  end
50
50
  end
51
51
 
52
52
  def shutdown
53
- send_command(command("shutdown"))
53
+ begin
54
+ send_command(command("shutdown"))
55
+ rescue Error
56
+ end
54
57
  end
55
58
 
56
59
  def create_sub_executor(context)
@@ -20,7 +20,7 @@ require "grntest/executors/base-executor"
20
20
  module Grntest
21
21
  module Executors
22
22
  class StandardIOExecutor < BaseExecutor
23
- def initialize(input, output, context=nil)
23
+ def initialize(input, output, context)
24
24
  super(context)
25
25
  @input = input
26
26
  @output = output
@@ -228,16 +228,51 @@ module Grntest
228
228
  end
229
229
  command_line << @tester.gdb
230
230
  gdb_command_path = context.temporary_directory_path + "groonga.gdb"
231
- File.open(gdb_command_path, "w") do |gdb_command|
232
- gdb_command.puts(<<-EOC)
231
+ gdb_command_path.open("w") do |gdb_command|
232
+ gdb_command.puts(<<-COMMANDS)
233
233
  break main
234
234
  run
235
- print chdir("#{context.temporary_directory_path}")
236
- EOC
235
+ call chdir("#{context.temporary_directory_path}")
236
+ COMMANDS
237
237
  end
238
238
  command_line << "--command=#{gdb_command_path}"
239
239
  command_line << "--quiet"
240
240
  command_line << "--args"
241
+ elsif @tester.valgrind
242
+ if libtool_wrapper?(command)
243
+ command_line << find_libtool(command)
244
+ command_line << "--mode=execute"
245
+ end
246
+ command_line << @tester.valgrind
247
+ command_line << "--leak-check=full"
248
+ command_line << "--show-reachable=yes"
249
+ command_line << "--track-origins=yes"
250
+ valgrind_suppressions_file_path =
251
+ context.temporary_directory_path + "groonga.supp"
252
+ valgrind_suppressions_file_path.open("w") do |suppressions|
253
+ suppressions.puts(<<-SUPPRESSIONS)
254
+ {
255
+ dlopen
256
+ Memcheck:Leak
257
+ match-leak-kinds: reachable
258
+ ...
259
+ fun:dlopen*
260
+ ...
261
+ }
262
+ {
263
+ _dl_catch_error
264
+ Memcheck:Leak
265
+ match-leak-kinds: reachable
266
+ ...
267
+ fun:_dl_catch_error
268
+ }
269
+ SUPPRESSIONS
270
+ end
271
+ command_line << "--suppressions=#{valgrind_suppressions_file_path}"
272
+ if @tester.valgrind_gen_suppressions?
273
+ command_line << "--gen-suppressions=all"
274
+ end
275
+ command_line << "--verbose"
241
276
  else
242
277
  spawn_options[:chdir] = context.temporary_directory_path.to_s
243
278
  end
@@ -282,10 +317,15 @@ EOC
282
317
  command_line = groonga_http_command(host, port, pid_file_path, context,
283
318
  spawn_options)
284
319
  pid = nil
320
+ shutdown_wait_timeout = 5
321
+ options = {}
322
+ if @tester.gdb
323
+ options[:read_timeout] = 60 * 10
324
+ end
285
325
  begin
286
326
  pid = Process.spawn(env, *command_line, spawn_options)
287
327
  begin
288
- executor = Executors::HTTPExecutor.new(host, port, context)
328
+ executor = Executors::HTTPExecutor.new(host, port, context, options)
289
329
  begin
290
330
  executor.ensure_groonga_ready
291
331
  rescue
@@ -298,10 +338,15 @@ EOC
298
338
  end
299
339
  yield(executor)
300
340
  ensure
301
- Process.kill(:TERM, pid)
302
- wait_groonga_http_shutdown(pid_file_path)
341
+ executor.shutdown
342
+ if wait_groonga_http_shutdown(pid_file_path, shutdown_wait_timeout)
343
+ pid = nil if wait_pid(pid, shutdown_wait_timeout)
344
+ end
303
345
  end
304
346
  ensure
347
+ return if pid.nil?
348
+ Process.kill(:TERM, pid)
349
+ wait_groonga_http_shutdown(pid_file_path, shutdown_wait_timeout)
305
350
  ensure_process_finished(pid)
306
351
  end
307
352
  end
@@ -314,20 +359,34 @@ EOC
314
359
  finished_pid = Process.waitpid(pid, Process::WNOHANG)
315
360
  break if finished_pid
316
361
  n_retries += 1
317
- break if n_retries > 10
362
+ break if n_retries > 100
318
363
  Process.kill(:TERM, pid)
319
364
  sleep(0.1)
320
365
  end
321
366
  end
322
367
 
323
- def wait_groonga_http_shutdown(pid_file_path)
368
+ def wait_pid(pid, timeout)
369
+ total_sleep_time = 0
370
+ sleep_time = 0.1
371
+ loop do
372
+ return true if Process.waitpid(pid, Process::WNOHANG)
373
+ sleep(sleep_time)
374
+ total_sleep_time += sleep_time
375
+ return false if total_sleep_time > timeout
376
+ end
377
+ end
378
+
379
+ def wait_groonga_http_shutdown(pid_file_path, timeout)
380
+ return false unless pid_file_path.exist?
381
+
324
382
  total_sleep_time = 0
325
383
  sleep_time = 0.1
326
384
  while pid_file_path.exist?
327
385
  sleep(sleep_time)
328
386
  total_sleep_time += sleep_time
329
- break if total_sleep_time > 1.0
387
+ break if total_sleep_time > timeout
330
388
  end
389
+ true
331
390
  end
332
391
 
333
392
  def groonga_http_command(host, port, pid_file_path, context, spawn_options)
@@ -402,6 +461,9 @@ http {
402
461
 
403
462
  def create_empty_database(db_path)
404
463
  output_fd = Tempfile.new("create-empty-database")
464
+ env = {
465
+ "GRN_FMALLOC_PROB" => nil,
466
+ }
405
467
  create_database_command = [
406
468
  @tester.groonga,
407
469
  "--output-fd", output_fd.to_i.to_s,
@@ -411,7 +473,7 @@ http {
411
473
  options = {
412
474
  output_fd.to_i => output_fd.to_i
413
475
  }
414
- system(*create_database_command, options)
476
+ system(env, *create_database_command, options)
415
477
  output_fd.close(true)
416
478
  end
417
479
 
@@ -442,8 +504,17 @@ http {
442
504
  when "json", "msgpack"
443
505
  status = nil
444
506
  values = nil
507
+ content = content.chomp
508
+ if type == "json" and /\A([^(]+\()(.+)(\);)\z/ =~ content
509
+ jsonp = true
510
+ jsonp_start = $1
511
+ content = $2
512
+ jsonp_end = $3
513
+ else
514
+ jsonp = false
515
+ end
445
516
  begin
446
- status, *values = ResponseParser.parse(content.chomp, type)
517
+ status, *values = ResponseParser.parse(content, type)
447
518
  rescue ParseError
448
519
  return $!.message
449
520
  end
@@ -454,7 +525,12 @@ http {
454
525
  if normalized_output.bytesize > @max_n_columns
455
526
  normalized_output = JSON.pretty_generate(normalized_output_content)
456
527
  end
457
- normalize_raw_content(normalized_output)
528
+ normalized_raw_content = normalize_raw_content(normalized_output)
529
+ if jsonp
530
+ "#{jsonp_start}#{normalized_raw_content.chomp}#{jsonp_end}\n"
531
+ else
532
+ normalized_raw_content
533
+ end
458
534
  when "xml"
459
535
  normalized_xml = normalize_output_xml(content, options)
460
536
  normalize_raw_content(normalized_xml)
@@ -64,6 +64,13 @@ module Grntest
64
64
  collect_count(:n_not_checked_tests)
65
65
  end
66
66
 
67
+ def have_failure?
68
+ @workers.any? do |worker|
69
+ worker.result.n_failed_tests > 0 or
70
+ worker.result.n_leaked_tests > 0
71
+ end
72
+ end
73
+
67
74
  private
68
75
  def collect_count(item)
69
76
  counts = @workers.collect do |worker|
@@ -1,6 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  #
3
- # Copyright (C) 2012-2013 Kouhei Sutou <kou@clear-code.com>
3
+ # Copyright (C) 2012-2015 Kouhei Sutou <kou@clear-code.com>
4
4
  #
5
5
  # This program is free software: you can redistribute it and/or modify
6
6
  # it under the terms of the GNU General Public License as published by
@@ -164,12 +164,30 @@ module Grntest
164
164
  tester.gdb = command || tester.default_gdb
165
165
  end
166
166
 
167
+ parser.on("--valgrind[=COMMAND]",
168
+ "Run groonga on valgrind and use COMMAND as valgrind",
169
+ "(#{tester.default_valgrind})") do |command|
170
+ tester.valgrind = command || tester.default_valgrind
171
+ end
172
+
173
+ parser.on("--[no-]valgrind-gen-suppressions",
174
+ "Generate suppressions for Valgrind",
175
+ "(#{tester.valgrind_gen_suppressions?})") do |boolean|
176
+ tester.valgrind_gen_suppressions = boolean
177
+ end
178
+
167
179
  parser.on("--[no-]keep-database",
168
180
  "Keep used database for debug after test is finished",
169
181
  "(#{tester.keep_database?})") do |boolean|
170
182
  tester.keep_database = boolean
171
183
  end
172
184
 
185
+ parser.on("--[no-]stop-on-failure",
186
+ "Stop immediately on the first non success test",
187
+ "(#{tester.stop_on_failure?})") do |boolean|
188
+ tester.stop_on_failure = boolean
189
+ end
190
+
173
191
  parser.on("--output=OUTPUT",
174
192
  "Output to OUTPUT",
175
193
  "(stdout)") do |output|
@@ -206,7 +224,10 @@ module Grntest
206
224
  attr_accessor :n_workers
207
225
  attr_accessor :output
208
226
  attr_accessor :gdb, :default_gdb
227
+ attr_accessor :valgrind, :default_valgrind
228
+ attr_writer :valgrind_gen_suppressions
209
229
  attr_writer :reporter, :keep_database, :use_color
230
+ attr_writer :stop_on_failure
210
231
  attr_reader :test_patterns, :test_suite_patterns
211
232
  attr_reader :exclude_test_patterns, :exclude_test_suite_patterns
212
233
  def initialize
@@ -223,12 +244,14 @@ module Grntest
223
244
  @output = $stdout
224
245
  @keep_database = false
225
246
  @use_color = nil
247
+ @stop_on_failure = false
226
248
  @test_patterns = []
227
249
  @test_suite_patterns = []
228
250
  @exclude_test_patterns = []
229
251
  @exclude_test_suite_patterns = []
230
252
  detect_suitable_diff
231
253
  initialize_debuggers
254
+ initialize_memory_checkers
232
255
  end
233
256
 
234
257
  def run(*targets)
@@ -262,6 +285,14 @@ module Grntest
262
285
  @use_color
263
286
  end
264
287
 
288
+ def stop_on_failure?
289
+ @stop_on_failure
290
+ end
291
+
292
+ def valgrind_gen_suppressions?
293
+ @valgrind_gen_suppressions
294
+ end
295
+
265
296
  def target_test?(test_name)
266
297
  selected_test?(test_name) and not excluded_test?(test_name)
267
298
  end
@@ -343,6 +374,12 @@ module Grntest
343
374
  @default_gdb = "gdb"
344
375
  end
345
376
 
377
+ def initialize_memory_checkers
378
+ @vagrind = nil
379
+ @default_valgrind = "valgrind"
380
+ @vagrind_gen_suppressions = false
381
+ end
382
+
346
383
  def command_exist?(name)
347
384
  ENV["PATH"].split(File::PATH_SEPARATOR).each do |path|
348
385
  absolute_path = File.join(path, name)
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2012-2014 Kouhei Sutou <kou@clear-code.com>
1
+ # Copyright (C) 2012-2015 Kouhei Sutou <kou@clear-code.com>
2
2
  #
3
3
  # This program is free software: you can redistribute it and/or modify
4
4
  # it under the terms of the GNU General Public License as published by
@@ -14,5 +14,5 @@
14
14
  # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
15
 
16
16
  module Grntest
17
- VERSION = "1.1.1"
17
+ VERSION = "1.1.2"
18
18
  end
@@ -63,7 +63,7 @@ module Grntest
63
63
  end
64
64
 
65
65
  class Worker
66
- attr_reader :id, :tester, :test_suites_rusult, :reporter
66
+ attr_reader :id, :tester, :reporter
67
67
  attr_reader :suite_name, :test_script_path, :test_name, :status, :result
68
68
  def initialize(id, tester, test_suites_result, reporter)
69
69
  @id = id
@@ -107,6 +107,9 @@ module Grntest
107
107
  succeeded = false unless runner.run
108
108
 
109
109
  break if interruptted?
110
+ if @tester.stop_on_failure? and @test_suites_result.have_failure?
111
+ break
112
+ end
110
113
  end
111
114
  @status = "finished"
112
115
  @reporter.on_suite_finish(@suite_name) if @suite_name
@@ -17,8 +17,8 @@ require "grntest/executors/base-executor"
17
17
 
18
18
  class TestBaseExecutor < Test::Unit::TestCase
19
19
  def setup
20
- @executor = Grntest::Executors::BaseExecutor.new
21
- @context = @executor.context
20
+ @context = Grntest::ExecutionContext.new
21
+ @executor = Grntest::Executors::BaseExecutor.new(@context)
22
22
  end
23
23
 
24
24
  class TestErrorLogLevel < self
@@ -21,8 +21,9 @@ class TestStandardIOExecutor < Test::Unit::TestCase
21
21
  def setup
22
22
  input = StringIO.new
23
23
  output = StringIO.new
24
- @executor = Grntest::Executors::StandardIOExecutor.new(input, output)
25
- @context = @executor.context
24
+ @context = Grntest::ExecutionContext.new
25
+ @executor = Grntest::Executors::StandardIOExecutor.new(input, output,
26
+ @context)
26
27
  @script = Tempfile.new("test-executor")
27
28
  end
28
29
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grntest
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kouhei Sutou
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-02-03 00:00:00.000000000 Z
12
+ date: 2015-07-08 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: json
@@ -204,8 +204,8 @@ specification_version: 4
204
204
  summary: Grntest is a testing framework for Groonga. You can write a test for Groonga
205
205
  by writing Groonga commands and expected result.
206
206
  test_files:
207
+ - test/test-log-parser.rb
207
208
  - test/executors/test-base-executor.rb
208
209
  - test/executors/test-standard-io-executor.rb
209
210
  - test/run-test.rb
210
- - test/test-log-parser.rb
211
211
  has_rdoc: