blob: b21830af91829da9f41b155cf1d75ee1b116258b [file] [log] [blame]
Paul Pendlebury7c1fdcf2011-05-04 12:39:15 -07001# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
Lloyd Piqueee8a2b92017-09-27 17:14:32 -07005"""A utility class used to run a gtest suite parsing individual tests."""
Paul Pendlebury7c1fdcf2011-05-04 12:39:15 -07006
Lloyd Piqueee8a2b92017-09-27 17:14:32 -07007import logging, os
Paul Pendlebury7c1fdcf2011-05-04 12:39:15 -07008from autotest_lib.server import autotest, hosts, host_attributes
Paul Pendlebury57593562011-06-15 10:45:49 -07009from autotest_lib.server import site_server_job_utils
Lloyd Piqueee8a2b92017-09-27 17:14:32 -070010from autotest_lib.client.common_lib import gtest_parser
Paul Pendlebury7c1fdcf2011-05-04 12:39:15 -070011
12
13class gtest_runner(object):
14 """Run a gtest test suite and evaluate the individual tests."""
15
16 def __init__(self):
17 """Creates an instance of gtest_runner to run tests on a remote host."""
18 self._results_dir = ''
19 self._gtest = None
20 self._host = None
21
22 def run(self, gtest_entry, machine, work_dir='.'):
23 """Run the gtest suite on a remote host, then parse the results.
24
25 Like machine_worker, gtest_runner honors include/exclude attributes on
26 the test item and will only run the test if the supplied host meets the
27 test requirements.
28
29 Note: This method takes a test and a machine as arguments, not a list
30 of tests and a list of machines like the parallel and distribute
31 methods do.
32
33 Args:
34 gtest_entry: Test tuple from control file. See documentation in
Nirnimesh717b0922011-11-09 12:03:48 -080035 site_server_job_utils.test_item class.
Paul Pendlebury7c1fdcf2011-05-04 12:39:15 -070036 machine: Name (IP) if remote host to run tests on.
37 work_dir: Local directory to run tests in.
38
39 """
Paul Pendlebury57593562011-06-15 10:45:49 -070040 self._gtest = site_server_job_utils.test_item(*gtest_entry)
Paul Pendlebury7c1fdcf2011-05-04 12:39:15 -070041 self._host = hosts.create_host(machine)
42 self._results_dir = work_dir
43
44 client_autotest = autotest.Autotest(self._host)
45 client_attributes = host_attributes.host_attributes(machine)
46 attribute_set = set(client_attributes.get_attributes())
47
48 if self._gtest.validate(attribute_set):
49 logging.info('%s %s Running %s', self._host,
50 [a for a in attribute_set], self._gtest)
Paul Pendlebury9af083b2011-05-17 13:38:58 -070051 try:
52 self._gtest.run_test(client_autotest, self._results_dir)
53 finally:
54 self.parse()
Paul Pendlebury7c1fdcf2011-05-04 12:39:15 -070055 else:
56 self.record_failed_test(self._gtest.test_name,
57 'No machines found for: ' + self._gtest)
58
59 def parse(self):
60 """Parse the gtest output recording individual test results.
61
62 Uses gtest_parser to pull the test results out of the gtest log file.
63 Then creates entries in status.log file for each test.
64 """
Paul Pendlebury7c1fdcf2011-05-04 12:39:15 -070065 # Find gtest log files from the autotest client run.
Nirnimesh717b0922011-11-09 12:03:48 -080066 log_path = os.path.join(
67 self._results_dir, self._gtest.tagged_test_name,
68 'debug', self._gtest.tagged_test_name + '.DEBUG')
Paul Pendlebury57593562011-06-15 10:45:49 -070069 if not os.path.exists(log_path):
70 logging.error('gtest log file "%s" is missing.', log_path)
71 return
72
Lloyd Piqueee8a2b92017-09-27 17:14:32 -070073 parser = gtest_parser.gtest_parser()
Paul Pendlebury7c1fdcf2011-05-04 12:39:15 -070074
75 # Read the log file line-by-line, passing each line into the parser.
76 with open(log_path, 'r') as log_file:
77 for log_line in log_file:
78 parser.ProcessLogLine(log_line)
79
80 logging.info('gtest_runner found %d tests.', parser.TotalTests())
81
82 # Record each failed test.
83 for failed in parser.FailedTests():
84 fail_description = parser.FailureDescription(failed)
Paul Pendlebury57593562011-06-15 10:45:49 -070085 if fail_description:
86 self.record_failed_test(failed, fail_description[0].strip(),
87 ''.join(fail_description))
Paul Pendlebury699d64e2011-06-22 12:03:56 -070088 else:
Paul Pendlebury57593562011-06-15 10:45:49 -070089 self.record_failed_test(failed, 'NO ERROR LINES FOUND.')
Paul Pendlebury7c1fdcf2011-05-04 12:39:15 -070090
91 # Finally record each successful test.
92 for passed in parser.PassedTests():
93 self.record_passed_test(passed)
94
95 def record_failed_test(self, failed_test, message, error_lines=None):
96 """Insert a failure record into status.log for this test.
97
98 Args:
99 failed_test: Name of test that failed.
100 message: Reason test failed, will be put in status.log file.
101 error_lines: Additional failure info, will be put in ERROR log.
102 """
Paul Pendleburydf3124a2011-05-12 14:00:14 -0700103 # Create a test name subdirectory to hold the test status.log file.
104 test_dir = os.path.join(self._results_dir, failed_test)
105 if not os.path.exists(test_dir):
106 try:
107 os.makedirs(test_dir)
108 except OSError:
109 logging.exception('Failed to created test directory: %s',
110 test_dir)
111
112 # Record failure into the global job and test specific status files.
113 self._host.record('START', failed_test, failed_test)
114 self._host.record('INFO', failed_test, 'FAILED: ' + failed_test)
115 self._host.record('END FAIL', failed_test, failed_test, message)
Paul Pendlebury7c1fdcf2011-05-04 12:39:15 -0700116
117 # If we have additional information on the failure, create an error log
118 # file for this test in the location a normal autotest would have left
119 # it so the frontend knows where to find it.
120 if error_lines is not None:
Paul Pendleburydf3124a2011-05-12 14:00:14 -0700121 fail_log_dir = os.path.join(test_dir, 'debug')
Paul Pendlebury7c1fdcf2011-05-04 12:39:15 -0700122 fail_log_path = os.path.join(fail_log_dir, failed_test + '.ERROR')
123
124 if not os.path.exists(fail_log_dir):
125 try:
126 os.makedirs(fail_log_dir)
127 except OSError:
128 logging.exception('Failed to created log directory: %s',
129 fail_log_dir)
130 return
131 try:
132 with open(fail_log_path, 'w') as fail_log:
133 fail_log.write(error_lines)
134 except IOError:
135 logging.exception('Failed to open log file: %s', fail_log_path)
136
137 def record_passed_test(self, passed_test):
138 """Insert a failure record into status.log for this test.
139
140 Args:
141 passed_test: Name of test that passed.
142 """
143 self._host.record('START', None, passed_test)
144 self._host.record('INFO', None, 'PASSED: ' + passed_test)
145 self._host.record('END GOOD', None, passed_test)