Как протестировать GPU и хаос Linux: руководство Митчелла Хашимото

Antithesis 62,5 тыс. 43 мин 7 мин 13.06.2025
Главное

Известный инженер и сооснователь компании HashiCorp Митчелл Хашимото выступил на техническом мероприятии компании Antithesis с докладом, посвященным преодолению барьеров в автоматизированном тестировании. На примере своих проектов — масштабных сетевых утилит HashiCorp и современного эмулятора терминала Ghosty — спикер продемонстрировал, как неочевидные паттерны проектирования позволяют тестировать графические процессоры, глубокие побочные эффекты и хаотичные конфигурации операционных систем. Главная идея выступления заключается в том, что практически любой «нетестируемый» код можно покрыть надежными проверками, если своевременно переработать архитектуру самого программного обеспечения.

🛠 Разрушая миф о «нетестируемом» коде 0:00

В практике разработки регулярно возникает ситуация, когда при открытии нового пул-реквеста автор заявляет, что написанный им код невозможно протестировать автоматически, добавляя, что он «проверил всё вручную». По словам Митчелла Хашимото, в реальности фраза «это нельзя протестировать» почти всегда означает лишь то, что это нельзя сделать легко или прямо сейчас. Инженер подчеркивает, что выбор, стоит ли тратить ресурсы на написание сложного теста, всегда остается за разработчиком, однако техническая возможность существует практически всегда.

Спикер разделяет весь процесс обеспечения качества на два одинаково важных направления, которые визуализированы в его презентации специальными пиктограммами:

По мнению Хашимото, разработчики слишком часто игнорируют именно тестируемость. Если архитектура приложения изначально не дружелюбна к автоматизации, не предоставляет нужных API и не имеет четкой структуры, то утверждение о невозможности тестирования становится горькой правдой. В своем докладе, который стал идейным продолжением его знаменитого выступления на GopherCon 2017 года, спикер представил эволюцию инженерных подходов от простых концепций к продвинутым.

📸 Снапшот-тестирование: магия контекстных диффов 8:22

Первой важной стратегией в арсенале инженера выступает снапшот-тестирование, известное также как тестирование по эталонным файлам (golden files) или поиск «абсолютной истины» (ground truth). Этот подход незаменим, когда программа генерирует сложный выходной формат, прямое попиксельное или построчное сравнение которого в коде превращается в кошмар. Однако Хашимото выделяет еще одно, более редкое преимущество снапшотов — они предоставляют разработчику гораздо более информативные разности (diffs) при сбоях.

В качестве примера спикер привел разработку своего текущего проекта Ghosty — кроссплатформенного эмулятора терминала с открытым исходным кодом, написанного на языке Zig. Программа содержит встроенный шрифт (sprite font) для рендеринга глифов (например, стрелок в стиле PowerShell), которые должны идеально вписываться в сетку терминала. В процессе разработки команда столкнулась с багом: при нечетных размерах сетки возникала ошибка смещения на один пиксель.

Для решения этой проблемы было внедрено снапшот-тестирование:

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

Подобный подход спикер успешно применял еще десять лет назад при работе над Terraform. На первом этапе Terraform строит граф ресурсов для выполнения. Проверять связи в графе обычными утверждениями (assertions) было тяжело, и инженеры тратили часы на поиск изменений. Хашимото изменил подход: система начала экспортировать ожидаемый и полученный графы в формат DOT, где отсутствующие связи окрашивались в красный цвет, а лишние — в зеленый, что драматически ускорило отладку.

🧼 Изоляция побочных эффектов: архитектура Read-Process-Write 13:47

Главным «множителем силы» для создания тестируемого кода Хашимото считает изоляцию побочных эффектов. Типичная дилемма разработчика: функция содержит полезную логику, но намертво завязана на внешнее сетевое или дисковое IO. Задача инженера — обнаружить внутри этого «супа со спецэффектами» чистую функциональную логику, извлечь её наружу и перестроить порядок выполнения.

На выходе получается чистый тест, работающий по принципу «мусор на входе — мусор на выходе» (garbage in, garbage out). Это означает, что разработчик сам искусственно передает входные данные вместо реального окружения, поэтому они должны быть строго реалистичными.

Анатомия эволюции этого паттерна наглядно видна на примере обработчика клавиатурного ввода в Ghosty:

  1. Ранняя архитектура: при нажатии клавиши функция последовательно опрашивала состояние мыши, проверяла состояние клавиатуры (модификаторы, повторы), считывала внутренние настройки терминала (схемы кодирования Kitty или legacy) и лишь затем кодировала символ и отправляла его в PTY. Код был полностью нетестируемым из-за сложности эмуляции состояний железа.
  2. Проблема регрессий: спикер признается, что исправление бага для одной раскладки ломало ввод для пользователей других, экзотических для него языков (русского, японского, китайского, венгерского). Например, симулировать настройки протокола Kitty совместно с русской раскладкой вручную было крайне сложно.
  3. Рефакторинг: Хашимото потратил несколько часов, чтобы разглядеть скрытую структуру кода, и переписал его в концепции Read-Process-Write. Весь сбор внешних данных (около 15 полей от ОС и настроек) был вынесен на самый верх функции. Сама логика кодирования превратилась в чистую функцию, которая принимает сухие параметры и возвращает результат.

Благодаря такой изоляции, сложнейший код кодирования стал легко поддаваться юнит-тестированию и фаззингу. По мнению Хашимото, именно этот шаг позволил Ghosty получить одну из самых стабильных и полных реализаций обработки клавиатурного ввода на рынке.

🎮 Как протестировать GPU, если документация молчит 21:12

Развивая тему изоляции, спикер перешел к одной из самых закрытых областей — программированию графических процессоров (GPU). Ghosty использует ресурсы видеокарты для отрисовки интерфейса терминала и некоторых сопутствующих вычислений. Когда Хашимото впервые задался вопросом, как тестировать шейдеры и логику рендеринга, он столкнулся с полным отсутствием информации в индустрии. Первая страница поисковой выдачи Google состояла из бесполезного мусора.

Тогда инженер скачал официальные спецификации DirectX, Direct 3D, Metal, OpenGL и Vulkan от Khronos Group, Microsoft и Apple. Общий объем изученных PDF-документов составил около 4000 страниц. Хашимото провел контекстный поиск по словам «test», «verify», «snapshot», «bug», «stability» и не обнаружил ни одного совпадения. В официальных руководствах трех крупнейших вендоров вообще не упоминалось, как тестировать код для GPU.

Для решения этой задачи Хашимото разделил архитектуру рендеринга на две изолированные составляющие, учитывая особенности работы с видеокартами:

Чтобы покрыть тестами CPU-часть (а это 13 000 из 15 000 строк кода рендерера Ghosty), Хашимото изолировал API-вызовы видеокарты. Функция принимает состояние сцены, генерирует граф задач и байтовые буферы, структура которых проверяется снапшот-тестами. Нетестируемым остался лишь тонкий слой перевода готового графа в нативные команды драйвера, который практически никогда не меняется.

Для тестирования GPU-стороны Хашимото применил метод полных проходов рендеринга (full render passes) с записью в буферы, доступные для чтения CPU. Тест искусственно создает миниатюрный терминал размером 2x2 пикселя, отправляет задачу на GPU, минуя вывод на физический экран, считывает получившийся фрейм из памяти и побайтово сравнивает картинки. Такой тест работает мгновенно и исключает проблемы стандартных скриншот-тестов (тайминги, рамки окон, позиции).

В качестве перспективных экспериментов для изоляции отдельных шейдеров спикер упомянул использование механизма Transform Feedback в OpenGL, позволяющего сбрасывать строковые переменные шейдеров напрямую в буфер CPU. Поскольку в современных API (Vulkan, Metal) эта функция отсутствует, для Metal Хашимото разработал концепт с вынесением логики в вычислительные шейдеры (compute shaders), хотя это и требует избыточного изменения структуры кода GPU.

❄️ Виртуализация через Nix: укрощение хаоса Linux-десктопа 34:39

Существуют компоненты системы, которые невозможно протестировать без полноценной симуляции внешнего мира — движений мыши, нажатий физических клавиш, сетевых сбоев или отказов диска. Для этих целей Хашимото задействует тестирование в виртуальных машинах через экосистему Nix. Признавая, что повальное увлечение Nix в индустрии порой утомляет разработчиков, спикер заявляет, что за свою 15-летнюю карьеру в сфере виртуализации и контейнеризации он не встретил более эффективного инструмента для этих задач.

Фреймворк тестирования Nix обладает тремя критически важными свойствами:

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

Процесс тестирования состоит из трех шагов: сначала в конфигурации NixOS описывается виртуальная машина со всеми драйверами, пользователями и ядрами; затем на языке Python пишется сценарий теста (фреймворк поддерживает даже распознавание текста на экране через OCR); после чего встроенный рантайм запускает тесты с возможностью интерактивной отладки через REPL.

Для Ghosty виртуальные машины жизненно необходимы при тестировании систем ввода (Input Methods), таких как IBus или FCITX, используемых для азиатских языков и эмодзи-клавиатур. По словам Хашимото, специфика Linux заключается в том, что фреймворк ввода, оконный менеджер и композитор пишутся разными командами, из-за чего их комбинации ведут себя непредсказуемо. В отличие от экосистемы Apple, где действует вертикальный мандат на жесткую интеграцию компонентов, разработчикам под Linux приходится вручную укрощать этот хаоc. Также через VM тестируются интеграции с рабочим столом, например появление пункта «Открыть в Ghosty» при правом клике мыши, что регулярно ломается при обновлениях графических сред.

💬 Цитаты

«В реальности фраза «это нельзя протестировать» почти всегда означает лишь то, что это нельзя сделать легко или прямо сейчас.»

Митчелл Хашимото 00:55

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

Митчелл Хашимото 07:16
👥 Спикер
🔗 Упомянутые сайты и проекты
📖 Термины
Снапшот-тестирование
Метод тестирования, при котором текущий результат работы программы сравнивается с сохраненным эталонным файлом.
Шейдер
Программа, предназначенная для выполнения на графическом процессоре (GPU) для рендеринга графики или вычислений.
NixOS
Дистрибутив Linux, построенный на базе пакетного менеджера Nix и использующий декларативный подход к конфигурации.
PTY
Псевдотерминал, программный интерфейс, эмулирующий поведение физического терминала в операционной системе.
📊 Цифры
🗓 Хронология
  1. 2017 год Митчелл Хашимото выступает на конференции GopherCon с докладом Advanced Testing with Go.
  2. Конец 2023 года Митчелл Хашимото официально покидает компанию HashiCorp.
⚖️ Другая сторона
Технологии и IT Митчелл Хашимото Ghosty NixOS снапшот-тестирование тестирование GPU