In a filesystem, there are two kinds of paths: relative paths and absolute paths. Absolute paths always point to the same target while the target of a relative path depends on the current working directory. In Matlab, paths are called "path names" and there is a third kind of path name: the partial path name. It is defined as follows:
A partial path name is the last portion of a full path name for a location on the MATLAB search path.
(The Matlab search path is similar to the environment variable PATH and is used to locate Matlab files.) In consequence of this definition, a path name may be treated by Matlab as a partial path name even if the user is unaware of them.
In this post, I will demonstrate Matlab behavior that is unexpected if you are not aware of the existence of partial path names. I will also explain how this causes a bug in xUnit 3 and xUnit 4.
Demonstration
I have only access to Matlab on Linux but this demonstration should also work on Windows and Mac OS. I will use the UNIX path separator /
. For this demonstration, create an empty directory, start Matlab and change (cd) into this new directory. Execute the following command:
exist('toolbox/matlab/elmat')
The return value of the function exist
(exist documentation) indicates if the given path name exists and whether we are dealing with files or directories. In this post, the following return values are of interest:
- 0 if the given path does not exist,
- 2 indicates a file,
- 7 indicates a directory.
Surprisingly, this call returns 7 on my system. exist
respects the Matlab search path so although there is no subdirectory toolbox
in the current working directory, the function call above returns 7 because toolbox/matlab/elmat
exists on the search path (type path
). That is, toolbox/matlab/elmat
is a partial path name.
Now execute
isdir('toolbox/matlab/elmat')
The isdir
documentation does not mention the Matlab search path and being used to UNIX systems, one could assume isdir
searches only the current working directory if a path name is given that is not an absolute path name. On the contrary, the command above returns logical 1 on my system meaning isdir
also accepts partial path names as parameter.
Partial path names are by definition the "last portion" of an absolute path name so all of the following calls will behave as if we had used the longer partial path name toolbox/matlab/elmat
above:
exist('matlab/elmat') exist('elmat') isdir('matlab/elmat') isdir('elmat')
Now enter
cd('elmat')
The path name given to cd
(documentation) must be an absolute or a relative path name but elmat
is a partial path name. Consequently, an exception is raised.
xUnit and Partial Paths
xUnit is a unit testing framework for Matlab which was superseded by the unit testing framework integrated in Matlab since release R2013a. Given the call runxunit(name)
, xUnit needs to determine if name
is the name of a package or a directory in the current working directory and call the unit tests in it (runxunit
from xUnit 4 is called runtests
in xUnit 3). To that end, runxunit
calls isdir(name)
in TestSuite.fromName
(TestSuite.m
, line 199) and if this call returns logical 1, xUnit calls cd(name)
afterwards (TestComponentInDir.m:48
). Obviously, if name
is a partial but not a relative path name, then calling cd(name)
will raise an exception. Note the exception will also be raised if name
is simultaneously a valid package name. The unit testing framework shipped with Matlab does not contain this bug but both xUnit 3.1.1 and xUnit 4 have it. I reported the bug.
Edit February 24, 2016: xUnit 4 was fixed (check the bug report).
How I stumbled over the xUnit Bug
In the Matlab program I used to maintain, all unit tests can be found in the package tests
and for more than two years, I was able to run these tests by calling runtests('tests')
. Recently, the system administrator updated the Matlab search path so that tests
was suddenly a partial path name. This triggered the bug and left me baffled because there were no changes to the source code, yet the unit tests had stopped working.