diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..a2abebc --- /dev/null +++ b/.github/workflows/release.yml @@ -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 diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml new file mode 100644 index 0000000..44d9d02 --- /dev/null +++ b/.github/workflows/testing.yml @@ -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 diff --git a/.gitignore b/.gitignore index d67baba..de5689e 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.ospx +oscript_modules/ diff --git a/README.md b/README.md index af84b0d..d189677 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,12 @@ |``Удалить()`` | удаляет все ранее созданные файлы и каталоги |``УдалитьФайл(Путь)`` | удаляет указанный файл/каталог. Если у файла указан файловый атрибут "только для чтения", перед удалением происходит попытка снять этот атрибут. |``БезопасноУдалитьФайл(Путь)`` | удаляет указанный файл полностью аналогично методу `УдалитьФайл` +|``УдалитьФайлСИсключением(Путь)`` | удаляет файл/каталог, при сбое пробрасывает исходное исключение файловой операции. Снимает readonly у самого объекта и содержимого каталога. Отсутствующий путь считается успешно удаленным. |``Файлы()`` | возвращает массив с путями ко всем временным файлам |``УдалитьНакопленныеВременныеФайлы(ВременныеФайлыДо)`` | удаляются все накопленные временные файлы, которые были добавлены после фиксации набора временных файлов с помощью метода `Файлы` +`УдалитьФайл` и `БезопасноУдалитьФайл` возвращают `Булево` и логируют ошибку при неудаче. Для передачи причины сбоя очистки вызывающему коду (например, в результат теста OneUnit) используйте `УдалитьФайлСИсключением`. + ## Собственный менеджер Все временные файлы управляются классом `МенеджерВременныхФайлов`. Один экземпляр этого класса библиотека tempfiles создает автоматически и регистрирует его, как глобальное свойство `ВременныеФайлы`. @@ -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*`; ручной запуск также следует выполнять на теге релиза. diff --git a/packagedef b/packagedef index 905014d..7fa279b 100644 --- a/packagedef +++ b/packagedef @@ -1,7 +1,9 @@  Описание.Имя("tempfiles") - .Версия("1.1.1") + .Версия("1.2.0") .ЗависитОт("logos") + .РазработкаЗависитОт("asserts", "1.6.2") + .РазработкаЗависитОт("oneunit", "0.5.0") .ВключитьФайл("ВременныеФайлы.os") .ВключитьФайл("lib.config") diff --git "a/tests/fixtures/\320\236\321\210\320\270\320\261\320\272\320\260\320\236\321\207\320\270\321\201\321\202\320\272\320\270.os" "b/tests/fixtures/\320\236\321\210\320\270\320\261\320\272\320\260\320\236\321\207\320\270\321\201\321\202\320\272\320\270.os" new file mode 100644 index 0000000..2c27b22 --- /dev/null +++ "b/tests/fixtures/\320\236\321\210\320\270\320\261\320\272\320\260\320\236\321\207\320\270\321\201\321\202\320\272\320\270.os" @@ -0,0 +1,28 @@ +#Использовать "../.." + +Перем Менеджер; +Перем Чтение; +Перем Путь; + +&Тест +Процедура ОткрытьФайл() Экспорт + Менеджер = Новый МенеджерВременныхФайлов; + Путь = Менеджер.СоздатьФайл("tempfiles-cleanup-error"); + Чтение = Новый ЧтениеТекста(Путь); +КонецПроцедуры + +&ПослеКаждого +Процедура ОчиститьФайл() Экспорт + + Попытка + Менеджер.УдалитьФайлСИсключением(Путь); + Исключение + Чтение.Закрыть(); + Менеджер.Удалить(); + ВызватьИсключение; + КонецПопытки; + + Чтение.Закрыть(); + Менеджер.Удалить(); + +КонецПроцедуры diff --git a/tests/oscript.cfg b/tests/oscript.cfg new file mode 100644 index 0000000..874f3d2 --- /dev/null +++ b/tests/oscript.cfg @@ -0,0 +1 @@ +lib.system=../oscript_modules diff --git "a/tests/\320\237\321\200\320\270\321\207\320\270\320\275\320\260\320\236\321\207\320\270\321\201\321\202\320\272\320\270.os" "b/tests/\320\237\321\200\320\270\321\207\320\270\320\275\320\260\320\236\321\207\320\270\321\201\321\202\320\272\320\270.os" new file mode 100644 index 0000000..31cd051 --- /dev/null +++ "b/tests/\320\237\321\200\320\270\321\207\320\270\320\275\320\260\320\236\321\207\320\270\321\201\321\202\320\272\320\270.os" @@ -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"); + +КонецПроцедуры diff --git "a/tests/\320\241\320\276\320\267\320\264\320\260\320\275\320\270\320\265\320\230\320\243\321\207\320\265\321\202.os" "b/tests/\320\241\320\276\320\267\320\264\320\260\320\275\320\270\320\265\320\230\320\243\321\207\320\265\321\202.os" new file mode 100644 index 0000000..b6c22b8 --- /dev/null +++ "b/tests/\320\241\320\276\320\267\320\264\320\260\320\275\320\270\320\265\320\230\320\243\321\207\320\265\321\202.os" @@ -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); + Для Каждого Ключ Из Источник Цикл + Ожидаем.Что(Структура[Ключ]).Равно(Ключ); + Ожидаем.Что(Соответствие[Ключ]).Равно(Ключ); + КонецЦикла; + +КонецПроцедуры diff --git "a/tests/\320\243\320\264\320\260\320\273\320\265\320\275\320\270\320\265.os" "b/tests/\320\243\320\264\320\260\320\273\320\265\320\275\320\270\320\265.os" new file mode 100644 index 0000000..65e7605 --- /dev/null +++ "b/tests/\320\243\320\264\320\260\320\273\320\265\320\275\320\270\320\265.os" @@ -0,0 +1,218 @@ +#Использовать asserts +#Использовать ".." + +Перем Менеджер; + +&ПередКаждым +Процедура СоздатьМенеджер() Экспорт + Менеджер = Новый МенеджерВременныхФайлов; +КонецПроцедуры + +&ПослеКаждого +Процедура УдалитьВременныеФайлы() Экспорт + Менеджер.Удалить(); + Для Каждого Путь Из Менеджер.Файлы() Цикл + Ожидаем.Что(Новый Файл(Путь).Существует(), Путь).ЭтоЛожь(); + КонецЦикла; +КонецПроцедуры + +&ПараметризованныйТест +&ИсточникЗначение("УдалитьФайл") +&ИсточникЗначение("БезопасноУдалитьФайл") +&ИсточникЗначение("УдалитьФайлСИсключением") +Процедура УдаляетсяОтдельныйReadonlyФайл(Метод) Экспорт + + Путь = Менеджер.СоздатьФайл(); + Файл = Новый Файл(Путь); + Файл.УстановитьТолькоЧтение(Истина); + Ожидаем.Что(Новый Файл(Путь).ПолучитьТолькоЧтение()).ЭтоИстина(); + + ПроверитьУдаление(Метод, Путь); + +КонецПроцедуры + +&ПараметризованныйТест +&ИсточникЗначение("УдалитьФайл") +&ИсточникЗначение("БезопасноУдалитьФайл") +&ИсточникЗначение("УдалитьФайлСИсключением") +Процедура УдаляетсяReadonlyКаталогСReadonlyСодержимым(Метод) Экспорт + + Путь = Менеджер.СоздатьКаталог(); + ВложенныйКаталог = ОбъединитьПути(Путь, "вложенный"); + СоздатьКаталог(ВложенныйКаталог); + Для Каждого Каталог Из СтрРазделить(Путь + "|" + ВложенныйКаталог, "|") Цикл + Запись = Новый ЗаписьТекста(ОбъединитьПути(Каталог, "файл")); + Запись.Закрыть(); + Файл = Новый Файл(ОбъединитьПути(Каталог, "файл")); + Файл.УстановитьТолькоЧтение(Истина); + Файл = Новый Файл(Каталог); + Файл.УстановитьТолькоЧтение(Истина); + КонецЦикла; + + ПроверитьУдаление(Метод, Путь); + +КонецПроцедуры + +&ПараметризованныйТест +&ИсточникЗначение("УдалитьФайл") +&ИсточникЗначение("БезопасноУдалитьФайл") +&ИсточникЗначение("УдалитьФайлСИсключением") +Процедура УдаляетсяОбычныйФайл(Метод) Экспорт + ПроверитьУдаление(Метод, Менеджер.СоздатьФайл()); +КонецПроцедуры + +&ПараметризованныйТест +&ИсточникЗначение("УдалитьФайл") +&ИсточникЗначение("БезопасноУдалитьФайл") +&ИсточникЗначение("УдалитьФайлСИсключением") +Процедура УдаляетсяПустойReadonlyКаталог(Метод) Экспорт + + Путь = Менеджер.СоздатьКаталог(); + Файл = Новый Файл(Путь); + Файл.УстановитьТолькоЧтение(Истина); + ПроверитьУдаление(Метод, Путь); + +КонецПроцедуры + +&ПараметризованныйТест +&ИсточникЗначение("УдалитьФайл") +&ИсточникЗначение("БезопасноУдалитьФайл") +&ИсточникЗначение("УдалитьФайлСИсключением") +Процедура ОтсутствующийПутьСчитаетсяУдаленным(Метод) Экспорт + ПроверитьУдаление(Метод, Менеджер.НовоеИмяФайла()); +КонецПроцедуры + +&ПараметризованныйТест +&ИсточникЗначение("УдалитьФайл") +&ИсточникЗначение("БезопасноУдалитьФайл") +&ВключенНаОС("Windows", Причина = "Открытый файл блокирует удаление в Windows") +Процедура СовместимыйМетодВозвращаетЛожьДляЗанятогоФайла(Метод) Экспорт + + Путь = Менеджер.СоздатьФайл(); + Чтение = Новый ЧтениеТекста(Путь); + + Попытка + Ожидаем.Что(ВызватьУдаление(Метод, Путь)).ЭтоЛожь(); + Ожидаем.Что(Новый Файл(Путь).Существует()).ЭтоИстина(); + Исключение + Чтение.Закрыть(); + ВызватьИсключение; + КонецПопытки; + + Чтение.Закрыть(); + +КонецПроцедуры + +&Тест +&ВключенНаОС("Windows", Причина = "Открытый файл блокирует удаление в Windows") +Процедура ПробрасываетсяИсходнаяОшибкаУдаленияЗанятогоФайла() Экспорт + + Путь = Менеджер.СоздатьФайл(); + Чтение = Новый ЧтениеТекста(Путь); + + Попытка + + ИсходнаяОшибка = Неопределено; + Попытка + УдалитьФайлы(Путь); + Исключение + ИсходнаяОшибка = ИнформацияОбОшибке(); + КонецПопытки; + Ожидаем.Что(ИсходнаяОшибка).Не_().Равно(Неопределено); + + Ошибка = Неопределено; + Попытка + Менеджер.УдалитьФайлСИсключением(Путь); + Исключение + Ошибка = ИнформацияОбОшибке(); + КонецПопытки; + Ожидаем.Что(Ошибка).Не_().Равно(Неопределено); + Ожидаем.Что(Ошибка.ИмяМодуля).Содержит("ВременныеФайлы.os"); + Ожидаем.Что(Ошибка.Описание).Содержит(Путь); + + Пока ИсходнаяОшибка <> Неопределено Цикл + Ожидаем.Что(Ошибка).Не_().Равно(Неопределено); + Ожидаем.Что(Ошибка.Описание).Равно(ИсходнаяОшибка.Описание); + Ошибка = Ошибка.Причина; + ИсходнаяОшибка = ИсходнаяОшибка.Причина; + КонецЦикла; + Ожидаем.Что(Ошибка).Равно(Неопределено); + + Исключение + Чтение.Закрыть(); + ВызватьИсключение; + КонецПопытки; + + Чтение.Закрыть(); + +КонецПроцедуры + +&Тест +&ВключенНаОС("Windows", Причина = "Открытый файл блокирует удаление в Windows") +Процедура МассовоеУдалениеСохраняетНеудаленныйПутьДляПовторнойПопытки() Экспорт + + ЗанятыйПуть = Менеджер.СоздатьФайл(); + ReadonlyПуть = Менеджер.СоздатьФайл(); + Файл = Новый Файл(ReadonlyПуть); + Файл.УстановитьТолькоЧтение(Истина); + Чтение = Новый ЧтениеТекста(ЗанятыйПуть); + + Попытка + Менеджер.Удалить(); + Ожидаем.Что(Новый Файл(ReadonlyПуть).Существует()).ЭтоЛожь(); + Ожидаем.Что(Менеджер.Файлы().Количество()).Равно(1); + Ожидаем.Что(Менеджер.Файлы()[0]).Равно(ЗанятыйПуть); + Исключение + Чтение.Закрыть(); + ВызватьИсключение; + КонецПопытки; + + Чтение.Закрыть(); + Менеджер.Удалить(); + Ожидаем.Что(Менеджер.Файлы().Количество()).Равно(0); + Ожидаем.Что(Новый Файл(ЗанятыйПуть).Существует()).ЭтоЛожь(); + +КонецПроцедуры + +&Тест +&ВключенНаОС("Windows", Причина = "Открытый файл блокирует удаление в Windows") +Процедура НакопительнаяОчисткаСохраняетЗанятыйПутьИПродолжаетУдаление() Экспорт + + СтарыйПуть = Менеджер.СоздатьФайл(); + Снимок = Менеджер.Файлы(); + УдаляемыйПуть = Менеджер.СоздатьФайл(); + ЗанятыйПуть = Менеджер.СоздатьФайл(); + Чтение = Новый ЧтениеТекста(ЗанятыйПуть); + + Попытка + Менеджер.УдалитьНакопленныеВременныеФайлы(Снимок); + Ожидаем.Что(Новый Файл(УдаляемыйПуть).Существует()).ЭтоЛожь(); + Ожидаем.Что(Новый Файл(СтарыйПуть).Существует()).ЭтоИстина(); + Ожидаем.Что(Менеджер.Файлы().Количество()).Равно(2); + Ожидаем.Что(Менеджер.Файлы()[1]).Равно(ЗанятыйПуть); + Исключение + Чтение.Закрыть(); + ВызватьИсключение; + КонецПопытки; + + Чтение.Закрыть(); + Менеджер.УдалитьНакопленныеВременныеФайлы(Снимок); + Ожидаем.Что(Новый Файл(ЗанятыйПуть).Существует()).ЭтоЛожь(); + Ожидаем.Что(Менеджер.Файлы().Количество()).Равно(1); + +КонецПроцедуры + +Процедура ПроверитьУдаление(Метод, Путь) + Результат = ВызватьУдаление(Метод, Путь); + Если Метод <> "УдалитьФайлСИсключением" Тогда + Ожидаем.Что(Результат).ЭтоИстина(); + КонецЕсли; + Ожидаем.Что(Новый Файл(Путь).Существует(), Путь).ЭтоЛожь(); +КонецПроцедуры + +Функция ВызватьУдаление(Метод, Путь) + Аргументы = Новый Массив; + Аргументы.Добавить(Путь); + Рефлектор = Новый Рефлектор; + Возврат Рефлектор.ВызватьМетод(Менеджер, Метод, Аргументы); +КонецФункции diff --git "a/\320\222\321\200\320\265\320\274\320\265\320\275\320\275\321\213\320\265\320\244\320\260\320\271\320\273\321\213.os" "b/\320\222\321\200\320\265\320\274\320\265\320\275\320\275\321\213\320\265\320\244\320\260\320\271\320\273\321\213.os" index a0990a1..e229cf0 100644 --- "a/\320\222\321\200\320\265\320\274\320\265\320\275\320\275\321\213\320\265\320\244\320\260\320\271\320\273\321\213.os" +++ "b/\320\222\321\200\320\265\320\274\320\265\320\275\320\275\321\213\320\265\320\244\320\260\320\271\320\273\321\213.os" @@ -48,7 +48,7 @@ Функция СоздатьФайл(Знач Расширение = "tmp") Экспорт ИмяФайла = НовоеИмяФайла(Расширение); - Кодировка = ?(ЭтоWindows, КодировкаТекста.ANSI, "utf-8"); + Кодировка = ?(ЭтоWindows, КодировкаТекста.ANSI, КодировкаТекста.UTF8NoBOM); ЗаписьТекста = Новый ЗаписьТекста(ИмяФайла, Кодировка); ЗаписьТекста.Закрыть(); Возврат ИмяФайла; @@ -113,31 +113,52 @@ // Функция БезопасноУдалитьФайл(Знач Путь) Экспорт + Попытка + УдалитьФайлСИсключением(Путь); + Возврат Истина; + Исключение + ТекстОшибки = "Попытка удаления " + Путь + " закончилась неудачей, по причине " + ОписаниеОшибки(); + Лог.Предупреждение(ТекстОшибки); + КонецПопытки; + + Возврат Ложь; + +КонецФункции + +// удаляет указанный файл/каталог, снимая атрибут "только для чтения" +// у самого объекта и содержимого каталога при неудачной первой попытке. +// При сбое пробрасывает исходное исключение файловой операции. +// Отсутствующий путь считается успешно удаленным. +// +// Параметры: +// Путь - Строка - путь файла или каталога +// +Процедура УдалитьФайлСИсключением(Знач Путь) Экспорт + Попытка УдалитьФайлы(Путь); - Возврат Истина; Исключение - Попытка + Файл = Новый Файл(Путь); + Если Файл.ПолучитьТолькоЧтение() Тогда + Файл.УстановитьТолькоЧтение(Ложь); + КонецЕсли; + + Если Файл.ЭтоКаталог() Тогда Для каждого Файл Из НайтиФайлы(Путь, ПолучитьМаскуВсеФайлы(), Истина) Цикл Если Файл.ПолучитьТолькоЧтение() Тогда Файл.УстановитьТолькоЧтение(Ложь); КонецЕсли; КонецЦикла; - УдалитьФайлы(Путь); - Возврат Истина; - Исключение - ТекстОшибки = "Попытка удаления " + Путь + " закончилась неудачей, по причине " + ОписаниеОшибки(); - Лог.Предупреждение(ТекстОшибки); - КонецПопытки; + КонецЕсли; + + УдалитьФайлы(Путь); КонецПопытки; - Возврат Ложь; - -КонецФункции +КонецПроцедуры // Массив накопленных путей временных файлов // @@ -182,7 +203,7 @@ ИмяВременногоФайла = ВременныеФайлыПосле[Индекс]; Если ОтборФайлов.Получить(ИмяВременногоФайла) = Неопределено И БезопасноУдалитьФайл(ИмяВременногоФайла) Тогда - ВременныеФайлыПосле.Удалить(Индекс); + мВременныеФайлы.Удалить(Индекс); КонецЕсли; КонецЦикла;