Pull to refresh

Comments 15

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

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

Гораздо лучшая стратегия, реализовать архитектуру приложения без необходимости ручного управления памятью (shared_ptr, unique_ptr), а освободившиеся ресурсы перенаправить на тестирование логики, которая в свою очередь должна покрываться тестами на 100% без исключения.

Спасибо вам за проявленный интерес.

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

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

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

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

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

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

И да, все нужно тестировать:-)

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

Повторю еще раз: Умные указатели не панацея и в рамках С++. Особенно, когда речь идет о многопоточности (еще один продуктовый стандарт). На эту тему уже писали здесь, на Хабре: https://habr.com/ru/articles/311560/

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

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

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

Повторю еще раз: Умные указатели не панацея и в рамках С++. ...

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

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

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

Но там для подобных извращений была необходимость, т.к. процент покрытия тестами был определен контрактом и являлся одним из критериев приемки проекта, т.е. процент покрытия тестами был оформлен как одно из бизнес-требований.

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

Эти приёмы больше смахивают на недофаззинг. Есть инструменты, которые занимаются подобным - скармливают странные/ошибочные данные, немного меняют поведение стандартных функций и прочие прелести LD_PRELOAD. Правда всё это занимает заметно больше времени. Про какой-то похожий test suite расказывал Антон Полухин (~15:30)- наборы для проверки отказоусточивости микросервисов.

Как альтернатива запускать тесты с ulimit и гнать нагрузку на систему, тогда часть выделений памяти провалится. Правда всё это работает в POSIX-совместимых осях и для Win надо смотреть аналоги отдельно.

Вы совершенно правы. Точечный, контролируемый фаззинг. Не как с ulimit, а когда точно знаешь где, когда и в какой потоковой функции выстрелит.

Вот еще пара функций, которая мне также неоднократно пригождалась

А для чего именно вам были полезны такие memset и memcpy? Я вижу, что при некоторых значениях аргумента n они просто возвращают NULL вместо того, чтобы делать свою работу, но ума не приложу, зачем такое поведение может понадобиться в тестах.

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

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

Там по вашей второй ссылке написано:

The memcpy() function shall return s1; no return value is reserved to indicate an error.

И то же самое сказано на странице про memset. Для меня это выглядит так, как будто проверять, не вернули ли эти функции NULL, нет никакого смысла.

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

Там написано то, что написано.

no return value is reserved to indicate an error

Всё остальное - субъективно.

Соответственно, я проверяю так, как написано.

Всё остальное - субъективно.

О какой вы субъективности?

Где ответ NULL зарезервирован и является индикатором ошибки.

Это неправда. Стандарт прямо говорит, что у memset и memcpy никакое значение не зарезервировано для индикации ошибки. То есть, ни NULL, ни какое-то ещё. Ваши функции тестируют невозможные ситуации.

Sign up to leave a comment.

Articles