# Использование данных в сборке и тестировании

Большие и/или бинарные файлы не следует коммитить в Аркадию напрямую, особенно если это часто обновляемые данные или нужные только для тестов.
Чёткой границы что считать большим и запрещающей прекоммитной проверки сейчас нет. Рекомендуется руководствоваться здравым смыслом и стараться
не хранить в Аркадии бинарные данные, а также огромные текстовые файлы. Такие данные надо загружать в Sandbox. Данные из Sandbox легко использовать
в сборке и в тестах.

{% note warning %}

Для обеспечения собираемости старых ревизий данные, используемые в сборке, должны загружаться в Sandbox с бесконечным временем жизни (параметр `--do-not-remove` команды [`ya upload`](https://docs.yandex-team.ru/yatool/commands/upload)).
Для макросов `FROM_SANDBOX` и `LARGE_DATA` реализованы автоматические тесты, проверяющие это правило.

{% endnote %}

## В каких случаях что использовать

Есть четыре основных способа использования данных из Sandbox в сборке и тестах.

1. Макрос [`FROM_SANDBOX`](#from_sandbox) позволяет привезти данные как в сборку, так и в тесты. Он имеет самые развитые средства управления скачиванием. Он может скачивать
   одиночные файлы и архивы с директориями и позволяет переименовывать файлы чтобы избежать конфликтов по именам, если несколько ресурсов содержат одинаковые файлы.
   Макрос поддерживает среди прочего подвоз исходного кода, позволяя указать дополнительные зависимости параметром `OUTPUT_INCLUDES`.

   Макрос удобен в случаях, когда ресурс генерируется внешним процессом и его не надо заливать с пользовательской системы и предварительно тестировать.

   Макрос имеет следующие ограничения, которые надо иметь в виду:

   - Макрос не требует знания имени файла в ресурсе, но это работает только если файл в ресурсе один. Его надо поименовать прямо в самом макросе.
   - Макрос можно использовать и в сборке и в тестах, но для использования в тестах его надо писать в отдельный модуль типа [`UNION`](#union), на который ставить `DEPENDS`
   - Макрос не поддерживает ресурсы-директории. Однако, он поддерживает ресурсы-архивы, которые он умеет автоматически распаковывать.
   - Макрос требует явного указания всех файлов, которые нужны на его выходе, он не позволяет привезти *все* файлы из ресурса.
   - Макрос не позволяет легко тестировать данные до загрузки в Sandbox. Чтобы протестировать новые данные, их надо загрузить и поменять ID ресурса в макросе.

2. Макрос [`LARGE_FILES`](#large_files) подойдёт вам, если вы загружаете данные в Sandbox сами или можете загрузить один раз и настроить обновления.
   Особенно он будет полезен в случаях, когда вы хотите тестировать данные локально до отправки их в Sandbox и коммита изменений в сборку.
   Данные для этого макроса обновлять проще всего. Он поддерживает использование локальных данных для целей тестирования.

   Однако, макрос имеет следующие ограничения, которые надо иметь в виду:

   - Макрос требует знания и указания имени файла в ресурсе.
   - Макрос можно использовать и в сборке и в тестах, но для использования в тестах его надо писать в отдельный модуль типа [`UNION`](#union), на который ставить `DEPENDS`
   - Метаинформация для макроса формируется командой [`ya upload`](#upload), формат метаинформации не фиксирован и может меняться. Лучше не создавать такой файл руками.
   - Макрос привозит один файл на ресурс и не поддерживает ресурсы-директории. Однако, можно привезти архив с ресурсами, который можно распаковать дополнительным макросом [`FROM_ARCHIVE`](#FROM_ARCHIVE). Локально данные должны лежать в запакованном виде, чтобы подхватываться этим макросом.
   - Макрос не поддерживает переименование, но поддерживает скачивание в поддиректории.
   - Макрос на данный момент не поддерживает превращение ресурса в исполняемый файл.
   - Макрос не поддерживает `OUT` и `OUTPUT_INCLUDES`, привезённые исходные данные не будут автоматически обработаны, а зависимости в репозитории могут быть потеряны (необходимые пересборки при их изменении не будут случаться).

Макрос [`DATA`](#data) привозит данные только в тесты, но зато не требует отдельного модуля для использования данных. Использование данных из Sandbox в макросе DATA возможно двумя разными способами:

3. [`DATA(sbr:<RES_ID>[=<dir>])`](#data_sbr) — прямое указание ID ресурса удобно если ресурс получается внешним образом, а не заливается пользователем. Это единственный на данный момент способ
   использовать ресурсы-директории. Ресурсы-архивы автоматически не распаковываются, соответственно в этом макросе каждый ресурс — это или один файл (с возможной распаковкой в коде теста),
   или одна директория. Хотя в макросе [`DATA`](#data) можно указывать как ссылки на ресурсы Sandbox, так и на файлы из Аркадии, тесту данные доступны по разным путям. Поэтому прямое указание
   ресурса не очень удобно для локального тестирования данных: для использования локальных данных придётся править не только `ya.make`, но и код теста.


4. [`DATA(ext:[dir]/<ext_file>)`](#data_ext)  — указание ресурса через external-файл. Удобен, если хочется тестировать данные перед отправкой в Sandbox. Макрос позволяет использовать как локальные данные, так и
   данные в Sandbox. При этом для переключения не нужно править ни `ya.make` ни код теста, локальные данные используются по факту их наличия. К сожалению на данный момент этот способ не поддерживает
   использование ресурсов-директорий, а архивы автоматически не распаковываются. Поэтому с помощью этого способа можно использовать только отдельные файлы из ресурсов или писать код распаковки в тесте.


**Итого:**

- Если тестовые данные запакованы стоит присмотреться к макросам [`LARGE_FILES`](#large_files) и [`FROM_SANDBOX`](#from_sandbox). Несмотря на необходимость описать дополнительный модуль, это может быть удобнее:
  данные будут автоматически распакованы. Дополнительными бонусами будут возможность переиспользовать весь модуль с данными между тестами, а также собрать данные из разных источников в единую структуру.
  Выбор между [`LARGE_FILES`](#large_files) и [`FROM_SANDBOX`](#from_sandbox) определяется необходимостью ручного обновления данных с предварительным тестированием.

- Если тестовые данные — это директория в ресурсе, то единственный выбор — [`DATA`](#data) с прямым указанием ID ресурса ([`DATA(sbr:<ResourceID>[=<dir>])`](#data_sbr)).

- Если тестовые данные — это один файл, то можно использовать один из вариантов макроса [`DATA`](#data). Какой именно зависит от необходимости тестирования данных и их ручного обновления.
  Если всё это нужно, лучше использовать [`DATA(ext:[dir]/<ext_file>)`](#data_ext), если данные получаются внешним образом, то можно использовать прямое указание ID ресурса.

## Краткая инструкция по использованию данных в тестах

**Если данные - это один или несколько файлов, которые используются в одной сьюте**

Чтобы использовать данные из Sandbox в тестах надо:
1. Положить данные локально туда, где их будет брать тест.
2. Пропиcать относительные пути в [`DATA(ext:<data_file_path>)`](#data_ext).
3. Поправить код доступа к данным в тесте:
   - В python-тестах можно воспользоваться `yatest.common.runtime.work_path()`.
   - В с++ unittest'aх `GetWorkPath()`..
   - Для java-тестов `ru.yandex.devtools.test.Paths.getSandboxResourcesRoot()`.
4. Проверить данные локально, запустив тесты.
5. Залить данные командой [`ya upload --update-external --move`](#upload).
6. Проверить ещё раз, что всё работает, теперь уже с данными в Sandbox.
7. Закоммитить все изменённые файлы, включая .external файлы, созданные `ya upload` на шаге (5.).

{% note tip %}

Если в `ya uplaod` на шаге (5.) и аналогичном шаге в следующем варианте не передать `--move`, то данные после загрузки останутся локально. В этом случае они будут использоваться на шаге (6.) вместо данных в Sandbox, а также могут быть случайно закоммичены на шаге (7.). Рекомендуется удалить или переместить данные самостоятельно после шага (5.). Использование `--move` по идее безопасно: данные удаляются только если загрузка в Sandbox удалась, и в случае проблем данные можно будет скачать.

{% endnote %}


**Если данные - это директория из которой используется фиксированный набор файлов**

*Этот способ также подойдёт для использования одних и тех же данных во многих тестовых проектах.*

Чтобы использовать данные из Sandbox в тестах надо:
1. Создать директорию для проекта данных.
2. В директории разместить архив с запакованными данными.
3. Создать там же `ya.make` примерно такого вида:
   ```cmake
   UNION()
     OWNER(<your_name>)
     LARGE_FILES(<your_data_archive>)
     FROM_ARCHIVE(<your_data_archive> OUT_NOAUTO <list_of_used_files>)
   END()
   ```
4. В тесте поставить `DEPENDS(<path_to_union_dir>)`.
5. Поправить код доступа к данным в тесте:
   - В python-тестах можно воспользоваться `yatest.common.runtime.build_path()`.
   - В с++ unittest'aх `GetBuildPath()`.
   - Для java-тестов `ru.yandex.devtools.test.Paths.getBuildPath()`.
6. Проверить данные локально, запустив тесты.
7. Залить данные командой [`ya upload --update-external --move`](#upload) в директории с модулем данных.
8. Проверить ещё раз, что всё работает, теперь уже с данными в Sandbox.
9. Закоммитить все изменённые файлы, включая .external файлы, созданные ya upload.

{% note tip %}

Обратите внимание, что в этом случае в отличие от двух остальных данные размещаются в `build_path`. Связано это с тем, что `DEPENDS` работает во время сборки, а не исполнения теста, и потому данные оказываются рядом с другими depends-зависимостями, которые строятся для теста.

{% endnote %}

{% note tip %}

Данный способ позволяет собрать в рамках одного описания [данные из разных источников](#union): Sandbox, Аркадии и полученных сборкой.

{% endnote %}


**Если данные - это директория в которой много файлов:**
Чтобы использовать данные из Sandbox в тестах надо:
1. Залить данные в sandbox [`ya upload --ttl=inf --backup <your data>`](#upload).
2. Добавить в макросе [`DATA()`](#data) использование ресурса как [`sbr://<RESOURCE_ID>`](#data_sbr).
3. Поправить код доступа к данным в тесте:
   - В python-тестах можно воспользоваться `yatest.common.runtime.work_path()`.
   - В с++ unittest'aх `GetWorkPath()`.
   - Для java-тестов `ru.yandex.devtools.test.Paths.getSandboxResourcesRoot()`.
4. Проверить что всё работает с данными в Sandbox.
5. Закоммитить все изменённые файлы.

{% note info %}

К сожалению у нас на данный момент нет средств для тестирование ресурсов-директорий до заливки. В этом варианте надо сначала залить данные, а потом уже явно (по ID) на них сослаться в `ya.make` и тестировать.

{% endnote %}


## FROM_SANDBOX

Макрос `FROM_SANDBOX(...)` загружает данные из Sandbox в сборку. Он может распаковывать ресурсы-архивы и позволяет гибко управлять дальнейшим использованием полученных данных.

```
FROM_SANDBOX(
  [FILE]
  resource_id
  [AUTOUPDATED resource_key]
  [PREFIX <subdir>]
  [RENAME RESOURCE]
  [RENAME in_file...]
  [OUT|OUT_NOAUTO out_file...]
  [OUTPUT_INCLUDES include_files...]
  [EXECUTABLE]
)
```

- С параметром `FILE` ресурс считается непосредственно файлом с данными, без указания — .tar-архивом, который будет распакован и файлы в `OUT`/`OUT_NOAUTO` будут из его содержимого.

- Параметры `OUT` и `OUT_NOAUTO` задают пути относительно текущей директории к файлам (обязательно файлы, не директории) которые будут в итоге получены. Без параметра `RENAME`
  это должно быть или имя файла-ресурса, или имена файлов в ресурсе-архиве. C параметром `RENAME` здесь указываются финальные (новые) имена файлов.
  `OUT` добавляет файлы в сборку как исходный код в соответствии с расширением, `OUT_NOAUTO` только привозит данные.

  {% note tip %}

  Практически всегда рекомендуется использовать `OUT_NOAUTO`. Параметр `OUT` стоит использовать только если есть явная необходимость собрать полученный файл.
  В этом случае не забудьте указать зависимости от файлов в Аркадии параметром `OUTPUT_INCLUDES`.

  {% endnote %}

- Параметр `PREFIX` позволяет распаковать (в случае архива) или переместить (в случае файла) данные в поддиректорию `<subdir>`. Как и в случае с `RENAME` в параметрах `OUT`/`OUT_NOAUTO` надо
  указывать окончательные имена файлов, то есть с префиксом. При совместном использовании c `RENAME` префикс должен быть у имён файлов именно в `RENAME`. Например:


```
FROM_SANDBOX(
  FILE
  216403734
  PREFIX x
  OUT_NOAUTO x/resource.tar.gz
)
FROM_SANDBOX(
  FILE 216403734
  PREFIX x
  RENAME x/resource.tar.gz
  OUT_NOAUTO 2.tar.gz
)
```

- Параметр `RENAME` позволяет переименовать полученные данные. Для этого в параметре `RENANE` указывают оригинальные имена файлов (как в ресурсе), а финальные имена указываются
  в параметрах `OUT`/`OUT_NOAUTO`. Параметры `RENAME` и `OUT`/`OUT_NOAUTO` соотносятся позиционно (первый файл из `RENAME` переименовывается в первый в `OUT`/`OUT_NOAUTO`, второй - во второй и т.д.).
  Если параметров `OUT`/`OUT_NOAUTO` больше, чем `RENAME`, то 'лишние' файлы считаются не переименованными и имеющими оригинальное имя (как в ресурсе). Параметров `RENAME` не может быть больше,
  чем соответствующих `OUT`/`OUT_NOAUTO`. `RENAME RESOURCE` позволяет переименовать файл ресурса без указания имени.

  {% note info %}

  Параметры `RENAME`, `OUT`, `OUT_NOAUTO` и `OUTPUT_INCLUDES` можно повторять в вызове макроса несколько раз. Полный список для каждого из параметров формируется объединением списков из всех вхождений.
  Для сопоставления `RENAME` и `OUT`/`OUT_NOAUTO` также используются списки по всем вхождениям.

  {% endnote %}


  **Примеры**


```
FROM_SANDBOX(
  resource_id
  RENAME in_file1
  OUT_NOAUTO out_file1
  RENAME in_file2
  OUT_NOAUTO out_file2 ...
)

FROM_SANDBOX(
  resource_id
  RENAME in_file1 in_file2 ...
  OUT_NOAUTO out_file1 out_file2 ...
)

FROM_SANDBOX(
  FILE resource_id
  RENAME RESOURCE
  OUT_NOAUTO file_name
)
```

- Параметр `OUTPUT_INCLUDES` задаёт зависимости файла, полученного из ресурса через `OUT` от других файлов в Аркадии. Например, если из ресурса привозится в сборку файл с C++ кодом,
  содержащий `#include` на заголовочные файлы из Аркадии, то файлы указанные в `#include` надо перечислить в `OUTPUT_INCLUDES`. Это обеспечит перекомпиляцию привезённого файла при изменении
  указанных заголовков.

- Параметр `EXECUATBLE` позволяет принудительно поставить скачанному файлу свойство исполняемости. Это может быть полезно, например, в тестах, чтобы привозить ресурсами и запускать необходимые программы.

- Параметр `AUTOUPDATED` включает [автообновление](#autoupdate) и задаёт ключ ресурса в его конфиге.

  {% note warning %}

  Логика автообновления ищет параметр `AUTOUPDATED` строго сразу после `ResourceID`, который ожидает увидеть первым или вторым (после `FILE`) параметром макроса.

  {% endnote %}

{% note tip %}

Для использования в тестах `FROM_SANDBOX` следует помещать в отдельном модуле [`UNION`](#union) или `PACKAGE`, а в тестах, которые хотят пользоваться данными, следует ставить
`DEPENDS` на этот [`UNION`](#union). Таким образом, несколько тестовых сюит смогут использовать одни и те же данные, причём будет возможность обновлять эти данные одновременно.
Сами данные во время исполнения теста попадают в сборочную директорию, которую можно получить как `yatest.common.build_path` или аналогично для других языков.

{% endnote %}

**Примеры использования:**

- Варианты использования: [devtools/dummy_arcadia/test/from_sandbox/from_sandbox_ok](https://a.yandex-team.ru/arc/trunk/arcadia/devtools/dummy_arcadia/test/from_sandbox/from_sandbox_ok/ya.make?rev=r7779614)
- Использование в тестах: [arcadia/devtools/examples/tests/attachments](https://a.yandex-team.ru/arc/trunk/arcadia/devtools/examples/tests/attachments)
- Автообновление: [devtools/dummy_arcadia/test_autoupdate_data/good](https://a.yandex-team.ru/arc/trunk/arcadia/devtools/dummy_arcadia/test_autoupdate_data/good/ya.make?rev=r7779681)

## LARGE_FILES

Макрос `LARGE_FILES` загружает файлы из Sandbox в сборку. Макрос поддерживает использование локальных файлов вместо скачивания для тестирования данных перед их отправкой в Sandbox.

`LARGE_FILES([AUTOUPDATED] Files...)`

Использовать локальные данные или данный из ресурса по ссылке в `.external-файле`
`Files...` - перечисление файлов, которые надо использовать. Файлы должны лежать в директории модуля или её поддиректории.

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

1. Если в директории модуля есть файл (**не симлинк и не директория**) с именем `<filename>`, указанным в макросе, то будет использован этот файл. `<filename>` - относительный путь от директории модуля.
  При сборке будет выдано предупреждение, что файл надо загрузить в Sandbox для использования в автосборке.
  ```
  Warn[-WPluginErr]: in $B/devtools/dummy_arcadia/test/large_files/local/data/large_files-local-data.pkg.fake: Used local large file $S/devtools/dummy_arcadia/test/large_files/local/data/test.txt.
  Don't forget to run 'ya upload --update-external' and commit $S/devtools/dummy_arcadia/test/large_files/local/data/test.txt.external`
  ```

2. Если файла нет, то в директории модуля должен быть файл `<filename>.external` с описанием, где взять `<filename>`. Если указан относительный путь, то он учтётся при поиске .external-файла.
   В большинстве случаев должно срабатывать именно это правило. Первое правило позволяет протестировать данные локально перед первоначальной загрузкой или обновлением.

   {% note warning %}

   Файл `<filename>.external` не надо создавать руками. Его порождает и складывает рядом с файлом данных команда [`ya upload --update-external`](#upload).
   Можно добавить `--move`, чтобы при успешной загрузке удалить оригинальный файл данных. Если `<filename>.external` есть, то команда его обновит, записав туда информацию о более свежем ресурсе.

   {% endnote %}

Параметр `AUTOUPDATED` управляет [автоматическим обновлением ресурсов](#autoupdate). Есть три особенности:

- Параметр применяется ко всем файлам в одном вызове макроса, если нужно автообновлять часть данных, а часть - не нужно напишите их в отдельных вызовах макроса `LARGE_FILES`.
- В качестве ключа ресурса в `ya.make.autoupdate` используется имя файла, дополнительный ключ указывать не нужно.
- Обновляется не `ya.make%` а `<file>.external`.

{% note tip %}

Сам по себе макрос умеет привозить только одиночные файлы и не умеет распаковывать архивы. Однако с помощью макроса [`FROM_ARCHIVE`](#from_archive) можно получить практически
все возможности макроса [`FROM_SANDBOX`](#from_sandbox) для ресурса, привезённого архивом. Единственным минусом такого подхода является то, что локальный файл, соответствующий ресурсу,
должен быть предварительно запакован.

{% endnote %}

{% note tip %}

Для использования в тестах `LARGE_FILES` следует помещать в отдельном модуле [`UNION`](#union) или `PACKAGE`, а в тестах, которые хотят пользоваться данными, следует ставить
`DEPENDS` на этот [`UNION`](#union). Таким образом, несколько тестовых сюит смогут использовать одни и те же данные, причём будет возможность обновлять эти данные одновременно.
Сами данные во время исполнения теста попадают в сборочную директорию, которую можно получить как `yatest.common.build_path` или аналогично для других языков.

{% endnote %}


**Примеры использования:**

- Использование в тестах: [devtools/dummy_arcadia/test/large_files/remote](https://a.yandex-team.ru/arc/trunk/arcadia/devtools/dummy_arcadia/test/large_files/remote)
- Распаковка данных: [devtools/dummy_arcadia/test/large_files/archive](https://a.yandex-team.ru/arc/trunk/arcadia/devtools/dummy_arcadia/test/large_files/archive)
- Автообновление: [devtools/dummy_arcadia/test_autoupdate_data_ext/only_large_files](https://a.yandex-team.ru/arc/trunk/arcadia/devtools/dummy_arcadia/test_autoupdate_data_ext/only_large_files)


## DATA

Макрос `DATA` описывает данные для теста. В аргументах указывается список ссылок на данные через пробел. На данный момент поддерживается 4 схемы ссылок:

`DATA(arcadia/<path>)`
`DATA(arcadia_tests_data/<path>)`
`DATA(sbr://ResourceID[=dst_dir] [AUTOUPDATED key])`
`DATA(ext:ext_path [AUTOUPDATED key])`


### DATA(arcadia/\<path\>)

Данные для тестов из репозитория с исходным кодом.

```
DATA(arcadia/<path>)
```

Даёт тесту доступ к файлам из Аркадии. С точки зрения теста данные оказываются в Source Root и доступны через `yatest.common.source_path` или аналогично для других языков.
`<path>` может быть как отдельным файлом, так и директорией. Во втором случае тест получает ко всему поддереву директорий по указанному пути.

{% note warning %}

Старайтесь не использовать большие директории в `DATA(arcadia/<path>)`. Тест буквально зависит от всех файлов по заданному пути. При построении графа зависимостей от всех этих файлов считается хэш,
в автосборке тесты запускаются на любые изменения в этом дереве, что может быть сильно чаще, чем нужно.

{% endnote %}

**Примеры использования:**

- [devtools/examples/tests/external_paths](https://a.yandex-team.ru/arc/trunk/arcadia/devtools/examples/tests/external_paths)


### DATA(arcadia_tests_data/)

Данные для тестов из специального репозитория `arcadia_tests_data`.

```
DATA(arcadia_tests_data/<path>)
```

{% note alert %}

Этот способ использования данных в тестах объявлен в Аркадии устаревшим. Новые проекты, использующие эту технологию, не должны появляться. Новые данные не должны помещаться в `arcadia_tests_data`.
Существующие проекты должны мигрировать на использование данных из Sandbox. Всё написанное ниже должно использоваться только для понимания написанного в файлах `ya.make`, но не для их разработки.

{% endnote %}

`<path>` — это относительный путь до данных от корня svn-репозитория `arcadia_tests_data`. `<path>` может быть как отдельным файлом, так и директорией, во втором случае тест получает доступ
ко всему поддереву директорий по указанному пути.

{% note warning %}

`arcadia_tests_data` не доступна в VCS arc и не является частью Arcadia в svn. Чтобы использовать `arcadia_tests_data` необходимо чекаутить её либо отдельно, либо чекаутить svn c уровня `/arc/trunk`.

{% endnote %}


С точки зрения теста данные оказываются в специальном дереве, доступном через `yatest.common.data_path` или аналогично для других языков.

`arcadia_tests_data` не находится внутри Аркадии, она ищется на одном уровне с директорией в которой расположена Аркадия, либо может быть передана в команду запуска тестов явно как
```
ya make -t --arcadia_tests_data ~/a_t_d
```

Поскольку `arcadia_tests_data` — это svn-репозиторий данные там доступны в момент складывания и обновляются коммитом. Однако это не работает в автосборке.

{% note alert %}

Для уменьшения нагрузки данной технологии на инфраструктуру, в автоматических проверках используется не последнее состояние репозитория `arcadia_tests_data`, а замороженное в файле
(**осторожно, файл большой**) [build/yandex_specific/atd/revisions.txt](https://a.yandex-team.ru/arc/trunk/arcadia/build/yandex_specific/atd/revisions.txt).
Файл фиксирует хэши всех данных на определённую ревизию и саму ревизию. Данные именно из этой ревизии используются в автосборке. Файл обновляется автоматически раз в сутки,
но может быть явно обновлён коммитом в Аркадию. Детали смотри [здесь](https://a.yandex-team.ru/arc/trunk/arcadia/build/yandex_specific/atd/readme.md).

{% endnote %}


### DATA(sbr://ResourceID){#data_sbr}

Данные из Sandbox по ID ресурса.

```
DATA(sbr://ResourceID[=dst_dir] [AUTOUPDATED key])
```

Даёт тесту доступ к файлам из Sandbox. `ResourceID` — это идентификатор ресурса в Sandbox. Ресурс может быть как файлом, так и директорией. Это единственный макрос, который поддерживает использование
ресурсов-директорий. Как в случае файла, так и в случае директории будет привезён ресурс целиком. Если файл в ресурсе - архив, то он не будет распакован, тест должен делать распаковку
в коде или привозить данных через `DEPENDS` на [`UNION`](#union) с [`FROM_SANDBOX`](#from_sandbox) или [`LARGE_FILES](#large_files).

Чтобы избежать конфликтов по именам или сделать размещение данных *как в production* ресурс можно положить в директорию `dst_dir` заданную синтаксисом `sbr:/ResourceID=dst_dir`.

С точки зрения теста данные оказываются в рабочей директории и доступны через `yatest.common.work_path` или аналогично для других языков. Соответственно `dst_dir` отсчитывается от неё.

{% note tip %}

К сожалению, данные из ресурсов и репозитория оказываются в разных директориях с точки зрения теста, поэтому не так просто поменять использование данных с Sandbox на локальные.
Кроме того, это не позволяет сформировать общую структуру файлов из разных источников. Чтобы упростить первую задачу мы поддерживаем [`DATA(ext:ext_path`](#data_ext), для решения
второй лучше использовать отдельный модуль [`UNION`](#union) или `PACKAGE` и делать из теста на него `DEPENDS`.

{% endnote %}

- Параметр `AUTOUPDATED` включает [автообновление](#autoupdate) и задаёт ключ ресурса в его конфиге.

- Для ресурсов, описанных в макросе DATA включена проверка ресурсов на `ttl == inf`. Данная проверка реализована в виде теста `validate_data_sbr`. Чтобы отключить данную проверку, нужно в тестовом ya.make файле написать макрос `DISABLE_DATA_VALIDATION`.

- После выставления ttl inf у ресурса и починки теста локально, в автосборке, в PR проверке, тест будет по прежнему падать, так как состояние теста закешировано. В противном случае автосборке пришлось бы делать сотни тысяч подключений к Sandbox каждый раз, чтобы проверять ttl одних и тех же ресурсов. Чтобы инициировать перезапуск `validate_resource` тестов в автосборке, нужно в тестовый `ya.make` добавить макрос `VALIDATE_DATA_RESTART(X)`, где `X` - произвольное число, при изменении которого будет меняться `uid` теста, приводя к перезапуску.

- В DistBuild (CI) размер одного ресурса ограничен 25 GiB.

**Примеры использования:**
- Примерчик кода:

  {% list tabs %}

  - ya_make:

    ```cmake
    PY2TEST()
    ...
    TEST_SRCS(test_resource.py)
    DATA(
      sbr://53620025=dir/data.txt
    )
    END()
    ```

  - test_resource.py:

    ```python
    def test_resource_is_here():
        # text.txt should come from sandbox via DATA section in ya.make
        with open(yatest.common.runtime.work_path("dir/data.txt")) as f:
            assert f.read().strip() == "test"
    ```

  {% endlist %}

- Тест с ресурсом: [devtools/examples/tests/sandbox_resource](https://a.yandex-team.ru/arc/trunk/arcadia/devtools/examples/tests/sandbox_resource)
- Автообновление: [devtools/dummy_arcadia/test_autoupdate_data_ext/only_data/ya.make](https://a.yandex-team.ru/arc/trunk/arcadia/devtools/dummy_arcadia/test_autoupdate_data_ext/only_data/ya.make)


### DATA(ext:path){#data_ext}

Данные из Sandbox через .external-файл.

```
DATA(ext:ext_path [AUTOUPDATED key])
```

Даёт тесту доступ к файлам из Sandbox по технологии .external-файлов. В качестве параметра указывается путь и имя файла с данными относительно директории модуля.

{% note warning %}

В отличие от схемы с `ResourceID`, данная схема поддерживает перемещение указанием пути к файлу, а не через синтаксис `=dst_dir`. В данном случае `=dst_dir` будет интерпретироваться как часть имени файла.

{% endnote %}


1. Если в директории модуля есть файл (**не симлинк и не директория**) с именем `<ext_path>`, указанным в макросе, то будет использован этот файл. `<ext_path>` - относительный путь от директории модуля.

2. Если файла нет, то в директории модуля должен быть файл `<ext_path>.external` с описанием, где взять `<ext_path>`. Если указан относительный путь, то он учтётся при поиске .external-файла.
   В большинстве случаев должно срабатывать именно это правило. Первое правило позволяет протестировать данные локально перед первоначальной загрузкой или обновлением.

   {% note warning %}

   Файл `<filename>.external` не надо создавать руками. Его порождает и складывает рядом с файлом данных команда [`ya upload --update-external`](https://docs.yandex-team.ru/yatool/commands/upload).
   Можно добавить `--move`, чтобы при успешной загрузке удалить оригинальный файл данных. Если `<filename>.external` есть, то команда его обновит, записав туда информацию о более свежем ресурсе.

   {% endnote %}

С точки зрения теста данные оказываются в рабочей директории и доступны через `yatest.common.work_path` или аналогично для других языков. Соответственно `ext_path` отсчитывается от неё.
Это верно и для данных, присутствующих локально и для данных, скачанных с Sandbox. Именно это упрощает тестирование данных — достаточно положить данные на их место, описанное в `ya.make`
и они автоматически подхватятся тестом. После этого одно запуска `ya upload --update-external --move` достаточно, чтобы данные отправились в Sandbox, а тест начал их использовать.

{% note info %}

К сожалению, эта технология не поддерживает директории в ресурсах. Множество файлов надо привозить архивом и распаковывать в коде теста или использовать
отдельный модуль [`UNION`](#union) или `PACKAGE` и делать из теста на него `DEPENDS`.

{% endnote %}

**Примеры использования:**

- Примерчик кода:

  {% list tabs %}


  - ya_make:
    ```cmake
    PY2TEST()
    ...
    TEST_SRCS(test_resource.py)
    DATA(
      ext:dir/data.txt
    )

    END()
    ```

  - test_resource.py:
    ```python

    def test_external_is_here():
        # text.txt is either local or will come from sandbox via DATA section in ya.make using data.txt.extenal file
        with open(yatest.common.runtime.work_path("dir/data.txt")) as f:
          assert f.read().strip() == "test"

    ```

  {% endlist %}

- Простейший пример: [devtools/ya/test/tests/ext_data/data/pytest_simple](https://a.yandex-team.ru/arc/trunk/arcadia/devtools/ya/test/tests/ext_data/data/pytest_simple)
- Данные в поддиректории: [devtools/ya/test/tests/ext_data/data/pytest_subdir_only_ext](https://a.yandex-team.ru/arc/trunk/arcadia/devtools/ya/test/tests/ext_data/data/pytest_subdir_only_ext)
- Автообновление: [devtools/dummy_arcadia/test_autoupdate_data_ext/only_data_ext/ya.make](https://a.yandex-team.ru/arc/trunk/arcadia/devtools/dummy_arcadia/test_autoupdate_data_ext/only_data_ext/ya.make)

## FROM_ARCHIVE

Макрос для распаковки архивов с данными, полученными, например макросом ['LARGE_FILES'](#large_files). Поддерживает для выходных файлов такие же возможности, как ['FROM_SANDBOX'](#from_sandbox).

```
FROM_ARCHIVE(
  Src
  [PREFIX <subdir>]
  [RENAME in_file...]
  [OUT|OUT_NOAUTO out_file...]
  [OUTPUT_INCLUDES include_files...]
  [EXECUTABLE]
)
```

{% note info %}

Макрос работает во время сборки и потому не работает с данными, привезёнными макросом [`DATA`](#data) — эти данные подвозятся во время исполнения теста, а не его сборки.
Для распаковки данных для тестов надо использовать [`LARGE_FILES`](#large_files) в отдельном модуле [`UNION`](#union) или `PACKAGE` и делать из теста на него `DEPENDS`.

{% endnote %}


Параметры макроса почти совпадают с параметрами ['FROM_SANDBOX'](#from_sandbox), только на вход он принимает имя файла архива, а не ID ресурса. Также отсутствуют параметры для работы собственно с ресурсом.

- Параметры `OUT` и `OUT_NOAUTO` задают пути относительно текущей директории к файлам (обязательно файлы, не директории) которые будут в итоге получены. Без параметра `RENAME`
  это должно быть или имя файла-ресурса или имена файлов в ресурсе-архиве. C параметром `RENAME` здесь указываются финальные (новые) имена файлов.
  `OUT` добавляет файлы в сборку как исходный код в соответствии с расширением, `OUT_NOAUTO` только привозит данные.

  {% note tip %}

  Практически всегда рекомендуется использовать `OUT_NOAUTO`. Параметр `OUT` стоит использовать только если есть явная необходимость собрать полученный файл.
  В этом случае не забудьте указать зависимости от файлов в Аркадии параметром `OUTPUT_INCLUDES`.

  {% endnote %}

- Параметр `PREFIX` позволяет распаковать данные в поддиректорию `<subdir>`. Как и в случае с `RENAME` в параметрах `OUT`/`OUT_NOAUTO` надо
  указывать окончательные имена файлов, то есть с префиксом. При совместном использовании c `RENAME` префикс должен быть у имён файлов именно в `RENAME`.
  `FROM_ARCHIVE(resource.tar.gz PREFIX x OUT_NOAUTO x/data_file)`
  `FROM_SANDBOX(resource.tar.gz PREFIX x RENAME x/data_file OUT_NOAUTO model/the_model)`

- Параметр `RENAME` позволяет переименовать полученные данные. Для этого в параметре `RENANE` указывают оригинальные имена файлов (как в ресурсе), а финальные имена указываются
  в параметрах `OUT`/`OUT_NOAUTO`. Параметры `RENAME` и `OUT`/`OUT_NOAUTO` соотносятся позиционно (первый файл из `RENAME` переименовывается в первый в `OUT`/`OUT_NOAUTO`, второй - во второй и т.д.).
  Если параметров `OUT`/`OUT_NOAUTO` больше, чем `RENAME`, то 'лишние' файлы считаются непереименованными и имеющими оригинальное имя (как в ресурсе). Параметров `RENAME` не может быть больше,
  чем соответствующих `OUT`/`OUT_NOAUTO`. `RENAME RESOURCE` позволяет переименовать файл ресурса без указания имени.

  {% note info %}

  Параметры `RENAME`, `OUT`, `OUT_NOAUTO` и `OUTPUT_INCLUDES` можно повторять в вызове макроса несколько раз. Полный список для каждого из параметров формируется объединением списков из всех вхождений.
  Для сопоставления `RENAME` и `OUT`/`OUT_NOAUTO` также используются списки по всем вхождениям.

  {% endnote %}


**Пример использования:**

- Использование в тестах: [devtools/dummy_arcadia/test/large_files/archive](https://a.yandex-team.ru/arc/trunk/arcadia/devtools/dummy_arcadia/test/large_files/archive)
- Варианты параметров: [devtools/dummy_arcadia/test/from_external/sandbox_ok/ya.make](https://a.yandex-team.ru/arc/trunk/arcadia/devtools/dummy_arcadia/test/from_external/sandbox_ok/ya.make)


## Ручная загрузка в Sandbox{#upload}

Чтобы использовать данные из Sandbox их надо туда загрузить и правильно сослаться на них в сборке.

Самый просто способ загрузить данные — это просто вызвать

```
ya upload --do-not-remove <your_file>
```
или

```
ya upload --do-not-remove --tar <data_dir>
```

В результате запуска на консоль будет выдан ID ресурса, который надо вписать в макрос [`FROM_SANDBOX`](`#from_sandbox`) или [`DATA(sbr://)`](#data_sbr).
После этого можно проверить тесты и закоммитить `ya.make`.

Чтобы использовать данные в Sandbox через .external-файлы, надо действовать следующим образом.

- Положить данные локально
- Начать использовать данные в `ya.make` в макросах [`LARGE_FILES`](#large_files) или [`DATA(ext:)`](#data_ext). Проверить, что тесты работают
- Запустить `ya upload --update-external --move`
- Не забыть закоммитить .external-файл.

Обновление данных делается аналогично, если .external-файл отсутствует, он будет создан, если существует, то обновлён.

Обратите внимание:

- Параметр `--move` удаляет локальные данные, так что после `ya upload` можно проверить работу уже с данными в Sandbox.
- Без указания какие данные обновлять `ya upload` анализирует локальный `ya.make` и обновляет данные, упомянутые в нём. Если файл указан, то обрабатываться будет именно он.


## Автоматическое обновление{#autoupdate}

Ресурсы в макросах [`FROM_SANDBOX`](#from_sandbox), [`LARGE_FILES`](#large_files) и [`DATA`](#data) можно сделать автообновляемыми.

Для этого необходимо

1. Положить рядом с `ya.make` json-файл `ya.make.autoupdate` следующей структуры:
  ```json
  {
      "<resource-key>": {
          "resource_type": <resource-type>,
          "attributes": {
              <attr-name-1>: <attr-value-1>,
              <attr-name-2>: <attr-value-2>,
              ...
              <attr-name-n>: <attr-value-n>,
          }
      }
  }
  ```

  Где

  - `<resource-key>` — идентификатор ресурса для ссылки из макросов
  - `<resource-type>` — тип ресурса в Sandbox
  - `"attributes"` содержит имена и значения атрибутов ресурсов в Sandbox

  **Пример:**
  ```json
  {
      "my_geodata": {
          "resource_type": "GEODATA6BIN_STABLE",
          "attributes": {
              "released": "stable"
          }
      }
  }
  ```
  *Подходящие ресурсы:*
  [GEODATA6BIN_STABLE](https://sandbox.yandex-team.ru/resources?hidden=false&type=GEODATA6BIN_STABLE&limit=20&attrs=%7B%22released%22%3A%22stable%22%7D&created=7_days)


2. добавить в макросы [`FROM_SANDBOX`](#from_sandbox) и [`DATA`](#data) параметр `AUTOUPDATED <resource-key>`. В [`LARGE_FILES`](#large_files) надо добавить просто `AUTOUPDATED`,
  в качестве ключа будет использовано имя файла (без пути) указанного в макросе.

  **Пример:**
  ```
  ...
  FROM_SANDBOX(FILE 2272139412 AUTOUPDATED my_geodata OUT_NOAUTO geodata6.bin)
  ...
  ```

Раз в пол часа вот этот [планировщик](https://sandbox.yandex-team.ru/scheduler/42167/view) запускает автоматику, которая читает все файлы ya.make.autoupdate в репозитории,
ищет в Sandbox ресурсы указанного типа с указанными атрибутами, и среди найденных ресурсов идентификатор последнего созданного коммитится в `ya.make`, .inc-файл или .external-файл.

В задаче сохраняется информация обо всех обнаруженных в процессе обновления ошибках, а коммит проверяется пост-коммитной проверкой в CI, чтобы проверить корректность обновления.


### Ручное автообновление { #manual }

Однако, такая автоматизация некоторым показалась слишком автоматической, поэтому есть способ для своего модуля запустить процесс локально вручную, можно использовать команду
[`ya project update resources path/to/autoupdated/project`](../../usage/ya_project#update).

Чтобы посмотреть, что изменится, но не делать реальное обновление, используйте ключ `--dry-run`.

Команда с флагом `--dry-run` не изменяет файлы и выводит на экран:

- Обрабатываемые проекты
- Какие ресурсы обновились
- Список ошибок

{% note tip %}

В отличие от автоматического обновления команда не коммитит изменения, не забудьте сделать это сами после запуска команды и локального запуска тестов.

{% endnote %}

Больше информации в `ya project update resources --help`

## Комбинирование источников данных{#union}

Чтобы сконструировать директорию, состоящую как из локальных файлов, так и из файлов в Sandbox-е, а также из собранных программ, нужно создать примерно такой модуль:

```cmake
  # специальный объединяющий модуль
  UNION()

  # локальные файлы в arcadia
  FILES(
      1.txt
      2/2.txt
  )

  # архив в sandbox-е из которого можно получить необходимые файлы
  FROM_SANDBOX(91668310 OUT 3.txt)


  # данные из Sandbox, доступные через my_data.external
  LARGE_FILES(my_data)

  BUNDLE(some/program)

  # используя привычный PEERDIR можно добавить зависимость на UNION/PROGRAM/
  PEERDIR(somewhere/else)

  END()

```

Для того, чтобы воспользоваться данными такого модуля из теста, нужно сослаться на него из тестового модуля макросом `DEPENDS`. Данные окажутся в сборочной директории теста.
Из pytest-а можно обратиться к файлам с помощью helper-а `yatest.common.build_path`. Аналогично для других языков.

Чтобы собрать такой модуль локально можно

- Запустить на него `ya make` и получить симлинки на данные в его директории. Файлы из `FILES` останутся на своих местах, а зависимости из `PEERDIR` не соберутся.
- Запустить `ya make -o some/dir`, в `some/dir` окажутся все данные, включая `FILES`, но зависимости из `PEERDIR` опять не соберутся.
- Дописать где-нибудь модуль `PACKAGE` и поставить из него `PEERDIR` на `UNION`. Тогда `PACKAGE` воссоздаст полную иерархию под своей директорией, собрав не только сам `UNION`, но и его `PEERDIR`-зависимости.

**Пример использования:**

- [arcadia/devtools/examples/tests/attachments](https://a.yandex-team.ru/arc/trunk/arcadia/devtools/examples/tests/attachments)
