rims 0.2.1

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.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/ChangeLog +379 -0
  4. data/Gemfile +11 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +566 -0
  7. data/Rakefile +29 -0
  8. data/bin/rims +11 -0
  9. data/lib/rims.rb +45 -0
  10. data/lib/rims/auth.rb +133 -0
  11. data/lib/rims/cksum_kvs.rb +68 -0
  12. data/lib/rims/cmd.rb +809 -0
  13. data/lib/rims/daemon.rb +338 -0
  14. data/lib/rims/db.rb +793 -0
  15. data/lib/rims/error.rb +23 -0
  16. data/lib/rims/gdbm_kvs.rb +76 -0
  17. data/lib/rims/hash_kvs.rb +66 -0
  18. data/lib/rims/kvs.rb +101 -0
  19. data/lib/rims/lock.rb +151 -0
  20. data/lib/rims/mail_store.rb +663 -0
  21. data/lib/rims/passwd.rb +251 -0
  22. data/lib/rims/pool.rb +88 -0
  23. data/lib/rims/protocol.rb +71 -0
  24. data/lib/rims/protocol/decoder.rb +1469 -0
  25. data/lib/rims/protocol/parser.rb +1114 -0
  26. data/lib/rims/rfc822.rb +456 -0
  27. data/lib/rims/server.rb +567 -0
  28. data/lib/rims/test.rb +391 -0
  29. data/lib/rims/version.rb +11 -0
  30. data/load_test/Rakefile +93 -0
  31. data/rims.gemspec +38 -0
  32. data/test/test_auth.rb +174 -0
  33. data/test/test_cksum_kvs.rb +121 -0
  34. data/test/test_config.rb +533 -0
  35. data/test/test_daemon_status_file.rb +169 -0
  36. data/test/test_daemon_waitpid.rb +72 -0
  37. data/test/test_db.rb +602 -0
  38. data/test/test_db_recovery.rb +732 -0
  39. data/test/test_error.rb +97 -0
  40. data/test/test_gdbm_kvs.rb +32 -0
  41. data/test/test_hash_kvs.rb +116 -0
  42. data/test/test_lock.rb +161 -0
  43. data/test/test_mail_store.rb +750 -0
  44. data/test/test_passwd.rb +203 -0
  45. data/test/test_protocol.rb +91 -0
  46. data/test/test_protocol_auth.rb +121 -0
  47. data/test/test_protocol_decoder.rb +6490 -0
  48. data/test/test_protocol_fetch.rb +994 -0
  49. data/test/test_protocol_request.rb +332 -0
  50. data/test/test_protocol_search.rb +974 -0
  51. data/test/test_rfc822.rb +696 -0
  52. metadata +174 -0
@@ -0,0 +1,391 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'fileutils'
4
+ require 'set'
5
+
6
+ module RIMS
7
+ module Test
8
+ module AssertUtility
9
+ def literal(text_string)
10
+ "{#{text_string.bytesize}}\r\n#{text_string}"
11
+ end
12
+ module_function :literal
13
+
14
+ def make_header_text(name_value_pair_list, select_list: [], reject_list: [])
15
+ name_value_pair_list = name_value_pair_list.to_a.dup
16
+ select_set = select_list.map{|name| name.downcase }.to_set
17
+ reject_set = reject_list.map{|name| name.downcase }.to_set
18
+
19
+ name_value_pair_list.select!{|name, value| select_set.include? name.downcase } unless select_set.empty?
20
+ name_value_pair_list.reject!{|name, value| reject_set.include? name.downcase } unless reject_set.empty?
21
+ name_value_pair_list.map{|name, value| "#{name}: #{value}\r\n" }.join('') + "\r\n"
22
+ end
23
+ module_function :make_header_text
24
+
25
+ def message_data_list(msg_data_array)
26
+ msg_data_array.map{|msg_data|
27
+ case (msg_data)
28
+ when String
29
+ msg_data
30
+ when Array
31
+ '(' << message_data_list(msg_data) << ')'
32
+ else
33
+ raise "unknown message data: #{msg_data}"
34
+ end
35
+ }.join(' ')
36
+ end
37
+ module_function :message_data_list
38
+
39
+ def make_body(description)
40
+ reader = RIMS::Protocol::RequestReader.new(StringIO.new('', 'r'), StringIO.new('', 'w'), Logger.new(STDOUT))
41
+ reader.parse(reader.scan_line(description))[0]
42
+ end
43
+ private :make_body
44
+
45
+ def assert_strenc_equal(expected_enc, expected_str, expr_str)
46
+ assert_equal(Encoding.find(expected_enc), expr_str.encoding)
47
+ assert_equal(expected_str.dup.force_encoding(expected_enc), expr_str)
48
+ end
49
+ module_function :assert_strenc_equal
50
+ end
51
+
52
+ module PseudoAuthenticationUtility
53
+ def make_pseudo_time_source(src_time)
54
+ t = src_time
55
+ proc{
56
+ t = t + 1
57
+ t.dup
58
+ }
59
+ end
60
+ module_function :make_pseudo_time_source
61
+
62
+ def make_pseudo_random_string_source(random_seed)
63
+ r = Random.new(random_seed)
64
+ proc{ r.bytes(16).each_byte.map{|c| format('%02x', c ) }.join('') }
65
+ end
66
+ module_function :make_pseudo_random_string_source
67
+ end
68
+
69
+ module ProtocolFetchMailSample
70
+ def make_mail_simple
71
+ @simple_mail = RIMS::RFC822::Message.new(<<-'EOF')
72
+ To: foo@nonet.org
73
+ From: bar@nonet.org
74
+ Subject: test
75
+ MIME-Version: 1.0
76
+ Content-Type: text/plain; charset=us-ascii
77
+ Content-Transfer-Encoding: 7bit
78
+ Date: Fri, 8 Nov 2013 06:47:50 +0900 (JST)
79
+
80
+ Hello world.
81
+ EOF
82
+ end
83
+ private :make_mail_simple
84
+
85
+ def make_mail_multipart
86
+ @mpart_mail = RIMS::RFC822::Message.new(<<-'EOF')
87
+ To: bar@nonet.com
88
+ From: foo@nonet.com
89
+ Subject: multipart test
90
+ MIME-Version: 1.0
91
+ Date: Fri, 8 Nov 2013 19:31:03 +0900
92
+ Content-Type: multipart/mixed; boundary="1383.905529.351297"
93
+
94
+ --1383.905529.351297
95
+ Content-Type: text/plain; charset=us-ascii
96
+
97
+ Multipart test.
98
+ --1383.905529.351297
99
+ Content-Type: application/octet-stream
100
+
101
+ 0123456789
102
+ --1383.905529.351297
103
+ Content-Type: message/rfc822
104
+
105
+ To: bar@nonet.com
106
+ From: foo@nonet.com
107
+ Subject: inner multipart
108
+ MIME-Version: 1.0
109
+ Date: Fri, 8 Nov 2013 19:31:03 +0900
110
+ Content-Type: multipart/mixed; boundary="1383.905529.351298"
111
+
112
+ --1383.905529.351298
113
+ Content-Type: text/plain; charset=us-ascii
114
+
115
+ Hello world.
116
+ --1383.905529.351298
117
+ Content-Type: application/octet-stream
118
+
119
+ 9876543210
120
+ --1383.905529.351298--
121
+ --1383.905529.351297
122
+ Content-Type: multipart/mixed; boundary="1383.905529.351299"
123
+
124
+ --1383.905529.351299
125
+ Content-Type: image/gif
126
+
127
+ --1383.905529.351299
128
+ Content-Type: message/rfc822
129
+
130
+ To: bar@nonet.com
131
+ From: foo@nonet.com
132
+ Subject: inner multipart
133
+ MIME-Version: 1.0
134
+ Date: Fri, 8 Nov 2013 19:31:03 +0900
135
+ Content-Type: multipart/mixed; boundary="1383.905529.351300"
136
+
137
+ --1383.905529.351300
138
+ Content-Type: text/plain; charset=us-ascii
139
+
140
+ HALO
141
+ --1383.905529.351300
142
+ Content-Type: multipart/alternative; boundary="1383.905529.351301"
143
+
144
+ --1383.905529.351301
145
+ Content-Type: text/plain; charset=us-ascii
146
+
147
+ alternative message.
148
+ --1383.905529.351301
149
+ Content-Type: text/html; charset=us-ascii
150
+
151
+ <html>
152
+ <body><p>HTML message</p></body>
153
+ </html>
154
+ --1383.905529.351301--
155
+ --1383.905529.351300--
156
+ --1383.905529.351299--
157
+ --1383.905529.351297--
158
+ EOF
159
+ end
160
+ private :make_mail_multipart
161
+
162
+ def make_mail_mime_subject
163
+ @mime_subject_mail = RIMS::RFC822::Message.new(<<-'EOF')
164
+ Date: Fri, 8 Nov 2013 19:31:03 +0900
165
+ Subject: =?ISO-2022-JP?B?GyRCJEYkOSRIGyhC?=
166
+ From: foo@nonet.com, bar <bar@nonet.com>
167
+ Sender: foo@nonet.com
168
+ Reply-To: foo@nonet.com
169
+ To: alice@test.com, bob <bob@test.com>
170
+ Cc: Kate <kate@test.com>
171
+ Bcc: foo@nonet.com
172
+ In-Reply-To: <20131106081723.5KJU1774292@smtp.testt.com>
173
+ Message-Id: <20131107214750.445A1255B9F@smtp.nonet.com>
174
+
175
+ Hello world.
176
+ EOF
177
+ end
178
+ private :make_mail_mime_subject
179
+
180
+ def make_mail_empty
181
+ @empty_mail = RIMS::RFC822::Message.new('')
182
+ end
183
+ private :make_mail_empty
184
+
185
+ def make_mail_no_body
186
+ @no_body_mail = RIMS::RFC822::Message.new('foo')
187
+ end
188
+ private :make_mail_no_body
189
+
190
+ def make_mail_address_header_pattern
191
+ @address_header_pattern_mail = RIMS::RFC822::Message.new(<<-'EOF')
192
+ To: "foo@nonet.org" <foo@nonet.org>
193
+ From: bar@nonet.org
194
+ Subject: test
195
+ MIME-Version: 1.0
196
+ Content-Type: text/plain; charset=us-ascii
197
+ Content-Transfer-Encoding: 7bit
198
+ Date: Fri, 8 Nov 2013 06:47:50 +0900 (JST)
199
+
200
+ Hello world.
201
+ EOF
202
+ end
203
+ private :make_mail_address_header_pattern
204
+ end
205
+
206
+ module KeyValueStoreTestUtility
207
+ def open_database
208
+ raise NotImplementedError, "not implemented to open `#{@name}'"
209
+ end
210
+
211
+ def make_key_value_store
212
+ raise NotImplementedError, 'not implemented.'
213
+ end
214
+
215
+ def db_close
216
+ @db.close
217
+ end
218
+
219
+ def db_closed?
220
+ @db.closed?
221
+ end
222
+
223
+ def db_fetch(key)
224
+ @db[key]
225
+ end
226
+
227
+ def db_key?(key)
228
+ @db.key? key
229
+ end
230
+
231
+ def db_each_key
232
+ return enum_for(:db_each_key) unless block_given?
233
+ @db.each_key do |key|
234
+ yield(key)
235
+ end
236
+ end
237
+
238
+ def setup
239
+ @base_dir = 'kvs_test_dir'
240
+ FileUtils.mkdir_p(@base_dir)
241
+ @name = File.join(@base_dir, "kvs_test.db.#{$$}")
242
+
243
+ @db = open_database
244
+ @kvs = make_key_value_store
245
+ end
246
+
247
+ def teardown
248
+ db_close unless db_closed?
249
+ FileUtils.rm_rf(@base_dir)
250
+ end
251
+
252
+ def test_store_fetch
253
+ assert_nil(db_fetch('foo'))
254
+ assert_nil(@kvs['foo'])
255
+
256
+ assert_equal('apple', (@kvs['foo'] = 'apple'))
257
+ assert_equal('apple', db_fetch('foo'))
258
+ assert_equal('apple', @kvs['foo'])
259
+
260
+ # update
261
+ assert_equal('banana', (@kvs['foo'] = 'banana'))
262
+ assert_equal('banana', db_fetch('foo'))
263
+ assert_equal('banana', @kvs['foo'])
264
+ end
265
+
266
+ def test_delete
267
+ assert_nil(@kvs.delete('foo'))
268
+
269
+ @kvs['foo'] = 'apple'
270
+ assert_equal('apple', @kvs.delete('foo'))
271
+
272
+ assert_nil(db_fetch('foo'))
273
+ assert_nil(@kvs['foo'])
274
+ end
275
+
276
+ def test_key?
277
+ assert_equal(false, (db_key? 'foo'))
278
+ assert_equal(false, (@kvs.key? 'foo'))
279
+
280
+ @kvs['foo'] = 'apple'
281
+ assert_equal(true, (db_key? 'foo'))
282
+ assert_equal(true, (@kvs.key? 'foo'))
283
+
284
+ # update
285
+ @kvs['foo'] = 'banana'
286
+ assert_equal(true, (db_key? 'foo'))
287
+ assert_equal(true, (@kvs.key? 'foo'))
288
+
289
+ @kvs.delete('foo')
290
+ assert_equal(false, (db_key? 'foo'))
291
+ assert_equal(false, (@kvs.key? 'foo'))
292
+ end
293
+
294
+ def test_each_key
295
+ assert_equal(%w[], db_each_key.to_a)
296
+ assert_equal(%w[], @kvs.each_key.to_a)
297
+
298
+ @kvs['foo'] = 'apple'
299
+ assert_equal(%w[ foo ], db_each_key.to_a)
300
+ assert_equal(%w[ foo ], @kvs.each_key.to_a)
301
+ assert_equal(%w[ apple ], @kvs.each_value.to_a)
302
+ assert_equal([ %w[ foo apple ] ], @kvs.each_pair.to_a)
303
+
304
+ @kvs['bar'] = 'banana'
305
+ assert_equal(%w[ foo bar ].sort, db_each_key.sort)
306
+ assert_equal(%w[ foo bar ].sort, @kvs.each_key.sort)
307
+ assert_equal(%w[ apple banana ].sort, @kvs.each_value.sort)
308
+ assert_equal([ %w[ foo apple ], %w[ bar banana ] ].sort, @kvs.each_pair.sort)
309
+
310
+ @kvs['baz'] = 'orange'
311
+ assert_equal(%w[ foo bar baz ].sort, db_each_key.sort)
312
+ assert_equal(%w[ foo bar baz ].sort, @kvs.each_key.sort)
313
+ assert_equal(%w[ apple banana orange ].sort, @kvs.each_value.sort)
314
+ assert_equal([ %w[ foo apple ], %w[ bar banana ], %w[ baz orange ] ].sort, @kvs.each_pair.sort)
315
+
316
+ @kvs.delete('bar')
317
+ assert_equal(%w[ foo baz ].sort, db_each_key.sort)
318
+ assert_equal(%w[ foo baz ].sort, @kvs.each_key.sort)
319
+ assert_equal(%w[ apple orange ].sort, @kvs.each_value.sort)
320
+ assert_equal([ %w[ foo apple ], %w[ baz orange ] ].sort, @kvs.each_pair.sort)
321
+
322
+ # update
323
+ @kvs['baz'] = 'melon'
324
+ assert_equal(%w[ foo baz ].sort, db_each_key.sort)
325
+ assert_equal(%w[ foo baz ].sort, @kvs.each_key.sort)
326
+ assert_equal(%w[ apple melon ].sort, @kvs.each_value.sort)
327
+ assert_equal([ %w[ foo apple ], %w[ baz melon ] ].sort, @kvs.each_pair.sort)
328
+ end
329
+
330
+ def test_sync
331
+ @kvs.sync
332
+ end
333
+
334
+ def test_close
335
+ @kvs.close
336
+ assert_equal(true, db_closed?)
337
+
338
+ # closed exception
339
+ assert_raise(RuntimeError) { @kvs['foo'] }
340
+ assert_raise(RuntimeError) { @kvs['foo'] = 'apple' }
341
+ assert_raise(RuntimeError) { @kvs.delete('foo') }
342
+ assert_raise(RuntimeError) { @kvs.key? 'foo' }
343
+ assert_raise(RuntimeError) { @kvs.each_key.to_a }
344
+ assert_raise(RuntimeError) { @kvs.each_value.to_a }
345
+ assert_raise(RuntimeError) { @kvs.each_pair.to_a }
346
+ end
347
+ end
348
+
349
+ module KeyValueStoreOpenCloseTestUtility
350
+ def get_kvs_name
351
+ raise NotImplementedError, 'not implemented.'
352
+ end
353
+
354
+ def get_config
355
+ {}
356
+ end
357
+
358
+ def setup
359
+ @base_dir = 'kvs_open_close_test_dir'
360
+ @name = File.join(@base_dir, 'test_kvs')
361
+ FileUtils.mkdir_p(@base_dir)
362
+
363
+ @Test_KeyValueStore = RIMS::KeyValueStore::FactoryBuilder.get_plug_in(get_kvs_name)
364
+ end
365
+
366
+ def teardown
367
+ FileUtils.rm_rf(@base_dir)
368
+ end
369
+
370
+ def test_open_close
371
+ assert_equal(false, (@Test_KeyValueStore.exist? @name))
372
+
373
+ kvs = @Test_KeyValueStore.open_with_conf(@name, get_config)
374
+ begin
375
+ assert_equal(true, (@Test_KeyValueStore.exist? @name))
376
+ ensure
377
+ kvs.close
378
+ end
379
+ assert_equal(true, (@Test_KeyValueStore.exist? @name))
380
+
381
+ kvs.destroy
382
+ assert_equal(false, (@Test_KeyValueStore.exist? @name))
383
+ end
384
+ end
385
+ end
386
+ end
387
+
388
+ # Local Variables:
389
+ # mode: Ruby
390
+ # indent-tabs-mode: nil
391
+ # End:
@@ -0,0 +1,11 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module RIMS
4
+ VERSION = '0.2.1'
5
+ MAILBOX_DATA_STRUCTURE_VERSION = 'mailbox.2'
6
+ end
7
+
8
+ # Local Variables:
9
+ # mode: Ruby
10
+ # indent-tabs-mode: nil
11
+ # End:
@@ -0,0 +1,93 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'rake/clean'
4
+ require 'yaml'
5
+
6
+ def start_pgrp(*cmds)
7
+ pgid = fork{
8
+ Process.setpgid($$, $$)
9
+ sh *cmds
10
+ }
11
+ Process.setpgid(pgid, pgid)
12
+
13
+ pgid
14
+ end
15
+
16
+ def run_server(*options)
17
+ pgid = start_pgrp('rims', 'server', *options)
18
+ begin
19
+ sleep 1
20
+ yield
21
+ ensure
22
+ Process.kill('TERM', -pgid)
23
+ Process.wait
24
+ end
25
+ end
26
+
27
+ def run_server_conf(path, conf, *options)
28
+ mkdir_p(path)
29
+
30
+ path_conf = File.join(path, 'config.yml')
31
+ IO.write(path_conf, conf.to_yaml)
32
+
33
+ run_server('-f', path_conf, *options) do
34
+ yield
35
+ end
36
+ end
37
+
38
+ def get_mail_file_list
39
+ FileUtils.mkdir 'mails' unless (File.directory? 'mails')
40
+ mail_file_list = Dir.entries('mails').map{|name| File.join('mails', name) }.select{|path| File.file? path }
41
+ if (mail_file_list.empty?) then
42
+ warn('WARNING: not found mail file(s) at a directory: mails/')
43
+ end
44
+ mail_file_list
45
+ end
46
+
47
+ def time_shell(*cmds)
48
+ sh 'time', '-p', '--', *cmds
49
+ end
50
+
51
+ PASSWD = {
52
+ 'foo' => 'foo',
53
+ '#postman' => '#postman'
54
+ }
55
+
56
+ USER_CONF = { 'user_list' => [] }
57
+ for user, pass in PASSWD
58
+ USER_CONF['user_list'].push({ 'user' => user, 'pass' => pass })
59
+ end
60
+
61
+ desc 'load test: rims post-mail'
62
+ task :post_mail do
63
+ mail_file_list = get_mail_file_list
64
+ puts "#{mail_file_list.length} mails."
65
+ run_server_conf('post_mail', USER_CONF, *%w[ -v quiet -l info --imap-host=localhost --imap-port=14300 ]) do
66
+ time_shell 'rims', 'post-mail', '--no-verbose', '--host=localhost', '--port=14300', '--username=#postman', "--password=#{PASSWD['#postman']}", 'foo', *mail_file_list
67
+ end
68
+ end
69
+
70
+ CLOBBER.include('post_mail')
71
+
72
+ desc 'load test: rims imap-append'
73
+ task :imap_append do
74
+ mail_file_list = get_mail_file_list
75
+ puts "#{mail_file_list.length} mails."
76
+ run_server_conf('imap_append', USER_CONF, *%w[ -v quiet -l info --imap-host=localhost --imap-port=14300 ]) do
77
+ time_shell 'rims', 'imap-append', '--no-verbose', '--host=localhost', '--port=14300', '--username=foo', "--password=#{PASSWD['foo']}", *mail_file_list
78
+ end
79
+ end
80
+
81
+ desc 'run server'
82
+ task :run_server do
83
+ run_server_conf('imap_server', USER_CONF, *%w[ -v debug -l debug --imap-host=localhost --imap-port=14300 ]) do
84
+ sleep
85
+ end
86
+ end
87
+
88
+ CLOBBER.include('imap_append')
89
+
90
+ # Local Variables:
91
+ # mode: Ruby
92
+ # indent-tabs-mode: nil
93
+ # End: