xref: /aosp_15_r20/external/autotest/tko/parsers/version_1_unittest.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1*9c5db199SXin Li#!/usr/bin/python3
2*9c5db199SXin Li
3*9c5db199SXin Lifrom autotest_lib.tko import models
4*9c5db199SXin Liimport datetime, time, unittest, mock
5*9c5db199SXin Li
6*9c5db199SXin Liimport common
7*9c5db199SXin Lifrom autotest_lib.client.common_lib import utils
8*9c5db199SXin Lifrom autotest_lib.tko.parsers import version_1
9*9c5db199SXin Li
10*9c5db199SXin Li
11*9c5db199SXin Liclass test_status_line(unittest.TestCase):
12*9c5db199SXin Li    """Tests for status lines."""
13*9c5db199SXin Li
14*9c5db199SXin Li    statuses = ['GOOD', 'WARN', 'FAIL', 'ABORT']
15*9c5db199SXin Li
16*9c5db199SXin Li
17*9c5db199SXin Li    def test_handles_start(self):
18*9c5db199SXin Li        """Tests that START is handled properly."""
19*9c5db199SXin Li        line = version_1.status_line(0, 'START', '----', 'test',
20*9c5db199SXin Li                                     '', {})
21*9c5db199SXin Li        self.assertEquals(line.type, 'START')
22*9c5db199SXin Li        self.assertEquals(line.status, None)
23*9c5db199SXin Li
24*9c5db199SXin Li
25*9c5db199SXin Li    def test_handles_info(self):
26*9c5db199SXin Li        """Tests that INFO is handled properly."""
27*9c5db199SXin Li        line = version_1.status_line(0, 'INFO', '----', '----',
28*9c5db199SXin Li                                     '', {})
29*9c5db199SXin Li        self.assertEquals(line.type, 'INFO')
30*9c5db199SXin Li        self.assertEquals(line.status, None)
31*9c5db199SXin Li
32*9c5db199SXin Li
33*9c5db199SXin Li    def test_handles_status(self):
34*9c5db199SXin Li        """Tests that STATUS is handled properly."""
35*9c5db199SXin Li        for stat in self.statuses:
36*9c5db199SXin Li            line = version_1.status_line(0, stat, '----', 'test',
37*9c5db199SXin Li                                         '', {})
38*9c5db199SXin Li            self.assertEquals(line.type, 'STATUS')
39*9c5db199SXin Li            self.assertEquals(line.status, stat)
40*9c5db199SXin Li
41*9c5db199SXin Li
42*9c5db199SXin Li    def test_handles_endstatus(self):
43*9c5db199SXin Li        """Tests that END is handled properly."""
44*9c5db199SXin Li        for stat in self.statuses:
45*9c5db199SXin Li            line = version_1.status_line(0, 'END ' + stat, '----',
46*9c5db199SXin Li                                         'test', '', {})
47*9c5db199SXin Li            self.assertEquals(line.type, 'END')
48*9c5db199SXin Li            self.assertEquals(line.status, stat)
49*9c5db199SXin Li
50*9c5db199SXin Li
51*9c5db199SXin Li    def test_fails_on_bad_status(self):
52*9c5db199SXin Li        """Tests that an exception is raised on a bad status."""
53*9c5db199SXin Li        for stat in self.statuses:
54*9c5db199SXin Li            self.assertRaises(AssertionError,
55*9c5db199SXin Li                              version_1.status_line, 0,
56*9c5db199SXin Li                              'BAD ' + stat, '----', 'test',
57*9c5db199SXin Li                              '', {})
58*9c5db199SXin Li
59*9c5db199SXin Li
60*9c5db199SXin Li    def test_saves_all_fields(self):
61*9c5db199SXin Li        """Tests that all fields are saved."""
62*9c5db199SXin Li        line = version_1.status_line(5, 'GOOD', 'subdir_name',
63*9c5db199SXin Li                                     'test_name', 'my reason here',
64*9c5db199SXin Li                                     {'key1': 'value',
65*9c5db199SXin Li                                      'key2': 'another value',
66*9c5db199SXin Li                                      'key3': 'value3'})
67*9c5db199SXin Li        self.assertEquals(line.indent, 5)
68*9c5db199SXin Li        self.assertEquals(line.status, 'GOOD')
69*9c5db199SXin Li        self.assertEquals(line.subdir, 'subdir_name')
70*9c5db199SXin Li        self.assertEquals(line.testname, 'test_name')
71*9c5db199SXin Li        self.assertEquals(line.reason, 'my reason here')
72*9c5db199SXin Li        self.assertEquals(line.optional_fields,
73*9c5db199SXin Li                          {'key1': 'value', 'key2': 'another value',
74*9c5db199SXin Li                           'key3': 'value3'})
75*9c5db199SXin Li
76*9c5db199SXin Li
77*9c5db199SXin Li    def test_parses_blank_subdir(self):
78*9c5db199SXin Li        """Tests that a blank subdirectory is parsed properly."""
79*9c5db199SXin Li        line = version_1.status_line(0, 'GOOD', '----', 'test',
80*9c5db199SXin Li                                     '', {})
81*9c5db199SXin Li        self.assertEquals(line.subdir, None)
82*9c5db199SXin Li
83*9c5db199SXin Li
84*9c5db199SXin Li    def test_parses_blank_testname(self):
85*9c5db199SXin Li        """Tests that a blank test name is parsed properly."""
86*9c5db199SXin Li        line = version_1.status_line(0, 'GOOD', 'subdir', '----',
87*9c5db199SXin Li                                     '', {})
88*9c5db199SXin Li        self.assertEquals(line.testname, None)
89*9c5db199SXin Li
90*9c5db199SXin Li
91*9c5db199SXin Li    def test_parse_line_smoketest(self):
92*9c5db199SXin Li        """Runs a parse line smoke test."""
93*9c5db199SXin Li        input_data = ('\t\t\tGOOD\t----\t----\t'
94*9c5db199SXin Li                      'field1=val1\tfield2=val2\tTest Passed')
95*9c5db199SXin Li        line = version_1.status_line.parse_line(input_data)
96*9c5db199SXin Li        self.assertEquals(line.indent, 3)
97*9c5db199SXin Li        self.assertEquals(line.type, 'STATUS')
98*9c5db199SXin Li        self.assertEquals(line.status, 'GOOD')
99*9c5db199SXin Li        self.assertEquals(line.subdir, None)
100*9c5db199SXin Li        self.assertEquals(line.testname, None)
101*9c5db199SXin Li        self.assertEquals(line.reason, 'Test Passed')
102*9c5db199SXin Li        self.assertEquals(line.optional_fields,
103*9c5db199SXin Li                          {'field1': 'val1', 'field2': 'val2'})
104*9c5db199SXin Li
105*9c5db199SXin Li    def test_parse_line_handles_newline(self):
106*9c5db199SXin Li        """Tests that newlines are handled properly."""
107*9c5db199SXin Li        input_data = ('\t\tGOOD\t----\t----\t'
108*9c5db199SXin Li                      'field1=val1\tfield2=val2\tNo newline here!')
109*9c5db199SXin Li        for suffix in ('', '\n'):
110*9c5db199SXin Li            line = version_1.status_line.parse_line(input_data +
111*9c5db199SXin Li                                                    suffix)
112*9c5db199SXin Li            self.assertEquals(line.indent, 2)
113*9c5db199SXin Li            self.assertEquals(line.type, 'STATUS')
114*9c5db199SXin Li            self.assertEquals(line.status, 'GOOD')
115*9c5db199SXin Li            self.assertEquals(line.subdir, None)
116*9c5db199SXin Li            self.assertEquals(line.testname, None)
117*9c5db199SXin Li            self.assertEquals(line.reason, 'No newline here!')
118*9c5db199SXin Li            self.assertEquals(line.optional_fields,
119*9c5db199SXin Li                              {'field1': 'val1',
120*9c5db199SXin Li                               'field2': 'val2'})
121*9c5db199SXin Li
122*9c5db199SXin Li
123*9c5db199SXin Li    def test_parse_line_fails_on_untabbed_lines(self):
124*9c5db199SXin Li        """Tests that untabbed lines do not parse."""
125*9c5db199SXin Li        input_data = '   GOOD\trandom\tfields\tof text'
126*9c5db199SXin Li        line = version_1.status_line.parse_line(input_data)
127*9c5db199SXin Li        self.assertEquals(line, None)
128*9c5db199SXin Li        line = version_1.status_line.parse_line(input_data.lstrip())
129*9c5db199SXin Li        self.assertEquals(line.indent, 0)
130*9c5db199SXin Li        self.assertEquals(line.type, 'STATUS')
131*9c5db199SXin Li        self.assertEquals(line.status, 'GOOD')
132*9c5db199SXin Li        self.assertEquals(line.subdir, 'random')
133*9c5db199SXin Li        self.assertEquals(line.testname, 'fields')
134*9c5db199SXin Li        self.assertEquals(line.reason, 'of text')
135*9c5db199SXin Li        self.assertEquals(line.optional_fields, {})
136*9c5db199SXin Li
137*9c5db199SXin Li
138*9c5db199SXin Li    def test_parse_line_fails_on_incomplete_lines(self):
139*9c5db199SXin Li        """Tests that incomplete lines do not parse."""
140*9c5db199SXin Li        input_data = '\t\tGOOD\tfield\tsecond field'
141*9c5db199SXin Li        complete_data = input_data + '\tneeded last field'
142*9c5db199SXin Li        line = version_1.status_line.parse_line(input_data)
143*9c5db199SXin Li        self.assertEquals(line, None)
144*9c5db199SXin Li        line = version_1.status_line.parse_line(complete_data)
145*9c5db199SXin Li        self.assertEquals(line.indent, 2)
146*9c5db199SXin Li        self.assertEquals(line.type, 'STATUS')
147*9c5db199SXin Li        self.assertEquals(line.status, 'GOOD')
148*9c5db199SXin Li        self.assertEquals(line.subdir, 'field')
149*9c5db199SXin Li        self.assertEquals(line.testname, 'second field')
150*9c5db199SXin Li        self.assertEquals(line.reason, 'needed last field')
151*9c5db199SXin Li        self.assertEquals(line.optional_fields, {})
152*9c5db199SXin Li
153*9c5db199SXin Li
154*9c5db199SXin Li    def test_good_reboot_passes_success_test(self):
155*9c5db199SXin Li        """Tests good reboot statuses."""
156*9c5db199SXin Li        line = version_1.status_line(0, 'NOSTATUS', None, 'reboot',
157*9c5db199SXin Li                                     'reboot success', {})
158*9c5db199SXin Li        self.assertEquals(line.is_successful_reboot('GOOD'), True)
159*9c5db199SXin Li        self.assertEquals(line.is_successful_reboot('WARN'), True)
160*9c5db199SXin Li
161*9c5db199SXin Li
162*9c5db199SXin Li    def test_bad_reboot_passes_success_test(self):
163*9c5db199SXin Li        """Tests bad reboot statuses."""
164*9c5db199SXin Li        line = version_1.status_line(0, 'NOSTATUS', None, 'reboot',
165*9c5db199SXin Li                                     'reboot success', {})
166*9c5db199SXin Li        self.assertEquals(line.is_successful_reboot('FAIL'), False)
167*9c5db199SXin Li        self.assertEquals(line.is_successful_reboot('ABORT'), False)
168*9c5db199SXin Li
169*9c5db199SXin Li
170*9c5db199SXin Li    def test_get_kernel_returns_kernel_plus_patches(self):
171*9c5db199SXin Li        """Tests that get_kernel returns the appropriate info."""
172*9c5db199SXin Li        line = version_1.status_line(0, 'GOOD', 'subdir', 'testname',
173*9c5db199SXin Li                                     'reason text',
174*9c5db199SXin Li                                     {'kernel': '2.6.24-rc40',
175*9c5db199SXin Li                                      'patch0': 'first_patch 0 0',
176*9c5db199SXin Li                                      'patch1': 'another_patch 0 0'})
177*9c5db199SXin Li        kern = line.get_kernel()
178*9c5db199SXin Li        kernel_hash = utils.hash('md5', '2.6.24-rc40,0,0').hexdigest()
179*9c5db199SXin Li        self.assertEquals(kern.base, '2.6.24-rc40')
180*9c5db199SXin Li        self.assertEquals(kern.patches[0].spec, 'first_patch')
181*9c5db199SXin Li        self.assertEquals(kern.patches[1].spec, 'another_patch')
182*9c5db199SXin Li        self.assertEquals(len(kern.patches), 2)
183*9c5db199SXin Li        self.assertEquals(kern.kernel_hash, kernel_hash)
184*9c5db199SXin Li
185*9c5db199SXin Li
186*9c5db199SXin Li    def test_get_kernel_ignores_out_of_sequence_patches(self):
187*9c5db199SXin Li        """Tests that get_kernel ignores patches that are out of sequence."""
188*9c5db199SXin Li        line = version_1.status_line(0, 'GOOD', 'subdir', 'testname',
189*9c5db199SXin Li                                     'reason text',
190*9c5db199SXin Li                                     {'kernel': '2.6.24-rc40',
191*9c5db199SXin Li                                      'patch0': 'first_patch 0 0',
192*9c5db199SXin Li                                      'patch2': 'another_patch 0 0'})
193*9c5db199SXin Li        kern = line.get_kernel()
194*9c5db199SXin Li        kernel_hash = utils.hash('md5', '2.6.24-rc40,0').hexdigest()
195*9c5db199SXin Li        self.assertEquals(kern.base, '2.6.24-rc40')
196*9c5db199SXin Li        self.assertEquals(kern.patches[0].spec, 'first_patch')
197*9c5db199SXin Li        self.assertEquals(len(kern.patches), 1)
198*9c5db199SXin Li        self.assertEquals(kern.kernel_hash, kernel_hash)
199*9c5db199SXin Li
200*9c5db199SXin Li
201*9c5db199SXin Li    def test_get_kernel_returns_unknown_with_no_kernel(self):
202*9c5db199SXin Li        """Tests that a missing kernel is handled properly."""
203*9c5db199SXin Li        line = version_1.status_line(0, 'GOOD', 'subdir', 'testname',
204*9c5db199SXin Li                                     'reason text',
205*9c5db199SXin Li                                     {'patch0': 'first_patch 0 0',
206*9c5db199SXin Li                                      'patch2': 'another_patch 0 0'})
207*9c5db199SXin Li        kern = line.get_kernel()
208*9c5db199SXin Li        self.assertEquals(kern.base, 'UNKNOWN')
209*9c5db199SXin Li        self.assertEquals(kern.patches, [])
210*9c5db199SXin Li        self.assertEquals(kern.kernel_hash, 'UNKNOWN')
211*9c5db199SXin Li
212*9c5db199SXin Li
213*9c5db199SXin Li    def test_get_timestamp_returns_timestamp_field(self):
214*9c5db199SXin Li        """Tests that get_timestamp returns the expected info."""
215*9c5db199SXin Li        timestamp = datetime.datetime(1970, 1, 1, 4, 30)
216*9c5db199SXin Li        timestamp -= datetime.timedelta(seconds=time.timezone)
217*9c5db199SXin Li        line = version_1.status_line(0, 'GOOD', 'subdir', 'testname',
218*9c5db199SXin Li                                     'reason text',
219*9c5db199SXin Li                                     {'timestamp': '16200'})
220*9c5db199SXin Li        self.assertEquals(timestamp, line.get_timestamp())
221*9c5db199SXin Li
222*9c5db199SXin Li
223*9c5db199SXin Li    def test_get_timestamp_returns_none_on_missing_field(self):
224*9c5db199SXin Li        """Tests that get_timestamp returns None if no timestamp exists."""
225*9c5db199SXin Li        line = version_1.status_line(0, 'GOOD', 'subdir', 'testname',
226*9c5db199SXin Li                                     'reason text', {})
227*9c5db199SXin Li        self.assertEquals(None, line.get_timestamp())
228*9c5db199SXin Li
229*9c5db199SXin Li
230*9c5db199SXin Liclass iteration_parse_line_into_dicts(unittest.TestCase):
231*9c5db199SXin Li    """Tests for parsing iteration keyvals into dictionaries."""
232*9c5db199SXin Li
233*9c5db199SXin Li    def parse_line(self, line):
234*9c5db199SXin Li        """
235*9c5db199SXin Li        Invokes parse_line_into_dicts with two empty dictionaries.
236*9c5db199SXin Li
237*9c5db199SXin Li        @param line: The line to parse.
238*9c5db199SXin Li
239*9c5db199SXin Li        @return A 2-tuple representing the filled-in attr and perf dictionaries,
240*9c5db199SXin Li            respectively.
241*9c5db199SXin Li
242*9c5db199SXin Li        """
243*9c5db199SXin Li        attr, perf = {}, {}
244*9c5db199SXin Li        version_1.iteration.parse_line_into_dicts(line, attr, perf)
245*9c5db199SXin Li        return attr, perf
246*9c5db199SXin Li
247*9c5db199SXin Li
248*9c5db199SXin Li    def test_perf_entry(self):
249*9c5db199SXin Li        """Tests a basic perf keyval line."""
250*9c5db199SXin Li        result = self.parse_line('perf-val{perf}=-173')
251*9c5db199SXin Li        self.assertEqual(({}, {'perf-val': -173}), result)
252*9c5db199SXin Li
253*9c5db199SXin Li
254*9c5db199SXin Li    def test_attr_entry(self):
255*9c5db199SXin Li        """Tests a basic attr keyval line."""
256*9c5db199SXin Li        result = self.parse_line('attr-val{attr}=173')
257*9c5db199SXin Li        self.assertEqual(({'attr-val': '173'}, {}), result)
258*9c5db199SXin Li
259*9c5db199SXin Li
260*9c5db199SXin Li    def test_untagged_is_perf(self):
261*9c5db199SXin Li        """Tests that an untagged keyval is considered to be perf by default."""
262*9c5db199SXin Li        result = self.parse_line('untagged=-678.5e5')
263*9c5db199SXin Li        self.assertEqual(({}, {'untagged': -678.5e5}), result)
264*9c5db199SXin Li
265*9c5db199SXin Li
266*9c5db199SXin Li    def test_invalid_tag_ignored(self):
267*9c5db199SXin Li        """Tests that invalid tags are ignored."""
268*9c5db199SXin Li        result = self.parse_line('bad-tag{invalid}=56')
269*9c5db199SXin Li        self.assertEqual(({}, {}), result)
270*9c5db199SXin Li
271*9c5db199SXin Li
272*9c5db199SXin Li    def test_non_numeric_perf_ignored(self):
273*9c5db199SXin Li        """Tests that non-numeric perf values are ignored."""
274*9c5db199SXin Li        result = self.parse_line('perf-val{perf}=FooBar')
275*9c5db199SXin Li        self.assertEqual(({}, {}), result)
276*9c5db199SXin Li
277*9c5db199SXin Li
278*9c5db199SXin Li    def test_non_numeric_untagged_ignored(self):
279*9c5db199SXin Li        """Tests that non-numeric untagged keyvals are ignored."""
280*9c5db199SXin Li        result = self.parse_line('untagged=FooBar')
281*9c5db199SXin Li        self.assertEqual(({}, {}), result)
282*9c5db199SXin Li
283*9c5db199SXin Li
284*9c5db199SXin Liclass perf_value_iteration_parse_line_into_dict(unittest.TestCase):
285*9c5db199SXin Li    """Tests for parsing perf value iterations into a dictionary."""
286*9c5db199SXin Li
287*9c5db199SXin Li    def parse_line(self, line):
288*9c5db199SXin Li        """
289*9c5db199SXin Li        Invokes parse_line_into_dict with a line to parse.
290*9c5db199SXin Li
291*9c5db199SXin Li        @param line: The string line to parse.
292*9c5db199SXin Li
293*9c5db199SXin Li        @return A dictionary containing the information parsed from the line.
294*9c5db199SXin Li
295*9c5db199SXin Li        """
296*9c5db199SXin Li        return version_1.perf_value_iteration.parse_line_into_dict(line)
297*9c5db199SXin Li
298*9c5db199SXin Li    def test_invalid_json(self):
299*9c5db199SXin Li        """Tests that a non-JSON line is handled properly."""
300*9c5db199SXin Li        result = self.parse_line('{"invalid_json" "string"}')
301*9c5db199SXin Li        self.assertEqual(result, {})
302*9c5db199SXin Li
303*9c5db199SXin Li    def test_single_value_int(self):
304*9c5db199SXin Li        """Tests that a single integer value is parsed properly."""
305*9c5db199SXin Li        result = self.parse_line('{"value": 7}')
306*9c5db199SXin Li        self.assertEqual(result, {'value': 7, 'stddev': 0})
307*9c5db199SXin Li
308*9c5db199SXin Li    def test_single_value_float(self):
309*9c5db199SXin Li        """Tests that a single float value is parsed properly."""
310*9c5db199SXin Li        result = self.parse_line('{"value": 1.298}')
311*9c5db199SXin Li        self.assertEqual(result, {'value': 1.298, 'stddev': 0})
312*9c5db199SXin Li
313*9c5db199SXin Li    def test_value_list_int(self):
314*9c5db199SXin Li        """Tests that an integer list is parsed properly."""
315*9c5db199SXin Li        result = self.parse_line('{"value": [10, 20, 30]}')
316*9c5db199SXin Li        self.assertEqual(result, {'value': 20.0, 'stddev': 10.0})
317*9c5db199SXin Li
318*9c5db199SXin Li    def test_value_list_float(self):
319*9c5db199SXin Li        """Tests that a float list is parsed properly."""
320*9c5db199SXin Li        result = self.parse_line('{"value": [2.0, 3.0, 4.0]}')
321*9c5db199SXin Li        self.assertEqual(result, {'value': 3.0, 'stddev': 1.0})
322*9c5db199SXin Li
323*9c5db199SXin Li
324*9c5db199SXin Liclass DummyAbortTestCase(unittest.TestCase):
325*9c5db199SXin Li    """Tests for the make_stub_abort function."""
326*9c5db199SXin Li
327*9c5db199SXin Li    def setUp(self):
328*9c5db199SXin Li        self.indent = 3
329*9c5db199SXin Li        self.subdir = "subdir"
330*9c5db199SXin Li        self.testname = 'testname'
331*9c5db199SXin Li        self.timestamp = 1220565792
332*9c5db199SXin Li        self.reason = 'Job aborted unexpectedly'
333*9c5db199SXin Li
334*9c5db199SXin Li
335*9c5db199SXin Li    def test_make_stub_abort_with_timestamp(self):
336*9c5db199SXin Li        """Tests make_stub_abort with a timestamp specified."""
337*9c5db199SXin Li        abort = version_1.parser.make_stub_abort(
338*9c5db199SXin Li            self.indent, self.subdir, self.testname, self.timestamp,
339*9c5db199SXin Li            self.reason)
340*9c5db199SXin Li        self.assertEquals(
341*9c5db199SXin Li            abort, '%sEND ABORT\t%s\t%s\ttimestamp=%d\t%s' % (
342*9c5db199SXin Li            '\t' * self.indent, self.subdir, self.testname, self.timestamp,
343*9c5db199SXin Li            self.reason))
344*9c5db199SXin Li
345*9c5db199SXin Li    def test_make_stub_abort_with_no_subdir(self):
346*9c5db199SXin Li        """Tests make_stub_abort with no subdir specified."""
347*9c5db199SXin Li        abort= version_1.parser.make_stub_abort(
348*9c5db199SXin Li            self.indent, None, self.testname, self.timestamp, self.reason)
349*9c5db199SXin Li        self.assertEquals(
350*9c5db199SXin Li            abort, '%sEND ABORT\t----\t%s\ttimestamp=%d\t%s' % (
351*9c5db199SXin Li            '\t' * self.indent, self.testname, self.timestamp, self.reason))
352*9c5db199SXin Li
353*9c5db199SXin Li    def test_make_stub_abort_with_no_testname(self):
354*9c5db199SXin Li        """Tests make_stub_abort with no testname specified."""
355*9c5db199SXin Li        abort= version_1.parser.make_stub_abort(
356*9c5db199SXin Li        self.indent, self.subdir, None, self.timestamp, self.reason)
357*9c5db199SXin Li        self.assertEquals(
358*9c5db199SXin Li            abort, '%sEND ABORT\t%s\t----\ttimestamp=%d\t%s' % (
359*9c5db199SXin Li            '\t' * self.indent, self.subdir, self.timestamp, self.reason))
360*9c5db199SXin Li
361*9c5db199SXin Li    def test_make_stub_abort_no_timestamp(self):
362*9c5db199SXin Li        """Tests make_stub_abort with no timestamp specified."""
363*9c5db199SXin Li        abort = version_1.parser.make_stub_abort(
364*9c5db199SXin Li            self.indent, self.subdir, self.testname, None, self.reason)
365*9c5db199SXin Li        self.assertEquals(
366*9c5db199SXin Li            abort, '%sEND ABORT\t%s\t%s\t%s' % (
367*9c5db199SXin Li            '\t' * self.indent, self.subdir, self.testname, self.reason))
368*9c5db199SXin Li
369*9c5db199SXin Li
370*9c5db199SXin Liclass test_parse_file(unittest.TestCase):
371*9c5db199SXin Li    """Tests for parsing a status.log file."""
372*9c5db199SXin Li
373*9c5db199SXin Li    class fake_job(models.job):
374*9c5db199SXin Li        """Fake job object."""
375*9c5db199SXin Li
376*9c5db199SXin Li        def exit_status(self):
377*9c5db199SXin Li            """Fake exit_status method."""
378*9c5db199SXin Li            return 'FAIL'
379*9c5db199SXin Li
380*9c5db199SXin Li    @staticmethod
381*9c5db199SXin Li    def _parse_host_keyval(job_dir, hostname):
382*9c5db199SXin Li        return {}
383*9c5db199SXin Li
384*9c5db199SXin Li    @mock.patch.object(models.test, 'parse_host_keyval', _parse_host_keyval)
385*9c5db199SXin Li    def test_top_level_fail_with_reason(self):
386*9c5db199SXin Li        """Tests that a status.log with a FAIL keeps the reason."""
387*9c5db199SXin Li        job = self.fake_job('dir', 'user', 'label', 'machine', None, None,
388*9c5db199SXin Li                            None, None, None, None, None, None)
389*9c5db199SXin Li        parser = version_1.parser()
390*9c5db199SXin Li        parser.start(job)
391*9c5db199SXin Li        tests = parser.end([
392*9c5db199SXin Li                'FAIL\t----\t----\ttimestamp=1615249387\tlocaltime=Mar 09 00:23:07\tThis is the reason.'
393*9c5db199SXin Li        ])
394*9c5db199SXin Li        self.assertEquals(tests[0].status, 'FAIL')
395*9c5db199SXin Li        self.assertEquals(tests[0].reason, 'This is the reason.')
396*9c5db199SXin Li
397*9c5db199SXin Li
398*9c5db199SXin Liif __name__ == '__main__':
399*9c5db199SXin Li    unittest.main()
400