blob: ef9cad498a75c2620d806362f8048458bd48c310 [file] [log] [blame]
Nick Coghlan029ba2b2011-08-22 16:05:44 +10001import mailcap
2import os
R David Murray347dc952016-09-09 20:04:23 -04003import copy
Nick Coghlan029ba2b2011-08-22 16:05:44 +10004import test.support
Hai Shi0c4f0f32020-06-30 21:46:31 +08005from test.support import os_helper
Nick Coghlan029ba2b2011-08-22 16:05:44 +10006import unittest
pxinwr996a1ef2020-11-29 05:49:47 +08007import sys
Nick Coghlan029ba2b2011-08-22 16:05:44 +10008
9# Location of mailcap file
10MAILCAPFILE = test.support.findfile("mailcap.txt")
11
12# Dict to act as mock mailcap entry for this test
13# The keys and values should match the contents of MAILCAPFILE
Ezio Melotti83feff52011-08-23 01:39:22 +030014MAILCAPDICT = {
15 'application/x-movie':
16 [{'compose': 'moviemaker %s',
17 'x11-bitmap': '"/usr/lib/Zmail/bitmaps/movie.xbm"',
18 'description': '"Movie"',
R David Murray347dc952016-09-09 20:04:23 -040019 'view': 'movieplayer %s',
20 'lineno': 4}],
Ezio Melotti83feff52011-08-23 01:39:22 +030021 'application/*':
22 [{'copiousoutput': '',
R David Murray347dc952016-09-09 20:04:23 -040023 'view': 'echo "This is \\"%t\\" but is 50 \\% Greek to me" \\; cat %s',
24 'lineno': 5}],
Ezio Melotti83feff52011-08-23 01:39:22 +030025 'audio/basic':
26 [{'edit': 'audiocompose %s',
27 'compose': 'audiocompose %s',
28 'description': '"An audio fragment"',
R David Murray347dc952016-09-09 20:04:23 -040029 'view': 'showaudio %s',
30 'lineno': 6}],
Ezio Melotti83feff52011-08-23 01:39:22 +030031 'video/mpeg':
R David Murray347dc952016-09-09 20:04:23 -040032 [{'view': 'mpeg_play %s', 'lineno': 13}],
Ezio Melotti83feff52011-08-23 01:39:22 +030033 'application/postscript':
R David Murray347dc952016-09-09 20:04:23 -040034 [{'needsterminal': '', 'view': 'ps-to-terminal %s', 'lineno': 1},
35 {'compose': 'idraw %s', 'view': 'ps-to-terminal %s', 'lineno': 2}],
Ezio Melotti83feff52011-08-23 01:39:22 +030036 'application/x-dvi':
R David Murray347dc952016-09-09 20:04:23 -040037 [{'view': 'xdvi %s', 'lineno': 3}],
Ezio Melotti83feff52011-08-23 01:39:22 +030038 'message/external-body':
39 [{'composetyped': 'extcompose %s',
40 'description': '"A reference to data stored in an external location"',
41 'needsterminal': '',
R David Murray347dc952016-09-09 20:04:23 -040042 'view': 'showexternal %s %{access-type} %{name} %{site} %{directory} %{mode} %{server}',
43 'lineno': 10}],
Ezio Melotti83feff52011-08-23 01:39:22 +030044 'text/richtext':
45 [{'test': 'test "`echo %{charset} | tr \'[A-Z]\' \'[a-z]\'`" = iso-8859-8',
46 'copiousoutput': '',
R David Murray347dc952016-09-09 20:04:23 -040047 'view': 'shownonascii iso-8859-8 -e richtext -p %s',
48 'lineno': 11}],
Ezio Melotti83feff52011-08-23 01:39:22 +030049 'image/x-xwindowdump':
R David Murray347dc952016-09-09 20:04:23 -040050 [{'view': 'display %s', 'lineno': 9}],
Ezio Melotti83feff52011-08-23 01:39:22 +030051 'audio/*':
R David Murray347dc952016-09-09 20:04:23 -040052 [{'view': '/usr/local/bin/showaudio %t', 'lineno': 7}],
Ezio Melotti83feff52011-08-23 01:39:22 +030053 'video/*':
R David Murray347dc952016-09-09 20:04:23 -040054 [{'view': 'animate %s', 'lineno': 12}],
Ezio Melotti83feff52011-08-23 01:39:22 +030055 'application/frame':
R David Murray347dc952016-09-09 20:04:23 -040056 [{'print': '"cat %s | lp"', 'view': 'showframe %s', 'lineno': 0}],
Ezio Melotti83feff52011-08-23 01:39:22 +030057 'image/rgb':
R David Murray347dc952016-09-09 20:04:23 -040058 [{'view': 'display %s', 'lineno': 8}]
Ezio Melotti83feff52011-08-23 01:39:22 +030059}
Nick Coghlan029ba2b2011-08-22 16:05:44 +100060
R David Murray347dc952016-09-09 20:04:23 -040061# For backwards compatibility, readmailcapfile() and lookup() still support
62# the old version of mailcapdict without line numbers.
63MAILCAPDICT_DEPRECATED = copy.deepcopy(MAILCAPDICT)
64for entry_list in MAILCAPDICT_DEPRECATED.values():
65 for entry in entry_list:
66 entry.pop('lineno')
67
Nick Coghlan029ba2b2011-08-22 16:05:44 +100068
69class HelperFunctionTest(unittest.TestCase):
70
71 def test_listmailcapfiles(self):
72 # The return value for listmailcapfiles() will vary by system.
73 # So verify that listmailcapfiles() returns a list of strings that is of
74 # non-zero length.
75 mcfiles = mailcap.listmailcapfiles()
Ezio Melotti83feff52011-08-23 01:39:22 +030076 self.assertIsInstance(mcfiles, list)
77 for m in mcfiles:
78 self.assertIsInstance(m, str)
Hai Shi0c4f0f32020-06-30 21:46:31 +080079 with os_helper.EnvironmentVarGuard() as env:
Nick Coghlan029ba2b2011-08-22 16:05:44 +100080 # According to RFC 1524, if MAILCAPS env variable exists, use that
81 # and only that.
82 if "MAILCAPS" in env:
83 env_mailcaps = env["MAILCAPS"].split(os.pathsep)
84 else:
85 env_mailcaps = ["/testdir1/.mailcap", "/testdir2/mailcap"]
86 env["MAILCAPS"] = os.pathsep.join(env_mailcaps)
87 mcfiles = mailcap.listmailcapfiles()
88 self.assertEqual(env_mailcaps, mcfiles)
89
90 def test_readmailcapfile(self):
91 # Test readmailcapfile() using test file. It should match MAILCAPDICT.
92 with open(MAILCAPFILE, 'r') as mcf:
R David Murray347dc952016-09-09 20:04:23 -040093 with self.assertWarns(DeprecationWarning):
94 d = mailcap.readmailcapfile(mcf)
95 self.assertDictEqual(d, MAILCAPDICT_DEPRECATED)
Nick Coghlan029ba2b2011-08-22 16:05:44 +100096
97 def test_lookup(self):
98 # Test without key
R David Murray347dc952016-09-09 20:04:23 -040099 expected = [{'view': 'animate %s', 'lineno': 12},
100 {'view': 'mpeg_play %s', 'lineno': 13}]
Nick Coghlan029ba2b2011-08-22 16:05:44 +1000101 actual = mailcap.lookup(MAILCAPDICT, 'video/mpeg')
102 self.assertListEqual(expected, actual)
103
104 # Test with key
105 key = 'compose'
106 expected = [{'edit': 'audiocompose %s',
107 'compose': 'audiocompose %s',
108 'description': '"An audio fragment"',
R David Murray347dc952016-09-09 20:04:23 -0400109 'view': 'showaudio %s',
110 'lineno': 6}]
Nick Coghlan029ba2b2011-08-22 16:05:44 +1000111 actual = mailcap.lookup(MAILCAPDICT, 'audio/basic', key)
112 self.assertListEqual(expected, actual)
113
R David Murray347dc952016-09-09 20:04:23 -0400114 # Test on user-defined dicts without line numbers
115 expected = [{'view': 'mpeg_play %s'}, {'view': 'animate %s'}]
116 actual = mailcap.lookup(MAILCAPDICT_DEPRECATED, 'video/mpeg')
117 self.assertListEqual(expected, actual)
118
Nick Coghlan029ba2b2011-08-22 16:05:44 +1000119 def test_subst(self):
120 plist = ['id=1', 'number=2', 'total=3']
121 # test case: ([field, MIMEtype, filename, plist=[]], <expected string>)
122 test_cases = [
Ezio Melotti83feff52011-08-23 01:39:22 +0300123 (["", "audio/*", "foo.txt"], ""),
124 (["echo foo", "audio/*", "foo.txt"], "echo foo"),
125 (["echo %s", "audio/*", "foo.txt"], "echo foo.txt"),
126 (["echo %t", "audio/*", "foo.txt"], "echo audio/*"),
R David Murray44b548d2016-09-08 13:59:53 -0400127 (["echo \\%t", "audio/*", "foo.txt"], "echo %t"),
Ezio Melotti83feff52011-08-23 01:39:22 +0300128 (["echo foo", "audio/*", "foo.txt", plist], "echo foo"),
129 (["echo %{total}", "audio/*", "foo.txt", plist], "echo 3")
130 ]
Nick Coghlan029ba2b2011-08-22 16:05:44 +1000131 for tc in test_cases:
132 self.assertEqual(mailcap.subst(*tc[0]), tc[1])
133
134
135class GetcapsTest(unittest.TestCase):
136
137 def test_mock_getcaps(self):
138 # Test mailcap.getcaps() using mock mailcap file in this dir.
139 # Temporarily override any existing system mailcap file by pointing the
140 # MAILCAPS environment variable to our mock file.
Hai Shi0c4f0f32020-06-30 21:46:31 +0800141 with os_helper.EnvironmentVarGuard() as env:
Nick Coghlan029ba2b2011-08-22 16:05:44 +1000142 env["MAILCAPS"] = MAILCAPFILE
143 caps = mailcap.getcaps()
144 self.assertDictEqual(caps, MAILCAPDICT)
145
146 def test_system_mailcap(self):
147 # Test mailcap.getcaps() with mailcap file(s) on system, if any.
148 caps = mailcap.getcaps()
Ezio Melotti83feff52011-08-23 01:39:22 +0300149 self.assertIsInstance(caps, dict)
Nick Coghlan029ba2b2011-08-22 16:05:44 +1000150 mailcapfiles = mailcap.listmailcapfiles()
151 existingmcfiles = [mcf for mcf in mailcapfiles if os.path.exists(mcf)]
152 if existingmcfiles:
153 # At least 1 mailcap file exists, so test that.
154 for (k, v) in caps.items():
Ezio Melotti83feff52011-08-23 01:39:22 +0300155 self.assertIsInstance(k, str)
156 self.assertIsInstance(v, list)
157 for e in v:
158 self.assertIsInstance(e, dict)
Nick Coghlan029ba2b2011-08-22 16:05:44 +1000159 else:
160 # No mailcap files on system. getcaps() should return empty dict.
161 self.assertEqual({}, caps)
162
163
164class FindmatchTest(unittest.TestCase):
165
166 def test_findmatch(self):
167
168 # default findmatch arguments
169 c = MAILCAPDICT
170 fname = "foo.txt"
171 plist = ["access-type=default", "name=john", "site=python.org",
172 "directory=/tmp", "mode=foo", "server=bar"]
Ezio Melotti83feff52011-08-23 01:39:22 +0300173 audio_basic_entry = {
174 'edit': 'audiocompose %s',
175 'compose': 'audiocompose %s',
176 'description': '"An audio fragment"',
R David Murray347dc952016-09-09 20:04:23 -0400177 'view': 'showaudio %s',
178 'lineno': 6
Ezio Melotti83feff52011-08-23 01:39:22 +0300179 }
R David Murray347dc952016-09-09 20:04:23 -0400180 audio_entry = {"view": "/usr/local/bin/showaudio %t", 'lineno': 7}
181 video_entry = {'view': 'animate %s', 'lineno': 12}
Ezio Melotti83feff52011-08-23 01:39:22 +0300182 message_entry = {
183 'composetyped': 'extcompose %s',
184 'description': '"A reference to data stored in an external location"', 'needsterminal': '',
R David Murray347dc952016-09-09 20:04:23 -0400185 'view': 'showexternal %s %{access-type} %{name} %{site} %{directory} %{mode} %{server}',
186 'lineno': 10,
Ezio Melotti83feff52011-08-23 01:39:22 +0300187 }
Nick Coghlan029ba2b2011-08-22 16:05:44 +1000188
189 # test case: (findmatch args, findmatch keyword args, expected output)
190 # positional args: caps, MIMEtype
191 # keyword args: key="view", filename="/dev/null", plist=[]
192 # output: (command line, mailcap entry)
193 cases = [
194 ([{}, "video/mpeg"], {}, (None, None)),
195 ([c, "foo/bar"], {}, (None, None)),
R David Murray347dc952016-09-09 20:04:23 -0400196 ([c, "video/mpeg"], {}, ('animate /dev/null', video_entry)),
Nick Coghlan029ba2b2011-08-22 16:05:44 +1000197 ([c, "audio/basic", "edit"], {}, ("audiocompose /dev/null", audio_basic_entry)),
198 ([c, "audio/basic", "compose"], {}, ("audiocompose /dev/null", audio_basic_entry)),
199 ([c, "audio/basic", "description"], {}, ('"An audio fragment"', audio_basic_entry)),
200 ([c, "audio/basic", "foobar"], {}, (None, None)),
201 ([c, "video/*"], {"filename": fname}, ("animate %s" % fname, video_entry)),
202 ([c, "audio/basic", "compose"],
203 {"filename": fname},
204 ("audiocompose %s" % fname, audio_basic_entry)),
205 ([c, "audio/basic"],
206 {"key": "description", "filename": fname},
207 ('"An audio fragment"', audio_basic_entry)),
208 ([c, "audio/*"],
209 {"filename": fname},
210 ("/usr/local/bin/showaudio audio/*", audio_entry)),
211 ([c, "message/external-body"],
212 {"plist": plist},
213 ("showexternal /dev/null default john python.org /tmp foo bar", message_entry))
Ezio Melotti83feff52011-08-23 01:39:22 +0300214 ]
Nick Coghlan029ba2b2011-08-22 16:05:44 +1000215 self._run_cases(cases)
216
217 @unittest.skipUnless(os.name == "posix", "Requires 'test' command on system")
pxinwr996a1ef2020-11-29 05:49:47 +0800218 @unittest.skipIf(sys.platform == "vxworks", "'test' command is not supported on VxWorks")
Nick Coghlan029ba2b2011-08-22 16:05:44 +1000219 def test_test(self):
220 # findmatch() will automatically check any "test" conditions and skip
221 # the entry if the check fails.
222 caps = {"test/pass": [{"test": "test 1 -eq 1"}],
223 "test/fail": [{"test": "test 1 -eq 0"}]}
224 # test case: (findmatch args, findmatch keyword args, expected output)
225 # positional args: caps, MIMEtype, key ("test")
226 # keyword args: N/A
227 # output: (command line, mailcap entry)
228 cases = [
229 # findmatch will return the mailcap entry for test/pass because it evaluates to true
230 ([caps, "test/pass", "test"], {}, ("test 1 -eq 1", {"test": "test 1 -eq 1"})),
231 # findmatch will return None because test/fail evaluates to false
232 ([caps, "test/fail", "test"], {}, (None, None))
Ezio Melotti83feff52011-08-23 01:39:22 +0300233 ]
Nick Coghlan029ba2b2011-08-22 16:05:44 +1000234 self._run_cases(cases)
235
236 def _run_cases(self, cases):
237 for c in cases:
238 self.assertEqual(mailcap.findmatch(*c[0], **c[1]), c[2])
239
240
Nick Coghlan029ba2b2011-08-22 16:05:44 +1000241if __name__ == '__main__':
Zachary Ware38c707e2015-04-13 15:00:43 -0500242 unittest.main()