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