RFC-036: тестовый фреймворк std.test и команда yaoxiang test
Аннотация
Введение стандартного тестового фреймворка std.test и подкоманды CLI yaoxiang test для YaoXiang. Тестовые файлы являются обычными файлами .yx, а успешное/неуспешное прохождение определяется через std.assert.assert + код завершения. Модуль std.test реализован на чистом YaoXiang и является первой библиотекой для самопроверки (dogfooding). yaoxiang test — это инструмент CLI, а не функциональность компилятора — не затрагивает никаких изменений в парсере, IR, байткоде или исполнительном механизме.
Мотивация
Зачем нужен тестовый фреймворк?
В текущей реализации YaoXiang покрытие тестами зависит от #[test] на стороне Rust и интеграционных тестов в tests/. Это означает:
- Модульные тесты стандартной библиотеки (std.math / std.list / std.dict / std.convert / std.io) нельзя писать на YaoXiang
#117 Покрытие модульными тестами стандартной библиотекизаблокировано, поскольку нет доступной тестовой инфраструктуры- Регрессионное тестирование языковых возможностей (например, изменение семантики spawn в RFC-032) не имеет автоматизации
Ключевые ограничения
- 17 ключевых слов как железное правило: не вводить никаких новых ключевых слов или синтаксических конструкций
- Нулевые изменения компилятора: не трогать парсер, IR, байткод, исполнитель
- Приоритет самопроверки: тестовая библиотека написана на YaoXiang, первая библиотека для dogfooding
Архитектура
┌──────────────────────────────────────────────────────────────┐
│ yaoxiang test │
│ │
│ CLI слой: yaoxiang test [--filter --fail-fast --json ...] │
│ │ │
│ Слой обнаружения: чтение yaoxiang.toml → [tool.test] patterns│
│ По умолчанию: tests/**/*.yx │
│ │ │
│ Слой выполнения: для каждого файла: yaoxiang run <file> │
│ проверка exit code → последовательное выполнение │
│ │ │
│ Слой отчётности: PASS/FAIL → агрегация │
│ Поддержка --json / --verbose / --fail-fast │
│ │
│ Слой проверок: std.test (чистый YaoXiang, самопроверка) │
│ Нижний уровень: std.assert.assert │
│ Диагностика: f"Expected {expected}, got {actual}" │
└──────────────────────────────────────────────────────────────┘Основные принципы
- Тестовый фреймворк — это не функциональность компилятора, а инструмент CLI —
yaoxiang runуже может "выполнять тесты", аyaoxiang testпросто помогает запускать все файлы и показывать отчёт - Нулевые изменения компилятора — не вводить сканирование аннотаций
@test, метаданные байткода, специальные точки входа в исполнитель - Самопроверка — модуль
std.testреализован на чистом YaoXiang, использует в качестве основыstd.assert.assert - Тестовые файлы — это обычные файлы
.yx— успешное/неуспешное прохождение определяется по коду завершения
Детальный дизайн
1. Дизайн CLI
yaoxiang test [OPTIONS] [PATHS]
Arguments:
[PATHS]... указать тестовые файлы или каталог (по умолчанию: из yaoxiang.toml, иначе tests/)
Options:
--filter <NAME> запустить только тесты с именем файла, содержащим <NAME>
--fail-fast остановиться при первом провале
--verbose, -v показать детальный stdout/stderr для каждого теста
--list только перечислить тестовые файлы, не запускать
--no-progress не показывать прогресс-бар (для CI)
--json вывести результаты в формате JSON (для интеграции CI)Формат вывода
Вывод по умолчанию:
Running 5 tests from 3 files...
tests/math_test.yx ........................ PASS (0.002s)
tests/list_test.yx ........................ PASS (0.001s)
tests/string_test.yx ...................... FAIL (0.003s)
`-- Expected "hello", got "world"
at tests/string_test.yx:12:5
Results: 2 passed, 1 failed, 0 skipped (0.006s)JSON вывод (--json):
{
"summary": { "total": 3, "passed": 2, "failed": 1, "skipped": 0, "time_secs": 0.006 },
"tests": [
{ "file": "tests/math_test.yx", "passed": true, "time_secs": 0.002 },
{
"file": "tests/string_test.yx",
"passed": false,
"time_secs": 0.003,
"error": "Expected \"hello\", got \"world\"",
"exit_code": 1
}
]
}2. Конфигурация yaoxiang.toml
Располагается в [tool.test], соответствует соглашению о сторонних расширениях [tool.*] из RFC-015:
[project]
name = "my-project"
[tool.test]
patterns = ["tests/**/*.yx"]
# В будущем можно расширить:
# exclude = ["tests/fixtures/**"]
# parallel = true- По умолчанию
patterns = ["tests/**/*.yx"]— нулевая конфигурация, работает из коробки - Однофайловый режим (
yaoxiang test foo.yx) запускается напрямую, без чтения конфигурации - В будущем возможно выделение в отдельный репозиторий (расположение
[tool.test]не изменится)
3. Модуль std.test (чистый YaoXiang)
// std/test.yx — Pure YaoXiang test assertion library
// First dogfooding library: YaoXiang's test library written in YaoXiang
use std.assert
assert_eq: (a: ?, b: ?) -> Void = (a, b) => {
assert.assert(a == b, f"Expected {b}, got {a}")
}
assert_ne: (a: ?, b: ?) -> Void = (a, b) => {
assert.assert(a != b, f"Expected not equal to {b}, got {a}")
}
assert_true: (cond: Bool) -> Void = (cond) => {
assert.assert(cond, f"Expected true, got {cond}")
}
assert_false: (cond: Bool) -> Void = (cond) => {
assert.assert(!cond, f"Expected false, got {cond}")
}- 4 функции проверки, все используют
f"..."для диагностических сообщений - Параметры-обобщения
?вassert_eq/assert_neзависят от системы обобщений std.testне зависит от нативного кода, реализован на чистом YaoXiang
4. Механизм загрузки стандартной библиотеки (ключевой дизайн)
Фаза 1: Встраивание в бинарный файл
std/test.yx (и все будущие модули стандартной библиотеки на YaoXiang) встраиваются в бинарный файл при сборке:
// build.rs или скрипт сборки, автогенерация
pub const STD_YX_FILES: &[(&str, &str)] = &[
("std/test.yx", r#"..."#), // текст исходного кода
// в будущем больше
];Загрузчик модулей при разборе use std.test:
- Сначала проверяет нативные модули Rust (существующий механизм, например
std.assert) - Если не найдено, ищет во встроенных
STD_YX_FILES, находит исходный кодstd/test.yx - Компилирует этот исходный код и регистрирует в системе модулей
Преимущества:
- В однофайловом режиме
use std.testтоже работает - Версия стандартной библиотеки жёстко привязана к бинарному файлу, невозможно несоответствие версий
- Не требует от пользователя настройки пути к стандартной библиотеке
Будущее: стандартная библиотека в файловой системе
Когда модель проектов YaoXiang созреет, стандартная библиотека перейдёт на файловую форму. Подробности в обновлении RFC-014.
5. Обнаружение и выполнение
Фаза обнаружения:
- Если указан
[PATHS], используются указанные пути напрямую - Иначе читается
[tool.test].patternsизyaoxiang.toml - Если конфигурация отсутствует, по умолчанию
tests/**/*.yx - Применяется фильтрация
--filter(имя файла содержит)
Фаза выполнения:
- Для каждого файла: запуск подпроцесса
yaoxiang run <file> - Проверка exit code: 0 = PASS, не 0 = FAIL
- Захват stdout/stderr для отчётности
- Только последовательное выполнение (Фаза 1), в будущем поддержка
--parallel - При
--fail-fast— немедленная остановка при первом FAIL
6. Изоляция тестов
Изоляция тестов естественно реализуется через границы процессов:
- Каждый тестовый файл выполняется в отдельном дочернем процессе
- Каждый дочерний процесс имеет независимые Heap, Frame, NativeContext
- Паника в одном тестовом файле не влияет на другие тестовые файлы
- Не требуется дополнительный механизм изоляции контекстов Heap
Связь с существующей системой
| Компонент | Связь |
|---|---|
Rust #[test] | Без изменений, внутренние тесты компилятора остаются на Rust |
Существующие интеграционные тесты .yx (tests/yaoxiang/) | Обнаруживаются и выполняются через yaoxiang test |
std.assert.assert(cond) | Сохраняется, std.test зависит от него на нижнем уровне |
#200 рефакторинг (io.println → assert.assert) | Полностью согласуется с yaoxiang test |
Аннотация @ | Не используется, @test не вводится |
Стратегия реализации
Фаза 1: Основная функциональность
Область изменений:
src/main.rs— новая подкомандаTestsrc/std/test.yx— новый модуль на чистом YaoXiangbuild.rs— встраиваниеstd/*.yxв бинарный файл- Загрузчик модулей — поддержка загрузки модулей
.yxиз встроенных источников - Парсинг конфигурации RFC-015 — секция
[tool.test] - Выполнение подпроцессов + отчётность
Результат:
yaoxiang testбазово работает- 4 функции проверки в
std.test - Обнаружение по умолчанию
tests/**/*.yx - Последовательное выполнение + формат вывода по умолчанию
Фаза 2: Улучшения
- Параметры
--filter/--fail-fast/--verbose - Вывод
--json(интеграция CI) - Опция
--list - Опция
--no-progress
Фаза 3: Продвинутые возможности
--parallelпараллельное выполнение (зависит от завершения модели параллелизма spawn)- Конфигурация
[tool.test].exclude - Дополнительные функции проверки (например,
assert_approx_eqдля Float)
Риски и митигация
| Риск | Вероятность | Митигация |
|---|---|---|
Ошибка интерполяции f"..." на обобщённых типах | Низкая | Базовая применимость к типам уже проверена в std.assert.assert |
| Накладные расходы на запуск подпроцессов влияют на скорость тестов | Средняя | Последовательное выполнение в Фазе 1 приемлемо; параллелизм в Фазе 3 смягчит |
Парсинг конфигурации yaoxiang.toml отсутствует в текущем CLI | Низкая | Простое расширение, не влияет на основную функциональность |
Обобщения ? недоступны в std.test | Низкая | Возможен откат к типу Any или специализация типов |
Встраивание исходных файлов .yx в бинарный файл увеличивает размер | Низкая | Исходные файлы .yx очень малы, можно пренебречь |
Открытые вопросы
- [ ] Сможет ли загрузчик модулей правильно разрешить импорт
use std.assertвstd/test.yx? Необходимо проверить разрешение зависимостей между встроенными модулями-источниками - [ ] Не создаст ли
to_stringобобщённого типа вf"..."новых ограничений типа? Необходимо проверить
Журнал решений по дизайну
| Решение | Решение | Дата | Обоснование |
|---|---|---|---|
| Способ маркировки тестов | Без аннотации @test, тестовые файлы — обычные .yx | 2026-07-26 | Нулевые изменения компилятора, изоляция через подпроцессы |
| Способ проверок | Функции модуля std.test на чистом YaoXiang | 2026-07-26 | Самопроверка, без нативного кода |
| Модель выполнения тестов | Подпроцесс yaoxiang run <file> + exit code | 2026-07-26 | Изоляция на уровне процессов, нулевые изменения компилятора |
| Загрузка стандартной библиотеки | Текущая: встраивание в бинарный файл, будущее: файловая система | 2026-07-26 | Привязка версий, работа в однофайловом режиме |
| Обобщённые проверки | Зависимость от параметра-обобщения ? | 2026-07-26 | Без специализации, доверие к системе обобщений |
Ссылки
- RFC-014: Дизайн системы пакетов — структура каталогов стандартной библиотеки
- RFC-015: Система конфигурации — секция конфигурации
[tool.test] - RFC-030: Механизм проверок assert — зависимость на нижнем уровне
- Механизм Rust
#[test]— референсный дизайн
