Здравствуйте, 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. Пинаешь того, кто вызывал твой код.
Через три месяца Виллабаджо захватила свою долю рынка бета-версией продукта, дерзкой и как пуля резкой, а Вилларибо продолжает обкладывать арифметику тестами и/или выпускать гигантского ленивца (каждая проверка имеет свою цену в байтах и наносекундах).