Re: дебагинг vs unit-тесты
От: Кодт Россия  
Дата: 29.04.16 18:05
Оценка: +8 :)
Здравствуйте, _hum_, Вы писали:

__>Всегда считал, что это ортогональные вещи, но, тут товарищ landerhigh заявил, что использование дебагера — каменный век:


Не каменный век, а каменный цветок. Иногда выходит, а иногда нет.

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

Но любое покрытие кода — хоть в виде тестов, хоть в виде ручного прохождения тех или иных сценариев работы с программой — требует известной выдумки.
(У нас С++, а не Эйфель, который принудительно покрывает всё, до чего может дотянуться).

Дебаг — это творческий процесс создания теста.
Делаем всё по шагам и вручную, смотрим, что там хорошо и что плохо, запоминаем, а потом насуём в код недостающие формальные контракты, ровно в нужном количестве.

Или, скажем, если какая-то проверка отвалилась, значит, неопределённое поведение возникло до неё. И потребуется отследить предысторию, хотя бы в объёме стека вызовов. А как ты напишешь новый нужный тест, если не проанализируешь стек?
Перекуём баги на фичи!
Re[6]: дебагинг vs unit-тесты vs ассерты
От: Кодт Россия  
Дата: 30.04.16 16:26
Оценка: 106 (4) +2
Здравствуйте, landerhigh, Вы писали:

L>100500 тестов не требуется. На юнит обычно нужно три набора тестов (три теста). И написать их выйдет быстрее, чем ковырять их в отладчике. И они станут бесплатной регрессией, когда начнут изменяться какие-то юниты.


Не одними юнитами.
А интеграционные тесты — или ты устроишь комбинаторный взрыв, покрывая все мыслимые сочетания, — и будешь гонять эту бомбу каждый раз при изменениях, и тогда узнаешь, что регрессия нифига не бесплатна; или перестанешь фанатеть и покроешь исключительно проблемные места; а какие места проблемные, выяснишь во время отладки.

L>>>Не говоря уже о том, что от написания какого-то кода, зарытого глубоко под капот системы, засунутой под капот подсистемы, засунутой в подмодуль модуля основной программы, до возможности его запустить в составе этой самой программы, может пройти полгода-год.


Интеграционный тест должен будет потратить столько же времени для проверки, наигрывается или нет тот самый баг. Если какой-то больной модуль ровно через год запускается.

Тебе придётся создавать специальную тестовую среду, джонни-сделай-монтаж, которую можно потом и сотрудникам-тестерам запускать.

Кроме того, тебе придётся создать инфраструктуру для эмуляции действий пользователя.

Например, реальный баг: после нажатия на кнопку [_] окошко не сворачивается, а захватывает мышь и тупит. Что это за хрень?
Ты пишешь тест: mainwindow()->minimize(). Тест проходит.
Пишешь mainwindow()->sendmessage(WM_COMMAND, ID_MINIMIZE). Проходит.
Понимаешь, что дело именно в клике мыши. Пишешь функцию point_of_x_button(), и функцию посылки клика mousedown, mouseup. Посылаешь окну. Тест, блин, проходит!
И т.д. и т.п., делаешь очень много работы, получаешь нахаляву кучу тестов для будущей регрессии, пока не докапываешься до правды: глюк был в обработчике WM_NCHITTEST, причём, не по всей площади кнопки, а только на её краю. И что самое смешное, это следствие кривых рекомендаций самого микрософта, который в 7, 8 и 10 продолжает экспериментировать с аэро и метро, и чьи программы тоже ведут себя странновато. (Ну, когда я знаю правду, я знаю, куда смотреть).

А может быть, проще было не выдумывать тесты, а запустить дебаггер и Spy++, и смотреть, как диспетчится мышь?
О да, у тебя теперь гора тестов для регрессии.
Хотя правильно будет выкинуть её, и оставить только покрытие багфикса. Ну, инфраструктуру-то заначить, ещё пригодится.

M>>И? Ты пишешь тесты, которые проверяют контракты модуля. Нормально. Я тоже так делаю. Просто когда что-то не работает в твоем алгоритме в соответсвии с контрактом, ты пишешь 100500 тестов, а я прохожу эту функцию под отладчиком, и смотрю, на каком шаге и что пошло не так. Я за пол-часа справился, а ты уже вторую неделю пишешь юнит-тесты.


L>Нет, не так. Ты полчаса (скорее полдня, кого ты обманываешь?) ковырялся в отладчике, а мне этого делать вообще не пришлось, поскольку у меня покрытие сценариев для определенных участков кода стремится к 100% и вероятность случаев, когда что-то не работает в покрытом тестами алгоритме, стремятся к нулю. Это не значит, что все сразу магически работает, более высокоуровневые баги случаются. Только они, как правило, имеют определенную природу. И самое забавное, что их причина либо очевидна, либо выявляется банальной дедукцией, что проверяется написанием простого тестового сценария, который после исправления бага становится regression-тестом.


Не обманывай себя. Тесты пишет человек. Вот насколько у тебя хватило фантазии покрыть предметную область, настолько ты и написал тесты.

Сделал, к примеру, транспонирование матрицы на месте, но она у тебя, скажем, стреляет по памяти при НОД(x,y)=17, а стрельба сходит с рук при x<100.
Да, ты написал тесты о том, что матрица транспонируется. Разумеется, только при маленьких размерах и выборочно (ну не перебирать же все x от 1 до 200 и y от 1 до 200).
А что ты сделал для проверки стрельбы?
Перекуём баги на фичи!
Re: дебагинг vs unit-тесты vs ассерты
От: T4r4sB Россия  
Дата: 29.04.16 18:16
Оценка: +5
А на самом деле никакого vs. Всё нужно.
Нет такой подлости и мерзости, на которую бы не пошёл gcc ради бессмысленных 5% скорости в никому не нужном синтетическом тесте
Re[4]: дебагинг vs unit-тесты vs ассерты
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 30.04.16 16:56
Оценка: 18 (1) +2
Здравствуйте, landerhigh, Вы писали:

L>Но главная причина состоит в том, что отладчик как инструмент разработки контрпродуктивен. Для проведения вскрытия (разбора крешдампа) — вещь отличная, но использовать его при разработке?


Как минимум ещё случаи
1. Странности работы платформы/фреймворка/etc., не документированные, плохо описанные, нарушающие документацию, etc.
2. Сходно с первым, но другая вводная — оОбучение новым языкам/фреймворкам — когда надо прочувствовать происходящее в виде последовательности состояний и разницы между ними. Где что за что цепляется, где на каком шаге что успело измениться...

тесты на всё это ты не нарисуешь, потому что в принципе не представляешь, что проверять, или почему оно не работает, несмотря ни на что.

L>Делать первый прогон под отладчиком, чтобы выяснить, какие варианты не учтены? Ну не знаю, мне для этого отладчик не нужен, и вообще звучит странно. Не говоря уже о том, что от написания какого-то кода, зарытого глубоко под капот системы, засунутой под капот подсистемы, засунутой в подмодуль модуля основной программы, до возможности его запустить в составе этой самой программы, может пройти полгода-год.


Увы. И когда тогда оказывается, что одна из самых базовых архитектурных идей была нерабочей...
именно поэтому agile (поклон соседнему треду) хорош тем, что там возможность запустить "в составе этой самой программы" с самого начала делается как можно раньше, чтобы найти все подводные грабли.
The God is real, unless declared integer.
Re: дебагинг vs unit-тесты
От: IT Россия linq2db.com
Дата: 01.05.16 20:49
Оценка: 6 (1) +2
Здравствуйте, _hum_, Вы писали:

__>Всегда считал, что это ортогональные вещи,


Именно. Сравнивать юнит-тесты и отладчик — это как сравнивать тёплое с мягким. Алтернативой отладчику могут быть логи и то с натяжкой. При чём тут юнит-тесты вообще не понятно

__>но, тут товарищ landerhigh заявил, что использование дебагера — каменный век:


Так говорят обычно те, кто не понимает что такое отладчик.
Если нам не помогут, то мы тоже никого не пощадим.
Re: дебагинг vs unit-тесты
От: chaotic-kotik  
Дата: 06.09.16 18:10
Оценка: 26 (2)
Здравствуйте, _hum_, Вы писали:

__>Всегда считал, что это ортогональные вещи, но, тут товарищ landerhigh заявил, что использование дебагера — каменный век:


Использование юнит-тестов это каменный век, а вовсе не использование дебага.

Классический юнит-тест по ТДД (это когда ты сначала пишешь тест, а потом реализуешь код) — крайне редко встречается в природе. На практике, тест пишется вместе с кодом, ты вначале можешь плохо представлять как в конечном итоге будет выглядеть интерфейс и как модуль будет использоваться. Юнит-тест это такая лаборатория, которая пишется вместе с кодом и которая позволяет начать запускать этот код в процессе ранней разработки (в том числе под отладчиком). До интеграции в продукт еще далеко, поэтому это единственный способ поработать с разрабатываемым модулем в живую (REPL-а то нет). Помимо этого, юнит-тест нужен для отладки билда, т.к. в системе сборки будут видны все зависимости этого модуля еще до того как он включится в основной продукт.

Юнит-тесты почти не находят ошибки. Они помогают разрабатывать код, это строительные леса, после стабилизации кода они практически не находят регрессии. Их запускают на CI сервере в основном для того, чтобы успокоить разработчиков, которые эти тесты написали.
Другое дело — функциональные тесты. На проектах, где я работал, большая часть регрессий находилась с помощью таких тестов, а юнит-тесты, написанные программистами, находили какие-то слезы. Но вот тут уже без отладчика сложно что-то сделать, т.к. тебе дают либо крэш-дамп, либо входной файл/базу данных. Здесь нужны именно классические навыки отладки + знание инструментов, таких как valgrind, asan и tsan. Та же история с фаззингом. Он находит много всего, больше всех ваших юнит-тестов вместе взятых, но его без отладчика опять же сложно использовать, а под asan-ом american fuzzy lop находит еще больше говна.

Вот например facebook недавно зарелизил ZStandard — новый комрессор (https://github.com/facebook/zstd). Вы там не найдете юнит-тестов вообще, но там есть fuzz-тесты и бенчмарки. Наверняка они еще и функциональные тесты используют, просто скрипты и датасеты для них в другом репозитории хранят. В общем, классические юнит-тесты сильно переоценены, а вот навыки отладки как раз наоборот — недооценены и редко встречаются. Тестировать код правильно тоже никто почти не умеет.
Re[11]: дебагинг vs unit-тесты vs ассерты
От: landerhigh Пират  
Дата: 04.05.16 15:47
Оценка: 16 (1) +1
Здравствуйте, Кодт, Вы писали:


К>Нет, пока что я вижу, куда ты тянешь дискуссию: "поскольку юнит-тесты — это круто, давайте выкинем отладчик".


Нет, не так. Попытка заменять тесты отладчиком приводят именно что к фигак-фикаг продакшену.

К>Проект хромиума покрыт тестами сверху донизу, но их настолько недостаточно, что приходится иногда даже трахаться с gdb в командной строке.


Есть мнение, что без тестов он вообще бы не взлетел.

К>Э, ты тут спрыгиваешь с темы.

К>Дано: написал кривой код, который на редких условиях зримо глючит. Обкладывай его тестами для поиска бага.

Эээ, тесты не для поиска багов. А для написания верифицируемого в контролируемых условиях кода. Почувствуйте разницу (с).

К>Можешь даже отрефакторить наивный алгоритм, исходно обложенный тестами. Собственно, так оно и происходит: взяли наивную реализацию, начали переписывать на замороченную, затейливо накосячили (а старые тесты при этом выполнились).


Еще раз — когда в код вносят новые граничные условия, для этих условий пишут новые тесты. Это часть процесса разработки. Если ему не следовать, то, как говорится, щасливой атладки!

К>>>Если будешь рожать "с листа", то риск накосячить с адресной арифметикой достаточно велик.

L>>Ключевое слово "риск". Привыкший работать по TDD моментально распознает эти риски. И задает себе вопрос — как я напишу тест, который проверит, накосячил ли я в данном месте? Это не всегда можно, но чаще всего — как раз можно.

К>До какой степени детализации ты будешь обкладывать код тестами?


До приемлимой. Наша задача — написать код с предсказуемым поведением в определенных условиях и верифицировать это поведение в пределах этих самых условий.

К>
К>void go_around_zero(int n) { for(int i = -n*10; i <= n*10; ++i) { do_smth(); } }
К>// TODO отрефакторить, чтобы покрыть тестами
К>


Эээ, тесты по определению тестируют наблюдаемое поведение. Что можно явно наблюсть у данного куска кода? Факт зацикливания? Это неявно. do_smth() явно не зависит ни от n, ни от i. Можно нужно переписать цикл, убрав чертовщину с -n*10.
Но ладно, в принципе, пример годный. Допустим, что исходный код имеет некий пока непонятный мне смысл. Сделаем неявное явным

template <class F>
void go_around_zero_v2(int n, F func) { for(int i = -n*10; i <= n*10; ++i) { func(); } }


TEST(go_around_zero_v2, testNegativeN)
{
    int n = 10;
    unsigned int expected = n*10*2+1;    // Ну или 100500 от балды, чтобы просто отловить бесконечный цикл
    unsigned int actual = 0;
    go_around_zero(-10, [&]() {
        ASSERT_LT(expected, actual++) << "Possible infinite loop";    // Враг не пройдет
        });
    ASSERT_EQ(expected, actual);        // Исходный код до этого момента не дойдет
}


Получаем провал теста.
получаем провал и начинаем думать — кто виноват и что делать. В смысле, должен ли go_around_zero выполнять проверку входных данных или по условию задачи они проверяются уровнем выше и тут их проверять смысла нет. Если последний вариант — добавляем огромный WARNING, нет, лучше ACHTUNG в описание функции. Если первый, то выясняем, что именно нужно проверить — только на отрицательное значение n или же на переполнение и т.п. Или же в ходе дискусии выяснится, что этот go_around_zero — остаток говнокода из каменного века, и т.к. do_something от индекса не зависит, то код можно переписать с условием цикла здорового человека, убрав возможности для неявного переполнения.
На больших проектах, где задействовано много людей, бывает так, что концов не найти и все отмахиваются, но при этом настаивают на сохранении go_around_zero. Для очистки совести меняю оригинальный код на
template <class F>
void go_around_zero_v2(int n, F func) { 
    if (math::abs(n) > MAX_UINT/20 -1)
    {
        throw std::runtime_error("Loop will go bananas!");
    }
    for(int i = -n*10; i <= n*10; ++i) { 
        func(); 
    } 
}

void go_around_zero(int n)
{
    go_around_zero_v2(n, do_smth);
}


Примерно все.

К>А в это время в Виллабаджо уже делают фигак-фигак-продакшен.




Через 3 месяца тестеры пишут баг-репорт "падает". Смотришь в лог, видишь bananas. Пинаешь того, кто вызывал твой код.
www.blinnov.com
Re[3]: дебагинг vs unit-тесты
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 01.05.16 05:14
Оценка: 10 (2)
Здравствуйте, Stanislav V. Zudin, Вы писали:

SVZ>Вот если взять твой пример с матрицей и спроецировать его, скажем, на построение адаптивной сетки (adaptive mesh refinement — можно погуглить картинки), то что у нас получается.


Интересный пример.

SVZ>Что придется тестировать:

SVZ>Случай 1. Сетка из одной ячейки.
SVZ>Подготовка данных занимает одну строчку кода, затем надо проверить, что все 6 граней инцидентны "воздуху".

Да, хорошая база. Собственно, эта проверка отвечает концепции BDD — если у нас есть понятные и легко формулируемые требования уровня ТЗ к реализации чего-то, их в первую очередь надо оформить в виде тестов верхнего уровня.
При любых изменениях внутренней логики именно нарушение этих тестов будет показывать какой-то крупный факап.

SVZ>Теперь ячейку надо рассечь.

SVZ>Варианты сечения: по осям X, Y, Z, X+Y, X+Z, Y+Z, X+Y+Z. Итого 7 вариантов сечения. В результате получается от 2 до 8 ячеек.
SVZ>Теперь нужно найти положение ячеек относительно соседей (не одна строчка). Затем проверить, что на периферии ячейки по-прежнему инцидентны воздуху, а внутри соединены между собой.

Примерно то же самое. Можно делать, если логика симметрична, тест не на все сразу, а на самый сложный вариант плюс по одному простому (типа, из X+Y, X+Z, Y+Z вначале взять только X+Z). Остальные сложить в низкоприоритетный TODO.

Но, по-моему, сечения по косым граням — это перебор. Что, параллелепипеда вам недостаточно? А почему бы тогда сразу не переходить к какой-нибудь трёхмерно-сотовой структуре?

[...]

SVZ>Случай 4. Тут самое интересное. Т.к. каждая ячейка своей гранью может соприкасаться только с 0, 1, 2 и 4 ячейками, то при определенных сечениях возникает конфликт, чтобы его разрешить необходимо подразбить соседа — начинается лавинообразный процесс. Как тут писать тест — у меня фантазии уже нет.


Вот если лавинообразный процесс — значит, он формулируется в терминах: шаг процесса (взять одну ячейку, если она ещё не пройдена на этом уровне (для данного шага и более крупного), изучить воздействие на соседей, добавить соседей в список обработки). Это вполне может быть одной функцией, для которой наготовить тестовых данных для типичных ситуаций, далее обеспечить покрытие всех веток логики (проверить каким-то автоматизированным средством). Ключ к качеству проверки — именно полное покрытие.
Далее сформулировать логику цикла по списку ячеек (вариант общеизвестного поиска в ширину) и на моках убедиться в её работе на произвольных данных нескольких случаев (пустой список, 2-3 варианта перекрытия распространения ячеек на одну и ту же). Тонкостью будет отдельный учёт одной ячейки на более мелкой сетке как новой сущности, но это решается в зависимости от варианта алгоритма (например, если шаг сетки кратен степени двойки, то учёт вообще банален).

SVZ>Итого. Чтобы запрограммировать алгоритм потребуется 1-2 недели и еще пара недель, как минимум, чтобы обложить его тестами, от которых будет хоть какой-то прок.

SVZ>Что интересно, когда код будет отлажен никто в него не полезет. А если полезет, то это означает, что предстоят очень серьезные изменения, которые могут перекроить все структуры данных и тогда написанные тесты пойдут псу под хвост.

SVZ>Как в таких условиях извлечь из юнит-тестов какую-то пользу — я пока не представляю.


См. выше. Тут чётко видно различие двух уровней логики — чисто программирования (представление ячеек само по себе, списки поиска в ширину для лавинного распространения...) и математического.
Если вынести оба в отдельные функции, их можно тестировать. А компилятор пусть потом инлайнит
The God is real, unless declared integer.
дебагинг vs unit-тесты
От: _hum_ Беларусь  
Дата: 29.04.16 13:33
Оценка: +2
Всегда считал, что это ортогональные вещи, но, тут товарищ landerhigh заявил, что использование дебагера — каменный век:

L>Вот смотри. Сейчас ваш процесс выглядит примерно так.


L>Тимлид: Нужно [исправить баг А|реализовать стори B|быстро что-то подправить в модуле C]

L>Разраб: Yes, sir!

L>Разработчк изменяет пару строк кода где-то очень глубоко в кишках исходников. И говорит "готово!". А тимлид "А ты проверил?". И разработчик идет курить бамбук, пока все пересобирается, чтобы потом еще полчаса вручную докликивать программу в состояние, в котором он может косвенно проверить часть функциональности внесенного изменения. Обнаруживает баг в багфиксе и все по-новому. Два часа рабочего времени жестоко убито. Мотивация утопилась в унитазе, а профессиональная гордость хлещет водяру из горла.


L>А вот как выглядит процесс разработки здорового человека:

  текст
L>Разраб: так, что у нас тут в беклоге вниманием обделено? О! Поддержка формата времени протокола (страшная аббривеатура) в сетевом модуле.
L>Разраб: Ага, понятно, наконец-то мы будет показывать настоящее время, а не забивать его <unknown>. Так, где у нас спецификация...
L>(5 минут)
L>Разраб: Я хочу того же, что курили аффтфры спецификации протокола. Открывает Wireshark и выдергивает из сетевой сессии с прибором сообщения, содержащие оные типы. В случае отсутствия прибора или wireshark'a изобретает Hex-dump нужного PDU самостоятельно или заимствует его из спецификации, если аффтары озаботились примерами. В процессе придумывает забавные примеры невалидного элемента. Добавляет в протокола поддержку нового типа, а в проект юнит-тестов — проверку работы парсера, используюя награбленные или придуманные сырые данные. Не забывает тест, в котором на вход парсера подается мусор или специально инвалидированные данные. Щелкает "билд" на юнит-тест проекте. Оный собирает измененную либу парсера протокола, собирается сам и запускается. В консоли — 2 failed tests, которые разработчик нарочно зафейлил, чтобы проверить сам себя. Исправляет проваленные тесты, добавляет новые для граничных условий и обнаруженных серых пятен в спецификации, перезапускает билд. Через 5 минут — XXX tests passed.

L>В итоге — полностью реализовананя новая функциональность, покрытая тестами и даже в некоторых случаях прошедшая regression — ранее написанные тесты покажут, если новая функциональность вносит breaking change. Причем, за время, которого не хватило бы на полную сборку проекта и хотя бы один ручной тестовый прогон. Причем, проект автоматически прогоняет даже такие условия, которые во время ручного теста проверить невозможно.


На мой ответ: "извините, но вы здесь описали вариант, когда либо готовый код правится, либо к готовому коду добавляется еще один готовый функциональный блок, и проверяется их совместимость. я нигде не увидел собственного написания.
ну, простейший пример — вам сказал тим лид — нужно написать функцию, транспонирующую матрицу. как в этом случае будет выглядеть работа с тестами без дебагера?" последовали опять общие фразы.

Потому вопрос в зал, может кто-нибудь, кто придерживается мнения landerhigh, на примере задачи написания функции транспонирования матрицы показать, как тесты заменяют работу с дебагером?



UPD.

L>Вот сходил ты в курилку и придумал, что назовешь свою функцию

L>
L>    Matrix transpose(const Matrix& original);
L>


L>Налил кофе и написал следующие три теста

L>
L>    TEST(transposeMatrix, transposeNegative)
L>    {
L>        // this is more a border case test
L>        ASSERT_TRUE(transpose(emptyMatrix).empty());   // Also checks that we don't crash if matrix is empty
L>    }

L>    TEST(transposeMatrix, transposePositiveSymmetrical)
L>    {
L>        Matrix symmetricalMatrix = ...; // Initialize
L>        Matrix expected = ...; // Manually transposed matrix
L>        ASSERT_EQ(expected, transpose(symmetricalMatrix));
L>        ASSERT_EQ(symmetricalMatrix, transpose(transpose(symmetricalMatrix)));
L>    }

L>    TEST(transposeMatrix, transposePositiveAsymmetrical)
L>    {
L>        Matrix asymmetricalMatrix = ...; // Initialize
L>        Matrix expected = ...; // Manually transposed matrix
L>        ASSERT_EQ(expected, transpose(asymmetricalMatrix ));
L>        ASSERT_EQ(asymmetricalMatrix , transpose(transpose(asymmetricalMatrix )));
L>    }
L>


L>Потом написал собственно код транспонирования.


L>Запустил тест. На все-про-все 5 минут времени и у тебя гораздо более полное покрытие, нежели ты может добиться ручными проверками в отладчике.



L>Ты мне лучше скажи, зачем тут вообще отладчик может понадобиться?


ответ:
так а где здесь код-то самого транспонирования? ведь ошибки именно там кроются. и если вы увидите срабатывание ассертов, что делать дальше будете?
как, например, обнаружите глупейшую ошибку типа Matrix[i][j] = Matrix[j][i] для in-place трансполнирования квадратной матрицы?
Отредактировано 29.04.2016 13:43 _hum_ . Предыдущая версия .
Re[5]: дебагинг vs unit-тесты vs ассерты
От: landerhigh Пират  
Дата: 30.04.16 01:54
Оценка: +1 -1
Здравствуйте, Marty, Вы писали:

L>>Но главная причина состоит в том, что отладчик как инструмент разработки контрпродуктивен. Для проведения вскрытия (разбора крешдампа) — вещь отличная, но использовать его при разработке?

M>Использование отладчика позволяет не писать 100500 тестов, которые после устаканивания контрактов интерфейсов будут выкинуты или переписаны, по мере изменения контраков

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

L>>Не говоря уже о том, что от написания какого-то кода, зарытого глубоко под капот системы, засунутой под капот подсистемы, засунутой в подмодуль модуля основной программы, до возможности его запустить в составе этой самой программы, может пройти полгода-год.


M>И? Ты пишешь тесты, которые проверяют контракты модуля. Нормально. Я тоже так делаю. Просто когда что-то не работает в твоем алгоритме в соответсвии с контрактом, ты пишешь 100500 тестов, а я прохожу эту функцию под отладчиком, и смотрю, на каком шаге и что пошло не так. Я за пол-часа справился, а ты уже вторую неделю пишешь юнит-тесты.


Нет, не так. Ты полчаса (скорее полдня, кого ты обманываешь?) ковырялся в отладчике, а мне этого делать вообще не пришлось, поскольку у меня покрытие сценариев для определенных участков кода стремится к 100% и вероятность случаев, когда что-то не работает в покрытом тестами алгоритме, стремятся к нулю. Это не значит, что все сразу магически работает, более высокоуровневые баги случаются. Только они, как правило, имеют определенную природу. И самое забавное, что их причина либо очевидна, либо выявляется банальной дедукцией, что проверяется написанием простого тестового сценария, который после исправления бага становится regression-тестом.

M>Не, если тебя на твоей работе терпят с таким подходом — ок, я не хочу занять твоё место, сиди там и дальше, пиши юнит-тесты. Пока я пишу продукт.


О, переход на личности во втором сообщении .
А еще нас обвиняют в фанатизме.
www.blinnov.com
Re[12]: дебагинг vs unit-тесты vs ассерты
От: Кодт Россия  
Дата: 04.05.16 16:28
Оценка: +1 :)
Здравствуйте, landerhigh, Вы писали:

К>>Нет, пока что я вижу, куда ты тянешь дискуссию: "поскольку юнит-тесты — это круто, давайте выкинем отладчик".

L>Нет, не так. Попытка заменять тесты отладчиком приводят именно что к фигак-фикаг продакшену.

Кто говорил "заменять"? Это ты всячески хочешь заменить отладчик тестами, а не я тесты отладчиком.

L>Еще раз — когда в код вносят новые граничные условия, для этих условий пишут новые тесты. Это часть процесса разработки. Если ему не следовать, то, как говорится, щасливой атладки!


Видишь ли, вся арифметика — это сплошные граничные условия.
И ты либо пишешь на идрисе или каких-то тому подобных языках, с нумералами Пеано и прочами ужасами.
Либо соглашаешься на то, чтоб какую-то совсем уж откровенную мелочёвку принять на веру. (А компилятор, в случае чего, тебя не простит и отомстит).

Вот в этом месте, как только ты где-то написал *10 или даже +1, — ты уже попал на граничные условия. В совершенно новом коде, в котором, казалось бы, нечему ломаться.

L>Эээ, тесты по определению тестируют наблюдаемое поведение. Что можно явно наблюсть у данного куска кода? Факт зацикливания? Это неявно. do_smth() явно не зависит ни от n, ни от i. Можно нужно переписать цикл, убрав чертовщину с -n*10.


Почему факт зацикливания? Умный гусь или шланг в -O3 способен творить чудеса, и на нашем форуме уже приводили примеры — как он и бесконечный, и пустой цикл создавал, и через условия перепрыгивал. Потому что Undefined даёт широкие возможности. Например, отладочный код будет работать правильно (тесты-то компилятся с -O0 либо -Ox -DDEBUG), а релизный хаотично, и даже диск форматнуть может при известной ловкости рук.

L>Но ладно, в принципе, пример годный. Допустим, что исходный код имеет некий пока непонятный мне смысл. Сделаем неявное явным

L>
L>template <class F>
L>void go_around_zero_v2(int n, F func) { 
L>    if (math::abs(n) > MAX_UINT/20 -1)
L>    {
L>        throw std::runtime_error("Loop will go bananas!");
L>    }
L>    for(int i = -n*10; i <= n*10; ++i) { 
L>        func(); 
L>    } 
L>}

L>void go_around_zero(int n)
L>{
L>    go_around_zero_v2(n, do_smth);
L>}
L>


L>Примерно все.


Моя мина-ловушка всё-таки сработала! abs(INT_MIN) даёт неопределённое поведение, если INT_MIN < -INT_MAX.

Для комплементарной арифметики abs(INT_MIN) == INT_MIN, и условие становится всегда-истинным.
После чего вылезает оптимизатор (который тоже посчитал границы цикла с неопределённым поведением) и делает там что-то очень странное. Но в тесты это не попадёт, потому что тестовое окружение задавило оптимизацию.

К>>А в это время в Виллабаджо уже делают фигак-фигак-продакшен.

L>Через 3 месяца тестеры пишут баг-репорт "падает". Смотришь в лог, видишь bananas. Пинаешь того, кто вызывал твой код.

Через три месяца Виллабаджо захватила свою долю рынка бета-версией продукта, дерзкой и как пуля резкой, а Вилларибо продолжает обкладывать арифметику тестами и/или выпускать гигантского ленивца (каждая проверка имеет свою цену в байтах и наносекундах).
Перекуём баги на фичи!
Re[8]: дебагинг vs unit-тесты vs ассерты
От: Кодт Россия  
Дата: 03.05.16 22:19
Оценка: 9 (1)
Здравствуйте, landerhigh, Вы писали:

L>Вот для того, чтобы не устраивать комбинаторный взрыв в интеграционных тестах, и нужны юнит-тесты, чтобы все возможные варианты работы юнита тестировать в изоляции. Интеграционные тесты должны тестировать интеграцию, не более.


Твоими бы тестами, да медку бы хапнуть!
Все эти "должны" — это прекрасно, а кто конкретно эти долги отдаёт?


К>>- и будешь гонять эту бомбу каждый раз при изменениях, и тогда узнаешь, что регрессия нифига не бесплатна; или перестанешь фанатеть и покроешь исключительно проблемные места; а какие места проблемные, выяснишь во время отладки.


L>Проблемные места имеют такое противное свойство, что могут возникать и после того, как их вроде бы как все "выяснили" во время отладки.


L>>>>>Не говоря уже о том, что от написания какого-то кода, зарытого глубоко под капот системы, засунутой под капот подсистемы, засунутой в подмодуль модуля основной программы, до возможности его запустить в составе этой самой программы, может пройти полгода-год.


К>>Интеграционный тест должен будет потратить столько же времени для проверки, наигрывается или нет тот самый баг. Если какой-то больной модуль ровно через год запускается.


L>Не должен. Это ответственность юнит-теста.


Только в том случае, если юнит-тестами покрыто абсолютно всё поведение модуля.
В том числе, нарушение контракта извне. То есть, у тебя есть мок, который наловчился диагностировать абсолютно все нарушения контракта, проходящие под категорией "неопределённое поведение".
Подчеркну: абсолютно все.

К>>Например, реальный баг: после нажатия на кнопку [_] окошко не сворачивается, а захватывает мышь и тупит. Что это за хрень?

К>>Ты пишешь тест: mainwindow()->minimize(). Тест проходит.

L>Ничего не пишу. От слова совсем. Тесты уже есть, и тесты позволяют исключить 100500 юзкейсов из подозрения. В данном случае, тесты гарантируют, что если сообщение доходит до обработчика бизнес-логики, то оно обрабатывается в 100% случаев. Значит, проблема либо в том, что сообщение как-то неправильно обрабатывается в нашем обработчике, либо имеем пример недокументированного поведения операционной системы. То есть вариантов вообще практически нет. Изолируем, включаем лог в обработчике и смотрим, что приходит на вход. То есть вообще ничего делать не надо. 15 минут — и WFT найден.


Да щас! "Тесты уже есть".
Тесты — это лишь ещё одна проекция предметной области на твоё понимание предметной области. (Первая проекция — это рабочий код).
Если ты чего-то недопонял, то тупо не догадаешься написать правильные тесты.

К>>А может быть, проще было не выдумывать тесты, а запустить дебаггер и Spy++, и смотреть, как диспетчится мышь?


L>Ничего выдумывать не нужно, все уже есть. Включаю лог и смотрю. Если уж совсем припрет, можно и отдалчик привлечь, как крайнюю меру.


К>>Не обманывай себя. Тесты пишет человек.

L>Хмм, а в отладчик кто смотрит, супермен, что ли?

И в отладчик смотрит человек. А человеку свойственно ошибаться, и поэтому надеяться только на тесты — это заранее согласиться на ложноотрицательную диагностику.
Как, впрочем, и одну лишь отладку...


К>>Сделал, к примеру, транспонирование матрицы на месте, но она у тебя, скажем, стреляет по памяти при НОД(x,y)=17, а стрельба сходит с рук при x<100.

К>>Да, ты написал тесты о том, что матрица транспонируется. Разумеется, только при маленьких размерах и выборочно (ну не перебирать же все x от 1 до 200 и y от 1 до 200).
К>>А что ты сделал для проверки стрельбы?

L>Во-первых, don't write shit code. Моей фантазии не хватает на то, чтобы представить, как можно в коде транспонирования матрицы случайно такой косяк допустить.


Ну давай, покажи простой и эффективный код транспонирования матрицы на месте.
https://en.wikipedia.org/wiki/In-place_matrix_transposition

Или ладно, не транспонирования, а быстрого умножения. Где ради борьбы с кешмиссами делается переупорядочивание по Мортону, в зависимости от архитектуры процессора ещё. Таких проблем, для которых есть дорогой наивный алгоритм без ошибок — тьма. Я так, навскидку про транспонирование вспомнил.

Если будешь рожать "с листа", то риск накосячить с адресной арифметикой достаточно велик.
L>Во-вторых, проверка стрельбы по памяти не входит в ответственность юнит-тестов. Я в принципе не уверен, что ее можно в общем случае обнаружить в момент собственно выстрела.

ВотЪ! Не всемогущи твои юнит-тесты. Настоящий, труъ баг они не распознают.

L>В-третьих, используя тот факт, что дважды транспонированная матрица равна самой себе, в случае подобных подозрений можно написать и автотест, который переберет все варианты и сделает это за O(x*y) время, что в 21 веке означает примерно "мгновенно".


Сколько пар (x,y) ты хочешь перебрать? В диапазоне [1..N] ты уже отхватишь O(N^4). Мгновенно...
Перекуём баги на фичи!
Re[11]: дебагинг vs unit-тесты vs ассерты
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 04.05.16 19:54
Оценка: 8 (1)
Здравствуйте, Кодт, Вы писали:

К>До какой степени детализации ты будешь обкладывать код тестами?

К>
К>void go_around_zero(int n) { for(int i = -n*10; i <= n*10; ++i) { do_smth(); } }
К>// TODO отрефакторить, чтобы покрыть тестами
К>


Уже писал рядом, но повторюсь: это вообще отвратительная провокация, и суть в том, что этот код, за счёт UB на переполнение и странной сущности внутри, не подлежит в таком виде ни тестированию, ни изучению отладчиком. Он подлежит статическому анализу и действиям по результату этого анализа.

Если гарантируется, что n<=INT_MAX/10, то проблемы в нём нет и тестировать, из-за простоты ситуации, нечего. Ну разве что вызвать с n=2 и убедиться в 41 запуске mock'а — этого будет достаточно для формальной отмазки. Можно добавить рантайм-проверку на предусловие.

Если же такой гарантии нет, то надо или обеспечить 20n+1 запусков вложенной функции (если она не зависит от i) вложенными циклами (лучше внутренний строго на 20 итераций, а внешний на n, чтобы компилятор развернул по вкусу), или (если i внутри значит) перейти на int64_t. Ну ладно, на int37_t, если такое есть или double. И тестировать только заведомо крайние случаи, которые на самом деле станут тестами гарантии окружения (что нам под видом int64_t не подсунули int32_t, и double под видом float). Всё остальное — не наше дело.

К>TEST(TenTimes, PositiveOverflow) { ASSERT_FAIL(ten_times(+INT_MAX/10 + 1)); }

К>TEST(TenTimes, NegativeOverflow) { ASSERT_FAIL(ten_times(-INT_MAX/10 — 1)); }

В случае C++ без -fwrapv или аналога сами эти тесты не имеют права выполняться как что-то полезное.

К>А в это время в Виллабаджо уже делают фигак-фигак-продакшен.


И удивляются, когда кто-то таки подаёт этому коду n=2e9.
The God is real, unless declared integer.
Re[2]: дебагинг vs unit-тесты vs ассерты
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 29.04.16 19:11
Оценка: :)
Здравствуйте, T4r4sB, Вы писали:

TB>А на самом деле никакого vs. Всё нужно.


Это если без фанатизма. Но это не варианты landerhigh'а и __kot'а
Маньяк Робокряк колесит по городу
Re[6]: дебагинг vs unit-тесты vs ассерты
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 30.04.16 02:04
Оценка: -1
Здравствуйте, landerhigh, Вы писали:

L>О, переход на личности во втором сообщении .

L>А еще нас обвиняют в фанатизме.

не буду даже спорить, ты победил
Маньяк Робокряк колесит по городу
Re[6]: дебагинг vs unit-тесты vs ассерты
От: мыщъх США http://nezumi-lab.org
Дата: 01.05.16 00:37
Оценка: +1
Здравствуйте, landerhigh, Вы писали:

L>Здравствуйте, Marty, Вы писали:



L> покрытие сценариев для определенных участков кода стремится к 100% и вероятность случаев,

покрытие сценаривев или покрытие кода? на одном из собеседований предложили прикинуть сколько нужно тестов для функции сортировки. вышло 100500 тестов и все они очень нетривиальные.

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

дальше больше. надо же как-то убедиться, что мы не пишем за пределы массива (если сортируется массив) и что нет утечек памяти (если выделяется память). плюс еще тонкости реализации чтобы не получилось так, что на 64 бит платформе при сортировке массива из двух+ миллиардов записей мы не получали кашу.

короче, получилось, что функция сортировки пишется минут за пол-часа и еще пол-года к ней пишутся тесты, причем часть тестов требуют либо низкоуровнего взаимодействия с системой (а как иначе отслеживать "промахи" по обращению мимо массива), либо спциального софта и оборудования (нужен 64-бит компилятор и система на которой можно выделить столько памяти, чтобы обнаружить, что указатели у нас 128 бит, а размер типа индекса 32 бита).

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

так что интересно как вы достигаете 100% покрытия для алгоритмов чуть-чуть сложнее чем разворот списка.

ЗЫ. разумеется, дебаггер тут не помощник, но мне странно слышать о легкости написания тестов. зачастую тесты пишет отдельный коллектив разработчиков и пишет их на фулл-тайм и по времени они занимают ну никак не меньше чем сам проект. если заглянуть в тесты того же огнелиса, то можно увидеть, что они неполные и покрывают лишь малую часть. в результате чего там постоянно находят баги и дыры.
americans fought a war for a freedom. another one to end slavery. so, what do some of them choose to do with their freedom? become slaves.
Re[7]: дебагинг vs unit-тесты vs ассерты
От: landerhigh Пират  
Дата: 01.05.16 10:20
Оценка: -1
Здравствуйте, мыщъх, Вы писали:

L>> покрытие сценариев для определенных участков кода стремится к 100% и вероятность случаев,

М>покрытие сценаривев или покрытие кода? на одном из собеседований предложили прикинуть сколько нужно тестов для функции сортировки. вышло 100500 тестов и все они очень нетривиальные.

Покрытие сценариев. Обычно три набора тестов — для верификации поведения на валидных данных, на граничных и на невалидных (проверка корректности негативного сценария).
Верификация собственно сортировки тривиальна, хотя и O(n)

М>убедиться, что если повторно сортируются уже отсортированные данные мы не получаем тормозов, выходящих за рамки требований к функции сортировки.


Это тоже можно и даже в некотором смысле элементарно, но это не совсем юнит-тест.

М>дальше больше. надо же как-то убедиться, что мы не пишем за пределы массива (если сортируется массив) и что нет утечек памяти (если выделяется память).


это в задачи юнит-тестов не входит. И, кстати, юнит-тесты для функции сортировки сразу показывают, что затея выделять память внтури оных функций — так себе идея.

М>плюс еще тонкости реализации чтобы не получилось так, что на 64 бит платформе при сортировке массива из двух+ миллиардов записей мы не получали кашу.


Тонкости реализации есть граничные случаи, которые юнит-тесты обязаны проверять, но конкретно данный случай проверять обычно смысла нет.

М>короче, получилось, что функция сортировки пишется минут за пол-часа и еще пол-года к ней пишутся тесты,


Получилось как в анекдоте про стеклянный бакен.

М>так что интересно как вы достигаете 100% покрытия для алгоритмов чуть-чуть сложнее чем разворот списка.


Достигаем обычно. Это необходимость.

М>ЗЫ. разумеется, дебаггер тут не помощник, но мне странно слышать о легкости написания тестов. зачастую тесты пишет отдельный коллектив разработчиков


Юнит-тесты всегда пишет тот же самый человек, что и тестируемый код. Без исключений.
www.blinnov.com
Re: дебагинг vs unit-тесты
От: __kot2  
Дата: 02.05.16 15:51
Оценка: +1
Здравствуйте, _hum_, Вы писали:
__>Потому вопрос в зал, может кто-нибудь, кто придерживается мнения landerhigh, на примере задачи написания функции транспонирования матрицы показать, как тесты заменяют работу с дебагером?
понадобятся 4 теста
[] -> [] // если упадет этот тест, то у вас не проверяются, понятно, пустные данные

1 -> 1  // если этот тест - выход за пределы матрицы при итерировании по ней

12 -> 1 // тут - ошибка в коде изменения размера матрицы или забыли итерироваться по столбцам
      2

12 -> 13 // если ошибка тут - то та самая ошибка с индексами или забыли итерироваться по строкам
34    24

если кажется, что этих тестов недостаточно, то приведите пример любого более-менее разумного кода транспонирования, который бы проходил эти тесты, но содержал бы опечатку или ошибку и не работал в каких-то случаях
Отредактировано 02.05.2016 16:00 __kot2 . Предыдущая версия .
Re[10]: дебагинг vs unit-тесты vs ассерты
От: Кодт Россия  
Дата: 04.05.16 11:59
Оценка: :)
Здравствуйте, landerhigh, Вы писали:

L>Я, кажется, знаю, куда эта дискуссия ведет — поскольку юнит-тесты не отлавливают расстрел памяти в сферическом случае в в вакууме, то и писать их не будем. Я такое видел 100500 раз.


Нет, пока что я вижу, куда ты тянешь дискуссию: "поскольку юнит-тесты — это круто, давайте выкинем отладчик".
Проект хромиума покрыт тестами сверху донизу, но их настолько недостаточно, что приходится иногда даже трахаться с gdb в командной строке.



К>>Ну давай, покажи простой и эффективный код транспонирования матрицы на месте.

L>Э, нет, я тут про тесты говорю, а не матирцы.

Э, ты тут спрыгиваешь с темы.
Дано: написал кривой код, который на редких условиях зримо глючит. Обкладывай его тестами для поиска бага.
Для тренировки: напиши исходно безглючный код для замороченного алгоритма.

Можешь даже отрефакторить наивный алгоритм, исходно обложенный тестами. Собственно, так оно и происходит: взяли наивную реализацию, начали переписывать на замороченную, затейливо накосячили (а старые тесты при этом выполнились).

К>>Если будешь рожать "с листа", то риск накосячить с адресной арифметикой достаточно велик.

L>Ключевое слово "риск". Привыкший работать по TDD моментально распознает эти риски. И задает себе вопрос — как я напишу тест, который проверит, накосячил ли я в данном месте? Это не всегда можно, но чаще всего — как раз можно.

До какой степени детализации ты будешь обкладывать код тестами?
void go_around_zero(int n) { for(int i = -n*10; i <= n*10; ++i) { do_smth(); } }
// TODO отрефакторить, чтобы покрыть тестами

ну поехали, что ли!
int ten_times(int x) { return x*10; }
// TODO покрыть тестами умножение на 10

void go_around_zero(int n) { for(int i = ten_times(-n), e = ten_times(n); i <= e; ++i) { do_smth(); } }
// TODO тесты для go_around_zero

начнём...
// названия макросов тестов вымышлены, по мотивам GTest.
TEST(TenTimes, ZeroIsZero)         { EXPECT_EQ(  0, ten_times( 0); }
TEST(TenTimes, PositiveIsPositive) { EXPECT_EQ(+10, ten_times(+1); }
TEST(TenTimes, NegativeIsNegative) { EXPECT_EQ(-10, ten_times(-1); }

потом, ах да, переполнение!
int ten_times(int x) {
  assert(abs(x) <= std::limits<int>::max() / 10; }
  return x*10;
}
TEST(TenTimes, PositiveOk)       { ASSERT_PASS(ten_times(+INT_MAX/10)); }
TEST(TenTimes, NegativeOk)       { ASSERT_PASS(ten_times(-INT_MAX/10)); }
TEST(TenTimes, PositiveOverflow) { ASSERT_FAIL(ten_times(+INT_MAX/10 + 1)); }
TEST(TenTimes, NegativeOverflow) { ASSERT_FAIL(ten_times(-INT_MAX/10 - 1)); }

(кстати, два UB, связанные с переполнением, я специально оставил на сладкое; причём одно добавилось в ходе написания теста...)

А в это время в Виллабаджо уже делают фигак-фигак-продакшен.
Перекуём баги на фичи!
Re[12]: дебагинг vs unit-тесты vs ассерты
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 04.05.16 18:57
Оценка: +1
Здравствуйте, landerhigh, Вы писали:

К>>Дано: написал кривой код, который на редких условиях зримо глючит. Обкладывай его тестами для поиска бага.

L>Эээ, тесты не для поиска багов. А для написания верифицируемого в контролируемых условиях кода. Почувствуйте разницу (с).

Вначале я думал этому мягко возразить, но, подумав, скорее соглашусь. Формулировка слегка коварная, но правильная. (За неё плюс и баллы, как бы я ни относился к остатку письма.)
Верификацию в нашей типичной реальности проводит в основном человек, с небольшой помощью компилятора. В некоторых случаях, как в соседнем треде про ATS, чуть больше чем небольшой, но всё равно участие человека основное.
И чтобы эта верификация проходила успешно, человеку надо помочь.

Более того, даже при машинной верификации тестирование всё равно необходимо — чтобы повысить шансы, что условия, включённые в аксиоматику верификатора, действительно истинны.

L>TEST(go_around_zero_v2, testNegativeN)


Вот тут уже начинаются нелады. Основная проблема в том, что ты поддался на откровенную провокацию Кодта. Провокация эта не в INT_MIN или в чём-то подобном; провокация в том, что ты согласился рассматривать именно его постановку задачи в виде этой функции и его разбиение кода этой функции на структурные части. А это тут не нужно. Проверка кода в таком виде или вообще не проверяется тестами, или тесты станут повторять последствия математической верификации кода; но тогда им нужно проверять не выходной скомпилированный результат, а промежуточный отчёт компилятора (gcc, например, на каком-то этапе выдаёт возможные границы значений переменных) или вообще исходный код! Это работа статического анализатора, который должен был сказать "эгегей, если у тебя n>214748364, то у тебя проблемы с UB, а если n<0, то вообще непонятно, чего ты тут хотел добиться". То есть всё равно верификатор.
Итого, лучше всего было просто отказаться рассматривать в таком виде.

L> unsigned int expected = n*10*2+1; // Ну или 100500 от балды, чтобы просто отловить бесконечный цикл


Так не отловишь же — оно может захотеть вообще ничего не делать. Да и в реальности бесконечного цикла не будет при любом из возможных переполнений.
Пока что, если ты пишешь тест, уже зная про потенциальную возможность UB, тебе нет смысла писать тест именно на UB именно потому, что UB непредсказуемо и поэтому заведомо за рамками тестирования.
(Разве что апостериорного статистического, но это совсем другой разговор.)

Вот если бы речь шла про C# и checked context, или аналогичное средство, где переполнение гарантируется, можно было бы его явно проверить только зачем? И без нас есть кому проверить компилятор

К>>А в это время в Виллабаджо уже делают фигак-фигак-продакшен.

L>)
L>Через 3 месяца тестеры пишут баг-репорт "падает". Смотришь в лог, видишь bananas. Пинаешь того, кто вызывал твой код.

Всё-таки в контрактах это уместнее.

L>До приемлимой.


На всякий случай — "приемлемой".
The God is real, unless declared integer.
Отредактировано 05.05.2016 3:58 netch80 . Предыдущая версия .
Re: дебагинг vs unit-тесты
От: К Тёте  
Дата: 29.04.16 18:11
Оценка:
__>ну, простейший пример — вам сказал тим лид — нужно написать функцию, транспонирующую матрицу. как в этом случае будет выглядеть работа с тестами без дебагера?" последовали опять общие фразы.

А зачем там дебаггер? Дебаггер позволяет за очень длительное время проверить ровно один тест кейс вручную. Вернее, ровно один тест-кейс с tracing'ом

L>>Ты мне лучше скажи, зачем тут вообще отладчик может понадобиться?


__>ответ:

__>так а где здесь код-то самого транспонирования? ведь ошибки именно там кроются. и если вы увидите срабатывание ассертов, что делать дальше будете?

Править код, естественно

__>как, например, обнаружите глупейшую ошибку типа Matrix[i][j] = Matrix[j][i] для in-place трансполнирования квадратной матрицы?



Иногда, в редчайших случаях можно запустить дебаггер. В наиредчайших.

Тест может/должен выявить, что ошибка проявляется на inplace транспонировании квадратных матриц. Смотрим в код, находим ошибку
На волю, в пампасы!
Re[2]: дебагинг vs unit-тесты
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 29.04.16 19:08
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Дебаг — это творческий процесс создания теста.

К>Делаем всё по шагам и вручную, смотрим, что там хорошо и что плохо, запоминаем, а потом насуём в код недостающие формальные контракты, ровно в нужном количестве.

Абсолютно согласен. Я обычно поначалу в дебагере несколько раз прохожу, и выясняю в процессе этого, какие варианты я не учел, и оперативно добавляю их в код. Если писать юнит тесты и краевые условия выявлять по ним, то чтобы нормально всё сделать вместо получаса уйдет рабочий день

К>Или, скажем, если какая-то проверка отвалилась, значит, неопределённое поведение возникло до неё. И потребуется отследить предысторию, хотя бы в объёме стека вызовов. А как ты напишешь новый нужный тест, если не проанализируешь стек?


Ну, можно каждую строчку алгоритма выделять например, в отдельную функцию, и обкладывать её тестами, но это на пару порядков больше работы.
Юнит тесты хороши для регрессионного тестирования, когда контракты кода уже зафиксированы и требуется чтобы ничего не поломалось при возможном последующем рефакторинге. Как их использовать при разработке, когда контракты не определены и уточняются, я

Было бы интересно посмотреть на какой-нибудь простой пример разработки через тесты, подробно описаный, если свидетели иеговыюнит-тестирования не сочтут за труд предоставить такой
Маньяк Робокряк колесит по городу
Re[3]: дебагинг vs unit-тесты vs ассерты
От: landerhigh Пират  
Дата: 30.04.16 00:45
Оценка:
Здравствуйте, Marty, Вы писали:

M>Это если без фанатизма. Но это не варианты landerhigh'а и __kot'а


Видишь ли... когда приходится писать на разных платформах, приходит понимание того, что отладчик — вещь платформеннозависимая. И навыки работы со студийным отладчиком никак не помогут в случае gdb, а если код вообще собирается для Соляриса или какого-нибудь AIX, то получается еще смешнее. Непереносимый навык, в общем. Это одна из причин, по которой я не пользуюсь отладчиком.
Но главная причина состоит в том, что отладчик как инструмент разработки контрпродуктивен. Для проведения вскрытия (разбора крешдампа) — вещь отличная, но использовать его при разработке?

Делать первый прогон под отладчиком, чтобы выяснить, какие варианты не учтены? Ну не знаю, мне для этого отладчик не нужен, и вообще звучит странно. Не говоря уже о том, что от написания какого-то кода, зарытого глубоко под капот системы, засунутой под капот подсистемы, засунутой в подмодуль модуля основной программы, до возможности его запустить в составе этой самой программы, может пройти полгода-год.
www.blinnov.com
Re[3]: дебагинг vs unit-тесты
От: landerhigh Пират  
Дата: 30.04.16 00:58
Оценка:
Здравствуйте, Marty, Вы писали:

M>Если писать юнит тесты и краевые условия выявлять по ним, то чтобы нормально всё сделать вместо получаса уйдет рабочий день


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

При правильной организации работы полный набор тестов для нового тестируемого юнита пишется за очень короткое время. Практически на автомате. Этого времени не хватит даже на однократный запуск отладчика.

К>>Или, скажем, если какая-то проверка отвалилась, значит, неопределённое поведение возникло до неё. И потребуется отследить предысторию, хотя бы в объёме стека вызовов. А как ты напишешь новый нужный тест, если не проанализируешь стек?


M>Ну, можно каждую строчку алгоритма выделять например, в отдельную функцию, и обкладывать её тестами, но это на пару порядков больше работы.


Это смотря что ставить целью

M>Было бы интересно посмотреть на какой-нибудь простой пример разработки через тесты, подробно описаный, если свидетели иеговыюнит-тестирования не сочтут за труд предоставить такой


На пример (результат) смотреть бессмысленно. Смотреть нужно на процесс достижения этого результата. Как сделать это на форуме —
www.blinnov.com
Re[2]: дебагинг vs unit-тесты
От: landerhigh Пират  
Дата: 30.04.16 01:03
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Или, скажем, если какая-то проверка отвалилась, значит, неопределённое поведение возникло до неё. И потребуется отследить предысторию, хотя бы в объёме стека вызовов. А как ты напишешь новый нужный тест, если не проанализируешь стек?


Методом исключения, к примеру.
www.blinnov.com
Re: дебагинг vs unit-тесты
От: landerhigh Пират  
Дата: 30.04.16 01:31
Оценка:
Здравствуйте, _hum_, Вы писали:

__>так а где здесь код-то самого транспонирования?


Он не нужен. Нужен код тестов.

__>ведь ошибки именно там кроются. и если вы увидите срабатывание ассертов, что делать дальше будете?


Покупать акции Газпрома, а что еще можно сделать?
Разбираться, конечно же, в причинах срабатывания ассертов.

__>как, например, обнаружите глупейшую ошибку типа Matrix[i][j] = Matrix[j][i] для in-place трансполнирования квадратной матрицы?


Вы не поверите...
Предположим, что у нас и правда завелась функция

void transposeInPlace(Matrix& mtx);


пишем тест для нее. Естественно, предполагается, что у нас есть оператор сравнения двух матриц.

TEST(transposeMatrix, inPlace)
{
    Matrix mtx = 
        {1 2 3
        4 5 6
        7 8 9};    // Псевдокод, вестимо
    Matrix expected = 
        {1 4 7
        2 5 8
        3 6 9}; // Транспонировали вручную, на клочке бумажки
    transposeInPlace(mtx);
    ASSERT_EQ(expected, mtx);  // Глупая ошибка не пройдет


Кстати, сигнатура

Matrix transpose(const Matrix& original);


подразумевает, что явного in-place транспонирования не предусмотрено. RVO тоже не сделает это транспонирование in-place, но мы все слышали про странные глюки в компиляторах, поэтому придумаем такое, чисто для очистки совести

TEST(transposeMatrix, testThatShouldNotExist)
{
    Matrix mtx = ...; // Тут инициализация квадратной матрицы
    Matrix expected = ...; // Вручную инициализированная транспонированная матрица
    mtx = transpose(mtx);
    ASSERT_EQ(expected, mtx);
www.blinnov.com
Отредактировано 30.04.2016 1:56 landerhigh . Предыдущая версия .
Re[4]: дебагинг vs unit-тесты vs ассерты
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 30.04.16 01:38
Оценка:
Здравствуйте, landerhigh, Вы писали:

M>>Это если без фанатизма. Но это не варианты landerhigh'а и __kot'а


L>Видишь ли... когда приходится писать на разных платформах, приходит понимание того, что отладчик — вещь платформеннозависимая. И навыки работы со студийным отладчиком никак не помогут в случае gdb, а если код вообще собирается для Соляриса или какого-нибудь AIX, то получается еще смешнее. Непереносимый навык, в общем. Это одна из причин, по которой я не пользуюсь отладчиком.


Видишь ли, когда набираешься опыта, то начинаешь понимать, что windbg по удобству хоть и превосходит gdb, но не ушел от него далеко. Тут я спорить даже не буду, я согласен, что гораздо проще написать 100500 юнит-тестов, чем пытаться отлаживаться при помощи gdb. И я, по возможности, избегаю использования gdb. Просто я пишу кроссплатформенный код, который, как минимум, работает под виндой, линуксами и фряхами, и отлаживаю его в студии msvc, и мне этого хватает. А для отладки платформозависимых вещей мне хватает printf'ов.

Так что навык вполне переносимый — никто не мешает писать и отлаживать в студии переносимый код, который будет потом работать и под линуксом, и под аиксом


L>Но главная причина состоит в том, что отладчик как инструмент разработки контрпродуктивен. Для проведения вскрытия (разбора крешдампа) — вещь отличная, но использовать его при разработке?


Использование отладчика позволяет не писать 100500 тестов, которые после устаканивания контрактов интерфейсов будут выкинуты или переписаны, по мере изменения контраков


L>Делать первый прогон под отладчиком, чтобы выяснить, какие варианты не учтены? Ну не знаю, мне для этого отладчик не нужен, и вообще звучит странно.


Ну, ты молодец, если умеешь сразу всё предусмотреть в уме


L>Не говоря уже о том, что от написания какого-то кода, зарытого глубоко под капот системы, засунутой под капот подсистемы, засунутой в подмодуль модуля основной программы, до возможности его запустить в составе этой самой программы, может пройти полгода-год.


И? Ты пишешь тесты, которые проверяют контракты модуля. Нормально. Я тоже так делаю. Просто когда что-то не работает в твоем алгоритме в соответсвии с контрактом, ты пишешь 100500 тестов, а я прохожу эту функцию под отладчиком, и смотрю, на каком шаге и что пошло не так. Я за пол-часа справился, а ты уже вторую неделю пишешь юнит-тесты. Не, если тебя на твоей работе терпят с таким подходом — ок, я не хочу занять твоё место, сиди там и дальше, пиши юнит-тесты. Пока я пишу продукт.
Маньяк Робокряк колесит по городу
Re[4]: дебагинг vs unit-тесты
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 30.04.16 01:52
Оценка:
Здравствуйте, landerhigh, Вы писали:

M>>Если писать юнит тесты и краевые условия выявлять по ним, то чтобы нормально всё сделать вместо получаса уйдет рабочий день


L>Краевые условия по тестам не выявляются. Они выявляются в процессе написания собственно кода, а юнит тесты пишутся для проверки правильности обработки этих краевых условий.


Интересно, как ты выявляшь краевые условия в процессе написания кода. Расскажи об этом поподробнее. Ты всё сразу продумываешь? И попадаешь в точку? Я так не умею. Я обычно подаю что-то на вход, ожидаю что-то на выходе. Если на входе не то, начинаю в отлпдчике по шпгпи проверять, где косяк


L>Про полчаса и рабочий день насмешил. Обычно наоборот, полдня в дебаггерре, потом еще полдня на кое-какерство в коде, ибо после нескольких часов в отладчике так и не приходит понимание, чего же там не так. Или приходит, и оказывается, что код нужно почти полностью переписать, но становится жалко усилий и начинается костылестроение.


При юнит-тестировании говнокод из эскизного концепт-проекта не надо переписывать?


L>При правильной организации работы полный набор тестов для нового тестируемого юнита пишется за очень короткое время. Практически на автомате. Этого времени не хватит даже на однократный запуск отладчика.


Ну, отладка у меня секунд за 5 запускается. Не пойму, какой набор тестов можно написать за это время. Или я просто не в курсе правильной организации?


К>>>Или, скажем, если какая-то проверка отвалилась, значит, неопределённое поведение возникло до неё. И потребуется отследить предысторию, хотя бы в объёме стека вызовов. А как ты напишешь новый нужный тест, если не проанализируешь стек?


M>>Ну, можно каждую строчку алгоритма выделять например, в отдельную функцию, и обкладывать её тестами, но это на пару порядков больше работы.


L>Это смотря что ставить целью


M>>Было бы интересно посмотреть на какой-нибудь простой пример разработки через тесты, подробно описаный, если свидетели иеговыюнит-тестирования не сочтут за труд предоставить такой


L>На пример (результат) смотреть бессмысленно. Смотреть нужно на процесс достижения этого результата. Как сделать это на форуме —


Результат как раз не пример. Нужно расписать процесс, все шаги от и до. На форуме это делается просто — берешь и рассказываешь. Последовательно, что и как делал, что на входе, каков результат.
Маньяк Робокряк колесит по городу
Re[5]: дебагинг vs unit-тесты vs ассерты
От: landerhigh Пират  
Дата: 30.04.16 17:05
Оценка:
Здравствуйте, netch80, Вы писали:

N>Как минимум ещё случаи

N>1. Странности работы платформы/фреймворка/etc., не документированные, плохо описанные, нарушающие документацию, etc.

В большинстве случаев их тоже можно протестировать автоматическими тестами. Более того, бывают (я вообще подозреваю, что чаще всего встречаются) такие странности, которые под отладчиком в принципе не отлавливаются и не повторяются. Более того, зачастую иначе, как синтетическим тестом, многие странности даже и не повторить.
У меня был эпичный случай неведомого бага из-за недодокументирования, под отладчиком не повторяющийся, и он привел к фиксу в boost::context

N>2. Сходно с первым, но другая вводная — оОбучение новым языкам/фреймворкам — когда надо прочувствовать происходящее в виде последовательности состояний и разницы между ними. Где что за что цепляется, где на каком шаге что успело измениться...


Только отладчик при этом — это тот самый противогаз, который нужно одевать, когда стоя и в гамаке.

N>тесты на всё это ты не нарисуешь, потому что в принципе не представляешь, что проверять, или почему оно не работает, несмотря ни на что.


L>>Делать первый прогон под отладчиком, чтобы выяснить, какие варианты не учтены? Ну не знаю, мне для этого отладчик не нужен, и вообще звучит странно. Не говоря уже о том, что от написания какого-то кода, зарытого глубоко под капот системы, засунутой под капот подсистемы, засунутой в подмодуль модуля основной программы, до возможности его запустить в составе этой самой программы, может пройти полгода-год.

N>Увы. И когда тогда оказывается, что одна из самых базовых архитектурных идей была нерабочей...

А кто-то покрывал тестами все модули с самого начала и никакого "оказалось" не случилось — косяк был выявлен на ранней стадии, а скорее всего даже и не случился, так как на неудачную архитектуру и тесты плохо ложатся.

N>именно поэтому agile (поклон соседнему треду) хорош тем, что там возможность запустить "в составе этой самой программы" с самого начала делается как можно раньше, чтобы найти все подводные грабли.


Да, это особенно помогает, когда пишется абсолютно новый коммуникационный уровень для поддержки нового устройства, которого в железе еще не существует. Не говоря уже о том, что ручное тестирование никогда не найдет все подводные грабли.
www.blinnov.com
Re[7]: дебагинг vs unit-тесты vs ассерты
От: landerhigh Пират  
Дата: 30.04.16 18:26
Оценка:
Здравствуйте, Кодт, Вы писали:

К>А интеграционные тесты — или ты устроишь комбинаторный взрыв, покрывая все мыслимые сочетания,


Вот для того, чтобы не устраивать комбинаторный взрыв в интеграционных тестах, и нужны юнит-тесты, чтобы все возможные варианты работы юнита тестировать в изоляции. Интеграционные тесты должны тестировать интеграцию, не более.

К>- и будешь гонять эту бомбу каждый раз при изменениях, и тогда узнаешь, что регрессия нифига не бесплатна; или перестанешь фанатеть и покроешь исключительно проблемные места; а какие места проблемные, выяснишь во время отладки.


Проблемные места имеют такое противное свойство, что могут возникать и после того, как их вроде бы как все "выяснили" во время отладки.

L>>>>Не говоря уже о том, что от написания какого-то кода, зарытого глубоко под капот системы, засунутой под капот подсистемы, засунутой в подмодуль модуля основной программы, до возможности его запустить в составе этой самой программы, может пройти полгода-год.


К>Интеграционный тест должен будет потратить столько же времени для проверки, наигрывается или нет тот самый баг. Если какой-то больной модуль ровно через год запускается.


Не должен. Это ответственность юнит-теста.

К>Например, реальный баг: после нажатия на кнопку [_] окошко не сворачивается, а захватывает мышь и тупит. Что это за хрень?

К>Ты пишешь тест: mainwindow()->minimize(). Тест проходит.

Ничего не пишу. От слова совсем. Тесты уже есть, и тесты позволяют исключить 100500 юзкейсов из подозрения. В данном случае, тесты гарантируют, что если сообщение доходит до обработчика бизнес-логики, то оно обрабатывается в 100% случаев. Значит, проблема либо в том, что сообщение как-то неправильно обрабатывается в нашем обработчике, либо имеем пример недокументированного поведения операционной системы. То есть вариантов вообще практически нет. Изолируем, включаем лог в обработчике и смотрим, что приходит на вход. То есть вообще ничего делать не надо. 15 минут — и WFT найден.

К>А может быть, проще было не выдумывать тесты, а запустить дебаггер и Spy++, и смотреть, как диспетчится мышь?


Ничего выдумывать не нужно, все уже есть. Включаю лог и смотрю. Если уж совсем припрет, можно и отдалчик привлечь, как крайнюю меру.

К>Не обманывай себя. Тесты пишет человек.


Хмм, а в отладчик кто смотрит, супермен, что ли?

K>Вот насколько у тебя хватило фантазии покрыть предметную область, настолько ты и написал тесты.


Особой фантазии для хорошего покрытия не требуется. Требуется желание разбивать модули так, чтобы свести домены вариантов использования к минимуму и покрывать их в принципе стандартным способом.

К>Сделал, к примеру, транспонирование матрицы на месте, но она у тебя, скажем, стреляет по памяти при НОД(x,y)=17, а стрельба сходит с рук при x<100.

К>Да, ты написал тесты о том, что матрица транспонируется. Разумеется, только при маленьких размерах и выборочно (ну не перебирать же все x от 1 до 200 и y от 1 до 200).
К>А что ты сделал для проверки стрельбы?

Во-первых, don't write shit code. Моей фантазии не хватает на то, чтобы представить, как можно в коде транспонирования матрицы случайно такой косяк допустить.
Во-вторых, проверка стрельбы по памяти не входит в ответственность юнит-тестов. Я в принципе не уверен, что ее можно в общем случае обнаружить в момент собственно выстрела.
В-третьих, используя тот факт, что дважды транспонированная матрица равна самой себе, в случае подобных подозрений можно написать и автотест, который переберет все варианты и сделает это за O(x*y) время, что в 21 веке означает примерно "мгновенно".
www.blinnov.com
Отредактировано 01.05.2016 10:12 landerhigh . Предыдущая версия .
Re[5]: дебагинг vs unit-тесты
От: landerhigh Пират  
Дата: 30.04.16 19:03
Оценка:
Здравствуйте, Marty, Вы писали:

L>>Краевые условия по тестам не выявляются. Они выявляются в процессе написания собственно кода, а юнит тесты пишутся для проверки правильности обработки этих краевых условий.


M>Интересно, как ты выявляшь краевые условия в процессе написания кода. Расскажи об этом поподробнее. Ты всё сразу продумываешь? И попадаешь в точку? Я так не умею. Я обычно подаю что-то на вход, ожидаю что-то на выходе. Если на входе не то, начинаю в отлпдчике по шпгпи проверять, где косяк


Что значит "продумываешь"? Я пишу код. Я знаю, как он будет работать, я знаю, какие у него будут краевые условия. А можно как-то иначе писать код?
Ну ладно, код пишут человеки, которые все на свете знать не могут, и код этот часто вызывает другой код, который вообще является черным ящиком. Вот вызываешь ты из своего кода функцию A, но не уверен, что знаешь, как она себя ведет при подаче на вход определенных данных, которые твой код может выдать. Документация далека от исчерпывающей. Пишешь тест, который именно такие условия и проверяет, и вопрос решается раз и навсегда.

M>При юнит-тестировании говнокод из эскизного концепт-проекта не надо переписывать?


Не знаю. Практически все мои прототипы становились основой для production code, т.к. писать говнокод при использовании TDD и его аналогов получается плохо.

L>>При правильной организации работы полный набор тестов для нового тестируемого юнита пишется за очень короткое время. Практически на автомате. Этого времени не хватит даже на однократный запуск отладчика.


M>Ну, отладка у меня секунд за 5 запускается. Не пойму, какой набор тестов можно написать за это время. Или я просто не в курсе правильной организации?


Отладка чего? Что именно ты запускашь? Рабочую систему? Так ее после этих 5 секунд нужно еще полчаса вручную докликать до состояния, в котором можно что-то проверить. И что делать, если до самой возможности запуска рабочей системы должно пройти еще три человеко-года? Что делаешь, если нужно протестировать ситуацию, которая происходит лишь в определенных условиях, которые под отладчиком не воспроизводятся или требуют определенного, возможно, не совсем корректного поведения внешней системы?
Или ты запускаешь специально написанные тестовые программки, реализующие разные варианты работы?

M>>>Было бы интересно посмотреть на какой-нибудь простой пример разработки через тесты, подробно описаный, если свидетели иеговыюнит-тестирования не сочтут за труд предоставить такой


Ну так я в соседней ветки описывал, как разрабатывается транспонирование матрицы с использованием тестов. Маловато?
www.blinnov.com
Re[6]: дебагинг vs unit-тесты vs ассерты
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 30.04.16 19:15
Оценка:
Здравствуйте, landerhigh, Вы писали:

N>>Как минимум ещё случаи

N>>1. Странности работы платформы/фреймворка/etc., не документированные, плохо описанные, нарушающие документацию, etc.
L>В большинстве случаев их тоже можно протестировать автоматическими тестами. Более того, бывают (я вообще подозреваю, что чаще всего встречаются) такие странности, которые под отладчиком в принципе не отлавливаются и не повторяются. Более того, зачастую иначе, как синтетическим тестом, многие странности даже и не повторить.

То в основном всякие темпоральные гейзенбаги. Я имел в виду случаи попроще. Есть цепочка вызовов API, где последние используют результат первых, что-то нарушено, единственный способ выяснить — всунуться между ними, а разносить в отдельные функции смысла нет аж совсем.

N>>2. Сходно с первым, но другая вводная — оОбучение новым языкам/фреймворкам — когда надо прочувствовать происходящее в виде последовательности состояний и разницы между ними. Где что за что цепляется, где на каком шаге что успело измениться...

L>Только отладчик при этом — это тот самый противогаз, который нужно одевать, когда стоя и в гамаке.

?

N>>Увы. И когда тогда оказывается, что одна из самых базовых архитектурных идей была нерабочей...

L>А кто-то покрывал тестами все модули с самого начала и никакого "оказалось" не случилось — косяк был выявлен на ранней стадии, а скорее всего даже и не случился, так как на неудачную архитектуру и тесты плохо ложатся.

В одном проекте изначальное решение понадеяться на саморегулирование нагрузочной способности под постоянным потоком данных оказалось самой большой ошибкой и вылечилось, фактически, переходом на поллинг вместо пуша. Такое тестами не выловится при любом покрытии "с самого начала".

N>>именно поэтому agile (поклон соседнему треду) хорош тем, что там возможность запустить "в составе этой самой программы" с самого начала делается как можно раньше, чтобы найти все подводные грабли.

L>Да, это особенно помогает, когда пишется абсолютно новый коммуникационный уровень для поддержки нового устройства, которого в железе еще не существует. Не говоря уже о том, что ручное тестирование никогда не найдет все подводные грабли.

Я и не предлагал отменять юнит-тестирование
The God is real, unless declared integer.
Re[7]: дебагинг vs unit-тесты vs ассерты
От: landerhigh Пират  
Дата: 30.04.16 19:21
Оценка:
Здравствуйте, netch80, Вы писали:

N>То в основном всякие темпоральные гейзенбаги.


Ооо, да. Крови попили изрядно.

N>Я имел в виду случаи попроще. Есть цепочка вызовов API, где последние используют результат первых, что-то нарушено, единственный способ выяснить — всунуться между ними, а разносить в отдельные функции смысла нет аж совсем.


Что-то не совсем понял, в чем тут проблема?

N>>>2. Сходно с первым, но другая вводная — оОбучение новым языкам/фреймворкам — когда надо прочувствовать происходящее в виде последовательности состояний и разницы между ними. Где что за что цепляется, где на каком шаге что успело измениться...

L>>Только отладчик при этом — это тот самый противогаз, который нужно одевать, когда стоя и в гамаке.
N>?

Ну неудобно!

N>В одном проекте изначальное решение понадеяться на саморегулирование нагрузочной способности под постоянным потоком данных оказалось самой большой ошибкой и вылечилось, фактически, переходом на поллинг вместо пуша. Такое тестами не выловится при любом покрытии "с самого начала".


Хех. Мы в подобных случаях ставили подобному риску самую высокую оценку и планировали нагрузочное тестирование на самый первый спринт, в котором это было бы возможно. То есть необходимо было обеспечить минимальный уровень бизнес-логики с кучей моков и стабов, но позволяющий протестировать бутылочные горлышки. Но это не задача юнит-тестов.

N>>>именно поэтому agile (поклон соседнему треду) хорош тем, что там возможность запустить "в составе этой самой программы" с самого начала делается как можно раньше, чтобы найти все подводные грабли.

L>>Да, это особенно помогает, когда пишется абсолютно новый коммуникационный уровень для поддержки нового устройства, которого в железе еще не существует. Не говоря уже о том, что ручное тестирование никогда не найдет все подводные грабли.
N>Я и не предлагал отменять юнит-тестирование

www.blinnov.com
Re[8]: дебагинг vs unit-тесты vs ассерты
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 30.04.16 19:27
Оценка:
Здравствуйте, landerhigh, Вы писали:

N>>Я имел в виду случаи попроще. Есть цепочка вызовов API, где последние используют результат первых, что-то нарушено, единственный способ выяснить — всунуться между ними, а разносить в отдельные функции смысла нет аж совсем.


L>Что-то не совсем понял, в чем тут проблема?


Проблемы нет — тогда, когда ты влезаешь между вызовами и смотришь, что же там реально передаётся между ними. А это или отладчик, или пересборка с логгингом.
Отладчик обычно проще, по крайней мере на один раз.

N>>>>2. Сходно с первым, но другая вводная — оОбучение новым языкам/фреймворкам — когда надо прочувствовать происходящее в виде последовательности состояний и разницы между ними. Где что за что цепляется, где на каком шаге что успело измениться...

L>>>Только отладчик при этом — это тот самый противогаз, который нужно одевать, когда стоя и в гамаке.
N>>?
L>Ну неудобно!

Для данных целей — не мешает.

N>>В одном проекте изначальное решение понадеяться на саморегулирование нагрузочной способности под постоянным потоком данных оказалось самой большой ошибкой и вылечилось, фактически, переходом на поллинг вместо пуша. Такое тестами не выловится при любом покрытии "с самого начала".

L>Хех. Мы в подобных случаях ставили подобному риску самую высокую оценку и планировали нагрузочное тестирование на самый первый спринт, в котором это было бы возможно. То есть необходимо было обеспечить минимальный уровень бизнес-логики с кучей моков и стабов, но позволяющий протестировать бутылочные горлышки. Но это не задача юнит-тестов.

Ну вот потому я и говорю про то, что надо собрать как можно раньше.
И слово "спринт", которое ты тут применил, из мира agile.

А так — в этой части у нас различие позиций только в акцентах.
The God is real, unless declared integer.
Re[9]: дебагинг vs unit-тесты vs ассерты
От: landerhigh Пират  
Дата: 30.04.16 21:02
Оценка:
Здравствуйте, netch80, Вы писали:


L>>Что-то не совсем понял, в чем тут проблема?

N>Проблемы нет — тогда, когда ты влезаешь между вызовами и смотришь, что же там реально передаётся между ними. А это или отладчик, или пересборка с логгингом.
N>Отладчик обычно проще, по крайней мере на один раз.

В смысле, зачем смотреть, что передается между вызовами стороннего API?

L>>Ну неудобно!

N>Для данных целей — не мешает.

Да и не помогает особо.

L>>Хех. Мы в подобных случаях ставили подобному риску самую высокую оценку и планировали нагрузочное тестирование на самый первый спринт, в котором это было бы возможно. То есть необходимо было обеспечить минимальный уровень бизнес-логики с кучей моков и стабов, но позволяющий протестировать бутылочные горлышки. Но это не задача юнит-тестов.


N>Ну вот потому я и говорю про то, что надо собрать как можно раньше.


Вовсе не обязательно собирать аппликуху. Вполне можно собрать функциональный тест на основе юнит-тест фреймворка.

N>И слово "спринт", которое ты тут применил, из мира agile.


Это просто способ отслеживать прогресс и упорядочивать хаос, в переводе на человеческий язык звучит как "сделать первым делом".
www.blinnov.com
Re[2]: дебагинг vs unit-тесты
От: Stanislav V. Zudin Россия  
Дата: 30.04.16 22:56
Оценка:
Здравствуйте, landerhigh, Вы писали:
L>Предположим, что у нас и правда завелась функция
L>пишем тест для нее. Естественно, предполагается, что у нас есть оператор сравнения двух матриц.
...

L>
L>TEST(transposeMatrix, testThatShouldNotExist)
L>{
L>    Matrix mtx = ...; // Тут инициализация квадратной матрицы
L>    Matrix expected = ...; // Вручную инициализированная транспонированная матрица
L>    mtx = transpose(mtx);
L>    ASSERT_EQ(expected, mtx);
L>


Я думал, что у тебя есть секретная комба - ломик в рукаве, поэтому у тебя получается, а у меня — ни фига.
А оказывается, я делаю всё то же самое.

Ты вот говоришь, что код, мол, неправильный.

SVZ>Не всегда просто и нифига не быстро.
Если не просто и не быстро, то вы либо не понимаете свой собственный код, либо алгоритм, либо предметную область. Значит, нужно заполнять пробелы.
SVZ>В моей практике это сложно и геморройно. И еще объемно.
Хороший индиактор того, что тестируемый код пора дробить на части.


А я думаю, что проблема не в коде, а в прикладной области и в решаемых задачах.
Есть алгоритмы, у которых на вход подается крошечный набор данных и легко проверить результат.
В качестве примера — Poco (недавно ковырял). Там, действительно все тесты по 5-10 строк. Такие тесты можно клепать по дюжине в час.

Вот если взять твой пример с матрицей и спроецировать его, скажем, на построение адаптивной сетки (adaptive mesh refinement — можно погуглить картинки), то что у нас получается.

Вот эта инициализация:
L> Matrix mtx = ...; // Тут инициализация квадратной матрицы
L> Matrix expected = ...; // Вручную инициализированная транспонированная матрица

Может занимать до сотни строк.
И еще столько же — проверка результатов.

Ну и тестов надо написать хренову тучу.

Что придется тестировать:
Случай 1. Сетка из одной ячейки.
Подготовка данных занимает одну строчку кода, затем надо проверить, что все 6 граней инцидентны "воздуху".
Теперь ячейку надо рассечь.
Варианты сечения: по осям X, Y, Z, X+Y, X+Z, Y+Z, X+Y+Z. Итого 7 вариантов сечения. В результате получается от 2 до 8 ячеек.
Теперь нужно найти положение ячеек относительно соседей (не одна строчка). Затем проверить, что на периферии ячейки по-прежнему инцидентны воздуху, а внутри соединены между собой.

Случай 2. Добавляем соседей. Строим однослойную структуру 3х3х1. Выбираем центральную ячейку.
Тут одной строчкой кода уже не обойдешься. И снова прогоняем все варианты сечения, и проверяем результаты. Но тут результаты уже совсем другие, поэтому и проверку результатов надо писать совсем другую.

Случай 3. Сверху и снизу добавляем ячеек — получаем "кубик" 3х3х3 ячеек. Снова ищем центральную ячейку и все по новой. Сложность проверки возрастает, т.к. любое (почти любое) сечение затрагивает соседей в нескольких слоях.

Случай 4. Тут самое интересное. Т.к. каждая ячейка своей гранью может соприкасаться только с 0, 1, 2 и 4 ячейками, то при определенных сечениях возникает конфликт, чтобы его разрешить необходимо подразбить соседа — начинается лавинообразный процесс. Как тут писать тест — у меня фантазии уже нет.

Итого. Чтобы запрограммировать алгоритм потребуется 1-2 недели и еще пара недель, как минимум, чтобы обложить его тестами, от которых будет хоть какой-то прок.
Что интересно, когда код будет отлажен никто в него не полезет. А если полезет, то это означает, что предстоят очень серьезные изменения, которые могут перекроить все структуры данных и тогда написанные тесты пойдут псу под хвост.

Как в таких условиях извлечь из юнит-тестов какую-то пользу — я пока не представляю.
_____________________
С уважением,
Stanislav V. Zudin
Re[3]: дебагинг vs unit-тесты
От: landerhigh Пират  
Дата: 30.04.16 23:36
Оценка:
Здравствуйте, Stanislav V. Zudin, Вы писали:

SVZ>Я думал, что у тебя есть секретная комба - ломик в рукаве, поэтому у тебя получается, а у меня — ни фига.

SVZ>А оказывается, я делаю всё то же самое.

Секретный ломик состоит в том, чтобы понять, что тест должен быть крайне простым. Из этого отрастают весьма любопытные вещи.

SVZ>Может занимать до сотни строк.

SVZ>И еще столько же — проверка результатов.

Так бывает, когда неверно выбран базис, к которому применяется тест.

SVZ>Ну и тестов надо написать хренову тучу.


Показатель того, что что-то делается неправильно.
Ведь это также означает, что для ручной верификации придется сидеть в отладчике годами.

SVZ>Что интересно, когда код будет отлажен никто в него не полезет. А если полезет, то это означает, что предстоят очень серьезные изменения, которые могут перекроить все структуры данных и тогда написанные тесты пойдут псу под хвост.


Это лишь означает, что и тесты и код были изначально написаны неправильно. Хорошие тесты даже при серьезных изменениях кода либо не меняются вообще, либо незначительно, так как они тестируют контракты, то есть видимое поведение.
В области 3D графики я чилийский лох, и у меня нет времени на то, чтобы в ней разбираться лишь для того, чтобы предоставить пример правильного подхода к использованию тестов в разработки кода в этой области. Но если там используются стабильные алгоритмы, то нет никаких причин, по которым там не получится эффективно использовать юнит тесты.

SVZ>Как в таких условиях извлечь из юнит-тестов какую-то пользу — я пока не представляю.


Для начала нужно определиться, какую пользу хочется извлечь. В нашей секте юнит-тесты
1. Помогают разработать наиболее эффективный и удобный контракт кода. Если что-то неудобно тестировать, то и использовать это тоже будет неудобно
2. Помогают проводить раннюю валидацию алгоритмов. Ни железа, ни системы еще нет, а бизнес-логика у нас уже работает согласно спецификациям.
3. Абсолютно незаменимы при итеративной разработке, так как выявляют breaking changes. Тут без комментариев.
4. Помогают в рефакторинге по той же причине.

Все вместе это приводит к парадоксальным результатам — кода в итоге пишется меньше, и времени на его написание тоже третбуется меньше.

И еще заметил такую вещь — если разработчик не понимает, как он будет тестировать свой собственный код, это означает, что он не понимает, что делает.
www.blinnov.com
Re[4]: дебагинг vs unit-тесты vs ассерты
От: мыщъх США http://nezumi-lab.org
Дата: 01.05.16 00:18
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>Здравствуйте, Marty, Вы писали:


M>>Это если без фанатизма. Но это не варианты landerhigh'а и __kot'а

L>Видишь ли... когда приходится писать на разных платформах, приходит понимание того, что отладчик — вещь платформеннозависимая.
строго говоря, нет. на определенном уровне абстракции различия исчезают.

L> И навыки работы со студийным отладчиком никак не помогут в случае gdb,

еще скажите что навыки написания кода в студии никак не могут, когда под рукой только x-code и text mate.

L> Непереносимый навык, в общем.

научившись отлаживать резидентов в дос я могу отлаживать что угодно и чем угодно. даже если это NEC V850 и микроконтроллер для автомобиля.

L> Но главная причина состоит в том, что отладчик как инструмент разработки контрпродуктивен.

с этим согласен. отберите у человека отладчик и код сразу станет лучше. а если еще и ide отобрать, то вообще супер (без сарказма)

интерактивный отладчик скорее зло, чем добро.
americans fought a war for a freedom. another one to end slavery. so, what do some of them choose to do with their freedom? become slaves.
Re[10]: дебагинг vs unit-тесты vs ассерты
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 01.05.16 04:14
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>>>Что-то не совсем понял, в чем тут проблема?

N>>Проблемы нет — тогда, когда ты влезаешь между вызовами и смотришь, что же там реально передаётся между ними. А это или отладчик, или пересборка с логгингом.
N>>Отладчик обычно проще, по крайней мере на один раз.
L>В смысле, зачем смотреть, что передается между вызовами стороннего API?

Мнэээ... ну я не знаю, наверно, чтобы решить проблему?
Если оно что-то не то делает, наверно, надо или заставить его работать, или заменить на другое, но замена может оказаться слишком дорогой.
Ну или расскажи, как бы ты это сделал именно в такой ситуации.

L>>>Ну неудобно!

N>>Для данных целей — не мешает.
L>Да и не помогает особо.

Именно что помогает. Breakpoint, посмотрели на переменные, ужаснулись, начали рисовать обходы.

N>>Ну вот потому я и говорю про то, что надо собрать как можно раньше.

L>Вовсе не обязательно собирать аппликуху. Вполне можно собрать функциональный тест на основе юнит-тест фреймворка.

Увы, при ретроспективном анализе я прихожу всё время к одному и тому же выводу — воспроизвести реальную проблему можно было только на коде, очень близком к финальному. До этого она не воспроизводилась.

N>>И слово "спринт", которое ты тут применил, из мира agile.

L>Это просто способ отслеживать прогресс и упорядочивать хаос, в переводе на человеческий язык звучит как "сделать первым делом".

Не не не дэвид блейн кролик упорхнул терминология ушла в массы.
The God is real, unless declared integer.
Re[7]: дебагинг vs unit-тесты vs ассерты
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 01.05.16 04:58
Оценка:
Здравствуйте, мыщъх, Вы писали:

М>покрытие сценаривев или покрытие кода? на одном из собеседований предложили прикинуть сколько нужно тестов для функции сортировки. вышло 100500 тестов и все они очень нетривиальные.


Подход в принципе из серии натянуть сову на глобус. Для сортировки надо сделать _верификацию_, а не тестирование, алгоритма, и тестирование применённых механизмов, вроде swap(). Какие-то тесты всего алгоритма, конечно, нужны, но они больше для того, чтобы убедиться, что не сломано само окружение, и как минимум часть из них будет проверкой этого окружения, в виде sizeof(int)>=4.

В соседнем треде обсуждают ATS, там верификация встраивается в сам код. Говорят, выглядит многообещающе.

А ещё подобные штуки тестируются случайными данными, то есть при каждом прогоне выбирается случайный массив входных данных, сортируется, и результат или сравнивается с тупым алгоритмом, или проверяется на базовые инварианты типа возрастания ключа. Главное — чтобы при обломе теста входные данные были чётко отражены в отчёте, а не "тут поломалось, я не знаю, почему".
Я слышал, как такое рандомизированное тестирование находило проблемы через много лет после вроде бы стабилизации кода.

М>убедиться, что в отсортированных данных есть все исходные данные и нет никаких левых. убедиться что сортировка (не)устройчивая.


Убедиться, что сортировка неустойчивая, как и с любым другим подобным случаем непредсказуемого невыполнения свойства, которое не обещалось, можно только статистически. На одиночном вызове — нет, оно чисто случайно может соблюсти устойчивость.

М> убедиться, что данные действительно отсортированы в нужном порядке. убедиться, что если повторно сортируются уже отсортированные данные мы не получаем тормозов, выходящих за рамки требований к функции сортировки.


Это частная проблема только для qsort без защиты по глубине рекурсии. Может быть проще проверить сам переход к более дорогим предсказуемым средствам (heapsort, медиана медиан), чем мерять всё вместе. Хотя такая проверка по времени должна быть сделана на верхнем уровне чисто для отмазки для проверяющих (включая себя).

М>дальше больше. надо же как-то убедиться, что мы не пишем за пределы массива (если сортируется массив)


Верификация. Указатель за пределами массива просто не может быть создан.
На qsort опасные места — только при формировании субпартиций — можно явно вставить проверки в рантайме, это будет дёшево, и/или опять же верифицировать.

М> и что нет утечек памяти (если выделяется память).


Может, тут лучше не проверять внешними средствами, а нарисовать простой менеджер одноразовой арены.

М> плюс еще тонкости реализации чтобы не получилось так, что на 64 бит платформе при сортировке массива из двух+ миллиардов записей мы не получали кашу.


У меня на второе был другой любимый пример — функция подсчёта utf8 codepoints
в строке, собирающая результат в однобайтном счётчике.
Против такого писать тесты — это надо ну очень постараться и вообще предположить, что кто-то переполнил эту границу. А это уже анализ кода.

М>так что интересно как вы достигаете 100% покрытия для алгоритмов чуть-чуть сложнее чем разворот списка.


Именно алгоритмы сложной логики как раз покрыть очень сложно.
Хотя у меня пара показательных примеров есть — вот, например, группировка элементов списка для одного ключа:

-spec(group_pc_repr(list({any(),list()})) -> list({any(),list()})).
group_pc_repr(List) ->
    rec_group_pc_repr([], List).

-spec(rec_group_pc_repr(list(), list({any(),list()})) -> list({any(),list()})).
rec_group_pc_repr(Out, []) ->
    Out;
rec_group_pc_repr(Out, [{Tag, List1}, {Tag, List2} | Rest]) ->
    rec_group_pc_repr(Out, [{Tag, List1 ++ List2} | Rest]);
rec_group_pc_repr(Out, [{Tag, List} | Rest]) ->
    rec_group_pc_repr(Out ++ [{Tag, List}], Rest).


Что тут тестировать? Очевидно, именно качество сочетания соседних элементов. С одинаковым ключом — собирать, с разными — нет ну и частные случаи входе пустого списка на входе. Получаем:

rec_group_pc_repr_test_() ->
    [
        {"rec_group_pc_repr() trivial",
            ?_assertMatch([], ?M:rec_group_pc_repr([], []))},
        {"rec_group_pc_repr() no change",
            ?_assertMatch([1,2], ?M:rec_group_pc_repr([1,2], []))},
        {"rec_group_pc_repr() no merge 1",
            ?_assertMatch(
                [{k1,[a]},{k2,[b]},{k1,[c]}],
                ?M:rec_group_pc_repr([], [{k1,[a]},{k2,[b]},{k1,[c]}]))},
        {"rec_group_pc_repr() merge 1",
            ?_assertMatch(
                [{k1,[a,b,c]}],
                ?M:rec_group_pc_repr([], [{k1,[a]},{k1,[b]},{k1,[c]}]))},
        {"rec_group_pc_repr() merge 2",
            ?_assertMatch(
                [{k1,[a,b]},{k2,[c]}],
                ?M:rec_group_pc_repr([], [{k1,[a]},{k1,[b]},{k2,[c]}]))},
        {"rec_group_pc_repr() merge 3",
            ?_assertMatch(
                [{k1,[a,b]},{k2,[c,d]}],
                ?M:rec_group_pc_repr([],
                    [{k1,[a]},{k1,[b]},{k2,[c]},{k2,[d]}]))},
        {"rec_group_pc_repr() merge 4",
            ?_assertMatch(
                [{k1,[a]},{k2,[b,c]},{k3,[d]}],
                ?M:rec_group_pc_repr([],
                    [{k1,[a]},{k2,[b]},{k2,[c]},{k3,[d]}]))}
    ].


Честно говоря, мне кажется, что я покрыл все возможные варианты
Хочу увидеть контрпример на хотя бы подозрительную ситуацию.

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


Это результат того, что у них moving target настолько быстрый, что при test-first они не успеют никуда
The God is real, unless declared integer.
Re[8]: дебагинг vs unit-тесты vs ассерты
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 01.05.16 08:12
Оценка:
Здравствуйте, landerhigh, Вы писали:

К>>Сделал, к примеру, транспонирование матрицы на месте, но она у тебя, скажем, стреляет по памяти при НОД(x,y)=17, а стрельба сходит с рук при x<100.

К>>Да, ты написал тесты о том, что матрица транспонируется. Разумеется, только при маленьких размерах и выборочно (ну не перебирать же все x от 1 до 200 и y от 1 до 200).
К>>А что ты сделал для проверки стрельбы?

L>Во-первых, don't write shit code. Моей фантазии не хватает на то, чтобы представить, как можно в коде транспонирования матрицы случайно такой косяк допустить.


Для 17 — да, для 16 — уже запросто — при попытке ускорения через SSE/AVX/etc.

L>Во-вторых, проверка стрельбы по памяти не входит в ответственность юнит-тестов. Я в принципе не уверен, что ее можно в общем случае обнаружить в момент собственно выстрела.


В общем случае — нет. В достаточно простых — например, порча соседних участков из-за вылезания за границы на плюс-минус константу — есть хорошо проработанные методы с guard zone.
The God is real, unless declared integer.
Re[4]: дебагинг vs unit-тесты
От: Stanislav V. Zudin Россия  
Дата: 01.05.16 08:29
Оценка:
Здравствуйте, netch80, Вы писали:

SVZ>>Варианты сечения: по осям X, Y, Z, X+Y, X+Z, Y+Z, X+Y+Z. Итого 7 вариантов сечения. В результате получается от 2 до 8 ячеек.

SVZ>>Теперь нужно найти положение ячеек относительно соседей (не одна строчка). Затем проверить, что на периферии ячейки по-прежнему инцидентны воздуху, а внутри соединены между собой.

N>Примерно то же самое. Можно делать, если логика симметрична, тест не на все сразу, а на самый сложный вариант плюс по одному простому (типа, из X+Y, X+Z, Y+Z вначале взять только X+Z). Остальные сложить в низкоприоритетный TODO.


Ну разве что. Будет, хотя бы, надежда, что удастся изловить самый вопиющий случай. Конечно это лучше, чем ничего, но жаба постоянно стоит у горла — очень соблазнительно писать новый функционал вместо теста. Пока соблазны побеждают.

N>Но, по-моему, сечения по косым граням — это перебор. Что, параллелепипеда вам недостаточно? А почему бы тогда сразу не переходить к какой-нибудь трёхмерно-сотовой структуре?


Пока обходимся параллелепипедами, сечение происходит вертикальными (X,Y) и горизонтальной (Z) плоскостями.
Но дальше, видимо, таки придется делать косые сечения (либо наклонять/поворачивать вертикальные грани) — можно здорово повысить точность моделирования без увеличения размерности задачи.
Триангуляция нам не подошла из-за особенностей солверов (солверы делаю не я, поэтому подробнее не смогу объяснить).
Готовые движки для построения сетки тоже не годятся. Работаем с печатными платами, а это явно выраженная слоистая структура — горизонтальные грани должны выравниваться на границу слоев (металл, диэлектрик, финишное покрытие и т.д.).
Вот и приходится самим всё делать.


SVZ>>Случай 4. Тут самое интересное. Т.к. каждая ячейка своей гранью может соприкасаться только с 0, 1, 2 и 4 ячейками, то при определенных сечениях возникает конфликт, чтобы его разрешить необходимо подразбить соседа — начинается лавинообразный процесс. Как тут писать тест — у меня фантазии уже нет.


N>Вот если лавинообразный процесс — значит, он формулируется в терминах: шаг процесса (взять одну ячейку, если она ещё не пройдена на этом уровне (для данного шага и более крупного), изучить воздействие на соседей, добавить соседей в список обработки). Это вполне может быть одной функцией, для которой наготовить тестовых данных для типичных ситуаций, далее обеспечить покрытие всех веток логики (проверить каким-то автоматизированным средством). Ключ к качеству проверки — именно полное покрытие.

N>Далее сформулировать логику цикла по списку ячеек (вариант общеизвестного поиска в ширину) и на моках убедиться в её работе на произвольных данных нескольких случаев (пустой список, 2-3 варианта перекрытия распространения ячеек на одну и ту же). Тонкостью будет отдельный учёт одной ячейки на более мелкой сетке как новой сущности, но это решается в зависимости от варианта алгоритма (например, если шаг сетки кратен степени двойки, то учёт вообще банален).

Ну это получается, что надо лезть внутрь алгоритма, чего очень не хотелось делать. Но, другого варианта на горизонте нет. Скоро предстоит опять ковырять генератор сетки.

N.B. В кои-то веки дискуссия про тестирование получилась продуктивной.
_____________________
С уважением,
Stanislav V. Zudin
Re[11]: дебагинг vs unit-тесты vs ассерты
От: landerhigh Пират  
Дата: 01.05.16 10:06
Оценка:
Здравствуйте, netch80, Вы писали:

N>Если оно что-то не то делает, наверно, надо или заставить его работать, или заменить на другое, но замена может оказаться слишком дорогой.

N>Ну или расскажи, как бы ты это сделал именно в такой ситуации.

Я подозреваю, что несколько не догоняю, что ты имеешь в виду. Пример можно?
Юнит-тесты, как правило, тестируют поведение своего собственного кода, изолируя особенности внешнего API. Зачастую можно сразу сказать, где косяк — внутри или снаружи.

L>>Да и не помогает особо.


N>Именно что помогает. Breakpoint, посмотрели на переменные, ужаснулись, начали рисовать обходы.


Или посмотрели на переменные, не ужаснулись, ибо не воспроизвелось — воспроизводится только при определенных таймингах, которые отладчик рушит

L>>Вовсе не обязательно собирать аппликуху. Вполне можно собрать функциональный тест на основе юнит-тест фреймворка.


N>Увы, при ретроспективном анализе я прихожу всё время к одному и тому же выводу — воспроизвести реальную проблему можно было только на коде, очень близком к финальному. До этого она не воспроизводилась.


Вот в этом-то и прикол — юнит тесты тестируют юниты в сценариях использования, полностью идентичным финальным.
Ну и еще это может быть просто показатель того, что при планировании архитектуры концентрируются не на том. Бывает, что с первого дня начинают задаваться цветом кнопок в UI, и полностью игнорируют тот факт, что без достаточной производительности никому этот UI не нужен будет.

N>>>И слово "спринт", которое ты тут применил, из мира agile.

L>>Это просто способ отслеживать прогресс и упорядочивать хаос, в переводе на человеческий язык звучит как "сделать первым делом".
N>Не не не дэвид блейн кролик упорхнул терминология ушла в массы.

А она удобная — всем сразу все понятно
www.blinnov.com
Re[9]: дебагинг vs unit-тесты vs ассерты
От: landerhigh Пират  
Дата: 01.05.16 10:11
Оценка:
Здравствуйте, netch80, Вы писали:

L>>Во-первых, don't write shit code. Моей фантазии не хватает на то, чтобы представить, как можно в коде транспонирования матрицы случайно такой косяк допустить.

N>Для 17 — да, для 16 — уже запросто — при попытке ускорения через SSE/AVX/etc.

О, хороший пример, как раз показывающий полезность юнит-тестирования.
Подобные подкапотные оптимизации не изменяют наблюдаемого поведения. Но это не случайное изменение, и разработчк, реализующий подобную оптипизацию, понимает, что он вносит еще один граничный случай в код (если он не понимает, то это уже нужно обсужать в форуме "о работе"). И знает, что ему нужно написать тест, который протестирует работу юнита именно в этих условиях. И он этот тест пишет, и не ждет, когда придет баг репорт из продакшена.

L>>Во-вторых, проверка стрельбы по памяти не входит в ответственность юнит-тестов. Я в принципе не уверен, что ее можно в общем случае обнаружить в момент собственно выстрела.

N>В общем случае — нет. В достаточно простых — например, порча соседних участков из-за вылезания за границы на плюс-минус константу — есть хорошо проработанные методы с guard zone.

Вот и я про то. Кроме того, налицо двойные стандарты — методом ручного лома код на отсутствие привычки стрелять по памяти не проверить от слова никак. А вот от юнит-тестов начинают внезапно требовать еще и это, и многое другое.
www.blinnov.com
Re[5]: дебагинг vs unit-тесты vs ассерты
От: landerhigh Пират  
Дата: 01.05.16 10:13
Оценка:
Здравствуйте, мыщъх, Вы писали:

L>> И навыки работы со студийным отладчиком никак не помогут в случае gdb,

М>еще скажите что навыки написания кода в студии никак не могут, когда под рукой только x-code и text mate.

По правде говоря, мне жаль разработчика, у которого отобрали студию и вручили vim, который он видит в первый раз в жизни.
Но это оффтопик.
www.blinnov.com
Re[12]: дебагинг vs unit-тесты vs ассерты
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 01.05.16 14:20
Оценка:
Здравствуйте, landerhigh, Вы писали:

N>>Если оно что-то не то делает, наверно, надо или заставить его работать, или заменить на другое, но замена может оказаться слишком дорогой.

N>>Ну или расскажи, как бы ты это сделал именно в такой ситуации.
L>Я подозреваю, что несколько не догоняю, что ты имеешь в виду. Пример можно?
L>Юнит-тесты, как правило, тестируют поведение своего собственного кода, изолируя особенности внешнего API. Зачастую можно сразу сказать, где косяк — внутри или снаружи.

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

L>>>Да и не помогает особо.

N>>Именно что помогает. Breakpoint, посмотрели на переменные, ужаснулись, начали рисовать обходы.
L>Или посмотрели на переменные, не ужаснулись, ибо не воспроизвелось — воспроизводится только при определенных таймингах, которые отладчик рушит

Ну я не про гейзенбаги. Это другой случай, по-своему серьёзный, но другой.

L>>>Вовсе не обязательно собирать аппликуху. Вполне можно собрать функциональный тест на основе юнит-тест фреймворка.

N>>Увы, при ретроспективном анализе я прихожу всё время к одному и тому же выводу — воспроизвести реальную проблему можно было только на коде, очень близком к финальному. До этого она не воспроизводилась.
L>Вот в этом-то и прикол — юнит тесты тестируют юниты в сценариях использования, полностью идентичным финальным.

В сценариях, не имеющих абсолютно ничего общего с финальными в таких вопросах, как отношение к нагрузке, суммарное использование памяти и тому подобное.
В этих вопросах юнит-тестирование это даже не игры в песочнице, это хуже — это игры на макете песочницы, рисованном кривыми линиями на рваном листике в полосочку.

L>Ну и еще это может быть просто показатель того, что при планировании архитектуры концентрируются не на том. Бывает, что с первого дня начинают задаваться цветом кнопок в UI, и полностью игнорируют тот факт, что без достаточной производительности никому этот UI не нужен будет.


Голой производительности хватало. Не учли несколько неустранимых мест, дающих O(N^2) от уровней перекосов нагрузки. Именно в этом "гладко было на бумаге, да забыли про овраги". Ну, не знали, но от этого не легче.

N>>>>И слово "спринт", которое ты тут применил, из мира agile.

L>>>Это просто способ отслеживать прогресс и упорядочивать хаос, в переводе на человеческий язык звучит как "сделать первым делом".
N>>Не не не дэвид блейн кролик упорхнул терминология ушла в массы.
L>А она удобная — всем сразу все понятно

Вот и я о том же
The God is real, unless declared integer.
Re[2]: дебагинг vs unit-тесты
От: landerhigh Пират  
Дата: 01.05.16 23:42
Оценка:
Здравствуйте, IT, Вы писали:

IT>Именно. Сравнивать юнит-тесты и отладчик — это как сравнивать тёплое с мягким. Алтернативой отладчику могут быть логи и то с натяжкой. При чём тут юнит-тесты вообще не понятно


При том, что тема выделена из другой, где т.с. на своем опыте столкнулся с тем, что попытка заменять тесты ручной отладкой приводит к тому, что процесс разработки встает намертво.

__>>но, тут товарищ landerhigh заявил, что использование дебагера — каменный век:

IT>Так говорят обычно те, кто не понимает что такое отладчик.

Скорее те, кто слишком хорошо знает, что таке отладчик. Кстати, по-английски он называется debugger, и называется так ИМХО вовсе не случайно.
www.blinnov.com
Re: дебагинг vs unit-тесты
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 02.05.16 07:34
Оценка:
Здравствуйте, _hum_, Вы писали:

__>Всегда считал, что это ортогональные вещи, но, тут товарищ landerhigh заявил, что использование дебагера — каменный век:


Я бы уточнил так: использование отладчика без тестов — или каменный век, или любая разновидность подхода, не ориентированного на целевое долговременное использование кода, или редкие особые случаи, когда автоматизация тестирования невозможна.

Во вторую ветку входят, совершенно без всякой отрицательной коннотации, учебные цели. Школьники/студенты пишут задачу, чинят отладчиком, сдают на сайтах типа e-olymp.com и забывают. Это нормально. Точно так же во вторую входит "одноразовый код" по Спольски, и это тоже нормально.
Ненормально, когда код должен жить хотя бы полгода, и ему не фиксируют тесты, но находят время гонять отладчик. Во времена, когда каждому доступны VCS, облака для сохранения не только кода, но и тестов, и тестовые фреймворки для всех языков, это действительно каменный век по уровню использования средств.

Но если хочется получить ценность результата, то программист в первую очередь ценит свои усилия. Запуск отладчика и вызов в нём чего-то вручную — это неповторяемые действия, требующие умственных затрат на их повторение. Автоматизация их повторения — это уже тестовый фреймворк. Но такие фреймворки качественного уровня, которые запускают готовую программу, правильно и вовремя вводя/выводя данные и иначе взаимодействуя с ней, сложны, дороги и в общем случае принципиально невозможны (все эти Selenium решают важные частные случаи, но не общую задачу). Зато оформление функций и модулей в понятно управляемые песочницы — реализуемы в подавляющем большинстве случаев.

Неиспользование отладчика должно быть не намеренным действием, а следствием усилий, приложенных к автоматизации тестирования. "Каменный век" активного использования отладчика — это интегральная статистическая характеристика всего процесса разработки, а не одного действия.

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

__>На мой ответ: "извините, но вы здесь описали вариант, когда либо готовый код правится, либо к готовому коду добавляется еще один готовый функциональный блок, и проверяется их совместимость. я нигде не увидел собственного написания.

__>ну, простейший пример — вам сказал тим лид — нужно написать функцию, транспонирующую матрицу. как в этом случае будет выглядеть работа с тестами без дебагера?" последовали опять общие фразы.

__>Потому вопрос в зал, может кто-нибудь, кто придерживается мнения landerhigh, на примере задачи написания функции транспонирования матрицы показать, как тесты заменяют работу с дебагером?


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

L>>Ты мне лучше скажи, зачем тут вообще отладчик может понадобиться?

__>ответ:
__>так а где здесь код-то самого транспонирования? ведь ошибки именно там кроются.

А зачем он тут нужен? Описанные тесты покрывают необходимое поведение по ТЗ, а не рассмотрение с учётом особенностей кода.

__> и если вы увидите срабатывание ассертов, что делать дальше будете?


Искать ошибку. Есть другие варианты?

__>как, например, обнаружите глупейшую ошибку типа Matrix[i][j] = Matrix[j][i] для in-place трансполнирования квадратной матрицы?


Простите, а что именно тут названо глупейшей ошибкой? Вообще-то транспонирование на месте именно так и делается.

Если Вы имели в виду, что циклы для этого должны быть организованы так, что 1<=i<=n, но 1<=j<i, а ошибкой будет 1<=j<=n, то именно это и надо явно говорить.
Но эта ошибка проверяется транспонированием диагонально несимметричной матрицы любого размера, хоть 2*2.
The God is real, unless declared integer.
Re[2]: дебагинг vs unit-тесты
От: landerhigh Пират  
Дата: 02.05.16 09:25
Оценка:
Здравствуйте, netch80, Вы писали:

N>Я бы уточнил так: использование отладчика без тестов — или каменный век, или любая разновидность подхода, не ориентированного на целевое долговременное использование кода, или редкие особые случаи, когда автоматизация тестирования невозможна.


+100500

__>>так а где здесь код-то самого транспонирования? ведь ошибки именно там кроются.

N>А зачем он тут нужен? Описанные тесты покрывают необходимое поведение по ТЗ, а не рассмотрение с учётом особенностей кода.

Плюс в сферическом идеальном мире тесты пишутся до того, как будет написан собственно код. Так, правда, почти никогда не бывает.

__>> и если вы увидите срабатывание ассертов, что делать дальше будете?

N>Искать ошибку. Есть другие варианты?
__>>как, например, обнаружите глупейшую ошибку типа Matrix[i][j] = Matrix[j][i] для in-place трансполнирования квадратной матрицы?
N>Простите, а что именно тут названо глупейшей ошибкой? Вообще-то транспонирование на месте именно так и делается.

Хех. Именно что глупейшая, которую легко допустить на автомате, ибо задача элементарная и моск был занят чем-то другим в момент написания кода. Но такая ошибки отлично отлавливается на счет "раз" юнит-тестами. Тут значение элемента [i][j] теряется. Вместо этого кода должно быть swap(Matrix[i][j],Matrix[j][i])
www.blinnov.com
Re[9]: дебагинг vs unit-тесты vs ассерты
От: landerhigh Пират  
Дата: 03.05.16 22:54
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Твоими бы тестами, да медку бы хапнуть!


Я, кажется, знаю, куда эта дискуссия ведет — поскольку юнит-тесты не отлавливают расстрел памяти в сферическом случае в в вакууме, то и писать их не будем. Я такое видел 100500 раз.

К>Все эти "должны" — это прекрасно, а кто конкретно эти долги отдаёт?


По опыту, долги приходится отдавать в основном тогда, когда на тестах "сэкономили".

К>Только в том случае, если юнит-тестами покрыто абсолютно всё поведение модуля.

К>В том числе, нарушение контракта извне. То есть, у тебя есть мок, который наловчился диагностировать абсолютно все нарушения контракта, проходящие под категорией "неопределённое поведение".

"Нарушение контракта извне" aka выдача невалидных данных на вход — один из трех сценариев использования юнита. Обязательно тестируется.

К>Подчеркну: абсолютно все.


Что значит "абсолютно все"? Юнит таков, что ему можно 100500 способами нарушить контракт? Так нужно его резать на съедобные части, только и всего.

К>Да щас! "Тесты уже есть".

К>Тесты — это лишь ещё одна проекция предметной области на твоё понимание предметной области. (Первая проекция — это рабочий код).

Не предметной области, а логики работы юнита.

К>Если ты чего-то недопонял, то тупо не догадаешься написать правильные тесты.


Когда допонял — дописываешь пропущенные. А то ведь так бывает, что допонял предметную часть и понял, что все, что накодил, нужно полностью переписывать.

К>И в отладчик смотрит человек. А человеку свойственно ошибаться, и поэтому надеяться только на тесты — это заранее согласиться на ложноотрицательную диагностику.

К>Как, впрочем, и одну лишь отладку...

Вот именно, вот именно.

К>Ну давай, покажи простой и эффективный код транспонирования матрицы на месте.


Э, нет, я тут про тесты говорю, а не матирцы.

К>Если будешь рожать "с листа", то риск накосячить с адресной арифметикой достаточно велик.


Ключевое слово "риск". Привыкший работать по TDD моментально распознает эти риски. И задает себе вопрос — как я напишу тест, который проверит, накосячил ли я в данном месте? Это не всегда можно, но чаще всего — как раз можно.

L>>Во-вторых, проверка стрельбы по памяти не входит в ответственность юнит-тестов. Я в принципе не уверен, что ее можно в общем случае обнаружить в момент собственно выстрела.

К>ВотЪ! Не всемогущи твои юнит-тесты. Настоящий, труъ баг они не распознают.

По нынешним временам стрельба по памяти — это уже не труъ багъ, а "скорее кто пустил студентов в репозиторий".
Тем не менее, гоняя юнит-тесты при каждой сборке, у тебя больше шансов узнать, что имеет место быть "упс", еще до выхода в тестирование.

L>>В-третьих, используя тот факт, что дважды транспонированная матрица равна самой себе, в случае подобных подозрений можно написать и автотест, который переберет все варианты и сделает это за O(x*y) время, что в 21 веке означает примерно "мгновенно".

К>Сколько пар (x,y) ты хочешь перебрать? В диапазоне [1..N] ты уже отхватишь O(N^4). Мгновенно...

Все же, наверное, квадрат. Вполне приемлимо для функционального теста.
Опять же, никто так не делает. Анализируют риски ошибок в реализации алгоритма, и пишут тесты, нацеленные на срабатывание именно этих рисков.
www.blinnov.com
Re[13]: дебагинг vs unit-тесты vs ассерты
От: landerhigh Пират  
Дата: 04.05.16 17:35
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Кто говорил "заменять"? Это ты всячески хочешь заменить отладчик тестами, а не я тесты отладчиком.


Нет, я предлагаю вылезти из каменного века и перестать точить кремниевые топоры гранитными камнями.

К>Видишь ли, вся арифметика — это сплошные граничные условия.

К>И ты либо пишешь на идрисе или каких-то тому подобных языках, с нумералами Пеано и прочами ужасами.
К>Либо соглашаешься на то, чтоб какую-то совсем уж откровенную мелочёвку принять на веру. (А компилятор, в случае чего, тебя не простит и отомстит).

Что-то всегда нужно принять на веру, а то так можно договориться до того, что начинать нужно с юнит-тестов для операций процессора.

К>Почему факт зацикливания? Умный гусь или шланг в -O3 способен творить чудеса, и на нашем форуме уже приводили примеры — как он и бесконечный, и пустой цикл создавал, и через условия перепрыгивал. Потому что Undefined даёт широкие возможности. Например, отладочный код будет работать правильно (тесты-то компилятся с -O0 либо -Ox -DDEBUG), а релизный хаотично, и даже диск форматнуть может при известной ловкости рук.


Именно поэтому у нас проект сразу собирается в релизе и тесты гоняются тоже в релизе. Мы можем себе это позволить, поскольку при разработке отладчик нам использовать незачем. И это нас уже спасло от кучи именно таких приколов.

  cut
L>>Но ладно, в принципе, пример годный. Допустим, что исходный код имеет некий пока непонятный мне смысл. Сделаем неявное явным
L>>
L>>template <class F>
L>>void go_around_zero_v2(int n, F func) { 
L>>    if (math::abs(n) > MAX_UINT/20 -1)
L>>    {
L>>        throw std::runtime_error("Loop will go bananas!");
L>>    }
L>>    for(int i = -n*10; i <= n*10; ++i) { 
L>>        func(); 
L>>    } 
L>>}

L>>void go_around_zero(int n)
L>>{
L>>    go_around_zero_v2(n, do_smth);
L>>}
L>>


L>>Примерно все.


К>Моя мина-ловушка всё-таки сработала! abs(INT_MIN) даёт неопределённое поведение, если INT_MIN < -INT_MAX.


Не сработала. Эта мини-ловушка не пройдет тест для граничных условий. Я просто в очередной раз наступаю своей же песне на хвост — тест с INT_MIN я напишу вторым, после INT_MAX. Мне это очевидно, тем, кто тесты писать не привык, нет. Я же говорю — тесты пишутся для граничных условиях на автомате за считанные секунды. Иногда приходится задуматься, как в случае с INT_MIN, чтобы понять, сколько же итераций в данном случае ожидается.
(А потом аффтар кода вспомнит, что писать код под гриппом с температурой — это еще хуже, чем под веществами (наверное) и больше этого делать не будет)

К>После чего вылезает оптимизатор (который тоже посчитал границы цикла с неопределённым поведением) и делает там что-то очень странное. Но в тесты это не попадёт, потому что тестовое окружение задавило оптимизацию.


Не делает — тест гоняются в релизе. И волосы остаются мягкими и шелковистыми.

К>Через три месяца Виллабаджо захватила свою долю рынка бета-версией продукта, дерзкой и как пуля резкой, а Вилларибо продолжает обкладывать арифметику тестами и/или выпускать гигантского ленивца (каждая проверка имеет свою цену в байтах и наносекундах).


Нет, не так. Через два месяца Виллариба выпустила стабильный первый релиз продукта, захвативший весь рынок. Через еще два — второй стабильный.
А виллабаджо никак из нестабильной беты вылезти не может.

Слушай, не надо только меряться. Ну не увеличивает использование тестов время разработки. Уменьшает. Часто — очень значительно уменьшает. А уж разница после передачи проекта в QA... но тут нужно один раз увидеть, чтобы поверить.
www.blinnov.com
Re[13]: дебагинг vs unit-тесты vs ассерты
От: landerhigh Пират  
Дата: 04.05.16 20:45
Оценка:
Здравствуйте, netch80, Вы писали:

N>Более того, даже при машинной верификации тестирование всё равно необходимо — чтобы повысить шансы, что условия, включённые в аксиоматику верификатора, действительно истинны.


L>>TEST(go_around_zero_v2, testNegativeN)


N>Вот тут уже начинаются нелады. Основная проблема в том, что ты поддался на откровенную провокацию Кодта.


Я знаю
На деле, что-то подобное всегда всплывает в разговорах о юнит-тестировании. Либо вот из таких сферических примеров в вакууме делается вывод, что юнит-тесты писать бесполезно, либо идут по другому пути — всем писать тесты с покрытием кода 100%, а кто не все, того накажем.

L>> unsigned int expected = n*10*2+1; // Ну или 100500 от балды, чтобы просто отловить бесконечный цикл


N>Так не отловишь же — оно может захотеть вообще ничего не делать. Да и в реальности бесконечного цикла не будет при любом из возможных переполнений.


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

L>>До приемлимой.

N>На всякий случай — "приемлемой".

У меня в последнее время дисграфия какая-то.
www.blinnov.com
Re[12]: дебагинг vs unit-тесты vs ассерты
От: Кодт Россия  
Дата: 04.05.16 21:53
Оценка:
Здравствуйте, netch80, Вы писали:

N>Уже писал рядом, но повторюсь: это вообще отвратительная провокация, и суть в том, что этот код, за счёт UB на переполнение и странной сущности внутри, не подлежит в таком виде ни тестированию, ни изучению отладчиком. Он подлежит статическому анализу и действиям по результату этого анализа.


Это было доведение до абсурда.

Конечно, программирование (как начисто, так и отладка) должно обращаться и к статическому анализу (часть которого делает компилятор), и к автоматическому тестированию, и к верификации "сверху" — доказательству корректности алгоритма, и "снизу" — доказательству соответствия реализации задумке. Но и к запуску под дебаггером, если предыдущие рубежи обороны оказались прорваны.

А то, что дорогостоящие рубежи обороны можно строить впустую, это во Второй Мировой показала линия Мажино, а в сях — моя провокация

Что касается компилятора, то он находится в состоянии неопределённости.
То ли программист знает что-то особенное за пределами возможностей компилятора, и мысленно поклялся, что UB не случится (предельный случай — write-only code).
То ли программист сам ещё не догадывается об ограничениях. (Или догадывается, но ошибается).
То ли догадывается, но хочет эксплуатировать.

Ну самое невинное:
for (int i = -n*10; i <= n*10; ++i) {...}

может быть неявно if(n >= 0), а может — так же неявно — assert(n >= 0).
В первом случае отрицательный аргумент окажется фичей, во втором — багом.

Хорошо тем, кто с самого начала выбрал писать на языке, придирчивом до контрактов. Эйфель там, не знаю, Ада какая-нибудь.
Если забудешь написать assert, или напишешь, но компилятор докажет, что реальные ограничения более жёсткие, чем заявленные, — он тебе надаёт по пальцам.
(Кстати, тот же гусь вполне мог бы при добавочном ключике --fimplicitly-add-magic-asserts-please навтыкать этих проверок в код. А может, есть уже такой ключик?)

А для всех остальных — огромная проблема легаси, во-первых (все те наработки, которые УЖЕ ещё не покрыты ассертами и тестами), и охоты на воробьёв, во-вторых (когда за горой проверок теряется замысел).

И не забываем, что никакой компилятор не всесилен. Там, где у него закончится горизонт понимания, он перестанет и за покрытием кода следить, и за диапазонами, хотя, с другой стороны, не активирует сценарии UB "инициативный дурак хуже услужливого".
И там, если человек облажался с ассертами (потому что и у человека есть горизонт, да и errare humanum est), придётся воевать со львами и драконами старым добрым дедовским отладчиком, логами и крешдампами.

К>>А в это время в Виллабаджо уже делают фигак-фигак-продакшен.

N>И удивляются, когда кто-то таки подаёт этому коду n=2e9.

Но это случается только у одного пользователя из миллиона, из серии "а вы на шкаф залезать не пробовали?"
А остальные 999999 несут в Виллабаджо денежку.
Собственно, это уже за рамками программирования, а в ведении бизнесменов. Пациентов терака чертовки жалко, марсоход очень досадно, а за дыры для джейлбрейка даже и спасибо.

Возвращаясь к провокации.
По-хорошему, надо было не клевать себе мозг, а написать
void go_around_zero(int n) {
  assert(n >= 0); // если кому-то нужно работать с вывернутым интервалом, подумайте о направлении итераций.
  assert(n < 1000); // какому психу понадобилась такая здоровая окрестность, пусть и чинит.
  for (int i = -n*10; i <= +n*10; ++i)
    do_smth(i);
  // P.S. если ты тот самый псих, не забудь последить за целочисленным переполнением здесь, и за корректностью do_smth там.
}

Доказательства корректности в рамках этих волюнтаритских рамок делаются в уме.
А какими тестами обложить этот код, чтобы это было не "отвяжись, тимлид", — пусть landerhigh поделится
Перекуём баги на фичи!
Re[13]: дебагинг vs unit-тесты vs ассерты
От: landerhigh Пират  
Дата: 04.05.16 22:20
Оценка:
Здравствуйте, Кодт, Вы писали:

К>А то, что дорогостоящие рубежи обороны можно строить впустую, это во Второй Мировой показала линия Мажино, а в сях — моя провокация


Никто не пишет тесты для случаев "угадай, что я имел в виду".

К>>>А в это время в Виллабаджо уже делают фигак-фигак-продакшен.

N>>И удивляются, когда кто-то таки подаёт этому коду n=2e9.
К>Но это случается только у одного пользователя из миллиона, из серии "а вы на шкаф залезать не пробовали?"
К>А остальные 999999 несут в Виллабаджо денежку.

это если этот самый не попадет за 100500 денежек и не раструбит об этом на 100500 углах.

К>Возвращаясь к провокации.

К>По-хорошему, надо было не клевать себе мозг, а написать
К>
К>void go_around_zero(int n) {
К>  assert(n >= 0); // если кому-то нужно работать с вывернутым интервалом, подумайте о направлении итераций.
К>  assert(n < 1000); // какому психу понадобилась такая здоровая окрестность, пусть и чинит.
К>  for (int i = -n*10; i <= +n*10; ++i)
К>    do_smth(i);
К>  // P.S. если ты тот самый псих, не забудь последить за целочисленным переполнением здесь, и за корректностью do_smth там.
К>}
К>


Внимание, вопрос — это отладочный ассерт или эмуляция контракта, существующая и в релизе?

К>Доказательства корректности в рамках этих волюнтаритских рамок делаются в уме.

К>А какими тестами обложить этот код, чтобы это было не "отвяжись, тимлид", — пусть landerhigh поделится

Сначала неплохо было бы описать, что же такое полезное этот do_smth делает, чтобы можно было наблюдаемое поведение наблюсть.
www.blinnov.com
Re[14]: дебагинг vs unit-тесты vs ассерты
От: Кодт Россия  
Дата: 05.05.16 13:45
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>Никто не пишет тесты для случаев "угадай, что я имел в виду".


Как отличить "тотальное покрытие" от "догадайся мол сама"?

К>>Но это случается только у одного пользователя из миллиона, из серии "а вы на шкаф залезать не пробовали?"

К>>А остальные 999999 несут в Виллабаджо денежку.
L>это если этот самый не попадет за 100500 денежек и не раструбит об этом на 100500 углах.

Управление рисками — для космолёта и радиохирурга одно, для свистелки под андроид другое. Тут ведь тоже "угадай, что скажут продажники".

К>>
К>>  assert(n >= 0); // если кому-то нужно работать с вывернутым интервалом, подумайте о направлении итераций.
К>>  assert(n < 1000); // какому психу понадобилась такая здоровая окрестность, пусть и чинит.
К>>  // P.S. если ты тот самый псих, не забудь последить за целочисленным переполнением здесь, и за корректностью do_smth там.
К>>


L>Внимание, вопрос — это отладочный ассерт или эмуляция контракта, существующая и в релизе?


Контракт, конечно. Слово "отладочный" мы не будем рассматривать, а то ведь и до gdb доберёмся.

По-хорошему, надо бы заставить следить за исполнением контракта компилятор, а не окружение контроля качества (включая и скомпилированные тесты, и штат сотрудников, запускающих программу и читающих логи и крешдампы).
Например, вытащить эти ассерты в тип аргумента. В аде это делается почти бесплатно, в плюсах — немножко секса на шаблонах, в идрисе — просто нельзя не сделать.

Причём, ассерты могут быть разными
— чисто для -DDEBUG, "мамой клянус, мы всё проверили, если в релизе наиграется — нэ судьба"
— панические, — "мамой клянус" и одновременно "врагу не сдаётся Варяг" — немедленно сдохнуть в релизе, пока UB не добралось до винчестера
— с известными отступлениями (fallback) в пользу живучести (robustness) — исключение там кинуть, по дефолту что-нибудь вернуть...

Но это уже зависит от общей картины. В конце концов, живучесть приложения целиком посредством легко дохнущих дочерних процессов — стратегия let them crash — тоже имеет место, хоть в Эрланге, хоть в Хромиуме.

К>>Доказательства корректности в рамках этих волюнтаритских рамок делаются в уме.

К>>А какими тестами обложить этот код, чтобы это было не "отвяжись, тимлид", — пусть landerhigh поделится

L>Сначала неплохо было бы описать, что же такое полезное этот do_smth делает, чтобы можно было наблюдаемое поведение наблюсть.


Ну это само собой. Вопрос был в том, нужно ли (и как) обвешать тестами аргумент n, если мы уже прописали контракт с помощью ассертов.
Перекуём баги на фичи!
Re[15]: дебагинг vs unit-тесты vs ассерты
От: landerhigh Пират  
Дата: 05.05.16 14:35
Оценка:
Здравствуйте, Кодт, Вы писали:

L>>Никто не пишет тесты для случаев "угадай, что я имел в виду".

К>Как отличить "тотальное покрытие" от "догадайся мол сама"?

Эээ, ну аффтар-то кода должен хотя бы понимать, что делает?
L>>это если этот самый не попадет за 100500 денежек и не раструбит об этом на 100500 углах.

К>Управление рисками — для космолёта и радиохирурга одно, для свистелки под андроид другое. Тут ведь тоже "угадай, что скажут продажники".


Космолет и радиохирург есть детский лепет. Возьми систему управления фабрикой, программный косяк в которой может привести к открытию не того вентиля не туда, в результате чего жители ближайшего городка выблюют собственные кишки.

L>>Внимание, вопрос — это отладочный ассерт или эмуляция контракта, существующая и в релизе?

К>Контракт, конечно. Слово "отладочный" мы не будем рассматривать, а то ведь и до gdb доберёмся.

Контракт в том или ином виде существует в любом коде. е это делается почти бесплатно, в плюсах — немножко секса на шаблонах, в идрисе — просто нельзя не сделать.

К>Причём, ассерты могут быть разными

К>- чисто для -DDEBUG, "мамой клянус, мы всё проверили, если в релизе наиграется — нэ судьба"

И что, в тестирование передавать отладочную сборку? Мочить в сортире.

К>- панические, — "мамой клянус" и одновременно "врагу не сдаётся Варяг" — немедленно сдохнуть в релизе, пока UB не добралось до винчестера

К>- с известными отступлениями (fallback) в пользу живучести (robustness) — исключение там кинуть, по дефолту что-нибудь вернуть...

А это уже как раз то, для чего хороши юнит-тесты. Вот есть юнит, в который передаются данные извне программы. От юзера, из датчиков и т.п. И вот сидит такой погромизд красивый, написал тест для случая корректных данных. А для случая некорректных — не может, так как непонятно, что делать-то. Думать надо, и не надеяться, что сойдет и так.

L>>Сначала неплохо было бы описать, что же такое полезное этот do_smth делает, чтобы можно было наблюдаемое поведение наблюсть.

К>Ну это само собой. Вопрос был в том, нужно ли (и как) обвешать тестами аргумент n, если мы уже прописали контракт с помощью ассертов.

А вот не всегда контракт с помощью ассертов применим. Вот есть протокол, в ем слой. В слое оном — сообщение. В сообщении — элемент. В элементе — длина оного. И якобы длина элемента не должна превышать длину посылки. Только вот очень иногда почему-то превышает, контракт нарушается. Что делать будем?
www.blinnov.com
Re[8]: дебагинг vs unit-тесты vs ассерты
От: IID Россия  
Дата: 25.08.16 17:24
Оценка:
Здравствуйте, netch80, Вы писали:

N>Я слышал, как такое рандомизированное тестирование находило проблемы через много лет после вроде бы стабилизации кода.


и до сих пор постоянно находит, см. Fuzzing
kalsarikännit
Re[2]: дебагинг vs unit-тесты
От: Sharov Россия  
Дата: 25.08.16 17:41
Оценка:
Здравствуйте, Кодт, Вы писали:

К>(У нас С++, а не Эйфель, который принудительно покрывает всё, до чего может дотянуться).


Не принудительно, а по-человечески: язык проектировался исходя из необходимости наличия встроенных контрактов.
Кодом людям нужно помогать!
Re[2]: дебагинг vs unit-тесты
От: landerhigh Пират  
Дата: 06.09.16 21:32
Оценка:
Здравствуйте, chaotic-kotik, Вы писали:

CK>Использование юнит-тестов это каменный век, а вовсе не использование дебага.


Наверное, поэтому технику юнит-тестирования до сих пор освоили считанные единицы.

CK>Классический юнит-тест по ТДД (это когда ты сначала пишешь тест, а потом реализуешь код) — крайне редко встречается в природе.


Это утопия. Хотя иногда встречается.

CK>На практике, тест пишется вместе с кодом, ты вначале можешь плохо представлять как в конечном итоге будет выглядеть интерфейс и как модуль будет использоваться. Юнит-тест это такая лаборатория, которая пишется вместе с кодом и которая позволяет начать запускать этот код в процессе ранней разработки (в том числе под отладчиком). До интеграции в продукт еще далеко, поэтому это единственный способ поработать с разрабатываемым модулем в живую (REPL-а то нет).


И уже одно это доказывает, что коду без юнит-тестов место в помоечъке (с).
Как минимум при разработке чего-то серьезнее CD-ejector

CK>Юнит-тесты почти не находят ошибки. Они помогают разрабатывать код, это строительные леса,


Юнит-тесты, внимание, это не инструмент для поиска ошибок. Это инструмент, помогающий эту ошибку не допустить. Он не гарантирует отсутствие ошибок, но при правильном пользовании позволяет свести вероятность их появления к минимуму

CK>после стабилизации кода они практически не находят регрессии.


Здрасте. Правильные юнит-тесты — это первая и зачастую самая главная линия регрессии. Они обязаны обнаруживать сломанную функциональность просто по определению.

CK>Другое дело — функциональные тесты. На проектах, где я работал, большая часть регрессий находилась с помощью таких тестов, а юнит-тесты, написанные программистами, находили какие-то слезы.


Бывает. Когда юнит-тесты пишут без понимания их сути или просто из-под палки.

CK>Но вот тут уже без отладчика сложно что-то сделать, т.к. тебе дают либо крэш-дамп, либо входной файл/базу данных. Здесь нужны именно классические навыки отладки + знание инструментов, таких как valgrind, asan и tsan.


С моего последнего проекта, переданного в тестирование, ни одного креш-дампа не пришло. Так и ушел в продакшен, не предоставив мне возможности отточить классические навыки отладки.

CK>Та же история с фаззингом. Он находит много всего, больше всех ваших юнит-тестов вместе взятых, но его без отладчика опять же сложно использовать


Ой. Фаззинг — это просто модное словечко, которым называют один из вариантов написания автотеста. Только вот верить в то, что он "много находит"... ну не знаю. У вас еще все впереди

CK>Вот например facebook недавно зарелизил ZStandard — новый комрессор (https://github.com/facebook/zstd). Вы там не найдете юнит-тестов вообще, но там есть fuzz-тесты и бенчмарки.


Нашел кучу говнотестов. Хорошая иллюстрация на тему "как делать не надо". Впрочем, под пиво да для кода на выброс, поддерживать который не собираются, пойдет.
www.blinnov.com
Re[3]: дебагинг vs unit-тесты
От: chaotic-kotik  
Дата: 07.09.16 06:39
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>Здравствуйте, chaotic-kotik, Вы писали:


CK>>Использование юнит-тестов это каменный век, а вовсе не использование дебага.

L>Наверное, поэтому технику юнит-тестирования до сих пор освоили считанные единицы.

Я не знаю где ты работаешь, но по моему опыту это не так. Практикуют все кому не лень.

CK>>На практике, тест пишется вместе с кодом, ты вначале можешь плохо представлять как в конечном итоге будет выглядеть интерфейс и как модуль будет использоваться. Юнит-тест это такая лаборатория, которая пишется вместе с кодом и которая позволяет начать запускать этот код в процессе ранней разработки (в том числе под отладчиком). До интеграции в продукт еще далеко, поэтому это единственный способ поработать с разрабатываемым модулем в живую (REPL-а то нет).


L>И уже одно это доказывает, что коду без юнит-тестов место в помоечъке (с).

L>Как минимум при разработке чего-то серьезнее CD-ejector

Можно с тем же успехом написать консольное приложение, которое будет как-то использовать код. Для этих целей не обязательно использовать именно юнит-тесты.

L>Юнит-тесты, внимание, это не инструмент для поиска ошибок. Это инструмент, помогающий эту ошибку не допустить. Он не гарантирует отсутствие ошибок, но при правильном пользовании позволяет свести вероятность их появления к минимуму


С последней фразой не согласен. До стабилизации — да, юнит-тесты помогают искать ошибки, код часто меняется и запускать функциональные тесты на каждый чих — дорого и сложно. А вот юнит-тесты — в самый раз. Поэтому разработчики их и любят, пока пишешь код — видишь эффект от их использования, ну а когда код стабилизирован — не видишь, но и с этим кодом ты не работаешь. А в нем продолжают находиться ошибки, но уже другими средствами.

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

CK>>после стабилизации кода они практически не находят регрессии.

L>Здрасте. Правильные юнит-тесты — это первая и зачастую самая главная линия регрессии. Они обязаны обнаруживать сломанную функциональность просто по определению.

Потому что старый код редко меняется, пишется новый. Новый код неправильно конфигурирует твой компонент, который проходит все юнит-тесты. В тестах нового кода твой компонент замокан. Баг в стелс режиме едет на прод.

CK>>Другое дело — функциональные тесты. На проектах, где я работал, большая часть регрессий находилась с помощью таких тестов, а юнит-тесты, написанные программистами, находили какие-то слезы.

L>Бывает. Когда юнит-тесты пишут без понимания их сути или просто из-под палки.

Либо код покрытый юнит-тестами хреново тестируют дальше, поэтому большая часть багов после них не находится. Хорошую систему функциональных тестов сделать куда сложнее. Существуют классы ошибок, которые модульным тестирование невозможно обнаружить в принципе. Ну и еще раз повторю — юнит-тесты не находят ошибки взаимодействия компонентов.

L>С моего последнего проекта, переданного в тестирование, ни одного креш-дампа не пришло. Так и ушел в продакшен, не предоставив мне возможности отточить классические навыки отладки.


У меня тоже такое было. Все зависит от проекта. Когда я один делал проект (сравнительно мало SLOC) такое было. Когда я работал на больших проектах — такого не было.

L>Ой. Фаззинг — это просто модное словечко, которым называют один из вариантов написания автотеста. Только вот верить в то, что он "много находит"... ну не знаю. У вас еще все впереди


А ты использовал когда-нибудь фаззинг?

CK>>Вот например facebook недавно зарелизил ZStandard — новый комрессор (https://github.com/facebook/zstd). Вы там не найдете юнит-тестов вообще, но там есть fuzz-тесты и бенчмарки.

L>Нашел кучу говнотестов. Хорошая иллюстрация на тему "как делать не надо". Впрочем, под пиво да для кода на выброс, поддерживать который не собираются, пойдет.


Это не код на выборс, а большой инфраструктурный проект для fb и модульных тестов там нет (что за говнотест ты там нашел?). Можешь посмотреть на код google brotli или LZ4, там та же песня. Причина в том, что ты не напишешь нормальный юнит-тест для компрессора. Что там тестировать можно модульно? Как ты себе это представляешь? Ну вот есть компонент генерирующих хаффмановский словарь — как ты узнаешь, правильно ли он сгенерирован? Захардкодишь веса или сделаешь параллельную реализацию?
Компрессоры тестируют с помощью round-trip тестов (есть и в zstd и в brotli и в lz4). Это когда берут кучу файлов и каждый из них сначала сжимают, а потом разжимают и сравнивают с оригиналом. Фаззинг тут еще неплохо работает.
Re[4]: дебагинг vs unit-тесты
От: landerhigh Пират  
Дата: 07.09.16 10:36
Оценка:
Здравствуйте, chaotic-kotik, Вы писали:

L>>Наверное, поэтому технику юнит-тестирования до сих пор освоили считанные единицы.

CK>Я не знаю где ты работаешь, но по моему опыту это не так. Практикуют все кому не лень.

Вовсе не так. Правильно применять юнит-тесты умеют единицы. В остальных случаях тесты пишут либо потому, что менеджер заставляет, либо разрождаются уродцем вроде того, что в твоей ссылке, когда уж совсем припрет.

CK>Можно с тем же успехом написать консольное приложение, которое будет как-то использовать код. Для этих целей не обязательно использовать именно юнит-тесты.


И это консольное приложение будет чем? Правильно, юнит-тестом

L>>Юнит-тесты, внимание, это не инструмент для поиска ошибок. Это инструмент, помогающий эту ошибку не допустить. Он не гарантирует отсутствие ошибок, но при правильном пользовании позволяет свести вероятность их появления к минимуму

CK>С последней фразой не согласен. До стабилизации — да, юнит-тесты помогают искать ошибки, код часто меняется и запускать функциональные тесты на каждый чих — дорого и сложно. А вот юнит-тесты — в самый раз. Поэтому разработчики их и любят, пока пишешь код — видишь эффект от их использования, ну а когда код стабилизирован — не видишь, но и с этим кодом ты не работаешь.

Распространенное мнение. С позиции "одноразового кода". Много раз его слышал, и чаще всего от людей, которым в жизни не приходилось поддерживать или развивать свой собственный или чужой код. Написали — фигакс в продакшен — в следующую контору, на следующий проект. А то, что в следующей версии пришедшие на их место программисты переписывают половину этого кода, это их не касается. Они просто гения не поняли, им еще расти и расти.

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


Уже только это помогает изолировать проблемы интеграции.

L>>Здрасте. Правильные юнит-тесты — это первая и зачастую самая главная линия регрессии. Они обязаны обнаруживать сломанную функциональность просто по определению.

CK>Потому что старый код редко меняется, пишется новый. Новый код неправильно конфигурирует твой компонент, который проходит все юнит-тесты. В тестах нового кода твой компонент замокан. Баг в стелс режиме едет на прод.

А у вас разработчики вообще не запускают сами систему после сборки?
В стелс режиме баг дальше QA не поедет. Там он проваливает регрессию. Возвращается разработчикам, которые сразу видят, что проблема в неправильном использовании компонента.

L>>Бывает. Когда юнит-тесты пишут без понимания их сути или просто из-под палки.

CK>Либо код покрытый юнит-тестами хреново тестируют дальше, поэтому большая часть багов после них не находится.

Проблемы хреновой постановки QA ортогональны юнит-тестированию.

CK>Хорошую систему функциональных тестов сделать куда сложнее. Существуют классы ошибок, которые модульным тестирование невозможно обнаружить в принципе. Ну и еще раз повторю — юнит-тесты не находят ошибки взаимодействия компонентов.


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

L>>С моего последнего проекта, переданного в тестирование, ни одного креш-дампа не пришло. Так и ушел в продакшен, не предоставив мне возможности отточить классические навыки отладки.

CK>У меня тоже такое было. Все зависит от проекта. Когда я один делал проект (сравнительно мало SLOC) такое было. Когда я работал на больших проектах — такого не было.

Это был очень большой и весьма сложный проект.

L>>Ой. Фаззинг — это просто модное словечко, которым называют один из вариантов написания автотеста. Только вот верить в то, что он "много находит"... ну не знаю. У вас еще все впереди

CK>А ты использовал когда-нибудь фаззинг?

Еще до того, как это модное слово придумали.

CK>

CK>Это не код на выборс, а большой инфраструктурный проект для fb и модульных тестов там нет (что за говнотест ты там нашел?). Можешь посмотреть на код google brotli или LZ4, там та же песня. Причина в том, что ты не напишешь нормальный юнит-тест для компрессора.

Я? Я-напишу.

CK>Что там тестировать можно модульно?


Эээ, модули. Или там весь компрессор представляет собой void compress()?

CK>Как ты себе это представляешь? Ну вот есть компонент генерирующих хаффмановский словарь — как ты узнаешь, правильно ли он сгенерирован? Захардкодишь веса или сделаешь параллельную реализацию?


Что значит "правильно"? Если есть понятие "правильно", значит, оно формализуется. А раз формализуется, значит — тестируется.

CK>Компрессоры тестируют с помощью round-trip тестов (есть и в zstd и в brotli и в lz4). Это когда берут кучу файлов и каждый из них сначала сжимают, а потом разжимают и сравнивают с оригиналом.


Так тоже можно. Но если внутрь компрессора закрался компонент с неопределенным поведением в определенном редком случае, то подобные тесты этот баг могут пропустить. И чаще всего пропускает.
www.blinnov.com
Re[5]: дебагинг vs unit-тесты
От: chaotic-kotik  
Дата: 07.09.16 12:12
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>Вовсе не так. Правильно применять юнит-тесты умеют единицы. В остальных случаях тесты пишут либо потому, что менеджер заставляет, либо разрождаются уродцем вроде того, что в твоей ссылке, когда уж совсем припрет.


Пример тех самых правильных юнит-тестов в исполнении этих "единиц" которые "умеют" можно увидеть?

L>И это консольное приложение будет чем? Правильно, юнит-тестом


Нет, оно не будет юнит-тестом, так как дергает много всего и сразу.

L>Распространенное мнение. С позиции "одноразового кода". Много раз его слышал, и чаще всего от людей, которым в жизни не приходилось поддерживать или развивать свой собственный или чужой код. Написали — фигакс в продакшен — в следующую контору, на следующий проект. А то, что в следующей версии пришедшие на их место программисты переписывают половину этого кода, это их не касается. Они просто гения не поняли, им еще расти и расти.


Я не предлагаю не писать юнит-тесты. Я просто утверждаю, что после определенного уровня зрелости и стабильности большая часть юнит-тестов теряет актуальность. Они так же могут замедлять разработку при радикальных рефакторингах.

L>Уже только это помогает изолировать проблемы интеграции.


Ты же сам в предыдущем сообщении присал что юнит-тесты не гарантируют наличие ошибок. Получается что и проблемы интеграции они не изолируют.

L> А у вас разработчики вообще не запускают сами систему после сборки?


Система большая, баг может проявляться под нагрузкой или при обработке какого-нибудь хитрого запроса на какой-нибудь отдельной платформе. У нас есть отдельная стойка, на которой только гоняются тесты и ничего больше. На полный прогон уходит много часов.

L>В стелс режиме баг дальше QA не поедет. Там он проваливает регрессию. Возвращается разработчикам, которые сразу видят, что проблема в неправильном использовании компонента.


Хочу обратить внимание на "замечательный" подход — я написал тесты и они проходят, остальное проблема QA. Собственно я и топлю за то, чтобы проактивно искать баги функциональными тестами, а не надеяться на то что QA прокликает страничку и что-то там найдет. Работа программиста не заканчивается на написании юнит-теста или интеграционного теста. Необходим постоянный поиск способов сломать код. Иначе получается что каждый разраб живет в своем уютном мирке, окруженном юнит-тестами, в полной уверенности в том что проблема не на его стороне.

L>Проблемы хреновой постановки QA ортогональны юнит-тестированию.


Не везде есть QA, иногда программисты сами себе QA.

L>Зато их находят интеграционные тесты. Которые во многих случаях являются теми же самыми юнит-тестами, только в качестве юнита используют интегрированный компонент.


Интеграционное тестирование трудоемко, плохо масштабируется и редко встречается в природе.

L>Это был очень большой и весьма сложный проект.


По твоему мнению (нет, ну серьезно, у программистов каждый проект большой, серьезный и сложный, без исключений)

L>Еще до того, как это модное слово придумали.


По моему ты просто не понимаешь о чем идет речь, рандомизированные тесты это не фаззинг.

CK>>Как ты себе это представляешь? Ну вот есть компонент генерирующих хаффмановский словарь — как ты узнаешь, правильно ли он сгенерирован? Захардкодишь веса или сделаешь параллельную реализацию?

L>Что значит "правильно"? Если есть понятие "правильно", значит, оно формализуется. А раз формализуется, значит — тестируется.

Проверить правильность словаря можно, ввод, на основе которого создан словарь, должен содержать в себе его элементы и частота встречаемости того или иного элемента должна соответствовать. Проблема в том, что ты юнит-тестом не докажешь, что не существует такой ввод, который сломает этот компонент. Этот тест будет не лучше round-trip тестов на корпусе документов. А фаззинг это может показать, если такой кейс есть, его скорее всего можно найти с помощью afl.

L>Так тоже можно. Но если внутрь компрессора закрался компонент с неопределенным поведением в определенном редком случае, то подобные тесты этот баг могут пропустить. И чаще всего пропускает.


И какова вероятность того что ты именно это найдешь своими юнит-тестами? По крайней мере я могу масштабировать round-trip тест и гонять его на хорошем железе в кучу потоков день и ночь.
Re[6]: дебагинг vs unit-тесты
От: landerhigh Пират  
Дата: 07.09.16 12:54
Оценка:
Здравствуйте, chaotic-kotik, Вы писали:

CK>Пример тех самых правильных юнит-тестов в исполнении этих "единиц" которые "умеют" можно увидеть?


Естествтенно, но для этого придется устроиться туда на работу сначала.

L>>И это консольное приложение будет чем? Правильно, юнит-тестом

CK>Нет, оно не будет юнит-тестом, так как дергает много всего и сразу.

А вот проблемы терминологии являются надуманными. Оно тестирует некий юнит? Тестирует. А то, что этот юнит "надерган" из кучи юнитов поменьше, к делу имеет уже опосредованное отношение.

CK>Я не предлагаю не писать юнит-тесты. Я просто утверждаю, что после определенного уровня зрелости и стабильности большая часть юнит-тестов теряет актуальность.


Значит, это были хорошие тесты. Они уже помогли при разработке и внедрении.

CK>Они так же могут замедлять разработку при радикальных рефакторингах.


А могут и наоборот.

L>>Уже только это помогает изолировать проблемы интеграции.

CK>Ты же сам в предыдущем сообщении присал что юнит-тесты не гарантируют наличие ошибок. Получается что и проблемы интеграции они не изолируют.

Вообще-то изолируют. Проблемы интеграции, как правило, ортогональны багам самих юнитов. Зачастую даже смотреть в кору не надо, чтобы понять, что именно случилось.

L>> А у вас разработчики вообще не запускают сами систему после сборки?

CK>Система большая, баг может проявляться под нагрузкой или при обработке какого-нибудь хитрого запроса на какой-нибудь отдельной платформе.

Воооооот. А может и не проявиться, пока через пару лет в продакшене не стрельнет. И, что характерно, практически всегда этот стрельнувший баг сводится к тому, что "в юните A не предусмотрена обработка события B". Потому что разработчики посчитали такое событие невозможным, а тестовая стойка вела себя корректно и совсем невероятные данные не генерировала. А клиенты по дешевке закупили в Китае оборудования, которое им нагенерировало всякого.

CK>У нас есть отдельная стойка, на которой только гоняются тесты и ничего больше. На полный прогон уходит много часов.


И все равно все возможные комбинации стойка прогнать не сможет.

CK>Хочу обратить внимание на "замечательный" подход — я написал тесты и они проходят, остальное проблема QA. Собственно я и топлю за то, чтобы проактивно искать баги функциональными тестами,


А кто тебе сказал, что у нас нет функциональных тестов? У нас все есть. И QA тоже есть поверху.

L>>Проблемы хреновой постановки QA ортогональны юнит-тестированию.

CK>Не везде есть QA, иногда программисты сами себе QA.

Это и есть хреновая постановка QA.

L>>Зато их находят интеграционные тесты. Которые во многих случаях являются теми же самыми юнит-тестами, только в качестве юнита используют интегрированный компонент.

CK>Интеграционное тестирование трудоемко, плохо масштабируется и редко встречается в природе.

Кроме случаев, когда немного подумали о том, как его сделать не трудоемким и масштабируемым

L>>Это был очень большой и весьма сложный проект.

CK>По твоему мнению (нет, ну серьезно, у программистов каждый проект большой, серьезный и сложный, без исключений)

Это как раз по мнению моего начальства. Проект болтался 5 лет в категории "надо бы, но песец как сложно".

L>>Еще до того, как это модное слово придумали.

CK>По моему ты просто не понимаешь о чем идет речь, рандомизированные тесты это не фаззинг.

Еще раз, на новомодные словечки мне давно положить.

CK>Проверить правильность словаря можно, ввод, на основе которого создан словарь, должен содержать в себе его элементы и частота встречаемости того или иного элемента должна соответствовать. Проблема в том, что ты юнит-тестом не докажешь, что не существует такой ввод, который сломает этот компонент.


Эээ, юнит-тесты вообще-то не для этого предназначены. Они проверяют, что на корректном, некорректном и граничном вводе поведение юнита соответствует ожидаемому. Не больше и не меньше. Задача программиста и состоит в том, чтобы выделить эти домены и определить критерии поведения.

CK>Этот тест будет не лучше round-trip тестов на корпусе документов. А фаззинг это может показать, если такой кейс есть, его скорее всего можно найти с помощью afl.


В крайне ограниченном домене.

L>>Так тоже можно. Но если внутрь компрессора закрался компонент с неопределенным поведением в определенном редком случае, то подобные тесты этот баг могут пропустить. И чаще всего пропускает.

CK>И какова вероятность того что ты именно это найдешь своими юнит-тестами? По крайней мере я могу масштабировать round-trip тест и гонять его на хорошем железе в кучу потоков день и ночь.

Примерно 100%. Я пишу код, и вижу, что у него есть такой вариант валидных с точки зрения вызывающего кода входных данных, при котором корректное поведение юнита невозможно. Соответственно нужно было решить, как должен повести себя юнит в таком случае и как это протестировать. На уровне юнита это сделать элементарно. На уровне стойки с оборудованием — невозможно.
Так мы отловили проблему со сторонним оборудованием, которое в редких случаях кодировало в посылке метки времени что-то забавное вроде 32 глюкабря. Если бы мы этого не предусмотрели на этапе юнит-тестирования, создали бы огромные проблемы клиентам. Вместо этого клиент получил доказательство и повод для рекламации поставщику оборудования.
www.blinnov.com
Re[7]: дебагинг vs unit-тесты
От: chaotic-kotik  
Дата: 07.09.16 14:09
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>Естествтенно, но для этого придется устроиться туда на работу сначала.


Т.е. нельзя. Ясно-понятно. Волшебные юнит тесты, которые существуют, но показать их нельзя. Видимо практика "правильно применять юнит-тесты" настолько сложная, что формализовать ее и описать не представляется возможным.

L>А вот проблемы терминологии являются надуманными. Оно тестирует некий юнит? Тестирует. А то, что этот юнит "надерган" из кучи юнитов поменьше, к делу имеет уже опосредованное отношение.


Поинт юнит-теста в том, что он тестирует что-то одно, поэтому ты можешь увидеть какой тест сломался и сразу понять что именно работает не так. Если тест работает не так, это не юнит-тест.

L>Кроме случаев, когда немного подумали о том, как его сделать не трудоемким и масштабируемым


Вот опять, никакой конкретики.

L>Еще раз, на новомодные словечки мне давно положить.


Звучит пассивно-агрессивно. Это не новомодные словечки а очень крутая современная технология, которая наряду с property based testing-ом, позволяет находить очень хитрые ошибки (например дыры в безопасности). Heartbleed был найден с помощью фаззинга (https://blog.hboeck.de/archives/868-How-Heartbleed-couldve-been-found.html), интересно, как бы ты его юнит-тестами искал?

L>Эээ, юнит-тесты вообще-то не для этого предназначены. Они проверяют, что на корректном, некорректном и граничном вводе поведение юнита соответствует ожидаемому. Не больше и не меньше. Задача программиста и состоит в том, чтобы выделить эти домены и определить критерии поведения.


Ну так покажи как ты это будешь тестировать? А может не граничный ввод сломает поведение юнита, а может просто качество работы снизится?

L>В крайне ограниченном домене.


Почему?

L>Примерно 100%. Я пишу код, и вижу, что у него есть такой вариант валидных с точки зрения вызывающего кода входных данных, при котором корректное поведение юнита невозможно.


Тебе знакомо словосочетание "комбинаторный взрыв"?

L>Так мы отловили проблему со сторонним оборудованием, которое в редких случаях кодировало в посылке метки времени что-то забавное вроде 32 глюкабря. Если бы мы этого не предусмотрели на этапе юнит-тестирования, создали бы огромные проблемы клиентам. Вместо этого клиент получил доказательство и повод для рекламации поставщику оборудования.


Ты меня ребячишь? Проверить на тупо некорректный ввод — самое первое и простое что можно сделать, даже юнит тестом. Метка времени у них не такая была, где такое видано вообще? :D
Re[8]: дебагинг vs unit-тесты
От: landerhigh Пират  
Дата: 07.09.16 15:26
Оценка:
Здравствуйте, chaotic-kotik, Вы писали:

L>>Естествтенно, но для этого придется устроиться туда на работу сначала.

CK>Т.е. нельзя. Ясно-понятно. Волшебные юнит тесты, которые существуют, но показать их нельзя. Видимо практика "правильно применять юнит-тесты" настолько сложная, что формализовать ее и описать не представляется возможным.

Наоборот, настолько простая, что даже не ясно, что там нужно рассказыват-показывать, и тем более формализовывать.

L>>А вот проблемы терминологии являются надуманными. Оно тестирует некий юнит? Тестирует. А то, что этот юнит "надерган" из кучи юнитов поменьше, к делу имеет уже опосредованное отношение.

CK>Поинт юнит-теста в том, что он тестирует что-то одно, поэтому ты можешь увидеть какой тест сломался и сразу понять что именно работает не так. Если тест работает не так, это не юнит-тест.

Это от размера и сложности юнита не зависит.

L>>Кроме случаев, когда немного подумали о том, как его сделать не трудоемким и масштабируемым

CK>Вот опять, никакой конкретики.

А какая тебе нужна конкретика? На любой пример найдется возражение про детский сад.

L>>Еще раз, на новомодные словечки мне давно положить.

CK>Звучит пассивно-агрессивно. Это не новомодные словечки а очень крутая современная технология, которая наряду с property based testing-ом, позволяет находить очень хитрые ошибки (например дыры в безопасности). Heartbleed был найден с помощью фаззинга (https://blog.hboeck.de/archives/868-How-Heartbleed-couldve-been-found.html),

Вот именно, что крутая современная технология, которая на поверку существует столько же, сколько и вся практика юнит-тестирования, пока некие блоггеры не придумали ей псевдонаучное название. Мы ее использовали ... да всегда, наверное. Удобный подход для определенных задач и совершенно неприменимый для других.

CK>интересно, как бы ты его юнит-тестами искал?


Никак. Юнит-тесты для этого не предназначены.

L>>Эээ, юнит-тесты вообще-то не для этого предназначены. Они проверяют, что на корректном, некорректном и граничном вводе поведение юнита соответствует ожидаемому. Не больше и не меньше. Задача программиста и состоит в том, чтобы выделить эти домены и определить критерии поведения.

CK>Ну так покажи как ты это будешь тестировать? А может не граничный ввод сломает поведение юнита, а может просто качество работы снизится?

Что значит "покажи"? Юнит-тестирование — это прикладная технология, на сферических юнитах в вакууме не рабоатет.

L>>В крайне ограниченном домене.

CK>Почему?

Каким бы крутым этот fuzzing тест не был, на сколько-нибудь сложной системе он тупо не сможет сгенерировать все возможные комбинации за обозримое время. Ты как раз про комбинаторный взрыв упоминал. Вот именно поэтому.

L>>Примерно 100%. Я пишу код, и вижу, что у него есть такой вариант валидных с точки зрения вызывающего кода входных данных, при котором корректное поведение юнита невозможно.

CK>Тебе знакомо словосочетание "комбинаторный взрыв"?

Именно поэтому я тестирую отдельные мелкие юниты.

L>>Так мы отловили проблему со сторонним оборудованием, которое в редких случаях кодировало в посылке метки времени что-то забавное вроде 32 глюкабря. Если бы мы этого не предусмотрели на этапе юнит-тестирования, создали бы огромные проблемы клиентам. Вместо этого клиент получил доказательство и повод для рекламации поставщику оборудования.

CK>Ты меня ребячишь? Проверить на тупо некорректный ввод — самое первое и простое что можно сделать, даже юнит тестом. Метка времени у них не такая была, где такое видано вообще? :D

Ага, это когда тебе рассказали и объяснили на пальцах, все кажется простым. А на подобных ерундовинах ежедневно крупные компании, сплошь из PhD и сортировщиков гномиков состоящие, апсираются.
Фишка в том, что некорректным вводом это становится только после скрупулезного обдумывания "а как мне это тестировать".
www.blinnov.com
Re[9]: дебагинг vs unit-тесты
От: chaotic-kotik  
Дата: 07.09.16 16:02
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>Наоборот, настолько простая, что даже не ясно, что там нужно рассказыват-показывать, и тем более формализовывать.


Тогда почему ты думаешь, что никто кроме тебя это не делал?

L>А какая тебе нужна конкретика? На любой пример найдется возражение про детский сад.


Детский сад.

L>Вот именно, что крутая современная технология, которая на поверку существует столько же, сколько и вся практика юнит-тестирования, пока некие блоггеры не придумали ей псевдонаучное название. Мы ее использовали ... да всегда, наверное. Удобный подход для определенных задач и совершенно неприменимый для других.


Щито?
Fuzzing существует давно и именно так и называется в научной литературе (о каких еще блоггерах ты говоришь?). Mutation-based fuzzing тулы появились сравнительно недавно, AFL где-то в конце 2014-го года стал более или менее юзабельным, libFuzzer еще позже. Ты по прежнему не понимаешь о чем я тут толкую, очевидно.

L>Никак. Юнит-тесты для этого не предназначены.


Ну вот видишь, целый класс ошибок, непаханное поле, а юнит-тесты не помогут.

L>Что значит "покажи"? Юнит-тестирование — это прикладная технология, на сферических юнитах в вакууме не рабоатет.


Классная отмазка. Так ведь про все что угодно можно сказать! А потом еще сослаться на NDA!

L>Каким бы крутым этот fuzzing тест не был, на сколько-нибудь сложной системе он тупо не сможет сгенерировать все возможные комбинации за обозримое время. Ты как раз про комбинаторный взрыв упоминал. Вот именно поэтому.


ОК. Фаззинг он такой, перебирает все возможные комбинаци ввода конечно же.

L>Именно поэтому я тестирую отдельные мелкие юниты.


ОК, юнит-тесты это самое главное. Видишь юнит-тесты в проекте, значит проект хороший, если их нет — то проект говно (привет google.brotli).

Пожалуй мне пора
Re[10]: дебагинг vs unit-тесты
От: landerhigh Пират  
Дата: 07.09.16 16:11
Оценка:
Здравствуйте, chaotic-kotik, Вы писали:

L>>Наоборот, настолько простая, что даже не ясно, что там нужно рассказыват-показывать, и тем более формализовывать.

CK>Тогда почему ты думаешь, что никто кроме тебя это не делал?

Многолетние наблюдения показывают, что внедрение юнит-тестирования чаще всего идет в виде карго-культа.

L>>А какая тебе нужна конкретика? На любой пример найдется возражение про детский сад.

CK>Детский сад.

Вот именно

CK>Fuzzing существует давно и именно так и называется в научной литературе (о каких еще блоггерах ты говоришь?). Mutation-based fuzzing тулы появились сравнительно недавно, AFL где-то в конце 2014-го года стал более или менее юзабельным, libFuzzer еще позже. Ты по прежнему не понимаешь о чем я тут толкую, очевидно.


Не появились, а ты о них прочитал. Почувствуйте разницу

L>>Никак. Юнит-тесты для этого не предназначены.

CK>Ну вот видишь, целый класс ошибок, непаханное поле, а юнит-тесты не помогут.

Помогут. Не сделать этих ошибок.

L>>Что значит "покажи"? Юнит-тестирование — это прикладная технология, на сферических юнитах в вакууме не рабоатет.

CK>Классная отмазка. Так ведь про все что угодно можно сказать! А потом еще сослаться на NDA!

Ну а "покажи" к чему тогда?

L>>Каким бы крутым этот fuzzing тест не был, на сколько-нибудь сложной системе он тупо не сможет сгенерировать все возможные комбинации за обозримое время. Ты как раз про комбинаторный взрыв упоминал. Вот именно поэтому.

CK>ОК. Фаззинг он такой, перебирает все возможные комбинаци ввода конечно же.

Вообще-то да. Для того, чтобы в системе из N компонентов, каждый из которых принимает всего лишь 3 разделимых домена данных (валидные, невалидные, и граничные), найти единственный, в котором забыли обработать один из этих доменов, случается переполнение, потребуется 3^N наборов данных. Твой любимый комбинаторный взрыв.
И это если у компонентов нет состояний.

L>>Именно поэтому я тестирую отдельные мелкие юниты.

CK>ОК, юнит-тесты это самое главное. Видишь юнит-тесты в проекте, значит проект хороший, если их нет — то проект говно (привет google.brotli).

Гугль в последнее время как раз эталон говнопроектов, так что камент весьма в тему.
www.blinnov.com
Re[11]: дебагинг vs unit-тесты
От: chaotic-kotik  
Дата: 07.09.16 16:56
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>Не появились, а ты о них прочитал. Почувствуйте разницу




L>Вообще-то да. Для того, чтобы в системе из N компонентов, каждый из которых принимает всего лишь 3 разделимых домена данных (валидные, невалидные, и граничные), найти единственный, в котором забыли обработать один из этих доменов, случается переполнение, потребуется 3^N наборов данных. Твой любимый комбинаторный взрыв.

L>И это если у компонентов нет состояний.

Я уже много раз упоминал afl, ты бы хоть загуглил и почитал как оно работает. Ну стыдно же!

Вот удивительно, ты (да и кто угодно тут вообще) мог бы узнать от меня много интересного, ну например как фаззить проекты в промышленных масштабах, у нас в проекте фаззится все что приходит от юзера, плюс те компоненты, которые декодируют данные БД (на случай corruption-а этих данных, чтобы быть уверенными в том что повреждение данных будет обработано и не повалит или повесит систему во время восстановления после сбоя). Ну либо узнать что-нибудь про системное тестирование перформанса, про те же функциональные тесты, как гонять их на _ВСЕХ_ платформах и всех возможных пользвоательских конфигурациях. Видимо поспорить интереснее, но раз так, то это сознательный выбор и глубокая убежденность в своей правоте, заслуживающие уважения, поэтому больше не буду лезть со своим
Re[12]: дебагинг vs unit-тесты
От: landerhigh Пират  
Дата: 07.09.16 17:02
Оценка:
Здравствуйте, chaotic-kotik, Вы писали:

L>>Не появились, а ты о них прочитал. Почувствуйте разницу

CK>

Перед зеркалом можешь это делать.
Мы точно использовали фаззер в 2008 для тестирования протоколов. Только называли его иначе.
Даже проект был по нахождению уязвимостей.

L>>Вообще-то да. Для того, чтобы в системе из N компонентов, каждый из которых принимает всего лишь 3 разделимых домена данных (валидные, невалидные, и граничные), найти единственный, в котором забыли обработать один из этих доменов, случается переполнение, потребуется 3^N наборов данных. Твой любимый комбинаторный взрыв.

L>>И это если у компонентов нет состояний.
CK>Я уже много раз упоминал afl, ты бы хоть загуглил и почитал как оно работает. Ну стыдно же!

Australian Football League?

CK>Вот удивительно, ты (да и кто угодно тут вообще) мог бы узнать от меня много интересного, ну например как фаззить проекты в промышленных масштабах,


Тебе говорят, что этой технике в обед сто лет. Ну назвали модным словом, суть от этого не меняется.
В любом случае, никакой алгоритм не победит комбинаторный взрыв.
Можно сколько угодно гонять фаззер, но баг, о котором никто не подозревал и который проявляется только в момент перевода часов на час назад, он в лучшем случае имеет шанс найти раз в год.
www.blinnov.com
Re[6]: дебагинг vs unit-тесты
От: Sharov Россия  
Дата: 07.09.16 17:46
Оценка:
Здравствуйте, chaotic-kotik, Вы писали:

CK>Интеграционное тестирование трудоемко, плохо масштабируется и редко встречается в природе.


Можете подробнее обосновать, почему плохо масштабируется?
Кодом людям нужно помогать!
Re[13]: дебагинг vs unit-тесты
От: chaotic-kotik  
Дата: 07.09.16 17:51
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>Перед зеркалом можешь это делать.

L>Мы точно использовали фаззер в 2008 для тестирования протоколов. Только называли его иначе.
L>Даже проект был по нахождению уязвимостей.

В 2008-м году ты использовал "тупой" фаззер, либо описывал протокол вручную и скармливал фаззеру.

L>Australian Football League?


American Fuzzy Lop
я тут уже и так и так писал, и по ссылке моей он есть, нужно очень сильно не желать слушать собеседника чтобы этого не заметить

L>Тебе говорят, что этой технике в обед сто лет. Ну назвали модным словом, суть от этого не меняется.

L>В любом случае, никакой алгоритм не победит комбинаторный взрыв.
L>Можно сколько угодно гонять фаззер, но баг, о котором никто не подозревал и который проявляется только в момент перевода часов на час назад, он в лучшем случае имеет шанс найти раз в год.

Фаззингу 100 лет в обед, да, кто спорит? Я тебе пытаюсь объяснить что туллинг поменялся, он больше не перебирает тупо все комбинации. Тот же afl — инструментирует исполняемый файл, гоняет твой код, определяет какие бранчи пройдены а какие нет и пытается сгенирировать такой ввод, который выдаст максимальное покрытие. Эта штука генерирует сама валидные структуры данных на диске, которые приложение потом может понять. В 2008-м ты ничего подобного использовать не мог, ничего подобного тогда не существовало.
И кстати, модное слово придумал чувак, который эту технику изобрел лет эдак 30 назад.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.