1# Copyright (c) 2008-2011 testtools developers. See LICENSE for details.
2
3import os
4import tempfile
5import unittest
6
7from testtools import TestCase
8from testtools.compat import (
9    _b,
10    _u,
11    StringIO,
12    )
13from testtools.content import (
14    attach_file,
15    Content,
16    content_from_file,
17    content_from_stream,
18    TracebackContent,
19    text_content,
20    )
21from testtools.content_type import (
22    ContentType,
23    UTF8_TEXT,
24    )
25from testtools.matchers import (
26    Equals,
27    MatchesException,
28    Raises,
29    raises,
30    )
31from testtools.tests.helpers import an_exc_info
32
33
34raises_value_error = Raises(MatchesException(ValueError))
35
36
37class TestContent(TestCase):
38
39    def test___init___None_errors(self):
40        self.assertThat(lambda: Content(None, None), raises_value_error)
41        self.assertThat(
42            lambda: Content(None, lambda: ["traceback"]), raises_value_error)
43        self.assertThat(
44            lambda: Content(ContentType("text", "traceback"), None),
45            raises_value_error)
46
47    def test___init___sets_ivars(self):
48        content_type = ContentType("foo", "bar")
49        content = Content(content_type, lambda: ["bytes"])
50        self.assertEqual(content_type, content.content_type)
51        self.assertEqual(["bytes"], list(content.iter_bytes()))
52
53    def test___eq__(self):
54        content_type = ContentType("foo", "bar")
55        one_chunk = lambda: [_b("bytes")]
56        two_chunk = lambda: [_b("by"), _b("tes")]
57        content1 = Content(content_type, one_chunk)
58        content2 = Content(content_type, one_chunk)
59        content3 = Content(content_type, two_chunk)
60        content4 = Content(content_type, lambda: [_b("by"), _b("te")])
61        content5 = Content(ContentType("f", "b"), two_chunk)
62        self.assertEqual(content1, content2)
63        self.assertEqual(content1, content3)
64        self.assertNotEqual(content1, content4)
65        self.assertNotEqual(content1, content5)
66
67    def test___repr__(self):
68        content = Content(ContentType("application", "octet-stream"),
69            lambda: [_b("\x00bin"), _b("ary\xff")])
70        self.assertIn("\\x00binary\\xff", repr(content))
71
72    def test_iter_text_not_text_errors(self):
73        content_type = ContentType("foo", "bar")
74        content = Content(content_type, lambda: ["bytes"])
75        self.assertThat(content.iter_text, raises_value_error)
76
77    def test_iter_text_decodes(self):
78        content_type = ContentType("text", "strange", {"charset": "utf8"})
79        content = Content(
80            content_type, lambda: [_u("bytes\xea").encode("utf8")])
81        self.assertEqual([_u("bytes\xea")], list(content.iter_text()))
82
83    def test_iter_text_default_charset_iso_8859_1(self):
84        content_type = ContentType("text", "strange")
85        text = _u("bytes\xea")
86        iso_version = text.encode("ISO-8859-1")
87        content = Content(content_type, lambda: [iso_version])
88        self.assertEqual([text], list(content.iter_text()))
89
90    def test_from_file(self):
91        fd, path = tempfile.mkstemp()
92        self.addCleanup(os.remove, path)
93        os.write(fd, 'some data')
94        os.close(fd)
95        content = content_from_file(path, UTF8_TEXT, chunk_size=2)
96        self.assertThat(
97            list(content.iter_bytes()), Equals(['so', 'me', ' d', 'at', 'a']))
98
99    def test_from_nonexistent_file(self):
100        directory = tempfile.mkdtemp()
101        nonexistent = os.path.join(directory, 'nonexistent-file')
102        content = content_from_file(nonexistent)
103        self.assertThat(content.iter_bytes, raises(IOError))
104
105    def test_from_file_default_type(self):
106        content = content_from_file('/nonexistent/path')
107        self.assertThat(content.content_type, Equals(UTF8_TEXT))
108
109    def test_from_file_eager_loading(self):
110        fd, path = tempfile.mkstemp()
111        os.write(fd, 'some data')
112        os.close(fd)
113        content = content_from_file(path, UTF8_TEXT, buffer_now=True)
114        os.remove(path)
115        self.assertThat(
116            _b('').join(content.iter_bytes()), Equals('some data'))
117
118    def test_from_stream(self):
119        data = StringIO('some data')
120        content = content_from_stream(data, UTF8_TEXT, chunk_size=2)
121        self.assertThat(
122            list(content.iter_bytes()), Equals(['so', 'me', ' d', 'at', 'a']))
123
124    def test_from_stream_default_type(self):
125        data = StringIO('some data')
126        content = content_from_stream(data)
127        self.assertThat(content.content_type, Equals(UTF8_TEXT))
128
129    def test_from_stream_eager_loading(self):
130        fd, path = tempfile.mkstemp()
131        self.addCleanup(os.remove, path)
132        os.write(fd, 'some data')
133        stream = open(path, 'rb')
134        content = content_from_stream(stream, UTF8_TEXT, buffer_now=True)
135        os.write(fd, 'more data')
136        os.close(fd)
137        self.assertThat(
138            _b('').join(content.iter_bytes()), Equals('some data'))
139
140    def test_from_text(self):
141        data = _u("some data")
142        expected = Content(UTF8_TEXT, lambda: [data.encode('utf8')])
143        self.assertEqual(expected, text_content(data))
144
145
146class TestTracebackContent(TestCase):
147
148    def test___init___None_errors(self):
149        self.assertThat(
150            lambda: TracebackContent(None, None), raises_value_error)
151
152    def test___init___sets_ivars(self):
153        content = TracebackContent(an_exc_info, self)
154        content_type = ContentType("text", "x-traceback",
155            {"language": "python", "charset": "utf8"})
156        self.assertEqual(content_type, content.content_type)
157        result = unittest.TestResult()
158        expected = result._exc_info_to_string(an_exc_info, self)
159        self.assertEqual(expected, ''.join(list(content.iter_text())))
160
161
162class TestAttachFile(TestCase):
163
164    def make_file(self, data):
165        fd, path = tempfile.mkstemp()
166        self.addCleanup(os.remove, path)
167        os.write(fd, data)
168        os.close(fd)
169        return path
170
171    def test_simple(self):
172        class SomeTest(TestCase):
173            def test_foo(self):
174                pass
175        test = SomeTest('test_foo')
176        data = 'some data'
177        path = self.make_file(data)
178        my_content = text_content(data)
179        attach_file(test, path, name='foo')
180        self.assertEqual({'foo': my_content}, test.getDetails())
181
182    def test_optional_name(self):
183        # If no name is provided, attach_file just uses the base name of the
184        # file.
185        class SomeTest(TestCase):
186            def test_foo(self):
187                pass
188        test = SomeTest('test_foo')
189        path = self.make_file('some data')
190        base_path = os.path.basename(path)
191        attach_file(test, path)
192        self.assertEqual([base_path], list(test.getDetails()))
193
194    def test_lazy_read(self):
195        class SomeTest(TestCase):
196            def test_foo(self):
197                pass
198        test = SomeTest('test_foo')
199        path = self.make_file('some data')
200        attach_file(test, path, name='foo', buffer_now=False)
201        content = test.getDetails()['foo']
202        content_file = open(path, 'w')
203        content_file.write('new data')
204        content_file.close()
205        self.assertEqual(''.join(content.iter_bytes()), 'new data')
206
207    def test_eager_read_by_default(self):
208        class SomeTest(TestCase):
209            def test_foo(self):
210                pass
211        test = SomeTest('test_foo')
212        path = self.make_file('some data')
213        attach_file(test, path, name='foo')
214        content = test.getDetails()['foo']
215        content_file = open(path, 'w')
216        content_file.write('new data')
217        content_file.close()
218        self.assertEqual(''.join(content.iter_bytes()), 'some data')
219
220
221def test_suite():
222    from unittest import TestLoader
223    return TestLoader().loadTestsFromName(__name__)
224