Visual regression тесты

Visual regression тестирование в CesiumJS основано на сравнении эталонных изображений (golden images) с текущими рендерами сцены. В отличие от unit-тестов, проверяющих поведение функций, визуальные тесты фиксируют итоговый результат WebGL-рендеринга: положение камеры, отрисовку тайлов, освещение, стилизацию объектов и взаимодействие слоёв. Основная цель — выявление непреднамеренных изменений визуального вывода при обновлении кода, зависимостей или конфигурации сцены.

В 3D-графике регрессия проявляется не как логическая ошибка, а как изменение пикселей. Даже незначительное смещение камеры или различие в порядке отрисовки приводит к заметным артефактам. В контексте CesiumJS ключевыми источниками визуальной нестабильности становятся:

  • недетерминированность GPU-рендеринга WebGL;
  • различия между драйверами видеокарт;
  • особенности компоновки тайлов (3D Tiles, imagery layers);
  • асинхронная загрузка ресурсов;
  • плавающая точка и накопление ошибок при вычислениях матриц;
  • динамическое освещение и атмосферные эффекты.

Из-за этого визуальные тесты требуют строгой фиксации состояния сцены перед созданием скриншота.

Базовая архитектура визуальных тестов

Типичный pipeline визуального регрессионного тестирования включает:

  1. Инициализация сцены CesiumJS.
  2. Фиксация камеры и параметров рендера.
  3. Дождаться завершения загрузки всех ассетов.
  4. Снять скриншот canvas.
  5. Сравнить изображение с эталоном.
  6. Сохранить diff при расхождениях.

Основой служит headless-браузер (Puppeteer или Playwright), выполняющий рендеринг в контролируемой среде. Важно, чтобы окружение было максимально стабильным: одинаковые версии браузера, фиксированное разрешение, одинаковые GPU-параметры (или их эмуляция).

Фиксация состояния сцены

В CesiumJS сцена динамична по умолчанию: движется время, обновляются тайлы, пересчитывается освещение. Для тестов это необходимо отключить или стабилизировать.

Фиксация времени

Одним из ключевых источников нестабильности является система времени JulianDate. Для устранения флуктуаций:

  • устанавливается фиксированное время сцены;
  • отключается анимация;
  • блокируется автоматическое обновление clock.

Это позволяет гарантировать, что солнце, тени и атмосферные эффекты остаются неизменными.

Камера и матрицы

Камера фиксируется вручную:

  • position (Cartesian3)
  • heading / pitch / roll
  • field of view

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

Управление рендер-циклом

CesiumJS по умолчанию работает в режиме непрерывного рендера. Для тестирования используется режим событийного обновления:

  • requestRenderMode = true снижает количество перерисовок;
  • maximumRenderTimeChange ограничивает скачки времени;
  • ручной вызов scene.render() обеспечивает контроль над моментом захвата кадра.

Это устраняет эффект «дрожания» изображения между кадрами.

Детерминизм тайлов и данных

3D Tiles и imagery layers являются асинхронными. В тестовой среде требуется:

  • дождаться полной загрузки tileset (tilesLoaded / readyPromise);
  • зафиксировать LOD-уровень;
  • отключить прогрессивную загрузку;
  • при необходимости использовать локальные тестовые данные.

Особую проблему создают глобальные датасеты, где содержимое зависит от геометрии камеры. Даже небольшое смещение приводит к подгрузке других тайлов.

Работа с WebGL-артефактами

WebGL не гарантирует бит-в-бит одинаковый результат между системами. Поэтому применяются стратегии стабилизации:

  • фиксированное значение devicePixelRatio;
  • отключение постобработки (bloom, FXAA, HDR);
  • унификация precision shaders;
  • отключение анизотропной фильтрации;
  • фиксированный canvas size.

Некоторые тестовые окружения используют software rendering (SwiftShader), что повышает воспроизводимость ценой производительности.

Подходы к снятию скриншотов

Снимок выполняется через canvas API или средствами браузера:

  • page.screenshot() (Playwright/Puppeteer);
  • capture canvas buffer через toDataURL;
  • прямой доступ к WebGL framebuffer.

На практике чаще используется браузерный screenshot, так как он включает финальную компоновку страницы.

Критически важно исключить:

  • DOM overlay элементы;
  • UI контролы Cesium;
  • всплывающие credits.

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

После получения двух изображений применяется pixel diff алгоритм. Наиболее распространённые подходы:

  • послойное сравнение RGB каналов;
  • пороговое значение (tolerance threshold);
  • игнорирование небольших отклонений (anti-aliasing noise);
  • маскирование динамических областей.

Типичный стек включает pixelmatch или аналогичные библиотеки.

Результатом сравнения становится:

  • процент различающихся пикселей;
  • diff-изображение;
  • статус прохождения теста.

Управление флаками (flaky tests)

Визуальные тесты CesiumJS склонны к нестабильности. Основные методы стабилизации:

  • увеличение tolerance;
  • повторный запуск с усреднением результатов;
  • фиксация seed для процедурных генераторов;
  • отключение анимаций и плавных переходов;
  • изоляция тестов друг от друга.

Особое внимание уделяется камере: даже микроскопические изменения матриц приводят к лавинообразным отличиям пикселей.

Организация эталонных изображений

Golden images формируются вручную или автоматически при первичном запуске тестов. Практика включает:

  • хранение базовых скриншотов в репозитории;
  • разделение по версиям браузера;
  • группировка по сценам (terrain, tiles, primitives);
  • явное версионирование эталонов.

При изменении визуального поведения обновление baseline происходит осознанно, с проверкой diff.

Сценарии тестирования в CesiumJS

Наиболее распространённые категории визуальных тестов:

Террейн и рельеф

Проверяется корректность:

  • интерполяции высот;
  • затенения склонов;
  • стыковки тайлов terrain provider.

Ошибки здесь часто проявляются как «разрывы» поверхности.

3D Tiles

Тестируется:

  • корректная загрузка иерархий;
  • уровень детализации;
  • геометрическая точность зданий и объектов.

Примитивы и геометрия

Проверка отрисовки:

  • полигонов;
  • линий;
  • billboards;
  • labels.

Атмосферные эффекты

Фиксируются:

  • небо и градиенты;
  • туман;
  • освещение по времени суток.

Эти элементы особенно чувствительны к floating point изменениям.

Интеграция с CI/CD

В CI среде визуальные тесты требуют дополнительной стабилизации:

  • фиксированные Docker-образы браузеров;
  • отключение GPU или использование software rasterization;
  • одинаковые шрифты и системные ресурсы;
  • кеширование данных Cesium.

Результаты тестов сохраняются как артефакты сборки:

  • diff images;
  • отчёты сравнения;
  • логи рендера.

Частые источники расхождений

В CesiumJS наиболее типичные причины визуальных регрессий:

  • изменение порядка отрисовки transparent объектов;
  • различия в depth buffer precision;
  • обновления shader компилятора;
  • изменение алгоритмов LOD;
  • обновления браузера;
  • изменение структуры tileset.

Даже обновление minor версии может вызвать визуальные сдвиги.

Стратегии минимизации ложных срабатываний

Для уменьшения false positives применяются:

  • маскирование динамических зон (credits, UI);
  • region-based diff вместо full-frame;
  • сглаживание изображения перед сравнением;
  • игнорирование 1–2 px шумов;
  • фиксация random seed для процедурных данных.

Некоторые системы вводят multi-threshold модель: строгую для критических зон и мягкую для фоновых областей.

Масштабирование набора тестов

При росте проекта визуальные тесты начинают занимать значительное время. Оптимизации включают:

  • параллельный запуск браузеров;
  • разделение сцен на группы;
  • инкрементальное тестирование только изменённых сцен;
  • кэширование tileset и imagery.

Также применяется стратегия «smoke visual tests» — небольшой набор критических сцен для быстрых проверок.

Работа с различиями GPU

Даже при одинаковом коде разные GPU дают разные результаты рендеринга. Это особенно заметно в:

  • антиалиасинге линий;
  • прозрачности;
  • сортировке фрагментов;
  • precision вычислениях.

Для минимизации влияния используют:

  • WebGL software backend;
  • строгие shader precision qualifiers;
  • унификацию браузерных настроек.

Маскирование нестабильных пикселей

В сложных сценах часто применяется маска стабильности:

  • определяются области сцены, важные для проверки;
  • остальная часть изображения игнорируется;
  • сравнение проводится только по ROI (region of interest).

Это позволяет тестировать сложные сцены без постоянных ложных падений.

Долгосрочная эволюция визуальных тестов

При развитии проекта визуальные тесты начинают выполнять не только роль регрессии, но и:

  • документации визуального состояния сцены;
  • контроля качества рендеринга;
  • анализа производительности через косвенные артефакты (LOD деградации);
  • проверки кросс-браузерной совместимости.

В экосистеме CesiumJS визуальное тестирование становится частью инфраструктуры стабильности 3D-приложений, где корректность определяется не логикой, а визуальной идентичностью кадров.