I am trying to devise a unit test for a MATLAB app that can run either in windowed or headless mode. The test runs through the program in headless mode and tries to detect if any windows get opened during process.
My thought on this was to attach a listener to the groot
property CurrentFigure
, and write a PostSet
callback that increments a counter that keeps track of how many windows get opened. At the end the test then makes sure that the value is 0. For the record, this doesn't seem to work. Even though I have ShowHiddenHandles
to 'on'
, it does not seem to catch modal windows. However, that is not my question.
My question is, I had an error in the test code between the place where I created the listener and the place where I delete
it. Now, there is a listener attached to groot
, but the listener handle variable is cleared, so I get really weird behavior every time I try to open a window. The test object opens back up and then throws an error at the listener callback.
How can I find and delete the listener attached to groot
now that all original references to it have been removed from the workspace? So far the only method that works is to restart MATLAB, but that seems like an inefficient debugging method.
To reproduce the error, create the following test class:
classdef MCVtest < matlab.unittest.TestCase
% Minimal, Complete, Verifiable example
properties
numOfFiguresCreated = 0;
end
methods
function figureCreatedListener(testCase)
testCase.numOfFiguresCreated = testCase.numOfFiguresCreated + 1;
end
end
methods (Test)
function testFiles(testCase)
%Create Listener for this particular input file.
listener = addlistener(groot, 'CurrentFigure', 'PostSet', @testCase.figureCreatedListener); %#ok<NASGU>
error('Well, this sucks...')
% delete Listener for this input file
delete(listener) %#ok<UNRCH>
% Verify That no graphics objects were created at all.
testCase.verifyEqual(testCase.numOfFiguresCreated, 0);
end
end
end
From the command line:
>> suite = matlab.unittest.TestSuite.fromClass(?MCVtest)
>> results = suite.run
After the error:
>> figure
Error using MCVtest/figureCreatedListener
Too many input arguments.
Error in MCVtest>@(varargin)testCase.figureCreatedListener(varargin{:}) (line 17)
listener = addlistener(groot, 'CurrentFigure', 'PostSet', @testCase.figureCreatedListener); %#ok<NASGU>
Of course, listener
no longer exists, so I can't delete it. I've tried clear
, clear all
, and clear classes
, in that order, and the listener still persists. The only way to clear it (so far as I've found) is to restart MATLAB.
You should be able to find the listeners by accessing the undocumented 'AutoListeners__'
property of the Root
(or any other graphics) object, which contains a cell array of the listeners.
For example:
a = addlistener(groot, 'CurrentFigure', 'PostSet', @(s,e)disp('hi'));
tmp = groot;
listeners = tmp.AutoListeners__;
Which gives us:
>> a == listeners{1}
ans =
logical
1
So we can do something like:
for ii = 1:numel(listeners)
delete(listeners{ii});
end
To remove all of the dangling listeners.
Note that AutoListeners__
does not exist if there are no listeners for the object.
Also note that MATLAB has 2 different listener implementations: addlistener
, which binds the listener to the object and is removed when the object goes out of scope, and listener
, which us unbound and will be removed when the listener goes out of scope.
By utilizing listener
instead of addlistener
in your unit testing, you can avoid having dangling listeners if your cleanup is not run for whatever reason, in this case due to an error.