Модульные тесты Matlab чередуются между прохождением и сбоем (прохождение нечетных прогонов, сбой при четных)

У меня есть несколько модульных тестов для кода, который выполняет очень незначительные манипуляции с изображениями (объединение нескольких маленьких изображений в большее изображение). Когда я запускал тесты, я заметил, что три из четырех из них не сработали на линии, где они читали изображение из каталога (сбой с ошибкой индекса за пределами).

Однако, если я запущу его снова, они все пройдут. Когда я писал код, я заметил, что всякий раз, когда я устанавливаю точку останова в своем коде, мне приходится запускать модульные тесты дважды, потому что (после самого первого раза) они будут выполняться через тесты без каких-либо точек останова.

У меня есть репо, организованное следующим образом:

src/
    /* source code .m files are in here */

unit_tests/
    images/
        squares/
            - img1.png
            - img2.png
            ...
            - imgn.png
    - unit_tests.m

И у меня есть строка в моей настройке (внутри unit_tests.m) для создания и добавления путей для всего кода:

function tests = unit_tests()
    addpath(genpath('..'));    
    tests = functiontests(localfunctions);
end

Все модульные тесты имеют следующий формат:

function testCompositeImage_2x3(testCase)
    squares = dir('images/squares/*.png');
    num_images = length(squares);
    img = imread([squares(1).folder filesep squares(1).name]); % all same size squares
    rows = 2;
    cols = 3;
    buffer = 2;

    for idx = 1:num_images - (rows*cols)
        imarray = cell(1,(rows*cols));
        n = 1;
        for ii = idx:idx +(rows*cols) -1
            imarray{n} = imread([squares(ii).folder filesep squares(ii).name]);
            n = n + 1;
        end

        newimg = createCompositeImage(rows,cols,imarray, buffer);

        expCols = cols*size(img,1) + (cols+1)*2*buffer;
        expRows = rows*size(img,2) + (rows+1)*2*buffer;
        assert(checksize(newimg, expRows, expCols, 3) == true);
    end
end

("checksize" - это просто помощник, который я написал, который возвращает логическое значение b/c, утверждение не сравнивает матрицы)

Когда я запускаю новый сеанс Matlab и запускаю модульные тесты (используя кнопку «Выполнить тесты» на вкладке редактора), они проходят с этим выводом:

>> runtests('unit_tests\unit_tests.m')
Running unit_tests
.......
Done unit_tests
__________


ans = 

  1×7 TestResult array with properties:

    Name
    Passed
    Failed
    Incomplete
    Duration
    Details

Totals:
   7 Passed, 0 Failed, 0 Incomplete.
   0.49467 seconds testing time.

Запускаем второй раз (опять же нажатием кнопки):

>> runtests('unit_tests')
Running unit_tests
..
================================================================================
Error occurred in unit_tests/testCompositeImage_2x2 and it did not run to completion.

    ---------
    Error ID:
    ---------
    'MATLAB:badsubscript'

    --------------
    Error Details:
    --------------
    Index exceeds array bounds.

    Error in unit_tests>testCompositeImage_2x2 (line 47)
        img = imread([squares(1).folder filesep squares(1).name]); % all same size
================================================================================
/*similar error info for the other two failing tests...*/
...
Done unit_tests
__________

Failure Summary:

     Name                               Failed  Incomplete  Reason(s)
    ==================================================================
     unit_tests/testCompositeImage_2x2    X         X       Errored.
    ------------------------------------------------------------------
     unit_tests/testCompositeImage_2x3    X         X       Errored.
    ------------------------------------------------------------------
     unit_tests/testCompositeImage_3x2    X         X       Errored.


ans = 

  1×7 TestResult array with properties:

    Name
    Passed
    Failed
    Incomplete
    Duration
    Details

Totals:
   4 Passed, 3 Failed (rerun), 3 Incomplete.
   0.0072287 seconds testing time.

Тот факт, что он дает сбой в основном в первой строке, потому что он ничего не читает из папки, заставляет меня подозревать, что даже когда другие 4 теста предположительно проходят, они на самом деле вообще не выполняются. И тем не менее, если я снова прогоню тесты, они все пройдут. Запустите его в 4-й раз, и они снова терпят неудачу.

Сначала я подумал, что, возможно, модульные тесты выполнялись слишком быстро (только как-то при четных прогонах?) и он запускал модульные тесты до завершения функций addpath/genpath в настройке, поэтому я добавил оператор паузы и повторно провел тесты, но у меня была та же проблема, только на этот раз он ждал необходимое количество секунд, прежде чем продолжить и потерпеть неудачу. Если я запущу его снова, никаких проблем — все мои тесты пройдены.

Я совершенно не понимаю, почему это происходит; Я использую vanilla matlab (R2018a), работающий на машине с Win10, и у меня нет ничего необычного. Я чувствую, что вы должны иметь возможность запускать свои модульные тесты столько раз, сколько захотите, и ожидать одного и того же результата! Есть что-то, что я как-то упустил из виду? Или это какая-то странная особенность?


person asdf    schedule 23.04.2020    source источник
comment
Похоже, что команда, которую вы используете для запуска тестов, отличается два раза.   -  person Cris Luengo    schedule 23.04.2020
comment
У меня ощущение, что виноват addpath. Я бы вычислил абсолютный путь с помощью mfilename, fileparts и fullfile, а не ...   -  person Cris Luengo    schedule 23.04.2020
comment
@CrisLuengo - не могу поверить, что проглядел это, но вы совершенно правы - они запускаются двумя разными командами! Я использовал кнопку «Выполнить тесты» и ошибочно предположил, что она использует ту же команду. Интересно, что если я дважды запускаю runtests('unit_tests') из командной строки, это дает мне разное количество пройденных неудачных тестов (2 вместо 3), в то время как runtests('unit_tests\unit_tests.m') всегда дает правильный результат.   -  person asdf    schedule 23.04.2020
comment
Я попытался изменить addpath на filepath = regexp(fileparts(which(mfilename)), '\unit_tests', 'split'); и addpath(genpath(filepath{1}));, однако по какой-то непостижимой причине это на самом деле ухудшило ситуацию. Теперь он успешно работает только один раз (нажатие кнопки использует только один раз runtests('unit_tests\unit_tests.m')) Есть ли у вас какие-либо предложения по правильному методу?   -  person asdf    schedule 23.04.2020
comment
Что-то вроде fullfile(fileparts(fileparts(mfilename('fullpath'))),'src') должно строить правильный путь, если я не ошибаюсь.   -  person Cris Luengo    schedule 23.04.2020
comment
@CrisLuengo спасибо, это сработало. Я написал свое исправление в качестве ответа на случай, если у кого-то возникнет такая же проблема. Если у вас есть более элегантное решение, не стесняйтесь редактировать /etc   -  person asdf    schedule 23.04.2020


Ответы (1)


Добавляю свое исправление на случай, если кто-то еще столкнется с той же проблемой.

Как указал Крис, кое-что о линии

addpath(genpath('..'));

приводит к тому, что графический интерфейс переходит в странное состояние, когда нажатие кнопки «Выполнить тесты» чередуется между вызовами runtests('unit_tests\unit_tests.m') и runtests('unit_tests'), что, в свою очередь, приводит к тому, что тесты попеременно проходят и не проходят. Похоже, проблема не в самой переменной пути (поскольку она всегда содержит, как минимум, необходимые каталоги), а скорее в чем-то, присущем самому Matlab. Самое близкое, что я смог добраться до корня проблемы, это вызов (скомпилированной) функции dir внутри функции genpath.

«Правильным» решением было полностью удалить строку из функции unit_tests и добавить ее в функцию setupOnce:

function tests = unit_tests()
    tests = functiontests(localfunctions);
end

function setupOnce(testCase)
    addpath(genpath('..'));
end

Взлом (не рекомендуется), который не требует функции setupOnce, заключается в следующем:

function tests = unit_tests()
    pth = fullfile(fileparts(fileparts(mfilename('fullpath'))),'src');
    paths = regexp(genpath(pth), ';', 'split');
    for idx = 1:length(paths) - 1 % last element is empty
        addpath(paths{idx});
    end
end

Мне нужно было перезапустить Matlab, чтобы изменения вступили в силу. Это сработало для моей настройки с использованием r2018a, работающего на Win10.

person asdf    schedule 23.04.2020
comment
Вы можете Accept ответить сами. На первый взгляд это может показаться извращенным, но на самом деле это дает более findable ответ другим людям с похожей проблемой. - person Rolf of Saxony; 21.05.2020