Тестовые оснастки
Мы можем использовать нашу простую реализацию как стандарт для сравнения в тесте, который размещает в памяти два массива, а затем сравнивает поведение разных реализаций при разных значениях n, с и отступа внутри массива:
Ошибка, вынуждающая memset писать вне границ своего массива, скорее всего, проявится в байтах рядом с началом и концом массива, так что, оставив буферную зону, проще увидеть поврежденные байты. Уменьшается и вероятность того, что будут перезаписаны какие-то части программы в памяти. Для проверки записи вне границ мы должны проверить все значения s0 и s1, а не только те n байтов, которые должны были быть записаны.
Таким образом, наш набор тестов должен включить в себя проверку всех комбинаций значений:
Для n должны быть подставлены, по крайней мере, значения 2' -1, 2' и 2' + 1 для всех n от 0 до 16.
Все перечисленные значения, естественно, не надо встраивать в основную часть оснастки – надо предусмотреть возможность записывать их в отдельные массивы – вручную или программно. Лучше генерировать их программно – тогда не составит труда задать больше степеней двойки, включить большее количество разных отступов или больше символов.
Наши тесты заставят memset поработать на совесть; написать же их совсем не долго, не говоря уже об исполнении – всего надо проверить менее 3500 комбинаций. Все тесты полностью переносимы, так что при необходимости их можно использовать в любой среде.
С тестированием memset связана одна история, которая может послужить вам хорошим уроком. Однажды мы дали копию тестов для memset одному программисту, разрабатывавшему операционную систему и библиотеки для нового процессора. Через несколько месяцев мы (авторы тестов) начали работать с этой новой машиной. В какой-то момент большое приложение не прошло своего набора тестов.
Мы стали искать причины и после кропотливого труда докопались до истоков – проблема состояла в трудноуловимой неточности, связанной со знаковым расширением" в реализации memset на ассемблере. По непонятным причинам создатель библиотеки изменил тесты для memset, исключив из них проверку значений, больших Ox7F. Естественно, ошибка была найдена при запуске изначальной версии теста сразу после того, как подозрение пало на memset.
Функции типа memset хорошо поддаются проверке замкнутыми тестами, потому что они достаточно просты для того, чтобы можно было подобрать тестовые данные, перебрав все возможные варианты и охватив тем самым весь код. Так, для функции memmove можно перебрать все возможные комбинации различных значений перекрытия, направления и выравнивания. Этого, конечно, недостаточно для проверки всех операций копирования, но достаточно для тестирования всех возможных значений вводимых параметров.