Тестирование рендеринга

Рендеринг в WebGL отличается от классической DOM-верстки тем, что результат вычисляется на GPU и не имеет прямого текстового представления в дереве элементов. В контексте Deck.gl это усложняется наличием многослойной архитектуры, где каждый слой управляет собственными шейдерами, буферами и состоянием WebGL-контекста. Тестирование таких систем требует комбинирования нескольких стратегий: модульного тестирования логики, интеграционного тестирования сцены и визуальной регрессии.

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

Архитектура тестируемости Deck.gl

Deck.gl строится вокруг концепции слоёв (layers), каждый из которых описывает:

  • геометрию данных
  • шейдеры (vertex/fragment)
  • параметры визуализации
  • обновление состояния при изменении props

Тестирование должно учитывать три уровня:

  1. Логический уровень — проверка преобразований данных и поведения слоёв
  2. WebGL-уровень — корректность создания буферов, шейдеров, uniforms
  3. Пиксельный уровень — итоговое изображение сцены

Разделение этих уровней позволяет локализовать ошибки и снижать стоимость тестов.

Модульное тестирование слоёв

Модульные тесты в Deck.gl обычно направлены на проверку методов жизненного цикла слоя:

  • initializeState
  • updateState
  • draw
  • finalizeState

Особое внимание уделяется attributeManager, который отвечает за управление атрибутами вершин.

Пример типичных проверок:

  • корректность вычисления атрибутов из входных данных
  • правильное обновление buffer attributes при изменении props
  • отсутствие лишних пересозданий WebGL-ресурсов

В таких тестах WebGL часто мокается или заменяется headless-реализацией, чтобы избежать зависимости от GPU.

Использование headless WebGL

Для запуска тестов без реального браузера применяется headless WebGL контекст, чаще всего через gl библиотеки:

  • headless-gl
  • regl headless context
  • custom WebGL mock layer

Основная цель — симулировать API WebGLRenderingContext.

Типичный подход:

  • создание виртуального контекста
  • инициализация слоя Deck.gl
  • вызов методов рендеринга
  • проверка вызовов WebGL API

Однако такой подход ограничен: он не гарантирует корректность итоговой визуализации, так как не проверяет работу GPU-пайплайна.

Интеграционное тестирование сцены

Интеграционные тесты проверяют взаимодействие нескольких слоёв в рамках одного Deck-инстанса.

В таких тестах проверяются:

  • порядок рендеринга слоёв
  • корректность композиции blending modes
  • работа освещения и материалов
  • взаимодействие с view state (zoom, pitch, bearing)

Особое значение имеет тестирование viewState, так как изменение камеры может влиять на матрицы трансформации в шейдерах.

Визуальная регрессия как основной метод

Наиболее надёжным способом тестирования рендеринга является визуальная регрессия. Суть заключается в сравнении эталонного изображения (golden image) с текущим результатом рендеринга.

Пайплайн обычно включает:

  • запуск сцены в браузере
  • рендер в canvas
  • сохранение PNG-снимка
  • сравнение с эталоном через pixel diff

Инструменты:

  • Puppeteer
  • Playwright
  • pixelmatch
  • reg-suit

Ключевой момент — контроль детерминизма. Даже небольшие изменения в GPU или браузере могут приводить к шуму в изображении.

Детеминизм рендеринга

Для стабильных тестов необходимо минимизировать недетерминированность:

  • фиксирование размеров canvas
  • отключение анимаций и transition эффектов
  • установка фиксированного viewState
  • использование стабильных данных
  • отключение device pixel ratio вариативности

Особое внимание уделяется WebGL параметрам:

  • preserveDrawingBuffer: true
  • фиксированный antialias
  • стабильный clearColor

Сравнение изображений

Сравнение пиксельных результатов выполняется с учётом допуска погрешности.

Алгоритм pixel diff:

  • сравнение RGB каналов
  • вычисление delta для каждого пикселя
  • применение порога tolerance
  • подсчёт процентного расхождения

Критически важно избегать ложных срабатываний из-за:

  • субпиксельного сглаживания
  • различий GPU
  • различий браузеров

Тестирование шейдеров

Deck.gl активно использует GLSL-шейдеры, которые невозможно тестировать стандартными unit-тестами.

Подходы:

1. Компиляционные тесты

Проверка, что шейдер компилируется без ошибок:

  • vertex shader
  • fragment shader

2. Snapshot uniforms

Фиксация значений uniform-переменных:

  • матрицы трансформации
  • цвета
  • параметры освещения

3. Частичная эмуляция GPU логики

Иногда используется CPU-версия вычислений для сравнения результатов.

Тестирование AttributeManager

AttributeManager является ключевым компонентом оптимизации Deck.gl.

Проверяются сценарии:

  • добавление атрибутов
  • обновление при изменении данных
  • корректность инстансинга
  • переиспользование buffer объектов

Важно контролировать:

  • количество вызовов update
  • пересоздание buffer’ов
  • корректность частичных обновлений (dirty flags)

Mocking Deck.gl контекста

Для ускорения тестов часто используется мокирование:

  • WebGL context
  • Texture creation
  • Framebuffer operations

При этом важно не переусердствовать: чрезмерный mocking может скрыть реальные ошибки рендеринга.

Баланс достигается разделением:

  • unit-тесты (моки)
  • integration-тесты (headless GL)
  • visual tests (реальный браузер)

Тестирование производительности рендеринга

Помимо корректности изображения важна производительность.

Метрики:

  • FPS при отрисовке
  • время обновления слоя
  • количество draw calls
  • использование GPU memory

Методы:

  • profiling через Chrome DevTools
  • измерение frame time через requestAnimationFrame
  • нагрузочные сцены с большим количеством объектов

Особое внимание уделяется слоям с инстансингом, где ошибка может приводить к резкому росту draw calls.

CI-интеграция тестов рендеринга

В CI-среде тестирование рендеринга требует стабильной среды выполнения:

  • фиксированная версия браузера
  • одинаковые GPU эмуляции (software rendering)
  • отключение фоновых процессов

Типичный pipeline:

  • установка зависимостей
  • запуск headless browser
  • прогон сцен
  • сохранение snapshots
  • сравнение с эталоном
  • генерация отчёта diff

При нестабильных результатах применяются retry-механизмы и расширенные пороги diff.

Типичные источники ошибок в визуальных тестах

На практике наиболее частые проблемы связаны с:

  • различиями WebGL драйверов
  • нестабильным order blending
  • асинхронной загрузкой данных
  • floating point precision
  • различиями DPR (device pixel ratio)
  • недетерминированным шумом шейдеров

Отдельно выделяются ошибки порядка рендеринга слоёв, которые могут проявляться только при определённых camera angles.

Стратегии изоляции тестов

Для повышения стабильности применяются следующие подходы:

  • один слой на тест при unit-уровне
  • фиксация всех входных данных
  • отключение анимаций
  • разделение тестов по типам слоёв (IconLayer, ScatterplotLayer, GeoJsonLayer)
  • минимизация сцены для визуальных тестов

Изоляция снижает количество флакiness и ускоряет диагностику.

Тестирование взаимодействия с внешними источниками данных

Deck.gl часто работает с потоковыми или асинхронными данными.

Тестирование включает:

  • подмену fetch-слоя
  • фиксацию JSON-ответов
  • имитацию streaming updates
  • проверку корректности incremental rendering

Особое внимание уделяется сценам, где данные обновляются без полного пересоздания слоя.

Проверка корректности трансформаций координат

Многие слои зависят от преобразования координат:

  • географические координаты (lat/lon)
  • экранные координаты
  • world coordinates

Тестирование включает проверку:

  • матриц проекции
  • корректности zoom scaling
  • стабильности при пересчёте viewport

Ошибки в трансформациях часто проявляются только при крайних значениях масштаба или на высоких zoom уровнях.

Долгоживущие тестовые сцены

Для выявления утечек памяти и деградации производительности используются long-running тесты:

  • многократное создание и уничтожение Deck instance
  • циклическое обновление данных
  • стресс-тесты с тысячами объектов

Контролируются:

  • рост memory usage
  • количество WebGL ресурсов
  • стабильность FPS во времени