File tetj.py of Package lsb-appchk-python
#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# Interface for generating TET like report journals
#
# tetj provides a way of creating test execution logs (jourals) in a format
# which can be analysed using standard TET journal tools without having to
# compile or link against the TET libraries. It does not intend to be a
# full working TET implementation
#
# Over time there's been an effort to add a few minimal smarts
# to avoid it being so easy to make invalid journals, but
# this is still mostly passive code: you have to take
# care of proper usage yourself
#
# Python module originally converted from C version (tetj.c 1.3)
# Author: Mats Wichmann, Intel Corporation on behalf of the LSB project
#
# Copyright (C) 2002-2009 The Linux Foundation
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the Linux Foundation nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import sys
import os
import pwd
import time
import cStringIO
TETJ_PASS = 0
TETJ_FAIL = 1
TETJ_UNRESOLVED = 2
TETJ_NOTINUSE = 3
TETJ_UNSUPPORTED = 4
TETJ_UNTESTED = 5
TETJ_UNINITIATED = 6
TETJ_UNREPORTED = 7
TETJ_WARNING = 101
TETJ_FIP = 102
TETJ_NOTIMP = 103
TETJ_UNAPPROVE = 104
RESULT_CODES = {
TETJ_PASS: 'PASS',
TETJ_FAIL: 'FAIL',
TETJ_UNRESOLVED: 'UNRESOLVED',
TETJ_NOTINUSE: 'NOTINUSE',
TETJ_UNSUPPORTED: 'UNSUPPORTED',
TETJ_UNTESTED: 'UNTESTED',
TETJ_UNINITIATED: 'UNITIATED',
TETJ_UNREPORTED: 'UNREPORTED',
TETJ_WARNING: 'WARNING',
TETJ_FIP: 'FIP',
TETJ_NOTIMP: 'NOTIMP',
TETJ_UNAPPROVE: 'UNAPPROVE',
}
def timestr():
"""format current time as needed for tet journal"""
return time.strftime('%H:%M:%S')
class Journal:
""" test journal class, implements tetj toolkit"""
tetj_vers = 'tetj.py-1.1'
def __init__(self, pathname, command_run):
"""starts a new journal file"""
try:
self.journalfile = open(pathname, 'w')
except IOError:
raise
self.journal = []
self.activity = -1 # count will start at 0
self.testcase = 0
self.block = 0
self.context = 0
self.sequence = 0
self.rescode = -1
(sysname, nodename, release, version, machine) = os.uname()
self.machine = machine # in case the test needs it
datetime = time.strftime('%H:%M:%S %Y%m%d')
uid = os.getuid()
try:
pwent = pwd.getpwuid(uid)
except KeyError:
pwent = 'Unknown'
self.__log(0, '%s %s' % (self.tetj_vers, datetime),
'User: %s (%i) TCC Start, Command line: %s' %
(pwent[0], uid, command_run))
self.__log(5, '%s %s %s %s %s' % (sysname, nodename, release,
version, machine), 'System Information')
# private methods
def __log(self, linetype, info, msg):
"""write message to journal with bounds checking"""
# historically, tet lines limited to 512 bytes, be compatible
# 3 delimiter chars, as in: linetype|info|msg\n
maxlen = 512 - len(str(linetype)) - len(info) - len(msg) -3
if len(msg) > maxlen:
self.journal.append("%d|%s|%s\n" % (linetype, info, msg[:maxlen]))
self.journal.append('510||warning: results file line truncated\n')
else:
self.journal.append("%d|%s|%s\n" % (linetype, info, msg))
def __setcontext(self):
"""update execution context for journals"""
if self.context != os.getpid():
self.context = os.getpid()
self.block = 1
self.sequence = 1
def __setblock(self):
"""update execution block for journals"""
self.block += 1
self.sequence = 1
def __error(self, message):
"""log an internal error message"""
self.__log(510, '', message)
def close(self):
"""closes a journal file and finishes all processing"""
self.__log(900, timestr(), 'TCC End')
self.journalfile.writelines(self.journal)
self.journalfile.close()
def config_start(self):
"""log a config-start event"""
self.__log(20, '', 'Config Start')
def add_config(self, message):
"""log a configuration description line"""
self.__log(30, '', message)
def config_end(self):
"""log a config-end event"""
self.__log(40, '', 'Config End')
def scenario_info(self, message):
"""log a scenario info message"""
self.__log(70, '', message)
def add_controller_error(self, message):
"""log a (pseudo) controller error event"""
self.__log(50, '', message)
def testcase_start(self, testpath, message=""):
"""log a testcase start event"""
self.activity += 1
self.testcase = 0
self.__log(10, '%u %s %s' % (self.activity, testpath, timestr()),
'TC Start %s' % message)
self.__log(15, '%u %s 1' % (self.activity, self.tetj_vers),
'TCM Start')
def testcase_end(self, message=""):
"""log a testcase end event"""
self.__log(80, '%u 0 %s' % (self.activity, timestr()),
'TC End %s' % message)
def purpose_start(self, message=""):
"""log a test purpose start event"""
# This is a shortcut. It assumes that every invocable component
# has a single test purpose, instead of potentially multiple ones.
# The wrapping should be: 400 (200 220) (200 220) ... 410
# Instead we make the call purpose_start mean 400 200,
# and predict the tp count within that ic as (hardwired) 1
self.testcase += 1
self.__log(400,
'%u %u 1 %s' % (self.activity, self.testcase, timestr()),
'IC Start')
self.__log(200, '%u %u %s' % (self.activity, self.testcase, timestr()),
'TP Start %s' %message)
def purpose_end(self):
"""log a test purpose end event"""
# log the previously saved result
# if no result recorded, add a UNREPORTED one
if self.rescode == -1:
self.rescode = 7
res = RESULT_CODES.get(self.rescode, 'UNREPORTED')
self.__log(220, '%u %u %u %s' % (self.activity, self.testcase,
self.rescode, timestr()), res)
self.rescode = -1
# Now log the test purpose end
# see note above: the hardwired '1' shouldn't be
self.__log(410, '%u %u 1 %s' %
(self.activity, self.testcase, timestr()),
'IC End')
def result(self, rescode):
"""log a test result"""
self.rescode = rescode
def testcase_info(self, context, block, sequence, message):
"""log a test information line"""
# note context/block/sequence are now ignored, left in as they
# were part of the "api"
if self.context == 0:
self.__setcontext()
# in case we got a multi-line info message, split it up
for line in cStringIO.StringIO(str(message)):
self.__log(520,
'%u %u %u %u %u' % (self.activity, self.testcase,
self.context, self.block, self.sequence),
line.strip())
self.sequence += 1
# add convenience methods for results
def result_pass(self):
self.result(TETJ_PASS)
def result_fail(self):
self.result(TETJ_FAIL)
def result_unresolved(self):
self.result(TETJ_UNRESOLVED)
def result_notinuse(self):
self.result(TETJ_NOTINUSE)
def result_unsupported(self):
self.result(TETJ_UNSUPPORTED)
def result_untested(self):
self.result(TETJ_UNTESTED)
def result_uninitiated(self):
self.result(TETJ_UNINITIATED)
def result_unreported(self):
self.result(TETJ_UNREPORTED)
def result_warning(self):
self.result(TETJ_WARNING)
def result_fip(self):
self.result(TETJ_FIP)
def result_notimp(self):
self.result(TETJ_NOTIMP)
def result_unapprove(self):
self.result(TETJ_UNAPPROVE)
#====================================================================
# example tetj-using program, used as a primitive self-test
# (Python version)
def _test():
"""exercise tetj.py code. not a formal selftest but should help"""
teststuff = [
('red', TETJ_PASS),
('green', TETJ_FAIL),
('blue', TETJ_UNRESOLVED),
('white', TETJ_NOTINUSE),
('black', TETJ_UNSUPPORTED),
('purple', TETJ_UNTESTED),
('teal', TETJ_UNINITIATED),
('yellow', TETJ_UNREPORTED),
('orange', TETJ_WARNING),
('plum', TETJ_FIP),
('foxglove', TETJ_NOTIMP),
('alabaster', TETJ_UNAPPROVE),
]
info = """This should be information about
why the test case did not succeed"""
try:
journal = Journal('journal.tetjtest-py', 'tetj.py')
except IOError:
print 'Cannot create journal.tetjtest.py'
sys.exit(1)
except:
raise
print 'tetj.py: writing journal to journal.tetjtest-py'
journal.add_config('VSX_NAME=tetjtest-py 0.1 (%s)' % journal.machine)
journal.config_end()
journal.scenario_info('"total tests in tetjtest 24"')
journal.testcase_start('foo')
for (purpose, tpresult) in teststuff:
journal.purpose_start(purpose)
if tpresult != TETJ_PASS:
journal.testcase_info(0, 0, 0, info)
journal.result(tpresult)
journal.purpose_end()
journal.testcase_end('foo')
journal.testcase_start('bar')
for (purpose, tpresult) in teststuff:
journal.purpose_start(purpose)
if tpresult != TETJ_PASS:
journal.testcase_info(0, 0, 0, info)
journal.result(tpresult)
journal.purpose_end()
journal.testcase_end('bar')
journal.close()
if __name__ == '__main__':
_test()