%TestSuite Collection of TestComponent objects % The TestSuite class defines a collection of TestComponent objects. % % TestSuite methods: % TestSuite - Constructor % add - Add test component to test suite % print - Display test suite summary to Command Window % run - Run the test suite % keepMatchingTestCase - Keep only the named test component % fromName - Construct test suite from directory or MATLAB function file name % fromTestCaseClassName - Construct test suite from TestCase class name % fromPackageName - Construct test suite from package name % fromPwd - Construct test suite from present directory % % TestSuite properties: % TestComponents - Cell array of TestComponent objects % % Examples % -------- % Run all the test cases in the SampleTests1 class. Display test suite % progress and a summary of results in the Command Window. % % TestSuite('SampleTests1').run() % % Construct a test suite from all test components found in the current % directory. % % suite = TestSuite.fromPwd(); % % Construct a test suite from all test components found in the package % 'mytool.tests'. (Note that the "+" character at the beginning of the package % folder name on disk is not part of the package name.) % % suite = TestSuite.fromPackageName('mytool.tests'); % % Run all the test cases in the SampleTests class. Display no output to the % Command Window. Upon completion, query the number of test failures and test % errors. % % logger = TestRunLogger(); % TestSuite('SampleTests1').run(logger); % numFailures = logger.NumFailures % numErrors = logger.NumErrors % % See also CommandWindowTestRunDisplay, TestCase, TestComponent, TestRunLogger % Steven L. Eddins % Copyright 2008-2010 The MathWorks, Inc. classdef TestSuite < TestComponent properties (SetAccess = protected) TestComponents = {}; end methods function self = TestSuite(name) %TestSuite Constructor % suite = TestSuite constructs an empty test suite. suite = % TestSuite(name) constructs a test suite by searching for test % cases defined in an M-file with the specified name. if nargin >= 1 self = TestSuite.fromName(name); end end function did_pass_out = run(self, monitor) %run Execute test cases in test suite % did_pass = suite.run() executes all test cases in the test % suite, returning a logical value indicating whether or not all % test cases passed. if nargin < 2 monitor = CommandWindowTestRunDisplay(); end monitor.testComponentStarted(self); did_pass = true; self.setUp(); for k = 1:numel(self.TestComponents) this_component_passed = self.TestComponents{k}.run(monitor); did_pass = did_pass && this_component_passed; end self.tearDown(); monitor.testComponentFinished(self, did_pass); if nargout > 0 did_pass_out = did_pass; end end function num = numTestCases(self) %numTestCases Number of test cases in test suite num = 0; for k = 1:numel(self.TestComponents) component_k = self.TestComponents{k}; num = num + component_k.numTestCases(); end end function print(self, numLeadingBlanks) %print Display test suite summary to Command Window % test_suite.print() displays a summary of the test suite to the % Command Window. if nargin < 2 numLeadingBlanks = 0; end fprintf('%s%s\n', blanks(numLeadingBlanks), self.Name); for k = 1:numel(self.TestComponents) self.TestComponents{k}.print(numLeadingBlanks + ... self.PrintIndentationSize); end end function add(self, component) %add Add test component to test suite % test_suite.add(component) adds the TestComponent object to the % test suite. if iscell(component) self.TestComponents((1:numel(component)) + end) = component; else self.TestComponents{end + 1} = component; end end function keepMatchingTestCase(self, name) %keepMatchingTestCase Keep only the named test component % test_suite.keepMatchingTestCase(name) keeps only the test % component with a matching name and discards the rest. idx = []; for k = 1:numel(self.TestComponents) if strcmp(self.TestComponents{k}.Name, name) idx = k; break; end end if isempty(idx) self.TestComponents = {}; else self.TestComponents = self.TestComponents(idx); end end end methods (Static) function suite = fromTestCaseClassName(class_name) %fromTestCaseClassName Construct test suite from TestCase class name % suite = TestSuite.fromTestCaseClassName(name) constructs a % TestSuite object from the name of a TestCase subclass. if ~xunit.utils.isTestCaseSubclass(class_name) error('xunit:fromTestCaseClassName', ... 'Input string "%s" is not the name of a TestCase class.', ... class_name); end suite = TestSuite; suite.Name = class_name; suite.Location = which(class_name); methods = getClassMethods(class_name); for k = 1:numel(methods) if methodIsConstructor(methods{k}) continue end method_name = methods{k}.Name; if xunit.utils.isTestString(method_name) suite.add(feval(class_name, method_name)); end end end function suite = fromName(name) %fromName Construct test suite from M-file name % test_suite = TestSuite.fromName(name) constructs a TestSuite % object from an M-file with the given name. The name can be of a % directory, a TestCase subclass, or an M-file containing a simple % test or containing subfunction-based tests. % % Optionally, name can contain a colon (':') followed by filter % string. The filter string is used to select a particular named % test case. For example, TestSuite.fromName('MyTests:testA') % constructs a TestSuite object containing only the test case % named 'testA' found in the TestCase subclass MyTests. if isdir(name) suite = TestSuiteInDir(name); suite.gatherTestCases(); return; end [name, filter_string] = strtok(name, ':'); if ~isempty(filter_string) filter_string = filter_string(2:end); end if xunit.utils.isTestCaseSubclass(name) suite = TestSuite.fromTestCaseClassName(name); elseif ~isempty(meta.class.fromName(name)) % Input is the name of a class that is not a TestCase subclass. % Return an empty test suite. suite = TestSuite(); suite.Name = name; elseif isPackage(name) suite = TestSuite.fromPackageName(name); else try if nargout(name) == 0 suite = TestSuite(); suite.Name = name; suite.add(FunctionHandleTestCase(str2func(name), [], [])); suite.Location = which(name); else suite = feval(name); if ~isa(suite, 'TestSuite') error('Function did not return a TestSuite object.'); end end catch % Ordinary function does not appear to contain tests. % Return an empty test suite. suite = TestSuite(); suite.Name = name; end end if ~isempty(filter_string) suite.keepMatchingTestCase(filter_string); end end function test_suite = fromPwd() %fromPwd Construct test suite from present directory % test_suite = TestSuite.fromPwd() constructs a TestSuite object % from all the test components in the present working directory. % all TestCase subclasses will be found, as well as simple and % subfunction-based M-file tests beginning with the string 'test' % or 'Test'. test_suite = TestSuite(); test_suite.Name = pwd; test_suite.Location = pwd; mfiles = dir(fullfile('.', '*.m')); for k = 1:numel(mfiles) [path, name] = fileparts(mfiles(k).name); if xunit.utils.isTestCaseSubclass(name) test_suite.add(TestSuite.fromTestCaseClassName(name)); elseif xunit.utils.isTestString(name) suite_k = TestSuite.fromName(name); if ~isempty(suite_k.TestComponents) test_suite.add(suite_k); end end end end function test_suite = fromPackageName(name) %fromPackageName Construct test suite from package name % test_suite = TestSuite.fromPackageName(name) constructs a % TestSuite object from all the test components found in the % specified package. package_info = meta.package.fromName(name); if isempty(package_info) error('xunit:fromPackageName:invalidName', ... 'Input string "%s" is not the name of a package.', ... name); end test_suite = TestSuite(); test_suite.Name = name; test_suite.Location = 'Package'; for k = 1:numel(package_info.Packages) pkg_name = package_info.Packages{k}.Name; pkg_suite = TestSuite.fromPackageName(pkg_name); if ~isempty(pkg_suite.TestComponents) test_suite.add(TestSuite.fromPackageName(pkg_name)); end end class_names = cell(1, numel(package_info.Classes)); for k = 1:numel(package_info.Classes) class_name = package_info.Classes{k}.Name; class_names{k} = class_name; if xunit.utils.isTestCaseSubclass(class_name) test_suite.add(TestSuite.fromTestCaseClassName(class_name)); end end for k = 1:numel(package_info.Functions) function_name = package_info.Functions{k}.Name; if xunit.utils.isTestString(function_name) full_function_name = [package_info.Name '.' package_info.Functions{k}.Name]; if ~ismember(full_function_name, class_names) suite_k = TestSuite.fromName(full_function_name); if ~isempty(suite_k.TestComponents) test_suite.add(suite_k); end end end end end end end function tf = isPackage(name) tf = ~isempty(meta.package.fromName(name)); end function methods = getClassMethods(class_name) class_meta = meta.class.fromName(class_name); methods = class_meta.Methods; end function result = methodIsConstructor(method) method_name = method.Name; if ~isempty(method.DefiningClass.ContainingPackage) method_name = [method.DefiningClass.ContainingPackage.Name, '.', ... method_name]; end result = strcmp(method_name, method.DefiningClass.Name); end