From 8c508a77b5fdd678fe2d9df41725d98ea30f61bf Mon Sep 17 00:00:00 2001 From: Dmitry Ivanov Date: Thu, 15 Oct 2026 21:24:57 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20=D0=BA=D0=BB=D0=B0=D1=81=D1=81=D0=B8=D1=84=D0=B8?= =?UTF-8?q?=D0=BA=D0=B0=D1=86=D0=B8=D1=8E=20=D0=BD=D0=B5=D1=81=D1=82=D1=80?= =?UTF-8?q?=D1=83=D0=BA=D1=82=D1=83=D1=80=D0=B8=D1=80=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D0=BD=D0=BD=D1=8B=D1=85=20=D0=BE=D1=88=D0=B8=D0=B1=D0=BE=D0=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 63 +- benchmarks/README.md | 16 +- ...21\210\320\270\320\261\320\276\320\272.os" | 229 +++++ ...21\210\320\270\320\261\320\276\320\272.os" | 84 ++ docs/README.md | 2 + ...21\210\320\270\320\261\320\276\320\272.md" | 181 ++++ ...21\210\320\270\320\261\320\276\320\272.md" | 316 ++++++ packagedef | 4 +- samples/README.md | 3 +- ...21\210\320\270\320\261\320\272\320\270.os" | 28 + ...21\210\320\270\320\261\320\276\320\272.os" | 406 ++++++++ ...21\210\320\270\320\261\320\272\320\270.os" | 264 +++++ ...21\210\320\270\320\261\320\276\320\272.os" | 515 ++++++++++ ...21\210\320\270\320\261\320\276\320\272.os" | 240 +++++ ...21\200\320\260\320\262\320\270\320\273.os" | 134 +++ ...21\210\320\270\320\261\320\276\320\272.os" | 340 +++++++ ...21\210\320\270\320\261\320\276\320\272.os" | 899 ++++++++++++++++++ ...21\210\320\270\320\261\320\276\320\272.os" | 120 +++ ...21\210\320\270\320\261\320\272\320\270.os" | 27 + 19 files changed, 3857 insertions(+), 14 deletions(-) create mode 100644 "benchmarks/\320\221\320\265\320\275\321\207\320\274\320\260\321\200\320\272\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\206\320\270\320\270\320\236\321\210\320\270\320\261\320\276\320\272.os" create mode 100644 "benchmarks/\320\221\320\265\320\275\321\207\320\274\320\260\321\200\320\272\320\234\320\260\321\201\321\210\321\202\320\260\320\261\320\270\321\200\320\276\320\262\320\260\320\275\320\270\321\217\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\206\320\270\320\270\320\236\321\210\320\270\320\261\320\276\320\272.os" create mode 100644 "docs/\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\202\320\276\321\200\320\236\321\210\320\270\320\261\320\276\320\272.md" create mode 100644 "docs/\320\237\321\200\320\260\320\262\320\270\320\273\320\276\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\206\320\270\320\270\320\236\321\210\320\270\320\261\320\276\320\272.md" create mode 100644 "samples/\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\206\320\270\321\217\320\235\320\265\321\201\321\202\321\200\321\203\320\272\321\202\321\203\321\200\320\270\321\200\320\276\320\262\320\260\320\275\320\275\320\276\320\271\320\236\321\210\320\270\320\261\320\272\320\270.os" create mode 100644 "src/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\230\320\275\320\264\320\265\320\272\321\201\320\237\321\200\320\260\320\262\320\270\320\273\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\206\320\270\320\270\320\236\321\210\320\270\320\261\320\276\320\272.os" create mode 100644 "src/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\246\320\265\320\277\320\276\321\207\320\272\320\260\320\232\320\260\320\264\321\200\320\276\320\262\320\236\321\210\320\270\320\261\320\272\320\270.os" create mode 100644 "src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\202\320\276\321\200\320\236\321\210\320\270\320\261\320\276\320\272.os" create mode 100644 "src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\237\321\200\320\260\320\262\320\270\320\273\320\276\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\206\320\270\320\270\320\236\321\210\320\270\320\261\320\276\320\272.os" create mode 100644 "tests/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\242\320\265\321\201\321\202\320\276\320\262\321\213\320\271\320\237\320\276\321\201\321\202\320\260\320\262\321\211\320\270\320\272\320\237\321\200\320\260\320\262\320\270\320\273.os" create mode 100644 "tests/\320\242\320\265\321\201\321\202\321\213\320\230\320\275\320\264\320\265\320\272\321\201\320\260\320\237\321\200\320\260\320\262\320\270\320\273\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\206\320\270\320\270\320\236\321\210\320\270\320\261\320\276\320\272.os" create mode 100644 "tests/\320\242\320\265\321\201\321\202\321\213\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\202\320\276\321\200\320\260\320\236\321\210\320\270\320\261\320\276\320\272.os" create mode 100644 "tests/\320\242\320\265\321\201\321\202\321\213\320\237\321\200\320\260\320\262\320\270\320\273\320\260\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\206\320\270\320\270\320\236\321\210\320\270\320\261\320\276\320\272.os" create mode 100644 "tests/\320\242\320\265\321\201\321\202\321\213\320\246\320\265\320\277\320\276\321\207\320\272\320\270\320\232\320\260\320\264\321\200\320\276\320\262\320\236\321\210\320\270\320\261\320\272\320\270.os" diff --git a/README.md b/README.md index 390aeb4..ff2686d 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,8 @@ - прикреплять к ошибкам структурированные диагностические данные; - находить нужные ошибки и группы ошибок во всей цепочке причин; - формировать пользовательское и диагностическое представления; -- проверять контракты аргументов с помощью стандартных структурированных ошибок. +- проверять контракты аргументов с помощью стандартных структурированных ошибок; +- классифицировать неструктурированные исключения по декларативным правилам. ## Навигация @@ -38,7 +39,7 @@ Установите пакет из OPM: -```console +```shell opm install errors ``` @@ -130,6 +131,56 @@ config.invalid_port: Некорректное значение порта Полный каталог фабрик и рекомендации по выбору близких кодов приведены в [руководстве по стандартным ошибкам](docs/СтандартныеОшибки.md). +### Классифицировать неструктурированную ошибку + +Декларативные правила присваивают стабильный код сообщениям внешних исключений. +Например, путь файла меняется, но тип ошибки, начало и окончание её сообщения остаются +постоянными: + +```bsl +Правила = Новый Массив; +Правила.Добавить( + Новый ПравилоКлассификацииОшибок + .СНативнымТипом("System.IO.IOException") + .НачинаетсяС("ru = 'Файл '''; en = 'The file '''") + .ОканчиваетсяНа("ru = ''' уже существует.'; en = ''' already exists.'") + .СИдентификатором("filesystem.already_exists.file_path") + .КлассифицируетКак( + "filesystem.already_exists", + "ru = 'Файл уже существует'; en = 'File already exists'" + ) +); + +Классификатор = Новый КлассификаторОшибок; +Классификатор.ДобавитьПравила(Правила); + +Попытка + ВыполнитьВнешнююОперациюСФайлом(); +Исключение + ИсходнаяОшибка = ИнформацияОбОшибке(); + // Например: External exception (System.IO.IOException): Файл 'report.json' уже существует. + + НормализованнаяОшибка = Классификатор.Классифицировать(ИсходнаяОшибка); +КонецПопытки; +``` + +Классификатор выделит из исходной ошибки тип `System.IO.IOException` и внутренний текст +`Файл 'report.json' уже существует.`. Правило проверит тип, префикс и окончание текста, +а путь файла между ними останется произвольным. Результатом будет ошибка со стабильным +кодом `filesystem.already_exists`. + + +Чтобы подключить отдельный набор правил, зарегистрируйте тип поставщика с методом `Все()`: + +```bsl +Классификатор.ДобавитьПоставщикаПравил(Тип("ПравилаОшибокШлюза")); +``` + +Регистрируйте тип поставщика, если ему не нужны параметры конструктора. Передавайте +готовый объект, когда правила зависят от настроек или других объектов. Примеры создания +и подключения наборов правил приведены в +[КлассификаторОшибок](docs/КлассификаторОшибок.md). + ### Добавить контекст Контекст описывает выполнявшуюся операцию, но сохраняет код исходной ошибки: @@ -235,6 +286,8 @@ ONESCRIPT_ERRORS_SECTIONS=current,causes,stack | [`РазборОшибки`](docs/РазборОшибки.md) | Сохранённый разбор цепочки причин | | [`СнимокКадраОшибки`](docs/СнимокКадраОшибки.md) | Безопасный снимок одного кадра цепочки | | [`ВыборкаОшибок`](docs/ВыборкаОшибок.md) | Фильтрация и группировка ошибок | +| [`КлассификаторОшибок`](docs/КлассификаторОшибок.md) | Классификация по декларативным правилам | +| [`ПравилоКлассификацииОшибок`](docs/ПравилоКлассификацииОшибок.md) | Описание одного правила | ## Документация и примеры @@ -249,18 +302,18 @@ ONESCRIPT_ERRORS_SECTIONS=current,causes,stack Установите зависимости: -```console +```shell opm install -l --dev ``` Запустите тесты: -```console +```shell oneunit execute ``` Соберите пакет: -```console +```shell opm build . ``` diff --git a/benchmarks/README.md b/benchmarks/README.md index 2085a04..56d6471 100644 --- a/benchmarks/README.md +++ b/benchmarks/README.md @@ -7,6 +7,8 @@ - [Создание и обёртывание ошибок](БенчмаркСозданияИОбертыванияОшибок.os) - [Создание с форматированием описания](БенчмаркСозданияОшибокСФорматированием.os) +- [Классификация неструктурированных ошибок](БенчмаркКлассификацииОшибок.os) +- [Масштабирование классификации по числу правил](БенчмаркМасштабированияКлассификацииОшибок.os) - [Разбор и поиск по цепочке](БенчмаркРазбораИПоискаОшибок.os) - [Материализация разбора](БенчмаркМатериализацииРазбораОшибки.os) - [Поиск в материализованной выборке](БенчмаркПоискаВВыборкеОшибок.os) @@ -21,24 +23,24 @@ Для запуска установите `BenchmarkOneScript`: -```powershell +```shell opm install benchmark ``` Запуск всех бенчмарков: -```powershell -benchos run .\benchmarks +```shell +benchos run ./benchmarks ``` Запуск отдельного набора: -```powershell -benchos run .\benchmarks\БенчмаркМатериализацииРазбораОшибки.os +```shell +benchos run ./benchmarks/БенчмаркМатериализацииРазбораОшибки.os ``` Короткий диагностический запуск: -```powershell -benchos run --iterationCount 2 --warmupCount 1 .\benchmarks +```shell +benchos run --iterationCount 2 --warmupCount 1 ./benchmarks ``` diff --git "a/benchmarks/\320\221\320\265\320\275\321\207\320\274\320\260\321\200\320\272\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\206\320\270\320\270\320\236\321\210\320\270\320\261\320\276\320\272.os" "b/benchmarks/\320\221\320\265\320\275\321\207\320\274\320\260\321\200\320\272\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\206\320\270\320\270\320\236\321\210\320\270\320\261\320\276\320\272.os" new file mode 100644 index 0000000..0f0dbf5 --- /dev/null +++ "b/benchmarks/\320\221\320\265\320\275\321\207\320\274\320\260\321\200\320\272\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\206\320\270\320\270\320\236\321\210\320\270\320\261\320\276\320\272.os" @@ -0,0 +1,229 @@ +#Использовать "../src" + +Перем _КлассификаторСТочнымПравилом; // КлассификаторОшибок +Перем _КлассификаторСоставнымПравилом; // КлассификаторОшибок +Перем _КлассификаторБезПодходящегоПравила; // КлассификаторОшибок +Перем _КлассификаторСПравиломПрефикса; // КлассификаторОшибок +Перем _КлассификаторСПравиломФрагмента; // КлассификаторОшибок +Перем _КлассификаторСПравиломОкончания; // КлассификаторОшибок +Перем _КлассификаторСПравиломПрефиксаИФрагмента; // КлассификаторОшибок +Перем _КлассификаторСПравиломПрефиксаИОкончания; // КлассификаторОшибок +Перем _КлассификаторСПравиломФрагментаИОкончания; // КлассификаторОшибок +Перем _ОшибкаСТочнымСообщением; // ИнформацияОбОшибке +Перем _ОшибкаСоставногоПравила; // ИнформацияОбОшибке +Перем _НераспознаннаяОшибка; // ИнформацияОбОшибке +Перем _Результат; // ИнформацияОбОшибке, Неопределено + +#Область ОбработчикиСобытий + +// Создаёт бенчмарк классификации неструктурированных ошибок. +&МониторингПамяти +&КоличествоИтераций(20) +&КоличествоПрогревочныхИтераций(10) +&КолонкаМакс +Процедура ПриСозданииОбъекта() +КонецПроцедуры + +// Подготавливает классификаторы и нативные ошибки для измерений. +// +// Параметры: +// Контекст - Структура - Контекст выполняемого кейса бенчмарка. +&ПередКаждымКейсом +Процедура ПодготовитьДанные(Контекст) Экспорт + + _КлассификаторСТочнымПравилом = СоздатьКлассификатор(ПравилоТочногоСообщения()); + _КлассификаторСоставнымПравилом = СоздатьКлассификатор(СоставноеПравило()); + _КлассификаторБезПодходящегоПравила = СоздатьКлассификатор(ПравилоТочногоСообщения()); + _КлассификаторСПравиломПрефикса = СоздатьКлассификатор(ПравилоПрефикса()); + _КлассификаторСПравиломФрагмента = СоздатьКлассификатор(ПравилоФрагмента()); + _КлассификаторСПравиломОкончания = СоздатьКлассификатор(ПравилоОкончания()); + _КлассификаторСПравиломПрефиксаИФрагмента = СоздатьКлассификатор(ПравилоПрефиксаИФрагмента()); + _КлассификаторСПравиломПрефиксаИОкончания = СоздатьКлассификатор(ПравилоПрефиксаИОкончания()); + _КлассификаторСПравиломФрагментаИОкончания = СоздатьКлассификатор(ПравилоФрагментаИОкончания()); + + _ОшибкаСТочнымСообщением = НоваяОшибка("External exception (System.IO.IOException): Файл недоступен"); + _ОшибкаСоставногоПравила = НоваяОшибка( + "External exception (System.IO.IOException): Операция с ресурсом не найдена" + ); + _НераспознаннаяОшибка = НоваяОшибка( + "External exception (System.InvalidOperationException): Неизвестная ошибка" + ); + +КонецПроцедуры + +#КонецОбласти + +#Область Бенчмарки + +// Измеряет классификацию правила по точному внутреннему сообщению и нативному типу. +&Бенчмарк(Наименование = "Точное правило") +Процедура КлассифицироватьПоТочномуПравилу() Экспорт + + _Результат = _КлассификаторСТочнымПравилом.Классифицировать(_ОшибкаСТочнымСообщением); + +КонецПроцедуры + +// Измеряет классификацию правила по префиксу, фрагменту и окончанию внутреннего сообщения. +&Бенчмарк(Наименование = "Составное текстовое правило") +Процедура КлассифицироватьПоСоставномуПравилу() Экспорт + + _Результат = _КлассификаторСоставнымПравилом.Классифицировать(_ОшибкаСоставногоПравила); + +КонецПроцедуры + +// Измеряет классификацию правила по префиксу внутреннего сообщения. +&Бенчмарк(Наименование = "Правило по префиксу") +Процедура КлассифицироватьПоПравилуПрефикса() Экспорт + + _Результат = _КлассификаторСПравиломПрефикса.Классифицировать(_ОшибкаСоставногоПравила); + +КонецПроцедуры + +// Измеряет классификацию правила по фрагменту внутреннего сообщения. +&Бенчмарк(Наименование = "Правило по фрагменту") +Процедура КлассифицироватьПоПравилуФрагмента() Экспорт + + _Результат = _КлассификаторСПравиломФрагмента.Классифицировать(_ОшибкаСоставногоПравила); + +КонецПроцедуры + +// Измеряет классификацию правила по окончанию внутреннего сообщения. +&Бенчмарк(Наименование = "Правило по окончанию") +Процедура КлассифицироватьПоПравилуОкончания() Экспорт + + _Результат = _КлассификаторСПравиломОкончания.Классифицировать(_ОшибкаСоставногоПравила); + +КонецПроцедуры + +// Измеряет классификацию правила по префиксу и фрагменту внутреннего сообщения. +&Бенчмарк(Наименование = "Правило по префиксу и фрагменту") +Процедура КлассифицироватьПоПравилуПрефиксаИФрагмента() Экспорт + + _Результат = _КлассификаторСПравиломПрефиксаИФрагмента.Классифицировать(_ОшибкаСоставногоПравила); + +КонецПроцедуры + +// Измеряет классификацию правила по префиксу и окончанию внутреннего сообщения. +&Бенчмарк(Наименование = "Правило по префиксу и окончанию") +Процедура КлассифицироватьПоПравилуПрефиксаИОкончания() Экспорт + + _Результат = _КлассификаторСПравиломПрефиксаИОкончания.Классифицировать(_ОшибкаСоставногоПравила); + +КонецПроцедуры + +// Измеряет классификацию правила по фрагменту и окончанию внутреннего сообщения. +&Бенчмарк(Наименование = "Правило по фрагменту и окончанию") +Процедура КлассифицироватьПоПравилуФрагментаИОкончания() Экспорт + + _Результат = _КлассификаторСПравиломФрагментаИОкончания.Классифицировать(_ОшибкаСоставногоПравила); + +КонецПроцедуры + +// Измеряет полный проход по правилам без результата. +&Бенчмарк(Наименование = "Промах правила") +Процедура НеКлассифицироватьНераспознаннуюОшибку() Экспорт + + _Результат = _КлассификаторБезПодходящегоПравила.Классифицировать(_НераспознаннаяОшибка); + +КонецПроцедуры + +#КонецОбласти + +#Область СлужебныеПроцедурыИФункции + +Функция СоздатьКлассификатор(Правило) + + Правила = Новый Массив; + Правила.Добавить(Правило); + + Классификатор = Новый КлассификаторОшибок; + Классификатор.ДобавитьПравила(Новый ФиксированныйМассив(Правила)); + + Возврат Классификатор; + +КонецФункции + +Функция ПравилоТочногоСообщения() + + Возврат Новый ПравилоКлассификацииОшибок() + .СНативнымТипом("System.IO.IOException") + .СовпадаетС("Файл недоступен") + .КлассифицируетКак("benchmark.exact", "Точная ошибка бенчмарка"); + +КонецФункции + +Функция СоставноеПравило() + + Возврат Новый ПравилоКлассификацииОшибок() + .СНативнымТипом("System.IO.IOException") + .НачинаетсяС("Операция") + .Содержит("ресурс") + .ОканчиваетсяНа("не найдена") + .КлассифицируетКак("benchmark.composite", "Составная ошибка бенчмарка"); + +КонецФункции + +Функция ПравилоПрефикса() + + Возврат Новый ПравилоКлассификацииОшибок() + .СНативнымТипом("System.IO.IOException") + .НачинаетсяС("Операция") + .КлассифицируетКак("benchmark.prefix", "Ошибка по префиксу"); + +КонецФункции + +Функция ПравилоФрагмента() + + Возврат Новый ПравилоКлассификацииОшибок() + .СНативнымТипом("System.IO.IOException") + .Содержит("ресурс") + .КлассифицируетКак("benchmark.fragment", "Ошибка по фрагменту"); + +КонецФункции + +Функция ПравилоОкончания() + + Возврат Новый ПравилоКлассификацииОшибок() + .СНативнымТипом("System.IO.IOException") + .ОканчиваетсяНа("не найдена") + .КлассифицируетКак("benchmark.suffix", "Ошибка по окончанию"); + +КонецФункции + +Функция ПравилоПрефиксаИФрагмента() + + Возврат Новый ПравилоКлассификацииОшибок() + .СНативнымТипом("System.IO.IOException") + .НачинаетсяС("Операция") + .Содержит("ресурс") + .КлассифицируетКак("benchmark.prefix_fragment", "Ошибка по префиксу и фрагменту"); + +КонецФункции + +Функция ПравилоПрефиксаИОкончания() + + Возврат Новый ПравилоКлассификацииОшибок() + .СНативнымТипом("System.IO.IOException") + .НачинаетсяС("Операция") + .ОканчиваетсяНа("не найдена") + .КлассифицируетКак("benchmark.prefix_suffix", "Ошибка по префиксу и окончанию"); + +КонецФункции + +Функция ПравилоФрагментаИОкончания() + + Возврат Новый ПравилоКлассификацииОшибок() + .СНативнымТипом("System.IO.IOException") + .Содержит("ресурс") + .ОканчиваетсяНа("не найдена") + .КлассифицируетКак("benchmark.fragment_suffix", "Ошибка по фрагменту и окончанию"); + +КонецФункции + +Функция НоваяОшибка(Описание) + + Возврат Новый ИнформацияОбОшибке(Описание, Новый Структура("Источник", "Бенчмарк")); + +КонецФункции + +#КонецОбласти diff --git "a/benchmarks/\320\221\320\265\320\275\321\207\320\274\320\260\321\200\320\272\320\234\320\260\321\201\321\210\321\202\320\260\320\261\320\270\321\200\320\276\320\262\320\260\320\275\320\270\321\217\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\206\320\270\320\270\320\236\321\210\320\270\320\261\320\276\320\272.os" "b/benchmarks/\320\221\320\265\320\275\321\207\320\274\320\260\321\200\320\272\320\234\320\260\321\201\321\210\321\202\320\260\320\261\320\270\321\200\320\276\320\262\320\260\320\275\320\270\321\217\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\206\320\270\320\270\320\236\321\210\320\270\320\261\320\276\320\272.os" new file mode 100644 index 0000000..73f041f --- /dev/null +++ "b/benchmarks/\320\221\320\265\320\275\321\207\320\274\320\260\321\200\320\272\320\234\320\260\321\201\321\210\321\202\320\260\320\261\320\270\321\200\320\276\320\262\320\260\320\275\320\270\321\217\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\206\320\270\320\270\320\236\321\210\320\270\320\261\320\276\320\272.os" @@ -0,0 +1,84 @@ +#Использовать "../src" + +&Параметры(1, 10, 100, 1000) +Перем КоличествоПравил Экспорт; // Число + +Перем _Классификатор; // КлассификаторОшибок +Перем _Ошибка; // ИнформацияОбОшибке +Перем _Результат; // ИнформацияОбОшибке, Неопределено + +#Область ОбработчикиСобытий + +// Создаёт бенчмарк классификации последним правилом набора. +&МониторингПамяти +&КоличествоИтераций(20) +&КоличествоПрогревочныхИтераций(10) +&КолонкаМакс +Процедура ПриСозданииОбъекта() +КонецПроцедуры + +// Подготавливает набор правил, в котором целевое правило добавлено последним. +// +// Параметры: +// Контекст - Структура - Контекст выполняемого кейса бенчмарка. +&ПередКаждымКейсом +Процедура ПодготовитьДанные(Контекст) Экспорт + + _Классификатор = СоздатьКлассификатор(); + _Ошибка = НоваяОшибка( + "External exception (System.IO.IOException): " + ТекстПравила(КоличествоПравил) + ); + +КонецПроцедуры + +#КонецОбласти + +#Область Бенчмарки + +// Измеряет классификацию, когда подходящее правило добавлено в набор последним. +&Бенчмарк(Наименование = "Последнее правило набора") +Процедура КлассифицироватьПоследнимПравилом() Экспорт + + _Результат = _Классификатор.Классифицировать(_Ошибка); + +КонецПроцедуры + +#КонецОбласти + +#Область СлужебныеПроцедурыИФункции + +Функция СоздатьКлассификатор() + + Правила = Новый Массив; + Для НомерПравила = 1 По КоличествоПравил Цикл + Правила.Добавить( + Новый ПравилоКлассификацииОшибок() + .СНативнымТипом("System.IO.IOException") + .СовпадаетС(ТекстПравила(НомерПравила)) + .КлассифицируетКак( + "benchmark.rules.rule_" + НомерПравила, + "Правило бенчмарка " + НомерПравила + ) + ); + КонецЦикла; + + Классификатор = Новый КлассификаторОшибок; + Классификатор.ДобавитьПравила(Новый ФиксированныйМассив(Правила)); + + Возврат Классификатор; + +КонецФункции + +Функция ТекстПравила(НомерПравила) + + Возврат "Ошибка правила " + НомерПравила; + +КонецФункции + +Функция НоваяОшибка(Описание) + + Возврат Новый ИнформацияОбОшибке(Описание, Новый Структура("Источник", "Бенчмарк")); + +КонецФункции + +#КонецОбласти diff --git a/docs/README.md b/docs/README.md index efc904e..caed3ca 100644 --- a/docs/README.md +++ b/docs/README.md @@ -15,6 +15,8 @@ - [РазборОшибки](РазборОшибки.md) - разбор физической цепочки и представления - [СнимокКадраОшибки](СнимокКадраОшибки.md) - снимок кадра - [ВыборкаОшибок](ВыборкаОшибок.md) - выборка и группировка ошибок +- [КлассификаторОшибок](КлассификаторОшибок.md) - классификация неструктурированных ошибок +- [ПравилоКлассификацииОшибок](ПравилоКлассификацииОшибок.md) - описание правила классификации ## Модули diff --git "a/docs/\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\202\320\276\321\200\320\236\321\210\320\270\320\261\320\276\320\272.md" "b/docs/\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\202\320\276\321\200\320\236\321\210\320\270\320\261\320\276\320\272.md" new file mode 100644 index 0000000..c905980 --- /dev/null +++ "b/docs/\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\202\320\276\321\200\320\236\321\210\320\270\320\261\320\276\320\272.md" @@ -0,0 +1,181 @@ +# КлассификаторОшибок + +Классифицирует неструктурированные `ИнформацияОбОшибке` по добавленным правилам. +Коды этих правил экземпляр считает собственными. + +## Оглавление + +- [Создание](#создание) +- [Методы](#методы) +- [Практический пример](#практический-пример) +- [Результат классификации](#результат-классификации) +- [Выбор правила](#выбор-правила) +- [Подготовка и проверка правил](#подготовка-и-проверка-правил) + +## Создание + +```bsl +Новый КлассификаторОшибок +``` + +Создаёт пустой классификатор. Правила добавляются методом `ДобавитьПравила` или +загружаются из поставщика, зарегистрированного методом `ДобавитьПоставщикаПравил`. + +## Методы + +### ДобавитьПравила + +```bsl +.ДобавитьПравила(<Правила>) +``` + +Добавляет `Массив` или `ФиксированныйМассив` правил. Все добавленные наборы +участвуют в общем выборе по силе совпадения и приоритету. Классификатор сохраняет +значения правил в момент добавления, поэтому их последующее изменение не влияет +на классификацию. + +### ДобавитьПоставщикаПравил + +```bsl +.ДобавитьПоставщикаПравил(<Поставщик>) +``` + +Регистрирует тип или готовый объект поставщика с методом `Все()`. Метод должен +возвращать `Массив` или `ФиксированныйМассив` объектов +`ПравилоКлассификацииОшибок`. + +Регистрация типа: + +```bsl +Классификатор = Новый КлассификаторОшибок; +Классификатор.ДобавитьПоставщикаПравил(Тип("ПравилаПриложения")); +``` + +Для типа требуется конструктор без параметров. Классификатор создаст объект +при первой классификации. + +Регистрация готового объекта: + +```bsl +Поставщик = Новый ПравилаПриложения(Настройки); +Классификатор = Новый КлассификаторОшибок; +Классификатор.ДобавитьПоставщикаПравил(Поставщик); +``` + +При регистрации объекта метод `Все()` также не вызывается. Успешно загруженные +правила и подготовленный индекс кешируются. Если загрузка завершилась ошибкой, +следующая классификация повторит загрузку. + +Регистрации сохраняются в порядке добавления, но порядок не устраняет конфликт +разных кодов. + +### Классифицировать + +```bsl +.Классифицировать(<Ошибка>) +``` + +Возвращает нормализованную ошибку или `Неопределено`. Порядок обработки: + +1. Ленивая подготовка поставщиков правил. +2. Корневая ошибка с собственным кодом. +3. Собственные структурированные коды во вложенных кадрах. +4. Все добавленные правила. + +Для совпадений одной силы с разными кодами нужен однозначный результат. Большее +значение `Приоритет` правила разрешает такой конфликт. При равных приоритетах +классификатор возвращает `Неопределено`. + +## Практический пример + +Правило распознаёт ошибку HTTP-запроса. Адрес и длительность ожидания в её тексте +меняются, но начало сообщения и причина таймаута остаются постоянными. + +```bsl +Правила = Новый Массив; +Правила.Добавить( + Новый ПравилоКлассификацииОшибок() + .НачинаетсяС("Ошибка HTTP-запроса:") + .Содержит("Connection timed out") + .КлассифицируетКак( + КодыСтандартныхОшибок.ИстеклоВремяОжиданияСоединения(), + "Истекло время ожидания соединения" + ) +); + +Классификатор = Новый КлассификаторОшибок; +Классификатор.ДобавитьПравила(Правила); + +Попытка + ВызватьИсключение + "Ошибка HTTP-запроса: https://api.example.org/users. Connection timed out after 30 seconds"; +Исключение + ИсходнаяОшибка = ИнформацияОбОшибке(); + НормализованнаяОшибка = Классификатор.Классифицировать(ИсходнаяОшибка); + + Если НормализованнаяОшибка <> Неопределено Тогда + КодОшибки = ДиагностикаОшибок.КодОшибки(НормализованнаяОшибка); + Сообщить(КодОшибки); + КонецЕсли; +КонецПопытки; +``` + +В результате `ДиагностикаОшибок.КодОшибки()` вернёт `connection.timeout`, а +`НормализованнаяОшибка.Причина` будет ссылаться на `ИсходнаяОшибка`. +`ВызватьИсключение` использован только для демонстрации: в прикладном коде на его +месте будет HTTP-запрос или другая операция, которая может завершиться такой ошибкой. + +## Результат классификации + +При совпадении классификатор создаёт новый внешний кадр вида `Ошибка`: + +- `КодОшибки` берётся из правила; +- поле `Сообщение` получает локализованное `ОписаниеОшибки` правила; +- `Данные.Источник` получает значение `ПравилаКлассификации`; +- `Данные.Правило` содержит явный или автоматический идентификатор; +- `Данные.НативныйТип` добавляется, если нативный тип известен для совпавшего кадра; +- `Причина` ссылается на исходную `ИнформацияОбОшибке`, переданную в `Классифицировать()`. + +Исходная цепочка не изменяется и полностью сохраняется как причина нового кадра. Поэтому +нормализованный код удобен прикладному коду, а исходные сообщения и стек остаются доступны +диагностике. + +## Выбор правила + +Сила определяется источником совпадения. Классификатор сначала оставляет кандидатов +максимальной силы и только затем сравнивает `Приоритет`. + +От более сильного к более слабому совпадения располагаются так: + +1. Собственный структурированный код во вложенном кадре. +2. Правило с .NET-типом в физическом кадре. +3. Правило с .NET-типом в кадре, восстановленном из подробного представления. +4. Текстовое правило. + +Все текстовые способы имеют одинаковую силу. Например, точное совпадение текста само по +себе не сильнее совпадения по префиксу. + +Произвольно большой `Приоритет` не может победить кандидата большей силы. Среди кандидатов +максимальной силы выбирается наибольший приоритет. Если после этого остаются совпадения +одного кода, результат однозначен. Если остаются разные коды, `Классифицировать()` возвращает +`Неопределено`. + +## Подготовка и проверка правил + +При `ДобавитьПравила()` классификатор сразу проверяет правила, строит неизменяемый индекс и +сохраняет подготовленные значения полей. Изменение объекта правила или исходного массива после +успешного добавления не влияет на классификацию. + +Для поставщика, зарегистрированного через `ДобавитьПоставщикаПравил()`, проверка и снимок +создаются при первой успешной ленивой загрузке. После этого правила и индекс кешируются; +последующие изменения объектов поставщика не меняют подготовленный набор. + +Проверка завершается структурированной ошибкой в следующих случаях: + +- не задан код или описание результата `КлассифицируетКак()`; +- `Приоритет` типа, отличного от `Число`, приводит к `argument.invalid_type`; +- точное сообщение сочетается с префиксом, фрагментом или окончанием; +- у текстового правила без `СНативнымТипом()` не задан `НачинаетсяС()`. + +Пустые необязательные и неиспользуемые поля допустимы. Локализованные поля разбираются +при подготовке индекса, и для каждого языка определяется полный согласованный набор. diff --git "a/docs/\320\237\321\200\320\260\320\262\320\270\320\273\320\276\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\206\320\270\320\270\320\236\321\210\320\270\320\261\320\276\320\272.md" "b/docs/\320\237\321\200\320\260\320\262\320\270\320\273\320\276\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\206\320\270\320\270\320\236\321\210\320\270\320\261\320\276\320\272.md" new file mode 100644 index 0000000..387b4f0 --- /dev/null +++ "b/docs/\320\237\321\200\320\260\320\262\320\270\320\273\320\276\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\206\320\270\320\270\320\236\321\210\320\270\320\261\320\276\320\272.md" @@ -0,0 +1,316 @@ +# ПравилоКлассификацииОшибок + +Описывает, как распознать неструктурированную ошибку и присвоить ей стабильный код. +[`КлассификаторОшибок`](КлассификаторОшибок.md) применяет условия правила к каждому +доступному кадру цепочки ошибки. + +## Создание + +```bsl +Правило = Новый ПравилоКлассификацииОшибок; +``` + +После создания задайте условия сопоставления, метаданные и результат. В примерах +`КлассифицируетКак()` указан последним, чтобы определение читалось от условий к результату. + +Для нативной ошибки все текстовые условия - `СовпадаетС()`, `НачинаетсяС()`, +`Содержит()` и `ОканчиваетсяНа()` - проверяют внутреннее сообщение без служебной +обёртки `External exception (...)`. `СНативнымТипом()` остаётся отдельным условием +для типа .NET. + +## Методы + +### СовпадаетС + +```bsl +.СовпадаетС(<ИсходноеСообщение>) +``` + +Задаёт точное сообщение ошибки. Его можно сочетать только с `СНативнымТипом()`. + +Например, для `External exception (System.IO.IOException): Файл уже существует` +сопоставляется `Файл уже существует`. + +#### Параметры + +| Параметр | Тип | Описание | +| --- | --- | --- | +| ИсходноеСообщение | `Строка` | Полное сообщение или шаблон `НСтр` | + +#### Возвращаемое значение + +`ПравилоКлассификацииОшибок`: это правило. + +### СНативнымТипом + +```bsl +.СНативнымТипом(<НативныйТип>) +``` + +Ограничивает правило полным именем типа .NET. Используйте его, когда одинаковый текст +может относиться к разным причинам ошибки. + +#### Параметры + +| Параметр | Тип | Описание | +| --- | --- | --- | +| НативныйТип | `Строка` | Полное имя типа .NET, например `System.IO.IOException` | + +#### Возвращаемое значение + +`ПравилоКлассификацииОшибок`: это правило. + +### НачинаетсяС + +```bsl +.НачинаетсяС(<Префикс>) +``` + +Задаёт начало сообщения. Для текстового правила без `СНативнымТипом()` префикс обязателен. + +#### Параметры + +| Параметр | Тип | Описание | +| --- | --- | --- | +| Префикс | `Строка` | Начальная часть сообщения или шаблон `НСтр` | + +#### Возвращаемое значение + +`ПравилоКлассификацииОшибок`: это правило. + +### Содержит + +```bsl +.Содержит(<Фрагмент>) +``` + +Требует наличия фрагмента в сообщении. Применяйте его вместе с префиксом или нативным +типом, чтобы не сопоставить постороннюю ошибку. + +#### Параметры + +| Параметр | Тип | Описание | +| --- | --- | --- | +| Фрагмент | `Строка` | Часть сообщения или шаблон `НСтр` | + +#### Возвращаемое значение + +`ПравилоКлассификацииОшибок`: это правило. + +### ОканчиваетсяНа + +```bsl +.ОканчиваетсяНа(<Окончание>) +``` + +Требует указанное окончание сообщения. + +#### Параметры + +| Параметр | Тип | Описание | +| --- | --- | --- | +| Окончание | `Строка` | Конечная часть сообщения или шаблон `НСтр` | + +#### Возвращаемое значение + +`ПравилоКлассификацииОшибок`: это правило. + +### СПриоритетом + +```bsl +.СПриоритетом(<Приоритет>) +``` + +Задаёт приоритет для выбора между совпадениями одинаковой силы. Большее значение имеет +преимущество. Не используйте приоритет для замены более точного условия. + +#### Параметры + +| Параметр | Тип | Описание | +| --- | --- | --- | +| Приоритет | `Число` | Приоритет правила | + +#### Возвращаемое значение + +`ПравилоКлассификацииОшибок`: это правило. + +### СИдентификатором + +```bsl +.СИдентификатором(<Идентификатор>) +``` + +Задаёт стабильное имя правила. Классификатор сохраняет его в поле `Правило` +диагностических данных результата. Если идентификатор не задан, классификатор создаёт его +автоматически. + +#### Параметры + +| Параметр | Тип | Описание | +| --- | --- | --- | +| Идентификатор | `Строка` | Стабильное имя правила | + +#### Возвращаемое значение + +`ПравилоКлассификацииОшибок`: это правило. + +### КлассифицируетКак + +```bsl +.КлассифицируетКак(<КодОшибки>, <ОписаниеОшибки>) +``` + +Задаёт обязательные код и описание результата. Положение метода в цепочке не влияет на +сопоставление. Правило без результата классификатор не зарегистрирует. + +#### Параметры + +| Параметр | Тип | Описание | +| --- | --- | --- | +| КодОшибки | `Строка` | Стабильный машинный код результата | +| ОписаниеОшибки | `Строка` | Описание результата или шаблон `НСтр` | + +#### Возвращаемое значение + +`ПравилоКлассификацииОшибок`: это правило. + +### НативныйТип + +```bsl +.НативныйТип() +``` + +Возвращает заданный .NET-тип или пустую строку. + +#### Возвращаемое значение + +`Строка`: полное имя .NET-типа или пустая строка. + +### ИсходноеСообщение + +```bsl +.ИсходноеСообщение() +``` + +Возвращает сообщение, заданное методом `СовпадаетС()`. + +#### Возвращаемое значение + +`Строка`: точное сообщение, шаблон `НСтр` или пустая строка. + +### Префикс + +```bsl +.Префикс() +``` + +Возвращает сообщение, заданное методом `НачинаетсяС()`. + +#### Возвращаемое значение + +`Строка`: префикс, шаблон `НСтр` или пустая строка. + +### Фрагмент + +```bsl +.Фрагмент() +``` + +Возвращает сообщение, заданное методом `Содержит()`. + +#### Возвращаемое значение + +`Строка`: фрагмент, шаблон `НСтр` или пустая строка. + +### Окончание + +```bsl +.Окончание() +``` + +Возвращает сообщение, заданное методом `ОканчиваетсяНа()`. + +#### Возвращаемое значение + +`Строка`: окончание, шаблон `НСтр` или пустая строка. + +### КодОшибки + +```bsl +.КодОшибки() +``` + +Возвращает код результата классификации. + +#### Возвращаемое значение + +`Строка`: код ошибки или пустая строка. + +### ОписаниеРезультата + +```bsl +.ОписаниеРезультата() +``` + +Возвращает описание результата классификации. + +#### Возвращаемое значение + +`Строка`: описание, шаблон `НСтр` или пустая строка. + +### Идентификатор + +```bsl +.Идентификатор() +``` + +Возвращает явно заданный идентификатор правила. + +#### Возвращаемое значение + +`Строка`: идентификатор или пустая строка. + +### Приоритет + +```bsl +.Приоритет() +``` + +Возвращает приоритет правила. + +#### Возвращаемое значение + +`Число`: приоритет правила. + +## Практический пример + +Внешнее исключение содержит изменяемый путь файла, но его тип и постоянные части текста +позволяют уверенно определить причину. Правило сначала описывает эти условия, а затем +задаёт код и описание результата: + +```bsl +Правило = Новый ПравилоКлассификацииОшибок + .СНативнымТипом("System.IO.IOException") + .НачинаетсяС("ru = 'Файл '''; en = 'The file '''") + .ОканчиваетсяНа("ru = ''' уже существует.'; en = ''' already exists.'") + .СИдентификатором("filesystem.already_exists.file_path") + .КлассифицируетКак( + "filesystem.already_exists", + "ru = 'Файл уже существует'; en = 'File already exists'" + ); +``` + +Для сообщения `External exception (System.IO.IOException): Файл 'report.json' уже +существует.` правило сопоставит тип `System.IO.IOException`, префикс и окончание, после +чего вернёт код `filesystem.already_exists`. Путь между ними в условие не входит. + +## Локализованные шаблоны + +Сообщение, префикс, фрагмент, окончание и описание могут быть обычной строкой или +шаблоном `НСтр` с вариантами `ru` и `en`. Обычная строка доступна для обоих языков. +Условия одного совпадения берутся из одного языка: русский префикс не объединяется с +английским окончанием. + +## Связанные сущности + +- [КлассификаторОшибок](КлассификаторОшибок.md) регистрирует и применяет правила diff --git a/packagedef b/packagedef index 0ce3804..0397f1a 100644 --- a/packagedef +++ b/packagedef @@ -1,5 +1,5 @@ Описание.Имя("errors") - .Версия("0.2.1") + .Версия("0.3.0") .Автор("Dmitry Ivanov") .АдресРепозитория("https://github.com/Stivo182/oscript-errors") .Описание("Библиотека структурированных ошибок") @@ -15,6 +15,8 @@ .ОпределяетКласс("СнимокКадраОшибки", "src/Классы/СнимокКадраОшибки.os") .ОпределяетКласс("ВыборкаОшибок", "src/Классы/ВыборкаОшибок.os") .ОпределяетКласс("РазборОшибки", "src/Классы/РазборОшибки.os") + .ОпределяетКласс("ПравилоКлассификацииОшибок", "src/Классы/ПравилоКлассификацииОшибок.os") + .ОпределяетКласс("КлассификаторОшибок", "src/Классы/КлассификаторОшибок.os") .ОпределяетМодуль("ДиагностикаОшибок", "src/Модули/ДиагностикаОшибок.os") .ОпределяетМодуль("КодыСтандартныхОшибок", "src/Модули/КодыСтандартныхОшибок.os") .ОпределяетМодуль("ПроверкаАргументов", "src/Модули/ПроверкаАргументов.os") diff --git a/samples/README.md b/samples/README.md index acd4f58..1beeafc 100644 --- a/samples/README.md +++ b/samples/README.md @@ -25,6 +25,7 @@ | Поиск причины | `ПоискВЦепочке.os` | `oscript samples/ПоискВЦепочке.os` | | Выборка нескольких кодов | `ВыборкаОшибок.os` | `oscript samples/ВыборкаОшибок.os` | | Текстовые представления | `ПредставлениеОшибки.os` | `oscript samples/ПредставлениеОшибки.os` | +| Классификация неструктурированной ошибки | `КлассификацияНеструктурированнойОшибки.os` | `oscript samples/КлассификацияНеструктурированнойОшибки.os` | ## Совместное применение API @@ -32,7 +33,7 @@ показывает классификацию настоящей ошибки преобразования, добавление контекста, оборачивание на границе приложения, поиск причины и диагностическое представление. -```console +```shell oscript samples/ОбработкаОшибкиКонфигурации.os ``` diff --git "a/samples/\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\206\320\270\321\217\320\235\320\265\321\201\321\202\321\200\321\203\320\272\321\202\321\203\321\200\320\270\321\200\320\276\320\262\320\260\320\275\320\275\320\276\320\271\320\236\321\210\320\270\320\261\320\272\320\270.os" "b/samples/\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\206\320\270\321\217\320\235\320\265\321\201\321\202\321\200\321\203\320\272\321\202\321\203\321\200\320\270\321\200\320\276\320\262\320\260\320\275\320\275\320\276\320\271\320\236\321\210\320\270\320\261\320\272\320\270.os" new file mode 100644 index 0000000..ab18234 --- /dev/null +++ "b/samples/\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\206\320\270\321\217\320\235\320\265\321\201\321\202\321\200\321\203\320\272\321\202\321\203\321\200\320\270\321\200\320\276\320\262\320\260\320\275\320\275\320\276\320\271\320\236\321\210\320\270\320\261\320\272\320\270.os" @@ -0,0 +1,28 @@ +// Демонстрирует присвоение стабильного кода внешней ошибке по её типу и тексту. +// Входная ошибка задана снимком, поэтому сценарий не зависит от файловой системы. + +#Использовать errors + +Правила = Новый Массив; +Правила.Добавить( + Новый ПравилоКлассификацииОшибок + .СНативнымТипом("System.IO.IOException") + .НачинаетсяС("ru = 'Файл '''; en = 'The file '''") + .ОканчиваетсяНа("ru = ''' уже существует.'; en = ''' already exists.'") + .СИдентификатором("filesystem.already_exists.file_path") + .КлассифицируетКак( + "filesystem.already_exists", + "ru = 'Файл уже существует'; en = 'File already exists'" + ) +); + +Классификатор = Новый КлассификаторОшибок; +Классификатор.ДобавитьПравила(Правила); + +ИсходнаяОшибка = Новый ИнформацияОбОшибке( + "External exception (System.IO.IOException): Файл 'report.json' уже существует.", + Новый Структура("Источник", "Пример") +); +НормализованнаяОшибка = Классификатор.Классифицировать(ИсходнаяОшибка); + +Сообщить("Код: " + ДиагностикаОшибок.КодОшибки(НормализованнаяОшибка)); diff --git "a/src/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\230\320\275\320\264\320\265\320\272\321\201\320\237\321\200\320\260\320\262\320\270\320\273\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\206\320\270\320\270\320\236\321\210\320\270\320\261\320\276\320\272.os" "b/src/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\230\320\275\320\264\320\265\320\272\321\201\320\237\321\200\320\260\320\262\320\270\320\273\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\206\320\270\320\270\320\236\321\210\320\270\320\261\320\276\320\272.os" new file mode 100644 index 0000000..839a42c --- /dev/null +++ "b/src/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\230\320\275\320\264\320\265\320\272\321\201\320\237\321\200\320\260\320\262\320\270\320\273\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\206\320\270\320\270\320\236\321\210\320\270\320\261\320\276\320\272.os" @@ -0,0 +1,406 @@ +Перем _ПравилаПоТипу; // ФиксированноеСоответствие из КлючИЗначение: + // * Ключ - Строка - Полное имя нативного типа исключения. + // * Значение - ФиксированныйМассив из ФиксированнаяСтруктура - Правила этого типа. +Перем _ПравилаПоСообщению; // ФиксированноеСоответствие из КлючИЗначение: + // * Ключ - Строка - Точное сообщение ошибки. + // * Значение - ФиксированныйМассив из ФиксированнаяСтруктура - Правила сообщения. +Перем _ПравилаПрефиксов; // ФиксированныйМассив из ФиксированнаяСтруктура +Перем _Источник; // Строка - Источник набора правил. + +#Область ОбработчикиСобытий + +// Создает неизменяемый индекс правил классификации. +// Локализованные шаблоны разбираются один раз при создании индекса. +// +// Параметры: +// Правила - Массив из ПравилоКлассификацииОшибок - Правила классификации. +// Источник - Строка - Источник набора правил. +// НачальныйНомерПравила - Число - Номер первого правила для автоматического идентификатора. +// +// Исключения: +// Если приоритет правила имеет тип, отличный от Число. +Процедура ПриСозданииОбъекта(Правила, Источник, НачальныйНомерПравила = 1) + + _Источник = Источник; + + ПоТипу = Новый Соответствие; + ПоСообщению = Новый Соответствие; + Префиксы = Новый Массив; + + НомерПравила = НачальныйНомерПравила; + Для Каждого Правило Из Правила Цикл + ПроверитьПравило(Правило); + ПодготовленноеПравило = ПодготовитьПравило(Правило, НомерПравила); + + Если Не ПустаяСтрока(Правило.ИсходноеСообщение()) + И ПустаяСтрока(Правило.НативныйТип()) Тогда + + Если ПодготовленноеПравило.ЕстьРусскийВариант Тогда + ДобавитьВИндекс( + ПоСообщению, + ПодготовленноеПравило.ИсходноеСообщение, + ПодготовленноеПравило + ); + КонецЕсли; + + Если ПодготовленноеПравило.ЕстьАнглийскийВариант + И (Не ПодготовленноеПравило.ЕстьРусскийВариант + Или ПодготовленноеПравило.ИсходноеСообщениеEN + <> ПодготовленноеПравило.ИсходноеСообщение) Тогда + ДобавитьВИндекс( + ПоСообщению, + ПодготовленноеПравило.ИсходноеСообщениеEN, + ПодготовленноеПравило + ); + КонецЕсли; + + ИначеЕсли Не ПустаяСтрока(Правило.НативныйТип()) Тогда + + ДобавитьВИндекс(ПоТипу, Правило.НативныйТип(), ПодготовленноеПравило); + + Иначе + Префиксы.Добавить(ПодготовленноеПравило); + КонецЕсли; + + НомерПравила = НомерПравила + 1; + КонецЦикла; + + _ПравилаПоТипу = ЗафиксироватьИндекс(ПоТипу); + _ПравилаПоСообщению = ЗафиксироватьИндекс(ПоСообщению); + _ПравилаПрефиксов = Новый ФиксированныйМассив(Префиксы); + +КонецПроцедуры + +#КонецОбласти + +#Область СлужебныйПрограммныйИнтерфейс + +// Добавляет совпавшие коды одного кадра в массив кандидатов. +// Одинаковые коды с одинаковым приоритетом объединяются с сохранением подтвержденного нативного типа. +// Индекс и данные кандидатов не изменяются между вызовами. +// +// Параметры: +// Кадр - ФиксированнаяСтруктура - Кадр ошибки: +// * Описание - Строка - Сообщение кадра. +// * НативныйТип - Строка, Неопределено - Полное имя нативного типа исключения. +// * ВнутреннееСообщение - Строка - Текст нативной ошибки без служебной обёртки типа. +// * РазрешенДляПоиска - Булево - Признак доступности кадра для сопоставления. +// Кандидаты - Массив из ФиксированнаяСтруктура - Пополняемые кандидаты: +// * Код - Строка - Код классифицированной ошибки. +// * Сообщение - Строка - Нормализованное описание ошибки. +// * Приоритет - Число - Приоритет совпавшего правила. +// * Данные - ФиксированнаяСтруктура - Диагностические данные: +// * Источник - Строка - Источник набора правил. +// * Правило - Строка - Код совпавшего правила. +// * НативныйТип - Строка - Проверенный правилом нативный тип, если использовался. +Процедура СобратьКандидаты(Кадр, Кандидаты) Экспорт + + Если Не Кадр.РазрешенДляПоиска Тогда + Возврат; + КонецЕсли; + + ДобавленныеКоды = Новый Соответствие; + ТекстСообщения = Кадр.ВнутреннееСообщение; + + // Типовые правила проверяются первыми, чтобы текстовый дубль не ослабил кандидат. + Если Не ПустаяСтрока(Кадр.НативныйТип) Тогда + ПравилаТипа = _ПравилаПоТипу.Получить(Кадр.НативныйТип); + Если ПравилаТипа <> Неопределено Тогда + СобратьСовпадения( + ПравилаТипа, + ТекстСообщения, + ТекстСообщения, + Кандидаты, + ДобавленныеКоды + ); + КонецЕсли; + КонецЕсли; + + ТочныеПравила = _ПравилаПоСообщению.Получить(ТекстСообщения); + Если ТочныеПравила <> Неопределено Тогда + Для Каждого Правило Из ТочныеПравила Цикл + ДобавитьКандидат(Правило.Кандидат, Кандидаты, ДобавленныеКоды); + КонецЦикла; + КонецЕсли; + + СобратьСовпадения( + _ПравилаПрефиксов, + ТекстСообщения, + ТекстСообщения, + Кандидаты, + ДобавленныеКоды + ); + +КонецПроцедуры + +#КонецОбласти + +#Область СлужебныеПроцедурыИФункции + +Процедура ДобавитьВИндекс(Индекс, Ключ, Правило) + + ПравилаКлюча = Индекс.Получить(Ключ); + Если ПравилаКлюча = Неопределено Тогда + ПравилаКлюча = Новый Массив; + Индекс.Вставить(Ключ, ПравилаКлюча); + КонецЕсли; + + ПравилаКлюча.Добавить(Правило); + +КонецПроцедуры + +Функция ЗафиксироватьИндекс(Индекс) + + Результат = Новый Соответствие; + Для Каждого Пара Из Индекс Цикл + Результат.Вставить(Пара.Ключ, Новый ФиксированныйМассив(Пара.Значение)); + КонецЦикла; + + Возврат Новый ФиксированноеСоответствие(Результат); + +КонецФункции + +Функция ПодготовитьПравило(Правило, НомерПравила) + + ИсходноеСообщение = ЛокализоватьШаблон(Правило.ИсходноеСообщение(), "ru"); + Префикс = ЛокализоватьШаблон(Правило.Префикс(), "ru"); + Фрагмент = ЛокализоватьШаблон(Правило.Фрагмент(), "ru"); + Окончание = ЛокализоватьШаблон(Правило.Окончание(), "ru"); + ИсходноеСообщениеEN = ЛокализоватьШаблон(Правило.ИсходноеСообщение(), "en"); + ПрефиксEN = ЛокализоватьШаблон(Правило.Префикс(), "en"); + ФрагментEN = ЛокализоватьШаблон(Правило.Фрагмент(), "en"); + ОкончаниеEN = ЛокализоватьШаблон(Правило.Окончание(), "en"); + ЕстьРусскийВариант = ДоступенВариант(Правило, "ru"); + ЕстьАнглийскийВариант = ДоступенВариант(Правило, "en"); + + Возврат Новый ФиксированнаяСтруктура( + Новый Структура( + "ИсходноеСообщение, Префикс, Фрагмент, Окончание, " + + "ИсходноеСообщениеEN, ПрефиксEN, " + + "ФрагментEN, ОкончаниеEN, " + + "ЕстьРусскийВариант, ЕстьАнглийскийВариант, Кандидат", + ИсходноеСообщение, + Префикс, + Фрагмент, + Окончание, + ИсходноеСообщениеEN, + ПрефиксEN, + ФрагментEN, + ОкончаниеEN, + ЕстьРусскийВариант, + ЕстьАнглийскийВариант, + ПодготовитьКандидат(Правило, НомерПравила) + ) + ); + +КонецФункции + +Функция ПодготовитьКандидат(Правило, НомерПравила) + + Данные = Новый Структура( + "Источник, Правило", + _Источник, + ИдентификаторПравила(Правило, НомерПравила) + ); + Если Не ПустаяСтрока(Правило.НативныйТип()) Тогда + Данные.Вставить("НативныйТип", Правило.НативныйТип()); + КонецЕсли; + + ОписаниеОшибки = Правило.ОписаниеРезультата(); + Если ЭтоЛокализованныйШаблон(ОписаниеОшибки) Тогда + ОписаниеОшибки = НСтр(ОписаниеОшибки); + КонецЕсли; + + Возврат Новый ФиксированнаяСтруктура( + Новый Структура( + "Код, Сообщение, Приоритет, Данные", + Правило.КодОшибки(), + ОписаниеОшибки, + Правило.Приоритет(), + Новый ФиксированнаяСтруктура(Данные) + ) + ); + +КонецФункции + +Функция ИдентификаторПравила(Правило, НомерПравила) + + Идентификатор = ""; + Попытка + Идентификатор = Правило.Идентификатор(); + Исключение + Идентификатор = ""; + КонецПопытки; + + Если Не ПустаяСтрока(Идентификатор) Тогда + Возврат Идентификатор; + КонецЕсли; + + Возврат СтрШаблон("%1.%2", _Источник, НомерПравила); + +КонецФункции + +Процедура ПроверитьПравило(Правило) + + ПроверитьНепустоеПоле(Правило.КодОшибки(), "КодОшибки"); + ПроверитьНепустоеПоле(Правило.ОписаниеРезультата(), "ОписаниеОшибки"); + + Если ТипЗнч(Правило.Приоритет()) <> Тип("Число") Тогда + ВызватьИсключение ФабрикаОшибок.Создать( + КодыСтандартныхОшибок.НеверныйТипАргумента(), + НСтр( + "ru = 'Поле правила Приоритет должно иметь тип Число'; " + + "en = 'Rule field Priority must have the Number type'" + ), + Новый Структура("ИмяПоля", "Приоритет") + ); + КонецЕсли; + + Если Не ПустаяСтрока(Правило.ИсходноеСообщение()) Тогда + Если Не ПустаяСтрока(Правило.Префикс()) + Или Не ПустаяСтрока(Правило.Фрагмент()) + Или Не ПустаяСтрока(Правило.Окончание()) Тогда + ВызватьИсключение ФабрикаОшибок.Создать( + КодыСтандартныхОшибок.НедопустимоеЗначениеАргумента(), + НСтр( + "ru = 'Точное сообщение нельзя сочетать с префиксом, фрагментом или окончанием'; " + + "en = 'Exact message cannot be combined with prefix, fragment, or suffix'" + ), + Новый Структура("ИмяПоля", "ИсходноеСообщение") + ); + КонецЕсли; + ИначеЕсли ПустаяСтрока(Правило.НативныйТип()) Тогда + ПроверитьНепустоеПоле(Правило.Префикс(), "Префикс"); + КонецЕсли; + +КонецПроцедуры + +Процедура ПроверитьНепустоеПоле(Значение, ИмяПоля) + + Если ПустаяСтрока(Значение) Тогда + ВызватьИсключение ФабрикаОшибок.Создать( + КодыСтандартныхОшибок.НедопустимоеЗначениеАргумента(), + СтрШаблон( + НСтр("ru = 'Не заполнено обязательное поле правила: %1'; " + + "en = 'Required rule field is not specified: %1'"), + ИмяПоля + ), + Новый Структура("ИмяПоля", ИмяПоля) + ); + КонецЕсли; + +КонецПроцедуры + +Процедура СобратьСовпадения(Правила, Описание, ТочноеСообщение, Кандидаты, ДобавленныеКоды) + + Для Каждого Правило Из Правила Цикл + Если Правило.ЕстьРусскийВариант + И Соответствует( + Описание, + ТочноеСообщение, + Правило.ИсходноеСообщение, + Правило.Префикс, + Правило.Фрагмент, + Правило.Окончание + ) + Или Правило.ЕстьАнглийскийВариант + И Соответствует( + Описание, + ТочноеСообщение, + Правило.ИсходноеСообщениеEN, + Правило.ПрефиксEN, + Правило.ФрагментEN, + Правило.ОкончаниеEN + ) Тогда + ДобавитьКандидат(Правило.Кандидат, Кандидаты, ДобавленныеКоды); + КонецЕсли; + КонецЦикла; + +КонецПроцедуры + +Процедура ДобавитьКандидат(Кандидат, Кандидаты, ДобавленныеКоды) + + КлючКандидата = Кандидат.Код + Символы.ПС + Строка(Кандидат.Приоритет); + Если ДобавленныеКоды.Получить(КлючКандидата) = Истина Тогда + Возврат; + КонецЕсли; + + Кандидаты.Добавить(Кандидат); + ДобавленныеКоды.Вставить(КлючКандидата, Истина); + +КонецПроцедуры + +Функция Соответствует(Описание, ТочноеСообщение, ИсходноеСообщение, Префикс, Фрагмент, Окончание) + + Если Не ПустаяСтрока(ИсходноеСообщение) Тогда + Возврат ТочноеСообщение = ИсходноеСообщение; + КонецЕсли; + + Если Не ПустаяСтрока(Префикс) + И Не СтрНачинаетсяС(Описание, Префикс) Тогда + Возврат Ложь; + КонецЕсли; + + Если Не ПустаяСтрока(Фрагмент) + И СтрНайти(Описание, Фрагмент) = 0 Тогда + Возврат Ложь; + КонецЕсли; + + Если Не ПустаяСтрока(Окончание) + И Не СтрЗаканчиваетсяНа(Описание, Окончание) Тогда + Возврат Ложь; + КонецЕсли; + + Возврат Истина; + +КонецФункции + +Функция ЛокализоватьШаблон(Шаблон, Язык) + + Если Не ЭтоЛокализованныйШаблон(Шаблон) Тогда + Возврат Шаблон; + КонецЕсли; + + Если Не СодержитЯзыковойВариант(Шаблон, Язык) Тогда + Возврат Неопределено; + КонецЕсли; + + Возврат НСтр(Шаблон, Язык); + +КонецФункции + +Функция ДоступенВариант(Правило, Язык) + + Возврат ДоступенШаблон(Правило.ИсходноеСообщение(), Язык) + И ДоступенШаблон(Правило.Префикс(), Язык) + И ДоступенШаблон(Правило.Фрагмент(), Язык) + И ДоступенШаблон(Правило.Окончание(), Язык); + +КонецФункции + +Функция ДоступенШаблон(Шаблон, Язык) + + Возврат Не ЭтоЛокализованныйШаблон(Шаблон) + Или СодержитЯзыковойВариант(Шаблон, Язык); + +КонецФункции + +Функция ЭтоЛокализованныйШаблон(Шаблон) + + Если СтрНайти(Шаблон, "=") = 0 Тогда + Возврат Ложь; + КонецЕсли; + + Возврат СодержитЯзыковойВариант(Шаблон, "ru") + Или СодержитЯзыковойВариант(Шаблон, "en"); + +КонецФункции + +Функция СодержитЯзыковойВариант(Шаблон, Язык) + + НормализованныйШаблон = СтрЗаменить(НРег(СокрЛП(Шаблон)), " ", ""); + Возврат СтрНайти(НормализованныйШаблон, Язык + "=") > 0; + +КонецФункции + +#КонецОбласти diff --git "a/src/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\246\320\265\320\277\320\276\321\207\320\272\320\260\320\232\320\260\320\264\321\200\320\276\320\262\320\236\321\210\320\270\320\261\320\272\320\270.os" "b/src/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\246\320\265\320\277\320\276\321\207\320\272\320\260\320\232\320\260\320\264\321\200\320\276\320\262\320\236\321\210\320\270\320\261\320\272\320\270.os" new file mode 100644 index 0000000..3d60dcf --- /dev/null +++ "b/src/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\246\320\265\320\277\320\276\321\207\320\272\320\260\320\232\320\260\320\264\321\200\320\276\320\262\320\236\321\210\320\270\320\261\320\272\320\270.os" @@ -0,0 +1,264 @@ +Перем _Кадры; // Массив из ФиксированнаяСтруктура + +#Область ОбработчикиСобытий + +// Инициализирует цепочку кадров ошибки. +// +// Параметры: +// Снимок - СнимокКадраОшибки - Снимок исходной ошибки. +Процедура ПриСозданииОбъекта(Снимок) + + _Кадры = СобратьКадры(Снимок); + +КонецПроцедуры + +#КонецОбласти + +#Область СлужебныйПрограммныйИнтерфейс + +// Возвращает независимую копию кадров для внутренних правил классификации. +// +// Возвращаемое значение: +// Массив из ФиксированнаяСтруктура - Независимая копия кадров в порядке обхода с полями: +// * Глубина - Число - Позиция физической ошибки в цепочке, начиная с нуля. +// * ИсходнаяОшибка - ИнформацияОбОшибке - Физическая ошибка, из которой получен кадр. +// * Описание - Строка - Текст кадра. +// * НативныйТип - Строка, Неопределено - Имя .NET-типа из текста внешнего исключения или Неопределено. +// * ВнутреннееСообщение - Строка - Текст нативной ошибки без служебной обёртки типа. +// * КодОшибки - Строка - Структурированный код ошибки или пустая строка. +// * Данные - Произвольный, Неопределено - Данные структурированной ошибки. +// * Восстановлен - Булево - Признак кадра из ПодробноеПредставлениеОшибки(). +// * РазрешенДляПоиска - Булево - Признак доступности текста кадра для поиска. +Функция Кадры() Экспорт + + Результат = Новый Массив; + Для Каждого Кадр Из _Кадры Цикл + Результат.Добавить(Кадр); + КонецЦикла; + + Возврат Результат; + +КонецФункции + +#КонецОбласти + +#Область СлужебныеПроцедурыИФункции + +Функция РазобратьНативнуюОшибку(Описание) + + ПрефиксРусскогоИсключения = "Внешнее исключение ("; + ПрефиксАнглийскогоИсключения = "External exception ("; + + Если СтрНачинаетсяС(Описание, ПрефиксРусскогоИсключения) Тогда + Префикс = ПрефиксРусскогоИсключения; + ИначеЕсли СтрНачинаетсяС(Описание, ПрефиксАнглийскогоИсключения) Тогда + Префикс = ПрефиксАнглийскогоИсключения; + Иначе + Возврат Неопределено; + КонецЕсли; + + ПозицияОкончанияТипа = СтрНайти(Описание, "):"); + Если ПозицияОкончанияТипа = 0 Тогда + Возврат Неопределено; + КонецЕсли; + + ДлинаТипа = ПозицияОкончанияТипа - СтрДлина(Префикс) - 1; + Если ДлинаТипа <= 0 Тогда + Возврат Неопределено; + КонецЕсли; + + Возврат Новый ФиксированнаяСтруктура(Новый Структура( + "НативныйТип, ВнутреннееСообщение", + Сред(Описание, СтрДлина(Префикс) + 1, ДлинаТипа), + СокрЛ(Сред(Описание, ПозицияОкончанияТипа + 2)) + )); + +КонецФункции + +Функция ВосстановитьОписанияИзПодробногоПредставления(Описание) + + Если Не ЭтоПодробноеПредставлениеОшибки(Описание) Тогда + Возврат Неопределено; + КонецЕсли; + + ПозицияКонцаЗаголовка = НайтиПозициюКонцаЗаголовка(Описание); + Если ПозицияКонцаЗаголовка = 0 Тогда + Возврат Неопределено; + КонецЕсли; + + Заголовок = Лев(Описание, ПозицияКонцаЗаголовка); + ОписаниеКадра = ИзвлечьОписаниеИзЗаголовка(Заголовок); + Если ОписаниеКадра = Неопределено Тогда + Возврат Неопределено; + КонецЕсли; + + Результат = Новый Массив; + Результат.Добавить(ОписаниеКадра); + ОжидаетсяПричина = Ложь; + Остаток = Сред(Описание, ПозицияКонцаЗаголовка + 1); + Строки = СтрРазделить(Остаток, Символы.ПС, Ложь); + + Для Индекс = 0 По Строки.ВГраница() Цикл + Строка = СокрЛП(Строки[Индекс]); + + Если ПустаяСтрока(Строка) Тогда + Продолжить; + КонецЕсли; + + Если ЭтоМаркерПричиныПодробногоПредставления(Строка) Тогда + Если ОжидаетсяПричина Тогда + Возврат Неопределено; + КонецЕсли; + + ОжидаетсяПричина = Истина; + Продолжить; + КонецЕсли; + + Если ОжидаетсяПричина Тогда + ОписаниеКадра = ИзвлечьОписаниеИзЗаголовка(Строка); + Если ОписаниеКадра = Неопределено Тогда + ОписаниеКадра = Строка; + КонецЕсли; + + Результат.Добавить(ОписаниеКадра); + ОжидаетсяПричина = Ложь; + КонецЕсли; + КонецЦикла; + + Возврат ?(ОжидаетсяПричина, Неопределено, Результат); + +КонецФункции + +Функция НайтиПозициюКонцаЗаголовка(Описание) + + ПозицияСВозвратомКаретки = СтрНайти(Описание, "}" + Символы.ВК + Символы.ПС); + ПозицияСПереводомСтроки = СтрНайти(Описание, "}" + Символы.ПС); + + Если ПозицияСВозвратомКаретки > 0 Тогда + Возврат ПозицияСВозвратомКаретки; + КонецЕсли; + + Если ПозицияСПереводомСтроки > 0 Тогда + Возврат ПозицияСПереводомСтроки; + КонецЕсли; + + Возврат ?(СтрЗаканчиваетсяНа(Описание, "}"), СтрДлина(Описание), 0); + +КонецФункции + +Функция ЭтоМаркерПричиныПодробногоПредставления(Строка) + + НормализованнаяСтрока = НРег(СокрЛП(Строка)); + Возврат НормализованнаяСтрока = "по причине:" + Или НормализованнаяСтрока = "caused by:"; + +КонецФункции + +Функция ЭтоПодробноеПредставлениеОшибки(Описание) + + Возврат СтрНачинаетсяС(Описание, "{Модуль ") + Или СтрНачинаетсяС(Описание, "{Module "); + +КонецФункции + +Функция ИзвлечьОписаниеИзЗаголовка(Строка) + + Если СтрЗаканчиваетсяНа(Строка, Символы.ВК) Тогда + Строка = Лев(Строка, СтрДлина(Строка) - 1); + КонецЕсли; + + Если Не ЭтоПодробноеПредставлениеОшибки(Строка) + Или Не СтрЗаканчиваетсяНа(Строка, "}") Тогда + Возврат Неопределено; + КонецЕсли; + + ВторойРазделитель = СтрНайти(Строка, " / ", НаправлениеПоиска.СНачала,, 2); + Если ВторойРазделитель = 0 Тогда + Возврат Неопределено; + КонецЕсли; + + ПозицияОписания = ВторойРазделитель + СтрДлина(" / "); + Возврат Сред(Строка, ПозицияОписания, СтрДлина(Строка) - ПозицияОписания); + +КонецФункции + +Функция СобратьКадры(КорневойСнимок) + + Кадры = Новый Массив; + ТекущаяОшибка = КорневойСнимок.ИсходнаяОшибка(); + Снимок = КорневойСнимок; + Глубина = 0; + + Пока ЭтоКадрОшибки(ТекущаяОшибка) Цикл + Если Снимок = Неопределено Тогда + Снимок = Новый СнимокКадраОшибки(ТекущаяОшибка); + КонецЕсли; + + Описание = Снимок.Сообщение(); + ВосстановленныеОписания = ВосстановитьОписанияИзПодробногоПредставления(Описание); + + Кадры.Добавить(НовыйКадр( + Глубина, + ТекущаяОшибка, + Описание, + Снимок.КодОшибки(), + Снимок.Данные(), + Ложь + )); + + Если ВосстановленныеОписания <> Неопределено Тогда + Для ИндексОписания = 0 По ВосстановленныеОписания.ВГраница() Цикл + Кадры.Добавить(НовыйКадр( + Глубина, + ТекущаяОшибка, + ВосстановленныеОписания[ИндексОписания], + "", + Неопределено, + Истина + )); + КонецЦикла; + КонецЕсли; + + ТекущаяОшибка = ТекущаяОшибка.Причина; + Снимок = Неопределено; + Глубина = Глубина + 1; + КонецЦикла; + + Возврат Кадры; + +КонецФункции + +Функция НовыйКадр(Глубина, ИсходнаяОшибка, Описание, КодОшибки, Данные, Восстановлен) + + НативнаяОшибка = РазобратьНативнуюОшибку(Описание); + НативныйТип = Неопределено; + ВнутреннееСообщение = Описание; + + Если НативнаяОшибка <> Неопределено Тогда + НативныйТип = НативнаяОшибка.НативныйТип; + ВнутреннееСообщение = НативнаяОшибка.ВнутреннееСообщение; + КонецЕсли; + + Поля = Новый Структура( + "Глубина, ИсходнаяОшибка, Описание, НативныйТип, ВнутреннееСообщение, " + + "КодОшибки, Данные, Восстановлен, РазрешенДляПоиска", + Глубина, + ИсходнаяОшибка, + Описание, + НативныйТип, + ВнутреннееСообщение, + КодОшибки, + Данные, + Восстановлен, + Восстановлен Или Не ЭтоПодробноеПредставлениеОшибки(Описание) + ); + + Возврат Новый ФиксированнаяСтруктура(Поля); + +КонецФункции + +Функция ЭтоКадрОшибки(Значение) + Возврат ТипЗнч(Значение) = Тип("ИнформацияОбОшибке"); +КонецФункции + +#КонецОбласти diff --git "a/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\202\320\276\321\200\320\236\321\210\320\270\320\261\320\276\320\272.os" "b/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\202\320\276\321\200\320\236\321\210\320\270\320\261\320\276\320\272.os" new file mode 100644 index 0000000..d894451 --- /dev/null +++ "b/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\202\320\276\321\200\320\236\321\210\320\270\320\261\320\276\320\272.os" @@ -0,0 +1,515 @@ +#Использовать "../internal" + +Перем _РегистрацииПравил; // Массив из Структура +Перем _ИндексыПравил; // Массив из ИндексПравилКлассификацииОшибок +Перем _СледующийНомерПравила; // Число +Перем _ТребуетсяПодготовкаПоставщиков; // Булево +Перем _БлокировкаПодготовкиПоставщиков; // БлокировкаРесурса +Перем _КодыПравил; // Соответствие из КлючИЗначение: + // * Ключ - Строка - Код из добавленных правил. + // * Значение - Булево - Всегда Истина. +Перем _СообщенияКодов; // Соответствие из КлючИЗначение: + // * Ключ - Строка - Код из добавленных правил. + // * Значение - Строка - Сообщение ошибки. + +#Область ОбработчикиСобытий + +// Создаёт пустой классификатор ошибок. +// До добавления правил или поставщика экземпляр не классифицирует ошибки и не считает собственным ни один код. +Процедура ПриСозданииОбъекта() + + _РегистрацииПравил = Новый Массив; + _ИндексыПравил = Новый Массив; + _СледующийНомерПравила = 1; + _ТребуетсяПодготовкаПоставщиков = Ложь; + _БлокировкаПодготовкиПоставщиков = Новый БлокировкаРесурса(); + _КодыПравил = Новый Соответствие; + _СообщенияКодов = Новый Соответствие; + +КонецПроцедуры + +#КонецОбласти + +#Область ПрограммныйИнтерфейс + +// Добавляет правила классификации. +// Все добавленные правила участвуют в общем выборе по силе совпадения и приоритету. +// Правила применяются после структурированных кодов. +// +// Параметры: +// Правила - Массив, ФиксированныйМассив из ПравилоКлассификацииОшибок - Правила классификации. +// +// Возвращаемое значение: +// КлассификаторОшибок - Этот классификатор. +Функция ДобавитьПравила(Правила) Экспорт + + ИндексПравил = Новый ИндексПравилКлассификацииОшибок( + Правила, + "ПравилаКлассификации", + _СледующийНомерПравила + ); + СведенияПравил = СнимокСведенийПравил(Правила); + _РегистрацииПравил.Добавить(НоваяРегистрацияПравил(ИндексПравил, СведенияПравил)); + _ИндексыПравил.Добавить(ИндексПравил); + + Для Каждого СведенияПравила Из СведенияПравил Цикл + ЗарегистрироватьКодПравила(СведенияПравила, _КодыПравил, _СообщенияКодов); + _СледующийНомерПравила = _СледующийНомерПравила + 1; + КонецЦикла; + + Возврат ЭтотОбъект; + +КонецФункции + +// Добавляет поставщика правил для ленивой загрузки. +// Поставщик может быть типом с конструктором без параметров или готовым объектом с методом Все(). +// Поставщик загружается перед обработкой собственных кодов и правил. +// Результат успешной загрузки кешируется, регистрации применяются в порядке добавления. +// +// Параметры: +// Поставщик - Тип, Произвольный - Тип или объект поставщика правил. +// +// Возвращаемое значение: +// КлассификаторОшибок - Этот классификатор. +Функция ДобавитьПоставщикаПравил(Поставщик) Экспорт + + _РегистрацииПравил.Добавить(НоваяРегистрацияПоставщика(Поставщик)); + _ТребуетсяПодготовкаПоставщиков = Истина; + + Возврат ЭтотОбъект; + +КонецФункции + +// Классифицирует ошибку по структурированным кодам и правилам. +// +// Параметры: +// Ошибка - ИнформацияОбОшибке - Исходная или уже структурированная ошибка. +// +// Возвращаемое значение: +// ИнформацияОбОшибке, Неопределено - Нормализованная ошибка либо Неопределено, +// если ошибка не распознана. +// +// Исключения: +// Если конструктор поставщика, его метод Все() или подготовка возвращенных правил завершились ошибкой. +Функция Классифицировать(Ошибка) Экспорт + + КорневойСнимок = Новый СнимокКадраОшибки(Ошибка); + Возврат КлассифицироватьНеНормализованнуюОшибку(Ошибка, КорневойСнимок); + +КонецФункции + +#КонецОбласти + +#Область СлужебныеПроцедурыИФункции + +Функция КлассифицироватьНеНормализованнуюОшибку(Ошибка, КорневойСнимок) + + ЦепочкаКадров = Новый ЦепочкаКадровОшибки(КорневойСнимок); + Кадры = ЦепочкаКадров.Кадры(); + + ПодготовитьПоставщиковПравил(); + + Если ЭтоКодПравила(КорневойСнимок.КодОшибки()) Тогда + Возврат Ошибка; + КонецЕсли; + + Результат = Неопределено; + Если ПопробоватьКлассифицироватьСтруктурированнымиКодами(Ошибка, Кадры, Результат) Тогда + Возврат Результат; + КонецЕсли; + + Возврат КлассифицироватьПравилами(Ошибка, Кадры); + +КонецФункции + +Функция ЭтоКодПравила(Код) + + Возврат Не ПустаяСтрока(Код) И _КодыПравил.Получить(Код) = Истина; + +КонецФункции + +Процедура ЗарегистрироватьКодПравила(Правило, КодыПравил, СообщенияКодов) + + КодыПравил.Вставить(Правило.КодОшибки, Истина); + + Если СообщенияКодов.Получить(Правило.КодОшибки) = Неопределено + И Не ПустаяСтрока(Правило.ОписаниеОшибки) Тогда + СообщенияКодов.Вставить( + Правило.КодОшибки, + ЛокализоватьОписаниеОшибкиПравила(Правило.ОписаниеОшибки) + ); + КонецЕсли; + +КонецПроцедуры + +Процедура ПодготовитьПоставщиковПравил() + + Если Не _ТребуетсяПодготовкаПоставщиков Тогда + Возврат; + КонецЕсли; + + _БлокировкаПодготовкиПоставщиков.Заблокировать(); + + Попытка + Если _ТребуетсяПодготовкаПоставщиков Тогда + ПересобратьПравилаИзРегистраций(); + КонецЕсли; + Исключение + _БлокировкаПодготовкиПоставщиков.Разблокировать(); + ВызватьИсключение; + КонецПопытки; + + _БлокировкаПодготовкиПоставщиков.Разблокировать(); + +КонецПроцедуры + +Процедура ПересобратьПравилаИзРегистраций() + + НовыеИндексыПравил = Новый Массив; + НовыеКодыПравил = Новый Соответствие; + НовыеСообщенияКодов = Новый Соответствие; + + Для Каждого Регистрация Из _РегистрацииПравил Цикл + Если Регистрация.Индекс = Неопределено Тогда + ПодготовитьРегистрациюПоставщика(Регистрация, _СледующийНомерПравила); + _СледующийНомерПравила = _СледующийНомерПравила + Регистрация.СведенияПравил.Количество(); + КонецЕсли; + + НовыеИндексыПравил.Добавить(Регистрация.Индекс); + Для Каждого СведенияПравила Из Регистрация.СведенияПравил Цикл + ЗарегистрироватьКодПравила(СведенияПравила, НовыеКодыПравил, НовыеСообщенияКодов); + КонецЦикла; + КонецЦикла; + + _ИндексыПравил = НовыеИндексыПравил; + _КодыПравил = НовыеКодыПравил; + _СообщенияКодов = НовыеСообщенияКодов; + _ТребуетсяПодготовкаПоставщиков = Ложь; + +КонецПроцедуры + +Процедура ПодготовитьРегистрациюПоставщика(Регистрация, СледующийНомерПравила) + + Поставщик = Регистрация.Поставщик; + Если ТипЗнч(Поставщик) = Тип("Тип") Тогда + Поставщик = Новый(Поставщик); + КонецЕсли; + + Правила = Поставщик.Все(); + ИндексПравил = Новый ИндексПравилКлассификацииОшибок( + Правила, + "ПравилаКлассификации", + СледующийНомерПравила + ); + СведенияПравил = СнимокСведенийПравил(Правила); + + Регистрация.Индекс = ИндексПравил; + Регистрация.СведенияПравил = СведенияПравил; + +КонецПроцедуры + +Функция НоваяРегистрацияПравил(ИндексПравил, СведенияПравил) + + Возврат Новый Структура( + "Поставщик, Индекс, СведенияПравил", + Неопределено, + ИндексПравил, + СведенияПравил + ); + +КонецФункции + +Функция НоваяРегистрацияПоставщика(Поставщик) + + Возврат Новый Структура( + "Поставщик, Индекс, СведенияПравил", + Поставщик, + Неопределено, + Неопределено + ); + +КонецФункции + +Функция СнимокСведенийПравил(Правила) + + СведенияПравил = Новый Массив; + Для Каждого Правило Из Правила Цикл + СведенияПравил.Добавить(Новый ФиксированнаяСтруктура( + Новый Структура( + "КодОшибки, ОписаниеОшибки", + Правило.КодОшибки(), + Правило.ОписаниеРезультата() + ) + )); + КонецЦикла; + + Возврат Новый ФиксированныйМассив(СведенияПравил); + +КонецФункции + +Функция ПопробоватьКлассифицироватьСтруктурированнымиКодами(Ошибка, Кадры, Результат) + + Кандидаты = СобратьСтруктурированныеКандидаты(Кадры); + Если Кандидаты.Количество() = 0 Тогда + Возврат Ложь; + КонецЕсли; + + Кандидат = ВыбратьКандидата(Кандидаты); + Если Кандидат <> Неопределено Тогда + Если ДиагностикаОшибок.КодОшибки(Ошибка) = Кандидат.Код Тогда + Результат = Ошибка; + Иначе + Результат = СоздатьНормализованнуюОшибку( + Ошибка, + Кандидат, + ПолучитьСообщениеСтруктурированногоКода(Кандидат.Код) + ); + КонецЕсли; + КонецЕсли; + + Возврат Истина; + +КонецФункции + +Функция КлассифицироватьПравилами(Ошибка, Кадры) + + Кандидат = ВыбратьКандидата(СобратьКандидатыПравил(Кадры)); + Если Кандидат = Неопределено Тогда + Возврат Неопределено; + КонецЕсли; + + Возврат СоздатьНормализованнуюОшибку(Ошибка, Кандидат); + +КонецФункции + +Функция СобратьСтруктурированныеКандидаты(Кадры) + + Кандидаты = Новый Массив; + + Для Каждого Кадр Из Кадры Цикл + Если ПустаяСтрока(Кадр.КодОшибки) Тогда + Продолжить; + КонецЕсли; + + Если Не ЭтоКодПравила(Кадр.КодОшибки) Тогда + Продолжить; + КонецЕсли; + + Источник = ЗначениеСвойства(Кадр.Данные, "Источник", "СтруктурированнаяОшибка"); + Правило = ЗначениеСвойства(Кадр.Данные, "Правило", Кадр.КодОшибки); + + Кандидаты.Добавить(НовыйКандидат( + Кадр.КодОшибки, + Кадр, + 300, + 0, + Правило, + Источник, + Кадр.Описание + )); + КонецЦикла; + + Возврат Кандидаты; + +КонецФункции + +Функция ПолучитьСообщениеСтруктурированногоКода(КодОшибки) + + Сообщение = _СообщенияКодов.Получить(КодОшибки); + Если Сообщение <> Неопределено Тогда + Возврат Сообщение; + КонецЕсли; + + ВызватьИсключение ФабрикаОшибок.Создать( + КодыСтандартныхОшибок.НарушенВнутреннийИнвариант(), + СтрШаблон( + НСтр("ru = 'Для кода %1 не найдено сообщение правила'; " + + "en = 'No rule message was found for code %1'"), + КодОшибки + ), + Новый Структура("Инвариант, КодОшибки", "Сообщение правила найдено", КодОшибки) + ); + +КонецФункции + +Функция ЛокализоватьОписаниеОшибкиПравила(ОписаниеОшибки) + + НормализованноеОписаниеОшибки = СтрЗаменить(НРег(СокрЛП(ОписаниеОшибки)), " ", ""); + Если СтрНайти(НормализованноеОписаниеОшибки, "ru=") > 0 + Или СтрНайти(НормализованноеОписаниеОшибки, "en=") > 0 Тогда + Возврат НСтр(ОписаниеОшибки); + КонецЕсли; + + Возврат ОписаниеОшибки; + +КонецФункции + +Функция СобратьКандидатыПравил(Кадры) + + Кандидаты = Новый Массив; + + Для Каждого Кадр Из Кадры Цикл + Если ЭтоЧужойСтруктурированныйКод(Кадр) Тогда + Продолжить; + КонецЕсли; + + ПервыйИндексКандидата = Кандидаты.Количество(); + Для Каждого ИндексПравил Из _ИндексыПравил Цикл + ИндексПравил.СобратьКандидаты(Кадр, Кандидаты); + КонецЦикла; + + ОформитьКандидатыПравил(Кандидаты, ПервыйИндексКандидата, Кадр, 100); + КонецЦикла; + + Возврат Кандидаты; + +КонецФункции + +Функция ЭтоЧужойСтруктурированныйКод(Кадр) + + Возврат Не ПустаяСтрока(Кадр.КодОшибки) + И Не ЭтоКодПравила(Кадр.КодОшибки); + +КонецФункции + +Процедура ОформитьКандидатыПравил(Кандидаты, ПервыйИндексКандидата, Кадр, СилаТекста) + + Для ИндексКандидата = ПервыйИндексКандидата По Кандидаты.ВГраница() Цикл + Результат = Кандидаты[ИндексКандидата]; + Код = Результат.Код; + Данные = Результат.Данные; + Источник = ЗначениеСвойства(Данные, "Источник", "ПравилаКлассификации"); + Правило = ЗначениеСвойства(Данные, "Правило", Код); + Сила = СилаТекста; + ИспользованныйНативныйТип = ЗначениеСвойства(Данные, "НативныйТип", Неопределено); + + Если Не ПустаяСтрока(ИспользованныйНативныйТип) И Кадр.Восстановлен Тогда + Сила = СилаТекста + 90; + ИначеЕсли Не ПустаяСтрока(ИспользованныйНативныйТип) Тогда + Сила = СилаТекста + 100; + КонецЕсли; + + Кандидаты[ИндексКандидата] = НовыйКандидат( + Код, + Кадр, + Сила, + Результат.Приоритет, + Правило, + Источник, + Результат.Сообщение + ); + КонецЦикла; + +КонецПроцедуры + +Функция НовыйКандидат( + Код, + Кадр, + Сила, + Приоритет, + Правило, + Источник, + Сообщение) + + Поля = Новый Структура( + "Код, Кадр, Сила, Приоритет, Правило, Источник, Сообщение", + Код, + Кадр, + Сила, + Приоритет, + Правило, + Источник, + Сообщение + ); + + Возврат Новый ФиксированнаяСтруктура(Поля); + +КонецФункции + +Функция ВыбратьКандидата(Кандидаты) + + Если Кандидаты.Количество() = 0 Тогда + Возврат Неопределено; + КонецЕсли; + + МаксимальнаяСила = Кандидаты[0].Сила; + Для Каждого Кандидат Из Кандидаты Цикл + Если Кандидат.Сила > МаксимальнаяСила Тогда + МаксимальнаяСила = Кандидат.Сила; + КонецЕсли; + КонецЦикла; + + МаксимальныйПриоритет = Неопределено; + Для Каждого Кандидат Из Кандидаты Цикл + Если Кандидат.Сила = МаксимальнаяСила + И (МаксимальныйПриоритет = Неопределено + Или Кандидат.Приоритет > МаксимальныйПриоритет) Тогда + МаксимальныйПриоритет = Кандидат.Приоритет; + КонецЕсли; + КонецЦикла; + + ЛучшиеПоКодам = Новый Соответствие; + Для Каждого Кандидат Из Кандидаты Цикл + Если Кандидат.Сила = МаксимальнаяСила + И Кандидат.Приоритет = МаксимальныйПриоритет + И ЛучшиеПоКодам.Получить(Кандидат.Код) = Неопределено Тогда + ЛучшиеПоКодам.Вставить(Кандидат.Код, Кандидат); + КонецЕсли; + КонецЦикла; + + Если ЛучшиеПоКодам.Количество() = 1 Тогда + Для Каждого Пара Из ЛучшиеПоКодам Цикл + Возврат Пара.Значение; + КонецЦикла; + КонецЕсли; + + Возврат Неопределено; + +КонецФункции + +Функция СоздатьНормализованнуюОшибку(Ошибка, Кандидат, Сообщение = Неопределено) + + Данные = Новый Структура; + Данные.Вставить("Источник", Кандидат.Источник); + Данные.Вставить("Правило", Кандидат.Правило); + + НативныйТип = ЗначениеСвойства( + Кандидат.Кадр.Данные, + "НативныйТип", + Кандидат.Кадр.НативныйТип + ); + + Если Не ПустаяСтрока(НативныйТип) Тогда + Данные.Вставить("НативныйТип", НативныйТип); + КонецЕсли; + + Если Сообщение = Неопределено Тогда + Сообщение = Кандидат.Сообщение; + КонецЕсли; + + Возврат ФабрикаОшибок.Обернуть( + Ошибка, + Кандидат.Код, + Сообщение, + Данные + ); + +КонецФункции + +Функция ЗначениеСвойства(Данные, Имя, ЗначениеПоУмолчанию) + + Если ТипЗнч(Данные) <> Тип("Структура") + И ТипЗнч(Данные) <> Тип("ФиксированнаяСтруктура") Тогда + Возврат ЗначениеПоУмолчанию; + КонецЕсли; + + Значение = Неопределено; + Если Данные.Свойство(Имя, Значение) Тогда + Возврат Значение; + КонецЕсли; + + Возврат ЗначениеПоУмолчанию; + +КонецФункции + +#КонецОбласти diff --git "a/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\237\321\200\320\260\320\262\320\270\320\273\320\276\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\206\320\270\320\270\320\236\321\210\320\270\320\261\320\276\320\272.os" "b/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\237\321\200\320\260\320\262\320\270\320\273\320\276\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\206\320\270\320\270\320\236\321\210\320\270\320\261\320\276\320\272.os" new file mode 100644 index 0000000..00bb151 --- /dev/null +++ "b/src/\320\232\320\273\320\260\321\201\321\201\321\213/\320\237\321\200\320\260\320\262\320\270\320\273\320\276\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\206\320\270\320\270\320\236\321\210\320\270\320\261\320\276\320\272.os" @@ -0,0 +1,240 @@ +Перем _НативныйТип; // Строка +Перем _ИсходноеСообщение; // Строка +Перем _Префикс; // Строка +Перем _Фрагмент; // Строка +Перем _Окончание; // Строка +Перем _КодОшибки; // Строка +Перем _ОписаниеОшибки; // Строка +Перем _Идентификатор; // Строка +Перем _Приоритет; // Число + +#Область ОбработчикиСобытий + +// Создает пустое правило классификации ошибки. +Процедура ПриСозданииОбъекта() + + _НативныйТип = ""; + _ИсходноеСообщение = ""; + _Префикс = ""; + _Фрагмент = ""; + _Окончание = ""; + _КодОшибки = ""; + _ОписаниеОшибки = ""; + _Идентификатор = ""; + _Приоритет = 0; + +КонецПроцедуры + +#КонецОбласти + +#Область ПрограммныйИнтерфейс + +// Устанавливает полное имя нативного типа .NET. +// При наличии нативного типа все текстовые условия сопоставляются с внутренним +// сообщением без служебной обёртки типа. +// +// Параметры: +// НативныйТип - Строка - Полное имя типа .NET. +// +// Возвращаемое значение: +// ПравилоКлассификацииОшибок - Это правило. +Функция СНативнымТипом(НативныйТип) Экспорт + + _НативныйТип = НативныйТип; + Возврат ЭтотОбъект; + +КонецФункции + +// Устанавливает точное сообщение ошибки. +// Для нативной ошибки сопоставляется внутреннее сообщение, а СНативнымТипом() +// дополнительно ограничивает тип. +// +// Параметры: +// ИсходноеСообщение - Строка - Полное сообщение или шаблон НСтр. +// +// Возвращаемое значение: +// ПравилоКлассификацииОшибок - Это правило. +Функция СовпадаетС(ИсходноеСообщение) Экспорт + + _ИсходноеСообщение = ИсходноеСообщение; + Возврат ЭтотОбъект; + +КонецФункции + +// Устанавливает префикс исходного сообщения. +// +// Параметры: +// Префикс - Строка - Начало сообщения или шаблон НСтр. +// +// Возвращаемое значение: +// ПравилоКлассификацииОшибок - Это правило. +Функция НачинаетсяС(Префикс) Экспорт + + _Префикс = Префикс; + Возврат ЭтотОбъект; + +КонецФункции + +// Устанавливает обязательный фрагмент исходного сообщения. +// +// Параметры: +// Фрагмент - Строка - Фрагмент сообщения или шаблон НСтр. +// +// Возвращаемое значение: +// ПравилоКлассификацииОшибок - Это правило. +Функция Содержит(Фрагмент) Экспорт + + _Фрагмент = Фрагмент; + Возврат ЭтотОбъект; + +КонецФункции + +// Устанавливает окончание исходного сообщения. +// +// Параметры: +// Окончание - Строка - Окончание сообщения или шаблон НСтр. +// +// Возвращаемое значение: +// ПравилоКлассификацииОшибок - Это правило. +Функция ОканчиваетсяНа(Окончание) Экспорт + + _Окончание = Окончание; + Возврат ЭтотОбъект; + +КонецФункции + +// Устанавливает приоритет правила. +// +// Параметры: +// Приоритет - Число - Приоритет среди равносильных совпадений. +// +// Возвращаемое значение: +// ПравилоКлассификацииОшибок - Это правило. +Функция СПриоритетом(Приоритет) Экспорт + + _Приоритет = Приоритет; + Возврат ЭтотОбъект; + +КонецФункции + +// Устанавливает стабильный идентификатор правила. +// +// Параметры: +// Идентификатор - Строка - Идентификатор для диагностических данных. +// +// Возвращаемое значение: +// ПравилоКлассификацииОшибок - Это правило. +Функция СИдентификатором(Идентификатор) Экспорт + + _Идентификатор = Идентификатор; + Возврат ЭтотОбъект; + +КонецФункции + +// Устанавливает результат классификации. +// +// Параметры: +// КодОшибки - Строка - Код классифицированной ошибки. +// ОписаниеОшибки - Строка - Нормализованное описание ошибки или шаблон НСтр. +// +// Возвращаемое значение: +// ПравилоКлассификацииОшибок - Это правило. +Функция КлассифицируетКак(КодОшибки, ОписаниеОшибки) Экспорт + + _КодОшибки = КодОшибки; + _ОписаниеОшибки = ОписаниеОшибки; + Возврат ЭтотОбъект; + +КонецФункции + +// Возвращает полное имя нативного типа .NET. +// +// Возвращаемое значение: +// Строка - Полное имя типа или пустая строка. +Функция НативныйТип() Экспорт + + Возврат _НативныйТип; + +КонецФункции + +// Возвращает точное исходное сообщение. +// +// Возвращаемое значение: +// Строка - Сообщение или шаблон НСтр. +Функция ИсходноеСообщение() Экспорт + + Возврат _ИсходноеСообщение; + +КонецФункции + +// Возвращает префикс исходного сообщения. +// +// Возвращаемое значение: +// Строка - Префикс или шаблон НСтр. +Функция Префикс() Экспорт + + Возврат _Префикс; + +КонецФункции + +// Возвращает обязательный фрагмент исходного сообщения. +// +// Возвращаемое значение: +// Строка - Фрагмент или шаблон НСтр. +Функция Фрагмент() Экспорт + + Возврат _Фрагмент; + +КонецФункции + +// Возвращает окончание исходного сообщения. +// +// Возвращаемое значение: +// Строка - Окончание или шаблон НСтр. +Функция Окончание() Экспорт + + Возврат _Окончание; + +КонецФункции + +// Возвращает код классифицированной ошибки. +// +// Возвращаемое значение: +// Строка - Код ошибки. +Функция КодОшибки() Экспорт + + Возврат _КодОшибки; + +КонецФункции + +// Возвращает нормализованное описание результата классификации. +// +// Возвращаемое значение: +// Строка - Описание или шаблон НСтр. +Функция ОписаниеРезультата() Экспорт + + Возврат _ОписаниеОшибки; + +КонецФункции + +// Возвращает идентификатор правила. +// +// Возвращаемое значение: +// Строка - Идентификатор или пустая строка. +Функция Идентификатор() Экспорт + + Возврат _Идентификатор; + +КонецФункции + +// Возвращает приоритет правила. +// +// Возвращаемое значение: +// Число - Приоритет среди равносильных совпадений. +Функция Приоритет() Экспорт + + Возврат _Приоритет; + +КонецФункции + +#КонецОбласти diff --git "a/tests/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\242\320\265\321\201\321\202\320\276\320\262\321\213\320\271\320\237\320\276\321\201\321\202\320\260\320\262\321\211\320\270\320\272\320\237\321\200\320\260\320\262\320\270\320\273.os" "b/tests/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\242\320\265\321\201\321\202\320\276\320\262\321\213\320\271\320\237\320\276\321\201\321\202\320\260\320\262\321\211\320\270\320\272\320\237\321\200\320\260\320\262\320\270\320\273.os" new file mode 100644 index 0000000..36532bb --- /dev/null +++ "b/tests/internal/\320\232\320\273\320\260\321\201\321\201\321\213/\320\242\320\265\321\201\321\202\320\276\320\262\321\213\320\271\320\237\320\276\321\201\321\202\320\260\320\262\321\211\320\270\320\272\320\237\321\200\320\260\320\262\320\270\320\273.os" @@ -0,0 +1,134 @@ +#Использовать "../../../src" + +Перем _Правила; // Массив, ФиксированныйМассив из ПравилоКлассификацииОшибок +Перем _КоличествоВызововВсе; // Число +Перем _КоличествоОшибокВсе; // Число +Перем _Задержка; // Число +Перем _Блокировка; // БлокировкаРесурса + +#Область ОбработчикиСобытий + +// Создает тестового поставщика правил с наблюдаемыми вызовами. +// +// Параметры: +// Правила - Массив, ФиксированныйМассив из ПравилоКлассификацииОшибок, Неопределено - Возвращаемые правила. +Процедура ПриСозданииОбъекта(Правила = Неопределено) + + _Блокировка = Новый БлокировкаРесурса(); + _КоличествоВызововВсе = 0; + _КоличествоОшибокВсе = 0; + _Задержка = 0; + _Правила = ?(Правила = Неопределено, ПравилаПоУмолчанию(), Правила); + + КоличествоСозданий = ПолучитьПеременнуюСреды("OSCRIPT_ERRORS_TEST_PROVIDER_CONSTRUCTORS"); + Если ПустаяСтрока(КоличествоСозданий) Тогда + КоличествоСозданий = 0; + КонецЕсли; + + УстановитьПеременнуюСреды( + "OSCRIPT_ERRORS_TEST_PROVIDER_CONSTRUCTORS", + Строка(Число(КоличествоСозданий) + 1) + ); + +КонецПроцедуры + +#КонецОбласти + +#Область ПрограммныйИнтерфейс + +// Возвращает правила и увеличивает счетчик вызовов. +// +// Возвращаемое значение: +// Массив, ФиксированныйМассив из ПравилоКлассификацииОшибок - Правила поставщика. +// +// Исключения: +// Если для очередного вызова настроена тестовая ошибка. +Функция Все() Экспорт + + _Блокировка.Заблокировать(); + _КоличествоВызововВсе = _КоличествоВызововВсе + 1; + + КоличествоОбщихВызовов = ПолучитьПеременнуюСреды("OSCRIPT_ERRORS_TEST_PROVIDER_CALLS"); + Если ПустаяСтрока(КоличествоОбщихВызовов) Тогда + КоличествоОбщихВызовов = 0; + КонецЕсли; + + УстановитьПеременнуюСреды( + "OSCRIPT_ERRORS_TEST_PROVIDER_CALLS", + Строка(Число(КоличествоОбщихВызовов) + 1) + ); + + ЗавершитьСОшибкой = _КоличествоОшибокВсе > 0; + Если ЗавершитьСОшибкой Тогда + _КоличествоОшибокВсе = _КоличествоОшибокВсе - 1; + КонецЕсли; + + _Блокировка.Разблокировать(); + + Если _Задержка > 0 Тогда + Приостановить(_Задержка); + КонецЕсли; + + Если ЗавершитьСОшибкой Тогда + ВызватьИсключение "Тестовая ошибка поставщика правил"; + КонецЕсли; + + Возврат _Правила; + +КонецФункции + +// Возвращает количество вызовов Все(). +// +// Возвращаемое значение: +// Число - Количество вызовов. +Функция КоличествоВызововВсе() Экспорт + + _Блокировка.Заблокировать(); + Результат = _КоличествоВызововВсе; + _Блокировка.Разблокировать(); + + Возврат Результат; + +КонецФункции + +// Настраивает ошибки следующих вызовов Все(). +// +// Параметры: +// Количество - Число - Количество вызовов, которые должны завершиться ошибкой. +Процедура ЗавершитьСледующиеВызовыОшибкой(Количество = 1) Экспорт + + _КоличествоОшибокВсе = Количество; + +КонецПроцедуры + +// Настраивает задержку вызова Все(). +// +// Параметры: +// Миллисекунды - Число - Длительность задержки. +Процедура УстановитьЗадержку(Миллисекунды) Экспорт + + _Задержка = Миллисекунды; + +КонецПроцедуры + +#КонецОбласти + +#Область СлужебныеПроцедурыИФункции + +Функция ПравилаПоУмолчанию() + + Правила = Новый Массив; + Правила.Добавить( + Новый ПравилоКлассификацииОшибок() + .СовпадаетС("Сообщение поставщика") + .КлассифицируетКак( + "test.provider", + "Ошибка поставщика" + ) + ); + + Возврат Правила; + +КонецФункции + +#КонецОбласти diff --git "a/tests/\320\242\320\265\321\201\321\202\321\213\320\230\320\275\320\264\320\265\320\272\321\201\320\260\320\237\321\200\320\260\320\262\320\270\320\273\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\206\320\270\320\270\320\236\321\210\320\270\320\261\320\276\320\272.os" "b/tests/\320\242\320\265\321\201\321\202\321\213\320\230\320\275\320\264\320\265\320\272\321\201\320\260\320\237\321\200\320\260\320\262\320\270\320\273\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\206\320\270\320\270\320\236\321\210\320\270\320\261\320\276\320\272.os" new file mode 100644 index 0000000..7df2d0f --- /dev/null +++ "b/tests/\320\242\320\265\321\201\321\202\321\213\320\230\320\275\320\264\320\265\320\272\321\201\320\260\320\237\321\200\320\260\320\262\320\270\320\273\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\206\320\270\320\270\320\236\321\210\320\270\320\261\320\276\320\272.os" @@ -0,0 +1,340 @@ +#Использовать asserts +#Использовать "../src" +#Использовать "../src/internal" + +#Область Тесты + +&Тест +Процедура ТестДолжен_СопоставитьТочноеЛокализованноеСообщение() Экспорт + + // Подготовка + Правило = Новый ПравилоКлассификацииОшибок() + .СовпадаетС("ru = 'Русское сообщение'; en = 'English message'") + .КлассифицируетКак( + "test.exact", + "Тестовая ошибка" + ); + Индекс = ИндексСПравилом(Правило); + Кандидаты = Новый Массив; + + // Действие + Индекс.СобратьКандидаты(Кадр("Русское сообщение"), Кандидаты); + Индекс.СобратьКандидаты(Кадр("English message"), Кандидаты); + + // Проверка + Ожидаем.Что(Кандидаты).ИмеетДлину(2); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_СопоставитьТочноеСообщениеНативнойОшибкиБезТипа() Экспорт + + // Подготовка + Правило = Новый ПравилоКлассификацииОшибок() + .СовпадаетС("Файл уже существует") + .КлассифицируетКак("test.native_exact", "Тестовая ошибка"); + Индекс = ИндексСПравилом(Правило); + Кандидаты = Новый Массив; + КадрОшибки = Кадр( + "External exception (System.IO.IOException): Файл уже существует", + "System.IO.IOException", + Истина, + "Файл уже существует" + ); + + // Действие + Индекс.СобратьКандидаты(КадрОшибки, Кандидаты); + + // Проверка + Ожидаем.Что(Кандидаты).ИмеетДлину(1); + Ожидаем.Что(Кандидаты[0].Код).Равно("test.native_exact"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_СопоставитьТочноеСообщениеНативнойОшибкиСТипом() Экспорт + + // Подготовка + Правило = Новый ПравилоКлассификацииОшибок() + .СНативнымТипом("System.IO.IOException") + .СовпадаетС("Файл уже существует") + .КлассифицируетКак("test.native_exact", "Тестовая ошибка"); + Индекс = ИндексСПравилом(Правило); + Кандидаты = Новый Массив; + КадрОшибки = Кадр( + "External exception (System.IO.IOException): Файл уже существует", + "System.IO.IOException", + Истина, + "Файл уже существует" + ); + КадрОшибкиДругогоТипа = Кадр( + "External exception (System.ArgumentException): Файл уже существует", + "System.ArgumentException", + Истина, + "Файл уже существует" + ); + + // Действие + Индекс.СобратьКандидаты(КадрОшибки, Кандидаты); + Индекс.СобратьКандидаты(КадрОшибкиДругогоТипа, Кандидаты); + + // Проверка + Ожидаем.Что(Кандидаты).ИмеетДлину(1); + Ожидаем.Что(Кандидаты[0].Код).Равно("test.native_exact"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_СопоставитьПрефиксФрагментИОкончание() Экспорт + + // Подготовка + Правило = Новый ПравилоКлассификацииОшибок() + .НачинаетсяС("Начало") + .Содержит("фрагмент") + .ОканчиваетсяНа("конец") + .КлассифицируетКак( + "test.text", + "Тестовая ошибка" + ); + Индекс = ИндексСПравилом(Правило); + Кандидаты = Новый Массив; + + // Действие + Индекс.СобратьКандидаты(Кадр("Начало фрагмент продолжение"), Кандидаты); + Индекс.СобратьКандидаты(Кадр("Постороннее фрагмент конец"), Кандидаты); + Индекс.СобратьКандидаты(Кадр("Начало без условия конец"), Кандидаты); + Индекс.СобратьКандидаты(Кадр("Начало фрагмент конец"), Кандидаты); + + // Проверка + Ожидаем.Что(Кандидаты).ИмеетДлину(1); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_СопоставитьТипПрефиксИОкончаниеОдногоКадра() Экспорт + + // Подготовка + Правило = Новый ПравилоКлассификацииОшибок() + .СНативнымТипом("System.IO.IOException") + .НачинаетсяС("Начало") + .ОканчиваетсяНа("конец") + .КлассифицируетКак( + "test.type", + "Тестовая ошибка" + ); + Индекс = ИндексСПравилом(Правило); + Кандидаты = Новый Массив; + + // Действие + Индекс.СобратьКандидаты(Кадр("Начало текста конец", "System.ArgumentException"), Кандидаты); + Индекс.СобратьКандидаты(Кадр("Другое текста конец", "System.IO.IOException"), Кандидаты); + Индекс.СобратьКандидаты(Кадр("Начало текста конец", "System.IO.IOException"), Кандидаты); + + // Проверка + Ожидаем.Что(Кандидаты).ИмеетДлину(1); + Ожидаем.Что(Кандидаты[0].Данные.НативныйТип).Равно("System.IO.IOException"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_СопоставитьУсловияТиповогоПравилаПоВнутреннемуСообщению() Экспорт + + // Подготовка + Правило = Новый ПравилоКлассификацииОшибок() + .СНативнымТипом("System.IO.IOException") + .НачинаетсяС("Начало") + .Содержит("фрагмент") + .ОканчиваетсяНа("конец") + .КлассифицируетКак( + "test.type_inner_message", + "Тестовая ошибка" + ); + Индекс = ИндексСПравилом(Правило); + Кандидаты = Новый Массив; + КадрОшибки = Кадр( + "External exception (System.IO.IOException): Начало фрагмент конец", + "System.IO.IOException", + Истина, + "Начало фрагмент конец" + ); + + // Действие + Индекс.СобратьКандидаты(КадрОшибки, Кандидаты); + + // Проверка + Ожидаем.Что(Кандидаты).ИмеетДлину(1); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_СопоставитьПравилоТолькоПоНативномуТипу() Экспорт + + // Подготовка + Правило = Новый ПравилоКлассификацииОшибок() + .СНативнымТипом("System.IO.IOException") + .КлассифицируетКак( + "test.type", + "Тестовая ошибка" + ); + Индекс = ИндексСПравилом(Правило); + Кандидаты = Новый Массив; + + // Действие + Индекс.СобратьКандидаты(Кадр("Любое сообщение", "System.IO.IOException"), Кандидаты); + + // Проверка + Ожидаем.Что(Кандидаты).ИмеетДлину(1); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_НеСопоставитьКадрЗапрещенныйДляПоиска() Экспорт + + // Подготовка + Правило = Новый ПравилоКлассификацииОшибок() + .СовпадаетС("Тестовое сообщение") + .КлассифицируетКак( + "test.exact", + "Тестовая ошибка" + ); + Индекс = ИндексСПравилом(Правило); + Кандидаты = Новый Массив; + + // Действие + Индекс.СобратьКандидаты(Кадр("Тестовое сообщение", Неопределено, Ложь), Кандидаты); + + // Проверка + Ожидаем.Что(Кандидаты).ИмеетДлину(0); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_СохранитьИдентификаторПравилаВКандидате() Экспорт + + // Подготовка + Правило = Новый ПравилоКлассификацииОшибок() + .СовпадаетС("Тестовое сообщение") + .СИдентификатором("test.exact.v1") + .КлассифицируетКак( + "test.exact", + "Тестовая ошибка" + ); + Индекс = ИндексСПравилом(Правило); + Кандидаты = Новый Массив; + + // Действие + Индекс.СобратьКандидаты(Кадр("Тестовое сообщение"), Кандидаты); + + // Проверка + Ожидаем.Что(Кандидаты[0].Данные.Правило).Равно("test.exact.v1"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ОтклонитьТочноеСообщениеСДругимУсловием() Экспорт + + // Подготовка + Правило = Новый ПравилоКлассификацииОшибок() + .СовпадаетС("Точное сообщение") + .Содержит("сообщение") + .КлассифицируетКак( + "test.invalid", + "Тестовая ошибка" + ); + + // Действие и Проверка + ПроверитьНекорректноеПравило( + Правило, + КодыСтандартныхОшибок.НедопустимоеЗначениеАргумента() + ); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ОтклонитьТекстовоеПравилоБезПрефикса() Экспорт + + // Подготовка + Правило = Новый ПравилоКлассификацииОшибок() + .Содержит("Фрагмент") + .КлассифицируетКак( + "test.invalid", + "Тестовая ошибка" + ); + + // Действие и Проверка + ПроверитьНекорректноеПравило( + Правило, + КодыСтандартныхОшибок.НедопустимоеЗначениеАргумента() + ); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ОтклонитьНечисловойПриоритет() Экспорт + + // Подготовка + Правило = Новый ПравилоКлассификацииОшибок() + .СовпадаетС("Тестовое сообщение") + .СПриоритетом("Высокий") + .КлассифицируетКак( + "test.invalid", + "Тестовая ошибка" + ); + + // Действие и Проверка + ПроверитьНекорректноеПравило( + Правило, + КодыСтандартныхОшибок.НеверныйТипАргумента() + ); + +КонецПроцедуры + +#КонецОбласти + +#Область СлужебныеПроцедурыИФункции + +Функция ИндексСПравилом(Правило) + + Правила = Новый Массив; + Правила.Добавить(Правило); + Возврат Новый ИндексПравилКлассификацииОшибок(Правила, "ТестовыеПравила"); + +КонецФункции + +Функция Кадр( + Описание, + НативныйТип = Неопределено, + РазрешенДляПоиска = Истина, + ВнутреннееСообщение = Неопределено +) + + Если ВнутреннееСообщение = Неопределено Тогда + ВнутреннееСообщение = Описание; + КонецЕсли; + + Возврат Новый ФиксированнаяСтруктура( + Новый Структура( + "Описание, НативныйТип, РазрешенДляПоиска, ВнутреннееСообщение", + Описание, + НативныйТип, + РазрешенДляПоиска, + ВнутреннееСообщение + ) + ); + +КонецФункции + +Процедура ПроверитьНекорректноеПравило(Правило, ОжидаемыйКодОшибки) + + Ошибка = Неопределено; + Попытка + Индекс = ИндексСПравилом(Правило); + Исключение + Ошибка = ИнформацияОбОшибке(); + КонецПопытки; + + Ожидаем.Что(ДиагностикаОшибок.КодОшибки(Ошибка)).Равно(ОжидаемыйКодОшибки); + +КонецПроцедуры + +#КонецОбласти diff --git "a/tests/\320\242\320\265\321\201\321\202\321\213\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\202\320\276\321\200\320\260\320\236\321\210\320\270\320\261\320\276\320\272.os" "b/tests/\320\242\320\265\321\201\321\202\321\213\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\202\320\276\321\200\320\260\320\236\321\210\320\270\320\261\320\276\320\272.os" new file mode 100644 index 0000000..b2e7bb1 --- /dev/null +++ "b/tests/\320\242\320\265\321\201\321\202\321\213\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\202\320\276\321\200\320\260\320\236\321\210\320\270\320\261\320\276\320\272.os" @@ -0,0 +1,899 @@ +#Использовать asserts +#Использовать "../src" +#Использовать "../src/internal" +#Использовать "./internal" + +Перем _БлокировкаСтарта; // БлокировкаРесурса +Перем _КоличествоГотовыхЗаданий; // Число +Перем _НачатьКлассификацию; // Булево + +#Область ОбработчикиСобытий + +// Инициализирует состояние тестового набора. +&ПередКаждым +Процедура ИнициализироватьТесты() Экспорт + + _БлокировкаСтарта = Новый БлокировкаРесурса(); + +КонецПроцедуры + +#КонецОбласти + +#Область Тесты + +&Тест +Процедура ТестДолжен_НеСоздаватьПоставщикаПоТипуПриРегистрации() Экспорт + + // Подготовка + УстановитьПеременнуюСреды("OSCRIPT_ERRORS_TEST_PROVIDER_CONSTRUCTORS", "0"); + Классификатор = Новый КлассификаторОшибок; + + // Действие + Классификатор.ДобавитьПоставщикаПравил(Тип("ТестовыйПоставщикПравил")); + + // Проверка + Ожидаем.Что(ПолучитьПеременнуюСреды("OSCRIPT_ERRORS_TEST_PROVIDER_CONSTRUCTORS")).Равно("0"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_НеВызыватьПоставщикаОбъектаПриРегистрации() Экспорт + + // Подготовка + Поставщик = Новый ТестовыйПоставщикПравил; + Классификатор = Новый КлассификаторОшибок; + + // Действие + Классификатор.ДобавитьПоставщикаПравил(Поставщик); + + // Проверка + Ожидаем.Что(Поставщик.КоличествоВызововВсе()).Равно(0); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ЗагрузитьПоставщикаПриПервойКлассификацииСобственнымиПравилами() Экспорт + + // Подготовка + Поставщик = Новый ТестовыйПоставщикПравил; + Классификатор = Новый КлассификаторОшибок; + Классификатор.ДобавитьПоставщикаПравил(Поставщик); + + // Действие + Результат = Классификатор.Классифицировать(ОшибкаССообщением("Сообщение поставщика")); + + // Проверка + Ожидаем.Что(ДиагностикаОшибок.КодОшибки(Результат)).Равно("test.provider"); + Ожидаем.Что(Поставщик.КоличествоВызововВсе()).Равно(1); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ПовторноИспользоватьПодготовленныйИндексПоставщика() Экспорт + + // Подготовка + Поставщик = Новый ТестовыйПоставщикПравил; + + Классификатор = Новый КлассификаторОшибок; + Классификатор.ДобавитьПоставщикаПравил(Поставщик); + Классификатор.Классифицировать(ОшибкаССообщением("Сообщение поставщика")); + + // Действие + Результат = Классификатор.Классифицировать(ОшибкаССообщением("Сообщение поставщика")); + + // Проверка + Ожидаем.Что(ДиагностикаОшибок.КодОшибки(Результат)).Равно("test.provider"); + Ожидаем.Что(Поставщик.КоличествоВызововВсе()).Равно(1); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ОдинРазЗагрузитьПоставщикаПоТипу() Экспорт + + // Подготовка + УстановитьПеременнуюСреды("OSCRIPT_ERRORS_TEST_PROVIDER_CONSTRUCTORS", "0"); + УстановитьПеременнуюСреды("OSCRIPT_ERRORS_TEST_PROVIDER_CALLS", "0"); + + Классификатор = Новый КлассификаторОшибок; + Классификатор.ДобавитьПоставщикаПравил(Тип("ТестовыйПоставщикПравил")); + + // Действие + ПервыйРезультат = Классификатор.Классифицировать(ОшибкаССообщением("Сообщение поставщика")); + ВторойРезультат = Классификатор.Классифицировать(ОшибкаССообщением("Сообщение поставщика")); + + // Проверка + Ожидаем.Что(ДиагностикаОшибок.КодОшибки(ПервыйРезультат)).Равно("test.provider"); + Ожидаем.Что(ДиагностикаОшибок.КодОшибки(ВторойРезультат)).Равно("test.provider"); + Ожидаем.Что(ПолучитьПеременнуюСреды("OSCRIPT_ERRORS_TEST_PROVIDER_CONSTRUCTORS")).Равно("1"); + Ожидаем.Что(ПолучитьПеременнуюСреды("OSCRIPT_ERRORS_TEST_PROVIDER_CALLS")).Равно("1"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_СохранитьПорядокПравилПередПоставщиком() Экспорт + + // Подготовка + Поставщик = Новый ТестовыйПоставщикПравил( + ПравилаДляСообщенияСОписанием("Общее сообщение", "test.same", "Второе описание") + ); + + Классификатор = Новый КлассификаторОшибок; + Классификатор.ДобавитьПравила( + ПравилаДляСообщенияСОписанием("Общее сообщение", "test.same", "Первое описание") + ); + Классификатор.ДобавитьПоставщикаПравил(Поставщик); + + // Действие + Результат = Классификатор.Классифицировать(ОшибкаССообщением("Общее сообщение")); + + // Проверка + Ожидаем.Что(Новый СнимокКадраОшибки(Результат).Сообщение()).Равно("Первое описание"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_СохранитьПорядокПоставщикаПередПравилами() Экспорт + + // Подготовка + Поставщик = Новый ТестовыйПоставщикПравил( + ПравилаДляСообщенияСОписанием("Общее сообщение", "test.same", "Первое описание") + ); + + Классификатор = Новый КлассификаторОшибок; + Классификатор.ДобавитьПоставщикаПравил(Поставщик); + Классификатор.ДобавитьПравила( + ПравилаДляСообщенияСОписанием("Общее сообщение", "test.same", "Второе описание") + ); + + // Действие + Результат = Классификатор.Классифицировать(ОшибкаССообщением("Общее сообщение")); + + // Проверка + Ожидаем.Что(Новый СнимокКадраОшибки(Результат).Сообщение()).Равно("Первое описание"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_НазначитьРазныеИдентификаторыСмешаннымПравилам() Экспорт + + // Подготовка + Поставщик = Новый ТестовыйПоставщикПравил( + ПравилаДляСообщения("Сообщение поставщика", "test.provider") + ); + + Классификатор = Новый КлассификаторОшибок; + Классификатор.ДобавитьПоставщикаПравил(Поставщик); + Классификатор.ДобавитьПравила(ПравилаДляСообщения("Готовое сообщение", "test.ready")); + + // Действие + РезультатПоставщика = Классификатор.Классифицировать(ОшибкаССообщением("Сообщение поставщика")); + РезультатГотовыхПравил = Классификатор.Классифицировать(ОшибкаССообщением("Готовое сообщение")); + ИдентификаторПоставщика = Новый СнимокКадраОшибки(РезультатПоставщика).Данные().Правило; + ИдентификаторГотовыхПравил = Новый СнимокКадраОшибки(РезультатГотовыхПравил).Данные().Правило; + + // Проверка + Ожидаем.Что(ИдентификаторПоставщика <> ИдентификаторГотовыхПравил).ЭтоИстина(); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ВыбратьПервоеСообщениеКодаИзСмешанныхРегистраций() Экспорт + + // Подготовка + Поставщик = Новый ТестовыйПоставщикПравил( + ПравилаДляСообщенияСОписанием("Второй шаблон", "test.same", "Второе описание") + ); + + Классификатор = Новый КлассификаторОшибок; + Классификатор.ДобавитьПравила( + ПравилаДляСообщенияСОписанием("Первый шаблон", "test.same", "Первое описание") + ); + Классификатор.ДобавитьПоставщикаПравил(Поставщик); + + Причина = ФабрикаОшибок.Создать("test.same", "Исходное сообщение"); + Ошибка = ФабрикаОшибок.Создать("foreign.code", "Внешняя ошибка", Неопределено, Причина); + + // Действие + Результат = Классификатор.Классифицировать(Ошибка); + + // Проверка + Ожидаем.Что(Новый СнимокКадраОшибки(Результат).Сообщение()).Равно("Первое описание"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ВыбратьСообщениеКодаПоставщикаПередГотовымиПравилами() Экспорт + + // Подготовка + Поставщик = Новый ТестовыйПоставщикПравил( + ПравилаДляСообщенияСОписанием("Первый шаблон", "test.same", "Первое описание") + ); + + Классификатор = Новый КлассификаторОшибок; + Классификатор.ДобавитьПоставщикаПравил(Поставщик); + Классификатор.ДобавитьПравила( + ПравилаДляСообщенияСОписанием("Второй шаблон", "test.same", "Второе описание") + ); + + Причина = ФабрикаОшибок.Создать("test.same", "Исходное сообщение"); + Ошибка = ФабрикаОшибок.Создать("foreign.code", "Внешняя ошибка", Неопределено, Причина); + + // Действие + Результат = Классификатор.Классифицировать(Ошибка); + + // Проверка + Ожидаем.Что(Новый СнимокКадраОшибки(Результат).Сообщение()).Равно("Первое описание"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_РаспознатьКорневойКодЛенивогоПоставщика() Экспорт + + // Подготовка + Поставщик = Новый ТестовыйПоставщикПравил; + Классификатор = Новый КлассификаторОшибок; + Классификатор.ДобавитьПоставщикаПравил(Поставщик); + Ошибка = ФабрикаОшибок.Создать("test.provider", "Исходное сообщение"); + + // Действие + Результат = Классификатор.Классифицировать(Ошибка); + + // Проверка + Ожидаем.Что(Результат).Идентично(Ошибка); + Ожидаем.Что(Поставщик.КоличествоВызововВсе()).Равно(1); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_РаспознатьВложенныйКодЛенивогоПоставщика() Экспорт + + // Подготовка + Поставщик = Новый ТестовыйПоставщикПравил; + Классификатор = Новый КлассификаторОшибок; + Классификатор.ДобавитьПоставщикаПравил(Поставщик); + Причина = ФабрикаОшибок.Создать("test.provider", "Исходное сообщение"); + Ошибка = ФабрикаОшибок.Создать("foreign.code", "Внешняя ошибка", Неопределено, Причина); + + // Действие + Результат = Классификатор.Классифицировать(Ошибка); + + // Проверка + Ожидаем.Что(ДиагностикаОшибок.КодОшибки(Результат)).Равно("test.provider"); + Ожидаем.Что(Новый СнимокКадраОшибки(Результат).Сообщение()).Равно("Ошибка поставщика"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ПовторитьЗагрузкуПоставщикаПослеОшибки() Экспорт + + // Подготовка + Поставщик = Новый ТестовыйПоставщикПравил; + Поставщик.ЗавершитьСледующиеВызовыОшибкой(); + Классификатор = Новый КлассификаторОшибок; + Классификатор.ДобавитьПоставщикаПравил(Поставщик); + ОшибкаВозникла = Ложь; + + // Действие + Попытка + Классификатор.Классифицировать(ОшибкаССообщением("Сообщение поставщика")); + Исключение + ОшибкаВозникла = Истина; + КонецПопытки; + + Результат = Классификатор.Классифицировать(ОшибкаССообщением("Сообщение поставщика")); + + // Проверка + Ожидаем.Что(ОшибкаВозникла).ЭтоИстина(); + Ожидаем.Что(ДиагностикаОшибок.КодОшибки(Результат)).Равно("test.provider"); + Ожидаем.Что(Поставщик.КоличествоВызововВсе()).Равно(2); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_СохранитьПодготовленногоПоставщикаПослеОшибкиСледующего() Экспорт + + // Подготовка + ПервыйПоставщик = Новый ТестовыйПоставщикПравил; + ВторойПоставщик = Новый ТестовыйПоставщикПравил(Новый Массив); + ВторойПоставщик.ЗавершитьСледующиеВызовыОшибкой(); + Классификатор = Новый КлассификаторОшибок; + Классификатор.ДобавитьПоставщикаПравил(ПервыйПоставщик); + Классификатор.ДобавитьПоставщикаПравил(ВторойПоставщик); + + // Действие + Попытка + Классификатор.Классифицировать(ОшибкаССообщением("Сообщение поставщика")); + Исключение + КонецПопытки; + + Результат = Классификатор.Классифицировать(ОшибкаССообщением("Сообщение поставщика")); + + // Проверка + Ожидаем.Что(ДиагностикаОшибок.КодОшибки(Результат)).Равно("test.provider"); + Ожидаем.Что(ПервыйПоставщик.КоличествоВызововВсе()).Равно(1); + Ожидаем.Что(ВторойПоставщик.КоличествоВызововВсе()).Равно(2); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ПодготовитьПоздноДобавленногоПоставщика() Экспорт + + // Подготовка + Классификатор = КлассификаторСОсновнымиПравилами(); + Классификатор.Классифицировать(ОшибкаССообщением("Основное сообщение")); + Поставщик = Новый ТестовыйПоставщикПравил; + Классификатор.ДобавитьПоставщикаПравил(Поставщик); + + // Действие + Результат = Классификатор.Классифицировать(ОшибкаССообщением("Сообщение поставщика")); + + // Проверка + Ожидаем.Что(ДиагностикаОшибок.КодОшибки(Результат)).Равно("test.provider"); + Ожидаем.Что(Поставщик.КоличествоВызововВсе()).Равно(1); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ОдинРазПодготовитьПустогоПоставщика() Экспорт + + // Подготовка + Поставщик = Новый ТестовыйПоставщикПравил(Новый Массив); + Классификатор = Новый КлассификаторОшибок; + Классификатор.ДобавитьПоставщикаПравил(Поставщик); + + // Действие + ПервыйРезультат = Классификатор.Классифицировать(ОшибкаССообщением("Неизвестное сообщение")); + ВторойРезультат = Классификатор.Классифицировать(ОшибкаССообщением("Неизвестное сообщение")); + + // Проверка + Ожидаем.Что(ПервыйРезультат).Идентично(Неопределено); + Ожидаем.Что(ВторойРезультат).Идентично(Неопределено); + Ожидаем.Что(Поставщик.КоличествоВызововВсе()).Равно(1); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ПринятьФиксированныйМассивПоставщика() Экспорт + + // Подготовка + Правила = Новый ФиксированныйМассив( + ПравилаДляСообщения("Фиксированное сообщение", "test.fixed_provider") + ); + Поставщик = Новый ТестовыйПоставщикПравил(Правила); + Классификатор = Новый КлассификаторОшибок; + Классификатор.ДобавитьПоставщикаПравил(Поставщик); + + // Действие + Результат = Классификатор.Классифицировать(ОшибкаССообщением("Фиксированное сообщение")); + + // Проверка + Ожидаем.Что(ДиагностикаОшибок.КодОшибки(Результат)).Равно("test.fixed_provider"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ОдинРазЗагрузитьПоставщикаПриПараллельномОбращении() Экспорт + + // Подготовка + _КоличествоГотовыхЗаданий = 0; + _НачатьКлассификацию = Ложь; + + Поставщик = Новый ТестовыйПоставщикПравил; + Поставщик.УстановитьЗадержку(50); + + Классификатор = Новый КлассификаторОшибок; + Классификатор.ДобавитьПоставщикаПравил(Поставщик); + + Параметры = Новый Массив; + Параметры.Добавить(Классификатор); + + Задания = Новый Массив; + Для НомерЗадания = 1 По 4 Цикл + Задания.Добавить(ФоновыеЗадания.Выполнить(ЭтотОбъект, "КлассифицироватьВФоне", Параметры)); + КонецЦикла; + + Для НомерОжидания = 1 По 5000 Цикл + Если КоличествоГотовыхЗаданий() = Задания.Количество() Тогда + Прервать; + КонецЕсли; + Приостановить(1); + КонецЦикла; + + // Действие и Проверка + КоличествоГотовыхЗаданий = КоличествоГотовыхЗаданий(); + РазрешитьПараллельнуюКлассификацию(); + Ожидаем.Что(КоличествоГотовыхЗаданий).Равно(Задания.Количество()); + + ФоновыеЗадания.ОжидатьВсе(Задания); + + Для Каждого Задание Из Задания Цикл + Ожидаем.Что(ДиагностикаОшибок.КодОшибки(Задание.Результат)).Равно("test.provider"); + КонецЦикла; + + Ожидаем.Что(Поставщик.КоличествоВызововВсе()).Равно(1); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ВернутьНеопределеноБезПравил() Экспорт + + // Подготовка + Классификатор = Новый КлассификаторОшибок; + + // Действие + Результат = Классификатор.Классифицировать(ОшибкаССообщением("Неизвестное сообщение")); + + // Проверка + Ожидаем.Что(Результат).Идентично(Неопределено); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_КлассифицироватьОшибкуСФиксированнымиПравилами() Экспорт + + // Подготовка + Классификатор = КлассификаторСПравилами(Новый ФиксированныйМассив(ОсновныеПравила())); + + // Действие + Результат = Классификатор.Классифицировать(ОшибкаССообщением("Основное сообщение")); + + // Проверка + Ожидаем.Что(ДиагностикаОшибок.КодОшибки(Результат)).Равно("test.main"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ВернутьНеопределеноДляНеизвестнойОшибки() Экспорт + + // Подготовка + Классификатор = КлассификаторСОсновнымиПравилами(); + Ошибка = ОшибкаССообщением("Неизвестное сообщение"); + + // Действие + Результат = Классификатор.Классифицировать(Ошибка); + + // Проверка + Ожидаем.Что(Результат).Идентично(Неопределено); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ВернутьКорневуюОшибкуСИзвестнымКодом() Экспорт + + // Подготовка + Классификатор = КлассификаторСОсновнымиПравилами(); + Ошибка = ФабрикаОшибок.Создать("test.main", "Исходное сообщение"); + + // Действие + Результат = Классификатор.Классифицировать(Ошибка); + + // Проверка + Ожидаем.Что(Результат).Идентично(Ошибка); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ВернутьКорневуюОшибкуСИзвестнымКодомПриДругомКодеПричины() Экспорт + + // Подготовка + Причина = ФабрикаОшибок.Создать("test.secondary", "Вложенная ошибка"); + Ошибка = ФабрикаОшибок.Создать("test.main", "Исходное сообщение", Неопределено, Причина); + Классификатор = КлассификаторСОсновнымиПравилами(); + + // Действие + Результат = Классификатор.Классифицировать(Ошибка); + + // Проверка + Ожидаем.Что(Результат).Идентично(Ошибка); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ПредпочестьВложенныйСтруктурированныйКодТекстовомуПравилу() Экспорт + + // Подготовка + Причина = ФабрикаОшибок.Создать("test.secondary", "Вложенная ошибка"); + Ошибка = Новый ИнформацияОбОшибке("Основное сообщение", Новый Структура, Причина); + Классификатор = КлассификаторСОсновнымиПравилами(); + + // Действие + Результат = Классификатор.Классифицировать(Ошибка); + + // Проверка + Ожидаем.Что(ДиагностикаОшибок.КодОшибки(Результат)).Равно("test.secondary"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_НеКлассифицироватьТекстФизическогоКадраСЧужимКодом() Экспорт + + // Подготовка + Ошибка = ФабрикаОшибок.Создать("foreign.code", "Основное сообщение"); + Классификатор = КлассификаторСОсновнымиПравилами(); + + // Действие + Результат = Классификатор.Классифицировать(Ошибка); + + // Проверка + Ожидаем.Что(Результат).Идентично(Неопределено); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_КлассифицироватьВосстановленныйКадрПослеЧужогоКода() Экспорт + + // Подготовка + Описание = "{Модуль Тест / Ошибка в строке: 1 / Основное сообщение}"; + Ошибка = ФабрикаОшибок.Создать("foreign.code", Описание); + Классификатор = КлассификаторСОсновнымиПравилами(); + + // Действие + Результат = Классификатор.Классифицировать(Ошибка); + + // Проверка + Ожидаем.Что(ДиагностикаОшибок.КодОшибки(Результат)).Равно("test.main"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ПросмотретьЦепочкуГлубжеШестидесятиЧетырехКадров() Экспорт + + // Подготовка + Ошибка = ФабрикаОшибок.Создать("test.main", "Глубокая ошибка"); + Для Номер = 1 По 65 Цикл + Ошибка = ФабрикаОшибок.ОбернутьКонтекстом(Ошибка, "Контекст " + Номер); + КонецЦикла; + + Классификатор = КлассификаторСОсновнымиПравилами(); + + // Действие + Результат = Классификатор.Классифицировать(Ошибка); + + // Проверка + Ожидаем.Что(ДиагностикаОшибок.КодОшибки(Результат)).Равно("test.main"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ВернутьНеопределеноДляРавносильныхКодовИзРазныхДобавлений() Экспорт + + // Подготовка + Классификатор = Новый КлассификаторОшибок; + Классификатор.ДобавитьПравила(ПравилаДляСообщения("Спорное сообщение", "test.main")); + Классификатор.ДобавитьПравила(ПравилаДляСообщения("Спорное сообщение", "test.secondary")); + + // Действие + Результат = Классификатор.Классифицировать(ОшибкаССообщением("Спорное сообщение")); + + // Проверка + Ожидаем.Что(Результат).Идентично(Неопределено); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ВыбратьПервоеСообщениеОдинаковогоКодаИзРазныхДобавлений() Экспорт + + // Подготовка + Классификатор = Новый КлассификаторОшибок; + ПервоеПравило = Новый Массив; + ПервоеПравило.Добавить(ТочноеПравило( + "test.same", + "Первое описание", + "Одинаковое сообщение" + )); + ВтороеПравило = Новый Массив; + ВтороеПравило.Добавить(ТочноеПравило( + "test.same", + "Второе описание", + "Одинаковое сообщение" + )); + Классификатор.ДобавитьПравила(ПервоеПравило); + Классификатор.ДобавитьПравила(ВтороеПравило); + + // Действие + Результат = Классификатор.Классифицировать(ОшибкаССообщением("Одинаковое сообщение")); + + // Проверка + Ожидаем.Что(Новый СнимокКадраОшибки(Результат).Сообщение()).Равно("Первое описание"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ВыбратьПравилоСБольшимПриоритетом() Экспорт + + // Подготовка + Правила = Новый Массив; + Правила.Добавить(ТочноеПравило("test.main", "Основная ошибка", "Приоритетное сообщение")); + Правила.Добавить(ТочноеПравило("test.specific", "Специфичная ошибка", "Приоритетное сообщение", 10)); + + Классификатор = КлассификаторСПравилами(Правила); + + // Действие + Результат = Классификатор.Классифицировать(ОшибкаССообщением("Приоритетное сообщение")); + + // Проверка + Ожидаем.Что(ДиагностикаОшибок.КодОшибки(Результат)).Равно("test.specific"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ВыбратьПравилоСБольшимПриоритетомИзРазныхДобавлений() Экспорт + + // Подготовка + Классификатор = Новый КлассификаторОшибок; + Классификатор.ДобавитьПравила(ПравилаДляСообщения("Основное сообщение", "test.main", 10)); + Классификатор.ДобавитьПравила(ПравилаДляСообщения("Основное сообщение", "test.specific", 0)); + + // Действие + Результат = Классификатор.Классифицировать(ОшибкаССообщением("Основное сообщение")); + + // Проверка + Ожидаем.Что(ДиагностикаОшибок.КодОшибки(Результат)).Равно("test.main"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_НеОбертыватьПовторноРезультатПравила() Экспорт + + // Подготовка + Правила = Новый Массив; + Правила.Добавить(ТочноеПравилоСИдентификатором("Пользовательское сообщение", "custom.rule", "custom.id")); + Классификатор = КлассификаторСОсновнымиПравилами(); + Классификатор.ДобавитьПравила(Правила); + ПервыйРезультат = Классификатор.Классифицировать(ОшибкаССообщением("Пользовательское сообщение")); + + // Действие + ВторойРезультат = Классификатор.Классифицировать(ПервыйРезультат); + + // Проверка + Ожидаем.Что(ВторойРезультат).Идентично(ПервыйРезультат); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_НеПриниматьЧужойКодЗаРезультатПравила() Экспорт + + // Подготовка + Правила = Новый Массив; + Правила.Добавить(ТочноеПравилоСИдентификатором("Первое сообщение", "custom.first", "shared.id")); + Классификатор = КлассификаторСОсновнымиПравилами(); + Классификатор.ДобавитьПравила(Правила); + Данные = Новый Структура("Источник, Правило", "ПравилаКлассификации", "shared.id"); + Ошибка = ФабрикаОшибок.Создать("custom.second", "Чужая ошибка", Данные); + + // Действие + Результат = Классификатор.Классифицировать(Ошибка); + + // Проверка + Ожидаем.Что(Результат).Идентично(Неопределено); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_КопироватьПравилаПриДобавлении() Экспорт + + // Подготовка + Основные = ОсновныеПравила(); + + Классификатор = Новый КлассификаторОшибок; + Классификатор.ДобавитьПравила(Основные); + + Основные.Очистить(); + + Правило = ТочноеПравило("custom.original", "Исходная ошибка", "Добавленное сообщение"); + Правила = Новый Массив; + Правила.Добавить(Правило); + + Классификатор.ДобавитьПравила(Правила); + + Правило.КлассифицируетКак("custom.changed", "Измененная ошибка"); + + Причина = ФабрикаОшибок.Создать("custom.original", "Исходное сообщение"); + ОшибкаСДобавленнымКодом = ФабрикаОшибок.Создать( + "foreign.code", + "Внешняя ошибка", + Неопределено, + Причина + ); + + Классификатор.ДобавитьПоставщикаПравил(Новый ТестовыйПоставщикПравил(Новый Массив)); + + // Действие + ОсновнойРезультат = Классификатор.Классифицировать(ОшибкаССообщением("Основное сообщение")); + ДобавленныйРезультат = Классификатор.Классифицировать(ОшибкаССообщением("Добавленное сообщение")); + РезультатСтруктурированногоКода = Классификатор.Классифицировать(ОшибкаСДобавленнымКодом); + + // Проверка + Ожидаем.Что(ДиагностикаОшибок.КодОшибки(ОсновнойРезультат)).Равно("test.main"); + Ожидаем.Что(ДиагностикаОшибок.КодОшибки(ДобавленныйРезультат)).Равно("custom.original"); + Ожидаем.Что(ДиагностикаОшибок.КодОшибки(РезультатСтруктурированногоКода)).Равно("custom.original"); + Ожидаем.Что(Новый СнимокКадраОшибки(РезультатСтруктурированногоКода).Сообщение()) + .Равно("Исходная ошибка"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ЛокализоватьОдноязычноеСообщениеСтруктурированногоКода() Экспорт + + // Подготовка + Правила = Новый Массив; + Правила.Добавить(ТочноеПравило( + "test.localized", + "ru = 'Локализованная ошибка'", + "Локализуемое сообщение" + )); + + Классификатор = Новый КлассификаторОшибок; + Классификатор.ДобавитьПравила(Правила); + + Причина = ФабрикаОшибок.Создать("test.localized", "Исходное сообщение"); + Ошибка = ФабрикаОшибок.Создать("foreign.code", "Внешняя ошибка", Неопределено, Причина); + + // Действие + Результат = Классификатор.Классифицировать(Ошибка); + + // Проверка + Ожидаем.Что(Новый СнимокКадраОшибки(Результат).Сообщение()).Равно("Локализованная ошибка"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ВернутьСебяИзМетодовДобавления() Экспорт + + // Подготовка + Классификатор = КлассификаторСОсновнымиПравилами(); + + // Действие + РезультатПравил = Классификатор.ДобавитьПравила(Новый Массив); + РезультатПоставщика = Классификатор.ДобавитьПоставщикаПравил(Новый ТестовыйПоставщикПравил); + + // Проверка + Ожидаем.Что(РезультатПравил).Идентично(Классификатор); + Ожидаем.Что(РезультатПоставщика).Идентично(Классификатор); + +КонецПроцедуры + +#КонецОбласти + +#Область СлужебныеПроцедурыИФункции + +Функция КлассификаторСОсновнымиПравилами() + + Возврат КлассификаторСПравилами(ОсновныеПравила()); + +КонецФункции + +Функция КлассификаторСПравилами(Правила) + + Классификатор = Новый КлассификаторОшибок; + Классификатор.ДобавитьПравила(Правила); + + Возврат Классификатор; + +КонецФункции + +Функция ПравилаДляСообщения(Сообщение, КодОшибки, Приоритет = 0) + + Правила = Новый Массив; + Правила.Добавить(ТочноеПравило(КодОшибки, КодОшибки, Сообщение, Приоритет)); + + Возврат Правила; + +КонецФункции + +Функция ПравилаДляСообщенияСОписанием(Сообщение, КодОшибки, Описание) + + Правила = Новый Массив; + Правила.Добавить(ТочноеПравило(КодОшибки, Описание, Сообщение)); + + Возврат Правила; + +КонецФункции + +Функция ОсновныеПравила() + + Правила = Новый Массив; + Правила.Добавить(ТочноеПравило("test.main", "Основная ошибка", "Основное сообщение")); + Правила.Добавить(ПрефиксПравило("test.secondary", "Вторичная ошибка", "Вторичное сообщение:")); + Правила.Добавить(ТочноеПравило("test.specific", "Специфичная ошибка", "Специфичное сообщение")); + + Возврат Правила; + +КонецФункции + +Функция ТочноеПравилоСИдентификатором(ИсходноеСообщение, КодОшибки, Идентификатор) + + Возврат Новый ПравилоКлассификацииОшибок() + .СовпадаетС(ИсходноеСообщение) + .СИдентификатором(Идентификатор) + .КлассифицируетКак( + КодОшибки, + ИсходноеСообщение + ); + +КонецФункции + +Функция ТочноеПравило(КодОшибки, ОписаниеОшибки, ИсходноеСообщение, Приоритет = 0) + + Возврат Новый ПравилоКлассификацииОшибок() + .СовпадаетС(ИсходноеСообщение) + .СПриоритетом(Приоритет) + .КлассифицируетКак( + КодОшибки, + ОписаниеОшибки + ); + +КонецФункции + +Функция ПрефиксПравило(КодОшибки, ОписаниеОшибки, Префикс) + + Возврат Новый ПравилоКлассификацииОшибок() + .НачинаетсяС(Префикс) + .КлассифицируетКак( + КодОшибки, + ОписаниеОшибки + ); + +КонецФункции + +Функция ОшибкаССообщением(Сообщение) + + Попытка + ВызватьИсключение Сообщение; + Исключение + Возврат ИнформацияОбОшибке(); + КонецПопытки; + + ВызватьИсключение "Ожидаемая ошибка не возникла"; + +КонецФункции + +Функция КлассифицироватьВФоне(Классификатор) Экспорт + + ЗарегистрироватьГотовоеЗадание(); + + Пока Не ПараллельнаяКлассификацияРазрешена() Цикл + Приостановить(1); + КонецЦикла; + + Возврат Классификатор.Классифицировать(ОшибкаССообщением("Сообщение поставщика")); + +КонецФункции + +Процедура ЗарегистрироватьГотовоеЗадание() + + _БлокировкаСтарта.Заблокировать(); + _КоличествоГотовыхЗаданий = _КоличествоГотовыхЗаданий + 1; + _БлокировкаСтарта.Разблокировать(); + +КонецПроцедуры + +Функция КоличествоГотовыхЗаданий() + + _БлокировкаСтарта.Заблокировать(); + Результат = _КоличествоГотовыхЗаданий; + _БлокировкаСтарта.Разблокировать(); + + Возврат Результат; + +КонецФункции + +Процедура РазрешитьПараллельнуюКлассификацию() + + _БлокировкаСтарта.Заблокировать(); + _НачатьКлассификацию = Истина; + _БлокировкаСтарта.Разблокировать(); + +КонецПроцедуры + +Функция ПараллельнаяКлассификацияРазрешена() + + _БлокировкаСтарта.Заблокировать(); + Результат = _НачатьКлассификацию; + _БлокировкаСтарта.Разблокировать(); + + Возврат Результат; + +КонецФункции + +#КонецОбласти diff --git "a/tests/\320\242\320\265\321\201\321\202\321\213\320\237\321\200\320\260\320\262\320\270\320\273\320\260\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\206\320\270\320\270\320\236\321\210\320\270\320\261\320\276\320\272.os" "b/tests/\320\242\320\265\321\201\321\202\321\213\320\237\321\200\320\260\320\262\320\270\320\273\320\260\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\206\320\270\320\270\320\236\321\210\320\270\320\261\320\276\320\272.os" new file mode 100644 index 0000000..7d929e7 --- /dev/null +++ "b/tests/\320\242\320\265\321\201\321\202\321\213\320\237\321\200\320\260\320\262\320\270\320\273\320\260\320\232\320\273\320\260\321\201\321\201\320\270\321\204\320\270\320\272\320\260\321\206\320\270\320\270\320\236\321\210\320\270\320\261\320\276\320\272.os" @@ -0,0 +1,120 @@ +#Использовать asserts +#Использовать "../src" +#Использовать "../src/internal" + +#Область Тесты + +&Тест +Процедура ТестДолжен_ЗадатьУсловияФлюентнымИнтерфейсом() Экспорт + + // Действие + Правило = Новый ПравилоКлассификацииОшибок + .СНативнымТипом("System.Net.WebException") + .НачинаетсяС("Gateway rejected request:") + .ОканчиваетсяНа("request denied") + .СПриоритетом(10) + .СИдентификатором("test.gateway_rejected.v1") + .КлассифицируетКак("test.gateway_rejected", "Шлюз отклонил запрос"); + + // Проверка + Ожидаем.Что(Правило.КодОшибки()).Равно("test.gateway_rejected"); + Ожидаем.Что(Правило.ОписаниеРезультата()).Равно("Шлюз отклонил запрос"); + Ожидаем.Что(Правило.НативныйТип()).Равно("System.Net.WebException"); + Ожидаем.Что(Правило.Префикс()).Равно("Gateway rejected request:"); + Ожидаем.Что(Правило.Окончание()).Равно("request denied"); + Ожидаем.Что(Правило.Приоритет()).Равно(10); + Ожидаем.Что(Правило.Идентификатор()).Равно("test.gateway_rejected.v1"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ОтклонитьПравилоБезРезультатаКлассификации() Экспорт + + // Подготовка + Правила = Новый Массив; + Правила.Добавить( + Новый ПравилоКлассификацииОшибок() + .НачинаетсяС("Gateway rejected request:") + ); + + // Действие + Ошибка = Неопределено; + Попытка + Индекс = Новый ИндексПравилКлассификацииОшибок(Правила, "ТестовыеПравила"); + Исключение + Ошибка = ИнформацияОбОшибке(); + КонецПопытки; + + // Проверка + Ожидаем.Что(ДиагностикаОшибок.КодОшибки(Ошибка)).Равно( + КодыСтандартныхОшибок.НедопустимоеЗначениеАргумента() + ); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_СопоставитьТипПрефиксИОкончаниеОдногоКадра() Экспорт + + // Подготовка + Правила = Новый Массив; + Правила.Добавить( + Новый ПравилоКлассификацииОшибок() + .СНативнымТипом("System.Net.WebException") + .НачинаетсяС("Gateway rejected request:") + .ОканчиваетсяНа("request denied") + .КлассифицируетКак( + "test.gateway_rejected", + "Шлюз отклонил запрос" + ) + ); + + Кандидаты = Новый Массив; + Индекс = Новый ИндексПравилКлассификацииОшибок(Правила, "ТестовыеПравила"); + Кадр = Новый ФиксированнаяСтруктура(Новый Структура( + "Описание, НативныйТип, ВнутреннееСообщение, РазрешенДляПоиска", + "Gateway rejected request: request denied", + "System.Net.WebException", + "Gateway rejected request: request denied", + Истина + )); + + // Действие + Индекс.СобратьКандидаты(Кадр, Кандидаты); + + // Проверка + Ожидаем.Что(Кандидаты).ИмеетДлину(1); + Ожидаем.Что(Кандидаты[0].Код).Равно("test.gateway_rejected"); + +КонецПроцедуры + +&Тест +Процедура ТестДолжен_ОтклонитьТочноеСообщениеСДополнительнымУсловием() Экспорт + + // Подготовка + Правила = Новый Массив; + Правила.Добавить( + Новый ПравилоКлассификацииОшибок() + .СовпадаетС("Точное сообщение") + .НачинаетсяС("Точное") + .КлассифицируетКак( + "test.invalid", + "Некорректное правило" + ) + ); + + // Действие + Ошибка = Неопределено; + Попытка + Индекс = Новый ИндексПравилКлассификацииОшибок(Правила, "ТестовыеПравила"); + Исключение + Ошибка = ИнформацияОбОшибке(); + КонецПопытки; + + // Проверка + Ожидаем.Что(ДиагностикаОшибок.КодОшибки(Ошибка)).Равно( + КодыСтандартныхОшибок.НедопустимоеЗначениеАргумента() + ); + +КонецПроцедуры + +#КонецОбласти diff --git "a/tests/\320\242\320\265\321\201\321\202\321\213\320\246\320\265\320\277\320\276\321\207\320\272\320\270\320\232\320\260\320\264\321\200\320\276\320\262\320\236\321\210\320\270\320\261\320\272\320\270.os" "b/tests/\320\242\320\265\321\201\321\202\321\213\320\246\320\265\320\277\320\276\321\207\320\272\320\270\320\232\320\260\320\264\321\200\320\276\320\262\320\236\321\210\320\270\320\261\320\272\320\270.os" new file mode 100644 index 0000000..c0e40e6 --- /dev/null +++ "b/tests/\320\242\320\265\321\201\321\202\321\213\320\246\320\265\320\277\320\276\321\207\320\272\320\270\320\232\320\260\320\264\321\200\320\276\320\262\320\236\321\210\320\270\320\261\320\272\320\270.os" @@ -0,0 +1,27 @@ +#Использовать asserts +#Использовать "../src" +#Использовать "../src/internal" + +#Область Тесты + +&Тест +Процедура ТестДолжен_ВыделитьВнутреннееСообщениеНативнойОшибки() Экспорт + + // Подготовка + Ошибка = Новый ИнформацияОбОшибке( + "External exception (System.IO.IOException): Файл уже существует", + Новый ФиксированнаяСтруктура(Новый Структура) + ); + Снимок = Новый СнимокКадраОшибки(Ошибка); + Цепочка = Новый ЦепочкаКадровОшибки(Снимок); + + // Действие + Кадры = Цепочка.Кадры(); + + // Проверка + Ожидаем.Что(Кадры[0].НативныйТип).Равно("System.IO.IOException"); + Ожидаем.Что(Кадры[0].ВнутреннееСообщение).Равно("Файл уже существует"); + +КонецПроцедуры + +#КонецОбласти