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. Пинаешь того, кто вызывал твой код.

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