| #!/usr/bin/python |
| # Copyright 2012 Google Inc. All Rights Reserved. |
| |
| """This Class writes test progress/error/result to files. |
| |
| This file defines the logging function of the automation test. It |
| logs test progress, results, error/warning to different files: |
| |
| results.txt: this is the file to log the test output |
| error.txt: all test errors are logged into this file |
| prompt.txt: a copy of terminal output, which hints the progress |
| of the current test. |
| """ |
| |
| __author__ = 'Lehan Meng (lmeng@google.com)' |
| |
| |
| import datetime |
| import errno |
| import os |
| |
| |
| class Logging(object): |
| """This Class collects and writes test information to log files.""" |
| |
| def __init__(self, **kwargs): |
| """initiate the logging instance. |
| |
| Args: |
| kwargs['f_result']: file name for test result |
| kwargs['f_error']: file name for test errors |
| kwargs['f_progres']: file name for test progres |
| kwargs['std_out']: by default, send a copy of the log info to stdout |
| """ |
| |
| self.params = {'std_out': '1', |
| 'f_result': 'result.log', |
| 'f_error': 'error.log', |
| 'f_progress': 'progress.log', |
| 'delimiter': '\t', |
| 'out_folder': 'log', |
| 'in_folder': 'data'} |
| for s in kwargs: |
| self.params[s] = kwargs[s] |
| |
| try: |
| os.makedirs(self.params['out_folder']) |
| except OSError, e: |
| if e.errno != errno.EEXIST: |
| raise |
| |
| now = datetime.datetime.now() |
| s_time = now.strftime('%Y-%m-%d_%H:%M:%S.%f') |
| for f_name in ['f_result', 'f_error', 'f_progress']: |
| name = self.ParseLogFileName(self.params[f_name], 'w', s_time) |
| if name: |
| self.params[f_name] = name |
| else: |
| print 'Error! Fail to create log File.' |
| self.rst_file = open(self.params['f_result'], 'w+') |
| self.err_file = open(self.params['f_error'], 'w+') |
| self.prg_file = open(self.params['f_progress'], 'w+') |
| self.NewTestStart() |
| |
| def CreateFile(self, f_name, access='r'): |
| """Create a file for read/write. |
| |
| Args: |
| f_name: the file name that needs to be created |
| access: 'r': read, input file |
| 'w': write, output file |
| 'rw': read and write |
| Returns: |
| f_obj: return the file object |
| if not succeed, return False |
| """ |
| f_obj = None |
| if access == 'r': |
| # for input file |
| try: |
| f_obj = open(f_name, 'r') |
| except IOError: |
| print 'Cannot open the input file, return' |
| return False |
| elif access == 'w': |
| # for input file |
| try: |
| os.makedirs(self.params['out_folder']) |
| except OSError, e: |
| if e.errno != errno.EEXIST: |
| print 'Cannot create folder! Return' |
| return False |
| full_path = self.ParseLogFileName(f_name, access) |
| if full_path: |
| try: |
| f_obj = open(full_path, 'w+') |
| except IOError: |
| print 'Cannot open the given file, return' |
| return False |
| else: |
| # Can not parse file name |
| print 'Cannot parse given file name' |
| return False |
| else: |
| # read-write file: |
| try: |
| f_obj = open(f_name, 'r') |
| except IOError: |
| print 'Cannot open the input file, return' |
| return False |
| return f_obj |
| |
| def ParseLogFileName(self, f_name, access='r', time_stamp=None): |
| """Generate an uniq log file name for each test case instance. |
| |
| Args: |
| f_name: file name |
| access: 'r': read mode |
| 'w': write mode |
| 'rw': read-write |
| time_stamp: the time tag used for file name |
| if None, create a new tag using current time |
| Returns: |
| return the parsed file name |
| """ |
| if not f_name: |
| f_name = 'logFile.log' |
| if not time_stamp: |
| now = datetime.datetime.now() |
| s_time = now.strftime('%Y-%m-%d_%H:%M:%S.%f') |
| else: |
| s_time = time_stamp |
| |
| ls = f_name.split('.') |
| ext_name = '' |
| name = '' |
| if len(ls) == 1: |
| name = ls[0] |
| else: |
| ext_name = ls.pop(-1) |
| for s in ls: |
| name += (s+'.') |
| name = name.rstrip('.') |
| |
| if not ext_name: |
| ext_name = 'log' |
| folder = None |
| if access == 'r': |
| folder = self.params['in_folder'] |
| elif access == 'w': |
| folder = self.params['out_folder'] |
| else: |
| folder = self.params['in_folder'] |
| name = (folder + '/' + name + '_testID-' + self.params['testID'] |
| + '_'+ s_time + '.' + ext_name) |
| return name |
| |
| def SendLineToResult(self, test_info, result_info): |
| """Send test result (one-line) information to the result file. |
| |
| Args: |
| test_info: test case related info: testID, start_time, key_word, etc |
| result_info: test results {Pass_Fail: pass, note: Extra_Information} |
| Returns: |
| return the status of file write operation |
| """ |
| now = datetime.datetime.now() |
| if test_info is not None: |
| line = (now.strftime('%Y-%m-%d %H:%M:%S.%f') + self.params['delimiter'] |
| + 'testID:' + test_info['testID'] |
| + self.params['delimiter'] + 'TestStartAt:' |
| + test_info['start_time'] |
| + self.params['delimiter'] + 'Keyword:' + test_info['key_word'] |
| + self.params['delimiter'] + result_info['Pass_Fail'] |
| + self.params['delimiter'] + result_info['note'] + '\n') |
| else: |
| line = ('Result log information should have testInfo available! ' |
| + self.params['delimiter'] + 'No logging performed') |
| |
| if self.params['std_out'] > 0: |
| print line |
| self.rst_file.write(line) |
| self.rst_file.flush() |
| |
| def NewTestStart(self): |
| """Mark the start of a new test in the file.""" |
| self.rst_file.write( |
| '################### NEW TEST STARTED ###################\n') |
| self.err_file.write( |
| '################### NEW TEST STARTED ###################\n') |
| self.prg_file.write( |
| '################### NEW TEST STARTED ###################\n') |
| |
| def SendLineToError(self, test_info, error_info): |
| """Send test error (one-line) information to the error file. |
| |
| Args: |
| test_info: test case related info: testID, start_time, key_word, etc |
| error_info: test errors, severity: critical/intermediate/low, and info |
| Returns: |
| returns the status of file write operation |
| """ |
| now = datetime.datetime.now() |
| if test_info is not None: |
| line = (now.strftime('%Y-%m-%d %H:%M:%S.%f') + self.params['delimiter'] |
| + 'testID:' + test_info['testID'] + self.params['delimiter'] |
| + 'TestStartAt:' + test_info['start_time'] |
| + self.params['delimiter'] + 'Keyword:' |
| + test_info['key_word'] + self.params['delimiter'] |
| + error_info['severity'] + self.params['delimiter'] |
| + error_info['note'] + '\n') |
| else: |
| line = (now.strftime('%Y-%m-%d %H:%M:%S.%f') + self.params['delimiter'] + |
| error_info['severity'] + self.params['delimiter'] |
| + error_info['note'] + '\n') |
| |
| if self.params['std_out'] > 0: |
| print line |
| self.err_file.write(line) |
| self.err_file.flush() |
| |
| def SendLineToProgress(self, test_info, progress_info): |
| """Send test progress (one-line) information to the progress file. |
| |
| Args: |
| test_info: test case related info: testID, start_time, key_word, etc |
| progress_info: test progress indication |
| {percent: [0-100]%, note: Information} |
| Returns: |
| returns the status of file write operation |
| """ |
| now = datetime.datetime.now() |
| if test_info is not None: |
| line = (now.strftime('%Y-%m-%d %H:%M:%S.%f') + self.params['delimiter'] |
| + 'testID:' + test_info['testID'] + self.params['delimiter'] |
| + 'TestStartAt:'+ test_info['start_time'] |
| + self.params['delimiter'] + 'Keyword:' + test_info['key_word'] |
| + self.params['delimiter'] + progress_info['percent'] |
| + self.params['delimiter'] + progress_info['note'] + '\n') |
| else: |
| line = (now.strftime('%Y-%m-%d %H:%M:%S.%f') + self.params['delimiter'] |
| + progress_info['percent'] + self.params['delimiter'] |
| + progress_info['note'] + '\n') |
| |
| if self.params['std_out'] > 0: |
| print line |
| self.prg_file.write(line) |
| self.prg_file.flush() |
| |
| def SendLine(self, test_info, info): |
| """Send test (one-line) information to the corresponding log file. |
| |
| Args: |
| test_info: test case related info: testID, start_time, key_word, etc |
| info: test results/error/progress information (single line) |
| test_info could be "None" for class other than TestCase |
| Returns: |
| return the status of file write operation |
| """ |
| if info['type'] == 'result': |
| return self.SendLineToResult(test_info, info) |
| elif info['type'] == 'error': |
| return self.SendLineToError(test_info, info) |
| elif info['type'] == 'progress': |
| return self.SendLineToProgress(test_info, info) |
| |
| def SendTestData(self, data_list): |
| """Send test data to result file.""" |
| for line in data_list: |
| self.rst_file.write(line + '\n') |
| self.err_file.flush() |
| |
| def SendLines(self, test_info, info): |
| """Send test output (multiple lines) to the corresponding log file. |
| |
| Args: |
| test_info: test case related info: testID, start_time, key_word, etc |
| info: test results/error/progress information |
| test_info could be "None" for class other than TestCase |
| Returns: |
| return 1 when succeed |
| """ |
| if info['type'] == 'result': |
| for line in info['note'].split('\n'): |
| if line == info['note'].split('\n')[0]: |
| dup_info = info.copy() |
| dup_info['note'] = line |
| self.SendLineToResult(test_info, dup_info) |
| else: |
| self.rst_file.write(line + '\n') |
| if self.params['std_out']: print line |
| self.rst_file.flush() |
| elif info['type'] == 'error': |
| for line in info['note'].split('\n'): |
| if line == info['note'].split('\n')[0]: |
| dup_info = info.copy() |
| dup_info['note'] = line |
| self.SendLineToError(test_info, dup_info) |
| else: |
| self.err_file.write(line + '\n') |
| if self.params['std_out']: print line |
| self.err_file.flush() |
| elif info['type'] == 'progress': |
| for line in info['note'].split('\n'): |
| if line == info['note'].split('\n')[0]: |
| dup_info = info.copy() |
| dup_info['note'] = line |
| self.SendLineToProgress(test_info, dup_info) |
| else: |
| self.prg_file.write(line + '\n') |
| if self.params['std_out']: print line |
| self.prg_file.flush() |
| return 1 |
| |
| def CreateResultInfo(self, status='Pass', msg=''): |
| """Create the result Info structure for logging. |
| |
| Args: |
| status: has value 'Pass' or 'Fail' |
| msg: test results information line(s) |
| Returns: |
| returns the infomation structure that should be logged to file |
| """ |
| d = {} |
| d.setdefault('type', 'result') |
| d.setdefault('Pass_Fail', status) |
| d.setdefault('note', msg) |
| return d |
| |
| def CreateErrorInfo(self, severity='intermediate', msg=''): |
| """Create the error Info structure for logging. |
| |
| Args: |
| severity: has value 'low', 'intermediate' or 'critical' |
| msg: test error information line(s) |
| Returns: |
| returns the infomation structure that should be logged to file |
| """ |
| d = {} |
| d.setdefault('type', 'error') |
| d.setdefault('severity', severity) |
| d.setdefault('note', msg) |
| return d |
| |
| def CreateProgressInfo(self, percent='0%', msg=''): |
| """Create the progress Info structure for logging. |
| |
| Args: |
| percent: completeness of test progress, in % |
| msg: test progress information line(s) |
| Returns: |
| returns the infomation structure that should be logged to file |
| """ |
| d = {} |
| d.setdefault('percent', percent) |
| d.setdefault('type', 'progress') |
| d.setdefault('note', msg) |
| return d |
| |
| def __del__(self): |
| """Close the log files.""" |
| self.rst_file.close() |
| self.err_file.close() |
| self.prg_file.close() |