mime 0.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,616 @@
1
+ # THIS IS STILL HERE BECAUSE A LOT OF WORK WENT INTO MAKING TESTS AGNOSTIC OF MIME HEADER ORDER.
2
+ # HOWEVER, THINGS GOT MESSY SO REVERTED BACK TO COMPARING AGAINST KNOWN GOOD MESSAGES.
3
+ # THIS IS HERE JUST IN CASE. IN THE FUTURE, REMOVE IT IF THERE IS NO NEED FOR IT.
4
+
5
+ gem 'minitest' # minitest in 1.9 stdlib is crufty
6
+ require 'minitest/autorun'
7
+ require 'mime'
8
+
9
+ # may be able to remove in 2.0
10
+ Encoding.default_external = 'ASCII-8BIT'
11
+
12
+ class MIMETest < Minitest::Test
13
+
14
+ CRLF = "\r\n"
15
+ HDR_BDY_SEP = CRLF * 2
16
+ MESSAGE_ID = /^Message-ID: <\d+@\d+>\r\n/
17
+ CONTENT_ID = /^Content-ID: <\d+\.\d+>\r\n/
18
+
19
+ CDISPOSITION = /^Content-Disposition: .*\r\n/
20
+ CTYPE = /^Content-Type: .*\r\n/
21
+
22
+ DATE = /^Date: ..., \d{1,2} ... \d{4} \d\d:\d\d:\d\d -\d{4}\r\n/
23
+ VERSION = /^MIME-Version: 1.0 \(Ruby MIME v\d\.\d\)\r\n/
24
+ #BOUNDARY = /^--Boundary_\d+\.\d+\r\n/
25
+ BOUNDARY = /^--Boundary_\d+\.\d+(--)?\r\n/
26
+ BOUNDARY_LAST = /^--Boundary_\d+\.\d+--\r\n/
27
+ CID = /cid:\d+\.\d+/
28
+
29
+ CTYPE_TEXT_PLAIN = /^Content-Type: text\/plain; charset=us-ascii\r\n/
30
+ CTYPE_TEXT_HTML = /^Content-Type: text\/html\r\n/
31
+ CTYPE_TEXT_XML = /^Content-Type: text\/xml\r\n/
32
+ CTYPE_IMAGE_JPEG = /^Content-Type: image\/jpeg\r\n/
33
+ CTYPE_IMAGE_PNG = /^Content-Type: image\/png\r\n/
34
+ CTYPE_VIDEO_MPEG = /^Content-Type: video\/mpeg\r\n/
35
+ CTYPE_AUDIO_MIDI = /^Content-Type: audio\/midi\r\n/
36
+ CTYPE_APPLICATION = /^Content-Type: application\/octet-stream\r\n/
37
+
38
+ CTYPE_MPART_FORM = /^Content-Type: multipart\/form-data; boundary=Boundary_\d+\.\d+\r\n/
39
+ CTYPE_MPART_ALT = /^Content-Type: multipart\/alternative; boundary=Boundary_\d+\.\d+\r\n/
40
+ CTYPE_MPART_MIXED = /^Content-Type: multipart\/mixed; boundary=Boundary_\d+\.\d+\r\n/
41
+
42
+ XFER_ENC_BINARY = /^Content-Transfer-Encoding: binary\r\n/
43
+ XFER_ENC_8BIT = /^Content-Transfer-Encoding: 8bit\r\n/
44
+
45
+ BINARY_DATA = '0110000101110101011001000110100101101111'
46
+
47
+ def test_make_top_level_rfc2822_message
48
+ rfc2822_msg = MIME::Message.new
49
+ rfc2822_msg.body = "\r\nmessage body"
50
+ msg = rfc2822_msg.to_s
51
+
52
+ assert_match MESSAGE_ID, msg
53
+ assert_match DATE, msg
54
+ assert_match VERSION, msg
55
+ assert_equal_num_headers 3, msg
56
+ assert_equal_body "message body", msg
57
+ end
58
+
59
+ # TODO remove audio.msg
60
+ def test_make_audio_message
61
+ audio_media = MIME::AudioMedia.new(BINARY_DATA, 'audio/midi')
62
+ audio_media.content_transfer_encoding = 'binary'
63
+ msg = MIME::Message.new(audio_media).to_s
64
+
65
+ [CONTENT_ID, XFER_ENC_BINARY, CTYPE_AUDIO_MIDI].each do |header|
66
+ assert_match header, msg
67
+ end
68
+ assert_equal_num_headers 6, msg
69
+ assert_equal_body BINARY_DATA, msg
70
+ end
71
+
72
+ # TODO remove expected_mime_msg = IO.read(sd('/application.msg'))
73
+ def test_make_application_message
74
+ application_media = MIME::ApplicationMedia.new(BINARY_DATA)
75
+ application_media.content_transfer_encoding = 'binary'
76
+ msg = MIME::Message.new(application_media).to_s
77
+
78
+ [CONTENT_ID, XFER_ENC_BINARY, CTYPE_APPLICATION].each do |header|
79
+ assert_match header, msg
80
+ end
81
+ assert_equal_num_headers 6, msg
82
+ assert_equal_body BINARY_DATA, msg
83
+ end
84
+
85
+ # TODO remove image.msg
86
+ def test_make_image_message
87
+ image = IO.read(sd('/image.jpg'))
88
+ image_media = MIME::ImageMedia.new(image)
89
+ image_media.content_type = 'image/jpeg'
90
+ image_media.content_transfer_encoding = 'binary'
91
+ msg = MIME::Message.new(image_media).to_s
92
+
93
+ [CONTENT_ID, XFER_ENC_BINARY, CTYPE_IMAGE_JPEG].each do |header|
94
+ assert_match header, msg
95
+ end
96
+
97
+ assert_equal_num_headers 6, msg
98
+ assert_equal_body image, msg
99
+ end
100
+
101
+ # TODO remove text.msg
102
+ def test_make_text_message
103
+ body = 'a plain text message'
104
+ msg = MIME::Message.new(MIME::TextMedia.new(body)).to_s
105
+
106
+ [CTYPE_TEXT_PLAIN].each do |header|
107
+ assert_match header, msg
108
+ end
109
+ end
110
+
111
+ # TODO remove video.msg
112
+ def test_make_video_message
113
+ video_media = MIME::VideoMedia.new(BINARY_DATA)
114
+ video_media.content_type = 'video/mpeg'
115
+ video_media.content_transfer_encoding = 'binary'
116
+ msg = MIME::Message.new(video_media).to_s
117
+
118
+ [CONTENT_ID, XFER_ENC_BINARY, CTYPE_VIDEO_MPEG].each do |header|
119
+ assert_match header, msg
120
+ end
121
+ assert_equal_num_headers 6, msg
122
+ assert_equal_body BINARY_DATA, msg
123
+ end
124
+
125
+ def test_no_instantiation_of_abstract_classes
126
+ e = MIME::AbstractClassError
127
+ assert_raises(e) {MIME::MediaType.new(nil, nil)}
128
+ assert_raises(e) {MIME::DiscreteMediaType.new(nil)}
129
+ assert_raises(e) {MIME::CompositeMediaType.new(nil)}
130
+ assert_raises(e) {MIME::MultipartMedia.new(nil)}
131
+ end
132
+
133
+ # def test_boundaries
134
+ # # CREATE a multipart message; take simplified version of next form data
135
+ # # test
136
+ # e = msg.scan(BOUNDARY).each
137
+ # first_boundary = e.next
138
+ # assert_equal first_boundary, e.next
139
+ # assert_equal first_boundary, e.next
140
+ # last_boundary = e.next
141
+ # refute_equal first_boundary, last_boundary
142
+ # assert_match /^--Boundary_\d+\.\d+\r\n/, first_boundary
143
+ # assert_match /^--Boundary_\d+\.\d+--\r\n/, last_boundary
144
+ # end
145
+ #
146
+ # def test_unique_content_ids_in_multipart_message
147
+ #
148
+ # content_ids = msg.scan(/^Content-ID: <(\d+.\d+)>/)
149
+ # assert_equal 4, content_ids.flatten.uniq.size # all IDs must be unique
150
+ # end
151
+
152
+ # TODO rm multipart_form_data_text.msg
153
+ def test_multipart_form_data_with_text_entities
154
+ txt_data = 'text body'
155
+ htm_data = IO.read(sd('data.htm'))
156
+ xml_data = IO.read(sd('data.xml'))
157
+
158
+ txt = MIME::TextMedia.new('text body')
159
+ htm = MIME::TextMedia.new(htm_data, 'text/html')
160
+ xml = MIME::TextMedia.new(xml_data, 'text/xml')
161
+
162
+ form_data = MIME::MultipartMedia::FormData.new
163
+ form_data.add_entity txt, 'txt'
164
+ form_data.add_entity htm, 'htm'
165
+ form_data.add_entity xml, 'xml'
166
+ msg = form_data.to_s
167
+
168
+ parts = msg.split(BOUNDARY)
169
+ assert_equal 5, parts.size, 'main header, 3 entities, last boundary'
170
+ assert_equal '--', parts.pop, 'remnant of last boundary marker'
171
+
172
+ # main header
173
+ assert_match CONTENT_ID, parts[0]
174
+ assert_match CTYPE_MPART_FORM, parts[0]
175
+ assert_equal_num_headers 2, parts[0]
176
+ assert_equal_body '', parts[0]
177
+
178
+ # xml entity
179
+ assert_match CONTENT_ID, parts[1]
180
+ assert_match CTYPE_TEXT_XML, parts[1]
181
+ assert_match /^Content-Disposition: form-data; name="xml"\r\n/, parts[1]
182
+ assert_equal_num_headers 3, parts[1]
183
+ assert_equal_body xml_data, parts[1]
184
+
185
+ # html entity
186
+ assert_match CONTENT_ID, parts[2]
187
+ assert_match CTYPE_TEXT_HTML, parts[2]
188
+ assert_match /^Content-Disposition: form-data; name="htm"\r\n/, parts[2]
189
+ assert_equal_num_headers 3, parts[2]
190
+ assert_equal_body htm_data, parts[2]
191
+
192
+ # text entity
193
+ assert_match CONTENT_ID, parts[3]
194
+ assert_match CTYPE_TEXT_PLAIN, parts[3]
195
+ assert_match /^Content-Disposition: form-data; name="txt"\r\n/, parts[3]
196
+ assert_equal_num_headers 3, parts[3]
197
+ assert_equal_body txt_data, parts[3]
198
+ end
199
+
200
+ # TODO test that only basename of the file is included in header
201
+ # test if no filename is used, then probably no filename param
202
+ # see next test for ideas
203
+ def test_content_disposition_filename
204
+
205
+ pass
206
+ end
207
+
208
+ # rm multipart_form_data_file.msg
209
+ # rm multipart_form_data_file_and_text.msg
210
+ def test_multipart_form_data_with_text_and_file_entities
211
+ img1_filename = 'image.jpg'
212
+ img2_filename = 'ruby.png'
213
+ img1_data = IO.read(sd(img1_filename))
214
+ img2_data = IO.read(sd(img2_filename))
215
+ img1 = MIME::ImageMedia.new(img1_data, 'image/jpeg')
216
+ img2 = MIME::ImageMedia.new(img2_data, 'image/png')
217
+ img1.content_transfer_encoding = '8bit'
218
+ img2.content_transfer_encoding = '8bit'
219
+
220
+ desc_data = 'This is plain text description of images.'
221
+ desc = MIME::TextMedia.new(desc_data)
222
+
223
+ form_data = MIME::MultipartMedia::FormData.new
224
+ form_data.add_entity desc, 'description'
225
+ form_data.add_entity img2, 'image_2', img2_filename
226
+ form_data.add_entity img1, 'image_1', img1_filename
227
+ msg = form_data.to_s
228
+
229
+ parts = msg.split(BOUNDARY)
230
+ assert_equal 5, parts.size, 'main header, 3 entities, last boundary'
231
+ assert_equal '--', parts.pop, 'remnant of last boundary marker'
232
+
233
+ # main header
234
+ assert_match CONTENT_ID, parts[0]
235
+ assert_match CTYPE_MPART_FORM, parts[0]
236
+ assert_equal_num_headers 2, parts[0]
237
+ assert_equal_body '', parts[0]
238
+
239
+ # first image entity
240
+ assert_match CONTENT_ID, parts[1]
241
+ assert_match CTYPE_IMAGE_JPEG, parts[1]
242
+ assert_match XFER_ENC_8BIT, parts[1]
243
+ assert_match /^Content-Disposition: form-data; name="image_1"; filename="#{img1_filename}"\r\n/, parts[1]
244
+ assert_equal_num_headers 4, parts[1]
245
+ assert_equal_body img1_data, parts[1]
246
+
247
+ # second image entity
248
+ assert_match CONTENT_ID, parts[2]
249
+ assert_match CTYPE_IMAGE_PNG, parts[2]
250
+ assert_match XFER_ENC_8BIT, parts[2]
251
+ assert_match /^Content-Disposition: form-data; name="image_2"; filename="#{img2_filename}"\r\n/, parts[2]
252
+ assert_equal_num_headers 4, parts[2]
253
+ assert_equal_body img2_data, parts[2]
254
+
255
+ # plain text entity
256
+ assert_match CONTENT_ID, parts[3]
257
+ assert_match CTYPE_TEXT_PLAIN, parts[3]
258
+ assert_match /^Content-Disposition: form-data; name="description"\r\n/, parts[3]
259
+ assert_equal_num_headers 3, parts[3]
260
+ assert_equal_body desc_data, parts[3]
261
+ end
262
+
263
+ # rm plain_text_email.msg
264
+ def test_construction_of_plain_text_email_message
265
+ subject = 'This is an important email'
266
+ body = 'This is the all important email body.'
267
+
268
+ email_msg = MIME::Message.new
269
+ email_msg.to = {
270
+ 'john@example.com' => 'John',
271
+ 'paul@example.com' => nil,
272
+ 'mary@example.com' => 'Mary'
273
+ }
274
+ email_msg.cc = {'Head Honcho' => 'boss@example.com'}
275
+ email_msg.from = {'jane@example.com' => nil}
276
+ email_msg.subject = subject
277
+ email_msg.body = MIME::TextMedia.new(body)
278
+
279
+ msg = email_msg.to_s
280
+
281
+ assert_equal_num_headers 9, msg
282
+ assert_match MESSAGE_ID, msg
283
+ assert_match CONTENT_ID, msg
284
+ assert_match CTYPE_TEXT_PLAIN, msg
285
+ assert_match DATE, msg
286
+ assert_match VERSION, msg
287
+ assert_match /^Subject: #{subject}\r\n/, msg
288
+ assert_match /^From: jane@example.com\r\n/, msg
289
+ assert_match /^To: .+, .+, .+\r\n/, msg # 3 addresses
290
+ assert_match /^To: .*John <john@example.com>.*\r\n/, msg
291
+ assert_match /^To: .*Mary <mary@example.com>.*\r\n/, msg
292
+ assert_match /^To: .*paul@example.com.*\r\n/, msg
293
+ assert_match /^Cc: boss@example.com <Head Honcho>\r\n/, msg
294
+ assert_equal_body body, msg
295
+ end
296
+
297
+ def test_content_type_detection
298
+ (o = Object.new).extend(MIME::ContentTypes)
299
+
300
+ # test using file path, relative and absolute
301
+ assert_equal 'application/pdf', o.file_type('book.pdf')
302
+ assert_equal 'video/quicktime', o.file_type('mini.mov')
303
+ assert_equal 'application/octet-stream', o.file_type('dsk.iso')
304
+ assert_equal 'audio/mpeg', o.file_type('/tmp/song.mp3')
305
+ assert_equal 'text/css', o.file_type('/tmp/main.css')
306
+ assert_equal nil, o.file_type('unknown.yyy')
307
+
308
+ # test using file object
309
+ img_type = open(sd('ruby.png')) {|f| o.file_type(f)}
310
+ assert_equal 'image/png', img_type
311
+ refute_equal 'image/jpeg', img_type
312
+ end
313
+
314
+ def test_object_instantiation_using_discrete_media_factory
315
+ app_file = sd('book.pdf')
316
+ audio_file = sd('song.mp3')
317
+ text_file = sd('data.xml')
318
+ video_file = sd('mini.mov')
319
+ image_file = sd('image.jpg')
320
+ unknown_file = sd('unknown.yyy')
321
+
322
+ dmf = MIME::DiscreteMediaFactory
323
+
324
+ # test using file path
325
+ assert_kind_of MIME::ApplicationMedia, dmf.create(app_file)
326
+ assert_kind_of MIME::AudioMedia, dmf.create(audio_file)
327
+ assert_kind_of MIME::TextMedia, dmf.create(text_file)
328
+ assert_kind_of MIME::VideoMedia, dmf.create(video_file)
329
+ assert_kind_of MIME::ImageMedia, dmf.create(image_file)
330
+
331
+ # test using file object
332
+ open(image_file) do |image_file_obj|
333
+ assert_kind_of MIME::ImageMedia, dmf.create(image_file_obj)
334
+ end
335
+ open(text_file) do |text_file_obj|
336
+ assert_kind_of MIME::TextMedia, dmf.create(text_file_obj)
337
+ end
338
+
339
+ # raise for unknown file path and File object
340
+ assert_raises(MIME::UnknownContentError) {dmf.create(unknown_file)}
341
+ open(unknown_file) do |unknown_file_obj|
342
+ assert_raises(MIME::UnknownContentError) {dmf.create(unknown_file_obj)}
343
+ end
344
+ end
345
+
346
+ def test_discrete_media_factory_creates_path_singleton_method
347
+ pdf_file_path = sd('book.pdf')
348
+
349
+ media_obj = MIME::DiscreteMediaFactory.create(pdf_file_path)
350
+ assert_equal pdf_file_path, media_obj.path
351
+
352
+ open(pdf_file_path) do |pdf_file_obj|
353
+ media_obj = MIME::DiscreteMediaFactory.create(pdf_file_obj)
354
+ assert_equal pdf_file_path, media_obj.path
355
+ end
356
+ end
357
+
358
+ # rm multipart_alternative.msg
359
+ def test_multipart_alternative_message_construction
360
+ txt_data = "*Header*\nmessage"
361
+ htm_data = "<html><body><h1>Header</h1><p>message</p></body></html>"
362
+ txt_msg = MIME::TextMedia.new(txt_data)
363
+ htm_msg = MIME::TextMedia.new(htm_data)
364
+
365
+ txt_msg.content_type = (txt_type = 'text/enhanced; charset=us-ascii')
366
+ htm_msg.content_type = (htm_type = 'text/html; charset=iso-8859-1')
367
+
368
+ alt_msg = MIME::MultipartMedia::Alternative.new
369
+ alt_msg.add_entity htm_msg
370
+ alt_msg.add_entity txt_msg
371
+ msg = alt_msg.to_s
372
+
373
+ parts = msg.split(BOUNDARY)
374
+ assert_equal 4, parts.size, 'main header, 2 entities, last boundary'
375
+ assert_equal '--', parts.pop, 'remnant of last boundary marker'
376
+
377
+ # main header
378
+ assert_match CONTENT_ID, parts[0]
379
+ assert_match CTYPE_MPART_ALT, parts[0]
380
+ assert_equal_num_headers 2, parts[0]
381
+ assert_equal_body '', parts[0]
382
+
383
+ # text entity
384
+ assert_match CONTENT_ID, parts[1]
385
+ assert_match /^Content-Type: #{txt_type}\r\n/, parts[1]
386
+ assert_equal_num_headers 2, parts[1]
387
+ assert_equal_body txt_data, parts[1]
388
+
389
+ # html entity
390
+ assert_match CONTENT_ID, parts[2]
391
+ assert_match /^Content-Type: #{htm_type}\r\n/, parts[2]
392
+ assert_equal_num_headers 2, parts[2]
393
+ assert_equal_body htm_data, parts[2]
394
+ end
395
+
396
+ # rm multipart_mixed_inline_and_attachment.msg
397
+ def test_multipart_mixed_with_inline_and_attachment
398
+ mixed_msg = MIME::MultipartMedia::Mixed.new
399
+
400
+ img_filename = 'image.jpg'
401
+ img_data = ''
402
+ open(sd(img_filename)) do |img_file|
403
+ img_data = img_file.read
404
+ img_msg = MIME::ImageMedia.new(img_data, 'image/jpeg')
405
+ mixed_msg.attach_entity(img_msg, 'filename' => img_file.path)
406
+ end
407
+
408
+ txt_data = 'This is plain text.'
409
+ mixed_msg.inline_entity(MIME::TextMedia.new(txt_data))
410
+ msg = mixed_msg.to_s
411
+
412
+ parts = msg.split(BOUNDARY)
413
+ assert_equal 4, parts.size, 'main header, 2 entities, last boundary'
414
+ assert_equal '--', parts.pop, 'remnant of last boundary marker'
415
+
416
+ # main header
417
+ assert_match CONTENT_ID, parts[0]
418
+ assert_match CTYPE_MPART_MIXED, parts[0]
419
+ assert_equal_num_headers 2, parts[0]
420
+ assert_equal_body '', parts[0]
421
+
422
+ # text entity
423
+ assert_match CONTENT_ID, parts[1]
424
+ assert_match CTYPE_TEXT_PLAIN, parts[1]
425
+ assert_match /^Content-Disposition: inline\r\n/, parts[1]
426
+ assert_equal_num_headers 3, parts[1]
427
+ assert_equal_body txt_data, parts[1]
428
+
429
+ # image entity
430
+ assert_match CONTENT_ID, parts[2]
431
+ assert_match CTYPE_IMAGE_JPEG, parts[2]
432
+ assert_match /^Content-Disposition: attachment; filename="#{img_filename}"\r\n/, parts[2]
433
+ assert_equal_num_headers 3, parts[2]
434
+ assert_equal_body img_data, parts[2]
435
+ end
436
+
437
+ # rm multipart_mixed_inline_and_attachment2.msg
438
+ def test_multipart_mixed_message_construction_using_media_factory
439
+ img1 = sd(img1_filename = 'image.jpg')
440
+ img2 = sd(img2_filename = 'ruby.png')
441
+ txt = sd(txt_filename = 'data.htm')
442
+ bot_img = MIME::DiscreteMediaFactory.create(img1)
443
+ top_img = MIME::DiscreteMediaFactory.create(img2)
444
+ top_txt = MIME::DiscreteMediaFactory.create(txt)
445
+
446
+ mixed_msg = MIME::MultipartMedia::Mixed.new
447
+ mixed_msg.attach_entity(bot_img)
448
+ mixed_msg.attach_entity(top_img)
449
+ mixed_msg.inline_entity(top_txt)
450
+ msg = mixed_msg.to_s
451
+
452
+ parts = msg.split(BOUNDARY)
453
+ assert_equal 5, parts.size, 'main header, 2 entities, last boundary'
454
+ assert_equal '--', parts.pop, 'remnant of last boundary marker'
455
+
456
+ # main header
457
+ assert_match CONTENT_ID, parts[0]
458
+ assert_match CTYPE_MPART_MIXED, parts[0]
459
+ assert_equal_num_headers 2, parts[0]
460
+ assert_equal_body '', parts[0]
461
+
462
+ # html entity
463
+ assert_match CONTENT_ID, parts[1]
464
+ assert_match CTYPE_TEXT_HTML, parts[1]
465
+ assert_match /^Content-Disposition: inline; filename="#{txt_filename}"\r\n/, parts[1]
466
+ assert_equal_num_headers 3, parts[1]
467
+ assert_equal_body top_txt.send(:body), parts[1]
468
+
469
+ # png image entity
470
+ assert_match CONTENT_ID, parts[2]
471
+ assert_match CTYPE_IMAGE_PNG, parts[2]
472
+ assert_match /^Content-Disposition: attachment; filename="#{img2_filename}"\r\n/, parts[2]
473
+ assert_equal_num_headers 3, parts[2]
474
+ assert_equal_body top_img.send(:body), parts[2]
475
+
476
+ # jpg image entity
477
+ assert_match CONTENT_ID, parts[3]
478
+ assert_match CTYPE_IMAGE_JPEG, parts[3]
479
+ assert_match /^Content-Disposition: attachment; filename="#{img1_filename}"\r\n/, parts[3]
480
+ assert_equal_num_headers 3, parts[3]
481
+ assert_equal_body bot_img.send(:body), parts[3]
482
+ end
483
+
484
+ def test_multipart_form_data_with_mixed_entity
485
+ txt = MIME::TextMedia.new('Joe Blow')
486
+ img1 = MIME::DiscreteMediaFactory.create(sd('image.jpg'))
487
+ img2 = MIME::DiscreteMediaFactory.create(sd('ruby.png'))
488
+
489
+ mixed_msg = MIME::MultipartMedia::Mixed.new
490
+ mixed_msg.attach_entity(img2)
491
+ mixed_msg.attach_entity(img1)
492
+
493
+ form = MIME::MultipartMedia::FormData.new
494
+ form.add_entity(mixed_msg, 'pics')
495
+ form.add_entity(txt, 'field1')
496
+
497
+ # similar to example 6 in RFC1867
498
+ expected = IO.read(sd('multipart_form_data_mixed.msg'))
499
+
500
+ assert_equal_mime_message expected, form.to_s
501
+
502
+ #IO.write('/tmp/mime.out', msg)
503
+ end
504
+
505
+ # def test_multipart_related_html_message_with_embedded_image
506
+ # img = DiscreteMediaFactory.create(sd('/ruby.png'))
507
+ # img.content_transfer_encoding = 'binary'
508
+ #
509
+ # html_msg = TextMedia.new(<<-html, 'text/html; charset=iso-8859-1')
510
+ # <html>
511
+ # <body>
512
+ # <h1>HTML multipart/related message</h1>
513
+ # <p>txt before pix</p>
514
+ # <img alt="cool ruby" src="cid:#{img.content_id}"/>
515
+ # <p>txt after pix</p>
516
+ # </body>
517
+ # </html>
518
+ # html
519
+ # html_msg.content_transfer_encoding = '7bit'
520
+ #
521
+ # related_msg = MultipartMedia::Related.new
522
+ # related_msg.inline_entity(img)
523
+ # related_msg.add_entity(html_msg)
524
+ #
525
+ # expected = IO.read(sd('/multipart_related.msg'))
526
+ #
527
+ # assert_equal_mime_message expected, related_msg.to_s
528
+ # end
529
+ #
530
+ # def test_multipart_alternative_with_related_html_entity
531
+ # img = DiscreteMediaFactory.create(sd('/ruby.png'))
532
+ # img.content_transfer_encoding = 'binary'
533
+ #
534
+ # html_msg = TextMedia.new(<<-html, 'text/html; charset=iso-8859-1')
535
+ # <html>
536
+ # <body>
537
+ # <h1>HTML multipart/alternative message</h1>
538
+ # <p>txt before pix</p>
539
+ # <img alt="cool ruby" src="cid:#{img.content_id}"/>
540
+ # <p>txt after pix</p>
541
+ # </body>
542
+ # </html>
543
+ # html
544
+ # html_msg.content_transfer_encoding = '7bit'
545
+ #
546
+ # text_msg = TextMedia.new(<<-text)
547
+ # *HTML multipart/alternative message*
548
+ # txt before pix
549
+ # <cool ruby image>
550
+ # txt after pix
551
+ # text
552
+ # text_msg.content_transfer_encoding = '7bit'
553
+ #
554
+ # related_msg = MultipartMedia::Related.new
555
+ # related_msg.inline_entity(img)
556
+ # related_msg.add_entity(html_msg)
557
+ #
558
+ # alt_msg = MultipartMedia::Alternative.new
559
+ # alt_msg.add_entity(related_msg)
560
+ # alt_msg.add_entity(text_msg)
561
+ #
562
+ # expected = IO.read(sd('/multipart_alternative_related.msg'))
563
+ #
564
+ # assert_equal_mime_message expected, alt_msg.to_s
565
+ # end
566
+
567
+
568
+ private
569
+
570
+ #
571
+ # Test the equality of the normalized +expected+ and +actual+ MIME messages.
572
+ #
573
+ def assert_equal_mime_message expected, actual
574
+ assert_equal normalize_message(expected), normalize_message(actual)
575
+ end
576
+
577
+ #
578
+ # Make messages comparable by removing *-ID header values, library version
579
+ # comment in MIME-Version header, Date header value, multipart/related
580
+ # content IDs, and boundaries.
581
+ #
582
+ def normalize_message message
583
+ # these are very delicate REs that are inter-dependent, be careful
584
+ match_id_headers = /-ID: <[^>]+>\r\n/
585
+ match_boundaries = /Boundary_\d+\.\d+/
586
+ match_lib_version = / \(Ruby MIME v\d\.\d\)/
587
+ match_date_header = /^Date: .*\d{4}\r\n/
588
+ match_related_cid = /cid:\d+\.\d+/
589
+
590
+ message.
591
+ gsub(match_related_cid, "cid").
592
+ gsub(match_id_headers, "-ID:\r\n").
593
+ gsub(match_boundaries, "Boundary_").
594
+ sub(match_date_header, "Date:\r\n").
595
+ sub(match_lib_version, " (Ruby MIME v0.0)")
596
+ end
597
+
598
+ def assert_equal_num_headers num, msg
599
+ assert_equal num, msg.split(HDR_BDY_SEP).first.split(CRLF).count
600
+ end
601
+
602
+ def assert_equal_body expected_body, msg
603
+ # FIXME the next line should be a fix
604
+ headers, actual_body = msg.split(HDR_BDY_SEP)
605
+ assert_equal expected_body, actual_body.to_s.chomp
606
+ #assert_equal body, msg.split("\r\n").last
607
+ end
608
+
609
+ #
610
+ # Return the absolute path of +file+ under the test/scaffold directory.
611
+ #
612
+ def sd file
613
+ File.join(File.dirname(__FILE__), 'scaffold', file)
614
+ end
615
+
616
+ end