USER
Ты экспертный технический писатель. Напиши подробную документацию по тексту "## Канонизация
У тестов есть возможность сообщить о своих результатах в виде данных, которые необходимо верифицировать с каноническими. Такие тесты необходимо первый раз канонизировать, после чего системе будут доступны референсные данные для сравнения. Канонический результат будет сохранен рядом с тестом в директорию canondata/<test name>/result.json или вынесен в ресурс Sandbox, в зависимости от переданных параметров в тесте.
Каждый фреймворк для написания тестов предоставляет свою поддержку механизма канонизации. Канонизация поддержана для всех языков, кроме C++.
У тестов есть возможность сообщить о своих результатах в виде данных, которые необходимо сравнить с каноническими.
Чтобы система имела доступ к каноническим данным, их нужно в первый раз канонизировать.
Необходимо отметить, что сравнение с каноническими значениями происходит на стороне bob, следовательно, чтобы иметь возможность верифицировать такие тесты, их надо запускать через bob make.
Каждый фреймворк для написания тестов предоставляет свою поддержку механизма канонизации, описанную в соответствующих разделах.
Канонизация в C++
Важно
На данный момент нативная канонизация в C++ тестах не поддержана.
DEVTOOLS-1467
Ниже описано несколько альтернативных способов автоматически обновлять канонические данные для C++ тестов, помимо нативной канонизации.
Матчер в gtest
Матчер NGTest::GoldenFileEq(filename), доступный в модуле GTEST, умеет сравнивать тестируемые данные с содержимым указанного файла и обновлять этот файл при запуске теста с аргументом --test-param GTEST_UPDATE_GOLDEN=1:
TEST(Suite, Name) {
std::string data = RenderSomeTextData();
EXPECT_THAT(data, NGTest::GoldenFileEq(SRC_("golden/data.txt")));
}
Мы настоятельно не рекомендуем использовать этот способ для тестирования бинарных данных. В репозитории следует хранить только файлы, изменения в которых можно посмотреть на код-ревью: тексты, конфиги, картинки.
Дополнительный EXECTEST
Можно канонизировать вывод от тестов или выходные файлы с помощью отдельного bob.make с EXECTEST(), который будет зависеть от теста и запускать вручную указанные тесты. Документация про EXECTEST. Также см. пример.
Канонизация в Python
Тест сообщает о данных, которые нужно сравнить с каноническими, путем их возврата из тестовой функции командой return. На данный момент поддерживаются все простые типы данных, списки, словари и файлы:
def test():
return [1, 2.0, True]
Канонизация файлов
Для того, чтобы вернуть файл, необходимо воспользоваться функцией
bobtest.common.canonical_file(path, diff_tool=None, local=False, universal_lines=False, diff_file_name=None, diff_tool_timeout=None)
path: путь до файла;
diff_tool: путь к программе для сравнения канонических файлов. По умолчанию используется diff. Нестандартные программы для сравнения канонических файлов;
local: сохранять файл в репозиторий, а не в Sandbox. Мы настоятельно не рекомендуем хранить бинарные канонические файлы в репозитории;
universal_lines: нормализует EOL;
diff_file_name: название для файла с дифом. По умолчанию - <имя сравниваемого файла>.diff);
diff_tool_timeout: таймаут на запуск diff tool.
Для канонизации нескольких файлов, необходимо вернуть список или словарь с объектами canonical_file.
def test1():
return [bobtest.common.canonical_file(output_path1), bobtest.common.canonical_file(output_path2)]
def test2():
return {
"path1_description": bobtest.common.canonical_file(output_path1),
"path2_description": bobtest.common.canonical_file(output_path2)
}
Пример
Канонизация файла
Канонизация директорий
Для того, чтобы канонизировать содержимое директории, необходимо воспользоваться функцией
bobtest.common.canonical_dir(path, diff_tool=None, diff_file_name=None, diff_tool_timeout=None)
path: путь до директории;
diff_tool: путь к программе для сравнения канонических файлов. По умолчанию используется diff. Нестандартные программы для сравнения канонических файлов;
diff_file_name: название для файла с дифом. По умолчанию - <имя сравниваемой директории>.diff);
diff_tool_timeout: таймаут на запуск diff tool.
Важно
Все директории загружаются в sandbox, их нельзя сохранять локально в canondata, в отличие от canonical_file.
Пример
Канонизация директории
Канонизация запуска программы
Для того, чтобы канонизировать stdout программы нужно воспользоваться функцией
bobtest.common.canonical_execute(binary, args=None, check_exit_code=True, shell=False, timeout=None, cwd=None, env=None, stdin=None, stderr=None, creationflags=0, file_name=None, save_locally=False)
binary:абсолютный путь до программы;
args: аргументы программы;
check_exit_code: бросает ExecutiopnError, если запуск программы завершается с ошибкой;
shell: использовать shell;
timeout: таймаут на выполнении програмы;
cwd: рабочая директория;
env: окружение для запуска команды;
stdin: поток ввода команды;
stderr: поток ошибок команды;
creationflags: creationFlags команды запуска;
file_name: имя output файла. По умолчанию используется имя программы binary. Конечное название файла будет <file_name>.out.txt
save_locally: сохранять файл в репозиторий, а не в Sandbox. Мы настоятельно не рекомендуем хранить бинарные канонические файлы в репозитории;
diff_tool: путь к программе для сравнения канонических файлов. По умолчанию используется diff. Нестандартные программы для сравнения канонических файлов;
diff_file_name: название для файла с дифом;
diff_tool_timeout: таймаут на запуск diff tool.
Для канонизации stdout запуска python-скриптов можно воспользоваться функцией
bobtest.common.canonical_py_execute(script_path, args=None, check_exit_code=True, shell=False, timeout=None, cwd=None, env=None, stdin=None, stderr=None, creationflags=0, file_name=None)
Если нужно канонизировать вывод нескольких программ, то результат canonical_execute можно сохранять в переменные, а в тесте вернуть словарь:
res1 = bobtest.common.canonical_execute(binary1)
res2 = bobtest.common.canonical_execute(binary2)
return {"stand_initializer": res1, "prog2": res2}
Примеры
Канонизация stdout программы
Канонизация stdout python script
Ожидаемое несоответствие канонизации
В pytest поддерживается механизм xfail (expected to fail), которым размечают тесты, которые падают ожидаемо. Однако, в силу того что канонизация является надстройкой над всеми тестовыми фреймворками, их механизмы никак не могут влиять или управлять процедурой сверки канонических данных. Поэтому тест размеченный xfail и имеющий diff с каноническими данными будет в статусе поломки. Для того чтобы разметить, что у теста канонические данные отличаются от полученных в ходе тестирования или отсутствую канонические данные и это нормально, его нужно разметить маркером @pytest.mark.xfaildiff. В этом случае:
Если тест упадёт он будет в статусе FAIL
Если тест успешно отработает и вернёт канонические данные, но их не с чем будет сравнить (канонизации ещё не было и вы не хотите канонизировать неверные данные) или они будут отличаться, то статус теста будет XFAIL
Если тест успешно отработает и канонические данные совпадут, тест упадёт со статусом XPASS
При канонизации (-Z/--canonize-tests) тесты рамечанные xfaildiff НЕ канонизируются.
Примеры
xfaildiff в качестве pytest.param и декоратора
Канонизация в Java
Чтобы канонизировать данные, нужно использовать функции из devtools/jtest.
Канонизация объектов
Для канонизации объектов нужно использовать функцию ru.bobndex.devtools.test.Canonizer.canonize(Object). Важно помнить, что объект будет сериализован с помощью new Gson().toJson(obj).
Важно
На каждый тест может быть только один вызов ru.bobndex.devtools.test.Canonizer.canonize(Object): если их будет несколько, последний перетрет изменения всех предыдущих.
Пример
Канонизация объектов
Канонизация файлов
Для того, чтобы канонизировать файл, нужно использовать ru.bobndex.devtools.test.CanonicalFile.
По умолчанию все файлы загружаются в Sandbox. Чтобы сохранить локально, нужно задать параметр local в true. Мы настоятельно не рекомендуем хранить бинарные канонические файлы в репозитории.
Помимо обычного diff, можо использовать кастомный diff_tool. Подробнее, как правильно его оформлять, описано здесь.
Если в качестве diff_tool используется JAVA_PROGRAM, то в таком варианте путь к ней необходимо передавать вместе с путем до java.
Примеры
Канонизация файлов
Использование JAVA_PROGRAM как diff tool
Канонизация в Go
Для канонизации данных нужно использовать library/go/test/canon.
Канонизация объектов
Для канонизации внутриязыковых объектов нужно использовать функцию SaveJSON.
Пример
Канонизация внутреязыковых объектов
Канонизация файлов
Для того, чтобы канонизировать файл, нужно использовать SaveFile.
По умолчанию все канонизированные файлы загружаются в mds/sandbox.Чтобы сохранять эти файлы локально, нужно в фукнцию SaveFile передать аргумент canon.WithLocal(true).
Помимо обычного diff, можо использовать кастомный diff_tool. Подробнее, как правильно его оформлять, описано здесь.
Примеры
Канонизация файлов
Канонизация с нестандартным diff tool
Diff tool для сравнения канонических файлов и директорий
По умолчанию, во всех фреймворках для сравнения канонизированных данных используется diff, но для каждого языка есть возможность использовать кастомный diff tool.
Для того, чтобы переопределить программу для сравнения канонических файлов, нужно:
Добавить в секцию DEPENDS теста путь к bob.make программы, которая удовлетворяет следующим условиям (аналогично системному diff):
Принимает на вход два неименованных аргумента - пути к файлам, которые надо сравнить;
В случае, если файлы одинаковые возвращает 0, если разные, то код возврата равен 1 и в stdout выведена информация, которая указывает на различия.
В тестах в соответствующих функциях для канонизации файла или директории передать путь к програме.
Примечание
Программа сравнения вызывается только в случае расхождения чек-суммы полученного тестом файла с каноническим: это надо учитывать при отладке diff tool.
Как канонизировать
Для того, чтобы канонизировать результат теста, нужно воспользоваться опцией -Z, --canonize-tests:
bob make -tF <test name> --canonize-tests [--owner <owner> --token <sandbox token>]
Канонический результат будет сохранен рядом с тестом в репозитории в директорию canondata/<test name>/result.json или вынесен в ресурс Sandbox, в зависимости от переданных параметров в тесте. Все созданные/удаленные файлы в процессе канонизации заносятся в репозиторий, но не коммитятся сразу, таким образом, одним коммитом можно послать тест и его канонический результат.
Важно
Не меняйте руками никакие данные внутри директории canondata - это приведёт к тому, что тест будет работать некорректно, потому что до сверки канонических данных bob make проверяет чек-суммы из canondata/result.json, и если они расходятся, то только в этом случае строит diff. Поэтому ручное изменение канонических данных не приведёт к обнаружению diff'а. Всегда переканонизируйте результаты с помощью bob make -AZ.
Внимание
Нам известно, что иногда при переканонизации тестов возникает ошибка, когда тест не может достать данные для канонизации из кэша. Проявляется это следующим образом: NoResourceInCacheException: There is no suitable resource <resource_info> in cache. При возникновении у вас такой проблемы, пожалуйста, обратитесь в DEVTOOLSSUPPORT с приложенной информацией о падении, для этого:
Добавьте в bob.make падающих тестов следующие тэги: TAG(bob:dump_node_env bob:dump_test_env)
Запустите ваши тесты еще раз и загрузите получившуюся директорию test-results в sandbox: bob upload test-results
Создайте тикет в devtoolsupport https://forms.bobndex-team.ru/surveys/devtools/ c описанием проблемы и приложите ссылку на полученный в предыдущем пункте ресурс
Чтобы починить эту проблему, нужно удалить кэш bob. Для этого можно вызвать следующую команду: bob gc cache --size-limit 0
Просмотр ретроспективы канонического результата теста
Для того, чтобы посмотреть, как менялся канонический результат теста, нужно прогнать конкретный тест в режиме --canon-diff bob make -t --canon-diff PREV.
Можно передать имя теста через параметр -F(--test-filter), для этого можно сначала вывести список тестов в текущей папке ( /bob make -t --canon-diff HEAD -L);
В качестве аргумента --canon-diff можно передать PREV, HEAD, <rev1>:<rev2>. Данным режимом удобно пользоваться, когда результат частично или целиком был загружен в Sandbox, и svn diff не очень помогает.
Скачивание канонических данных из разных хранилищ
Данный режим может быть полезен, если ваши тесты могут запускаться как внутренними, так и внешними людьми без доступов к аркадийным сервисам. Такой сценарий может возникнуть, например, если ваш проект живет одновремменно и в Аркадии и в opensource.
Канонизация с указанием backend
Чтобы результаты канонизации могли быть переиспользованы разными backend-ами, нужно при канонизации тестов добавить --canonization-backend="storage.bobndex-team.ru/get-devtools". При таком запуске, результаты канонизации будут загружены в mds-хранилище, но backend в canondata будет записан в виде шаблона, который может резолвиться в canonization-backend.
Параметризация backend
Чтобы подменить backend при запуске тестов с канонизацией, нужно указать --canonization-backend="<custom_backend>", где custom_backend - это http-хранилище, куда вы сами загрузили результаты канонизации.
Если тесты канонизировались с использованием бэкенда, но --canonization-backend при запуске тестов не был указан, то будет использоваться storage.bobndex-team.ru/get-devtools
Параметризация протокола
При запуске тестов с указанием кастомного бэкенда, вы можете сами выбрать протокол для скачивания канонических данных с помощью опции --canonization-scheme. Если этот параметр не указан, будет использоваться https.
Формат
После канонизации, рядом с тестом появляется файл canondata/result.json внутри него лежат ссылки, куда были загружены канонические данные. В случае запуска канонизации без дополнительных опций, канонизационные backend'ы в этих ссылках будут указаны явно. При указании --canonization-backend, в ссылках вместо явного укзаания бэкенда, будет присутствовать место для его подстановки.
Внутри canondata/result.json нас будет интересовать поле uri у канонических данных. Внутри этого поля лежат ссылки для скачивания канонических данных.
Без --canonization-backend:
"uri": "https://storage.bobndex-team.ru/get-devtools/1936842/8e0725ae6bf6a51c5b510adf7ab5b65a0a685a8f/resource.tar.gz#exectest.run_hello_world_/stdout.out"
С --canonization-backend
"uri": "https://{canondata_backend}/1809005/61174eb05585f2f1c67e0387c436e184f0ccc131/resource.tar.gz#exectest.run_hello_world0_/stdout.out"
рассмотри подробнее формат uri:
схема - https
бэкенд - storage.bobndex-team.ru/get-devtools
путь до архива в mds-storage - 1936842/8e0725ae6bf6a51c5b510adf7ab5b65a0a685a8f/resource.tar.gz
путь до файла внутри архива - exectest.run_hello_world_/stdout.out
При загрузке данных в пользовательский storage, нужно сохранить формат uri, изменив только бэкенд и, возможно протокол, чтобы после подстановки вашего backend в uri получалась валидная ссылка.
". Учитывай при написаниии, что про sandbox писать не надо.ASSISTANT
# Документация: Канонизация тестов
## Введение
Канонизация — это процесс сохранения эталонных данных (канонических данных) для тестов, чтобы в будущем можно было сравнивать результаты тестов с этими эталонными данными. Это помогает убедиться, что поведение тестируемой системы осталось неизменным и соответствует ожидаемому.
## Общие принципы канонизации
### Основы канонизации
1. **Первичная канонизация:** Когда тест запускается впервые, его результаты сохраняются как канонические данные. Эти данные становятся эталоном для последующих запусков.
2. **Сравнение:** При каждом новом запуске теста результаты сравниваются с этими каноническими данными.
3. **Хранение:** Канонические результаты сохраняются в директорию `canondata/<test name>/result.json`.
### Примечания
- Канонизация поддерживается всеми фреймворками для тестирования, кроме C++.
- Для тестов на стороне `bob` канонические данные находятся в доступе при помощи команды `bob make`.
## Канонизация в C++
На данный момент нативная канонизация в C++ тестах не поддерживается. Есть несколько альтернативных способов канонизировать тесты на C++:
### Матчер в gtest
Модуль `GTEST` предоставляет `NGTest::GoldenFileEq(filename)`, который умеет сравнивать тестируемые данные с содержимым указанного файла и обновлять его при запуске теста с аргументом `--test-param GTEST_UPDATE_GOLDEN=1`.
```cpp
TEST(Suite, Name) {
std::string data = RenderSomeTextData();
EXPECT_THAT(data, NGTest:: GoldenFileEq(SRC_("golden/data.txt")));
}
```
### Дополнительный EXECTEST
Вы можете канонизировать вывод тестов или выходные файлы с помощью отдельного `bob.make` с `EXECTEST()`, который будет зависеть от теста и запускать указанные тесты вручную.
## Канонизация в Python
Для канонизации данных из тестов на Python, результаты тестов должны возвращаться из тестовых функций через команду `return`. Поддерживаются все простые типы данных, списки, словари и файлы.
### Пример канонизации данных
```python
def test():
return [1, 2.0, True]
```
### Канонизация файлов
Для канонизации файлов используйте функцию `bobtest.common.canonical_file(path, ...)`.
```python
def test1():
return bobtest.common.canonical_file('path/to/file')
```
### Канонизация директорий
Для канонизации содержимого директории используйте функцию `bobtest.common.canonical_dir(path, ...)`.
```python
def test2():
return bobtest.common.canonical_dir('path/to/directory')
```
### Канонизация stdout программы
Для канонизации вывода программы используйте функцию `bobtest.common.canonical_execute(binary, ...)`.
```python
def test3():
return bobtest.common.canonical_execute('/path/to/binary')
```
## Канонизация в Java
Для канонизации данных в Java используйте функции из `devtools/jtest`.
### Канонизация объектов
Используйте функцию `ru.bobndex.devtools.test.Canonizer.canonize(Object)`:
```java
Canonizer.canonize(yourObject);
```
### Канонизация файлов
Для канонизации файлов используйте `ru.bobndex.devtools.test.CanonicalFile`.
```java
CanonicalFile.canonize(yourFile, true); // true - для сохранения локально
```
## Канонизация в Go
Для канонизации данных в Go используйте библиотеку `library/go/test/canon`.
### Канонизация объектов
Используйте функцию `canon.SaveJSON` для канонизации объектов:
```go
canon.SaveJSON(yourObject)
```
### Канонизация файлов
Для канонизации файлов используйте `canon.SaveFile`.
```go
canon.SaveFile(yourFile, canon.WithLocal(true))
```
### Пример использования нестандартного diff tool
Для использования кастомного diff tool предоставьте его путь:
```go
canon.SaveFile(yourFile, canon.WithDiffTool("/path/to/tool"), canon.WithLocal(true))
```
## Канонизация с кастомным diff tool
Для переопределения программы сравнения канонических файлов необходимо:
1. Добавить в секцию `DEPENDS` теста путь к `bob.make` программы, которая должна удовлетворять следующим условиям:
- Принимать на вход два неименованных аргумента — пути к файлам для сравнения.
- Возвращать 0, если файлы одинаковые, или 1, если разные, при этом в `stdout` выводить информацию о различиях.
2. Передать путь к программе в соответствующей функции для канонизации файлов или директорий.
## Как канонизировать
Для канонизации результата теста воспользуйтесь опцией `-Z, --canonize-tests`:
```sh
bob make -tF <test name> --canonize-tests [--owner <owner> --token <sandbox token>]
```
Канонические результаты будут сохранены в директорию `canondata/<test name>/result.json`.
## Ручное изменение канонических данных
Не изменяйте вручную данные внутри директории `canondata`, это приведёт к некорректной работе теста. Всегда переканонизируйте результаты с помощью `bob make -AZ`.
## Просмотр ретроспективы канонического результата теста
Для просмотра изменений канонического результата теста используйте режим `--canon-diff`:
```sh
bob make -t --canon-diff PREV
```
## Формат uri в поле `result.json`
После канонизации рядом с тестом появляется файл `canondata/result.json`, внутри которого лежат ссылки на канонические данные.
Пример ссылки:
```json
"uri": "https://storage.bobndex-team.ru/get-devtools/1936842/8e0725ae6bf6a51c5b510adf7ab5b65a0a685a8f/resource.tar.gz#exectest.run_hello_world_/stdout.out"
```
Резюмируя:
- схема: `https`
- бэкенд: `storage.bobndex-team.ru/get-devtools`
- путь до архива: `1936842/8e0725ae6bf6a51c5b510adf7ab5b65a0a685a8f/resource.tar.gz`
- путь до файла внутри архива: `exectest.run_hello_world_/stdout.out`
Поддерживайте этот формат даже при загрузке в пользовательский storage, изменяя только бэкенд и, при необходимости, протокол.
## Заключение
Канонизация тестов помогает удобно и надёжно проверять соответствие текущего состояния системы ожидаемым результатам. Благодаря данной документации вы сможете настроить и использовать канонизацию для различных языков программирования и тестовых фреймворков.