grntest 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.yardopts ADDED
@@ -0,0 +1,5 @@
1
+ --output-dir doc/reference/en
2
+ --markup markdown
3
+ --markup-provider redcarpet
4
+ -
5
+ doc/text/*
data/README.md CHANGED
@@ -212,6 +212,9 @@ Here are available `NAME` s:
212
212
  * `suggest-create-dataset`
213
213
  * `include`
214
214
  * `copy-path`
215
+ * `long-timeout`
216
+ * `on-error`
217
+ * `omit`
215
218
 
216
219
  `ARGUMENTS...` are depends on directive. A directive doesn't require
217
220
  any arguments but a directive requires arguments.
@@ -245,7 +248,7 @@ select Users --query _key:User29
245
248
 
246
249
  See also: `enable-logging`
247
250
 
248
- ### `enable-logging`
251
+ #### `enable-logging`
249
252
 
250
253
  Usage:
251
254
 
@@ -257,7 +260,7 @@ It enables logging that is disabled by `disable-logging` directive.
257
260
 
258
261
  See also: `disable-logging`
259
262
 
260
- ### suggest-create-dataset
263
+ #### `suggest-create-dataset`
261
264
 
262
265
  Usage:
263
266
 
@@ -284,7 +287,7 @@ load --table event_rurema --each 'suggest_preparer(_id, type, item, sequence, ti
284
287
 
285
288
  See also: `--groonga-suggest-create-dataset` option
286
289
 
287
- ### include
290
+ #### `include`
288
291
 
289
292
  Usage:
290
293
 
@@ -339,9 +342,9 @@ select Users --query _key:Alice
339
342
 
340
343
  See also: `--base-directory` option
341
344
 
342
- ### `copy-path`
345
+ #### `copy-path`
343
346
 
344
- Usage
347
+ Usage:
345
348
 
346
349
  ```
347
350
  #@copy-path SOURCE DESTINATION
@@ -357,6 +360,75 @@ Example:
357
360
  register "query_expanders/tsv"
358
361
  ```
359
362
 
363
+ #### `long-timeout`
364
+
365
+ Usage:
366
+
367
+ ```
368
+ #@long-timeout TIMEOUT
369
+ ```
370
+
371
+ It specifies a timeout for commands that may take long time.
372
+ `TIMEOUT` must be a number or `default`. If you specify `default` as
373
+ `TIMEOUT`, the default timeout is used. The default timeout is 180
374
+ seconds.
375
+
376
+ Here are the commands that may take long time:
377
+
378
+ * `column_create`
379
+ * `register`
380
+
381
+ Example:
382
+
383
+ ```
384
+ # Wait 300 seconds until commands that may take long time output their results
385
+ #@long-timeout 300
386
+ column_create Lexicon users_name COLUMN_INDEX Users name
387
+ # Reset custom timeout for commands that may take long time.
388
+ #@long-timeout default
389
+ ```
390
+
391
+ #### `on-error`
392
+
393
+ Usage:
394
+
395
+ ```
396
+ #@on-error ACTION
397
+ ```
398
+
399
+ It specifies a action on error. `ACTION` must be `default` or
400
+ `omit`. If you specify `default` as `ACTION`, the running test is
401
+ continued. If you specify `omit` as `ACTION`, the running test is
402
+ aborted and the running test is marked as omitted.
403
+
404
+ Example:
405
+
406
+ ```
407
+ # Omit this test if TokenKyTea tokenizer isn't available.
408
+ #@on-error omit
409
+ register tokenizers/kytea
410
+ # Don't omit this test when any error is occurred in the following commands.
411
+ #@on-error default
412
+ ```
413
+
414
+ #### `omit`
415
+
416
+ Usage:
417
+
418
+ ```
419
+ #@omit REASON
420
+ ```
421
+
422
+ Omit the test with `REASON`.
423
+
424
+ Example:
425
+
426
+ ```
427
+ # Omit this test until the feature is implemented.
428
+ #@omit "This feature is not implemented yet."
429
+ new_excelent_command
430
+ ```
431
+
360
432
  ## Options
361
433
 
362
434
  Grntest has many options. You don't need to specify many of them
data/doc/text/news.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # News
2
2
 
3
+ ## 1.0.2: 2012-12-11
4
+
5
+ This is the release that add some directive.
6
+
7
+ ### Improvements
8
+
9
+ * Used long timeout for `column_create` and `register`.
10
+ * Added `long-timeout` directive.
11
+ * Added `on-error` directive.
12
+ * Added "omit" status and `omit` directive.
13
+ * Aborted a test when a command in it can't be parsed by
14
+ Groonga::Command::Parser.
15
+
16
+ ### Fixes
17
+
18
+ * Used stty only when the standard input is tty.
19
+
3
20
  ## 1.0.1: 2012-10-15
4
21
 
5
22
  This has a backward incompatible change. It is directive syntax.
@@ -12,7 +29,7 @@ New:
12
29
 
13
30
  #@NAME ARGUMENT
14
31
 
15
- This change is for easy to debug. Conssider about we have a typo in
32
+ This change is for easy to debug. Consider about we have a typo in
16
33
  `NAME`. It is just ignored in old syntax because it is also a comment
17
34
  line. It is reported in new syntax because it syntax is only for
18
35
  directive. Grntest can know that user want to use a directive. If
data/grntest.gemspec CHANGED
@@ -35,6 +35,7 @@ Gem::Specification.new do |spec|
35
35
  spec.summary, spec.description, = description.split(/\n\n+/, 3)
36
36
  spec.license = "GPLv3 or later"
37
37
  spec.files = ["README.md", "Rakefile", "Gemfile", "#{spec.name}.gemspec"]
38
+ spec.files += [".yardopts"]
38
39
  spec.files += Dir.glob("lib/**/*.rb")
39
40
  spec.files += Dir.glob("doc/text/*")
40
41
  spec.test_files += Dir.glob("test/**/*")
@@ -42,12 +43,14 @@ Gem::Specification.new do |spec|
42
43
  spec.executables = Dir.glob("*")
43
44
  end
44
45
 
45
- spec.add_dependency("json")
46
- spec.add_dependency("msgpack")
46
+ spec.add_runtime_dependency("json")
47
+ spec.add_runtime_dependency("msgpack")
48
+ spec.add_runtime_dependency("groonga-command")
47
49
 
48
50
  spec.add_development_dependency("bundler")
49
51
  spec.add_development_dependency("rake")
50
52
  spec.add_development_dependency("test-unit")
53
+ spec.add_development_dependency("test-unit-rr")
51
54
  spec.add_development_dependency("packnga")
52
55
  spec.add_development_dependency("redcarpet")
53
56
  end
@@ -22,11 +22,12 @@ require "fileutils"
22
22
  require "tempfile"
23
23
  require "shellwords"
24
24
  require "open-uri"
25
- require "cgi/util"
26
25
 
27
26
  require "json"
28
27
  require "msgpack"
29
28
 
29
+ require "groonga/command"
30
+
30
31
  require "grntest/version"
31
32
 
32
33
  module Grntest
@@ -407,13 +408,14 @@ module Grntest
407
408
 
408
409
  class WorkerResult < Result
409
410
  attr_reader :n_tests, :n_passed_tests, :n_leaked_tests
410
- attr_reader :n_not_checked_tests
411
+ attr_reader :n_omitted_tests, :n_not_checked_tests
411
412
  attr_reader :failed_tests
412
413
  def initialize
413
414
  super
414
415
  @n_tests = 0
415
416
  @n_passed_tests = 0
416
417
  @n_leaked_tests = 0
418
+ @n_omitted_tests = 0
417
419
  @n_not_checked_tests = 0
418
420
  @failed_tests = []
419
421
  end
@@ -422,23 +424,27 @@ module Grntest
422
424
  @failed_tests.size
423
425
  end
424
426
 
425
- def test_finished
427
+ def on_test_finish
426
428
  @n_tests += 1
427
429
  end
428
430
 
429
- def test_passed
431
+ def on_test_success
430
432
  @n_passed_tests += 1
431
433
  end
432
434
 
433
- def test_failed(name)
435
+ def on_test_failure(name)
434
436
  @failed_tests << name
435
437
  end
436
438
 
437
- def test_leaked(name)
439
+ def on_test_leak(name)
438
440
  @n_leaked_tests += 1
439
441
  end
440
442
 
441
- def test_not_checked
443
+ def on_test_omission
444
+ @n_omitted_tests += 1
445
+ end
446
+
447
+ def on_test_no_check
442
448
  @n_not_checked_tests += 1
443
449
  end
444
450
  end
@@ -471,16 +477,16 @@ module Grntest
471
477
  succeeded = true
472
478
 
473
479
  @result.measure do
474
- @reporter.start_worker(self)
480
+ @reporter.on_worker_start(self)
475
481
  catch do |tag|
476
482
  loop do
477
483
  suite_name, test_script_path, test_name = queue.pop
478
484
  break if test_script_path.nil?
479
485
 
480
486
  unless @suite_name == suite_name
481
- @reporter.finish_suite(self) if @suite_name
487
+ @reporter.on_suite_finish(self) if @suite_name
482
488
  @suite_name = suite_name
483
- @reporter.start_suite(self)
489
+ @reporter.on_suite_start(self)
484
490
  end
485
491
  @test_script_path = test_script_path
486
492
  @test_name = test_name
@@ -490,48 +496,54 @@ module Grntest
490
496
  break if interruptted?
491
497
  end
492
498
  @status = "finished"
493
- @reporter.finish_suite(@suite_name) if @suite_name
499
+ @reporter.on_suite_finish(@suite_name) if @suite_name
494
500
  @suite_name = nil
495
501
  end
496
502
  end
497
- @reporter.finish_worker(self)
503
+ @reporter.on_worker_finish(self)
498
504
 
499
505
  succeeded
500
506
  end
501
507
 
502
- def start_test
508
+ def on_test_start
503
509
  @status = "running"
504
510
  @test_result = nil
505
- @reporter.start_test(self)
511
+ @reporter.on_test_start(self)
506
512
  end
507
513
 
508
- def pass_test(result)
514
+ def on_test_success(result)
509
515
  @status = "passed"
510
- @result.test_passed
511
- @reporter.pass_test(self, result)
516
+ @result.on_test_success
517
+ @reporter.on_test_success(self, result)
512
518
  end
513
519
 
514
- def fail_test(result)
520
+ def on_test_failure(result)
515
521
  @status = "failed"
516
- @result.test_failed(test_name)
517
- @reporter.fail_test(self, result)
522
+ @result.on_test_failure(test_name)
523
+ @reporter.on_test_failure(self, result)
518
524
  end
519
525
 
520
- def leaked_test(result)
526
+ def on_test_leak(result)
521
527
  @status = "leaked(#{result.n_leaked_objects})"
522
- @result.test_leaked(test_name)
523
- @reporter.leaked_test(self, result)
528
+ @result.on_test_leak(test_name)
529
+ @reporter.on_test_leak(self, result)
524
530
  end
525
531
 
526
- def not_checked_test(result)
532
+ def on_test_omission(result)
533
+ @status = "omitted"
534
+ @result.on_test_omission
535
+ @reporter.on_test_omission(self, result)
536
+ end
537
+
538
+ def on_test_no_check(result)
527
539
  @status = "not checked"
528
- @result.test_not_checked
529
- @reporter.not_checked_test(self, result)
540
+ @result.on_test_no_check
541
+ @reporter.on_test_no_check(self, result)
530
542
  end
531
543
 
532
- def finish_test(result)
533
- @result.test_finished
534
- @reporter.finish_test(self, result)
544
+ def on_test_finish(result)
545
+ @result.on_test_finish
546
+ @reporter.on_test_finish(self, result)
535
547
  @test_script_path = nil
536
548
  @test_name = nil
537
549
  end
@@ -571,6 +583,10 @@ module Grntest
571
583
  collect_count(:n_leaked_tests)
572
584
  end
573
585
 
586
+ def n_omitted_tests
587
+ collect_count(:n_omitted_tests)
588
+ end
589
+
574
590
  def n_not_checked_tests
575
591
  collect_count(:n_not_checked_tests)
576
592
  end
@@ -597,7 +613,7 @@ module Grntest
597
613
  @result.measure do
598
614
  succeeded = run_test_suites(test_suites)
599
615
  end
600
- @reporter.finish(@result)
616
+ @reporter.on_finish(@result)
601
617
 
602
618
  succeeded
603
619
  end
@@ -623,7 +639,7 @@ module Grntest
623
639
  workers << Worker.new(i, @tester, @result, @reporter)
624
640
  end
625
641
  @result.workers = workers
626
- @reporter.start(@result)
642
+ @reporter.on_start(@result)
627
643
 
628
644
  succeeded = true
629
645
  worker_threads = []
@@ -660,6 +676,7 @@ module Grntest
660
676
  class TestResult < Result
661
677
  attr_accessor :worker_id, :test_name
662
678
  attr_accessor :expected, :actual, :n_leaked_objects
679
+ attr_writer :omitted
663
680
  def initialize(worker)
664
681
  super()
665
682
  @worker_id = worker.id
@@ -667,9 +684,12 @@ module Grntest
667
684
  @actual = nil
668
685
  @expected = nil
669
686
  @n_leaked_objects = 0
687
+ @omitted = false
670
688
  end
671
689
 
672
690
  def status
691
+ return :omitted if omitted?
692
+
673
693
  if @expected
674
694
  if @actual == @expected
675
695
  if @n_leaked_objects.zero?
@@ -688,6 +708,51 @@ module Grntest
688
708
  end
689
709
  end
690
710
  end
711
+
712
+ def omitted?
713
+ @omitted
714
+ end
715
+ end
716
+
717
+ class ResponseParser
718
+ class << self
719
+ def parse(content, type)
720
+ parser = new(type)
721
+ parser.parse(content)
722
+ end
723
+ end
724
+
725
+ def initialize(type)
726
+ @type = type
727
+ end
728
+
729
+ def parse(content)
730
+ case @type
731
+ when "json", "msgpack"
732
+ parse_result(content.chomp)
733
+ else
734
+ content
735
+ end
736
+ end
737
+
738
+ def parse_result(result)
739
+ case @type
740
+ when "json"
741
+ begin
742
+ JSON.parse(result)
743
+ rescue JSON::ParserError
744
+ raise ParseError.new(@type, result, $!.message)
745
+ end
746
+ when "msgpack"
747
+ begin
748
+ MessagePack.unpack(result.chomp)
749
+ rescue MessagePack::UnpackError, NoMemoryError
750
+ raise ParseError.new(@type, result, $!.message)
751
+ end
752
+ else
753
+ raise ParseError.new(@type, result, "unknown type")
754
+ end
755
+ end
691
756
  end
692
757
 
693
758
  class TestRunner
@@ -703,35 +768,37 @@ module Grntest
703
768
  def run
704
769
  succeeded = true
705
770
 
706
- @worker.start_test
771
+ @worker.on_test_start
707
772
  result = TestResult.new(@worker)
708
773
  result.measure do
709
- result.actual = execute_groonga_script
774
+ execute_groonga_script(result)
710
775
  end
711
776
  normalize_actual_result(result)
712
777
  result.expected = read_expected_result
713
778
  case result.status
714
779
  when :success
715
- @worker.pass_test(result)
780
+ @worker.on_test_success(result)
716
781
  remove_reject_file
717
782
  when :failure
718
- @worker.fail_test(result)
783
+ @worker.on_test_failure(result)
719
784
  output_reject_file(result.actual)
720
785
  succeeded = false
721
786
  when :leaked
722
- @worker.leaked_test(result)
787
+ @worker.on_test_leak(result)
723
788
  succeeded = false
789
+ when :omitted
790
+ @worker.on_test_omission(result)
724
791
  else
725
- @worker.not_checked_test(result)
792
+ @worker.on_test_no_check(result)
726
793
  output_actual_file(result.actual)
727
794
  end
728
- @worker.finish_test(result)
795
+ @worker.on_test_finish(result)
729
796
 
730
797
  succeeded
731
798
  end
732
799
 
733
800
  private
734
- def execute_groonga_script
801
+ def execute_groonga_script(result)
735
802
  create_temporary_directory do |directory_path|
736
803
  if @tester.database_path
737
804
  db_path = Pathname(@tester.database_path).expand_path
@@ -751,7 +818,8 @@ module Grntest
751
818
  executor.execute(test_script_path)
752
819
  end
753
820
  check_memory_leak(context)
754
- context.result
821
+ result.omitted = context.omitted?
822
+ result.actual = context.result
755
823
  end
756
824
  end
757
825
 
@@ -781,11 +849,14 @@ module Grntest
781
849
  create_empty_database(context.db_path.to_s)
782
850
  end
783
851
 
784
- case @tester.interface
785
- when :stdio
786
- run_groonga_stdio(context, &block)
787
- when :http
788
- run_groonga_http(context, &block)
852
+ catch do |tag|
853
+ context.abort_tag = tag
854
+ case @tester.interface
855
+ when :stdio
856
+ run_groonga_stdio(context, &block)
857
+ when :http
858
+ run_groonga_http(context, &block)
859
+ end
789
860
  end
790
861
  end
791
862
 
@@ -1022,7 +1093,7 @@ EOF
1022
1093
  status = nil
1023
1094
  values = nil
1024
1095
  begin
1025
- status, *values = parse_result(content.chomp, type)
1096
+ status, *values = ResponseParser.parse(content.chomp, type)
1026
1097
  rescue ParseError
1027
1098
  return $!.message
1028
1099
  end
@@ -1038,25 +1109,6 @@ EOF
1038
1109
  end
1039
1110
  end
1040
1111
 
1041
- def parse_result(result, type)
1042
- case type
1043
- when "json"
1044
- begin
1045
- JSON.parse(result)
1046
- rescue JSON::ParserError
1047
- raise ParseError.new(type, result, $!.message)
1048
- end
1049
- when "msgpack"
1050
- begin
1051
- MessagePack.unpack(result.chomp)
1052
- rescue MessagePack::UnpackError, NoMemoryError
1053
- raise ParseError.new(type, result, $!.message)
1054
- end
1055
- else
1056
- raise ParseError.new(type, result, "unknown type")
1057
- end
1058
- end
1059
-
1060
1112
  def normalize_status(status)
1061
1113
  return_code, started_time, elapsed_time, *rest = status
1062
1114
  _ = started_time = elapsed_time # for suppress warnings
@@ -1135,6 +1187,8 @@ EOF
1135
1187
  attr_accessor :groonga_suggest_create_dataset
1136
1188
  attr_accessor :result
1137
1189
  attr_accessor :output_type
1190
+ attr_accessor :on_error
1191
+ attr_accessor :abort_tag
1138
1192
  def initialize
1139
1193
  @logging = true
1140
1194
  @base_directory = Pathname(".")
@@ -1145,6 +1199,9 @@ EOF
1145
1199
  @result = []
1146
1200
  @output_type = "json"
1147
1201
  @log = nil
1202
+ @on_error = :default
1203
+ @abort_tag = nil
1204
+ @omitted = false
1148
1205
  end
1149
1206
 
1150
1207
  def logging?
@@ -1173,6 +1230,30 @@ EOF
1173
1230
  def relative_db_path
1174
1231
  @db_path.relative_path_from(@temporary_directory_path)
1175
1232
  end
1233
+
1234
+ def omitted?
1235
+ @omitted
1236
+ end
1237
+
1238
+ def error
1239
+ case @on_error
1240
+ when :omit
1241
+ omit
1242
+ end
1243
+ end
1244
+
1245
+ def omit
1246
+ @omitted = true
1247
+ abort
1248
+ end
1249
+
1250
+ def abort
1251
+ throw @abort_tag
1252
+ end
1253
+ end
1254
+
1255
+ module ReturnCode
1256
+ SUCCESS = 0
1176
1257
  end
1177
1258
 
1178
1259
  attr_reader :context
@@ -1182,6 +1263,7 @@ EOF
1182
1263
  @pending_load_command = nil
1183
1264
  @current_command_name = nil
1184
1265
  @output_type = nil
1266
+ @long_timeout = default_long_timeout
1185
1267
  @context = context || Context.new
1186
1268
  end
1187
1269
 
@@ -1192,17 +1274,19 @@ EOF
1192
1274
 
1193
1275
  @context.execute do
1194
1276
  script_path.open("r:ascii-8bit") do |script_file|
1277
+ parser = create_parser
1195
1278
  script_file.each_line do |line|
1196
1279
  begin
1197
- if @loading
1198
- execute_line_on_loading(line)
1199
- else
1200
- execute_line_with_continuation_line_support(line)
1201
- end
1202
- rescue Error
1280
+ parser << line
1281
+ rescue Error, Groonga::Command::ParseError
1203
1282
  line_info = "#{script_path}:#{script_file.lineno}:#{line.chomp}"
1204
1283
  log_error("#{line_info}: #{$!.message}")
1205
- raise unless @context.top_level?
1284
+ if $!.is_a?(Groonga::Command::ParseError)
1285
+ @context.abort
1286
+ else
1287
+ log_error("#{line_info}: #{$!.message}")
1288
+ raise unless @context.top_level?
1289
+ end
1206
1290
  end
1207
1291
  end
1208
1292
  end
@@ -1212,42 +1296,21 @@ EOF
1212
1296
  end
1213
1297
 
1214
1298
  private
1215
- def execute_line_on_loading(line)
1216
- log_input(line)
1217
- @pending_load_command << line
1218
- if line == "]\n"
1219
- execute_command(@pending_load_command)
1220
- @pending_load_command = nil
1221
- @loading = false
1222
- end
1223
- end
1224
-
1225
- def execute_line_with_continuation_line_support(line)
1226
- if /\\$/ =~ line
1227
- @pending_command << $PREMATCH
1228
- else
1229
- if @pending_command.empty?
1230
- execute_line(line)
1231
- else
1232
- @pending_command << line
1233
- execute_line(@pending_command)
1234
- @pending_command = ""
1299
+ def create_parser
1300
+ parser = Groonga::Command::Parser.new
1301
+ parser.on_command do |command|
1302
+ execute_command(command)
1303
+ end
1304
+ parser.on_load_complete do |command|
1305
+ execute_command(command)
1306
+ end
1307
+ parser.on_comment do |comment|
1308
+ if /\A@/ =~ comment
1309
+ directive_content = $POSTMATCH
1310
+ execute_directive("\##{comment}", directive_content)
1235
1311
  end
1236
1312
  end
1237
- end
1238
-
1239
- def execute_line(line)
1240
- case line
1241
- when /\A\#@/
1242
- directive_content = $POSTMATCH
1243
- execute_directive(line, directive_content)
1244
- when /\A\s*\z/
1245
- # do nothing
1246
- when /\A\s*\#/
1247
- # ignore comment
1248
- else
1249
- execute_command_line(line)
1250
- end
1313
+ parser
1251
1314
  end
1252
1315
 
1253
1316
  def resolve_path(path)
@@ -1295,6 +1358,54 @@ EOF
1295
1358
  FileUtils.cp_r(source.to_s, destination.to_s)
1296
1359
  end
1297
1360
 
1361
+ def execute_directive_long_timeout(line, content, options)
1362
+ long_timeout, = options
1363
+ invalid_value_p = false
1364
+ case long_timeout
1365
+ when "default"
1366
+ @long_timeout = default_long_timeout
1367
+ when nil
1368
+ invalid_value_p = true
1369
+ else
1370
+ begin
1371
+ @long_timeout = Float(long_timeout)
1372
+ rescue ArgumentError
1373
+ invalid_value_p = true
1374
+ end
1375
+ end
1376
+
1377
+ if invalid_value_p
1378
+ log_input(line)
1379
+ message = "long-timeout must be number or 'default': <#{long_timeout}>"
1380
+ log_error("#|e| [long-timeout] #{message}")
1381
+ end
1382
+ end
1383
+
1384
+ def execute_directive_on_error(line, content, options)
1385
+ action, = options
1386
+ invalid_value_p = false
1387
+ valid_actions = ["default", "omit"]
1388
+ if valid_actions.include?(action)
1389
+ @context.on_error = action.to_sym
1390
+ else
1391
+ invalid_value_p = true
1392
+ end
1393
+
1394
+ if invalid_value_p
1395
+ log_input(line)
1396
+ valid_actions_label = "[#{valid_actions.join(', ')}]"
1397
+ message = "on-error must be one of #{valid_actions_label}"
1398
+ log_error("#|e| [on-error] #{message}: <#{action}>")
1399
+ end
1400
+ end
1401
+
1402
+ def execute_directive_omit(line, content, options)
1403
+ reason, = options
1404
+ @output_type = "raw"
1405
+ log_output("omit: #{reason}")
1406
+ @context.omit
1407
+ end
1408
+
1298
1409
  def execute_directive(line, content)
1299
1410
  command, *options = Shellwords.split(content)
1300
1411
  case command
@@ -1308,6 +1419,12 @@ EOF
1308
1419
  execute_directive_include(line, content, options)
1309
1420
  when "copy-path"
1310
1421
  execute_directive_copy_path(line, content, options)
1422
+ when "long-timeout"
1423
+ execute_directive_long_timeout(line, content, options)
1424
+ when "on-error"
1425
+ execute_directive_on_error(line, content, options)
1426
+ when "omit"
1427
+ execute_directive_omit(line, content, options)
1311
1428
  else
1312
1429
  log_input(line)
1313
1430
  log_error("#|e| unknown directive: <#{command}>")
@@ -1335,46 +1452,24 @@ EOF
1335
1452
  executor.execute(resolve_path(script_path))
1336
1453
  end
1337
1454
 
1338
- def execute_command_line(command_line)
1339
- extract_command_info(command_line)
1340
- log_input(command_line)
1341
- if multiline_load_command?
1342
- @loading = true
1343
- @pending_load_command = command_line.dup
1344
- else
1345
- execute_command(command_line)
1346
- end
1347
- end
1348
-
1349
- def extract_command_info(command_line)
1350
- @current_command, *@current_arguments = Shellwords.split(command_line)
1351
- if @current_command == "dump"
1455
+ def extract_command_info(command)
1456
+ @current_command = command
1457
+ if @current_command.name == "dump"
1352
1458
  @output_type = "groonga-command"
1353
1459
  else
1354
- @output_type = @context.output_type
1355
- @current_arguments.each_with_index do |word, i|
1356
- if /\A--output_type(?:=(.+))?\z/ =~ word
1357
- @output_type = $1 || words[i + 1]
1358
- break
1359
- end
1360
- end
1361
- end
1362
- end
1363
-
1364
- def have_output_type_argument?
1365
- @current_arguments.any? do |argument|
1366
- /\A--output_type(?:=.+)?\z/ =~ argument
1460
+ @output_type = @current_command[:output_type] || @context.output_type
1367
1461
  end
1368
1462
  end
1369
1463
 
1370
- def multiline_load_command?
1371
- @current_command == "load" and
1372
- not @current_arguments.include?("--values")
1373
- end
1374
-
1375
1464
  def execute_command(command)
1376
- log_output(send_command(command))
1465
+ extract_command_info(command)
1466
+ log_input("#{command.original_source}\n")
1467
+ response = send_command(command)
1468
+ type = @output_type
1469
+ log_output(response)
1377
1470
  log_error(read_error_log)
1471
+
1472
+ @context.error if error_response?(response, type)
1378
1473
  end
1379
1474
 
1380
1475
  def read_error_log
@@ -1396,8 +1491,10 @@ EOF
1396
1491
  timeout = first_timeout
1397
1492
  while IO.select([output], [], [], timeout)
1398
1493
  break if output.eof?
1399
- content << output.readpartial(65535)
1400
- timeout = 0
1494
+ request_bytes = 1024
1495
+ read_content = output.readpartial(request_bytes)
1496
+ content << read_content
1497
+ timeout = 0 if read_content.bytesize < request_bytes
1401
1498
  end
1402
1499
  content
1403
1500
  end
@@ -1410,6 +1507,18 @@ EOF
1410
1507
  message.start_with?("/")
1411
1508
  end
1412
1509
 
1510
+ def error_response?(response, type)
1511
+ status = nil
1512
+ begin
1513
+ status, = ResponseParser.parse(response, type)
1514
+ rescue ParseError
1515
+ return false
1516
+ end
1517
+
1518
+ return_code, = status
1519
+ return_code != ReturnCode::SUCCESS
1520
+ end
1521
+
1413
1522
  def log(tag, content, options={})
1414
1523
  return unless @context.logging?
1415
1524
  log_force(tag, content, options)
@@ -1435,6 +1544,10 @@ EOF
1435
1544
  def log_error(content)
1436
1545
  log_force(:error, content, {})
1437
1546
  end
1547
+
1548
+ def default_long_timeout
1549
+ 180
1550
+ end
1438
1551
  end
1439
1552
 
1440
1553
  class StandardIOExecutor < Executor
@@ -1444,12 +1557,14 @@ EOF
1444
1557
  @output = output
1445
1558
  end
1446
1559
 
1447
- def send_command(command_line)
1448
- unless have_output_type_argument?
1560
+ def send_command(command)
1561
+ command_line = @current_command.original_source
1562
+ unless @current_command.has_key?(:output_type)
1449
1563
  command_line = command_line.sub(/$/, " --output_type #{@output_type}")
1450
1564
  end
1451
1565
  begin
1452
1566
  @input.print(command_line)
1567
+ @input.print("\n")
1453
1568
  @input.flush
1454
1569
  rescue SystemCallError
1455
1570
  message = "failed to write to groonga: <#{command_line}>: #{$!}"
@@ -1470,7 +1585,17 @@ EOF
1470
1585
 
1471
1586
  private
1472
1587
  def read_output
1473
- read_all_readable_content(@output)
1588
+ options = {}
1589
+ options[:first_timeout] = @long_timeout if may_slow_command?
1590
+ read_all_readable_content(@output, options)
1591
+ end
1592
+
1593
+ MAY_SLOW_COMMANDS = [
1594
+ "column_create",
1595
+ "register",
1596
+ ]
1597
+ def may_slow_command?
1598
+ MAY_SLOW_COMMANDS.include?(@current_command)
1474
1599
  end
1475
1600
  end
1476
1601
 
@@ -1517,84 +1642,8 @@ EOF
1517
1642
  end
1518
1643
 
1519
1644
  def to_url
1520
- command = nil
1521
- arguments = nil
1522
- load_values = ""
1523
- @gqtp_command.each_line.with_index do |line, i|
1524
- if i.zero?
1525
- command, *arguments = Shellwords.split(line)
1526
- else
1527
- load_values << line
1528
- end
1529
- end
1530
- arguments.concat(["--values", load_values]) unless load_values.empty?
1531
-
1532
- named_arguments = convert_to_named_arguments(command, arguments)
1533
- build_url(command, named_arguments)
1534
- end
1535
-
1536
- private
1537
- def convert_to_named_arguments(command, arguments)
1538
- named_arguments = {}
1539
-
1540
- last_argument_name = nil
1541
- n_non_named_arguments = 0
1542
- arguments.each do |argument|
1543
- if /\A--/ =~ argument
1544
- last_argument_name = $POSTMATCH
1545
- next
1546
- end
1547
-
1548
- if last_argument_name.nil?
1549
- argument_name = arguments_name(command)[n_non_named_arguments]
1550
- n_non_named_arguments += 1
1551
- else
1552
- argument_name = last_argument_name
1553
- last_argument_name = nil
1554
- end
1555
-
1556
- named_arguments[argument_name] = argument
1557
- end
1558
-
1559
- named_arguments
1560
- end
1561
-
1562
- def arguments_name(command)
1563
- case command
1564
- when "table_create"
1565
- ["name", "flags", "key_type", "value_type", "default_tokenizer"]
1566
- when "column_create"
1567
- ["table", "name", "flags", "type", "source"]
1568
- when "load"
1569
- ["values", "table", "columns", "ifexists", "input_type"]
1570
- when "select"
1571
- ["table"]
1572
- when "suggest"
1573
- [
1574
- "types", "table", "column", "query", "sortby",
1575
- "output_columns", "offset", "limit", "frequency_threshold",
1576
- "conditional_probability_threshold", "prefix_search"
1577
- ]
1578
- when "truncate"
1579
- ["table"]
1580
- when "get"
1581
- ["table", "key", "output_columns", "id"]
1582
- else
1583
- nil
1584
- end
1585
- end
1586
-
1587
- def build_url(command, named_arguments)
1588
- url = "/d/#{command}"
1589
- query_parameters = []
1590
- named_arguments.each do |name, argument|
1591
- query_parameters << "#{CGI.escape(name)}=#{CGI.escape(argument)}"
1592
- end
1593
- unless query_parameters.empty?
1594
- url << "?"
1595
- url << query_parameters.join("&")
1596
- end
1597
- url
1645
+ command = Groonga::Command::Parser.parse(@gqtp_command)
1646
+ command.to_uri_format
1598
1647
  end
1599
1648
  end
1600
1649
 
@@ -1623,27 +1672,30 @@ EOF
1623
1672
  puts(colorize(summary, result))
1624
1673
  end
1625
1674
 
1626
- def statistics_header
1627
- items = [
1628
- "tests/sec",
1629
- "tests",
1630
- "passes",
1631
- "failures",
1632
- "leaked",
1633
- "!checked",
1675
+ def columns
1676
+ [
1677
+ # label, format value
1678
+ ["tests/sec", lambda {|result| "%9.2f" % throughput(result)}],
1679
+ [" tests", lambda {|result| "%8d" % result.n_tests}],
1680
+ [" passes", lambda {|result| "%8d" % result.n_passed_tests}],
1681
+ ["failures", lambda {|result| "%8d" % result.n_failed_tests}],
1682
+ [" leaked", lambda {|result| "%8d" % result.n_leaked_tests}],
1683
+ [" omitted", lambda {|result| "%8d" % result.n_omitted_tests}],
1684
+ ["!checked", lambda {|result| "%8d" % result.n_not_checked_tests}],
1634
1685
  ]
1635
- " " + ((["%-9s"] * items.size).join(" | ") % items) + " |"
1686
+ end
1687
+
1688
+ def statistics_header
1689
+ labels = columns.collect do |label, format_value|
1690
+ label
1691
+ end
1692
+ " " + labels.join(" | ") + " |"
1636
1693
  end
1637
1694
 
1638
1695
  def statistics(result)
1639
- items = [
1640
- "%9.2f" % throughput(result),
1641
- "%9d" % result.n_tests,
1642
- "%9d" % result.n_passed_tests,
1643
- "%9d" % result.n_failed_tests,
1644
- "%9d" % result.n_leaked_tests,
1645
- "%9d" % result.n_not_checked_tests,
1646
- ]
1696
+ items = columns.collect do |label, format_value|
1697
+ format_value.call(result)
1698
+ end
1647
1699
  " " + items.join(" | ") + " |"
1648
1700
  end
1649
1701
 
@@ -1764,7 +1816,9 @@ EOF
1764
1816
  end
1765
1817
 
1766
1818
  def guess_term_width_from_stty
1767
- case `stty -a`
1819
+ return nil unless STDIN.tty?
1820
+
1821
+ case tty_info
1768
1822
  when /(\d+) columns/
1769
1823
  $1
1770
1824
  when /columns (\d+)/
@@ -1772,8 +1826,14 @@ EOF
1772
1826
  else
1773
1827
  nil
1774
1828
  end
1775
- rescue SystemCallError
1776
- nil
1829
+ end
1830
+
1831
+ def tty_info
1832
+ begin
1833
+ `stty -a`
1834
+ rescue SystemCallError
1835
+ nil
1836
+ end
1777
1837
  end
1778
1838
 
1779
1839
  def string_width(string)
@@ -1788,6 +1848,8 @@ EOF
1788
1848
  :failure
1789
1849
  elsif result.n_leaked_tests > 0
1790
1850
  :leaked
1851
+ elsif result.n_omitted_tests > 0
1852
+ :omitted
1791
1853
  elsif result.n_not_checked_tests > 0
1792
1854
  :not_checked
1793
1855
  else
@@ -1810,6 +1872,8 @@ EOF
1810
1872
  "%s%s%s" % [failure_color, message, reset_color]
1811
1873
  when :leaked
1812
1874
  "%s%s%s" % [leaked_color, message, reset_color]
1875
+ when :omitted
1876
+ "%s%s%s" % [omitted_color, message, reset_color]
1813
1877
  when :not_checked
1814
1878
  "%s%s%s" % [not_checked_color, message, reset_color]
1815
1879
  else
@@ -1856,6 +1920,19 @@ EOF
1856
1920
  })
1857
1921
  end
1858
1922
 
1923
+ def omitted_color
1924
+ escape_sequence({
1925
+ :color => :blue,
1926
+ :color_256 => [0, 0, 1],
1927
+ :background => true,
1928
+ },
1929
+ {
1930
+ :color => :white,
1931
+ :color_256 => [5, 5, 5],
1932
+ :bold => true,
1933
+ })
1934
+ end
1935
+
1859
1936
  def not_checked_color
1860
1937
  escape_sequence({
1861
1938
  :color => :cyan,
@@ -1930,25 +2007,25 @@ EOF
1930
2007
  super
1931
2008
  end
1932
2009
 
1933
- def start(result)
2010
+ def on_start(result)
1934
2011
  end
1935
2012
 
1936
- def start_worker(worker)
2013
+ def on_worker_start(worker)
1937
2014
  end
1938
2015
 
1939
- def start_suite(worker)
2016
+ def on_suite_start(worker)
1940
2017
  end
1941
2018
 
1942
- def start_test(worker)
2019
+ def on_test_start(worker)
1943
2020
  end
1944
2021
 
1945
- def pass_test(worker, result)
2022
+ def on_test_success(worker, result)
1946
2023
  synchronize do
1947
2024
  report_test_result_mark(".", result)
1948
2025
  end
1949
2026
  end
1950
2027
 
1951
- def fail_test(worker, result)
2028
+ def on_test_failure(worker, result)
1952
2029
  synchronize do
1953
2030
  report_test_result_mark("F", result)
1954
2031
  puts
@@ -1957,13 +2034,22 @@ EOF
1957
2034
  end
1958
2035
  end
1959
2036
 
1960
- def leaked_test(worker, result)
2037
+ def on_test_leak(worker, result)
1961
2038
  synchronize do
1962
2039
  report_test_result_mark("L(#{result.n_leaked_objects})", result)
1963
2040
  end
1964
2041
  end
1965
2042
 
1966
- def not_checked_test(worker, result)
2043
+ def on_test_omission(worker, result)
2044
+ synchronize do
2045
+ report_test_result_mark("O", result)
2046
+ puts
2047
+ report_test(worker, result)
2048
+ report_actual(result)
2049
+ end
2050
+ end
2051
+
2052
+ def on_test_no_check(worker, result)
1967
2053
  synchronize do
1968
2054
  report_test_result_mark("N", result)
1969
2055
  puts
@@ -1972,16 +2058,16 @@ EOF
1972
2058
  end
1973
2059
  end
1974
2060
 
1975
- def finish_test(worker, result)
2061
+ def on_test_finish(worker, result)
1976
2062
  end
1977
2063
 
1978
- def finish_suite(worker)
2064
+ def on_suite_finish(worker)
1979
2065
  end
1980
2066
 
1981
- def finish_worker(worker_id)
2067
+ def on_worker_finish(worker_id)
1982
2068
  end
1983
2069
 
1984
- def finish(result)
2070
+ def on_finish(result)
1985
2071
  puts
1986
2072
  puts
1987
2073
  report_summary(result)
@@ -2006,13 +2092,13 @@ EOF
2006
2092
  super
2007
2093
  end
2008
2094
 
2009
- def start(result)
2095
+ def on_start(result)
2010
2096
  end
2011
2097
 
2012
- def start_worker(worker)
2098
+ def on_worker_start(worker)
2013
2099
  end
2014
2100
 
2015
- def start_suite(worker)
2101
+ def on_suite_start(worker)
2016
2102
  if worker.suite_name.bytesize <= @term_width
2017
2103
  puts(worker.suite_name)
2018
2104
  else
@@ -2021,39 +2107,44 @@ EOF
2021
2107
  @output.flush
2022
2108
  end
2023
2109
 
2024
- def start_test(worker)
2110
+ def on_test_start(worker)
2025
2111
  print(" #{worker.test_name}")
2026
2112
  @output.flush
2027
2113
  end
2028
2114
 
2029
- def pass_test(worker, result)
2115
+ def on_test_success(worker, result)
2030
2116
  report_test_result(result, worker.status)
2031
2117
  end
2032
2118
 
2033
- def fail_test(worker, result)
2119
+ def on_test_failure(worker, result)
2034
2120
  report_test_result(result, worker.status)
2035
2121
  report_failure(result)
2036
2122
  end
2037
2123
 
2038
- def leaked_test(worker, result)
2124
+ def on_test_leak(worker, result)
2125
+ report_test_result(result, worker.status)
2126
+ end
2127
+
2128
+ def on_test_omission(worker, result)
2039
2129
  report_test_result(result, worker.status)
2130
+ report_actual(result)
2040
2131
  end
2041
2132
 
2042
- def not_checked_test(worker, result)
2133
+ def on_test_no_check(worker, result)
2043
2134
  report_test_result(result, worker.status)
2044
2135
  report_actual(result)
2045
2136
  end
2046
2137
 
2047
- def finish_test(worker, result)
2138
+ def on_test_finish(worker, result)
2048
2139
  end
2049
2140
 
2050
- def finish_suite(worker)
2141
+ def on_suite_finish(worker)
2051
2142
  end
2052
2143
 
2053
- def finish_worker(worker_id)
2144
+ def on_worker_finish(worker_id)
2054
2145
  end
2055
2146
 
2056
- def finish(result)
2147
+ def on_finish(result)
2057
2148
  puts
2058
2149
  report_summary(result)
2059
2150
  end
@@ -2066,59 +2157,66 @@ EOF
2066
2157
  @minimum_redraw_interval = 0.1
2067
2158
  end
2068
2159
 
2069
- def start(result)
2160
+ def on_start(result)
2070
2161
  @test_suites_result = result
2071
2162
  end
2072
2163
 
2073
- def start_worker(worker)
2164
+ def on_worker_start(worker)
2074
2165
  end
2075
2166
 
2076
- def start_suite(worker)
2167
+ def on_suite_start(worker)
2077
2168
  redraw
2078
2169
  end
2079
2170
 
2080
- def start_test(worker)
2171
+ def on_test_start(worker)
2081
2172
  redraw
2082
2173
  end
2083
2174
 
2084
- def pass_test(worker, result)
2175
+ def on_test_success(worker, result)
2085
2176
  redraw
2086
2177
  end
2087
2178
 
2088
- def fail_test(worker, result)
2179
+ def on_test_failure(worker, result)
2089
2180
  redraw do
2090
2181
  report_test(worker, result)
2091
2182
  report_failure(result)
2092
2183
  end
2093
2184
  end
2094
2185
 
2095
- def leaked_test(worker, result)
2186
+ def on_test_leak(worker, result)
2096
2187
  redraw do
2097
2188
  report_test(worker, result)
2098
2189
  report_marker(result)
2099
2190
  end
2100
2191
  end
2101
2192
 
2102
- def not_checked_test(worker, result)
2193
+ def on_test_omission(worker, result)
2194
+ redraw do
2195
+ report_test(worker, result)
2196
+ report_actual(result)
2197
+ end
2198
+ end
2199
+
2200
+ def on_test_no_check(worker, result)
2103
2201
  redraw do
2104
2202
  report_test(worker, result)
2105
2203
  report_actual(result)
2106
2204
  end
2107
2205
  end
2108
2206
 
2109
- def finish_test(worker, result)
2207
+ def on_test_finish(worker, result)
2110
2208
  redraw
2111
2209
  end
2112
2210
 
2113
- def finish_suite(worker)
2211
+ def on_suite_finish(worker)
2114
2212
  redraw
2115
2213
  end
2116
2214
 
2117
- def finish_worker(worker)
2215
+ def on_worker_finish(worker)
2118
2216
  redraw
2119
2217
  end
2120
2218
 
2121
- def finish(result)
2219
+ def on_finish(result)
2122
2220
  draw
2123
2221
  puts
2124
2222
  report_summary(result)