#! /usr/bin/env python # encoding: utf-8 # Carlos Rafael Giani, 2006 """ Unit tests run in the shutdown() method, and for c/c++ programs One should NOT have to give parameters to programs to execute In the shutdown method, add the following code: >>> def shutdown(): ... ut = UnitTest.unit_test() ... ut.run() ... ut.print_results() Each object to use as a unit test must be a program and must have X{obj.unit_test=1} """ import Params, Object, pproc class unit_test: "Unit test representation" def __init__(self): self.returncode_ok = 0 # Unit test returncode considered OK. All returncodes differing from this one # will cause the unit test to be marked as "FAILED". # The following variables are filled with data by run(). # print_results() uses these for printing the unit test summary, # but if there is need for direct access to the results, # they can be retrieved here, after calling run(). self.num_tests_ok = 0 # Number of successful unit tests self.num_tests_failed = 0 # Number of failed unit tests self.num_tests_err = 0 # Tests that have not even run self.total_num_tests = 0 # Total amount of unit tests self.max_label_length = 0 # Maximum label length (pretty-print the output) self.unit_tests = {} # Unit test dictionary. Key: the label (unit test filename relative # to the build dir), value: unit test filename with absolute path self.unit_test_results = {} # Dictionary containing the unit test results. # Key: the label, value: result (true = success false = failure) self.unit_test_erroneous = {} # Dictionary indicating erroneous unit tests. # Key: the label, value: true = unit test has an error false = unit test is ok def run(self): "Run the unit tests and gather results (note: no output here)" self.num_tests_ok = 0 self.num_tests_failed = 0 self.num_tests_err = 0 self.total_num_tests = 0 self.max_label_length = 0 self.unit_tests = {} self.unit_test_results = {} self.unit_test_erroneous = {} # If waf is not building, don't run anything if not Params.g_commands['build']: return # Gather unit tests to call for obj in Object.g_allobjs: if not hasattr(obj,'unit_test'): continue unit_test = getattr(obj,'unit_test') if not unit_test: continue try: if obj.m_type == 'program': filename = obj.m_linktask.m_outputs[0].abspath(obj.env) label = obj.m_linktask.m_outputs[0].bldpath(obj.env) self.max_label_length = max(self.max_label_length, len(label)) self.unit_tests[label] = filename except: pass self.total_num_tests = len(self.unit_tests) # Now run the unit tests for label, filename in self.unit_tests.iteritems(): try: pp = pproc.Popen(filename, stdout = pproc.PIPE, stderr = pproc.PIPE) # PIPE for ignoring output pp.wait() result = int(pp.returncode == self.returncode_ok) if result: self.num_tests_ok += 1 else: self.num_tests_failed += 1 self.unit_test_results[label] = result self.unit_test_erroneous[label] = 0 except: self.unit_test_erroneous[label] = 1 self.num_tests_err += 1 def print_results(self): "Pretty-prints a summary of all unit tests, along with some statistics" # If waf is not building, don't output anything if not Params.g_commands['build']: return p = Params.pprint # Early quit if no tests were performed if self.total_num_tests == 0: p('YELLOW', 'No unit tests present') return p('GREEN', 'Running unit tests') print for label, filename in self.unit_tests.iteritems(): err = 0 result = 0 try: err = self.unit_test_erroneous[label] except KeyError: pass try: result = self.unit_test_results[label] except KeyError: pass n = self.max_label_length - len(label) if err: n += 4 elif result: n += 7 else: n += 3 line = '%s %s' % (label, '.' * n) print line, if err: p('RED', 'ERROR') elif result: p('GREEN', 'OK') else: p('YELLOW', 'FAILED') percentage_ok = float(self.num_tests_ok) / float(self.total_num_tests) * 100.0 percentage_failed = float(self.num_tests_failed) / float(self.total_num_tests) * 100.0 percentage_erroneous = float(self.num_tests_err) / float(self.total_num_tests) * 100.0 print print "Successful tests: %i (%.1f%%)" % (self.num_tests_ok, percentage_ok) print "Failed tests: %i (%.1f%%)" % (self.num_tests_failed, percentage_failed) print "Erroneous tests: %i (%.1f%%)" % (self.num_tests_err, percentage_erroneous) print print "Total number of tests: %i" % self.total_num_tests print p('GREEN', 'Unit tests finished')