rims 0.2.6 → 0.2.7

Sign up to get free protection for your applications and to get access to all the features.
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