Модуль 3.2 — Тестирование и фаззинг
Упражнение 3.2.1: Декодирование DNS
В этом упражнении вы потренируетесь в модульном тестировании, измерении покрытия кода и написании небольшого фаззера с помощью cargo-fuzz.
Вы будете работать с функцией dns_decode, которая реализует часть декодирования доменных имён из RFC 1035. В этом формате каждая часть доменного имени кодируется префиксом из одного байта с её длиной, за которым следуют байты самой части. Само доменное имя завершается нулевым байтом: «пустая часть» означает конец имени. Точки между частями не кодируются.
Например, mailcrab.tweedegolf.nl можно закодировать как b"\x08mailcrab\x0Atweedegolf\x02nl\0". При этом действуют два ограничения:
- Максимальный размер одной части — 63 байта.
- Максимальный размер полного доменного имени (включая точки) — 255 байт.
Мы подготовили «первую попытку» в exercises/3-crate-engineering/2-testing/1-dns-decode. Определение функции находится в src/lib.rs, а рядом есть src/main.rs, чтобы можно было запускать функцию интерактивно. В src/lib.rs даже есть несколько модульных тестов, так что старт хороший! Однако функция далеко не полностью протестирована и содержит ошибки. Возможно, вы их найдёте?
Упражнение 3.2.1.A: Улучшаем покрытие кода
Если вы ещё этого не делали, установите cargo-llvm-cov:
cargo install cargo-llvm-cov
После этого посмотреть покрытие можно, запустив cargo llvm-cov из каталога крейта. Также можно выполнить cargo llvm-cov --open, чтобы открыть отчёт о покрытии в браузере.
Хотя покрытие строк высокое, как видно, оно не равно 100%. Напишите модульный тест, чтобы покрытие строк для src/lib.rs действительно достигло 100% (на практике этого редко удаётся добиться).
Упражнение 3.2.1.B: Фаззинг
Даже при 100% покрытии в коде могут скрываться ошибки. Найдём их с помощью cargo-fuzz. Чтобы его установить, нужно не только установить cargo-fuzz, но и использовать тулчейн «nightly», который содержит все экспериментальные возможности. Установить оба инструмента можно так:
rustup toolchain install nightly
cargo install cargo-fuzz
Затем из каталога exercises/3-crate-engineering/2-testing/1-dns-decode выполните cargo fuzz init. Эта команда создаст подкаталог fuzz/ с шаблоном цели для фаззинга.
(Обратите внимание: подкаталог fuzz/ — это небольшой проект на Rust со своим Cargo.toml! Поэтому в него тоже можно добавлять зависимости, как в любой другой проект на Rust.)
Список доступных целей можно посмотреть командой:
cargo +nightly fuzz list
Цель fuzz_target_1 уже можно запустить командой
cargo +nightly fuzz run fuzz_target_1
Но она не найдёт ошибок. Отредактируйте fuzz/fuzz_targets/fuzz_target_1.rs, чтобы она делала что-то интересное! Также подумайте о добавлении других целей командой cargo +nightly fuzz add <TARGETNAME>.
Подсказки:
- Чтобы обратиться к функции
dns_decode, импортируйте её в цели фаззинга:#![allow(unused)] fn main() { use dns_parse::decode_dns_name; } - Начните с простого: напишите фаззер, который просто вызывает
dns_decodeдля входных данных&[u8], и проверьте, находит ли он падения. - Попробуйте проверить два других свойства, которые должны выполняться для
dns_decode:- максимальная длина части — 63 байта;
- максимальная длина доменного имени — 255 байт.
- Как и в модульных тестах,
assert!— ваш лучший друг.