Филиал sqaunderhood в Телеграме

Про требования (ссылка):

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

Всё-as-a-Code (ссылка):

Мы привыкли, что артефакты для большинства стадии разработки ПО (SDLC) можно представлять в виде кода: тесты как код, инфраструктура как код, документация как код (@docops), архитектура как код, мокапы как код (https://imagineui.github.io/ru/). Потому что в таком случае к этим артефактам применимы все те же подходы, которые используются для кода: версионирование, ревью, автоматические проверки и т.д. Казалось, что требования к ПО были последним бастионом в этом движении, но с doorstop пал и этот бастион и теперь даже системные требования превратить в код. Каждое требование - отдельный файл в формате YAML, есть интеграция с Python.

Кстати требования для самого инструмента описаны в виде требований doorstop. Ещё больше деталей в презентации.

Про виртуализацию (ссылка):

Контейнерная и гипервизорная виртуализация сильно упростили создание тестовых стендов и уже трудно представить как мы обходились без этого раньше. Расстраивает, что из-за высокой популярности инструментов коллеги начинают переусложняют вещи, которые можно было бы сделать проще. Если процессу нужно ограничить доступ к файловой системе, то вполне можно обойтись chroot(8), если нужно изолировать сетевой стек или сделать ограничение по доступу к ресурсам системы, то обойтись unshare(1). Это не намного сложнее, чем использовать тот же docker или podman, но нужно один раз разобраться в том, из чего построены контейнеры.

Базовых механизмов всего четыре:

  • namespaces (пространства имён) используются для группировки объектов ядра в различные наборы, к которым могут обращаться определенные деревья процессов. Звучит немного сложно, поэтому сразу пример - пространства имен PID ограничивают представление списка процессов процессами в пространстве имен. Всего есть несколько видов пространства имен, см. ниже.
  • capability используются для более тонкой настройки полномочий для процесса. Если вы использовали опцию -cap-add для docker, то это оно.
  • cgroups это механизм установки ограничений на доступ процесса к ресурсам системы (память, процессор).
  • setrlimit - ещё один механизм для ограничения доступа к ресурсам системы наряду с cgroups. Он старее, чем cgroups, но может делать то, что cgroups не позволяют.

Пространства имён бывают следующими:

  • mount - монтирование и размонтирование ФС не будет иметь никакого эффекта на ФС самой системы.
  • UTS - установка имени машины (hostname) или доменного имени не будет иметь никакого эффекта для основной ОС.
  • IPC - процесс будет иметь независимые от основной ОС объекты IPC: очереди сообщений, семафоры и разделяемую память.
  • network - процесс сможет иметь независимые от основной ОС стеки протоколов IPv4 и IPv6, таблицы маршрутизации и др.
  • PID - процесс будет иметь отдельное представление дерева процессов.
  • user- процесс с таким пространством имён будет иметь отдельный набор UID, GID. Например суперпользователь в этом пространстве имён не будет иметь ничего общего с суперпользователем из основной ОС.

Чтобы понять лучше эти механизмы можно воспользоваться двумя утилитами: unshare и nsenter. Первая позволить из командной строки создавать пространства имен для отдельных процессов, а вторая подключаться к уже созданным пространствам имён.

Когда прийдёт понимание этих механизмов, то при необходимости использования контейнерной виртуализации вы сами себя будете спрашивать: “- Мне действительно нужно использовать docker с его абстракциями в тысячи строк кода или можно обойтись более простыми средствами?”.

Прекрасной иллюстрацией к сказанному будет статья, в которой автор описывает тестирование сетевого сервера lldpd с использованием pytest и сетевых пространств имён.

Про Hypothesis (ссылка):

За последнее время в Hypothesis (библиотека на Python для property-based тестирования) появились новые фишки, о которых я расскажу в нескольких следующих постах.

Гугл недавно опубликовал под свободной лицензией исходный код Аtheris. Это фаззер для кода на Python, он использует LibFuzzer и, как следствие, генерирует такие входные данные, которые максимимизируют покрытие кода, то есть это фаззер с обратной связью. Как пишут в анонсе, в 2013 году гуглеры организовались и начали писать фаззеры для внутренних проектов кода. В рамках этой активности и был создан Аtheris. Hypothesis позволяет использовать внешние фаззеры для генерирования тесткейсов (например на базе AFL - python-afl), и теперь есть интеграция с Atheris. В инфраструктуре oss-fuzz, в которой Гугл фаззит код открытых проектов, теперь появилась поддержка проектов на Python и туда уже добавили два модуля (ujson и urllib3), которые теперь регулярно тестируются с помощью связки Hypothesis и atheris. Все это говорит о том, что Hypothesis умеет генерировать тесты не только с использованием свойств, но и абсолютно случайными данными. Кстати нативная поддержка coverage-guided генератора была в самом Hypothesis, но её удалили в основном из-за проблем с производительностью в 2018 году (см. тикет).

Пример кода для тестирования с использованием Atheris и Hypothesis:

@given(obj=JSON_OBJECTS, kwargs=st.fixed_dictionaries(UJSON_ENCODE_KWARGS))
def test_ujson_roundtrip(obj, kwargs):
    """Check that all JSON objects round-trip regardless of other options."""
    assert obj == ujson.decode(ujson.encode(obj, **kwargs))

if __name__ == "__main__":
    atheris.Setup(sys.argv, test_ujson_roundtrip.hypothesis.fuzz_one_input)
    atheris.Fuzz()

Про баг в сетевом стеке Linux ядра (ссылка):

Захватывающая история о неработающей синхронизации в rsync, причиной которой был баг 24-летней (!) давности в реализации протокола TCP Linux ядра. Буквально через несколько часов после появления письма с описанием проблемы в рассылке Neal Cardwell подготовил патч с исправлением (фикс из двух строк). Знаю Neal Cardwell как автора packetdrill - утилиты для функционального тестирования TCP, IP протоколов. С её помощью тесткейсы для тестирования можно описывать на DSL в декларативном стиле и они выглядят короче и нагляднее, чем такой же тексткейс, но на Си.

// Create a listening TCP socket.
0 socket(..., SOCK_STREAM, IPPROTO_TCP) = 3
+0 setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
+0 bind(3, ..., ...) = 0
+0 listen(3, 1) = 0

// Establish a new connection.
+0 < S 0:0(0) win 32792 <mss 1000,sackOK,nop,nop,nop,wscale 7>
+0 > S. 0:0(0) ack 1 win 29200 <mss
 1460,nop,nop,sackOK,nop,wscale 6>
+.1 < . 1:1(0) ack 1 win 257
+0 accept(3, ..., ...) = 4

// sequence number out of window!
+.010 < R. 29202:29202(0) ack 1 win 257

// verify that the connection is OK
+.010 write(4, ..., 1000) = 1000
+0 > P. 1:1001(1000) ack 1

Подписывайтесь!

Теги: softwaretestingfeed