Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: Публикация релиза

on:
release:
types: [published]
workflow_dispatch:

permissions:
contents: read
id-token: write

jobs:
release:
uses: autumn-library/workflows/.github/workflows/release.yml@main
with:
package_mask: "tempfiles-*.ospx"
trusted_publishing: true
23 changes: 23 additions & 0 deletions .github/workflows/testing.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Тестирование

on:
push:
branches: [master]
pull_request:
workflow_dispatch:

permissions:
contents: read

jobs:
test:
strategy:
fail-fast: false
matrix:
oscript_version: [stable, dev]
uses: autumn-library/workflows/.github/workflows/test.yml@main
with:
oscript_version: ${{ matrix.oscript_version }}
test_engine: oneunit
test_dir_paths: tests
build_package: true
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
*.ospx
oscript_modules/
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@
|``Удалить()`` | удаляет все ранее созданные файлы и каталоги
|``УдалитьФайл(Путь)`` | удаляет указанный файл/каталог. Если у файла указан файловый атрибут "только для чтения", перед удалением происходит попытка снять этот атрибут.
|``БезопасноУдалитьФайл(Путь)`` | удаляет указанный файл полностью аналогично методу `УдалитьФайл`
|``УдалитьФайлСИсключением(Путь)`` | удаляет файл/каталог, при сбое пробрасывает исходное исключение файловой операции. Снимает readonly у самого объекта и содержимого каталога. Отсутствующий путь считается успешно удаленным.
|``Файлы()`` | возвращает массив с путями ко всем временным файлам
|``УдалитьНакопленныеВременныеФайлы(ВременныеФайлыДо)`` | удаляются все накопленные временные файлы, которые были добавлены после фиксации набора временных файлов с помощью метода `Файлы`

`УдалитьФайл` и `БезопасноУдалитьФайл` возвращают `Булево` и логируют ошибку при неудаче. Для передачи причины сбоя очистки вызывающему коду (например, в результат теста OneUnit) используйте `УдалитьФайлСИсключением`.

## Собственный менеджер

Все временные файлы управляются классом `МенеджерВременныхФайлов`. Один экземпляр этого класса библиотека tempfiles создает автоматически и регистрирует его, как глобальное свойство `ВременныеФайлы`.
Expand Down Expand Up @@ -48,3 +51,14 @@
ВременныеФайлы.Удалить(); // все файлы, созданные менеджером ВременныеФайлы - удалены. Файл менеджера МойМенеджер - не тронут.
МойМенеджер.Удалить(); // все файлы, созданные менеджером МойМенеджер - удалены. Файл менеджера ВременныеФайлы, и всех прочих менеджеров - не тронуты.
```

## Тестирование

```sh
opm install --local --dev
oscript_modules/bin/oneunit e -d tests
```

Тесты охватывают весь публичный API, readonly-файлы и каталоги, повторную очистку и передачу причины ошибки в результат OneUnit. Проверки блокировки открытого файла выполняются в Windows.

GitHub Actions запускает тесты на Windows, Linux и macOS с версиями OneScript `stable` и `dev`. Workflow релиза публикует пакет в хаб через OIDC без постоянного токена. Для доверенной публикации используется `release.yml` из репозитория `oscript-library/tempfiles` на тегах `v*`; ручной запуск также следует выполнять на теге релиза.
4 changes: 3 additions & 1 deletion packagedef
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@

Описание.Имя("tempfiles")
.Версия("1.1.1")
.Версия("1.2.0")
.ЗависитОт("logos")
.РазработкаЗависитОт("asserts", "1.6.2")
.РазработкаЗависитОт("oneunit", "0.5.0")
.ВключитьФайл("ВременныеФайлы.os")
.ВключитьФайл("lib.config")

28 changes: 28 additions & 0 deletions tests/fixtures/ОшибкаОчистки.os
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#Использовать "../.."

Перем Менеджер;
Перем Чтение;
Перем Путь;

&Тест
Процедура ОткрытьФайл() Экспорт
Менеджер = Новый МенеджерВременныхФайлов;
Путь = Менеджер.СоздатьФайл("tempfiles-cleanup-error");
Чтение = Новый ЧтениеТекста(Путь);
КонецПроцедуры

&ПослеКаждого
Процедура ОчиститьФайл() Экспорт

Попытка
Менеджер.УдалитьФайлСИсключением(Путь);
Исключение
Чтение.Закрыть();
Менеджер.Удалить();
ВызватьИсключение;
КонецПопытки;

Чтение.Закрыть();
Менеджер.Удалить();

КонецПроцедуры
1 change: 1 addition & 0 deletions tests/oscript.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
lib.system=../oscript_modules
31 changes: 31 additions & 0 deletions tests/ПричинаОчистки.os
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#Использовать asserts
#Использовать ".."
#Использовать autumn
#Использовать oneunit

&Тест
&ВключенНаОС("Windows", Причина = "Открытый файл блокирует удаление в Windows")
Процедура РезультатOneUnitСодержитПричинуСбояОчистки() Экспорт

Детальки = Новый Соответствие;
Детальки.Вставить("OneUnit.КаталогиТестов", ОбъединитьПути(ТекущийСценарий().Каталог, "fixtures"));
Детальки.Вставить("OneUnit.ТестовыеНаборыВключаемые", "ОшибкаОчистки");
Совет = Новый СоветДругогоМастера;
Совет.ЗначенияДеталек(Детальки);
Поделка = Новый Поделка(Совет);
Поделка.ЗапуститьПриложение();
План = Поделка.НайтиЖелудь("МенеджерТестирования").Тестировать();
Статистика = Поделка.НайтиЖелудь("РепортерСтатистика");

Ожидаем.Что(План.Дети().Количество()).Равно(1);
Набор = План.Дети().Получить(0);
Ожидаем.Что(Набор.Дети().Количество()).Равно(1);
Результат = Статистика.Результат(Набор.Дети().Получить(0));
Ожидаем.Что(Результат.Состояние()).Не_().Равно(СостоянияВыполненияТестов.Успех);
Ожидаем.Что(Результат.Причины().Количество()).Больше(0);
Описание = ТестированиеСлужебный.ПодробноеОписаниеОшибки("Причина", Результат.Причины().Получить(0));
Ожидаем.Что(Описание).Содержит(".tempfiles-cleanup-error");
Ожидаем.Что(Описание).Содержит("ВременныеФайлы.os");
Ожидаем.Что(Описание).Содержит("System.IO.IOException");

КонецПроцедуры
175 changes: 175 additions & 0 deletions tests/СозданиеИУчет.os
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
#Использовать asserts
#Использовать ".."

Перем Менеджер;
Перем ДругойМенеджер;

&ПередКаждым
Процедура СоздатьМенеджеры() Экспорт
Менеджер = Новый МенеджерВременныхФайлов;
ДругойМенеджер = Новый МенеджерВременныхФайлов;
КонецПроцедуры

&ПослеКаждого
Процедура УдалитьВременныеФайлы() Экспорт
Менеджер.Удалить();
ДругойМенеджер.Удалить();
Ожидаем.Что(Менеджер.Файлы().Количество()).Равно(0);
Ожидаем.Что(ДругойМенеджер.Файлы().Количество()).Равно(0);
КонецПроцедуры

&Тест
Процедура НовыйМенеджерНеСодержитПутей() Экспорт
Ожидаем.Что(Менеджер.Файлы().Количество()).Равно(0);
КонецПроцедуры

&Тест
Процедура НовоеИмяУникальноИНеСоздаетФайл() Экспорт

ПервыйПуть = Менеджер.НовоеИмяФайла();
ВторойПуть = Менеджер.НовоеИмяФайла();
Ожидаем.Что(ПервыйПуть).Не_().Равно(ВторойПуть);
Ожидаем.Что(Новый Файл(ПервыйПуть).Расширение).Равно(".tmp");
Ожидаем.Что(Новый Файл(ПервыйПуть).Существует()).ЭтоЛожь();
Ожидаем.Что(Новый Файл(ВторойПуть).Существует()).ЭтоЛожь();
Ожидаем.Что(Менеджер.Файлы().Количество()).Равно(2);
Ожидаем.Что(Менеджер.Файлы()[0]).Равно(ПервыйПуть);
Ожидаем.Что(Менеджер.Файлы()[1]).Равно(ВторойПуть);

КонецПроцедуры

&ПараметризованныйТест
&ИсточникЗначение("tmp")
&ИсточникЗначение("json")
&ИсточникЗначение("")
Процедура ИмяУчитываетРасширение(Расширение) Экспорт
Путь = Менеджер.НовоеИмяФайла(Расширение);
Ожидаем.Что(Путь).Не_().Равно("");
Если Расширение <> "" Тогда
Ожидаем.Что(Новый Файл(Путь).Расширение).Равно("." + Расширение);
КонецЕсли;
КонецПроцедуры

&ПараметризованныйТест
&ИсточникЗначение("СоздатьФайл", Ложь)
&ИсточникЗначение("СоздатьКаталог", Истина)
Процедура СоздаетсяПустойРесурсПоЗарегистрированномуПути(Метод, ЭтоКаталог) Экспорт

Аргументы = Новый Массив;
Аргументы.Добавить("data");
Рефлектор = Новый Рефлектор;
Путь = Рефлектор.ВызватьМетод(Менеджер, Метод, Аргументы);
Файл = Новый Файл(Путь);
Ожидаем.Что(Файл.Существует()).ЭтоИстина();
Ожидаем.Что(Файл.ЭтоКаталог()).Равно(ЭтоКаталог);
Ожидаем.Что(Файл.ЭтоФайл()).Равно(Не ЭтоКаталог);
Ожидаем.Что(Файл.Расширение).Равно(".data");
Ожидаем.Что(Менеджер.Файлы().Количество()).Равно(1);
Ожидаем.Что(Менеджер.Файлы()[0]).Равно(Путь);

Если ЭтоКаталог Тогда
Ожидаем.Что(НайтиФайлы(Путь, ПолучитьМаскуВсеФайлы()).Количество()).Равно(0);
Иначе
Ожидаем.Что(Файл.Размер()).Равно(0);
КонецЕсли;

КонецПроцедуры

&ПараметризованныйТест
&ИсточникЗначение("НовоеИмяФайла")
&ИсточникЗначение("СоздатьФайл")
&ИсточникЗначение("СоздатьКаталог")
Процедура ИспользуетсяЗаданныйБазовыйКаталог(Метод) Экспорт

Каталог = ДругойМенеджер.СоздатьКаталог();
Менеджер.БазовыйКаталог = Каталог;
Рефлектор = Новый Рефлектор;
Путь = Рефлектор.ВызватьМетод(Менеджер, Метод);
Ожидаем.Что(Новый Файл(Путь).Путь).Равно(Каталог + ПолучитьРазделительПути());
Ожидаем.Что(Менеджер.Файлы()[0]).Равно(Путь);

КонецПроцедуры

&Тест
Процедура ФайлыВозвращаетНезависимыйСнимок() Экспорт

Путь = Менеджер.НовоеИмяФайла();
Снимок = Менеджер.Файлы();
Снимок.Очистить();
Ожидаем.Что(Менеджер.Файлы().Количество()).Равно(1);
Ожидаем.Что(Менеджер.Файлы()[0]).Равно(Путь);
Снимок = Менеджер.Файлы();
Менеджер.НовоеИмяФайла();
Ожидаем.Что(Снимок.Количество()).Равно(1);

КонецПроцедуры

&Тест
Процедура УдалитьОчищаетТолькоСвойМенеджерИДопускаетПовторныйВызов() Экспорт

СвойФайл = Менеджер.СоздатьФайл();
СвойКаталог = Менеджер.СоздатьКаталог();
Менеджер.НовоеИмяФайла();
ЧужойФайл = ДругойМенеджер.СоздатьФайл();
Менеджер.Удалить();
Менеджер.Удалить();
Ожидаем.Что(Менеджер.Файлы().Количество()).Равно(0);
Ожидаем.Что(Новый Файл(СвойФайл).Существует()).ЭтоЛожь();
Ожидаем.Что(Новый Файл(СвойКаталог).Существует()).ЭтоЛожь();
Ожидаем.Что(Новый Файл(ЧужойФайл).Существует()).ЭтоИстина();
Ожидаем.Что(ДругойМенеджер.Файлы().Количество()).Равно(1);

КонецПроцедуры

&Тест
Процедура УдаляютсяТолькоНакопленныеПослеСнимкаРесурсы() Экспорт

СтарыйПуть = Менеджер.СоздатьФайл();
Снимок = Менеджер.Файлы();
НовыйФайл = Менеджер.СоздатьФайл();
НовыйКаталог = Менеджер.СоздатьКаталог();
ReadonlyФайл = Новый Файл(НовыйФайл);
ReadonlyФайл.УстановитьТолькоЧтение(Истина);
Менеджер.НовоеИмяФайла();

Менеджер.УдалитьНакопленныеВременныеФайлы(Снимок);
Менеджер.УдалитьНакопленныеВременныеФайлы(Снимок);
Ожидаем.Что(Новый Файл(СтарыйПуть).Существует()).ЭтоИстина();
Ожидаем.Что(Новый Файл(НовыйФайл).Существует()).ЭтоЛожь();
Ожидаем.Что(Новый Файл(НовыйКаталог).Существует()).ЭтоЛожь();
Ожидаем.Что(Снимок.Количество()).Равно(1);
Ожидаем.Что(Снимок[0]).Равно(СтарыйПуть);
Ожидаем.Что(Менеджер.Файлы().Количество()).Равно(1);
Ожидаем.Что(Менеджер.Файлы()[0]).Равно(СтарыйПуть);

КонецПроцедуры

&Тест
Процедура ПустойСнимокУдаляетВсеНакопленныеРесурсы() Экспорт
Путь = Менеджер.СоздатьФайл();
Менеджер.УдалитьНакопленныеВременныеФайлы(Новый Массив);
Ожидаем.Что(Новый Файл(Путь).Существует()).ЭтоЛожь();
Ожидаем.Что(Менеджер.Файлы().Количество()).Равно(0);
КонецПроцедуры

&Тест
Процедура КоллекцияКопируетсяВСтруктуруИСоответствие() Экспорт

Источник = Новый Массив;
Источник.Добавить("Первый");
Источник.Добавить("Второй");
Источник.Добавить("Первый");
Структура = Новый Структура;
Структура.Вставить("Первый", "СтароеЗначение");
Соответствие = Новый Соответствие;
Менеджер.СкопироватьКоллекцию(Источник, Структура);
Менеджер.СкопироватьКоллекцию(Источник, Соответствие);
Менеджер.СкопироватьКоллекцию(Новый Массив, Соответствие);
Ожидаем.Что(Структура.Количество()).Равно(2);
Ожидаем.Что(Соответствие.Количество()).Равно(2);
Для Каждого Ключ Из Источник Цикл
Ожидаем.Что(Структура[Ключ]).Равно(Ключ);
Ожидаем.Что(Соответствие[Ключ]).Равно(Ключ);
КонецЦикла;

КонецПроцедуры
Loading
Loading