rims 0.2.6 → 0.2.7

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/lib/rims/test.rb CHANGED
@@ -68,29 +68,30 @@ module RIMS
68
68
  end
69
69
 
70
70
  module ProtocolFetchMailSample
71
- def make_mail_simple
72
- @simple_mail_body = <<-'EOF'
71
+ simple_mail_body = <<-'EOF'.freeze
73
72
  Hello world.
74
- EOF
73
+ EOF
75
74
 
76
- md5_digest = Digest::MD5.digest(@simple_mail_body)
77
- @simple_mail = RIMS::RFC822::Message.new(<<-"EOF" + @simple_mail_body)
75
+ MAIL_SIMPLE_TEXT = (<<-"EOF" + simple_mail_body).freeze
78
76
  To: foo@nonet.org
79
77
  From: bar@nonet.org
80
78
  Subject: test
81
79
  MIME-Version: 1.0
82
80
  Content-Type: text/plain; charset=us-ascii
83
81
  Content-Transfer-Encoding: 7bit
84
- Content-MD5: #{[ md5_digest ].pack('m').strip}
82
+ Content-MD5: #{[ Digest::MD5.digest(simple_mail_body) ].pack('m').strip}
85
83
  Content-Language: en-US, en
86
84
  Date: Fri, 8 Nov 2013 06:47:50 +0900 (JST)
87
85
 
88
- EOF
86
+ EOF
87
+
88
+ def make_mail_simple
89
+ @simple_mail = RIMS::RFC822::Message.new(MAIL_SIMPLE_TEXT)
90
+ @simple_mail_body = @simple_mail.body.raw_source
89
91
  end
90
92
  private :make_mail_simple
91
93
 
92
- def make_mail_multipart
93
- @mpart_mail = RIMS::RFC822::Message.new(<<-'EOF')
94
+ MPART_MAIL_TEXT = <<-'EOF'.freeze
94
95
  To: bar@nonet.com
95
96
  From: foo@nonet.com
96
97
  Subject: multipart test
@@ -173,12 +174,14 @@ Content-Location: baz
173
174
  --1383.905529.351300--
174
175
  --1383.905529.351299--
175
176
  --1383.905529.351297--
176
- EOF
177
+ EOF
178
+
179
+ def make_mail_multipart
180
+ @mpart_mail = RIMS::RFC822::Message.new(MPART_MAIL_TEXT)
177
181
  end
178
182
  private :make_mail_multipart
179
183
 
180
- def make_mail_mime_subject
181
- @mime_subject_mail = RIMS::RFC822::Message.new(<<-'EOF')
184
+ MAIL_MIME_SUBJECT_TEXT = <<-'EOF'.freeze
182
185
  Date: Fri, 8 Nov 2013 19:31:03 +0900
183
186
  Subject: =?ISO-2022-JP?B?GyRCJEYkOSRIGyhC?=
184
187
  From: foo@nonet.com, bar <bar@nonet.com>
@@ -194,22 +197,28 @@ In-Reply-To: <20131106081723.5KJU1774292@smtp.test.com>
194
197
  Message-Id: <20131107214750.445A1255B9F@smtp.nonet.com>
195
198
 
196
199
  Hello world.
197
- EOF
200
+ EOF
201
+
202
+ def make_mail_mime_subject
203
+ @mime_subject_mail = RIMS::RFC822::Message.new(MAIL_MIME_SUBJECT_TEXT)
198
204
  end
199
205
  private :make_mail_mime_subject
200
206
 
207
+ MAIL_EMPTY_TEXT = ''.freeze
208
+
201
209
  def make_mail_empty
202
- @empty_mail = RIMS::RFC822::Message.new('')
210
+ @empty_mail = RIMS::RFC822::Message.new(MAIL_EMPTY_TEXT)
203
211
  end
204
212
  private :make_mail_empty
205
213
 
214
+ MAIL_NO_BODY_TEXT = "Subject: foo\r\n\r\n".freeze
215
+
206
216
  def make_mail_no_body
207
- @no_body_mail = RIMS::RFC822::Message.new("Subject: foo\r\n\r\n")
217
+ @no_body_mail = RIMS::RFC822::Message.new(MAIL_NO_BODY_TEXT)
208
218
  end
209
219
  private :make_mail_no_body
210
220
 
211
- def make_mail_address_header_pattern
212
- @address_header_pattern_mail = RIMS::RFC822::Message.new(<<-'EOF')
221
+ MAIL_ADDRESS_HEADER_PATTERN_TEXT = <<-'EOF'.freeze
213
222
  To: "foo@nonet.org" <foo@nonet.org>
214
223
  From: bar@nonet.org
215
224
  Subject: test
@@ -219,7 +228,10 @@ Content-Transfer-Encoding: 7bit
219
228
  Date: Fri, 8 Nov 2013 06:47:50 +0900 (JST)
220
229
 
221
230
  Hello world.
222
- EOF
231
+ EOF
232
+
233
+ def make_mail_address_header_pattern
234
+ @address_header_pattern_mail = RIMS::RFC822::Message.new(MAIL_ADDRESS_HEADER_PATTERN_TEXT)
223
235
  end
224
236
  private :make_mail_address_header_pattern
225
237
  end
data/lib/rims/version.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
3
  module RIMS
4
- VERSION = '0.2.6'
4
+ VERSION = '0.2.7'
5
5
  MAILBOX_DATA_STRUCTURE_VERSION = 'mailbox.2'
6
6
  end
7
7
 
data/rims.gemspec CHANGED
@@ -26,7 +26,7 @@ Gem::Specification.new do |spec|
26
26
 
27
27
  spec.required_ruby_version = '>= 2.0.0'
28
28
 
29
- spec.add_runtime_dependency "rims-rfc822", '>= 0.2.0'
29
+ spec.add_runtime_dependency "rims-rfc822", '>= 0.2.2'
30
30
  spec.add_runtime_dependency "riser", '>= 0.1.8'
31
31
  spec.add_runtime_dependency "logger-joint"
32
32
  spec.add_development_dependency "bundler"
@@ -140,6 +140,18 @@ module RIMS::Test
140
140
  '--read-polling-interval' => [ %W[ -f #{BASE_DIR}/config.yml --read-polling-interval=5 ] ],
141
141
  '--command-wait-timeout' => [ %W[ -f #{BASE_DIR}/config.yml --command-wait-timeout=3600 ] ],
142
142
 
143
+ # charset aliases:
144
+ '--use-default-charset-aliases' => [ %W[ -f #{BASE_DIR}/config.yml --use-default-charset-aliases ] ],
145
+ '--no-use-default-charset-aliases' => [ %W[ -f #{BASE_DIR}/config.yml --no-use-default-charset-aliases ] ],
146
+ '--add-charset-alias' => [ %W[ -f #{BASE_DIR}/config.yml --no-use-default-charset-aliases --add-charset-alias=iso-2022-jp,CP50221 ] ],
147
+
148
+ # charset convert_options:
149
+ '--replace-charset-invalid' => [ %W[ -f #{BASE_DIR}/config.yml --replace-charset-invalid ] ],
150
+ '--no-replace-charset-invalid' => [ %W[ -f #{BASE_DIR}/config.yml --no-replace-charset-invalid ] ],
151
+ '--replace-charset-undef' => [ %W[ -f #{BASE_DIR}/config.yml --replace-charset-undef ] ],
152
+ '--no-replace-charset-undef' => [ %W[ -f #{BASE_DIR}/config.yml --no-replace-charset-undef ] ],
153
+ '--charset-replaced-mark' => [ %W[ -f #{BASE_DIR}/config.yml --charset-replaced-mark=? ] ],
154
+
143
155
  # drb_services:
144
156
  '--drb-process-num' => [ %W[ -f #{BASE_DIR}/config.yml --drb-process-num=4 ] ],
145
157
 
@@ -1232,7 +1244,7 @@ Hello world.
1232
1244
  assert_equal(seqno[1], imap_search.call([ 'ANSWERED' ])) # *a
1233
1245
  assert_equal(seqno[3], imap_search.call([ 'BCC', 'foo' ])) # *b
1234
1246
  assert_equal(seqno[1], imap_search.call([ 'BEFORE', @mpart_mail.date ]))
1235
- assert_equal(seqno[1, 3], imap_search.call([ 'BODY', 'Hello world.' ]))
1247
+ assert_equal(seqno[1, 2, 3], imap_search.call([ 'BODY', 'Hello world.' ]))
1236
1248
  assert_equal(seqno[3], imap_search.call([ 'CC', 'kate' ]))
1237
1249
  assert_equal(seqno[], imap_search.call([ 'DELETED' ]))
1238
1250
  assert_equal(seqno[2], imap_search.call([ 'DRAFT' ]))
@@ -293,13 +293,17 @@ module RIMS::Test
293
293
  @unique_user_id = RIMS::Authentication.unique_user_id('foo')
294
294
 
295
295
  @bulk_response_count = 10
296
+ @charset_aliases = RIMS::RFC822::CharsetAliases.new
297
+ @charset_convert_options = {}
296
298
  @drb_services = Riser::DRbServices.new(0)
297
299
  @drb_services.add_sticky_process_service(:engine,
298
300
  Riser::ResourceSet.build{|builder|
299
301
  builder.at_create{|unique_user_id|
300
302
  mail_store = RIMS::MailStore.build(unique_user_id, @kvs_open, @kvs_open)
301
303
  RIMS::Protocol::Decoder::Engine.new(unique_user_id, mail_store, @logger,
302
- bulk_response_count: @bulk_response_count)
304
+ bulk_response_count: @bulk_response_count,
305
+ charset_aliases: @charset_aliases,
306
+ charset_convert_options: @charset_convert_options)
303
307
  }
304
308
  builder.at_destroy{|engine|
305
309
  engine.destroy
@@ -2869,7 +2873,8 @@ module RIMS::Test
2869
2873
  test_search_charset_skip_encoding_error
2870
2874
  end
2871
2875
 
2872
- def test_search_interrupt_by_bad_response
2876
+ # skip this test because `EncodingError' is ignored.
2877
+ def _test_search_interrupt_by_bad_response
2873
2878
  imap_decode_engine_evaluate{
2874
2879
  if (stream_test?) then
2875
2880
  assert_untagged_response{|assert|
@@ -2915,7 +2920,7 @@ module RIMS::Test
2915
2920
  assert_equal(true, @decoder.selected?)
2916
2921
  end
2917
2922
 
2918
- assert_imap_command('SEARCH TEXT foo') {|assert|
2923
+ assert_imap_command('SEARCH CHARSET utf-8 TEXT foo') {|assert|
2919
2924
  assert.match(/\A\* SEARCH( \d+)+\r\n\z/, peek_next_line: true).no_match(/ #{@bulk_response_count.succ}/)
2920
2925
  assert.equal("#{tag} BAD internal server error\r\n")
2921
2926
  }
@@ -2927,7 +2932,8 @@ module RIMS::Test
2927
2932
  }
2928
2933
  end
2929
2934
 
2930
- def test_search_interrupt_by_bad_response_stream
2935
+ # skip this test because `EncodingError' is ignored.
2936
+ def _test_search_interrupt_by_bad_response_stream
2931
2937
  use_imap_stream_decode_engine
2932
2938
  test_search_interrupt_by_bad_response
2933
2939
  end
@@ -6175,6 +6181,168 @@ module RIMS::Test
6175
6181
  use_imap_stream_decode_engine
6176
6182
  test_idle_untagged_server_response
6177
6183
  end
6184
+
6185
+ def test_charset_aliases
6186
+ imap_decode_engine_evaluate{
6187
+ if (stream_test?) then
6188
+ assert_untagged_response{|assert|
6189
+ assert.equal("* OK RIMS v#{RIMS::VERSION} IMAP4rev1 service ready.")
6190
+ }
6191
+ end
6192
+
6193
+ platform_dependent_character = "\u2460"
6194
+ open_mail_store{
6195
+ add_msg("Content-Type: text/plain; charset=iso-2022-jp\r\n" +
6196
+ "\r\n" +
6197
+ "#{platform_dependent_character.encode(Encoding::CP50221).b}")
6198
+ }
6199
+
6200
+ if (command_test?) then
6201
+ assert_equal(false, @decoder.auth?)
6202
+ assert_equal(false, @decoder.selected?)
6203
+ end
6204
+
6205
+ assert_imap_command('LOGIN foo open_sesame') {|assert|
6206
+ assert.equal("#{tag} OK LOGIN completed")
6207
+ }
6208
+
6209
+ if (command_test?) then
6210
+ assert_equal(true, @decoder.auth?)
6211
+ assert_equal(false, @decoder.selected?)
6212
+ end
6213
+
6214
+ assert_imap_command('SELECT INBOX') {|assert|
6215
+ assert.skip_while{|line| line =~ /^\* /}
6216
+ assert.equal("#{tag} OK [READ-WRITE] SELECT completed")
6217
+ }
6218
+
6219
+ if (command_test?) then
6220
+ assert_equal(true, @decoder.auth?)
6221
+ assert_equal(true, @decoder.selected?)
6222
+ end
6223
+
6224
+ assert_imap_command("SEARCH CHARSET utf-8 TEXT #{literal(platform_dependent_character)}") {|assert|
6225
+ assert.match(/^\+ /)
6226
+ assert.equal("* SEARCH\r\n") # skip by `EncodingError'
6227
+ assert.equal("#{tag} OK SEARCH completed\r\n")
6228
+ }
6229
+
6230
+ # not recommend changing charset aliases after passing them to the decoder.
6231
+ # here, the charset aliases are reluctantly changed for testing.
6232
+ @charset_aliases.add_alias('iso-2022-jp', Encoding::CP50221)
6233
+
6234
+ assert_imap_command("SEARCH CHARSET utf-8 TEXT #{literal(platform_dependent_character)}") {|assert|
6235
+ assert.match(/^\+ /)
6236
+ assert.equal("* SEARCH 1\r\n") # matched!
6237
+ assert.equal("#{tag} OK SEARCH completed\r\n")
6238
+ }
6239
+
6240
+ assert_imap_command('CLOSE') {|assert|
6241
+ assert.equal("#{tag} OK CLOSE completed")
6242
+ }
6243
+
6244
+ if (command_test?) then
6245
+ assert_equal(true, @decoder.auth?)
6246
+ assert_equal(false, @decoder.selected?)
6247
+ end
6248
+
6249
+ assert_imap_command('LOGOUT') {|assert|
6250
+ assert.match(/^\* BYE /)
6251
+ assert.equal("#{tag} OK LOGOUT completed")
6252
+ }
6253
+
6254
+ if (command_test?) then
6255
+ assert_equal(false, @decoder.auth?)
6256
+ assert_equal(false, @decoder.selected?)
6257
+ end
6258
+ }
6259
+ end
6260
+
6261
+ def test_charset_aliases_stream
6262
+ use_imap_stream_decode_engine
6263
+ test_charset_aliases
6264
+ end
6265
+
6266
+ def test_charset_convert_options
6267
+ imap_decode_engine_evaluate{
6268
+ if (stream_test?) then
6269
+ assert_untagged_response{|assert|
6270
+ assert.equal("* OK RIMS v#{RIMS::VERSION} IMAP4rev1 service ready.")
6271
+ }
6272
+ end
6273
+
6274
+ platform_dependent_character = "\u2460"
6275
+ open_mail_store{
6276
+ add_msg("Content-Type: text/plain; charset=iso-2022-jp\r\n" +
6277
+ "\r\n" +
6278
+ "#{platform_dependent_character.encode(Encoding::CP50221).b}")
6279
+ }
6280
+
6281
+ if (command_test?) then
6282
+ assert_equal(false, @decoder.auth?)
6283
+ assert_equal(false, @decoder.selected?)
6284
+ end
6285
+
6286
+ assert_imap_command('LOGIN foo open_sesame') {|assert|
6287
+ assert.equal("#{tag} OK LOGIN completed")
6288
+ }
6289
+
6290
+ if (command_test?) then
6291
+ assert_equal(true, @decoder.auth?)
6292
+ assert_equal(false, @decoder.selected?)
6293
+ end
6294
+
6295
+ assert_imap_command('SELECT INBOX') {|assert|
6296
+ assert.skip_while{|line| line =~ /^\* /}
6297
+ assert.equal("#{tag} OK [READ-WRITE] SELECT completed")
6298
+ }
6299
+
6300
+ if (command_test?) then
6301
+ assert_equal(true, @decoder.auth?)
6302
+ assert_equal(true, @decoder.selected?)
6303
+ end
6304
+
6305
+ assert_imap_command(%Q'SEARCH CHARSET utf-8 TEXT #{literal("\uFFFD")}') {|assert|
6306
+ assert.match(/^\+ /)
6307
+ assert.equal("* SEARCH\r\n") # skip by `EncodingError'
6308
+ assert.equal("#{tag} OK SEARCH completed\r\n")
6309
+ }
6310
+
6311
+ # not recommend changing charset convert options after passing them to the decoder.
6312
+ # here, the options are reluctantly changed for testing.
6313
+ @charset_convert_options[:undef] = :replace
6314
+
6315
+ assert_imap_command("SEARCH CHARSET utf-8 TEXT #{literal("\uFFFD")}") {|assert|
6316
+ assert.match(/^\+ /)
6317
+ assert.equal("* SEARCH 1\r\n") # matched!
6318
+ assert.equal("#{tag} OK SEARCH completed\r\n")
6319
+ }
6320
+
6321
+ assert_imap_command('CLOSE') {|assert|
6322
+ assert.equal("#{tag} OK CLOSE completed")
6323
+ }
6324
+
6325
+ if (command_test?) then
6326
+ assert_equal(true, @decoder.auth?)
6327
+ assert_equal(false, @decoder.selected?)
6328
+ end
6329
+
6330
+ assert_imap_command('LOGOUT') {|assert|
6331
+ assert.match(/^\* BYE /)
6332
+ assert.equal("#{tag} OK LOGOUT completed")
6333
+ }
6334
+
6335
+ if (command_test?) then
6336
+ assert_equal(false, @decoder.auth?)
6337
+ assert_equal(false, @decoder.selected?)
6338
+ end
6339
+ }
6340
+ end
6341
+
6342
+ def test_charset_convert_options_stream
6343
+ use_imap_stream_decode_engine
6344
+ test_charset_aliases
6345
+ end
6178
6346
  end
6179
6347
 
6180
6348
  class ProtocolMailDeliveryDecoderTest < Test::Unit::TestCase
@@ -516,25 +516,27 @@ module RIMS::Test
516
516
  assert_fetch(2,
517
517
  [
518
518
  'BODY ' +
519
- encode_bodystructure([ 'APPLICATION',
520
- 'OCTET-STREAM',
519
+ encode_bodystructure([ 'TEXT',
520
+ 'PLAIN',
521
521
  nil,
522
522
  nil,
523
523
  nil,
524
524
  nil,
525
- @empty_mail.raw_source.bytesize
525
+ @empty_mail.raw_source.bytesize,
526
+ @empty_mail.raw_source.each_line.count
526
527
  ])
527
528
  ])
528
529
  assert_fetch(3,
529
530
  [
530
531
  'BODY ' +
531
- encode_bodystructure([ 'APPLICATION',
532
- 'OCTET-STREAM',
532
+ encode_bodystructure([ 'TEXT',
533
+ 'PLAIN',
533
534
  nil,
534
535
  nil,
535
536
  nil,
536
537
  nil,
537
- @no_body_mail.raw_source.bytesize
538
+ @no_body_mail.raw_source.bytesize,
539
+ @no_body_mail.raw_source.each_line.count
538
540
  ])
539
541
  ])
540
542
  }
@@ -654,13 +656,14 @@ module RIMS::Test
654
656
  ])
655
657
  assert_fetch(2, [
656
658
  'BODYSTRUCTURE ' +
657
- encode_bodystructure([ 'APPLICATION',
658
- 'OCTET-STREAM',
659
+ encode_bodystructure([ 'TEXT',
660
+ 'PLAIN',
659
661
  nil,
660
662
  nil,
661
663
  nil,
662
664
  nil,
663
665
  @empty_mail.raw_source.bytesize,
666
+ @empty_mail.raw_source.each_line.count,
664
667
  nil,
665
668
  nil,
666
669
  nil,
@@ -669,13 +672,14 @@ module RIMS::Test
669
672
  ])
670
673
  assert_fetch(3, [
671
674
  'BODYSTRUCTURE ' +
672
- encode_bodystructure([ 'APPLICATION',
673
- 'OCTET-STREAM',
675
+ encode_bodystructure([ 'TEXT',
676
+ 'PLAIN',
674
677
  nil,
675
678
  nil,
676
679
  nil,
677
680
  nil,
678
681
  @no_body_mail.raw_source.bytesize,
682
+ @no_body_mail.raw_source.each_line.count,
679
683
  nil,
680
684
  nil,
681
685
  nil,
@@ -23,11 +23,6 @@ module RIMS::Test
23
23
  end
24
24
  private :add_msg
25
25
 
26
- def get_msg_flag(uid, flag_name)
27
- @mail_store.msg_flag(@inbox_id, uid, flag_name)
28
- end
29
- private :get_msg_flag
30
-
31
26
  def set_msg_flag(uid, flag_name, flag_value)
32
27
  @mail_store.set_msg_flag(@inbox_id, uid, flag_name, flag_value)
33
28
  nil
@@ -48,14 +43,8 @@ module RIMS::Test
48
43
  end
49
44
  private :assert_msg_uid
50
45
 
51
- def assert_msg_flag(flag_name, *flag_value_list)
52
- uid_list = @mail_store.each_msg_uid(@inbox_id).to_a
53
- assert_equal(uid_list.map{|uid| get_msg_flag(uid, flag_name) }, flag_value_list)
54
- end
55
- private :assert_msg_flag
56
-
57
46
  def make_search_parser(charset: nil)
58
- yield
47
+ yield if block_given?
59
48
  @folder = @mail_store.open_folder(@inbox_id, read_only: true).reload
60
49
  @parser = RIMS::Protocol::SearchParser.new(@mail_store, @folder)
61
50
  @parser.charset = charset if charset
@@ -73,8 +62,8 @@ module RIMS::Test
73
62
  end
74
63
  private :parse_search_key
75
64
 
76
- def assert_search_cond(msg_idx, expected_found_flag)
77
- assert_equal(expected_found_flag, @cond.call(@folder[msg_idx]))
65
+ def assert_search_cond(msg_idx, expected_found_flag, *optional)
66
+ assert_equal(expected_found_flag, @cond.call(@folder[msg_idx]), *optional)
78
67
  end
79
68
  private :assert_search_cond
80
69
 
@@ -93,86 +82,125 @@ module RIMS::Test
93
82
  end
94
83
  private :assert_search_syntax_error
95
84
 
96
- def test_parse_all
97
- make_search_parser{
98
- add_msg('foo')
99
- assert_msg_uid(1)
100
- }
101
-
102
- parse_search_key([ 'ALL' ]) {
103
- assert_search_cond(0, true)
104
- }
105
- end
106
-
107
- def test_parse_answered
108
- make_search_parser{
109
- add_msg('foo')
110
- add_msg('foo')
111
- assert_msg_uid(1, 2)
112
-
113
- set_msg_flag(1, 'answered', true)
114
- assert_msg_flag('answered', true, false)
115
- }
116
-
117
- parse_search_key([ 'ANSWERED' ]) {
118
- assert_search_cond(0, true)
119
- assert_search_cond(1, false)
120
- }
121
- end
122
-
123
- def test_parse_bcc
124
- make_search_parser{
125
- add_msg("Bcc: foo\r\n" +
126
- "\r\n" +
127
- "foo")
128
- add_msg("Bcc: bar\r\n" +
129
- "\r\n" +
130
- "foo")
131
- add_msg('foo')
132
- assert_msg_uid(1, 2, 3)
133
- }
134
-
135
- parse_search_key([ 'BCC', 'foo' ]) {
136
- assert_search_cond(0, true)
137
- assert_search_cond(1, false)
138
- assert_search_cond(2, false)
139
- }
140
-
141
- assert_search_syntax_error([ 'BCC' ], /need for a search string/)
142
- assert_search_syntax_error([ 'BCC', [ :group, 'foo' ] ], /search string expected as <String> but was/)
143
- end
144
-
145
- def test_parse_before
146
- make_search_parser{
147
- add_msg('foo', Time.parse('2013-11-07 12:34:56'))
148
- add_msg('foo', Time.parse('2013-11-08 12:34:56'))
149
- add_msg('foo', Time.parse('2013-11-09 12:34:56'))
150
- assert_msg_uid(1, 2, 3)
151
- }
152
-
153
- parse_search_key([ 'BEFORE', '08-Nov-2013' ]) {
154
- assert_search_cond(0, true)
155
- assert_search_cond(1, false)
156
- assert_search_cond(2, false)
157
- }
158
-
159
- assert_search_syntax_error([ 'BEFORE' ], /need for a search date/)
160
- assert_search_syntax_error([ 'BEFORE', '99-Nov-2013' ], /search date is invalid/)
161
- assert_search_syntax_error([ 'BEFORE', [ :group, '08-Nov-2013'] ], /search date string expected as <String> but was/)
162
- end
163
-
164
- def test_parse_body
165
- make_search_parser{
166
- add_msg("Content-Type: text/plain\r\n" +
167
- "\r\n" +
168
- "foo")
169
- add_msg("Content-Type: text/plain\r\n" +
170
- "\r\n" +
171
- "bar")
172
- add_msg("Content-Type: message/rfc822\r\n" +
173
- "\r\n" +
174
- "foo")
175
- add_msg(<<-'EOF')
85
+ # test data format:
86
+ # { search: <search key array>,
87
+ # charset: <charset (optional)>,
88
+ # messages: [
89
+ # [ <expected search condition>,
90
+ # <message text>,
91
+ # <message flags>,
92
+ # (<optional arguments for `RIMS::MailStore#add_msg'>)
93
+ # ],
94
+ # ...
95
+ # ]
96
+ # }
97
+ data('ALL', {
98
+ search: %w[ ALL ],
99
+ messages: [
100
+ [ true, 'foo', {} ]
101
+ ]
102
+ })
103
+ [ [ 'ANSWERED', %w[ ANSWERED ], [ true, false ] ],
104
+ [ 'UNANSWERED', %w[ UNANSWERED ], [ false, true ] ]
105
+ ].each do |label, search, cond_list|
106
+ data(label, {
107
+ search: search,
108
+ messages: [
109
+ [ ' foo', { answered: true } ],
110
+ [ 'foo', { answered: false } ]
111
+ ].zip(cond_list).map{|msg, cond| [ cond ] + msg }
112
+ })
113
+ end
114
+ [ [ 'us-ascii', %w[ BCC foo ], [ true, true, false, false, false, false, false, false ] ],
115
+ [ 'charset', %W[ BCC \u306F\u306B\u307B ], [ false, false, false, false, true, false, true, false ], 'utf-8' ]
116
+ ].each do |label, search, cond_list, charset|
117
+ data("BCC:#{label}", {
118
+ search: search,
119
+ charset: charset,
120
+ messages: [
121
+ [ "Bcc: foo\r\n" +
122
+ "\r\n" +
123
+ "foo",
124
+ {}
125
+ ],
126
+ [ "Bcc: FOO\r\n" +
127
+ "\r\n" +
128
+ "foo",
129
+ {}
130
+ ],
131
+ [ "Bcc: bar\r\n" +
132
+ "\r\n" +
133
+ "foo",
134
+ {},
135
+ ],
136
+ [ 'foo',
137
+ {}
138
+ ],
139
+ [ "Bcc: =?UTF-8?B?44GE44KN44Gv44Gr44G744G444Go?=\r\n" +
140
+ "\r\n" +
141
+ "foo",
142
+ {},
143
+ ],
144
+ [ "Bcc: =?UTF-8?B?44Gh44KK44Gs44KL44KS?=\r\n" +
145
+ "\r\n" +
146
+ "foo",
147
+ {}
148
+ ],
149
+ [ "Bcc: =?ISO-2022-JP?B?GyRCJCQkbSRPJEskWyRYJEgbKEI=?=\r\n" +
150
+ "\r\n" +
151
+ "foo",
152
+ {},
153
+ ],
154
+ [ "Bcc: =?ISO-2022-JP?B?GyRCJEEkaiRMJGskchsoQg==?=\r\n" +
155
+ "\r\n" +
156
+ "foo",
157
+ {},
158
+ ]
159
+ ].zip(cond_list).map{|msg, cond| [ cond ] + msg }
160
+ })
161
+ end
162
+ [ [ 'BEFORE', %w[ BEFORE 08-Nov-2013 ], [ true, false, false ] ],
163
+ [ 'ON', %w[ ON 08-Nov-2013 ], [ false, true, false ] ],
164
+ [ 'SINCE', %w[ SINCE 08-Nov-2013 ], [ false, false, true ] ]
165
+ ].each do |label, search, cond_list|
166
+ data(label, {
167
+ search: search,
168
+ messages: [
169
+ [ 'foo', {}, Time.parse('2013-11-07 12:34:56 +0000') ],
170
+ [ 'foo', {}, Time.parse('2013-11-08 12:34:56 +0000') ],
171
+ [ 'foo', {}, Time.parse('2013-11-09 12:34:56 +0000') ]
172
+ ].zip(cond_list).map{|msg, cond| [ cond ] + msg }
173
+ })
174
+ end
175
+ data('BODY', {
176
+ search: %w[ BODY foo ],
177
+ messages: [
178
+ [ true,
179
+ "Content-Type: text/plain\r\n" +
180
+ "\r\n" +
181
+ "foo",
182
+ {}
183
+ ],
184
+ [ true,
185
+ "Content-Type: text/plain\r\n" +
186
+ "\r\n" +
187
+ "FOO",
188
+ {}
189
+ ],
190
+ [ false,
191
+ "Content-Type: text/plain\r\n" +
192
+ "\r\n" +
193
+ "bar",
194
+ {}
195
+ ],
196
+ [ true,
197
+ "Content-Type: message/rfc822\r\n" +
198
+ "\r\n" +
199
+ "foo",
200
+ {}
201
+ ],
202
+ [ true,
203
+ <<-'EOF',
176
204
  Content-Type: multipart/alternative; boundary="1383.905529.351297"
177
205
 
178
206
  --1383.905529.351297
@@ -184,543 +212,652 @@ Content-Type: text/html
184
212
 
185
213
  <html><body><p>foo</p></body></html>
186
214
  --1383.905529.351297--
187
- EOF
188
-
189
- assert_msg_uid(1, 2, 3, 4)
190
- }
191
-
192
- parse_search_key([ 'BODY', 'foo' ]) {
193
- assert_search_cond(0, true)
194
- assert_search_cond(1, false)
195
- assert_search_cond(2, true)
196
- assert_search_cond(3, false) # ignored text part of multipart message.
197
- }
198
-
199
- assert_search_syntax_error([ 'BODY' ], /need for a search string/)
200
- assert_search_syntax_error([ 'BODY', [ :group, 'foo' ] ], /search string expected as <String> but was/)
201
- end
202
-
203
- def test_parse_cc
204
- make_search_parser{
205
- add_msg("Cc: foo\r\n" +
206
- "\r\n" +
207
- "foo")
208
- add_msg("Cc: bar\r\n" +
209
- "\r\n" +
210
- "foo")
211
- add_msg('foo')
212
- assert_msg_uid(1, 2, 3)
213
- }
214
-
215
- parse_search_key([ 'CC', 'foo' ]) {
216
- assert_search_cond(0, true)
217
- assert_search_cond(1, false)
218
- assert_search_cond(2, false)
219
- }
220
-
221
- assert_search_syntax_error([ 'CC' ], /need for a search string/)
222
- assert_search_syntax_error([ 'CC', [ :group, 'foo' ] ], /search string expected as <String> but was/)
223
- end
224
-
225
- def test_parse_deleted
226
- make_search_parser{
227
- add_msg('foo')
228
- add_msg('foo')
229
- assert_msg_uid(1, 2)
230
-
231
- set_msg_flag(1, 'deleted', true)
232
- assert_msg_flag('deleted', true, false)
233
- }
234
-
235
- parse_search_key([ 'DELETED' ]) {
236
- assert_search_cond(0, true)
237
- assert_search_cond(1, false)
238
- }
239
- end
240
-
241
- def test_parse_draft
242
- make_search_parser{
243
- add_msg('foo')
244
- add_msg('foo')
245
- assert_msg_uid(1, 2)
246
-
247
- set_msg_flag(1, 'draft', true)
248
- assert_msg_flag('draft', true, false)
249
- }
250
-
251
- parse_search_key([ 'DRAFT' ]) {
252
- assert_search_cond(0, true)
253
- assert_search_cond(1, false)
254
- }
255
- end
256
-
257
- def test_parse_flagged
258
- make_search_parser{
259
- add_msg('foo')
260
- add_msg('foo')
261
- assert_msg_uid(1, 2)
262
-
263
- set_msg_flag(1, 'flagged', true)
264
- assert_msg_flag('flagged', true, false)
265
- }
266
-
267
- parse_search_key([ 'FLAGGED' ]) {
268
- assert_search_cond(0, true)
269
- assert_search_cond(1, false)
270
- }
271
- end
272
-
273
- def test_parse_from
274
- make_search_parser{
275
- add_msg("From: foo\r\n" +
276
- "\r\n" +
277
- "foo")
278
- add_msg("From: bar\r\n" +
279
- "\r\n" +
280
- "foo")
281
- add_msg('foo')
282
- assert_msg_uid(1, 2, 3)
283
- }
284
-
285
- parse_search_key([ 'FROM', 'foo' ]) {
286
- assert_search_cond(0, true)
287
- assert_search_cond(1, false)
288
- assert_search_cond(2, false)
289
- }
290
-
291
- assert_search_syntax_error([ 'FROM' ], /need for a search string/)
292
- assert_search_syntax_error([ 'FROM', [ :group, 'foo' ] ], /search string expected as <String> but was/)
293
- end
294
-
295
- def test_parse_header
296
- make_search_parser{
297
- add_msg("X-Foo: alice\r\n" +
298
- "X-Bar: bob\r\n" +
299
- "\r\n" +
300
- "foo")
301
- add_msg("X-Foo: bob\r\n" +
302
- "X-Bar: alice\r\n" +
303
- "\r\n" +
304
- "foo")
305
- add_msg('foo')
306
- assert_msg_uid(1, 2, 3)
307
- }
308
-
309
- parse_search_key([ 'HEADER', 'x-foo', 'alice' ]) {
310
- assert_search_cond(0, true)
311
- assert_search_cond(1, false)
312
- assert_search_cond(2, false)
313
- }
314
-
315
- parse_search_key([ 'HEADER', 'x-foo', 'bob' ]) {
316
- assert_search_cond(0, false)
317
- assert_search_cond(1, true)
318
- assert_search_cond(2, false)
319
- }
320
-
321
- parse_search_key([ 'HEADER', 'x-bar', 'alice' ]) {
322
- assert_search_cond(0, false)
323
- assert_search_cond(1, true)
324
- assert_search_cond(2, false)
325
- }
326
-
327
- parse_search_key([ 'HEADER', 'x-bar', 'bob' ]) {
328
- assert_search_cond(0, true)
329
- assert_search_cond(1, false)
330
- assert_search_cond(2, false)
331
- }
332
-
333
- assert_search_syntax_error([ 'HEADER' ], /need for a search string/)
334
- assert_search_syntax_error([ 'HEADER', 'Received' ], /need for a search string/)
335
- assert_search_syntax_error([ 'HEADER', 'Received', [ :group, 'foo' ] ], /search string expected as <String> but was/)
336
- assert_search_syntax_error([ 'HEADER', [ :group, 'Received' ], 'foo' ], /search string expected as <String> but was/)
337
- end
338
-
339
- def test_parse_keyword
340
- make_search_parser{
341
- add_msg('')
342
- assert_msg_uid(1)
343
- }
344
-
345
- parse_search_key([ 'KEYWORD', 'foo' ]) {
346
- assert_search_cond(0, false) # always false
347
- }
348
-
349
- assert_search_syntax_error([ 'KEYWORD' ], /need for a search string/)
350
- assert_search_syntax_error([ 'KEYWORD', [ :group, 'foo' ] ], /search string expected as <String> but was/)
351
- end
352
-
353
- def test_parse_larger
354
- make_search_parser{
355
- add_msg('foo')
356
- add_msg('1234')
357
- add_msg('bar')
358
- assert_msg_uid(1, 2, 3)
359
- }
360
-
361
- parse_search_key([ 'LARGER', '3' ]) {
362
- assert_search_cond(0, false)
363
- assert_search_cond(1, true)
364
- assert_search_cond(2, false)
365
- }
366
-
367
- assert_search_syntax_error([ 'LARGER' ], /need for a octet size/)
368
- assert_search_syntax_error([ 'LARGER', [ :group, '3' ] ], /octet size is expected as numeric string but was/)
369
- assert_search_syntax_error([ 'LARGER', 'nonum' ], /octet size is expected as numeric string but was/)
370
- end
371
-
372
- def test_parse_new
373
- make_search_parser{
374
- add_msg('foo')
375
- add_msg('bar')
376
- add_msg('baz')
377
- assert_msg_uid(1, 2, 3)
378
-
379
- set_msg_flag(3, 'recent', false)
380
- set_msg_flag(2, 'seen', true)
381
- assert_msg_flag('recent', true, true, false)
382
- assert_msg_flag('seen', false, true, false)
383
- }
384
-
385
- parse_search_key([ 'NEW' ]) {
386
- assert_search_cond(0, true)
387
- assert_search_cond(1, false)
388
- assert_search_cond(2, false)
389
- }
390
- end
391
-
392
- def test_parse_not
393
- make_search_parser{
394
- add_msg('foo')
395
- add_msg('1234')
396
- add_msg('bar')
397
- assert_msg_uid(1, 2, 3)
398
-
399
- set_msg_flag(1, 'answered', true)
400
- assert_msg_flag('answered', true, false, false)
401
- }
402
-
403
- parse_search_key([ 'NOT', 'LARGER', '3' ]) {
404
- assert_search_cond(0, true)
405
- assert_search_cond(1, false)
406
- assert_search_cond(2, true)
407
- }
408
-
409
- parse_search_key([ 'NOT', 'ANSWERED' ]) {
410
- assert_search_cond(0, false)
411
- assert_search_cond(1, true)
412
- assert_search_cond(2, true)
413
- }
414
-
415
- assert_search_syntax_error([ 'NOT' ], 'unexpected end of search key.')
416
- end
417
-
418
- def test_parse_old
419
- make_search_parser{
420
- add_msg('foo')
421
- add_msg('bar')
422
- assert_msg_uid(1, 2)
423
-
424
- set_msg_flag(1, 'recent', false)
425
- assert_msg_flag('recent', false, true)
426
- }
427
-
428
- parse_search_key([ 'OLD' ]) {
429
- assert_search_cond(0, true)
430
- assert_search_cond(1, false)
431
- }
432
- end
433
-
434
- def test_parse_on
435
- make_search_parser{
436
- add_msg('foo', Time.parse('2013-11-07 12:34:56'))
437
- add_msg('foo', Time.parse('2013-11-08 12:34:56'))
438
- add_msg('foo', Time.parse('2013-11-09 12:34:56'))
439
- assert_msg_uid(1, 2, 3)
440
- }
441
-
442
- parse_search_key([ 'ON', '08-Nov-2013' ]) {
443
- assert_search_cond(0, false)
444
- assert_search_cond(1, true)
445
- assert_search_cond(2, false)
446
- }
447
-
448
- assert_search_syntax_error([ 'ON' ], /need for a search date/)
449
- assert_search_syntax_error([ 'ON', '99-Nov-2013' ], /search date is invalid/)
450
- assert_search_syntax_error([ 'ON', [ :group, '08-Nov-2013'] ], /search date string expected as <String> but was/)
451
- end
452
-
453
- def test_parse_or
454
- make_search_parser{
455
- add_msg('foo')
456
- add_msg('foo')
457
- add_msg('foo')
458
- add_msg('foo')
459
- assert_msg_uid(1, 2, 3, 4)
460
-
461
- set_msg_flag(1, 'answered', true)
462
- set_msg_flag(2, 'answered', true)
463
- set_msg_flag(1, 'flagged', true)
464
- set_msg_flag(3, 'flagged', true)
465
- assert_msg_flag('answered', true, true, false, false)
466
- assert_msg_flag('flagged', true, false, true, false)
467
- }
468
-
469
- parse_search_key([ 'OR', 'ANSWERED', 'FLAGGED' ]) {
470
- assert_search_cond(0, true)
471
- assert_search_cond(1, true)
472
- assert_search_cond(2, true)
473
- assert_search_cond(3, false)
474
-
475
- }
476
-
477
- assert_search_syntax_error([ 'OR' ], 'unexpected end of search key.')
478
- assert_search_syntax_error([ 'OR', 'ANSWERED' ], 'unexpected end of search key.')
479
- end
480
-
481
- def test_parse_recent
482
- make_search_parser{
483
- add_msg('foo')
484
- add_msg('foo')
485
- assert_msg_uid(1, 2)
486
-
487
- set_msg_flag(1, 'recent', false)
488
- assert_msg_flag('recent', false, true)
489
- }
490
-
491
- parse_search_key([ 'RECENT' ]) {
492
- assert_search_cond(0, false)
493
- assert_search_cond(1, true)
494
- }
495
- end
496
-
497
- def test_parse_seen
498
- make_search_parser{
499
- add_msg('foo')
500
- add_msg('foo')
501
- assert_msg_uid(1, 2)
502
-
503
- set_msg_flag(1, 'seen', true)
504
- assert_msg_flag('seen', true, false)
505
- }
506
-
507
- parse_search_key([ 'SEEN' ]) {
508
- assert_search_cond(0, true)
509
- assert_search_cond(1, false)
510
- }
511
- end
512
-
513
- def test_parse_sentbefore
514
- make_search_parser{
515
- add_msg("Date: Thu, 07 Nov 2013 12:34:56 +0900\r\n" +
516
- "\r\n" +
517
- "foo")
518
- add_msg("Date: Fri, 08 Nov 2013 12:34:56 +0900\r\n" +
519
- "\r\n" +
520
- "foo")
521
- add_msg("Date: Sat, 09 Nov 2013 12:34:56 +0900\r\n" +
522
- "\r\n" +
523
- "foo")
524
- add_msg('foo')
525
- assert_msg_uid(1, 2, 3, 4)
526
- }
527
-
528
- parse_search_key([ 'SENTBEFORE', '08-Nov-2013' ]) {
529
- assert_search_cond(0, true)
530
- assert_search_cond(1, false)
531
- assert_search_cond(2, false)
532
- assert_search_cond(3, false)
533
- }
534
-
535
- assert_search_syntax_error([ 'SENTBEFORE' ], /need for a search date/)
536
- assert_search_syntax_error([ 'SENTBEFORE', '99-Nov-2013' ], /search date is invalid/)
537
- assert_search_syntax_error([ 'SENTBEFORE', [ :group, '08-Nov-2013'] ], /search date string expected as <String> but was/)
538
- end
539
-
540
- def test_parse_senton
541
- make_search_parser{
542
- add_msg("Date: Thu, 07 Nov 2013 12:34:56 +0900\r\n" +
543
- "\r\n" +
544
- "foo")
545
- add_msg("Date: Fri, 08 Nov 2013 12:34:56 +0900\r\n" +
546
- "\r\n" +
547
- "foo")
548
- add_msg("Date: Sat, 09 Nov 2013 12:34:56 +0900\r\n" +
549
- "\r\n" +
550
- "foo")
551
- add_msg('foo')
552
- assert_msg_uid(1, 2, 3, 4)
553
- }
554
-
555
- parse_search_key([ 'SENTON', '08-Nov-2013' ]) {
556
- assert_search_cond(0, false)
557
- assert_search_cond(1, true)
558
- assert_search_cond(2, false)
559
- assert_search_cond(3, false)
560
- }
561
-
562
- assert_search_syntax_error([ 'SENTON' ], /need for a search date/)
563
- assert_search_syntax_error([ 'SENTON', '99-Nov-2013' ], /search date is invalid/)
564
- assert_search_syntax_error([ 'SENTON', [ :group, '08-Nov-2013'] ], /search date string expected as <String> but was/)
565
- end
566
-
567
- def test_parse_sentsince
568
- make_search_parser{
569
- add_msg("Date: Thu, 07 Nov 2013 12:34:56 +0900\r\n" +
570
- "\r\n" +
571
- "foo")
572
- add_msg("Date: Fri, 08 Nov 2013 12:34:56 +0900\r\n" +
573
- "\r\n" +
574
- "foo")
575
- add_msg("Date: Sat, 09 Nov 2013 12:34:56 +0900\r\n" +
576
- "\r\n" +
577
- "foo")
578
- add_msg('foo')
579
- assert_msg_uid(1, 2, 3, 4)
580
- }
581
-
582
- parse_search_key([ 'SENTSINCE', '08-Nov-2013' ]) {
583
- assert_search_cond(0, false)
584
- assert_search_cond(1, false)
585
- assert_search_cond(2, true)
586
- assert_search_cond(3, false)
587
- }
588
-
589
- assert_search_syntax_error([ 'SENTSINCE' ], /need for a search date/)
590
- assert_search_syntax_error([ 'SENTSINCE', '99-Nov-2013' ], /search date is invalid/)
591
- assert_search_syntax_error([ 'SENTSINCE', [ :group, '08-Nov-2013'] ], /search date string expected as <String> but was/)
592
- end
593
-
594
- def test_parse_since
595
- make_search_parser{
596
- add_msg('foo', Time.parse('2013-11-07 12:34:56'))
597
- add_msg('foo', Time.parse('2013-11-08 12:34:56'))
598
- add_msg('foo', Time.parse('2013-11-09 12:34:56'))
599
- assert_msg_uid(1, 2, 3)
600
- }
601
-
602
- parse_search_key([ 'SINCE', '08-Nov-2013' ]) {
603
- assert_search_cond(0, false)
604
- assert_search_cond(1, false)
605
- assert_search_cond(2, true)
606
- }
607
-
608
- assert_search_syntax_error([ 'SINCE' ], /need for a search date/)
609
- assert_search_syntax_error([ 'SINCE', '99-Nov-2013' ], /search date is invalid/)
610
- assert_search_syntax_error([ 'SINCE', [ :group, '08-Nov-2013'] ], /search date string expected as <String> but was/)
611
- end
612
-
613
- def test_parse_smaller
614
- make_search_parser{
615
- add_msg('foo')
616
- add_msg('12')
617
- add_msg('bar')
618
- assert_msg_uid(1, 2, 3)
619
- }
620
-
621
- parse_search_key([ 'SMALLER', '3' ]) {
622
- assert_search_cond(0, false)
623
- assert_search_cond(1, true)
624
- assert_search_cond(2, false)
625
- }
626
-
627
- assert_search_syntax_error([ 'SMALLER' ], /need for a octet size/)
628
- assert_search_syntax_error([ 'SMALLER', [ :group, '3' ] ], /octet size is expected as numeric string but was/)
629
- assert_search_syntax_error([ 'SMALLER', 'nonum' ], /octet size is expected as numeric string but was/)
630
- end
631
-
632
- def test_parse_subject
633
- make_search_parser{
634
- add_msg("Subject: foo\r\n" +
635
- "\r\n" +
636
- "foo")
637
- add_msg("Subject: bar\r\n" +
638
- "\r\n" +
639
- "foo")
640
- add_msg('foo')
641
- assert_msg_uid(1, 2, 3)
642
- }
643
-
644
- parse_search_key([ 'SUBJECT', 'foo' ]) {
645
- assert_search_cond(0, true)
646
- assert_search_cond(1, false)
647
- assert_search_cond(2, false)
648
- }
649
-
650
- assert_search_syntax_error([ 'SUBJECT' ], /need for a search string/)
651
- assert_search_syntax_error([ 'SUBJECT', [ :group, 'foo' ] ], /search string expected as <String> but was/)
652
- end
653
-
654
- def test_parse_text
655
- make_search_parser{
656
- add_msg("Content-Type: text/plain\r\n" +
657
- "Subject: foo\r\n" +
658
- "\r\n" +
659
- "bar")
660
- assert_msg_uid(1)
661
- }
662
-
663
- parse_search_key([ 'TEXT', 'jec' ]) {
664
- assert_search_cond(0, true)
665
- }
666
- parse_search_key([ 'TEXT', 'foo' ]) {
667
- assert_search_cond(0, true)
668
- }
669
- parse_search_key([ 'TEXT', 'bar' ]) {
670
- assert_search_cond(0, true)
671
- }
672
- parse_search_key([ 'TEXT', 'baz' ]) {
673
- assert_search_cond(0, false)
674
- }
675
-
676
- assert_search_syntax_error([ 'TEXT' ], /need for a search string/)
677
- assert_search_syntax_error([ 'TEXT', [ :group, 'foo'] ], /search string expected as <String> but was/)
678
- end
679
-
680
- def test_parse_text_multipart
681
- make_mail_multipart
682
- make_search_parser{
683
- add_msg(@mpart_mail.raw_source)
684
- assert_msg_uid(1)
685
- }
686
-
687
- parse_search_key([ 'TEXT', 'Subject: multipart test' ]) {
688
- assert_search_cond(0, true)
689
- }
690
- parse_search_key([ 'TEXT', 'Subject: inner multipart' ]) {
691
- assert_search_cond(0, true)
692
- }
693
- parse_search_key([ 'TEXT', 'Hello world.' ]) {
694
- assert_search_cond(0, true)
695
- }
696
- parse_search_key([ 'TEXT', 'HALO' ]) {
697
- assert_search_cond(0, true)
698
- }
699
- parse_search_key([ 'TEXT', 'detarame' ]) {
700
- assert_search_cond(0, false)
701
- }
702
- end
703
-
704
- def test_parse_to
705
- make_search_parser{
706
- add_msg("To: foo\r\n" +
707
- "\r\n" +
708
- "foo")
709
- add_msg("To: bar\r\n" +
710
- "\r\n" +
711
- "foo")
712
- add_msg('foo')
713
- assert_msg_uid(1, 2, 3)
714
- }
715
-
716
- parse_search_key([ 'TO', 'foo' ]) {
717
- assert_search_cond(0, true)
718
- assert_search_cond(1, false)
719
- assert_search_cond(2, false)
720
- }
721
-
722
- assert_search_syntax_error([ 'TO' ], /need for a search string/)
723
- assert_search_syntax_error([ 'TO', [ :group, 'foo' ] ], /search string expected as <String> but was/)
215
+ EOF
216
+ {}
217
+ ]
218
+ ]
219
+ })
220
+ [ [ 'us-ascii', %w[ CC foo ], [ true, true, false, false, false, false, false, false ] ],
221
+ [ 'charset', %W[ CC \u306F\u306B\u307B ], [ false, false, false, false, true, false, true, false ], 'utf-8' ]
222
+ ].each do |label, search, cond_list, charset|
223
+ data("CC:#{label}", {
224
+ search: search,
225
+ charset: charset,
226
+ messages: [
227
+ [ "Cc: foo\r\n" +
228
+ "\r\n" +
229
+ "foo",
230
+ {}
231
+ ],
232
+ [ "Cc: FOO\r\n" +
233
+ "\r\n" +
234
+ "foo",
235
+ {}
236
+ ],
237
+ [ "Cc: bar\r\n" +
238
+ "\r\n" +
239
+ "foo",
240
+ {}
241
+ ],
242
+ [ 'foo',
243
+ {}
244
+ ],
245
+ [ "Cc: =?UTF-8?B?44GE44KN44Gv44Gr44G744G444Go?=\r\n" +
246
+ "\r\n" +
247
+ "foo",
248
+ {},
249
+ ],
250
+ [ "Cc: =?UTF-8?B?44Gh44KK44Gs44KL44KS?=\r\n" +
251
+ "\r\n" +
252
+ "foo",
253
+ {}
254
+ ],
255
+ [ "Cc: =?ISO-2022-JP?B?GyRCJCQkbSRPJEskWyRYJEgbKEI=?=\r\n" +
256
+ "\r\n" +
257
+ "foo",
258
+ {},
259
+ ],
260
+ [ "Cc: =?ISO-2022-JP?B?GyRCJEEkaiRMJGskchsoQg==?=\r\n" +
261
+ "\r\n" +
262
+ "foo",
263
+ {},
264
+ ]
265
+ ].zip(cond_list).map{|msg, cond| [ cond ] + msg }
266
+ })
267
+ end
268
+ [ [ 'DELETED', %w[ DELETED ], [ true, false ] ],
269
+ [ 'UNDELETED', %w[ UNDELETED ], [ false, true ] ]
270
+ ].each do |label, search, cond_list|
271
+ data(label, {
272
+ search: search,
273
+ messages: [
274
+ [ 'foo', { deleted: true } ],
275
+ [ 'foo', { deleted: false } ]
276
+ ].zip(cond_list).map{|msg, cond| [ cond ] + msg }
277
+ })
278
+ end
279
+ [ [ 'DRAFT', %w[ DRAFT ], [ true, false ] ],
280
+ [ 'UNDRAFT', %w[ UNDRAFT ], [ false, true ] ]
281
+ ].each do |label, search, cond_list|
282
+ data(label, {
283
+ search: search,
284
+ messages: [
285
+ [ 'foo', { draft: true } ],
286
+ [ 'foo', { draft: false } ]
287
+ ].zip(cond_list).map{|msg, cond| [ cond ] + msg }
288
+ })
289
+ end
290
+ [ [ 'FLAGGED', %w[ FLAGGED ], [ true, false ] ],
291
+ [ 'UNFLAGGED', %w[ UNFLAGGED ], [ false, true ] ]
292
+ ].each do |label, search, cond_list|
293
+ data(label, {
294
+ search: search,
295
+ messages: [
296
+ [ 'foo', { flagged: true } ],
297
+ [ 'foo', { flagged: false } ]
298
+ ].zip(cond_list).map{|msg, cond| [ cond ] + msg }
299
+ })
300
+ end
301
+ [ [ 'us-ascii', %w[ FROM foo ], [ true, true, false, false, false, false, false, false ] ],
302
+ [ 'charset', %W[ FROM \u306F\u306B\u307B ],[ false, false, false, false, true, false, true, false ], 'utf-8' ]
303
+ ].each do |label, search, cond_list, charset|
304
+ data("FROM:#{label}", {
305
+ search: search,
306
+ charset: charset,
307
+ messages: [
308
+ [ "From: foo\r\n" +
309
+ "\r\n" +
310
+ "foo",
311
+ {}
312
+ ],
313
+ [ "From: FOO\r\n" +
314
+ "\r\n" +
315
+ "foo",
316
+ {}
317
+ ],
318
+ [ "From: bar\r\n" +
319
+ "\r\n" +
320
+ "foo",
321
+ {}
322
+ ],
323
+ [ 'foo',
324
+ {}
325
+ ],
326
+ [ "From: =?UTF-8?B?44GE44KN44Gv44Gr44G744G444Go?=\r\n" +
327
+ "\r\n" +
328
+ "foo",
329
+ {},
330
+ ],
331
+ [ "From: =?UTF-8?B?44Gh44KK44Gs44KL44KS?=\r\n" +
332
+ "\r\n" +
333
+ "foo",
334
+ {}
335
+ ],
336
+ [ "From: =?ISO-2022-JP?B?GyRCJCQkbSRPJEskWyRYJEgbKEI=?=\r\n" +
337
+ "\r\n" +
338
+ "foo",
339
+ {},
340
+ ],
341
+ [ "From: =?ISO-2022-JP?B?GyRCJEEkaiRMJGskchsoQg==?=\r\n" +
342
+ "\r\n" +
343
+ "foo",
344
+ {},
345
+ ]
346
+ ].zip(cond_list).map{|msg, cond| [ cond ] + msg }
347
+ })
348
+ end
349
+ [ [ 'x-foo_alice', %w[ HEADER x-foo alice ], [ true, true, false, false, false ] ],
350
+ [ 'x-foo_bob', %w[ HEADER x-foo bob ], [ false, false, true, true, false ] ],
351
+ [ 'x-foo_foo', %w[ HEADER x-foo foo ], [ false, false, false, false, false ] ],
352
+ [ 'x-bar_alice', %w[ HEADER x-bar alice ], [ false, false, true, true, false ] ],
353
+ [ 'x-bar_bob', %w[ HEADER x-bar bob ], [ true, true, false, false, false ] ],
354
+ [ 'x-bar_foo', %w[ HEADER x-bar foo ], [ false, false, false, false, false ] ]
355
+ ].each do |label, search, cond_list|
356
+ data("HEADER:#{label}", {
357
+ search: search,
358
+ messages: [
359
+ [ "X-Foo: alice\r\n" +
360
+ "X-Bar: bob\r\n" +
361
+ "\r\n" +
362
+ "foo",
363
+ {}
364
+ ],
365
+ [ "X-Foo: Alice\r\n" +
366
+ "X-Bar: Bob\r\n" +
367
+ "\r\n" +
368
+ "foo",
369
+ {}
370
+ ],
371
+ [ "X-Foo: bob\r\n" +
372
+ "X-Bar: alice\r\n" +
373
+ "\r\n" +
374
+ "foo",
375
+ {},
376
+ ],
377
+ [ "X-Foo: Bob\r\n" +
378
+ "X-Bar: Alice\r\n" +
379
+ "\r\n" +
380
+ "foo",
381
+ {},
382
+ ],
383
+ [ 'foo',
384
+ {}
385
+ ]
386
+ ].zip(cond_list).map{|msg, cond| [ cond ] + msg }
387
+ })
388
+ end
389
+ data('HEADER:charset', {
390
+ search: %W[ HEADER x-foo \u306F\u306B\u307B ],
391
+ charset: 'utf-8',
392
+ messages: [
393
+ [ true,
394
+ "X-Foo: =?UTF-8?B?44GE44KN44Gv44Gr44G744G444Go?=\r\n" +
395
+ "\r\n" +
396
+ "foo",
397
+ {}
398
+ ],
399
+ [ false,
400
+ "X-Foo: =?UTF-8?B?44Gh44KK44Gs44KL44KS?=\r\n" +
401
+ "\r\n" +
402
+ "foo",
403
+ {}
404
+ ],
405
+ [ true,
406
+ "X-Foo: =?ISO-2022-JP?B?GyRCJCQkbSRPJEskWyRYJEgbKEI=?=\r\n" +
407
+ "\r\n" +
408
+ "foo",
409
+ {},
410
+ ],
411
+ [ false,
412
+ "X-Foo: =?ISO-2022-JP?B?GyRCJEEkaiRMJGskchsoQg==?=\r\n" +
413
+ "\r\n" +
414
+ "foo",
415
+ {},
416
+ ]
417
+ ]
418
+ })
419
+ [ [ 'KEYWORD', %w[ KEYWORD foo ], [ false ] ], # always false
420
+ [ 'UNKEYWORD', %w[ UNKEYWORD foo ], [ true ] ] # always true
421
+ ].each do |label, search, cond_list|
422
+ data(label, {
423
+ search: search,
424
+ messages: [
425
+ [ '', {} ]
426
+ ].zip(cond_list).map{|msg, cond| [ cond ] + msg }
427
+ })
428
+ end
429
+ [ [ 'LARGER', %w[ LARGER 3 ], [ false, false, true, false ] ],
430
+ [ 'SMALLER', %w[ SMALLER 3 ], [ false, true, false, false ] ]
431
+ ].each do |label, search, cond_list|
432
+ data(label, {
433
+ search: search,
434
+ messages: [
435
+ [ 'foo', {} ],
436
+ [ '12', {} ],
437
+ [ '1234', {} ],
438
+ [ 'bar', {} ]
439
+ ].zip(cond_list).map{|msg, cond| [ cond ] + msg }
440
+ })
441
+ end
442
+ [ [ 'NEW', %w[ NEW ], [ true, false, false ] ],
443
+ [ 'OLD', %w[ OLD ], [ false, false, true ] ]
444
+ ].each do |label, search, cond_list|
445
+ data(label, {
446
+ search: search,
447
+ messages: [
448
+ [ 'foo', { recent: true, seen: false } ],
449
+ [ 'bar', { recent: true, seen: true } ],
450
+ [ 'baz', { recent: false, seen: false } ]
451
+ ].zip(cond_list).map{|msg, cond| [ cond ] + msg }
452
+ })
453
+ end
454
+ [ [ 'LARGER', %w[ NOT LARGER 3 ], [ true, false, true ] ],
455
+ [ 'ANSWERED', %w[ NOT ANSWERED ], [ false, true, true ] ]
456
+ ].each do |label, search, cond_list|
457
+ data("NOT:#{label}", {
458
+ search: search,
459
+ messages: [
460
+ [ 'foo', { answered: true } ],
461
+ [ '1234', { answered: false } ],
462
+ [ 'bar', { answered: false } ]
463
+ ].zip(cond_list).map{|msg, cond| [ cond ] + msg }
464
+ })
465
+ end
466
+ data('OR', {
467
+ search: %w[ OR ANSWERED FLAGGED ],
468
+ messages: [
469
+ [ true, 'foo', { answered: true, flagged: true } ],
470
+ [ true, 'foo', { answered: true, flagged: false } ],
471
+ [ true, 'foo', { answered: false, flagged: true } ],
472
+ [ false, 'foo', { answered: false, flagged: false } ]
473
+ ]
474
+ })
475
+ data('RECENT', {
476
+ search: %w[ RECENT ],
477
+ messages: [
478
+ [ false, 'foo', { recent: false } ],
479
+ [ true, 'foo', { recent: true } ]
480
+ ]
481
+ })
482
+ [ [ 'SEEN', %w[ SEEN ], [ true, false ] ],
483
+ [ 'UNSEEN', %w[ UNSEEN ], [ false, true ] ]
484
+ ].each do |label, search, cond_list|
485
+ data(label, {
486
+ search: search,
487
+ messages: [
488
+ [ 'foo', { seen: true } ],
489
+ [ 'foo', { seen: false } ]
490
+ ].zip(cond_list).map{|msg, cond| [ cond ] + msg }
491
+ })
492
+ end
493
+ [ [ 'SENTBEFORE', %w[ SENTBEFORE 08-Nov-2013 ], [ true, false, false, false ] ],
494
+ [ 'SENTON', %w[ SENTON 08-Nov-2013 ], [ false, true, false, false ] ],
495
+ [ 'SENTSINCE', %w[ SENTSINCE 08-Nov-2013 ], [ false, false, true, false ] ],
496
+ ].each do |label, search, cond_list|
497
+ data(label, {
498
+ search: search,
499
+ messages: [
500
+ [ "Date: Thu, 07 Nov 2013 12:34:56 +0000\r\n" +
501
+ "\r\n" +
502
+ "foo",
503
+ {}
504
+ ],
505
+ [ "Date: Fri, 08 Nov 2013 12:34:56 +0000\r\n" +
506
+ "\r\n" +
507
+ "foo",
508
+ {}
509
+ ],
510
+ [ "Date: Sat, 09 Nov 2013 12:34:56 +0000\r\n" +
511
+ "\r\n" +
512
+ "foo",
513
+ {}
514
+ ],
515
+ [ 'foo',
516
+ {}
517
+ ]
518
+ ].zip(cond_list).map{|msg, cond| [ cond ] + msg }
519
+ })
520
+ end
521
+ [ [ 'us-ascii', %w[ SUBJECT foo ], [ true, true, false, false, false, false, false, false ] ],
522
+ [ 'charset', %W[ SUBJECT \u306F\u306B\u307B ], [ false, false, false, false, true, false, true, false ], 'utf-8' ]
523
+ ].each do |label, search, cond_list, charset|
524
+ data("SUBJECT:#{label}", {
525
+ search: search,
526
+ charset: charset,
527
+ messages: [
528
+ [ "Subject: foo\r\n" +
529
+ "\r\n" +
530
+ "foo",
531
+ {}
532
+ ],
533
+ [ "Subject: FOO\r\n" +
534
+ "\r\n" +
535
+ "foo",
536
+ {}
537
+ ],
538
+ [ "Subject: bar\r\n" +
539
+ "\r\n" +
540
+ "foo",
541
+ {}
542
+ ],
543
+ [ 'foo',
544
+ {}
545
+ ],
546
+ [ "Subject: =?UTF-8?B?44GE44KN44Gv44Gr44G744G444Go?=\r\n" +
547
+ "\r\n" +
548
+ "foo",
549
+ {},
550
+ ],
551
+ [ "Subject: =?UTF-8?B?44Gh44KK44Gs44KL44KS?=\r\n" +
552
+ "\r\n" +
553
+ "foo",
554
+ {}
555
+ ],
556
+ [ "Subject: =?ISO-2022-JP?B?GyRCJCQkbSRPJEskWyRYJEgbKEI=?=\r\n" +
557
+ "\r\n" +
558
+ "foo",
559
+ {},
560
+ ],
561
+ [ "Subject: =?ISO-2022-JP?B?GyRCJEEkaiRMJGskchsoQg==?=\r\n" +
562
+ "\r\n" +
563
+ "foo",
564
+ {},
565
+ ]
566
+ ].zip(cond_list).map{|msg, cond| [ cond ] + msg }
567
+ })
568
+ end
569
+ [ [ 'header_field_name', %w[ TEXT jec ], [ true ] ],
570
+ [ 'case_insensitive', %w[ TEXT sUB ], [ true ] ],
571
+ [ 'header_field_value', %w[ TEXT foo ], [ true ] ],
572
+ [ 'body', %w[ TEXT bar ], [ true ] ],
573
+ [ 'no_match', %w[ TEXT baz ], [ false ] ]
574
+ ].each do |label, search, cond_list|
575
+ data("TEXT:#{label}", {
576
+ search: search,
577
+ messages: [
578
+ [ "Content-Type: text/plain\r\n" +
579
+ "Subject: foo\r\n" +
580
+ "\r\n" +
581
+ "bar",
582
+ {}
583
+ ]
584
+ ].zip(cond_list).map{|msg, cond| [ cond ] + msg }
585
+ })
586
+ end
587
+ [ [ 'header', [ 'TEXT', 'Subject: multipart test' ], [ true ] ],
588
+ [ 'inner_header', [ 'TEXT', 'Subject: inner multipart' ], [ true ] ],
589
+ [ 'part_body', [ 'TEXT', 'Hello world.' ], [ true ] ],
590
+ [ 'inner_part_body', [ 'TEXT', 'HALO' ], [ true ] ],
591
+ [ 'no_match', [ 'TEXT', 'detarame' ], [ false ] ]
592
+ ].each do |label, search, cond_list|
593
+ data("TEXT:multipart:#{label}", {
594
+ search: search,
595
+ messages: [
596
+ [ MPART_MAIL_TEXT, {} ]
597
+ ].zip(cond_list).map{|msg, cond| [ cond ] + msg }
598
+ })
599
+ end
600
+ [ [ 'us-ascii', %w[ TO foo ], [ true, true, false, false, false, false, false, false ] ],
601
+ [ 'charset', %W[ TO \u306F\u306B\u307B ], [ false, false, false, false, true, false, true, false ], 'utf-8' ]
602
+ ].each do |label, search, cond_list, charset|
603
+ data("TO:#{label}", {
604
+ search: search,
605
+ charset: charset,
606
+ messages: [
607
+ [ "To: foo\r\n" +
608
+ "\r\n" +
609
+ "foo",
610
+ {}
611
+ ],
612
+ [ "To: FOO\r\n" +
613
+ "\r\n" +
614
+ "foo",
615
+ {}
616
+ ],
617
+ [ "To: bar\r\n" +
618
+ "\r\n" +
619
+ "foo",
620
+ {}
621
+ ],
622
+ [ 'foo',
623
+ {}
624
+ ],
625
+ [ "To: =?UTF-8?B?44GE44KN44Gv44Gr44G744G444Go?=\r\n" +
626
+ "\r\n" +
627
+ "foo",
628
+ {},
629
+ ],
630
+ [ "To: =?UTF-8?B?44Gh44KK44Gs44KL44KS?=\r\n" +
631
+ "\r\n" +
632
+ "foo",
633
+ {}
634
+ ],
635
+ [ "To: =?ISO-2022-JP?B?GyRCJCQkbSRPJEskWyRYJEgbKEI=?=\r\n" +
636
+ "\r\n" +
637
+ "foo",
638
+ {},
639
+ ],
640
+ [ "To: =?ISO-2022-JP?B?GyRCJEEkaiRMJGskchsoQg==?=\r\n" +
641
+ "\r\n" +
642
+ "foo",
643
+ {},
644
+ ]
645
+ ].zip(cond_list).map{|msg, cond| [ cond ] + msg }
646
+ })
647
+ end
648
+ [ [ 'us-ascii', %w[ BODY foo ], [ true, true, true, false, false ] ],
649
+ [ 'us-ascii:no_match', %w[ BODY bar ], [ false, false, false, false, false ] ],
650
+ [ 'utf-8', %W[ BODY \u306F\u306B\u307B ], [ false, false, false, true, true ] ]
651
+ ].each do |label, search, cond_list|
652
+ data("BODY:charset:#{label}", {
653
+ search: search,
654
+ charset: 'utf-8',
655
+ messages: [
656
+ [ "Content-Type: text/plain\r\n" +
657
+ "\r\n" +
658
+ "foo",
659
+ {}
660
+ ],
661
+ [ "Content-Type: text/plain; charset=utf-8\r\n" +
662
+ "\r\n" +
663
+ "foo",
664
+ {}
665
+ ],
666
+ [ "Content-Type: text/plain; charset=iso-2022-jp\r\n" +
667
+ "\r\n" +
668
+ "foo",
669
+ {}
670
+ ],
671
+ [ "Content-Type: text/plain; charset=utf-8\r\n" +
672
+ "\r\n" +
673
+ "\u3053\u3093\u306B\u3061\u306F\r\n" +
674
+ "\u3044\u308D\u306F\u306B\u307B\u3078\u3068\r\n" +
675
+ "\u3042\u3044\u3046\u3048\u304A\r\n",
676
+ {}
677
+ ],
678
+ [ "Content-Type: text/plain; charset=iso-2022-jp\r\n" +
679
+ "\r\n" +
680
+ "\e$B$3$s$K$A$O\e(B\r\n\e$B$$$m$O$K$[$X$H\e(B\r\n\e$B$\"$$$&$($*\e(B\r\n",
681
+ {}
682
+ ]
683
+ ].zip(cond_list).map{|msg, cond| [ cond ] + msg }
684
+ })
685
+ end
686
+ [ [ 'us-ascii:header_and_body', %w[ TEXT foo ], [ true, true, true, false, false, false, false ] ],
687
+ [ 'us-ascii:body', %w[ TEXT bar ], [ false, true, true, false, false, false, false ] ],
688
+ [ 'us-ascii:no_match', %w[ TEXT baz ], [ false, false, false, false, false, false, false ] ],
689
+ [ 'utf-8:header_and_body', %W[ TEXT \u306F\u306B\u307B ], [ false, false, false, true, true, true, true ] ]
690
+ ].each do |label, search, cond_list|
691
+ data("TEXT:charset:#{label}", {
692
+ search: search,
693
+ charset: 'utf-8',
694
+ messages: [
695
+ [ "Content-Type: text/plain\r\n" +
696
+ "\r\n" +
697
+ "foo",
698
+ {}
699
+ ],
700
+ [ "Content-Type: text/plain; charset=utf-8\r\n" +
701
+ "X-foo: dummy\r\n" +
702
+ "\r\n" +
703
+ "bar",
704
+ {}
705
+ ],
706
+ [ "Content-Type: text/plain; charset=iso-2022-jp\r\n" +
707
+ "X-dummy: foo\r\n" +
708
+ "\r\n" +
709
+ "bar",
710
+ {}
711
+ ],
712
+ [ "Content-Type: text/plain; charset=utf-8\r\n" +
713
+ "\r\n" +
714
+ "\u3053\u3093\u306B\u3061\u306F\r\n" +
715
+ "\u3044\u308D\u306F\u306B\u307B\u3078\u3068\r\n" +
716
+ "\u3042\u3044\u3046\u3048\u304A\r\n",
717
+ {}
718
+ ],
719
+ [ "Content-Type: text/plain; charset=iso-2022-jp\r\n" +
720
+ "\r\n" +
721
+ "\e$B$3$s$K$A$O\e(B\r\n\e$B$$$m$O$K$[$X$H\e(B\r\n\e$B$\"$$$&$($*\e(B\r\n",
722
+ {}
723
+ ],
724
+ [ "Subject: =?UTF-8?B?44GE44KN44Gv44Gr44G744G444Go?=\r\n" +
725
+ "\r\n" +
726
+ "",
727
+ {}
728
+ ],
729
+ [ "Subject: =?ISO-2022-JP?B?GyRCJCQkbSRPJEskWyRYJEgbKEI=?=\r\n" +
730
+ "\r\n" +
731
+ "",
732
+ {},
733
+ ]
734
+ ].zip(cond_list).map{|msg, cond| [ cond] + msg }
735
+ })
736
+ end
737
+ data('msg_set', {
738
+ search: %w[ 1,2,* ],
739
+ messages: [
740
+ [ true, 'foo', {} ],
741
+ [ true, 'foo', {} ],
742
+ [ false, 'foo', {} ],
743
+ [ false, 'foo', {} ],
744
+ [ true, 'foo', {} ]
745
+ ]
746
+ })
747
+ [ [ 'list', %w[ ANSWERED FLAGGED ], [ true, false, false, false ] ],
748
+ [ 'group', [ [ :group, 'ANSWERED', 'FLAGGED' ] ], [ true, false, false, false ] ]
749
+ ].each do |label, search, cond_list|
750
+ data("group:#{label}", {
751
+ search: search,
752
+ messages: [
753
+ [ 'foo', { answered: true, flagged: true } ],
754
+ [ 'foo', { answered: true, flagged: false } ],
755
+ [ 'foo', { answered: false, flagged: true } ],
756
+ [ 'foo', { answered: false, flagged: false } ]
757
+ ].zip(cond_list).map{|msg, cond| [ cond ] + msg }
758
+ })
759
+ end
760
+ def test_parse_and_search(data)
761
+ search = data[:search]
762
+ charset = data[:charset]
763
+ msg_list = data[:messages]
764
+
765
+ make_search_parser(charset: charset) {
766
+ for _, msg, flags, *optional in msg_list
767
+ uid = add_msg(msg, *optional)
768
+ for name, value in flags
769
+ set_msg_flag(uid, name.to_s, value)
770
+ end
771
+ end
772
+ }
773
+
774
+ search = search.map{|key| (key.is_a? String) ? key.b : key }
775
+ parse_search_key(search) {
776
+ msg_list.each_with_index do |(expected_cond, *_), i|
777
+ assert_search_cond(i, expected_cond, "message index: #{i}")
778
+ end
779
+ }
780
+ end
781
+
782
+ %w[ BCC BODY CC FROM KEYWORD SUBJECT TEXT TO UNKEYWORD ].each do |key|
783
+ data("#{key}:no_string", [
784
+ [ key ],
785
+ /need for a search string/
786
+ ])
787
+ data("#{key}:not_string", [
788
+ [ key, [ :group, 'foo' ] ],
789
+ /search string expected as <String> but was/
790
+ ])
791
+ end
792
+ %w[ BEFORE ON SENTBEFORE SENTON SENTSINCE SINCE ].each do |key|
793
+ data("#{key}:no_date", [
794
+ [ key ],
795
+ /need for a search date/
796
+ ])
797
+ data("#{key}:invalid_date", [
798
+ [ key, '99-Nov-2013' ],
799
+ /search date is invalid/
800
+ ])
801
+ data("#{key}:not_date", [
802
+ [ key, [ :group, '08-Nov-2013'] ],
803
+ /search date string expected as <String> but was/
804
+ ])
805
+ end
806
+ %w[ LARGER SMALLER ].each do |key|
807
+ data("#{key}:no_size", [
808
+ %w[ LARGER ],
809
+ /need for a octet size/
810
+ ])
811
+ data("#{key}:invalid_size", [
812
+ %w[ LARGER nonum ],
813
+ /octet size is expected as numeric string but was/
814
+ ])
815
+ data("#{key}:not_size", [
816
+ [ 'LARGER', [ :group, '3' ] ],
817
+ /octet size is expected as numeric string but was/
818
+ ])
819
+ end
820
+ data('HEADER:no_field_name', [
821
+ %w[ HEADER ],
822
+ /need for a search string/
823
+ ])
824
+ data('HEADER:no_string', [
825
+ %w[ HEADER Received ],
826
+ /need for a search string/
827
+ ])
828
+ data('HEADER:not_field_name', [
829
+ [ 'HEADER', [ :group, 'Received' ], 'foo' ],
830
+ /search string expected as <String> but was/
831
+ ])
832
+ data('HEADER:not_string', [
833
+ [ 'HEADER', 'Received', [ :group, 'foo' ] ],
834
+ /search string expected as <String> but was/
835
+ ])
836
+ data('NOT:no_search_key', [
837
+ %w[ NOT ],
838
+ 'unexpected end of search key.'
839
+ ])
840
+ data('OR:no_left_search_key', [
841
+ %w[ OR ],
842
+ 'unexpected end of search key.'
843
+ ])
844
+ data('OR:no_right_search_key', [
845
+ %w[ OR ANSWERED ],
846
+ 'unexpected end of search key.'
847
+ ])
848
+ [ [ 'string', %w[ detarame ] ],
849
+ [ 'symbol', [ :detarame ] ],
850
+ [ 'array', [ [ :detarame, 'ANSWERED', 'FLAGGED' ] ] ],
851
+ ].each do |label, search_key|
852
+ data("unknown_search_key:#{label}", [
853
+ search_key,
854
+ /unknown search key/
855
+ ])
856
+ end
857
+ def test_search_syntax_error(data)
858
+ search, expected_pattern = data
859
+ make_search_parser
860
+ assert_search_syntax_error(search, expected_pattern)
724
861
  end
725
862
 
726
863
  def test_parse_uid
@@ -741,269 +878,10 @@ Content-Type: text/html
741
878
  assert_search_cond(2, true)
742
879
  }
743
880
 
744
- begin
745
- @parser.parse([ 'UID', 'detarame' ])
746
- rescue
747
- error = $!
748
- end
881
+ error = assert_raise(RIMS::MessageSetSyntaxError) { @parser.parse([ 'UID', 'detarame' ]) }
749
882
  assert_kind_of(RIMS::SyntaxError, error)
750
- end
751
-
752
- def test_parse_unanswered
753
- make_search_parser{
754
- add_msg('foo')
755
- add_msg('foo')
756
- assert_msg_uid(1, 2)
757
-
758
- set_msg_flag(1, 'answered', true)
759
- assert_msg_flag('answered', true, false)
760
- }
761
-
762
- parse_search_key([ 'UNANSWERED' ]) {
763
- assert_search_cond(0, false)
764
- assert_search_cond(1, true)
765
- }
766
- end
767
-
768
- def test_parse_undeleted
769
- make_search_parser{
770
- add_msg('foo')
771
- add_msg('foo')
772
- assert_msg_uid(1, 2)
773
-
774
- set_msg_flag(1, 'deleted', true)
775
- assert_msg_flag('deleted', true, false)
776
- }
777
-
778
- parse_search_key([ 'UNDELETED' ]) {
779
- assert_search_cond(0, false)
780
- assert_search_cond(1, true)
781
- }
782
- end
783
-
784
- def test_parse_undraft
785
- make_search_parser{
786
- add_msg('foo')
787
- add_msg('foo')
788
- assert_msg_uid(1, 2)
789
-
790
- set_msg_flag(1, 'draft', true)
791
- assert_msg_flag('draft', true, false)
792
- }
793
-
794
- parse_search_key([ 'UNDRAFT' ]) {
795
- assert_search_cond(0, false)
796
- assert_search_cond(1, true)
797
- }
798
- end
799
-
800
- def test_parse_unflagged
801
- make_search_parser{
802
- add_msg('foo')
803
- add_msg('foo')
804
- assert_msg_uid(1, 2)
805
-
806
- set_msg_flag(1, 'flagged', true)
807
- assert_msg_flag('flagged', true, false)
808
- }
809
-
810
- parse_search_key([ 'UNFLAGGED' ]) {
811
- assert_search_cond(0, false)
812
- assert_search_cond(1, true)
813
- }
814
- end
815
-
816
- def test_parse_unkeyword
817
- make_search_parser{
818
- add_msg('')
819
- assert_msg_uid(1)
820
- }
821
-
822
- parse_search_key([ 'UNKEYWORD', 'foo' ]) {
823
- assert_search_cond(0, true) # always true
824
- }
825
-
826
- assert_search_syntax_error([ 'UNKEYWORD' ], /need for a search string/)
827
- assert_search_syntax_error([ 'UNKEYWORD', [ :group, 'foo' ] ], /search string expected as <String> but was/)
828
- end
829
-
830
- def test_parse_unseen
831
- make_search_parser{
832
- add_msg('foo')
833
- add_msg('foo')
834
- assert_msg_uid(1, 2)
835
-
836
- set_msg_flag(1, 'seen', true)
837
- assert_msg_flag('seen', true, false)
838
- }
839
-
840
- parse_search_key([ 'UNSEEN' ]) {
841
- assert_search_cond(0, false)
842
- assert_search_cond(1, true)
843
- }
844
- end
845
-
846
- def test_parse_msg_set
847
- make_search_parser{
848
- add_msg('foo')
849
- add_msg('foo')
850
- add_msg('foo')
851
- add_msg('foo')
852
- add_msg('foo')
853
- add_msg('foo')
854
- expunge(1, 3, 5)
855
- assert_msg_uid(2, 4, 6)
856
- }
857
-
858
- parse_search_key([ '1,*' ]) {
859
- assert_search_cond(0, true)
860
- assert_search_cond(1, false)
861
- assert_search_cond(2, true)
862
- }
863
-
864
- assert_search_syntax_error([ 'detarame' ], /unknown search key/)
865
- end
866
-
867
- def test_parse_group
868
- make_search_parser{
869
- add_msg('foo')
870
- add_msg('foo')
871
- add_msg('foo')
872
- add_msg('foo')
873
- assert_msg_uid(1, 2, 3, 4)
874
-
875
- set_msg_flag(1, 'answered', true)
876
- set_msg_flag(2, 'answered', true)
877
- set_msg_flag(1, 'flagged', true)
878
- set_msg_flag(3, 'flagged', true)
879
- assert_msg_flag('answered', true, true, false, false)
880
- assert_msg_flag('flagged', true, false, true, false)
881
- }
882
-
883
- parse_search_key([ 'ANSWERED', 'FLAGGED' ]) {
884
- assert_search_cond(0, true)
885
- assert_search_cond(1, false)
886
- assert_search_cond(2, false)
887
- assert_search_cond(3, false)
888
- }
889
-
890
- parse_search_key([ [ :group, 'ANSWERED', 'FLAGGED' ] ]) {
891
- assert_search_cond(0, true)
892
- assert_search_cond(1, false)
893
- assert_search_cond(2, false)
894
- assert_search_cond(3, false)
895
- }
896
-
897
- assert_search_syntax_error([ [ :block, 'ANSWERED', 'FLAGGED' ] ], /unknown search key/)
898
- end
899
-
900
- def test_parse_unknown
901
- make_search_parser{}
902
- assert_search_syntax_error([ :detarame ], /unknown search key/)
903
- end
904
-
905
- def test_parse_charset_body
906
- make_search_parser(charset: 'utf-8') {
907
- add_msg("Content-Type: text/plain\r\n" +
908
- "\r\n" +
909
- "foo")
910
- add_msg("Content-Type: text/plain; charset=utf-8\r\n" +
911
- "\r\n" +
912
- "foo")
913
- add_msg("Content-Type: text/plain; charset=iso-2022-jp\r\n" +
914
- "\r\n" +
915
- "foo")
916
- add_msg("Content-Type: text/plain; charset=utf-8\r\n" +
917
- "\r\n" +
918
- "\u3053\u3093\u306B\u3061\u306F\r\n" +
919
- "\u3044\u308D\u306F\u306B\u307B\u3078\u3068\r\n" +
920
- "\u3042\u3044\u3046\u3048\u304A\r\n")
921
- add_msg("Content-Type: text/plain; charset=iso-2022-jp\r\n" +
922
- "\r\n" +
923
- "\e$B$3$s$K$A$O\e(B\r\n\e$B$$$m$O$K$[$X$H\e(B\r\n\e$B$\"$$$&$($*\e(B\r\n")
924
- assert_msg_uid(1, 2, 3, 4, 5)
925
- }
926
-
927
- parse_search_key([ 'BODY', 'foo' ]) {
928
- assert_search_cond(0, true)
929
- assert_search_cond(1, true)
930
- assert_search_cond(2, true)
931
- assert_search_cond(3, false)
932
- assert_search_cond(4, false)
933
- }
934
-
935
- parse_search_key([ 'BODY', 'bar' ]) {
936
- assert_search_cond(0, false)
937
- assert_search_cond(1, false)
938
- assert_search_cond(2, false)
939
- assert_search_cond(3, false)
940
- assert_search_cond(4, false)
941
- }
942
-
943
- parse_search_key([ 'BODY', "\u306F\u306B\u307B".b ]) {
944
- assert_search_cond(0, false)
945
- assert_search_cond(1, false)
946
- assert_search_cond(2, false)
947
- assert_search_cond(3, true)
948
- assert_search_cond(4, true)
949
- }
950
- end
951
-
952
- def test_parse_charset_text
953
- make_search_parser(charset: 'utf-8') {
954
- add_msg("Content-Type: text/plain\r\n" +
955
- "\r\n" +
956
- "foo")
957
- add_msg("Content-Type: text/plain; charset=utf-8\r\n" +
958
- "X-foo: dummy\r\n" +
959
- "\r\n" +
960
- "bar")
961
- add_msg("Content-Type: text/plain; charset=iso-2022-jp\r\n" +
962
- "X-dummy: foo\r\n" +
963
- "\r\n" +
964
- "bar")
965
- add_msg("Content-Type: text/plain; charset=utf-8\r\n" +
966
- "\r\n" +
967
- "\u3053\u3093\u306B\u3061\u306F\r\n" +
968
- "\u3044\u308D\u306F\u306B\u307B\u3078\u3068\r\n" +
969
- "\u3042\u3044\u3046\u3048\u304A\r\n")
970
- add_msg("Content-Type: text/plain; charset=iso-2022-jp\r\n" +
971
- "\r\n" +
972
- "\e$B$3$s$K$A$O\e(B\r\n\e$B$$$m$O$K$[$X$H\e(B\r\n\e$B$\"$$$&$($*\e(B\r\n")
973
- assert_msg_uid(1, 2, 3, 4, 5)
974
- }
975
-
976
- parse_search_key([ 'TEXT', 'foo' ]) {
977
- assert_search_cond(0, true)
978
- assert_search_cond(1, true)
979
- assert_search_cond(2, true)
980
- assert_search_cond(3, false)
981
- assert_search_cond(4, false)
982
- }
983
-
984
- parse_search_key([ 'TEXT', 'bar' ]) {
985
- assert_search_cond(0, false)
986
- assert_search_cond(1, true)
987
- assert_search_cond(2, true)
988
- assert_search_cond(3, false)
989
- assert_search_cond(4, false)
990
- }
991
-
992
- parse_search_key([ 'TEXT', 'baz' ]) {
993
- assert_search_cond(0, false)
994
- assert_search_cond(1, false)
995
- assert_search_cond(2, false)
996
- assert_search_cond(3, false)
997
- assert_search_cond(4, false)
998
- }
999
-
1000
- parse_search_key([ 'TEXT', "\u306F\u306B\u307B".b ]) {
1001
- assert_search_cond(0, false)
1002
- assert_search_cond(1, false)
1003
- assert_search_cond(2, false)
1004
- assert_search_cond(3, true)
1005
- assert_search_cond(4, true)
1006
- }
883
+ assert_match(/invalid message sequence format/, error.message)
884
+ assert_match(/detarame/, error.message)
1007
885
  end
1008
886
  end
1009
887
  end