Reliably resize uifigure - sometimes set Position is ignored under test automation
Show older comments
Hi,
I'm facing a strange issue, where resizing of a uifigure doesn't work consistently under testautomation stress.
Maybe someone has a tip for me, is there a better way to ensure that fig.Postion = [something] is visually applied, than drawnow/pause?
I have set of complicated dialogs, where frames needs to be extended to accomodate additional controls, when user clicks something. Everything works OK for manual testing, but starts failing on test automation.
I have a class object, representing the dialog, and it has property "mainFigure" - uifigure. For resizing of it (extra space on the lower part) I've implemented method
function pos = increaseMainFigureHeight(obj, increase)
pos = obj.mainFigure.Position;
% move bottom
pos(2) = pos(2) - increase;
% and increase the height
pos(4) = pos(4) + increase;
obj.mainFigure.Position = pos; <<<
% Flush callbacks and allow layout/window to settle before asserting position
desiredPos = pos;
for k = 1:60
drawnow;
if isequal(obj.mainFigure.Position, desiredPos) <<<<< this read may return different value!
disp(['increase ok: ' num2str(k)]);
break;
end
pause(0.05);
end
if k>=60
Messaging.debug('Figure height update not solved in 60 rounds', level=Messaging.Warning);
end
end
And very similar for decrease the height.
Under manual conditions the "disp(['increase ok: ' num2str(k)]);" prints always 1 - after setting new positions and drawnow, reading of the position will correspond to the positions, which were just set!
However, as soon as I get it under testautomation:
classdef ReusableDialogTest < matlab.unittest.TestCase
methods (Test)
function testIncreaseDecreaseMainFigureHeight(testCase)
dlg = dialogs.AIConfigDialog(); % Just create some dialog, inherited from ReusableDialog
clp = onCleanup(@() delete(dlg));
increaseSize = 100;
initialPos = dlg.mainFigure.Position;
extendedPosition = initialPos;
extendedPosition(2) = initialPos(2) - increaseSize;
extendedPosition(4) = initialPos(4) + increaseSize;
for repeat = 1:20
dlg.increaseMainFigureHeight(increaseSize);
testCase.assertEqual(dlg.mainFigure.Position, extendedPosition);
dlg.decreaseMainFigureHeight(increaseSize);
testCase.assertEqual(dlg.mainFigure.Position, initialPos);
end
end
end
end
I'm starting getting problems.
Note: I've inserted the loop for 20 repetitions, just to make the issue more visible.
Typical output for running the test will look like:
Running ReusableDialogTest
Setting up ReusableDialogTest
Done setting up ReusableDialogTest in 0 seconds
Running ReusableDialogTest/testIncreaseDecreaseMainFigureHeight
increase ok: 3
decrease ok: 1
increase ok: 2
decrease ok: 1
increase ok: 2
decrease ok: 2
...
So, sometimes several rounds of drawnow/pause are required.
But sometimes it stucks completely. In following case decrease didn't succeed with 60 hops of drawnow/pause:
Running ReusableDialogTest
Setting up ReusableDialogTest
Done setting up ReusableDialogTest in 0 seconds
Running ReusableDialogTest/testIncreaseDecreaseMainFigureHeight
increase ok: 3
Warning: Figure height update not solved in 60 rounds
================================================================================
Assertion failed in ReusableDialogTest/testIncreaseDecreaseMainFigureHeight and it did not run to completion.
---------------------
Framework Diagnostic:
---------------------
assertEqual failed.
--> The numeric values are not equal using "isequaln".
--> Failure table:
Index Actual Expected Error RelativeError
_____ ______ ________ _____ __________________
2 478 578 -100 -0.173010380622837
4 252 152 100 0.657894736842105
Actual Value:
729 478 583 252
Expected Value:
729 578 583 152
------------------
Stack Information:
Accepted Answer
More Answers (1)
There's <another current thread of similar symptom> that is known that occasionally one will get back the unchanged coordinates on an immediate query even after the object has been rendered owing to internal latency of the variable memory locations being updated to reflect the changes before the memory read occurs. I've never seen it take more than just a few 10s of msec however.
I'd recast the update function a little, however. Don't keep forcing the re-rendering; you've already told it once, you're starting it all over when demanding it again. That isn't necessary and is probably not a wise thing to do, you'll be stackng events faster than the rendering engine will be able to handle them and with drawnow alone, they'll all be queued. (It's a lot like code seen pretty often asserting "hold on" inside a loop--once set, 'on' can't get any 'on-er'). At worst, use the 'limitrate' and 'nocallbacks', but since you're not actually needing to change the new position again, just be patient and let it do its thing in its own sweet time...it would be agoodthing™ if Mathworks could figure out a way to prevent this race condtion internally so it didn't occur, but so far it is "just the way it is".
See if the following is any more better...
function pos = increaseMainFigureHeight(obj, increase)
pos = obj.mainFigure.Position;
pos=pos+increase*[0 -1 0 1]; % move bottom and increase height - my idiomatic way to write
%pos=pos+[0 -increase 0 increase]; % alternate idiom using the variable twice but w/o multiplication
desiredPos = pos; % save new location wanted
obj.mainFigure.Position = pos; % set the new
drawnow; % force rendering
for k = 1:60
pause(0.05); % wait a little before querying
if isequal(obj.mainFigure.Position, desiredPos) % make sure memory is updated, too
disp(['increase ok: ' num2str(k)]);
return; % everything is ok, we're done here
end
end
% if here, things went south
Messaging.debug('Figure height update not solved in 60 rounds', level=Messaging.Warning);
end
You potentially could make it more responsive by moving the pause statement to the end of the for...end loop; in the cases in which the race condition doesn't exist it would get by without the delay at all. Be interesting to see how the statistics might change between the two structurings.
If you can still make this fail, then it may take some more in-depth digging about whether the testing regimen you've established is causing something else to go on.
4 Comments
dpb
on 13 Feb 2026
The only thing I can see that could cause a failure in the above other than an internal bug is the outside possibility of the position vector being non-integral values such that the floating point comparison fails. Since MATLAB will accept a double for the position property, if the positions were being calculated from some formula rather than being entered as integer numbers, it is then possible there could be internal rounding. While MATLAB will render the figure rounding the values to nearest pixel, internally the 'Position' property would reflect the values passed in. That could lead to a later failure in an exact comparison if the new position values were calculated/set in a different manner.
That could be avoided if it were assured the values passed initially were always rounded first.
Not likely one would presume, but at least possible.
Nick
on 14 Feb 2026
dpb
on 14 Feb 2026
There have been quite a few reports of rendering issues on Mac and Linux so perhaps this is another symptom.
Have you tried the 'SizeChangedFcn' callback to see if it is always being generated? I'm not sure where in the hierarchy chain it is triggered; whether it might not be in the failing cases or if it were but the rendering still didn't occur might be a clue.
Sounds as though there is something internal going on, however...is there anything else happening during these tests that could switch focus or the like?
Nick
on 15 Feb 2026
Categories
Find more on Programming Utilities in Help Center and File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!