classdef TextTestRunner < TestRunner
% TEXTTESTRUNNER A text-only interface for running tests.
%
% This class is part of the mlunit_2008a testing framework.
%
% The "public" interface of the class is as follows:
%
% TextTestRunner([names])
% Constructor for the TextTestRunner. The optional names parameter
% lets you set up the runner with an initial set of tests. See
% addTests below for syntax of the names parameter.
%
% addTests(obj, names)
% Adds tests to the list of tests to be run by the runner. The names
% parameter can be a string or a cell array of strings. Each string
% should be the name of a test case or test suite class, which must
% be in the current MATLAB path. Any test results are lost.
%
% clearTests(obj)
% Removes any previously added tests and any associated results.
%
% runTests(obj)
% Runs all tests. A text progress bar is provided, but note that it
% will be completely munged if any of the code under test uses disp
% or fprintf to write out to the screen. After the run, a summary of
% test results is displayed.
%
% showReport(obj)
% Shows a summary of any existing test results. This is useful, for
% instance, while fixing a set of broken tests; rather than rerunning
% all of the tests, you can use showReport() to display the summary
% of the most recent test run.
%
properties
progressCount;
failFlag;
end
% "public" interface
methods
function self = TextTestRunner(names)
if nargin < 1, names = {}; end
if ~iscell(names)
names = {names};
end
self.test = DynamicTestSuite(names);
self.initialize();
end
function addTests(self, names)
if ~iscell(names)
names = {names};
end
for i = 1:length(names)
self.test.add(names{i});
end
self.initialize();
end
function clearTests(self)
self.test = DynamicTestSuite();
self.initialize();
end
function runTests(self, names)
if nargin == 2
self.clearTests();
self.addTests(names);
end
self.initialize();
% setup for progress monitoring
fprintf('\nRunning tests:\n');
fprintf('%% complete: 0');
for p = 1:9
fprintf('---+');
end
fprintf('--100\n');
fprintf(' |');
self.progressCount = 0;
self.failFlag = false;
try
self.testResult.runTests();
catch ME
fprintf('\n\nStopped on exception:\n\n');
disp(ME.getReport());
end
self.showReport();
end
function showReport(self)
tc = self.testResult.testCount;
fc = self.testResult.failureCount;
sc = self.testResult.successCount;
runs = fc + sc;
fprintf('\nTest results: %d/%d attempted, %d successful, %d failed.\n\n', runs, tc, sc, fc);
if fc > 0
uresp = input('Show individual failures (y/N)? ', 's');
fprintf('\n');
if strncmpi(uresp, 'y', 1)
self.showFailures(self.testResult.children, 0);
end
end
end
end
% "private" methods
methods
function initialize(self)
initialize@TestRunner(self);
self.testResult.parent = self;
end
function incrementFailureCount(self)
self.failFlag = true;
self.updateProgress();
end
function incrementSuccessCount(self)
self.updateProgress();
end
function updateProgress(self)
tc = self.testResult.testCount;
fc = self.testResult.failureCount;
sc = self.testResult.successCount;
runs = fc + sc;
ticks = floor(40 * runs / tc);
if ticks > self.progressCount
c = '=';
if self.failFlag
c = 'x';
end
while self.progressCount < ticks
fprintf(c);
self.progressCount = self.progressCount + 1;
end
self.failFlag = false;
end
if runs == tc
fprintf('|\n');
end
end
function showFailures(self, tresults, level)
rnames = fieldnames(tresults);
for i = 1:length(rnames)
rn = rnames{i};
tr = tresults.(rn);
if tr.failureCount > 0
self.printBlanks(level);
fprintf('%s\n', rn);
if tr.isSuite();
self.showFailures(tr.children, level+1);
else
self.showMethodFailures(tr.children, level+1);
end
end
end
fprintf('\n');
end
function showMethodFailures(self, mresults, level)
mnames = fieldnames(mresults);
for i = 1:length(mnames)
mn = mnames{i};
mr = mresults.(mn);
if isstruct(mr)
self.printBlanks(level);
fprintf('%s: %s\n', mn, mr.message);
end
end
end
function printBlanks(self, count)
for i = 1:count
fprintf(' ');
end
end
function display(self)
fprintf('a TextTestRunner ');
if isempty(self.test) || isempty(self.test.children)
fprintf('(no tests)');
elseif isempty(self.testResult) || (self.testResult.failureCount + self.testResult.successCount) == 0
fprintf('(no results)');
else
tc = self.testResult.testCount;
fc = self.testResult.failureCount;
sc = self.testResult.successCount;
runs = fc + sc;
fprintf('(%d/%d results, %d successful, %d failed)', runs, tc, sc, fc);
end
fprintf('\n');
end
end
end