From 4cea0f6cd1ef9b82109a4d407e31e2dcd9d56d2a Mon Sep 17 00:00:00 2001 From: Joris Kraak Date: Fri, 9 Dec 2016 12:52:54 +0100 Subject: [PATCH 1/4] test(arrayToString): respect visual changes to `disp` output across versions --- tests/test_arrayToString.m | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tests/test_arrayToString.m b/tests/test_arrayToString.m index 62ad644..5f86d8f 100644 --- a/tests/test_arrayToString.m +++ b/tests/test_arrayToString.m @@ -12,7 +12,14 @@ function test_largeInput A = zeros(1000, 1000); -assertEqual(xunit.utils.arrayToString(A), '[1000x1000 double]'); + +% The way `disp` visualizes 'times' has changed in R2016b and onwards, so the +% test needs to be a bit more flexible +assertTrue(xunit.utils.containsRegexp('[1000.1000 double]', ... + xunit.utils.arrayToString(A))); function test_emptyInput -assertEqual(xunit.utils.arrayToString(zeros(1,0,2)), '[1x0x2 double]'); +% The way `disp` visualizes 'times' has changed in R2016b and onwards, so the +% test needs to be a bit more flexible +assertTrue(xunit.utils.containsRegexp('[1.0.2 double]', ... + xunit.utils.arrayToString(zeros(1,0,2)))); From bae98a71cd31d9a98f4636a6617f2f9ec2a59e03 Mon Sep 17 00:00:00 2001 From: annehendrikse Date: Thu, 1 Dec 2016 15:43:20 +0100 Subject: [PATCH 2/4] feat(buildFunctionHandleTestSuite): provide function-based test suites on R2016b and later The `initTestSuite` can no longer be used in R2016b and later, due to the way scripts are scoped in those versions. Instead the `buildFunctionHandleTestSuite` function should be used. In fact, this is the preferred way for generating these types of test suites across all MATLAB versions that support them. `initTestSuite` has been updated to generate warnings about the changed behavior in R2016b and errors out with a descriptive error on affected versions. And documentation is available in `buildFunctionHandleTestSuite` describing how to support older MATLAB versions. Fixes #19. --- .../html/matlab_xunit_architecture.html | 20 ++-- architecture/matlab_xunit_architecture.m | 13 +-- architecture/testSample.m | 4 +- doc/exSubfunctionTests.m | 6 +- doc/example_subfunction_tests/testFliplr.m | 4 +- doc/examples_general/testBadSinTest.m | 4 +- doc/examples_general/testCos.m | 4 +- doc/examples_general/testSetupExample.m | 4 +- doc/examples_general/testWithSetupError.m | 4 +- doc/html/exException.html | 4 +- doc/html/exSubfunctionTests.html | 15 ++- doc/html/exTestFixtures.html | 4 +- matlab-xunit/Contents.m | 17 +-- src/+xunit/+private/retrieveSetupFunction.m | 35 ++++++ .../+private/retrieveTearDownFunction.m | 36 ++++++ src/+xunit/+private/retrieveTestFunctions.m | 30 +++++ src/TestSuite.m | 31 +++++- src/buildFunctionHandleTestSuite.m | 86 +++++++++++++++ src/initTestSuite.m | 79 ++++---------- src/runxunit.m | 2 +- tests/+xunit/+mocktests/+subpkg/test_a_bit.m | 6 +- tests/+xunit/+mocktests/test_that.m | 6 +- tests/TestInitTestSuite.m | 67 ++++++++++++ tests/cwd_test/testSubfunctions.m | 4 +- tests/dir1/test_thatPasses.m | 4 +- tests/dir2/test_thatFails.m | 4 +- tests/helper_classes/notTestString.m | 4 +- tests/helper_classes/testFunctionHandlesA.m | 4 +- tests/helper_classes/testFunctionHandlesB.m | 4 +- tests/helper_classes/testFunctionHandlesC.m | 4 +- tests/helper_classes/testFunctionHandlesD.m | 4 +- tests/helper_classes/testFunctionHandlesE.m | 4 +- .../testFunctionHandlesTeardownNoSetup.m | 4 +- .../+packaged/initTestSuiteTest.m | 11 ++ tests/initTestSuiteTest/initTestSuiteTest.m | 11 ++ tests/src/+xunit/+private/private/setUp2.m | 4 + tests/src/+xunit/+private/private/setup.m | 4 + tests/src/+xunit/+private/private/tearDown.m | 4 + tests/src/+xunit/+private/private/tearDown2.m | 4 + .../+private/testRetrieveSetupFunction.m | 54 +++++++++ .../+private/testRetrieveTearDownFunction.m | 54 +++++++++ .../+private/testRetrieveTestFunctions.m | 47 ++++++++ tests/src/private/emptyTest.m | 4 + tests/src/private/persistValue.m | 12 ++ tests/src/private/runTest.m | 8 ++ tests/src/private/setup.m | 4 + tests/src/private/teardown.m | 4 + tests/src/private/testAtTheStart.m | 4 + tests/src/testBuildFunctionHandleTestSuite.m | 103 ++++++++++++++++++ tests/testAssertEqual.m | 4 +- tests/testAssertExceptionThrown.m | 4 +- tests/testAssertFalse.m | 4 +- tests/testAssertTrue.m | 4 +- tests/testContainsRegexp.m | 4 +- tests/testIsSetUpString.m | 4 +- tests/testIsTearDownString.m | 4 +- tests/testIsTestCaseSubclass.m | 4 +- tests/testIsTestString.m | 4 +- tests/testRunxunitWithDirectoryName.m | 4 +- tests/test_TestSuiteInDir.m | 4 +- tests/test_arrayToString.m | 4 +- tests/test_assertElementsAlmostEqual.m | 4 +- tests/test_assertFilesEqual.m | 4 +- tests/test_assertVectorsAlmostEqual.m | 4 +- tests/test_compareFloats.m | 4 +- tests/test_comparisonMessage.m | 4 +- tests/test_packageName.m | 4 +- tests/test_parseFloatAssertInputs.m | 4 +- tests/test_stringToCellArray.m | 4 +- 69 files changed, 753 insertions(+), 180 deletions(-) create mode 100644 src/+xunit/+private/retrieveSetupFunction.m create mode 100644 src/+xunit/+private/retrieveTearDownFunction.m create mode 100644 src/+xunit/+private/retrieveTestFunctions.m create mode 100644 src/buildFunctionHandleTestSuite.m create mode 100644 tests/TestInitTestSuite.m create mode 100644 tests/initTestSuiteTest/+packaged/initTestSuiteTest.m create mode 100644 tests/initTestSuiteTest/initTestSuiteTest.m create mode 100644 tests/src/+xunit/+private/private/setUp2.m create mode 100644 tests/src/+xunit/+private/private/setup.m create mode 100644 tests/src/+xunit/+private/private/tearDown.m create mode 100644 tests/src/+xunit/+private/private/tearDown2.m create mode 100644 tests/src/+xunit/+private/testRetrieveSetupFunction.m create mode 100644 tests/src/+xunit/+private/testRetrieveTearDownFunction.m create mode 100644 tests/src/+xunit/+private/testRetrieveTestFunctions.m create mode 100644 tests/src/private/emptyTest.m create mode 100644 tests/src/private/persistValue.m create mode 100644 tests/src/private/runTest.m create mode 100644 tests/src/private/setup.m create mode 100644 tests/src/private/teardown.m create mode 100644 tests/src/private/testAtTheStart.m create mode 100644 tests/src/testBuildFunctionHandleTestSuite.m diff --git a/architecture/html/matlab_xunit_architecture.html b/architecture/html/matlab_xunit_architecture.html index f2c9b30..29ea2ff 100644 --- a/architecture/html/matlab_xunit_architecture.html +++ b/architecture/html/matlab_xunit_architecture.html @@ -98,9 +98,10 @@ ...
  function C
      ...
  function D
      ...

The first function in the file, A, has the same name as the file. When other code outside this function calls A, it is this first function that gets called. Functions B, C, and D are called subfunctions. Normally, these subfunctions are only visible to and can only be called by A. The only way that code elsewhere might be able to call B, C, or D is if function A forms handles to them and passes those handles out of its scope. Normally this would be done by returning the function handles as output arguments.

Note that no code executing outside the scope of a function in A.m can form function handles to B, C, or D, or can even determine that these functions exist.

This obviously poses a problem for test discovery!

The MATLAB xUnit solution is to establish the following convention for subfunction-based tests. The first function in a test M-file containing subfunction tests has to begin with these lines:

  === File A.m ===
-  function test_suite = A
-  initTestSuite;
-  ...

initTestSuite is a script that runs in the scope of the function A. initTestSuite determines which subfunctions are test functions, as well as setup or teardown functions. It forms handles to these functions and constructs a set of FunctionHandleTestCase objects, which function A returns as the output argument test_suite.

TestRunMonitor

The abstract TestRunMonitor class defines the interface for an object that "observe" the in-progress execution of a test suite. MATLAB xUnit provides two subclasses of TestRunMonitor:

  • TestRunLogger silently logs test suite events and captures the details of any test failures or test errors.
  • CommandWindowTestRunDisplay prints the progress of an executing test suite to the Command Window.

A TestRunMonitor is passed to the run() method of a TestComponent object. The run() method calls the appropriate notification methods of the monitor.

Here is the output when using the CommandWindowTestRunDisplay object on the MATLAB xUnit's own test suite:

  runxunit
+  function testSuite = A
+  testSuite = buildFunctionHandleTestSuite(localfunctions);
+  ...
+

buildFunctionHandleTestSuite(localfunctions) determines which subfunctions are test functions, as well as setup or teardown functions. It forms handles to these functions and constructs a set of FunctionHandleTestCase objects, which function A returns as the output argument testSuite.

TestRunMonitor

The abstract TestRunMonitor class defines the interface for an object that "observe" the in-progress execution of a test suite. MATLAB xUnit provides two subclasses of TestRunMonitor:

  • TestRunLogger silently logs test suite events and captures the details of any test failures or test errors.
  • CommandWindowTestRunDisplay prints the progress of an executing test suite to the Command Window.

A TestRunMonitor is passed to the run() method of a TestComponent object. The run() method calls the appropriate notification methods of the monitor.

Here is the output when using the CommandWindowTestRunDisplay object on the MATLAB xUnit's own test suite:

  runxunit
   Starting test run with 92 test cases.
   ....................
   ....................
@@ -313,15 +314,14 @@
 % subfunction tests has to begin with these lines:
 %
 %    === File A.m ===
-%    function test_suite = A
-%    initTestSuite;
+%    function testSuite = A
+%    testSuite = buildFunctionHandleTestSuite(localfunctions);
 %    ...
 %
-% |initTestSuite| is a _script_ that runs in the scope of the function |A|.
-% |initTestSuite| determines which subfunctions are test functions, as well as setup
-% or teardown functions.  It forms handles to these functions and constructs a
-% set of FunctionHandleTestCase objects, which function |A| returns as the
-% output argument |test_suite|.
+% |buildFunctionHandleTestSuite(localfunctions)| determines which subfunctions
+% are test functions, as well as setup or teardown functions.  It forms
+% handles to these functions and constructs a set of FunctionHandleTestCase
+% objects, which function |A| returns as the output argument |testSuite|.
 
 %% TestRunMonitor
 % The abstract |TestRunMonitor| class defines the interface for an object that
diff --git a/architecture/matlab_xunit_architecture.m b/architecture/matlab_xunit_architecture.m
index 86c5977..5c74108 100644
--- a/architecture/matlab_xunit_architecture.m
+++ b/architecture/matlab_xunit_architecture.m
@@ -179,15 +179,14 @@
 % subfunction tests has to begin with these lines:
 %
 %    === File A.m ===
-%    function test_suite = A
-%    initTestSuite;
+%    function testSuite = A
+%    testSuite = buildFunctionHandleTestSuite(localfunctions);
 %    ...
 %
-% |initTestSuite| is a _script_ that runs in the scope of the function |A|.
-% |initTestSuite| determines which subfunctions are test functions, as well as setup
-% or teardown functions.  It forms handles to these functions and constructs a
-% set of FunctionHandleTestCase objects, which function |A| returns as the
-% output argument |test_suite|.
+% |buildFunctionHandleTestSuite(localfunctions)| determines which subfunctions
+% are test functions, as well as setup or teardown functions.  It forms handles
+% to these functions and constructs a set of FunctionHandleTestCase objects,
+% which function |A| returns as the output argument |testSuite|.
 
 %% TestRunMonitor
 % The abstract |TestRunMonitor| class defines the interface for an object that
diff --git a/architecture/testSample.m b/architecture/testSample.m
index 846f49c..9356869 100644
--- a/architecture/testSample.m
+++ b/architecture/testSample.m
@@ -1,5 +1,5 @@
-function test_suite = testSample
-initTestSuite;
+function testSuite = testSample
+testSuite = buildFunctionHandleTestSuite(localfunctions);
 
 function testMyCode
 assertEqual(1, 1);
diff --git a/doc/exSubfunctionTests.m b/doc/exSubfunctionTests.m
index d16297c..1d95ed0 100644
--- a/doc/exSubfunctionTests.m
+++ b/doc/exSubfunctionTests.m
@@ -6,10 +6,10 @@
 % Name your M-file beginning or ending with "test", like
 % "testMyFunc".  Start by putting the following two lines at the
 % beginning of the file.  It's important that the output variable
-% name on line 1 be |test_suite|.
+% name on line 1 be |testSuite|.
 %
-%    function test_suite = testMyFunc
-%    initTestSuite;
+%    function testSuite = testMyFunc
+%    testSuite = buildFunctionHandleTestSuite(localfunctions);
 %
 % Next, add subfunctions to the file.  Each subfunction beginning
 % or ending with "test" becomes an individual test case.
diff --git a/doc/example_subfunction_tests/testFliplr.m b/doc/example_subfunction_tests/testFliplr.m
index 3a5b3da..12e2248 100644
--- a/doc/example_subfunction_tests/testFliplr.m
+++ b/doc/example_subfunction_tests/testFliplr.m
@@ -1,5 +1,5 @@
-function test_suite = testFliplr
-initTestSuite;
+function testSuite = testFliplr
+testSuite = buildFunctionHandleTestSuite(localfunctions);
 
 function testFliplrMatrix
 in = magic(3);
diff --git a/doc/examples_general/testBadSinTest.m b/doc/examples_general/testBadSinTest.m
index 9a5bfb4..51c24be 100644
--- a/doc/examples_general/testBadSinTest.m
+++ b/doc/examples_general/testBadSinTest.m
@@ -1,5 +1,5 @@
-function test_suite = testBadSinTest
-initTestSuite;
+function testSuite = testBadSinTest
+testSuite = buildFunctionHandleTestSuite(localfunctions);
 
 function testSinPi
 % Example of a failing test case.  The test writer should have used
diff --git a/doc/examples_general/testCos.m b/doc/examples_general/testCos.m
index 642ac5f..1a89969 100644
--- a/doc/examples_general/testCos.m
+++ b/doc/examples_general/testCos.m
@@ -1,5 +1,5 @@
-function test_suite = testCos
-initTestSuite;
+function testSuite = testCos
+testSuite = buildFunctionHandleTestSuite(localfunctions);
 
 function testTooManyInputs
 assertExceptionThrown(@() cos(1, 2), 'MATLAB:maxrhs');
\ No newline at end of file
diff --git a/doc/examples_general/testSetupExample.m b/doc/examples_general/testSetupExample.m
index eec3b60..cad2ac3 100644
--- a/doc/examples_general/testSetupExample.m
+++ b/doc/examples_general/testSetupExample.m
@@ -1,5 +1,5 @@
-function test_suite = testSetupExample
-initTestSuite;
+function testSuite = testSetupExample
+testSuite = buildFunctionHandleTestSuite(localfunctions);
 
 function fh = setup
 fh = figure;
diff --git a/doc/examples_general/testWithSetupError.m b/doc/examples_general/testWithSetupError.m
index 52becab..196111b 100644
--- a/doc/examples_general/testWithSetupError.m
+++ b/doc/examples_general/testWithSetupError.m
@@ -1,8 +1,8 @@
-function test_suite = testWithSetupError
+function testSuite = testWithSetupError
 %Example of a test with an error.  The setup function calls cos with
 %too many input arguments.
 
-initTestSuite;
+testSuite = buildFunctionHandleTestSuite(localfunctions);
 
 function testData = setup
 testData = cos(1, 2);
diff --git a/doc/html/exException.html b/doc/html/exException.html
index 1b41874..e2d77c0 100644
--- a/doc/html/exException.html
+++ b/doc/html/exException.html
@@ -81,8 +81,8 @@
 

assertExceptionThrown verifies that when f() is called, an error results with the specified error identifier.

Here's our error condition test for the cos function.

cd examples_general
 type testCos
 
-function test_suite = testCos
-initTestSuite;
+function testSuite = testCos
+testSuite = buildFunctionHandleTestSuite(localfunctions);
 
 function testTooManyInputs
 assertExceptionThrown(@() cos(1, 2), 'MATLAB:maxrhs');
diff --git a/doc/html/exSubfunctionTests.html b/doc/html/exSubfunctionTests.html
index aee8652..4db635b 100644
--- a/doc/html/exSubfunctionTests.html
+++ b/doc/html/exSubfunctionTests.html
@@ -65,13 +65,13 @@
 
 
 
-  

MATLAB xUnit Test Framework: How to Put Multiple Test Cases in One M-file

The Quick Start example showed how you can write a simple M-file to be a single test case. This example shows you how to put multiple test cases in one M-file.

Name your M-file beginning or ending with "test", like "testMyFunc". Start by putting the following two lines at the beginning of the file. It's important that the output variable name on line 1 be test_suite.

  function test_suite = testMyFunc
-  initTestSuite;

Next, add subfunctions to the file. Each subfunction beginning or ending with "test" becomes an individual test case.

The directory example_subfunction_tests contains a test M-file containing subfunction test cases for the fliplr function.

cd example_subfunction_tests
+  

MATLAB xUnit Test Framework: How to Put Multiple Test Cases in One M-file

The Quick Start example showed how you can write a simple M-file to be a single test case. This example shows you how to put multiple test cases in one M-file.

Name your M-file beginning or ending with "test", like "testMyFunc". Start by putting the following two lines at the beginning of the file. It's important that the output variable name on line 1 be testSuite.

  function testSuite = testMyFunc
+  testSuite = buildFunctionHandleTestSuite(localfunctions);

Next, add subfunctions to the file. Each subfunction beginning or ending with "test" becomes an individual test case.

The directory example_subfunction_tests contains a test M-file containing subfunction test cases for the fliplr function.

cd example_subfunction_tests
 
 type testFliplr
 
-function test_suite = testFliplr
-initTestSuite;
+function testSuite = testFliplr
+testSuite = buildFunctionHandleTestSuite(localfunctions);
 
 function testFliplrMatrix
 in = magic(3);
@@ -97,11 +97,10 @@
 %
 % Name your M-file beginning or ending with "test", like
 % "testMyFunc".  Start by putting the following two lines at the
-% beginning of the file.  It's important that the output variable
-% name on line 1 be |test_suite|.
+% beginning of the file.
 %
-%    function test_suite = testMyFunc
-%    initTestSuite;
+%    function testSuite = testMyFunc
+%    testSuite = buildFunctionHandleTestSuite(localfunctions);
 %
 % Next, add subfunctions to the file.  Each subfunction beginning
 % or ending with "test" becomes an individual test case.
diff --git a/doc/html/exTestFixtures.html b/doc/html/exTestFixtures.html
index 868f46b..2e9cdcd 100644
--- a/doc/html/exTestFixtures.html
+++ b/doc/html/exTestFixtures.html
@@ -68,8 +68,8 @@
   

MATLAB xUnit Test Framework: How to Write Tests That Share Common Set-Up Code

Sometimes you want to write a set of test cases in which the same set of initialization steps is performed before each test case, or in which the same set of cleanup steps is performed after each test case. This set of common setup and teardown code is called a test fixture.

In subfunction-based test files, you can add subfunctions whose names begin with "setup" and "teardown". These functions will be called before and after every test-case subfunction is called. If the setup function returns an output argument, that value is saved and passed to every test-case subfunction and also to the teardown function.

This example shows a setup function that creates a figure and returns its handle. The figure handle is passed to each test-case subfunction. The figure handle is also passed to the teardown function, which cleans up after each test case by deleting the figure.

cd examples_general
 type testSetupExample
 
-function test_suite = testSetupExample
-initTestSuite;
+function testSuite = testSetupExample
+testSuite = buildFunctionHandleTestSuite(localfunctions);
 
 function fh = setup
 fh = figure;
diff --git a/matlab-xunit/Contents.m b/matlab-xunit/Contents.m
index 08cee93..0d62d26 100644
--- a/matlab-xunit/Contents.m
+++ b/matlab-xunit/Contents.m
@@ -5,14 +5,15 @@
 %   runxunit                  - Run unit tests
 %
 % Writing Unit Tests
-%   assertElementsAlmostEqual - Assert floating-point array elements almost equal
-%   assertEqual               - Assert that inputs are equal
-%   assertFilesEqual          - Assert that two files have the same content
-%   assertExceptionThrown     - Assert that specified exception is thrown
-%   assertFalse               - Assert that input condition is false
-%   assertTrue                - Assert that input condition is true
-%   assertVectorsAlmostEqual  - Assert floating-point vectors almost equal in norm sense
-%   initTestSuite             - Utility script used for subfunction-based tests
+%   assertElementsAlmostEqual    - Assert floating-point array elements almost equal
+%   assertEqual                  - Assert that inputs are equal
+%   assertFilesEqual             - Assert that two files have the same content
+%   assertExceptionThrown        - Assert that specified exception is thrown
+%   assertFalse                  - Assert that input condition is false
+%   assertTrue                   - Assert that input condition is true
+%   assertVectorsAlmostEqual     - Assert floating-point vectors almost equal in norm sense
+%   buildFunctionHandleTestSuite - Utility function used for subfunction-based tests
+%   initTestSuite                - (Deprecated) Utility script used for subfunction-based tests
 %
 % Framework Classes
 %   CommandWindowTestRunDisplay - Print test suite results to command window
diff --git a/src/+xunit/+private/retrieveSetupFunction.m b/src/+xunit/+private/retrieveSetupFunction.m
new file mode 100644
index 0000000..881b362
--- /dev/null
+++ b/src/+xunit/+private/retrieveSetupFunction.m
@@ -0,0 +1,35 @@
+%XUNIT.PRIVATE.RETRIEVESETUPFUNCTION retrieves the setup function from a list of function handles
+%
+% Description:
+%   buildFunctionHandleTestSuite needs to retrieve the setup function from the
+%   list of local function handles. retrieveSetupFunction does this retrieval.
+%   If multiple functions qualify as a setup function, an exception is thrown.
+%
+% Syntax:
+%   hSetupFunction = xunit.private.retrieveSetupFunction(hFunctions)
+%
+% Input:
+%   hFunctions - cell array of function handles, possibly containing a function
+%                handle to a valid setup function
+%
+% Output:
+%   hSetupFunction - function handle to the setup function in the provided
+%                    array of function handles.
+
+function hSetupFunction = retrieveSetupFunction(hFunctions)
+  functionNames = cellfun(@functionHandleToName, hFunctions, 'UniformOutput', false);
+  isSetupFunction = xunit.utils.isSetUpString(functionNames);
+  setupFunctions = hFunctions(isSetupFunction);
+  if numel(setupFunctions) > 1
+    error('findSubfunctionTests:tooManySetupFcns', ...
+      'Found more than one setup subfunction.')
+  elseif isempty(setupFunctions)
+    hSetupFunction = [];
+  else
+    hSetupFunction = setupFunctions{1};
+  end
+end
+
+function name = functionHandleToName(hFunc)
+  name = getfield(functions(hFunc), 'function');
+end
diff --git a/src/+xunit/+private/retrieveTearDownFunction.m b/src/+xunit/+private/retrieveTearDownFunction.m
new file mode 100644
index 0000000..94e36b5
--- /dev/null
+++ b/src/+xunit/+private/retrieveTearDownFunction.m
@@ -0,0 +1,36 @@
+%XUNIT.PRIVATE.RETRIEVETEARDOWNFUNCTION retrieves the tear down function from a list of function handles
+%
+% Description:
+%   buildFunctionHandleTestSuite needs to retrieve the tear down function from
+%   the list of local function handles. retrieveSetupFunction does this
+%   retrieval. If multiple functions qualify as a setup function, an exception
+%   is thrown.
+%
+% Syntax:
+%   hTearDownFunction = xunit.private.retrieveTearDownFunction(hFunctions)
+%
+% Input:
+%   hFunctions - cell array of function handles, possibly containing a function
+%                handle to a valid tear down function
+%
+% Output:
+%   hTearDownFunction - function handle to the tear down function in the
+%                       provided array of function handles.
+
+function hTearDownFunction = retrieveTearDownFunction(hFunctions)
+  functionNames = cellfun(@functionHandleToName, hFunctions, 'UniformOutput', false);
+  isTearDownFunction = xunit.utils.isTearDownString(functionNames);
+  tearDownFunctions = hFunctions(isTearDownFunction);
+  if numel(tearDownFunctions) > 1
+    error('findSubfunctionTests:tooManyTearDownFcns', ...
+      'Found more than one teardown subfunction.')
+  elseif isempty(tearDownFunctions)
+    hTearDownFunction = [];
+  else
+    hTearDownFunction = tearDownFunctions{1};
+  end
+end
+
+function name = functionHandleToName(hFunc)
+  name = getfield(functions(hFunc), 'function');
+end
diff --git a/src/+xunit/+private/retrieveTestFunctions.m b/src/+xunit/+private/retrieveTestFunctions.m
new file mode 100644
index 0000000..614d041
--- /dev/null
+++ b/src/+xunit/+private/retrieveTestFunctions.m
@@ -0,0 +1,30 @@
+%XUNIT.PRIVATE.RETRIEVETESTFUNCTIONS picks the handles to test functions from a list of function handles
+%
+% Description:
+%   To build a test suite from an m file which has the test cases written as
+%   local functions, `buildFunctionHandleTestSuite` needs to extract the actual
+%   test functions from the list of function handles returned by, for instance,
+%   `localfunctions`.
+%
+% Syntax:
+%   hTestFunctions = xunit.private.retrieveTestFunctions(hFunctions)
+%
+% Input:
+%   hFunctions - a cell array of function handles
+%
+% Output:
+%   hTestFunctions - a cell array of all the function handles of the hFunctions
+%                    that qualify as test functions.
+%
+% Example:
+%   >> hTestFunctions = xunit.private.retrieveTestFunctions(localfunctions)
+
+function hTestFunctions = retrieveTestFunctions(hFunctions)
+  isTestFunctions = cellfun(@isaTestFunction, hFunctions);
+  hTestFunctions = hFunctions(isTestFunctions);
+end
+
+function bool = isaTestFunction(hFunction)
+  funcDetails = functions(hFunction);
+  bool = xunit.utils.isTestString(funcDetails.function);
+end
diff --git a/src/TestSuite.m b/src/TestSuite.m
index bc5262d..9d41f6d 100644
--- a/src/TestSuite.m
+++ b/src/TestSuite.m
@@ -243,24 +243,45 @@ function keepMatchingTestCase(self, name)
             else
                 
                 try
+                    % The following attempts to create a test suite given
+                    % `name` can generate exceptions in the case of it not
+                    % being a valid m-file, it being a script or being a valid
+                    % function requiring input arguments. All these cases are
+                    % converted into a single empty test suite which will be
+                    % ignored when the suite is run
                     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.');
+                        try
+                            suite = feval(name);
+                            if ~isa(suite, 'TestSuite')
+                                error('xunit:TestSuite:noTestSuiteReturned', ...
+                                  'Function did not return a TestSuite object.');
+                            end
+                        catch
+                            error('xunit:TestSuite:noTestSuiteReturned', ...
+                              'Function did not return a TestSuite object.');
                         end
                     end
                     
-                catch
+                catch exception
+                  notTestSuiteErrors = {
+                    'xunit:TestSuite:noTestSuiteReturned';
+                    'MATLAB:narginout:notValidMfile';
+                    'MATLAB:nargin:isScript'
+                  };
+
+                  if any(strcmp(notTestSuiteErrors, exception.identifier))
                     % Ordinary function does not appear to contain tests.
                     % Return an empty test suite.
                     suite = TestSuite();
                     suite.Name = name;
+                  else
+                    rethrow(exception);
+                  end
                 end
             end
             
diff --git a/src/buildFunctionHandleTestSuite.m b/src/buildFunctionHandleTestSuite.m
new file mode 100644
index 0000000..6774a03
--- /dev/null
+++ b/src/buildFunctionHandleTestSuite.m
@@ -0,0 +1,86 @@
+%BUILDFUNCTIONHANDLETESTSUITE builds a test suite from a set of function handles
+%
+% Description:
+%   Builds a test suite from a set of function handles. These should be named
+%   according to xUnit's naming convention and can include setUp and tearDown
+%   functions which will be handled accordingly.
+%
+%   If no output arguments are requested, or if the optional input flag is
+%   specified, the generated test suite is run before returning.
+%
+% Syntax:
+%   testSuite = buildFunctionHandleTestSuite(functionHandles, { runImmediately })
+%
+% Input:
+%   functionHandles - a cell array of function handles, typically acquired by
+%                     calling `localfunctions` in a file with sub functions
+%                     defining a test suite
+%
+% Optional Input:
+%   runImmediately - a boolean indicating whether to run the test suite as soon
+%                    as it has been build
+%
+% Optional Output:
+%   testSuite - the generated test suite if requested. When not requested the
+%               generated test suite is executed immediately
+%
+% Examples:
+%   % Build and execute the test suite defined by a set of function handles
+%   >> buildFunctionHandleTestSuite(localfunctions)
+%
+%   % Build a test suite defined by a set of function handles
+%   >> testSuite = buildFunctionHandleTestSuite(localfunctions)
+%
+%   % Run the generated test suite immediately when run in 'interactive' mode,
+%   % i.e. when F5 is pressed to execute the file defining the tests as
+%   % subfunctions. Note that nargout can only be used inside functions! This
+%   % will not work from the command line, although passing any other variable
+%   % evaluating to a boolean would
+%   >> testSuite = buildFunctionHandleTestSuite(localfunctions, ~nargout)
+%
+%   % On versions prior to R2014b, `localfunctions` does not properly detect or
+%   % does not even exist _at all_. In these situations a cell array of
+%   % function handles can be obtained using
+%   >> localFunctionHandles = cellfun(@str2func, ...
+%        which('-subfun', mfilename('fullpath')), 'UniformOutput', false));
+%   >> testSuite = buildFunctionHandleTestSuite(localFunctionHandles);
+
+function testSuite = buildFunctionHandleTestSuite(hLocalFunctions, runImmediately)
+  hSetupFunction = xunit.private.retrieveSetupFunction(hLocalFunctions);
+  hTearDownFunction = xunit.private.retrieveTearDownFunction(hLocalFunctions);
+  hTestFunctions = xunit.private.retrieveTestFunctions(hLocalFunctions);
+
+  [callerName, callerFile] = getCallerData();
+  testSuite = TestSuite();
+  testSuite.Name = callerName;
+  testSuite.Location = which(callerFile);
+  for k = 1:numel(hTestFunctions)
+    testSuite.add(FunctionHandleTestCase(hTestFunctions{k}, ...
+      hSetupFunction, hTearDownFunction));
+  end
+
+  if nargout == 0 || (nargin > 1 && runImmediately)
+    testSuite.run();
+  end
+end
+
+function [callerName, callerFile] = getCallerData()
+  [ST,I] = dbstack('-completenames');
+
+  if numel(ST) <= 2
+    throwAsCaller(MException('xunit:buildFunctionHandleTestSuite:invalidCallingSyntax', ...
+      'This function must be called from within another function'));
+  end
+
+  % TODO: The first branch here exists to accomodate backwards compatibility
+  % with the initTestSuite script approach which worked prior to MATLAB R2016b.
+  % It can be removed once support for this syntax is dropped, otherwise the
+  % names to the local functions cannot be found as the wrong file is checked
+  if strcmp(ST(I + 2).name, 'initTestSuite')
+    callerFile = ST(I + 3).file;
+    callerName = ST(I + 3).name;
+  else
+    callerFile = ST(I + 2).file;
+    callerName = ST(I + 2).name;
+  end
+end
diff --git a/src/initTestSuite.m b/src/initTestSuite.m
index 6b74d1c..8a11511 100644
--- a/src/initTestSuite.m
+++ b/src/initTestSuite.m
@@ -1,58 +1,21 @@
-%findSubfunctionTests Utility script used for subfunction-based tests
-%   This file is a script that is called at the top of M-files containing
-%   subfunction-based tests.
-%
-%   The top of a typical M-file using this script looks like this:
-%
-%       function test_suite = testFeatureA
-%
-%       findSubfunctionTests;
-%
-%   IMPORTANT NOTE
-%   --------------
-%   The output variable name for an M-file using this script must be test_suite.
-
-%   Steven L. Eddins
-%   Copyright 2008-2009 The MathWorks, Inc.
-
-[ST,I] = dbstack('-completenames');
-caller_name = ST(I + 1).name;
-caller_file = ST(I + 1).file;
-subFcns = which('-subfun', caller_file);
-
-setup_fcn_name = subFcns(xunit.utils.isSetUpString(subFcns));
-if numel(setup_fcn_name) > 1
-    error('findSubfunctionTests:tooManySetupFcns', ...
-        'Found more than one setup subfunction.')
-elseif isempty(setup_fcn_name)
-    setup_fcn = [];
-else
-    setup_fcn = str2func(setup_fcn_name{1});
-end
-
-teardown_fcn_name = subFcns(xunit.utils.isTearDownString(subFcns));
-if numel(teardown_fcn_name) > 1
-    error('findSubfunctionTests:tooManyTeardownFcns', ...
-        'Found more than one teardown subfunction.')
-elseif isempty(teardown_fcn_name)
-    teardown_fcn = [];
-else
-    teardown_fcn = str2func(teardown_fcn_name{1});
-end
-
-test_fcns = cellfun(@str2func, subFcns(xunit.utils.isTestString(subFcns)), ...
-    'UniformOutput', false);
-
-suite = TestSuite;
-suite.Name = caller_name;
-suite.Location = which(caller_file);
-for k = 1:numel(test_fcns)
-    suite.add(FunctionHandleTestCase(test_fcns{k}, setup_fcn, teardown_fcn));
-end
-
-if nargout > 0
-    test_suite = suite;
-else
-    suite.run();
-end
-
+feedback = {
+  'xunit:initTestSuite:deprecatedSyntax', ...
+  ['Starting with MATLAB R2016b, `initTestSuite` can no longer be ' ...
+   'called without input arguments due to the way scripts are scoped from ' ...
+   'that version onwards. The function `buildFunctionHandleTestSuite` can ' ...
+   'be used as an alternative. See its help for usage instructions.\n\nThe ' ...
+   '`initTestSuite` script will be removed in the next major release.']
+};
+
+matlabVersionInfo = ver('matlab');
+if str2double(matlabVersionInfo.Version) >= 9.1 % R2016b and later
+  error(feedback{:});
+else
+  warning(feedback{:});
+
+  [ST,I] = dbstack('-completenames');
+  callerFile = ST(I + 1).file;
+  subFunctionNames = which('-subfun', callerFile);
+  localFunctionHandles = cellfun(@str2func, subFunctionNames, 'UniformOutput', false);
+  test_suite = buildFunctionHandleTestSuite(localFunctionHandles, ~nargout);
+end
diff --git a/src/runxunit.m b/src/runxunit.m
index b0212c0..f8846ae 100644
--- a/src/runxunit.m
+++ b/src/runxunit.m
@@ -10,7 +10,7 @@
 %
 %       * An M-file function whose name starts or ends with "test" or
 %         "Test" and that contains subfunction tests and uses the
-%         initTestSuite script to return a TestSuite object.
+%         buildFunctionHandleTestSuite funtion to return a TestSuite object.
 %
 %       * An M-file defining a subclass of TestCase.
 %
diff --git a/tests/+xunit/+mocktests/+subpkg/test_a_bit.m b/tests/+xunit/+mocktests/+subpkg/test_a_bit.m
index d48071d..76c1671 100644
--- a/tests/+xunit/+mocktests/+subpkg/test_a_bit.m
+++ b/tests/+xunit/+mocktests/+subpkg/test_a_bit.m
@@ -1,5 +1,7 @@
-function test_suite = test_a_bit
-initTestSuite
+function testSuite = test_a_bit
+testSuite = buildFunctionHandleTestSuite( ...
+  cellfun(@str2func, which('-subfun', mfilename('fullpath')), ...
+  'UniformOutput', false));
 
 function test_now
 
diff --git a/tests/+xunit/+mocktests/test_that.m b/tests/+xunit/+mocktests/test_that.m
index 9f6cb91..ce62111 100644
--- a/tests/+xunit/+mocktests/test_that.m
+++ b/tests/+xunit/+mocktests/test_that.m
@@ -1,6 +1,8 @@
 % test_that.m is a subfunction test file.
-function test_suite = test_that
-initTestSuite
+function testSuite = test_that
+testSuite = buildFunctionHandleTestSuite( ...
+  cellfun(@str2func, which('-subfun', mfilename('fullpath')), ...
+  'UniformOutput', false));
 
 function test_the_other
 a = magic(3);
diff --git a/tests/TestInitTestSuite.m b/tests/TestInitTestSuite.m
new file mode 100644
index 0000000..0fec67d
--- /dev/null
+++ b/tests/TestInitTestSuite.m
@@ -0,0 +1,67 @@
+classdef TestInitTestSuite < TestCase
+  methods (Static, Access = private)
+    function version = getDecimalMatlabVersion()
+      matlabVersionInfo = ver('matlab');
+      version = str2double(matlabVersionInfo.Version);
+    end
+
+    function cleanupHandle = moveToTestDir()
+      startDir = cd(fullfile(fileparts(mfilename('fullpath')), 'initTestSuiteTest'));
+      cleanupHandle = onCleanup(@() cd(startDir));
+    end
+  end
+
+  methods (Access = private)
+    function assertSuccessfulTestSuiteExecution(obj, command)
+      cleanupHandle = obj.moveToTestDir();
+
+      testRunOutput = evalc(command);
+
+      expectedString = 'Warning: Starting with MATLAB R2016b';
+      % Cannot use exact matching here as `warning` generates 'invisible' color
+      % codes at the start of the output
+      containsWarning = ~isempty(strfind(testRunOutput, expectedString));
+      assertTrue(containsWarning, ...
+        'Expected test run output to contain a usage warning about initTestSuite');
+
+      % The test suites have 1 passing and 1 failing test, the output should
+      % reflect this, including the correct failure message
+      matcher = 'with 2 test cases.\n\.F\nFAILED.+Asserted condition is not true';
+      containsTestSuiteResults = ~isempty(regexp(testRunOutput, ...
+        matcher, 'once'));
+      assertTrue(containsTestSuiteResults, sprintf( ...
+        ['Expected test run output to contain the proper suite results. ' ...
+         'Expected \n%s\n to match regular expression \n%s\n'], ...
+        testRunOutput, matcher));
+    end
+  end
+
+  methods
+    function obj = TestInitTestSuite(name)
+      obj = obj@TestCase(name);
+    end
+
+    function testErrorsOn2016bAndLater(obj)
+      if obj.getDecimalMatlabVersion() >= 9.1
+        cleanupHandle = obj.moveToTestDir();
+
+        assertExceptionThrown(@() initTestSuiteTest, ...
+          'xunit:initTestSuite:deprecatedSyntax');
+      end
+    end
+
+    function testPackagedTestSuiteBefore2016b(obj)
+      matlabVersion = obj.getDecimalMatlabVersion();
+      if matlabVersion < 9.1
+        obj.assertSuccessfulTestSuiteExecution('packaged.initTestSuiteTest();')
+      end
+    end
+
+    function testUnpackagedTestSuiteBefore2016b(obj)
+      matlabVersion = obj.getDecimalMatlabVersion();
+      if matlabVersion < 9.1
+        obj.assertSuccessfulTestSuiteExecution('packaged.initTestSuiteTest();')
+      end
+    end
+  end
+end
diff --git a/tests/cwd_test/testSubfunctions.m b/tests/cwd_test/testSubfunctions.m
index 97c665e..f2ad703 100644
--- a/tests/cwd_test/testSubfunctions.m
+++ b/tests/cwd_test/testSubfunctions.m
@@ -1,10 +1,10 @@
-function test_suite = testSubfunctions
+function testSuite = testSubfunctions
 %testSubfunctions Contains two passing subfunction tests
 
 %   Steven L. Eddins
 %   Copyright 2008 The MathWorks, Inc.
 
-initTestSuite;
+testSuite = buildFunctionHandleTestSuite(localfunctions);
 
 function testSub1
 
diff --git a/tests/dir1/test_thatPasses.m b/tests/dir1/test_thatPasses.m
index f9d67d8..ec8c097 100644
--- a/tests/dir1/test_thatPasses.m
+++ b/tests/dir1/test_thatPasses.m
@@ -1,5 +1,5 @@
-function test_suite = test_thatPasses
-initTestSuite;
+function testSuite = test_thatPasses
+testSuite = buildFunctionHandleTestSuite(localfunctions);
 
 function test_case
 assertTrue(true);
diff --git a/tests/dir2/test_thatFails.m b/tests/dir2/test_thatFails.m
index 5b76c70..95fcb20 100644
--- a/tests/dir2/test_thatFails.m
+++ b/tests/dir2/test_thatFails.m
@@ -1,5 +1,5 @@
-function test_suite = test_thatFails
-initTestSuite;
+function testSuite = test_thatFails
+testSuite = buildFunctionHandleTestSuite(localfunctions);
 
 function test_case
 assertTrue(false);
diff --git a/tests/helper_classes/notTestString.m b/tests/helper_classes/notTestString.m
index 697a72a..a549eb7 100644
--- a/tests/helper_classes/notTestString.m
+++ b/tests/helper_classes/notTestString.m
@@ -1,7 +1,7 @@
-function suite = notTestString
+function testSuite = notTestString
 % This function exists to help test that the TestSuite.fromPwd() method does not
 % pick up function-handle test files that do not match the naming convention.
-initTestSuite;
+testSuite = buildFunctionHandleTestSuite(localfunctions);
 
 function testA
 
diff --git a/tests/helper_classes/testFunctionHandlesA.m b/tests/helper_classes/testFunctionHandlesA.m
index e88606d..a7aaf43 100644
--- a/tests/helper_classes/testFunctionHandlesA.m
+++ b/tests/helper_classes/testFunctionHandlesA.m
@@ -1,11 +1,11 @@
-function test_suite = testFunctionHandlesA
+function testSuite = testFunctionHandlesA
 %testFunctionHandlesA Test file used by TestFunctionHandlesTest
 %   Contains two passing tests.
 
 %   Steven L. Eddins
 %   Copyright 2008 The MathWorks, Inc.
 
-initTestSuite;
+testSuite = buildFunctionHandleTestSuite(localfunctions);
 
 function testA
 
diff --git a/tests/helper_classes/testFunctionHandlesB.m b/tests/helper_classes/testFunctionHandlesB.m
index 6d0fcea..ce9b456 100644
--- a/tests/helper_classes/testFunctionHandlesB.m
+++ b/tests/helper_classes/testFunctionHandlesB.m
@@ -1,11 +1,11 @@
-function test_suite = testFunctionHandlesB
+function testSuite = testFunctionHandlesB
 %testFunctionHandlesB Test file used by TestFunctionHandlesTest
 %   Contains two passing tests that use a test fixture.
 
 %   Steven L. Eddins
 %   Copyright 2008 The MathWorks, Inc.
 
-initTestSuite;
+testSuite = buildFunctionHandleTestSuite(localfunctions);
 
 function testData = setUpFcn
 testData = 5;
diff --git a/tests/helper_classes/testFunctionHandlesC.m b/tests/helper_classes/testFunctionHandlesC.m
index 4c95a5d..c99338d 100644
--- a/tests/helper_classes/testFunctionHandlesC.m
+++ b/tests/helper_classes/testFunctionHandlesC.m
@@ -1,4 +1,4 @@
-function test_suite = testFunctionHandlesC
+function testSuite = testFunctionHandlesC
 %testFunctionHandlesC Test file used by TestFunctionHandlesTest
 %   Contains two passing tests that use a test fixture containing an intentional
 %   error.
@@ -6,7 +6,7 @@
 %   Steven L. Eddins
 %   Copyright 2008 The MathWorks, Inc.
 
-initTestSuite;
+testSuite = buildFunctionHandleTestSuite(localfunctions);
 
 function testData = setUpFcn
 testData = 5;
diff --git a/tests/helper_classes/testFunctionHandlesD.m b/tests/helper_classes/testFunctionHandlesD.m
index f0dbda6..0d593f7 100644
--- a/tests/helper_classes/testFunctionHandlesD.m
+++ b/tests/helper_classes/testFunctionHandlesD.m
@@ -1,11 +1,11 @@
-function test_suite = testFunctionHandlesD
+function testSuite = testFunctionHandlesD
 %testFunctionHandlesD Test file used by TestFunctionHandlesTest
 %   Contains two passing tests that use a test fixture with no test data.
 
 %   Steven L. Eddins
 %   Copyright 2008 The MathWorks, Inc.
 
-initTestSuite;
+testSuite = buildFunctionHandleTestSuite(localfunctions);
 
 function setUpFcn
 
diff --git a/tests/helper_classes/testFunctionHandlesE.m b/tests/helper_classes/testFunctionHandlesE.m
index cbf48a2..f2fde0f 100644
--- a/tests/helper_classes/testFunctionHandlesE.m
+++ b/tests/helper_classes/testFunctionHandlesE.m
@@ -1,11 +1,11 @@
-function test_suite = testFunctionHandlesA
+function testSuite = testFunctionHandlesE
 %testFunctionHandlesE Test file used by TestFunctionHandlesTest
 %   Contains one failing test.
 
 %   Steven L. Eddins
 %   Copyright 2008 The MathWorks, Inc.
 
-initTestSuite;
+testSuite = buildFunctionHandleTestSuite(localfunctions);
 
 function testA
 error('testFunctionHandlesA:expectedFailure', 'Bogus message');
diff --git a/tests/helper_classes/testFunctionHandlesTeardownNoSetup.m b/tests/helper_classes/testFunctionHandlesTeardownNoSetup.m
index ad2c061..bb7e05e 100644
--- a/tests/helper_classes/testFunctionHandlesTeardownNoSetup.m
+++ b/tests/helper_classes/testFunctionHandlesTeardownNoSetup.m
@@ -1,7 +1,7 @@
-function suite = testFunctionHandlesTeardownNoSetup
+function testSuite = testFunctionHandlesTeardownNoSetup
 % Verify that test file works if it has a teardown function but no setup
 % function.
-initTestSuite;
+testSuite = buildFunctionHandleTestSuite(localfunctions);
 
 function teardown
 close all
diff --git a/tests/initTestSuiteTest/+packaged/initTestSuiteTest.m b/tests/initTestSuiteTest/+packaged/initTestSuiteTest.m
new file mode 100644
index 0000000..7f624fa
--- /dev/null
+++ b/tests/initTestSuiteTest/+packaged/initTestSuiteTest.m
@@ -0,0 +1,11 @@
+function test_suite = initTestSuiteTest()
+  initTestSuite;
+end
+
+function a_test()
+  assertTrue(true);
+end
+
+function another_test()
+  assertTrue(false);
+end
diff --git a/tests/initTestSuiteTest/initTestSuiteTest.m b/tests/initTestSuiteTest/initTestSuiteTest.m
new file mode 100644
index 0000000..7f624fa
--- /dev/null
+++ b/tests/initTestSuiteTest/initTestSuiteTest.m
@@ -0,0 +1,11 @@
+function test_suite = initTestSuiteTest()
+  initTestSuite;
+end
+
+function a_test()
+  assertTrue(true);
+end
+
+function another_test()
+  assertTrue(false);
+end
diff --git a/tests/src/+xunit/+private/private/setUp2.m b/tests/src/+xunit/+private/private/setUp2.m
new file mode 100644
index 0000000..aa61866
--- /dev/null
+++ b/tests/src/+xunit/+private/private/setUp2.m
@@ -0,0 +1,4 @@
+%SETUP2 is a dummy function to be used in the retrieveSetupFunction test suite
+
+function setUp2
+end
diff --git a/tests/src/+xunit/+private/private/setup.m b/tests/src/+xunit/+private/private/setup.m
new file mode 100644
index 0000000..416f444
--- /dev/null
+++ b/tests/src/+xunit/+private/private/setup.m
@@ -0,0 +1,4 @@
+%SETUP is a dummy function to be used in the retrieveSetupFunction test suite
+
+function setup
+end
diff --git a/tests/src/+xunit/+private/private/tearDown.m b/tests/src/+xunit/+private/private/tearDown.m
new file mode 100644
index 0000000..416f444
--- /dev/null
+++ b/tests/src/+xunit/+private/private/tearDown.m
@@ -0,0 +1,4 @@
+%SETUP is a dummy function to be used in the retrieveSetupFunction test suite
+
+function setup
+end
diff --git a/tests/src/+xunit/+private/private/tearDown2.m b/tests/src/+xunit/+private/private/tearDown2.m
new file mode 100644
index 0000000..aa61866
--- /dev/null
+++ b/tests/src/+xunit/+private/private/tearDown2.m
@@ -0,0 +1,4 @@
+%SETUP2 is a dummy function to be used in the retrieveSetupFunction test suite
+
+function setUp2
+end
diff --git a/tests/src/+xunit/+private/testRetrieveSetupFunction.m b/tests/src/+xunit/+private/testRetrieveSetupFunction.m
new file mode 100644
index 0000000..0fc0930
--- /dev/null
+++ b/tests/src/+xunit/+private/testRetrieveSetupFunction.m
@@ -0,0 +1,54 @@
+%XUNIT.PRIVATE.TESTRETRIEVESETUPFUNCTION contains a test suite for xunit.private.retrieveSetupFunction
+
+function testSuite = testRetrieveSetupFunction()
+  testSuite = buildFunctionHandleTestSuite(localfunctions);
+end
+
+function testNoSetupFunction()
+  functionList = getDefaultFunctionList();
+  
+  actual = xunit.private.retrieveSetupFunction(functionList);
+  
+  assertEqual([], actual, ...
+    'A list of function names without a setup function should return empty.');
+ end
+
+function testOneSetupFunction()
+  functionList = [getDefaultFunctionList(); {@setup}];
+  
+  actual = xunit.private.retrieveSetupFunction(functionList);
+  
+  assertEqual(@setup, actual, ...
+    'A list of function names with one setup function handle should that handle.');
+end
+
+function testMultipleSetupFunction()
+  functionList = [getDefaultFunctionList(); {@setup; @setUp2}];
+  
+  hExceptFun = @() xunit.private.retrieveSetupFunction(functionList);
+  
+  assertExceptionThrown(hExceptFun, 'findSubfunctionTests:tooManySetupFcns', ...
+    'A list of function names without a setup function should return empty.');
+ end
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Helper functions
+
+function basicList = getDefaultFunctionList()
+  basicList = {
+    @foo
+    @bar
+    @camelCase
+  };
+end
+
+% Bogus functions
+
+function foo
+end
+
+function bar
+end
+
+function camelCase
+end
diff --git a/tests/src/+xunit/+private/testRetrieveTearDownFunction.m b/tests/src/+xunit/+private/testRetrieveTearDownFunction.m
new file mode 100644
index 0000000..d8d99ba
--- /dev/null
+++ b/tests/src/+xunit/+private/testRetrieveTearDownFunction.m
@@ -0,0 +1,54 @@
+%XUNIT.PRIVATE.TESTRETRIEVESETUPFUNCTION contains a test suite for xunit.private.retrieveTearDownFunction
+
+function testSuite = testRetrieveTearDownFunction()
+  testSuite = buildFunctionHandleTestSuite(localfunctions);
+end
+
+function testNoTearDownFunction()
+  functionList = getDefaultFunctionList();
+  
+  actual = xunit.private.retrieveTearDownFunction(functionList);
+  
+  assertEqual([], actual, ...
+    'A list of function names without a tear down function should return empty.');
+ end
+
+function testOneTearDownFunction()
+  functionList = [getDefaultFunctionList(); {@tearDown}];
+  
+  actual = xunit.private.retrieveTearDownFunction(functionList);
+  
+  assertEqual(@tearDown, actual, ...
+    'A list of function names with one setup function handle should that handle.');
+end
+
+function testMultipleTearDownFunction()
+  functionList = [getDefaultFunctionList(); {@tearDown; @tearDown2}];
+  
+  hExceptFun = @() xunit.private.retrieveTearDownFunction(functionList);
+  
+  assertExceptionThrown(hExceptFun, 'findSubfunctionTests:tooManyTearDownFcns', ...
+    'A list of function names with multiple tear down functions should throw an exception.');
+ end
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Helper functions
+
+function basicList = getDefaultFunctionList()
+  basicList = {
+    @foo
+    @bar
+    @camelCase
+  };
+end
+
+% Bogus functions
+
+function foo
+end
+
+function bar
+end
+
+function camelCase
+end
diff --git a/tests/src/+xunit/+private/testRetrieveTestFunctions.m b/tests/src/+xunit/+private/testRetrieveTestFunctions.m
new file mode 100644
index 0000000..3b5fdab
--- /dev/null
+++ b/tests/src/+xunit/+private/testRetrieveTestFunctions.m
@@ -0,0 +1,47 @@
+%TESTRETRIEVETESTFUNCTIONS contains a test suite for xunit.private.testRetrieveTestFunctions
+
+function testSuite = testRetrieveTestFunctions()
+  testSuite = buildFunctionHandleTestSuite(localfunctions);
+end
+
+function testNoTestFiles()
+  testList = getDefaultFunctionList();
+  
+  actual = xunit.private.retrieveTestFunctions(testList);
+  
+  expected = cell(0,1);
+  assertEqual(expected, actual, ...
+    'A list without any test functions should return an empty list.');
+end
+
+function testMultipleTestFiles()
+  expected = {@testNoTestFiles; @testMultipleTestFiles};
+  testList = [getDefaultFunctionList; expected];
+
+  actual = xunit.private.retrieveTestFunctions(testList);
+  
+  assertEqual(expected, actual, ...
+    'A list without any test functions should return an empty list.');
+end
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Helper functions
+
+function basicList = getDefaultFunctionList()
+  basicList = {
+    @foo
+    @bar
+    @camelCase
+  };
+end
+
+% Bogus functions
+
+function foo
+end
+
+function bar
+end
+
+function camelCase
+end
diff --git a/tests/src/private/emptyTest.m b/tests/src/private/emptyTest.m
new file mode 100644
index 0000000..c0907a7
--- /dev/null
+++ b/tests/src/private/emptyTest.m
@@ -0,0 +1,4 @@
+%EMPTYTEST is an empty test for usage in the buildFunctionHandleTestSuite test suite
+
+function emptyTest()
+end
diff --git a/tests/src/private/persistValue.m b/tests/src/private/persistValue.m
new file mode 100644
index 0000000..5d60509
--- /dev/null
+++ b/tests/src/private/persistValue.m
@@ -0,0 +1,12 @@
+function value = persistValue(newValue)
+  persistent perVal
+  
+  if ~exist('perVal', 'var')
+    perVal = [];
+  end
+  value = perVal;
+  
+  if exist('newValue', 'var')
+    perVal = newValue;
+  end
+end
diff --git a/tests/src/private/runTest.m b/tests/src/private/runTest.m
new file mode 100644
index 0000000..fcea126
--- /dev/null
+++ b/tests/src/private/runTest.m
@@ -0,0 +1,8 @@
+function runTest()
+  curPath = pwd;
+  cleanUpObj = onCleanup(@() cd(curPath));
+  myPath = fileparts(mfilename('fullpath'));
+  cd(myPath);
+  
+  persistValue('testSuiteHasRun');
+end
diff --git a/tests/src/private/setup.m b/tests/src/private/setup.m
new file mode 100644
index 0000000..20b363f
--- /dev/null
+++ b/tests/src/private/setup.m
@@ -0,0 +1,4 @@
+function common = setup()
+  common = struct('setupHasRun', {true});
+  error('xunit:setup:called', 'This error should not be seen');
+end
diff --git a/tests/src/private/teardown.m b/tests/src/private/teardown.m
new file mode 100644
index 0000000..71826b4
--- /dev/null
+++ b/tests/src/private/teardown.m
@@ -0,0 +1,4 @@
+function common = teardown()
+  common = struct('tearDownHasRun', {true});
+  error('xunit:teardown:called', 'This error should not be seen');
+end
diff --git a/tests/src/private/testAtTheStart.m b/tests/src/private/testAtTheStart.m
new file mode 100644
index 0000000..4f2fe89
--- /dev/null
+++ b/tests/src/private/testAtTheStart.m
@@ -0,0 +1,4 @@
+%TESTATTHESTART is an empty test for usage in the buildFunctionHandleTestSuite test suite
+
+function testAtTheStart()
+end
diff --git a/tests/src/testBuildFunctionHandleTestSuite.m b/tests/src/testBuildFunctionHandleTestSuite.m
new file mode 100644
index 0000000..9535f0a
--- /dev/null
+++ b/tests/src/testBuildFunctionHandleTestSuite.m
@@ -0,0 +1,103 @@
+%TESTBUILDFUNCTIONHANDLETESTSUITE contains a test suite for buildFunctionHandleTestSuite
+
+function testSuite = testBuildFunctionHandleTestSuite()
+  testSuite = buildFunctionHandleTestSuite(localfunctions);
+end
+
+function testBuildSuiteNoTests()
+  defaultList = getDefaultFunctionList();
+
+  testSuite = buildFunctionHandleTestSuite(defaultList);
+
+  assertEqual(0, numel(testSuite.TestComponents), ...
+    'An m-file with no test functions should result in a TestSuite with no tests.');
+end
+
+function testBuildSuiteWithTests()
+  defaultList = getDefaultFunctionList();
+  testList = {@emptyTest; @testAtTheStart};
+  funcList = [defaultList; testList];
+
+  testSuite = buildFunctionHandleTestSuite(funcList);
+
+  assertEqual(2, numel(testSuite.TestComponents), ...
+    'An m-file with 2 test functions should result in a TestSuite with 2 tests.');
+end
+
+function testBuildSuiteWithSetup()
+  defaultList = getDefaultFunctionList();
+  testList = {@emptyTest};
+  setupList = {@setup};
+  funcList = [defaultList; setupList; testList];
+
+  testSuite = buildFunctionHandleTestSuite(funcList);
+  hExceptionCall = @() testSuite.TestComponents{1}.setUp;
+
+  assertExceptionThrown(hExceptionCall, 'xunit:setup:called', ...
+    'The setup function is not called');
+end
+
+function testBuildSuiteWithTearDown()
+  defaultList = getDefaultFunctionList();
+  testList = {@emptyTest};
+  tearDownList = {@teardown};
+  funcList = [defaultList; tearDownList; testList];
+
+  testSuite = buildFunctionHandleTestSuite(funcList);
+  hExceptionCall = @() testSuite.TestComponents{1}.tearDown;
+
+  assertExceptionThrown(hExceptionCall, 'xunit:teardown:called', ...
+    'The tear down function is not called');
+end
+
+function testRunTestsOnRequest()
+  defaultList = getDefaultFunctionList();
+  testList = {@runTest};
+  funcList = [defaultList; testList]; %#ok
+
+  persistValue('testSuiteDidNotRun');
+  % Suppress output from running the test suite
+  evalc('testSuite = buildFunctionHandleTestSuite(funcList, true)');
+  actual = persistValue();
+
+  expected = 'testSuiteHasRun';
+  assertEqual(expected, actual, ...
+    'If no output arguments are requested, the test suite should be run.');
+end
+
+function testRunTestsIfNoOutputRequested()
+  defaultList = getDefaultFunctionList();
+  testList = {@runTest};
+  funcList = [defaultList; testList]; %#ok
+
+  persistValue('testSuiteDidNotRun');
+  % Suppress output from running the test suite
+  evalc('buildFunctionHandleTestSuite(funcList)');
+  actual = persistValue();
+
+  expected = 'testSuiteHasRun';
+  assertEqual(expected, actual, ...
+    'If no output arguments are requested, the test suite should be run.');
+end
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Helper functions
+
+function basicList = getDefaultFunctionList()
+  basicList = {
+    @foo
+    @bar
+    @camelCase
+  };
+end
+
+% Bogus functions
+
+function foo
+end
+
+function bar
+end
+
+function camelCase
+end
diff --git a/tests/testAssertEqual.m b/tests/testAssertEqual.m
index 4ddd602..ba2387a 100644
--- a/tests/testAssertEqual.m
+++ b/tests/testAssertEqual.m
@@ -1,10 +1,10 @@
-function test_suite = testAssertEqual
+function testSuite = testAssertEqual
 %testAssertEqual Unit tests for assertEqual
 
 %   Steven L. Eddins
 %   Copyright 2008 The MathWorks, Inc.
 
-initTestSuite;
+testSuite = buildFunctionHandleTestSuite(localfunctions);
 
 function testAssertEqualHappyCase
 assertEqual(5, 5);
diff --git a/tests/testAssertExceptionThrown.m b/tests/testAssertExceptionThrown.m
index fed675d..5a063f0 100644
--- a/tests/testAssertExceptionThrown.m
+++ b/tests/testAssertExceptionThrown.m
@@ -1,10 +1,10 @@
-function test_suite = testAssertExceptionThrown
+function testSuite = testAssertExceptionThrown
 %testAssertExceptionThrown Unit tests for assertExceptionThrown
 
 %   Steven L. Eddins
 %   Copyright 2008 The MathWorks, Inc.
 
-initTestSuite;
+testSuite = buildFunctionHandleTestSuite(localfunctions);
 
 function test_happyCase
 assertExceptionThrown(...
diff --git a/tests/testAssertFalse.m b/tests/testAssertFalse.m
index 35bcfd5..567483d 100644
--- a/tests/testAssertFalse.m
+++ b/tests/testAssertFalse.m
@@ -1,10 +1,10 @@
-function test_suite = testAssertFalse
+function testSuite = testAssertFalse
 %testAssertFalse Unit tests for assertFalse
 
 %   Steven L. Eddins
 %   Copyright 2008 The MathWorks, Inc.
 
-initTestSuite;
+testSuite = buildFunctionHandleTestSuite(localfunctions);
 
 function testAssertFalseHappyCase
 assertFalse(false);
diff --git a/tests/testAssertTrue.m b/tests/testAssertTrue.m
index 55701e4..06e9b7c 100644
--- a/tests/testAssertTrue.m
+++ b/tests/testAssertTrue.m
@@ -1,10 +1,10 @@
-function test_suite = testAssertTrue
+function testSuite = testAssertTrue
 %testAssertTrue Unit tests for assertTrue
 
 %   Steven L. Eddins
 %   Copyright 2008 The MathWorks, Inc.
 
-initTestSuite;
+testSuite = buildFunctionHandleTestSuite(localfunctions);
 
 function testAssertTrueHappyCase
 assertTrue(true);
diff --git a/tests/testContainsRegexp.m b/tests/testContainsRegexp.m
index b23b0ae..321a12b 100644
--- a/tests/testContainsRegexp.m
+++ b/tests/testContainsRegexp.m
@@ -1,10 +1,10 @@
-function test_suite = testContainsRegexp
+function testSuite = testContainsRegexp
 %testContainsRegexp Unit tests for containsRegexp
 
 %   Steven L. Eddins
 %   Copyright 2008 The MathWorks, Inc.
 
-initTestSuite;
+testSuite = buildFunctionHandleTestSuite(localfunctions);
 
 function testOneStringContains
 assertTrue(xunit.utils.containsRegexp('MATLAB is great', '[A-Z]'));
diff --git a/tests/testIsSetUpString.m b/tests/testIsSetUpString.m
index a8dbb2b..1bcb3f2 100644
--- a/tests/testIsSetUpString.m
+++ b/tests/testIsSetUpString.m
@@ -1,10 +1,10 @@
-function test_suite = testIsSetUpString
+function testSuite = testIsSetUpString
 %testIsSetUpString Unit tests for isSetUpString
 
 %   Steven L. Eddins
 %   Copyright 2008 The MathWorks, Inc.
 
-initTestSuite;
+testSuite = buildFunctionHandleTestSuite(localfunctions);
 
 function testOneStringIs
 assertTrue(xunit.utils.isSetUpString('setup'));
diff --git a/tests/testIsTearDownString.m b/tests/testIsTearDownString.m
index f4d129c..53f339d 100644
--- a/tests/testIsTearDownString.m
+++ b/tests/testIsTearDownString.m
@@ -1,10 +1,10 @@
-function test_suite = testIsTearDownString
+function testSuite = testIsTearDownString
 %testIsTearDownString Unit tests for isTearDownString
 
 %   Steven L. Eddins
 %   Copyright 2008 The MathWorks, Inc.
 
-initTestSuite;
+testSuite = buildFunctionHandleTestSuite(localfunctions);
 
 function testOneStringIs
 assertTrue(xunit.utils.isTearDownString('teardownfoobar'));
diff --git a/tests/testIsTestCaseSubclass.m b/tests/testIsTestCaseSubclass.m
index 9b8278f..3efeff9 100644
--- a/tests/testIsTestCaseSubclass.m
+++ b/tests/testIsTestCaseSubclass.m
@@ -1,10 +1,10 @@
-function test_suite = testIsTestCaseSubclass
+function testSuite = testIsTestCaseSubclass
 %testIsTestCaseSubclass Unit tests for isTestCaseSubclass
 
 %   Steven L. Eddins
 %   Copyright 2008 The MathWorks, Inc.
 
-initTestSuite;
+testSuite = buildFunctionHandleTestSuite(localfunctions);
 
 function testTestCase
 assertTrue(xunit.utils.isTestCaseSubclass('TestCase'));
diff --git a/tests/testIsTestString.m b/tests/testIsTestString.m
index 8380658..42270d1 100644
--- a/tests/testIsTestString.m
+++ b/tests/testIsTestString.m
@@ -1,10 +1,10 @@
-function test_suite = testIsTestString
+function testSuite = testIsTestString
 %testIsTestString Unit tests for isTestString
 
 %   Steven L. Eddins
 %   Copyright 2008 The MathWorks, Inc.
 
-initTestSuite;
+testSuite = buildFunctionHandleTestSuite(localfunctions);
 
 function testOneStringIs
 assertTrue(xunit.utils.isTestString('testFoobar'));
diff --git a/tests/testRunxunitWithDirectoryName.m b/tests/testRunxunitWithDirectoryName.m
index 39c098f..4f3d3ed 100644
--- a/tests/testRunxunitWithDirectoryName.m
+++ b/tests/testRunxunitWithDirectoryName.m
@@ -1,7 +1,7 @@
-function test_suite = testRunxunitWithDirectoryName
+function testSuite = testRunxunitWithDirectoryName
 %testRunxunitWithDirectoryName Unit test for mtest('dirname') syntax.
 
-initTestSuite;
+testSuite = buildFunctionHandleTestSuite(localfunctions);
 
 function testDirName
 current_dir = pwd;
diff --git a/tests/test_TestSuiteInDir.m b/tests/test_TestSuiteInDir.m
index f4e4f51..63c606a 100644
--- a/tests/test_TestSuiteInDir.m
+++ b/tests/test_TestSuiteInDir.m
@@ -1,10 +1,10 @@
-function test_suite = test_TestSuiteInDir
+function testSuite = test_TestSuiteInDir
 %test_TestSuiteInDir Unit test for TestSuiteInDir class.
 
 %   Steven L. Eddins
 %   Copyright 2009 The MathWorks, Inc.
 
-initTestSuite;
+testSuite = buildFunctionHandleTestSuite(localfunctions);
 
 function test_constructor
 this_test_path = fileparts(which(mfilename));
diff --git a/tests/test_arrayToString.m b/tests/test_arrayToString.m
index 5f86d8f..88bc7a9 100644
--- a/tests/test_arrayToString.m
+++ b/tests/test_arrayToString.m
@@ -1,10 +1,10 @@
-function test_suite = test_arrayToString
+function testSuite = test_arrayToString
 %test_arrayToString Unit test for arrayToString.
 
 %   Steven L. Eddins
 %   Copyright 2009 The MathWorks, Inc.
 
-initTestSuite;
+testSuite = buildFunctionHandleTestSuite(localfunctions);
 
 function test_smallInput
 A = [1 2 3];
diff --git a/tests/test_assertElementsAlmostEqual.m b/tests/test_assertElementsAlmostEqual.m
index ba491b3..adc1cc3 100644
--- a/tests/test_assertElementsAlmostEqual.m
+++ b/tests/test_assertElementsAlmostEqual.m
@@ -1,5 +1,5 @@
-function suite = test_assertElementsAlmostEqual
-initTestSuite;
+function testSuite = test_assertElementsAlmostEqual
+testSuite = buildFunctionHandleTestSuite(localfunctions);
 
 %===============================================================================
 function test_happyCase
diff --git a/tests/test_assertFilesEqual.m b/tests/test_assertFilesEqual.m
index 676efd7..c0a6a7c 100644
--- a/tests/test_assertFilesEqual.m
+++ b/tests/test_assertFilesEqual.m
@@ -1,10 +1,10 @@
-function test_suite = test_assertFilesEqual
+function testSuite = test_assertFilesEqual
 %test_assertFilesEqual Unit test for assertFilesEqual
 
 %   Steven L. Eddins
 %   Copyright 2009 The MathWorks, Inc.
 
-initTestSuite;
+testSuite = buildFunctionHandleTestSuite(localfunctions);
 
 function test_equal
 assertFilesEqual('black.tif', 'black.tif');
diff --git a/tests/test_assertVectorsAlmostEqual.m b/tests/test_assertVectorsAlmostEqual.m
index 4289276..ef2eda7 100644
--- a/tests/test_assertVectorsAlmostEqual.m
+++ b/tests/test_assertVectorsAlmostEqual.m
@@ -1,5 +1,5 @@
-function suite = test_assertVectorsAlmostEqual
-initTestSuite;
+function testSuite = test_assertVectorsAlmostEqual
+testSuite = buildFunctionHandleTestSuite(localfunctions);
 
 %===============================================================================
 function test_happyCase
diff --git a/tests/test_compareFloats.m b/tests/test_compareFloats.m
index fa1e5db..eaba44d 100644
--- a/tests/test_compareFloats.m
+++ b/tests/test_compareFloats.m
@@ -1,5 +1,5 @@
-function suite = test_compareFloats
-initTestSuite;
+function testSuite = test_compareFloats
+testSuite = buildFunctionHandleTestSuite(localfunctions);
 
 %===============================================================================
 function test_elementwiseRelativeTolerance
diff --git a/tests/test_comparisonMessage.m b/tests/test_comparisonMessage.m
index 2b2ecaa..c6c19ff 100644
--- a/tests/test_comparisonMessage.m
+++ b/tests/test_comparisonMessage.m
@@ -1,10 +1,10 @@
-function test_suite = test_comparisonMessage
+function testSuite = test_comparisonMessage
 %test_comparisonMessage Unit test for comparisonMessage.
 
 %   Steven L. Eddins
 %   Copyright 2009 The MathWorks, Inc.
 
-initTestSuite;
+testSuite = buildFunctionHandleTestSuite(localfunctions);
 
 function test_happyCase
 s = xunit.utils.comparisonMessage('user message', 'assertion message', ...
diff --git a/tests/test_packageName.m b/tests/test_packageName.m
index 5a545dd..a069921 100644
--- a/tests/test_packageName.m
+++ b/tests/test_packageName.m
@@ -1,5 +1,5 @@
-function test_suite = test_packageName
-initTestSuite;
+function testSuite = test_packageName
+testSuite = buildFunctionHandleTestSuite(localfunctions);
 
 function test_happyCase
 suite = TestSuite.fromPackageName('xunit.mocktests');
diff --git a/tests/test_parseFloatAssertInputs.m b/tests/test_parseFloatAssertInputs.m
index a8e1a59..5ecd292 100644
--- a/tests/test_parseFloatAssertInputs.m
+++ b/tests/test_parseFloatAssertInputs.m
@@ -1,5 +1,5 @@
-function suite = test_parseFloatAssertInputs
-initTestSuite;
+function testSuite = test_parseFloatAssertInputs
+testSuite = buildFunctionHandleTestSuite(localfunctions);
 
 %===============================================================================
 function test_tooFewInputs()
diff --git a/tests/test_stringToCellArray.m b/tests/test_stringToCellArray.m
index 10de142..714150a 100644
--- a/tests/test_stringToCellArray.m
+++ b/tests/test_stringToCellArray.m
@@ -1,10 +1,10 @@
-function test_suite = test_stringToCellArray
+function testSuite = test_stringToCellArray
 %test_stringToCellArray Unit test for stringToCellArray
 
 %   Steven L. Eddins
 %   Copyright 2009 The MathWorks, Inc.
 
-initTestSuite;
+testSuite = buildFunctionHandleTestSuite(localfunctions);
 
 function test_happyCase
 s = sprintf('Hello\nWorld');

From 6dd76c00766229b186072a5017671836b66e15e3 Mon Sep 17 00:00:00 2001
From: Joris Kraak 
Date: Fri, 9 Dec 2016 11:14:53 +0100
Subject: [PATCH 3/4] docs(license): remove trailing whitespace

---
 license.txt | 132 ++++++++++++++++++++++++++--------------------------
 1 file changed, 66 insertions(+), 66 deletions(-)

diff --git a/license.txt b/license.txt
index be3df7c..ea9f99f 100644
--- a/license.txt
+++ b/license.txt
@@ -1,106 +1,106 @@
 Portions Copyright (c) 2010, The MathWorks, Inc.
 All rights reserved.
 
-Redistribution and use in source and binary forms, with or without 
-modification, are permitted provided that the following conditions are 
+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 
+    * 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 
+    * 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 The MathWorks, Inc. nor the names 
-      of its contributors may be used to endorse or promote products derived 
+    * Neither the name of the The MathWorks, Inc. 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 
+
+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.
 
 
 Portions Copyright (c) 2010, Thomas Grenfell Smith
 All rights reserved.
 
-Redistribution and use in source and binary forms, with or without 
-modification, are permitted provided that the following conditions are 
+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 
+    * 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 
+    * 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
-      
-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 
+
+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.
 
 
 Portions Copyright (c) 2007, Jaroslaw Tuszynski
 All rights reserved.
 
-Redistribution and use in source and binary forms, with or without 
-modification, are permitted provided that the following conditions are 
+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 
+    * 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 
+    * 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
-      
-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 
+
+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.
 
 
 Portions Copyright (c) 2014, Paul Sexton
 All rights reserved.
 
-Redistribution and use in source and binary forms, with or without 
-modification, are permitted provided that the following conditions are 
+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 
+    * 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 
+    * 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
-      
-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 
+
+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.
 

From d59a6f0ba74ce1609e769e60bfda30e5d649680e Mon Sep 17 00:00:00 2001
From: Joris Kraak 
Date: Fri, 9 Dec 2016 11:15:09 +0100
Subject: [PATCH 4/4] docs(license): add GN ReSound copyright notice

---
 license.txt | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/license.txt b/license.txt
index ea9f99f..1afb894 100644
--- a/license.txt
+++ b/license.txt
@@ -104,3 +104,28 @@ 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.
 
+
+Portions Copyright (c) 2016, GN ReSound
+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
+
+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.