subunit: Import new version.
[nivanova/samba-autobuild/.git] / lib / subunit / python / subunit / details.py
1 #
2 #  subunit: extensions to Python unittest to get test results from subprocesses.
3 #  Copyright (C) 2005  Robert Collins <robertc@robertcollins.net>
4 #
5 #  Licensed under either the Apache License, Version 2.0 or the BSD 3-clause
6 #  license at the users choice. A copy of both licenses are available in the
7 #  project source as Apache-2.0 and BSD. You may not use this file except in
8 #  compliance with one of these two licences.
9 #  
10 #  Unless required by applicable law or agreed to in writing, software
11 #  distributed under these licenses is distributed on an "AS IS" BASIS, WITHOUT
12 #  WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
13 #  license you chose for the specific language governing permissions and
14 #  limitations under that license.
15 #
16
17 """Handlers for outcome details."""
18
19 from cStringIO import StringIO
20
21 from testtools import content, content_type
22
23 import chunked
24
25
26 class DetailsParser(object):
27     """Base class/API reference for details parsing."""
28
29
30 class SimpleDetailsParser(DetailsParser):
31     """Parser for single-part [] delimited details."""
32
33     def __init__(self, state):
34         self._message = ""
35         self._state = state
36
37     def lineReceived(self, line):
38         if line == "]\n":
39             self._state.endDetails()
40             return
41         if line[0:2] == " ]":
42             # quoted ] start
43             self._message += line[1:]
44         else:
45             self._message += line
46
47     def get_details(self, style=None):
48         result = {}
49         if not style:
50             result['traceback'] = content.Content(
51                 content_type.ContentType("text", "x-traceback"),
52                 lambda:[self._message])
53         else:
54             if style == 'skip':
55                 name = 'reason'
56             else:
57                 name = 'message'
58             result[name] = content.Content(
59                 content_type.ContentType("text", "plain"),
60                 lambda:[self._message])
61         return result
62
63     def get_message(self):
64         return self._message
65
66
67 class MultipartDetailsParser(DetailsParser):
68     """Parser for multi-part [] surrounded MIME typed chunked details."""
69
70     def __init__(self, state):
71         self._state = state
72         self._details = {}
73         self._parse_state = self._look_for_content
74
75     def _look_for_content(self, line):
76         if line == "]\n":
77             self._state.endDetails()
78             return
79         # TODO error handling
80         field, value = line[:-1].split(' ', 1)
81         main, sub = value.split('/')
82         self._content_type = content_type.ContentType(main, sub)
83         self._parse_state = self._get_name
84
85     def _get_name(self, line):
86         self._name = line[:-1]
87         self._body = StringIO()
88         self._chunk_parser = chunked.Decoder(self._body)
89         self._parse_state = self._feed_chunks
90
91     def _feed_chunks(self, line):
92         residue = self._chunk_parser.write(line)
93         if residue is not None:
94             # Line based use always ends on no residue.
95             assert residue == ''
96             body = self._body
97             self._details[self._name] = content.Content(
98                 self._content_type, lambda:[body.getvalue()])
99             self._chunk_parser.close()
100             self._parse_state = self._look_for_content
101
102     def get_details(self, for_skip=False):
103         return self._details
104
105     def get_message(self):
106         return None
107
108     def lineReceived(self, line):
109         self._parse_state(line)