PageRenderTime 67ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/Lib/test/test_mailbox.py

https://github.com/albertz/CPython
Python | 1211 lines | 1058 code | 77 blank | 76 comment | 29 complexity | 439dd5824edd2713feae3f805114f98f MD5 | raw file
  1. import os
  2. import sys
  3. import time
  4. import stat
  5. import socket
  6. import email
  7. import email.message
  8. import re
  9. import io
  10. import tempfile
  11. from test import support
  12. import unittest
  13. import textwrap
  14. import mailbox
  15. import glob
  16. class TestBase:
  17. all_mailbox_types = (mailbox.Message, mailbox.MaildirMessage,
  18. mailbox.mboxMessage, mailbox.MHMessage,
  19. mailbox.BabylMessage, mailbox.MMDFMessage)
  20. def _check_sample(self, msg):
  21. # Inspect a mailbox.Message representation of the sample message
  22. self.assertIsInstance(msg, email.message.Message)
  23. self.assertIsInstance(msg, mailbox.Message)
  24. for key, value in _sample_headers.items():
  25. self.assertIn(value, msg.get_all(key))
  26. self.assertTrue(msg.is_multipart())
  27. self.assertEqual(len(msg.get_payload()), len(_sample_payloads))
  28. for i, payload in enumerate(_sample_payloads):
  29. part = msg.get_payload(i)
  30. self.assertIsInstance(part, email.message.Message)
  31. self.assertNotIsInstance(part, mailbox.Message)
  32. self.assertEqual(part.get_payload(), payload)
  33. def _delete_recursively(self, target):
  34. # Delete a file or delete a directory recursively
  35. if os.path.isdir(target):
  36. support.rmtree(target)
  37. elif os.path.exists(target):
  38. support.unlink(target)
  39. class TestMailbox(TestBase):
  40. maxDiff = None
  41. _factory = None # Overridden by subclasses to reuse tests
  42. _template = 'From: foo\n\n%s\n'
  43. def setUp(self):
  44. self._path = support.TESTFN
  45. self._delete_recursively(self._path)
  46. self._box = self._factory(self._path)
  47. def tearDown(self):
  48. self._box.close()
  49. self._delete_recursively(self._path)
  50. def test_add(self):
  51. # Add copies of a sample message
  52. keys = []
  53. keys.append(self._box.add(self._template % 0))
  54. self.assertEqual(len(self._box), 1)
  55. keys.append(self._box.add(mailbox.Message(_sample_message)))
  56. self.assertEqual(len(self._box), 2)
  57. keys.append(self._box.add(email.message_from_string(_sample_message)))
  58. self.assertEqual(len(self._box), 3)
  59. keys.append(self._box.add(io.BytesIO(_bytes_sample_message)))
  60. self.assertEqual(len(self._box), 4)
  61. keys.append(self._box.add(_sample_message))
  62. self.assertEqual(len(self._box), 5)
  63. keys.append(self._box.add(_bytes_sample_message))
  64. self.assertEqual(len(self._box), 6)
  65. with self.assertWarns(DeprecationWarning):
  66. keys.append(self._box.add(
  67. io.TextIOWrapper(io.BytesIO(_bytes_sample_message))))
  68. self.assertEqual(len(self._box), 7)
  69. self.assertEqual(self._box.get_string(keys[0]), self._template % 0)
  70. for i in (1, 2, 3, 4, 5, 6):
  71. self._check_sample(self._box[keys[i]])
  72. _nonascii_msg = textwrap.dedent("""\
  73. From: foo
  74. Subject: Falinaptár házhozszállítással. Már rendeltél?
  75. 0
  76. """)
  77. def test_add_invalid_8bit_bytes_header(self):
  78. key = self._box.add(self._nonascii_msg.encode('latin-1'))
  79. self.assertEqual(len(self._box), 1)
  80. self.assertEqual(self._box.get_bytes(key),
  81. self._nonascii_msg.encode('latin-1'))
  82. def test_invalid_nonascii_header_as_string(self):
  83. subj = self._nonascii_msg.splitlines()[1]
  84. key = self._box.add(subj.encode('latin-1'))
  85. self.assertEqual(self._box.get_string(key),
  86. 'Subject: =?unknown-8bit?b?RmFsaW5hcHThciBo4Xpob3pzeuFsbO104XNz'
  87. 'YWwuIE3hciByZW5kZWx06Ww/?=\n\n')
  88. def test_add_nonascii_string_header_raises(self):
  89. with self.assertRaisesRegex(ValueError, "ASCII-only"):
  90. self._box.add(self._nonascii_msg)
  91. self._box.flush()
  92. self.assertEqual(len(self._box), 0)
  93. self.assertMailboxEmpty()
  94. def test_add_that_raises_leaves_mailbox_empty(self):
  95. def raiser(*args, **kw):
  96. raise Exception("a fake error")
  97. support.patch(self, email.generator.BytesGenerator, 'flatten', raiser)
  98. with self.assertRaises(Exception):
  99. self._box.add(email.message_from_string("From: Alphöso"))
  100. self.assertEqual(len(self._box), 0)
  101. self._box.close()
  102. self.assertMailboxEmpty()
  103. _non_latin_bin_msg = textwrap.dedent("""\
  104. From: foo@bar.com
  105. To: báz
  106. Subject: Maintenant je vous présente mon collègue, le pouf célèbre
  107. \tJean de Baddie
  108. Mime-Version: 1.0
  109. Content-Type: text/plain; charset="utf-8"
  110. Content-Transfer-Encoding: 8bit
  111. Да, они летят.
  112. """).encode('utf-8')
  113. def test_add_8bit_body(self):
  114. key = self._box.add(self._non_latin_bin_msg)
  115. self.assertEqual(self._box.get_bytes(key),
  116. self._non_latin_bin_msg)
  117. with self._box.get_file(key) as f:
  118. self.assertEqual(f.read(),
  119. self._non_latin_bin_msg.replace(b'\n',
  120. os.linesep.encode()))
  121. self.assertEqual(self._box[key].get_payload(),
  122. "Да, они летят.\n")
  123. def test_add_binary_file(self):
  124. with tempfile.TemporaryFile('wb+') as f:
  125. f.write(_bytes_sample_message)
  126. f.seek(0)
  127. key = self._box.add(f)
  128. self.assertEqual(self._box.get_bytes(key).split(b'\n'),
  129. _bytes_sample_message.split(b'\n'))
  130. def test_add_binary_nonascii_file(self):
  131. with tempfile.TemporaryFile('wb+') as f:
  132. f.write(self._non_latin_bin_msg)
  133. f.seek(0)
  134. key = self._box.add(f)
  135. self.assertEqual(self._box.get_bytes(key).split(b'\n'),
  136. self._non_latin_bin_msg.split(b'\n'))
  137. def test_add_text_file_warns(self):
  138. with tempfile.TemporaryFile('w+') as f:
  139. f.write(_sample_message)
  140. f.seek(0)
  141. with self.assertWarns(DeprecationWarning):
  142. key = self._box.add(f)
  143. self.assertEqual(self._box.get_bytes(key).split(b'\n'),
  144. _bytes_sample_message.split(b'\n'))
  145. def test_add_StringIO_warns(self):
  146. with self.assertWarns(DeprecationWarning):
  147. key = self._box.add(io.StringIO(self._template % "0"))
  148. self.assertEqual(self._box.get_string(key), self._template % "0")
  149. def test_add_nonascii_StringIO_raises(self):
  150. with self.assertWarns(DeprecationWarning):
  151. with self.assertRaisesRegex(ValueError, "ASCII-only"):
  152. self._box.add(io.StringIO(self._nonascii_msg))
  153. self.assertEqual(len(self._box), 0)
  154. self._box.close()
  155. self.assertMailboxEmpty()
  156. def test_remove(self):
  157. # Remove messages using remove()
  158. self._test_remove_or_delitem(self._box.remove)
  159. def test_delitem(self):
  160. # Remove messages using __delitem__()
  161. self._test_remove_or_delitem(self._box.__delitem__)
  162. def _test_remove_or_delitem(self, method):
  163. # (Used by test_remove() and test_delitem().)
  164. key0 = self._box.add(self._template % 0)
  165. key1 = self._box.add(self._template % 1)
  166. self.assertEqual(len(self._box), 2)
  167. method(key0)
  168. self.assertEqual(len(self._box), 1)
  169. self.assertRaises(KeyError, lambda: self._box[key0])
  170. self.assertRaises(KeyError, lambda: method(key0))
  171. self.assertEqual(self._box.get_string(key1), self._template % 1)
  172. key2 = self._box.add(self._template % 2)
  173. self.assertEqual(len(self._box), 2)
  174. method(key2)
  175. self.assertEqual(len(self._box), 1)
  176. self.assertRaises(KeyError, lambda: self._box[key2])
  177. self.assertRaises(KeyError, lambda: method(key2))
  178. self.assertEqual(self._box.get_string(key1), self._template % 1)
  179. method(key1)
  180. self.assertEqual(len(self._box), 0)
  181. self.assertRaises(KeyError, lambda: self._box[key1])
  182. self.assertRaises(KeyError, lambda: method(key1))
  183. def test_discard(self, repetitions=10):
  184. # Discard messages
  185. key0 = self._box.add(self._template % 0)
  186. key1 = self._box.add(self._template % 1)
  187. self.assertEqual(len(self._box), 2)
  188. self._box.discard(key0)
  189. self.assertEqual(len(self._box), 1)
  190. self.assertRaises(KeyError, lambda: self._box[key0])
  191. self._box.discard(key0)
  192. self.assertEqual(len(self._box), 1)
  193. self.assertRaises(KeyError, lambda: self._box[key0])
  194. def test_get(self):
  195. # Retrieve messages using get()
  196. key0 = self._box.add(self._template % 0)
  197. msg = self._box.get(key0)
  198. self.assertEqual(msg['from'], 'foo')
  199. self.assertEqual(msg.get_payload(), '0\n')
  200. self.assertIsNone(self._box.get('foo'))
  201. self.assertIs(self._box.get('foo', False), False)
  202. self._box.close()
  203. self._box = self._factory(self._path)
  204. key1 = self._box.add(self._template % 1)
  205. msg = self._box.get(key1)
  206. self.assertEqual(msg['from'], 'foo')
  207. self.assertEqual(msg.get_payload(), '1\n')
  208. def test_getitem(self):
  209. # Retrieve message using __getitem__()
  210. key0 = self._box.add(self._template % 0)
  211. msg = self._box[key0]
  212. self.assertEqual(msg['from'], 'foo')
  213. self.assertEqual(msg.get_payload(), '0\n')
  214. self.assertRaises(KeyError, lambda: self._box['foo'])
  215. self._box.discard(key0)
  216. self.assertRaises(KeyError, lambda: self._box[key0])
  217. def test_get_message(self):
  218. # Get Message representations of messages
  219. key0 = self._box.add(self._template % 0)
  220. key1 = self._box.add(_sample_message)
  221. msg0 = self._box.get_message(key0)
  222. self.assertIsInstance(msg0, mailbox.Message)
  223. self.assertEqual(msg0['from'], 'foo')
  224. self.assertEqual(msg0.get_payload(), '0\n')
  225. self._check_sample(self._box.get_message(key1))
  226. def test_get_bytes(self):
  227. # Get bytes representations of messages
  228. key0 = self._box.add(self._template % 0)
  229. key1 = self._box.add(_sample_message)
  230. self.assertEqual(self._box.get_bytes(key0),
  231. (self._template % 0).encode('ascii'))
  232. self.assertEqual(self._box.get_bytes(key1), _bytes_sample_message)
  233. def test_get_string(self):
  234. # Get string representations of messages
  235. key0 = self._box.add(self._template % 0)
  236. key1 = self._box.add(_sample_message)
  237. self.assertEqual(self._box.get_string(key0), self._template % 0)
  238. self.assertEqual(self._box.get_string(key1).split('\n'),
  239. _sample_message.split('\n'))
  240. def test_get_file(self):
  241. # Get file representations of messages
  242. key0 = self._box.add(self._template % 0)
  243. key1 = self._box.add(_sample_message)
  244. with self._box.get_file(key0) as file:
  245. data0 = file.read()
  246. with self._box.get_file(key1) as file:
  247. data1 = file.read()
  248. self.assertEqual(data0.decode('ascii').replace(os.linesep, '\n'),
  249. self._template % 0)
  250. self.assertEqual(data1.decode('ascii').replace(os.linesep, '\n'),
  251. _sample_message)
  252. def test_get_file_can_be_closed_twice(self):
  253. # Issue 11700
  254. key = self._box.add(_sample_message)
  255. f = self._box.get_file(key)
  256. f.close()
  257. f.close()
  258. def test_iterkeys(self):
  259. # Get keys using iterkeys()
  260. self._check_iteration(self._box.iterkeys, do_keys=True, do_values=False)
  261. def test_keys(self):
  262. # Get keys using keys()
  263. self._check_iteration(self._box.keys, do_keys=True, do_values=False)
  264. def test_itervalues(self):
  265. # Get values using itervalues()
  266. self._check_iteration(self._box.itervalues, do_keys=False,
  267. do_values=True)
  268. def test_iter(self):
  269. # Get values using __iter__()
  270. self._check_iteration(self._box.__iter__, do_keys=False,
  271. do_values=True)
  272. def test_values(self):
  273. # Get values using values()
  274. self._check_iteration(self._box.values, do_keys=False, do_values=True)
  275. def test_iteritems(self):
  276. # Get keys and values using iteritems()
  277. self._check_iteration(self._box.iteritems, do_keys=True,
  278. do_values=True)
  279. def test_items(self):
  280. # Get keys and values using items()
  281. self._check_iteration(self._box.items, do_keys=True, do_values=True)
  282. def _check_iteration(self, method, do_keys, do_values, repetitions=10):
  283. for value in method():
  284. self.fail("Not empty")
  285. keys, values = [], []
  286. for i in range(repetitions):
  287. keys.append(self._box.add(self._template % i))
  288. values.append(self._template % i)
  289. if do_keys and not do_values:
  290. returned_keys = list(method())
  291. elif do_values and not do_keys:
  292. returned_values = list(method())
  293. else:
  294. returned_keys, returned_values = [], []
  295. for key, value in method():
  296. returned_keys.append(key)
  297. returned_values.append(value)
  298. if do_keys:
  299. self.assertEqual(len(keys), len(returned_keys))
  300. self.assertEqual(set(keys), set(returned_keys))
  301. if do_values:
  302. count = 0
  303. for value in returned_values:
  304. self.assertEqual(value['from'], 'foo')
  305. self.assertLess(int(value.get_payload()), repetitions)
  306. count += 1
  307. self.assertEqual(len(values), count)
  308. def test_contains(self):
  309. # Check existence of keys using __contains__()
  310. self.assertNotIn('foo', self._box)
  311. key0 = self._box.add(self._template % 0)
  312. self.assertIn(key0, self._box)
  313. self.assertNotIn('foo', self._box)
  314. key1 = self._box.add(self._template % 1)
  315. self.assertIn(key1, self._box)
  316. self.assertIn(key0, self._box)
  317. self.assertNotIn('foo', self._box)
  318. self._box.remove(key0)
  319. self.assertNotIn(key0, self._box)
  320. self.assertIn(key1, self._box)
  321. self.assertNotIn('foo', self._box)
  322. self._box.remove(key1)
  323. self.assertNotIn(key1, self._box)
  324. self.assertNotIn(key0, self._box)
  325. self.assertNotIn('foo', self._box)
  326. def test_len(self, repetitions=10):
  327. # Get message count
  328. keys = []
  329. for i in range(repetitions):
  330. self.assertEqual(len(self._box), i)
  331. keys.append(self._box.add(self._template % i))
  332. self.assertEqual(len(self._box), i + 1)
  333. for i in range(repetitions):
  334. self.assertEqual(len(self._box), repetitions - i)
  335. self._box.remove(keys[i])
  336. self.assertEqual(len(self._box), repetitions - i - 1)
  337. def test_set_item(self):
  338. # Modify messages using __setitem__()
  339. key0 = self._box.add(self._template % 'original 0')
  340. self.assertEqual(self._box.get_string(key0),
  341. self._template % 'original 0')
  342. key1 = self._box.add(self._template % 'original 1')
  343. self.assertEqual(self._box.get_string(key1),
  344. self._template % 'original 1')
  345. self._box[key0] = self._template % 'changed 0'
  346. self.assertEqual(self._box.get_string(key0),
  347. self._template % 'changed 0')
  348. self._box[key1] = self._template % 'changed 1'
  349. self.assertEqual(self._box.get_string(key1),
  350. self._template % 'changed 1')
  351. self._box[key0] = _sample_message
  352. self._check_sample(self._box[key0])
  353. self._box[key1] = self._box[key0]
  354. self._check_sample(self._box[key1])
  355. self._box[key0] = self._template % 'original 0'
  356. self.assertEqual(self._box.get_string(key0),
  357. self._template % 'original 0')
  358. self._check_sample(self._box[key1])
  359. self.assertRaises(KeyError,
  360. lambda: self._box.__setitem__('foo', 'bar'))
  361. self.assertRaises(KeyError, lambda: self._box['foo'])
  362. self.assertEqual(len(self._box), 2)
  363. def test_clear(self, iterations=10):
  364. # Remove all messages using clear()
  365. keys = []
  366. for i in range(iterations):
  367. self._box.add(self._template % i)
  368. for i, key in enumerate(keys):
  369. self.assertEqual(self._box.get_string(key), self._template % i)
  370. self._box.clear()
  371. self.assertEqual(len(self._box), 0)
  372. for i, key in enumerate(keys):
  373. self.assertRaises(KeyError, lambda: self._box.get_string(key))
  374. def test_pop(self):
  375. # Get and remove a message using pop()
  376. key0 = self._box.add(self._template % 0)
  377. self.assertIn(key0, self._box)
  378. key1 = self._box.add(self._template % 1)
  379. self.assertIn(key1, self._box)
  380. self.assertEqual(self._box.pop(key0).get_payload(), '0\n')
  381. self.assertNotIn(key0, self._box)
  382. self.assertIn(key1, self._box)
  383. key2 = self._box.add(self._template % 2)
  384. self.assertIn(key2, self._box)
  385. self.assertEqual(self._box.pop(key2).get_payload(), '2\n')
  386. self.assertNotIn(key2, self._box)
  387. self.assertIn(key1, self._box)
  388. self.assertEqual(self._box.pop(key1).get_payload(), '1\n')
  389. self.assertNotIn(key1, self._box)
  390. self.assertEqual(len(self._box), 0)
  391. def test_popitem(self, iterations=10):
  392. # Get and remove an arbitrary (key, message) using popitem()
  393. keys = []
  394. for i in range(10):
  395. keys.append(self._box.add(self._template % i))
  396. seen = []
  397. for i in range(10):
  398. key, msg = self._box.popitem()
  399. self.assertIn(key, keys)
  400. self.assertNotIn(key, seen)
  401. seen.append(key)
  402. self.assertEqual(int(msg.get_payload()), keys.index(key))
  403. self.assertEqual(len(self._box), 0)
  404. for key in keys:
  405. self.assertRaises(KeyError, lambda: self._box[key])
  406. def test_update(self):
  407. # Modify multiple messages using update()
  408. key0 = self._box.add(self._template % 'original 0')
  409. key1 = self._box.add(self._template % 'original 1')
  410. key2 = self._box.add(self._template % 'original 2')
  411. self._box.update({key0: self._template % 'changed 0',
  412. key2: _sample_message})
  413. self.assertEqual(len(self._box), 3)
  414. self.assertEqual(self._box.get_string(key0),
  415. self._template % 'changed 0')
  416. self.assertEqual(self._box.get_string(key1),
  417. self._template % 'original 1')
  418. self._check_sample(self._box[key2])
  419. self._box.update([(key2, self._template % 'changed 2'),
  420. (key1, self._template % 'changed 1'),
  421. (key0, self._template % 'original 0')])
  422. self.assertEqual(len(self._box), 3)
  423. self.assertEqual(self._box.get_string(key0),
  424. self._template % 'original 0')
  425. self.assertEqual(self._box.get_string(key1),
  426. self._template % 'changed 1')
  427. self.assertEqual(self._box.get_string(key2),
  428. self._template % 'changed 2')
  429. self.assertRaises(KeyError,
  430. lambda: self._box.update({'foo': 'bar',
  431. key0: self._template % "changed 0"}))
  432. self.assertEqual(len(self._box), 3)
  433. self.assertEqual(self._box.get_string(key0),
  434. self._template % "changed 0")
  435. self.assertEqual(self._box.get_string(key1),
  436. self._template % "changed 1")
  437. self.assertEqual(self._box.get_string(key2),
  438. self._template % "changed 2")
  439. def test_flush(self):
  440. # Write changes to disk
  441. self._test_flush_or_close(self._box.flush, True)
  442. def test_popitem_and_flush_twice(self):
  443. # See #15036.
  444. self._box.add(self._template % 0)
  445. self._box.add(self._template % 1)
  446. self._box.flush()
  447. self._box.popitem()
  448. self._box.flush()
  449. self._box.popitem()
  450. self._box.flush()
  451. def test_lock_unlock(self):
  452. # Lock and unlock the mailbox
  453. self.assertFalse(os.path.exists(self._get_lock_path()))
  454. self._box.lock()
  455. self.assertTrue(os.path.exists(self._get_lock_path()))
  456. self._box.unlock()
  457. self.assertFalse(os.path.exists(self._get_lock_path()))
  458. def test_close(self):
  459. # Close mailbox and flush changes to disk
  460. self._test_flush_or_close(self._box.close, False)
  461. def _test_flush_or_close(self, method, should_call_close):
  462. contents = [self._template % i for i in range(3)]
  463. self._box.add(contents[0])
  464. self._box.add(contents[1])
  465. self._box.add(contents[2])
  466. oldbox = self._box
  467. method()
  468. if should_call_close:
  469. self._box.close()
  470. self._box = self._factory(self._path)
  471. keys = self._box.keys()
  472. self.assertEqual(len(keys), 3)
  473. for key in keys:
  474. self.assertIn(self._box.get_string(key), contents)
  475. oldbox.close()
  476. def test_dump_message(self):
  477. # Write message representations to disk
  478. for input in (email.message_from_string(_sample_message),
  479. _sample_message, io.BytesIO(_bytes_sample_message)):
  480. output = io.BytesIO()
  481. self._box._dump_message(input, output)
  482. self.assertEqual(output.getvalue(),
  483. _bytes_sample_message.replace(b'\n', os.linesep.encode()))
  484. output = io.BytesIO()
  485. self.assertRaises(TypeError,
  486. lambda: self._box._dump_message(None, output))
  487. def _get_lock_path(self):
  488. # Return the path of the dot lock file. May be overridden.
  489. return self._path + '.lock'
  490. class TestMailboxSuperclass(TestBase, unittest.TestCase):
  491. def test_notimplemented(self):
  492. # Test that all Mailbox methods raise NotImplementedException.
  493. box = mailbox.Mailbox('path')
  494. self.assertRaises(NotImplementedError, lambda: box.add(''))
  495. self.assertRaises(NotImplementedError, lambda: box.remove(''))
  496. self.assertRaises(NotImplementedError, lambda: box.__delitem__(''))
  497. self.assertRaises(NotImplementedError, lambda: box.discard(''))
  498. self.assertRaises(NotImplementedError, lambda: box.__setitem__('', ''))
  499. self.assertRaises(NotImplementedError, lambda: box.iterkeys())
  500. self.assertRaises(NotImplementedError, lambda: box.keys())
  501. self.assertRaises(NotImplementedError, lambda: box.itervalues().__next__())
  502. self.assertRaises(NotImplementedError, lambda: box.__iter__().__next__())
  503. self.assertRaises(NotImplementedError, lambda: box.values())
  504. self.assertRaises(NotImplementedError, lambda: box.iteritems().__next__())
  505. self.assertRaises(NotImplementedError, lambda: box.items())
  506. self.assertRaises(NotImplementedError, lambda: box.get(''))
  507. self.assertRaises(NotImplementedError, lambda: box.__getitem__(''))
  508. self.assertRaises(NotImplementedError, lambda: box.get_message(''))
  509. self.assertRaises(NotImplementedError, lambda: box.get_string(''))
  510. self.assertRaises(NotImplementedError, lambda: box.get_bytes(''))
  511. self.assertRaises(NotImplementedError, lambda: box.get_file(''))
  512. self.assertRaises(NotImplementedError, lambda: '' in box)
  513. self.assertRaises(NotImplementedError, lambda: box.__contains__(''))
  514. self.assertRaises(NotImplementedError, lambda: box.__len__())
  515. self.assertRaises(NotImplementedError, lambda: box.clear())
  516. self.assertRaises(NotImplementedError, lambda: box.pop(''))
  517. self.assertRaises(NotImplementedError, lambda: box.popitem())
  518. self.assertRaises(NotImplementedError, lambda: box.update((('', ''),)))
  519. self.assertRaises(NotImplementedError, lambda: box.flush())
  520. self.assertRaises(NotImplementedError, lambda: box.lock())
  521. self.assertRaises(NotImplementedError, lambda: box.unlock())
  522. self.assertRaises(NotImplementedError, lambda: box.close())
  523. class TestMaildir(TestMailbox, unittest.TestCase):
  524. _factory = lambda self, path, factory=None: mailbox.Maildir(path, factory)
  525. def setUp(self):
  526. TestMailbox.setUp(self)
  527. if (os.name == 'nt') or (sys.platform == 'cygwin'):
  528. self._box.colon = '!'
  529. def assertMailboxEmpty(self):
  530. self.assertEqual(os.listdir(os.path.join(self._path, 'tmp')), [])
  531. def test_add_MM(self):
  532. # Add a MaildirMessage instance
  533. msg = mailbox.MaildirMessage(self._template % 0)
  534. msg.set_subdir('cur')
  535. msg.set_info('foo')
  536. key = self._box.add(msg)
  537. self.assertTrue(os.path.exists(os.path.join(self._path, 'cur', '%s%sfoo' %
  538. (key, self._box.colon))))
  539. def test_get_MM(self):
  540. # Get a MaildirMessage instance
  541. msg = mailbox.MaildirMessage(self._template % 0)
  542. msg.set_subdir('cur')
  543. msg.set_flags('RF')
  544. key = self._box.add(msg)
  545. msg_returned = self._box.get_message(key)
  546. self.assertIsInstance(msg_returned, mailbox.MaildirMessage)
  547. self.assertEqual(msg_returned.get_subdir(), 'cur')
  548. self.assertEqual(msg_returned.get_flags(), 'FR')
  549. def test_set_MM(self):
  550. # Set with a MaildirMessage instance
  551. msg0 = mailbox.MaildirMessage(self._template % 0)
  552. msg0.set_flags('TP')
  553. key = self._box.add(msg0)
  554. msg_returned = self._box.get_message(key)
  555. self.assertEqual(msg_returned.get_subdir(), 'new')
  556. self.assertEqual(msg_returned.get_flags(), 'PT')
  557. msg1 = mailbox.MaildirMessage(self._template % 1)
  558. self._box[key] = msg1
  559. msg_returned = self._box.get_message(key)
  560. self.assertEqual(msg_returned.get_subdir(), 'new')
  561. self.assertEqual(msg_returned.get_flags(), '')
  562. self.assertEqual(msg_returned.get_payload(), '1\n')
  563. msg2 = mailbox.MaildirMessage(self._template % 2)
  564. msg2.set_info('2,S')
  565. self._box[key] = msg2
  566. self._box[key] = self._template % 3
  567. msg_returned = self._box.get_message(key)
  568. self.assertEqual(msg_returned.get_subdir(), 'new')
  569. self.assertEqual(msg_returned.get_flags(), 'S')
  570. self.assertEqual(msg_returned.get_payload(), '3\n')
  571. def test_consistent_factory(self):
  572. # Add a message.
  573. msg = mailbox.MaildirMessage(self._template % 0)
  574. msg.set_subdir('cur')
  575. msg.set_flags('RF')
  576. key = self._box.add(msg)
  577. # Create new mailbox with
  578. class FakeMessage(mailbox.MaildirMessage):
  579. pass
  580. box = mailbox.Maildir(self._path, factory=FakeMessage)
  581. box.colon = self._box.colon
  582. msg2 = box.get_message(key)
  583. self.assertIsInstance(msg2, FakeMessage)
  584. def test_initialize_new(self):
  585. # Initialize a non-existent mailbox
  586. self.tearDown()
  587. self._box = mailbox.Maildir(self._path)
  588. self._check_basics()
  589. self._delete_recursively(self._path)
  590. self._box = self._factory(self._path, factory=None)
  591. self._check_basics()
  592. def test_initialize_existing(self):
  593. # Initialize an existing mailbox
  594. self.tearDown()
  595. for subdir in '', 'tmp', 'new', 'cur':
  596. os.mkdir(os.path.normpath(os.path.join(self._path, subdir)))
  597. self._box = mailbox.Maildir(self._path)
  598. self._check_basics()
  599. def _check_basics(self, factory=None):
  600. # (Used by test_open_new() and test_open_existing().)
  601. self.assertEqual(self._box._path, os.path.abspath(self._path))
  602. self.assertEqual(self._box._factory, factory)
  603. for subdir in '', 'tmp', 'new', 'cur':
  604. path = os.path.join(self._path, subdir)
  605. mode = os.stat(path)[stat.ST_MODE]
  606. self.assertTrue(stat.S_ISDIR(mode), "Not a directory: '%s'" % path)
  607. def test_list_folders(self):
  608. # List folders
  609. self._box.add_folder('one')
  610. self._box.add_folder('two')
  611. self._box.add_folder('three')
  612. self.assertEqual(len(self._box.list_folders()), 3)
  613. self.assertEqual(set(self._box.list_folders()),
  614. set(('one', 'two', 'three')))
  615. def test_get_folder(self):
  616. # Open folders
  617. self._box.add_folder('foo.bar')
  618. folder0 = self._box.get_folder('foo.bar')
  619. folder0.add(self._template % 'bar')
  620. self.assertTrue(os.path.isdir(os.path.join(self._path, '.foo.bar')))
  621. folder1 = self._box.get_folder('foo.bar')
  622. self.assertEqual(folder1.get_string(folder1.keys()[0]),
  623. self._template % 'bar')
  624. def test_add_and_remove_folders(self):
  625. # Delete folders
  626. self._box.add_folder('one')
  627. self._box.add_folder('two')
  628. self.assertEqual(len(self._box.list_folders()), 2)
  629. self.assertEqual(set(self._box.list_folders()), set(('one', 'two')))
  630. self._box.remove_folder('one')
  631. self.assertEqual(len(self._box.list_folders()), 1)
  632. self.assertEqual(set(self._box.list_folders()), set(('two',)))
  633. self._box.add_folder('three')
  634. self.assertEqual(len(self._box.list_folders()), 2)
  635. self.assertEqual(set(self._box.list_folders()), set(('two', 'three')))
  636. self._box.remove_folder('three')
  637. self.assertEqual(len(self._box.list_folders()), 1)
  638. self.assertEqual(set(self._box.list_folders()), set(('two',)))
  639. self._box.remove_folder('two')
  640. self.assertEqual(len(self._box.list_folders()), 0)
  641. self.assertEqual(self._box.list_folders(), [])
  642. def test_clean(self):
  643. # Remove old files from 'tmp'
  644. foo_path = os.path.join(self._path, 'tmp', 'foo')
  645. bar_path = os.path.join(self._path, 'tmp', 'bar')
  646. with open(foo_path, 'w') as f:
  647. f.write("@")
  648. with open(bar_path, 'w') as f:
  649. f.write("@")
  650. self._box.clean()
  651. self.assertTrue(os.path.exists(foo_path))
  652. self.assertTrue(os.path.exists(bar_path))
  653. foo_stat = os.stat(foo_path)
  654. os.utime(foo_path, (time.time() - 129600 - 2,
  655. foo_stat.st_mtime))
  656. self._box.clean()
  657. self.assertFalse(os.path.exists(foo_path))
  658. self.assertTrue(os.path.exists(bar_path))
  659. def test_create_tmp(self, repetitions=10):
  660. # Create files in tmp directory
  661. hostname = socket.gethostname()
  662. if '/' in hostname:
  663. hostname = hostname.replace('/', r'\057')
  664. if ':' in hostname:
  665. hostname = hostname.replace(':', r'\072')
  666. pid = os.getpid()
  667. pattern = re.compile(r"(?P<time>\d+)\.M(?P<M>\d{1,6})P(?P<P>\d+)"
  668. r"Q(?P<Q>\d+)\.(?P<host>[^:/]*)")
  669. previous_groups = None
  670. for x in range(repetitions):
  671. tmp_file = self._box._create_tmp()
  672. head, tail = os.path.split(tmp_file.name)
  673. self.assertEqual(head, os.path.abspath(os.path.join(self._path,
  674. "tmp")),
  675. "File in wrong location: '%s'" % head)
  676. match = pattern.match(tail)
  677. self.assertIsNotNone(match, "Invalid file name: '%s'" % tail)
  678. groups = match.groups()
  679. if previous_groups is not None:
  680. self.assertGreaterEqual(int(groups[0]), int(previous_groups[0]),
  681. "Non-monotonic seconds: '%s' before '%s'" %
  682. (previous_groups[0], groups[0]))
  683. if int(groups[0]) == int(previous_groups[0]):
  684. self.assertGreaterEqual(int(groups[1]), int(previous_groups[1]),
  685. "Non-monotonic milliseconds: '%s' before '%s'" %
  686. (previous_groups[1], groups[1]))
  687. self.assertEqual(int(groups[2]), pid,
  688. "Process ID mismatch: '%s' should be '%s'" %
  689. (groups[2], pid))
  690. self.assertEqual(int(groups[3]), int(previous_groups[3]) + 1,
  691. "Non-sequential counter: '%s' before '%s'" %
  692. (previous_groups[3], groups[3]))
  693. self.assertEqual(groups[4], hostname,
  694. "Host name mismatch: '%s' should be '%s'" %
  695. (groups[4], hostname))
  696. previous_groups = groups
  697. tmp_file.write(_bytes_sample_message)
  698. tmp_file.seek(0)
  699. self.assertEqual(tmp_file.read(), _bytes_sample_message)
  700. tmp_file.close()
  701. file_count = len(os.listdir(os.path.join(self._path, "tmp")))
  702. self.assertEqual(file_count, repetitions,
  703. "Wrong file count: '%s' should be '%s'" %
  704. (file_count, repetitions))
  705. def test_refresh(self):
  706. # Update the table of contents
  707. self.assertEqual(self._box._toc, {})
  708. key0 = self._box.add(self._template % 0)
  709. key1 = self._box.add(self._template % 1)
  710. self.assertEqual(self._box._toc, {})
  711. self._box._refresh()
  712. self.assertEqual(self._box._toc, {key0: os.path.join('new', key0),
  713. key1: os.path.join('new', key1)})
  714. key2 = self._box.add(self._template % 2)
  715. self.assertEqual(self._box._toc, {key0: os.path.join('new', key0),
  716. key1: os.path.join('new', key1)})
  717. self._box._refresh()
  718. self.assertEqual(self._box._toc, {key0: os.path.join('new', key0),
  719. key1: os.path.join('new', key1),
  720. key2: os.path.join('new', key2)})
  721. def test_refresh_after_safety_period(self):
  722. # Issue #13254: Call _refresh after the "file system safety
  723. # period" of 2 seconds has passed; _toc should still be
  724. # updated because this is the first call to _refresh.
  725. key0 = self._box.add(self._template % 0)
  726. key1 = self._box.add(self._template % 1)
  727. self._box = self._factory(self._path)
  728. self.assertEqual(self._box._toc, {})
  729. # Emulate sleeping. Instead of sleeping for 2 seconds, use the
  730. # skew factor to make _refresh think that the filesystem
  731. # safety period has passed and re-reading the _toc is only
  732. # required if mtimes differ.
  733. self._box._skewfactor = -3
  734. self._box._refresh()
  735. self.assertEqual(sorted(self._box._toc.keys()), sorted([key0, key1]))
  736. def test_lookup(self):
  737. # Look up message subpaths in the TOC
  738. self.assertRaises(KeyError, lambda: self._box._lookup('foo'))
  739. key0 = self._box.add(self._template % 0)
  740. self.assertEqual(self._box._lookup(key0), os.path.join('new', key0))
  741. os.remove(os.path.join(self._path, 'new', key0))
  742. self.assertEqual(self._box._toc, {key0: os.path.join('new', key0)})
  743. # Be sure that the TOC is read back from disk (see issue #6896
  744. # about bad mtime behaviour on some systems).
  745. self._box.flush()
  746. self.assertRaises(KeyError, lambda: self._box._lookup(key0))
  747. self.assertEqual(self._box._toc, {})
  748. def test_lock_unlock(self):
  749. # Lock and unlock the mailbox. For Maildir, this does nothing.
  750. self._box.lock()
  751. self._box.unlock()
  752. def test_folder (self):
  753. # Test for bug #1569790: verify that folders returned by .get_folder()
  754. # use the same factory function.
  755. def dummy_factory (s):
  756. return None
  757. box = self._factory(self._path, factory=dummy_factory)
  758. folder = box.add_folder('folder1')
  759. self.assertIs(folder._factory, dummy_factory)
  760. folder1_alias = box.get_folder('folder1')
  761. self.assertIs(folder1_alias._factory, dummy_factory)
  762. def test_directory_in_folder (self):
  763. # Test that mailboxes still work if there's a stray extra directory
  764. # in a folder.
  765. for i in range(10):
  766. self._box.add(mailbox.Message(_sample_message))
  767. # Create a stray directory
  768. os.mkdir(os.path.join(self._path, 'cur', 'stray-dir'))
  769. # Check that looping still works with the directory present.
  770. for msg in self._box:
  771. pass
  772. @unittest.skipUnless(hasattr(os, 'umask'), 'test needs os.umask()')
  773. @unittest.skipUnless(hasattr(os, 'stat'), 'test needs os.stat()')
  774. def test_file_permissions(self):
  775. # Verify that message files are created without execute permissions
  776. msg = mailbox.MaildirMessage(self._template % 0)
  777. orig_umask = os.umask(0)
  778. try:
  779. key = self._box.add(msg)
  780. finally:
  781. os.umask(orig_umask)
  782. path = os.path.join(self._path, self._box._lookup(key))
  783. mode = os.stat(path).st_mode
  784. self.assertFalse(mode & 0o111)
  785. @unittest.skipUnless(hasattr(os, 'umask'), 'test needs os.umask()')
  786. @unittest.skipUnless(hasattr(os, 'stat'), 'test needs os.stat()')
  787. def test_folder_file_perms(self):
  788. # From bug #3228, we want to verify that the file created inside a Maildir
  789. # subfolder isn't marked as executable.
  790. orig_umask = os.umask(0)
  791. try:
  792. subfolder = self._box.add_folder('subfolder')
  793. finally:
  794. os.umask(orig_umask)
  795. path = os.path.join(subfolder._path, 'maildirfolder')
  796. st = os.stat(path)
  797. perms = st.st_mode
  798. self.assertFalse((perms & 0o111)) # Execute bits should all be off.
  799. def test_reread(self):
  800. # Do an initial unconditional refresh
  801. self._box._refresh()
  802. # Put the last modified times more than two seconds into the past
  803. # (because mtime may have a two second granularity)
  804. for subdir in ('cur', 'new'):
  805. os.utime(os.path.join(self._box._path, subdir),
  806. (time.time()-5,)*2)
  807. # Because mtime has a two second granularity in worst case (FAT), a
  808. # refresh is done unconditionally if called for within
  809. # two-second-plus-a-bit of the last one, just in case the mbox has
  810. # changed; so now we have to wait for that interval to expire.
  811. #
  812. # Because this is a test, emulate sleeping. Instead of
  813. # sleeping for 2 seconds, use the skew factor to make _refresh
  814. # think that 2 seconds have passed and re-reading the _toc is
  815. # only required if mtimes differ.
  816. self._box._skewfactor = -3
  817. # Re-reading causes the ._toc attribute to be assigned a new dictionary
  818. # object, so we'll check that the ._toc attribute isn't a different
  819. # object.
  820. orig_toc = self._box._toc
  821. def refreshed():
  822. return self._box._toc is not orig_toc
  823. self._box._refresh()
  824. self.assertFalse(refreshed())
  825. # Now, write something into cur and remove it. This changes
  826. # the mtime and should cause a re-read. Note that "sleep
  827. # emulation" is still in effect, as skewfactor is -3.
  828. filename = os.path.join(self._path, 'cur', 'stray-file')
  829. support.create_empty_file(filename)
  830. os.unlink(filename)
  831. self._box._refresh()
  832. self.assertTrue(refreshed())
  833. class _TestSingleFile(TestMailbox):
  834. '''Common tests for single-file mailboxes'''
  835. def test_add_doesnt_rewrite(self):
  836. # When only adding messages, flush() should not rewrite the
  837. # mailbox file. See issue #9559.
  838. # Inode number changes if the contents are written to another
  839. # file which is then renamed over the original file. So we
  840. # must check that the inode number doesn't change.
  841. inode_before = os.stat(self._path).st_ino
  842. self._box.add(self._template % 0)
  843. self._box.flush()
  844. inode_after = os.stat(self._path).st_ino
  845. self.assertEqual(inode_before, inode_after)
  846. # Make sure the message was really added
  847. self._box.close()
  848. self._box = self._factory(self._path)
  849. self.assertEqual(len(self._box), 1)
  850. def test_permissions_after_flush(self):
  851. # See issue #5346
  852. # Make the mailbox world writable. It's unlikely that the new
  853. # mailbox file would have these permissions after flush(),
  854. # because umask usually prevents it.
  855. mode = os.stat(self._path).st_mode | 0o666
  856. os.chmod(self._path, mode)
  857. self._box.add(self._template % 0)
  858. i = self._box.add(self._template % 1)
  859. # Need to remove one message to make flush() create a new file
  860. self._box.remove(i)
  861. self._box.flush()
  862. self.assertEqual(os.stat(self._path).st_mode, mode)
  863. class _TestMboxMMDF(_TestSingleFile):
  864. def tearDown(self):
  865. super().tearDown()
  866. self._box.close()
  867. self._delete_recursively(self._path)
  868. for lock_remnant in glob.glob(self._path + '.*'):
  869. support.unlink(lock_remnant)
  870. def assertMailboxEmpty(self):
  871. with open(self._path) as f:
  872. self.assertEqual(f.readlines(), [])
  873. def test_add_from_string(self):
  874. # Add a string starting with 'From ' to the mailbox
  875. key = self._box.add('From foo@bar blah\nFrom: foo\n\n0\n')
  876. self.assertEqual(self._box[key].get_from(), 'foo@bar blah')
  877. self.assertEqual(self._box[key].get_payload(), '0\n')
  878. def test_add_from_bytes(self):
  879. # Add a byte string starting with 'From ' to the mailbox
  880. key = self._box.add(b'From foo@bar blah\nFrom: foo\n\n0\n')
  881. self.assertEqual(self._box[key].get_from(), 'foo@bar blah')
  882. self.assertEqual(self._box[key].get_payload(), '0\n')
  883. def test_add_mbox_or_mmdf_message(self):
  884. # Add an mboxMessage or MMDFMessage
  885. for class_ in (mailbox.mboxMessage, mailbox.MMDFMessage):
  886. msg = class_('From foo@bar blah\nFrom: foo\n\n0\n')
  887. key = self._box.add(msg)
  888. def test_open_close_open(self):
  889. # Open and inspect previously-created mailbox
  890. values = [self._template % i for i in range(3)]
  891. for value in values:
  892. self._box.add(value)
  893. self._box.close()
  894. mtime = os.path.getmtime(self._path)
  895. self._box = self._factory(self._path)
  896. self.assertEqual(len(self._box), 3)
  897. for key in self._box.iterkeys():
  898. self.assertIn(self._box.get_string(key), values)
  899. self._box.close()
  900. self.assertEqual(mtime, os.path.getmtime(self._path))
  901. def test_add_and_close(self):
  902. # Verifying that closing a mailbox doesn't change added items
  903. self._box.add(_sample_message)
  904. for i in range(3):
  905. self._box.add(self._template % i)
  906. self._box.add(_sample_message)
  907. self._box._file.flush()
  908. self._box._file.seek(0)
  909. contents = self._box._file.read()
  910. self._box.close()
  911. with open(self._path, 'rb') as f:
  912. self.assertEqual(contents, f.read())
  913. self._box = self._factory(self._path)
  914. @unittest.skipUnless(hasattr(os, 'fork'), "Test needs fork().")
  915. @unittest.skipUnless(hasattr(socket, 'socketpair'), "Test needs socketpair().")
  916. def test_lock_conflict(self):
  917. # Fork off a child process that will lock the mailbox temporarily,
  918. # unlock it and exit.
  919. c, p = socket.socketpair()
  920. self.addCleanup(c.close)
  921. self.addCleanup(p.close)
  922. pid = os.fork()
  923. if pid == 0:
  924. # child
  925. try:
  926. # lock the mailbox, and signal the parent it can proceed
  927. self._box.lock()
  928. c.send(b'c')
  929. # wait until the parent is done, and unlock the mailbox
  930. c.recv(1)
  931. self._box.unlock()
  932. finally:
  933. os._exit(0)
  934. # In the parent, wait until the child signals it locked the mailbox.
  935. p.recv(1)
  936. try:
  937. self.assertRaises(mailbox.ExternalClashError,
  938. self._box.lock)
  939. finally:
  940. # Signal the child it can now release the lock and exit.
  941. p.send(b'p')
  942. # Wait for child to exit. Locking should now succeed.
  943. exited_pid, status = os.waitpid(pid, 0)
  944. self._box.lock()
  945. self._box.unlock()
  946. def test_relock(self):
  947. # Test case for bug #1575506: the mailbox class was locking the
  948. # wrong file object in its flush() method.
  949. msg = "Subject: sub\n\nbody\n"
  950. key1 = self._box.add(msg)
  951. self._box.flush()
  952. self._box.close()
  953. self._box = self._factory(self._path)
  954. self._box.lock()
  955. key2 = self._box.add(msg)
  956. self._box.flush()
  957. self.assertTrue(self._box._locked)
  958. self._box.close()
  959. class TestMbox(_TestMboxMMDF, unittest.TestCase):
  960. _factory = lambda self, path, factory=None: mailbox.mbox(path, factory)
  961. @unittest.skipUnless(hasattr(os, 'umask'), 'test needs os.umask()')
  962. @unittest.skipUnless(hasattr(os, 'stat'), 'test needs os.stat()')
  963. def test_file_perms(self):
  964. # From bug #3228, we want to verify that the mailbox file isn't executable,
  965. # even if the umask is set to something that would leave executable bits set.
  966. # We only run this test on platforms that support umask.
  967. try:
  968. old_umask = os.umask(0o077)
  969. self._box.close()
  970. os.unlink(self._path)
  971. self._box = mailbox.mbox(self._path, create=True)
  972. self._box.add('')
  973. self._box.close()
  974. finally:
  975. os.umask(old_umask)
  976. st = os.stat(self._path)
  977. perms = st.st_mode
  978. self.assertFalse((perms & 0o111)) # Execute bits should all be off.
  979. def test_terminating_newline(self):
  980. message = email.message.Message()
  981. message['From'] = 'john@example.com'
  982. message.set_payload('No newline at the end')
  983. i = self._box.add(message)
  984. # A newline should have been appended to the payload
  985. message = self._box.get(i)
  986. self.assertEqual(message.get_payload(), 'No newline at the end\n')
  987. def test_message_separator(self):
  988. # Check there's always a single blank line after each message
  989. self._box.add('From: foo\n\n0') # No newline at the end
  990. with open(self._path) as f:
  991. data = f.read()
  992. self.assertEqual(data[-3:], '0\n\n')
  993. self._box.add('From: foo\n\n0\n') # Newline at the end
  994. with open(self._path) as f:
  995. data = f.read()
  996. self.assertEqual(data[-3:], '0\n\n')
  997. class TestMMDF(_TestMboxMMDF, unittest.TestCase):
  998. _factory = lambda self, path, factory=None: mailbox.MMDF(path, factory)
  999. class TestMH(TestMailbox, unittest.TestCase):
  1000. _factory = lambda self, path, factory=None: mailbox.MH(path, factory)
  1001. def assertMailboxEmpty(self):
  1002. self.assertEqual(os.listdir(self._path), ['.mh_sequences'])
  1003. def test_list_folders(self):
  1004. # List folders
  1005. self._box.add_folder('one')
  1006. self._box.add_folder('two')
  1007. self._box.add_folder('three')
  1008. self.assertEqual(len(self._box.list_folders()), 3)
  1009. self.assertEqual(set(self._box.list_folders()),
  1010. set(('one', 'two', 'three')))
  1011. def test_get_folder(self):
  1012. # Open folders
  1013. def dummy_factory (s):
  1014. return None
  1015. self._box = self._factory(self._path, dummy_factory)
  1016. new_folder = self._box.add_folder('foo.bar')
  1017. folder0 = self._box.get_folder('foo.bar')
  1018. folder0.add(self._template % 'bar')
  1019. self.assertTrue(os.path.isdir(os.path.join(self._path, 'foo.bar')))
  1020. folder1 = self._box.get_folder('foo.bar')
  1021. self.assertEqual(folder1.get_string(folder1.keys()[0]),
  1022. self._template % 'bar')
  1023. # Test for bug #1569790: verify that folders returned by .get_folder()
  1024. # use the same factory function.
  1025. self.assertIs(new_folder._factory, self._box._factory)
  1026. self.assertIs(folder0._factory, self._box._factory)
  1027. def test_add_and_remove_folders(self):
  1028. # Delete folders
  1029. self._box.add_folder('one')
  1030. self._box.add_folder('two')
  1031. self.assertEqual(len(self._box.list_folders()), 2)
  1032. self.assertEqual(set(self._box.list_folders()), set(('one', 'two')))
  1033. self._box.remove_folder('one')
  1034. self.assertEqual(len(self._box.list_folders()), 1)
  1035. self.assertEqual(set(self._box.list_folders()), set(('two',)))
  1036. self._box.add_folder('three')
  1037. self.assertEqual(len(self._box.list_folders()), 2)
  1038. self.assertEqual(set(self._box.list_folders()), set(('two', 'three')))
  1039. self._box.remove_folder('three')
  1040. self.assertEqual(len(self._box.list_folders()), 1)
  1041. self.assertEqual(set(self._box.list_folders()), set(('two',)))
  1042. self._box.remove_folder('two')
  1043. self.assertEqual(len(self._box.list_folders()), 0)
  1044. self.assertEqual(self._box.list_folders(), [])
  1045. def test_sequences(self):
  1046. # Get and set sequences
  1047. self.assertEqual(self._box.get_sequences(), {})
  1048. msg0 = mailbox.MHMessage(self._template % 0)
  1049. msg0.add_sequence('foo')
  1050. key0 = self._box.add(msg0)
  1051. self.assertEqual(self._box.get_sequences(), {'foo':[key0]})
  1052. msg1 = mailbox.MHMessage(self._template % 1)
  1053. msg1.set_sequences(['bar', 'replied', 'foo'])
  1054. key1 = self._box.add(msg1)
  1055. self.assertEqual(self._box.get_sequences(),
  1056. {'foo':[key0, key1], 'bar':[key1], 'replied':[key1]})
  1057. msg0.set_sequences(['flagged'])
  1058. self._box[key0] = msg0
  1059. self.assertEqual(self._box.get_sequences(),
  1060. {'foo':[key1]