Index: Application/Ringtoets/src/Application.Ringtoets.Storage/Application.Ringtoets.Storage.csproj =================================================================== diff -u -r3ddadfbc5e096c8df7d709be0d9a9057cf4b8dd6 -r3456261423844a9f804b352472ac08feb22485ac --- Application/Ringtoets/src/Application.Ringtoets.Storage/Application.Ringtoets.Storage.csproj (.../Application.Ringtoets.Storage.csproj) (revision 3ddadfbc5e096c8df7d709be0d9a9057cf4b8dd6) +++ Application/Ringtoets/src/Application.Ringtoets.Storage/Application.Ringtoets.Storage.csproj (.../Application.Ringtoets.Storage.csproj) (revision 3456261423844a9f804b352472ac08feb22485ac) @@ -323,7 +323,7 @@ - + Index: Application/Ringtoets/src/Application.Ringtoets.Storage/BackedUpFileWriter.cs =================================================================== diff -u --- Application/Ringtoets/src/Application.Ringtoets.Storage/BackedUpFileWriter.cs (revision 0) +++ Application/Ringtoets/src/Application.Ringtoets.Storage/BackedUpFileWriter.cs (revision 3456261423844a9f804b352472ac08feb22485ac) @@ -0,0 +1,231 @@ +// Copyright (C) Stichting Deltares 2016. All rights reserved. +// +// This file is part of Ringtoets. +// +// Ringtoets is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// All names, logos, and references to "Deltares" are registered trademarks of +// Stichting Deltares and remain full property of Stichting Deltares at all times. +// All rights reserved. + +using System; +using System.IO; +using Application.Ringtoets.Storage.Properties; +using Core.Common.Utils; + +namespace Application.Ringtoets.Storage +{ + /// + /// Class for providing a safe way of writing files by creating a temporary backup file of targeted files. + /// + public class BackedUpFileWriter + { + private const string temporarySuffix = "~"; + + private readonly string temporaryFilePath; + private readonly string targetFilePath; + private bool createTemporaryFile; + + /// + /// Creates an instance of . + /// + /// The path of the file which will be overwritten. + /// is not a valid path. + public BackedUpFileWriter(string targetFilePath) + { + FileUtils.ValidateFilePath(targetFilePath); + + this.targetFilePath = targetFilePath; + temporaryFilePath = targetFilePath + temporarySuffix; + } + + /// + /// Performs the in a safe way by backing up the targeted file provided when + /// constructing the . It is expected that the + /// will throw an exception when the operation fails, so that the backed up target file can be restored. + /// + /// The action to perform after backing up the targeted file. + /// Thrown when: + /// + /// The temporary file already exists and cannot be deleted. + /// The temporary file cannot be created from the target file. + /// When reverting, the original file cannot be restored. + /// When cleaning up, the temporary file cannot be removed. + /// + /// + /// Any thrown by will be rethrown. + public void Perform(Action writeAction) + { + CreateTemporaryFile(); + + try + { + writeAction(); + } + catch + { + Revert(); + throw; + } + Finish(); + } + + /// + /// Removes the temporary file if it was created. + /// + /// The temporary file cannot be removed. + private void Finish() + { + if (createTemporaryFile) + { + DeleteTemporaryFile(); + } + } + + /// + /// Reverts the target file to the temporary file if it was created. If the operation fails, + /// the temporary file will remain in the directory of the target file. + /// + /// The original file cannot be restored. + private void Revert() + { + if (createTemporaryFile) + { + RestoreOriginalFile(); + } + } + + /// + /// Creates a temporary file from the target file, if there is any. Creates a new file at the target + /// file path. + /// + /// Thrown when either: + /// + /// The temporary file already exists and cannot be deleted. + /// The temporary file cannot be created from the target file. + /// + /// + private void CreateTemporaryFile() + { + createTemporaryFile = File.Exists(targetFilePath); + + if (createTemporaryFile) + { + RemoveAlreadyExistingTemporaryFile(); + CreateNewTemporaryFile(); + } + } + + /// + /// Removes the temporary file for the target file if it already exists. + /// + /// The temporary file already exists and cannot be deleted. + private void RemoveAlreadyExistingTemporaryFile() + { + if (File.Exists(temporaryFilePath)) + { + try + { + File.Delete(temporaryFilePath); + } + catch (Exception e) + { + if (e is ArgumentException || e is IOException || e is SystemException) + { + var message = string.Format( + Resources.SafeOverwriteFileHelper_RemoveAlreadyExistingTemporaryFile_Already_existing_temporary_file_at_FilePath_0_could_not_be_removed, + temporaryFilePath); + throw new IOException(message, e); + } + throw; + } + } + } + + /// + /// Creates a temporary file from the target file. + /// + /// The temporary file cannot be created from the target file. + private void CreateNewTemporaryFile() + { + try + { + File.Move(targetFilePath, temporaryFilePath); + } + catch (Exception e) + { + if (e is ArgumentException || e is IOException || e is SystemException) + { + var message = string.Format( + Resources.SafeOverwriteFileHelper_CreateNewTemporaryFile_Cannot_create_temporary_FilePath_0_Try_change_save_location, + targetFilePath); + throw new IOException(message, e); + } + throw; + } + } + + /// + /// Moves the temporary file back to the original path. If the operation fails, the temporary file + /// will remain. + /// + /// Thrown when either: + /// + /// The new file could not be deleted. + /// The temporary file could not be moved to its original place. + /// + private void RestoreOriginalFile() + { + try + { + File.Delete(targetFilePath); + File.Move(temporaryFilePath, targetFilePath); + } + catch (Exception e) + { + if (e is ArgumentException || e is IOException || e is SystemException) + { + var message = string.Format( + Resources.SafeOverwriteFileHelper_RestoreOriginalFile_Cannot_revert_to_original_FilePath_0_Try_reverting_manually, + targetFilePath); + throw new IOException(message, e); + } + throw; + } + } + + /// + /// Deletes the created temporary file. + /// + /// The temporary file cannot be removed. + private void DeleteTemporaryFile() + { + try + { + File.Delete(temporaryFilePath); + } + catch (Exception e) + { + if (e is ArgumentException || e is IOException || e is SystemException) + { + var message = string.Format( + Resources.SafeOverwriteFileHelper_DeleteTemporaryFile_Cannot_remove_temporary_FilePath_0_Try_removing_manually, + temporaryFilePath); + throw new IOException(message, e); + } + throw; + } + } + } +} \ No newline at end of file Index: Application/Ringtoets/src/Application.Ringtoets.Storage/Properties/Resources.Designer.cs =================================================================== diff -u -r669853e6468fcf1301a64f5df1400456fe6929eb -r3456261423844a9f804b352472ac08feb22485ac --- Application/Ringtoets/src/Application.Ringtoets.Storage/Properties/Resources.Designer.cs (.../Resources.Designer.cs) (revision 669853e6468fcf1301a64f5df1400456fe6929eb) +++ Application/Ringtoets/src/Application.Ringtoets.Storage/Properties/Resources.Designer.cs (.../Resources.Designer.cs) (revision 3456261423844a9f804b352472ac08feb22485ac) @@ -63,7 +63,7 @@ /// /// Looks up a localized string similar to /* ---------------------------------------------------- */ ////* Generated by Enterprise Architect Version 12.0 */ - ////* Created On : 19-jul-2016 13:22:41 */ + ////* Created On : 21-jul-2016 11:30:42 */ ////* DBMS : SQLite */ ////* ---------------------------------------------------- */ /// @@ -135,79 +135,52 @@ } /// - /// Looks up a localized string similar to Kan geen nieuw doelbestand maken ({0}). Probeer ergens anders op te slaan.. + /// Looks up a localized string similar to Kan geen tijdelijk bestand maken van het originele bestand ({0}).. /// - internal static string SafeOverwriteFileHelper_CreateNewTargetFile_Cannot_create_target_file_0_Try_change_save_location { + internal static string SafeOverwriteFileHelper_CreateNewTemporaryFile_Cannot_create_temporary_FilePath_0_Try_change_save_location { get { - return ResourceManager.GetString("SafeOverwriteFileHelper_CreateNewTargetFile_Cannot_create_target_file_0_Try_chang" + - "e_save_location", resourceCulture); + return ResourceManager.GetString("SafeOverwriteFileHelper_CreateNewTemporaryFile_Cannot_create_temporary_FilePath_0" + + "_Try_change_save_location", resourceCulture); } } /// - /// Looks up a localized string similar to Kan geen tijdelijk bestand maken van het originele bestand ({0}). Probeer ergens anders op te slaan.. + /// Looks up a localized string similar to Kan het tijdelijke bestand ({0}) niet opruimen. Het tijdelijke bestand dient handmatig verwijderd te worden.. /// - internal static string SafeOverwriteFileHelper_CreateTemporaryFile_Cannot_create_temporary_file_0_Try_change_save_location { + internal static string SafeOverwriteFileHelper_DeleteTemporaryFile_Cannot_remove_temporary_FilePath_0_Try_removing_manually { get { - return ResourceManager.GetString("SafeOverwriteFileHelper_CreateTemporaryFile_Cannot_create_temporary_file_0_Try_ch" + - "ange_save_location", resourceCulture); + return ResourceManager.GetString("SafeOverwriteFileHelper_DeleteTemporaryFile_Cannot_remove_temporary_FilePath_0_Tr" + + "y_removing_manually", resourceCulture); } } /// - /// Looks up a localized string similar to Kan het tijdelijke bestand ({0}) niet opruimen. Het tijdelijke bestand dient handmatig verwijderd te worden.. + /// Looks up a localized string similar to Er bestaat al een tijdelijk bestand ({0}) dat niet verwijderd kan worden. Het bestaande tijdelijke bestand dient handmatig verwijderd te worden.. /// - internal static string SafeOverwriteFileHelper_CreateTemporaryFile_Cannot_remove_temporary_file_0_Try_removing_manually { + internal static string SafeOverwriteFileHelper_RemoveAlreadyExistingTemporaryFile_Already_existing_temporary_file_at_FilePath_0_could_not_be_removed { get { - return ResourceManager.GetString("SafeOverwriteFileHelper_CreateTemporaryFile_Cannot_remove_temporary_file_0_Try_re" + - "moving_manually", resourceCulture); + return ResourceManager.GetString("SafeOverwriteFileHelper_RemoveAlreadyExistingTemporaryFile_Already_existing_tempo" + + "rary_file_at_FilePath_0_could_not_be_removed", resourceCulture); } } /// /// Looks up a localized string similar to Kan het originele bestand ({0}) niet herstellen. Het tijdelijke bestand dient handmatig hersteld worden.. /// - internal static string SafeOverwriteFileHelper_CreateTemporaryFile_Cannot_rever_to_original_file_0_Try_removing_manually { + internal static string SafeOverwriteFileHelper_RestoreOriginalFile_Cannot_revert_to_original_FilePath_0_Try_reverting_manually { get { - return ResourceManager.GetString("SafeOverwriteFileHelper_CreateTemporaryFile_Cannot_rever_to_original_file_0_Try_r" + - "emoving_manually", resourceCulture); + return ResourceManager.GetString("SafeOverwriteFileHelper_RestoreOriginalFile_Cannot_revert_to_original_FilePath_0_" + + "Try_reverting_manually", resourceCulture); } } /// - /// Looks up a localized string similar to Kon het bestand '{0}' niet aanmaken op de aangewezen locatie.. - /// - internal static string StorageSqLite_CreateNewFile_Could_not_create_file_at_path_0 { - get { - return ResourceManager.GetString("StorageSqLite_CreateNewFile_Could_not_create_file_at_path_0", resourceCulture); - } - } - - /// /// Looks up a localized string similar to Het bestand is geen geldig Ringtoets bestand.. /// internal static string StorageSqLite_LoadProject_Invalid_Ringtoets_database_file { get { return ResourceManager.GetString("StorageSqLite_LoadProject_Invalid_Ringtoets_database_file", resourceCulture); } } - - /// - /// Looks up a localized string similar to Kon het bestand '{0}' niet verplaatsen naar '{1}'.. - /// - internal static string StorageSqLite_MoveTargetToTemporaryFile_Could_not_move_file_0_to_1 { - get { - return ResourceManager.GetString("StorageSqLite_MoveTargetToTemporaryFile_Could_not_move_file_0_to_1", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Kon het reeds bestaande tijdelijk bestand '{0}' niet verwijderen.. - /// - internal static string StorageSqLite_TryDeleteExistingTemporaryFile_Could_not_delete_existing_file_0 { - get { - return ResourceManager.GetString("StorageSqLite_TryDeleteExistingTemporaryFile_Could_not_delete_existing_file_0", resourceCulture); - } - } } } Index: Application/Ringtoets/src/Application.Ringtoets.Storage/Properties/Resources.resx =================================================================== diff -u -r669853e6468fcf1301a64f5df1400456fe6929eb -r3456261423844a9f804b352472ac08feb22485ac --- Application/Ringtoets/src/Application.Ringtoets.Storage/Properties/Resources.resx (.../Resources.resx) (revision 669853e6468fcf1301a64f5df1400456fe6929eb) +++ Application/Ringtoets/src/Application.Ringtoets.Storage/Properties/Resources.resx (.../Resources.resx) (revision 3456261423844a9f804b352472ac08feb22485ac) @@ -139,25 +139,16 @@ Het bestand is geen geldig Ringtoets bestand. - - Kon het reeds bestaande tijdelijk bestand '{0}' niet verwijderen. + + Kan geen tijdelijk bestand maken van het originele bestand ({0}). - - Kon het bestand '{0}' niet aanmaken op de aangewezen locatie. - - - Kon het bestand '{0}' niet verplaatsen naar '{1}'. - - - Kan geen nieuw doelbestand maken ({0}). Probeer ergens anders op te slaan. - - - Kan geen tijdelijk bestand maken van het originele bestand ({0}). Probeer ergens anders op te slaan. - - + Kan het originele bestand ({0}) niet herstellen. Het tijdelijke bestand dient handmatig hersteld worden. - + Kan het tijdelijke bestand ({0}) niet opruimen. Het tijdelijke bestand dient handmatig verwijderd te worden. + + Er bestaat al een tijdelijk bestand ({0}) dat niet verwijderd kan worden. Het bestaande tijdelijke bestand dient handmatig verwijderd te worden. + \ No newline at end of file Fisheye: Tag 3456261423844a9f804b352472ac08feb22485ac refers to a dead (removed) revision in file `Application/Ringtoets/src/Application.Ringtoets.Storage/SafeOverwriteFileHelper.cs'. Fisheye: No comparison available. Pass `N' to diff? Index: Application/Ringtoets/src/Application.Ringtoets.Storage/StorageSqLite.cs =================================================================== diff -u -r1c18575a6c396cd3c2d9d6079e4ed593871fc5f4 -r3456261423844a9f804b352472ac08feb22485ac --- Application/Ringtoets/src/Application.Ringtoets.Storage/StorageSqLite.cs (.../StorageSqLite.cs) (revision 1c18575a6c396cd3c2d9d6079e4ed593871fc5f4) +++ Application/Ringtoets/src/Application.Ringtoets.Storage/StorageSqLite.cs (.../StorageSqLite.cs) (revision 3456261423844a9f804b352472ac08feb22485ac) @@ -61,14 +61,12 @@ /// to save. /// Thrown when is null. /// is invalid. - /// No file is present at - /// at the time a connection is made. /// Thrown when /// - /// The database does not contain the table version - /// The file was not created. - /// Saving the to the database failed. + /// No backup file could be created or restored when overwriting an existing file. + /// The file at cannot be created. /// The connection to the database file failed. + /// Saving the to the database failed. /// /// public void SaveProjectAs(string databaseFilePath, IProject project) @@ -78,20 +76,19 @@ throw new ArgumentNullException("project"); } - SafeOverwriteFileHelper overwriteHelper = GetSafeOverwriteFileHelper(databaseFilePath); - if (overwriteHelper != null) + try { - try + BackedUpFileWriter writer = new BackedUpFileWriter(databaseFilePath); + writer.Perform(() => { SetConnectionToNewFile(databaseFilePath); SaveProjectInDatabase(databaseFilePath, (RingtoetsProject) project); - } - catch - { - CleanUpTemporaryFile(overwriteHelper, true); - } - CleanUpTemporaryFile(overwriteHelper, false); + }); } + catch (IOException e) + { + throw new StorageException(e.Message, e); + } } /// @@ -218,46 +215,6 @@ } } - /// - /// Cleans up a new . - /// - /// The to use for cleaning up. - /// Value indicating whether the should revert to - /// original file or keep the new file. - /// A file IO operation fails while cleaning up using the - /// . - private void CleanUpTemporaryFile(SafeOverwriteFileHelper overwriteHelper, bool revert) - { - try - { - overwriteHelper.Finish(revert); - } - catch (IOException e) - { - throw new StorageException(e.Message, e); - } - } - - /// - /// Creates a new . - /// - /// The path for which to create the . - /// A new - /// is invalid. - /// A file IO operation fails while initializing the - /// . - private SafeOverwriteFileHelper GetSafeOverwriteFileHelper(string databaseFilePath) - { - try - { - return new SafeOverwriteFileHelper(databaseFilePath); - } - catch (IOException e) - { - throw new StorageException(e.Message, e); - } - } - private void SaveProjectInDatabase(string databaseFilePath, RingtoetsProject project) { using (var dbContext = new RingtoetsEntities(connectionString)) @@ -337,16 +294,15 @@ /// Sets the connection to a newly created (empty) Ringtoets database file. /// /// Path to database file. - /// is invalid. - /// No file is present at - /// at the time a connection is made. + /// Thrown when: + /// + /// is invalid + /// points to an existing file + /// /// Thrown when: - /// was not created /// executing DatabaseStructure script failed /// /// - /// Thrown when the could not - /// be overwritten. private void SetConnectionToNewFile(string databaseFilePath) { FileUtils.ValidateFilePath(databaseFilePath); Index: Application/Ringtoets/src/Application.Ringtoets.Storage/StorageSqliteCreator.cs =================================================================== diff -u -r233ccdab7d2f44be39fd95e1f7617f4c72ee0df1 -r3456261423844a9f804b352472ac08feb22485ac --- Application/Ringtoets/src/Application.Ringtoets.Storage/StorageSqliteCreator.cs (.../StorageSqliteCreator.cs) (revision 233ccdab7d2f44be39fd95e1f7617f4c72ee0df1) +++ Application/Ringtoets/src/Application.Ringtoets.Storage/StorageSqliteCreator.cs (.../StorageSqliteCreator.cs) (revision 3456261423844a9f804b352472ac08feb22485ac) @@ -19,6 +19,7 @@ // Stichting Deltares and remain full property of Stichting Deltares at all times. // All rights reserved. +using System; using System.Data.SQLite; using System.IO; using Application.Ringtoets.Storage.Properties; @@ -34,19 +35,27 @@ public static class StorageSqliteCreator { /// - /// Creates the basic database structure for a Ringtoets database file. + /// Creates a new file with the basic database structure for a Ringtoets database at + /// . /// - /// Path to database file. - /// is invalid. + /// Path of the new database file. + /// Thrown when either: + /// + /// is invalid + /// points to an existing file + /// /// Thrown when executing DatabaseStructure script fails. public static void CreateDatabaseStructure(string databaseFilePath) { FileUtils.ValidateFilePath(databaseFilePath); - if (!File.Exists(databaseFilePath)) + if (File.Exists(databaseFilePath)) { - SQLiteConnection.CreateFile(databaseFilePath); + var message = string.Format("File '{0}' already exists.", databaseFilePath); + throw new ArgumentException(message); } + + SQLiteConnection.CreateFile(databaseFilePath); var connectionString = SqLiteConnectionStringBuilder.BuildSqLiteConnectionString(databaseFilePath); try { Index: Application/Ringtoets/test/Application.Ringtoets.Storage.Test/Application.Ringtoets.Storage.Test.csproj =================================================================== diff -u -r3ddadfbc5e096c8df7d709be0d9a9057cf4b8dd6 -r3456261423844a9f804b352472ac08feb22485ac --- Application/Ringtoets/test/Application.Ringtoets.Storage.Test/Application.Ringtoets.Storage.Test.csproj (.../Application.Ringtoets.Storage.Test.csproj) (revision 3ddadfbc5e096c8df7d709be0d9a9057cf4b8dd6) +++ Application/Ringtoets/test/Application.Ringtoets.Storage.Test/Application.Ringtoets.Storage.Test.csproj (.../Application.Ringtoets.Storage.Test.csproj) (revision 3456261423844a9f804b352472ac08feb22485ac) @@ -193,7 +193,7 @@ - + Index: Application/Ringtoets/test/Application.Ringtoets.Storage.Test/BackedUpFileWriterTest.cs =================================================================== diff -u --- Application/Ringtoets/test/Application.Ringtoets.Storage.Test/BackedUpFileWriterTest.cs (revision 0) +++ Application/Ringtoets/test/Application.Ringtoets.Storage.Test/BackedUpFileWriterTest.cs (revision 3456261423844a9f804b352472ac08feb22485ac) @@ -0,0 +1,399 @@ +// Copyright (C) Stichting Deltares 2016. All rights reserved. +// +// This file is part of Ringtoets. +// +// Ringtoets is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// All names, logos, and references to "Deltares" are registered trademarks of +// Stichting Deltares and remain full property of Stichting Deltares at all times. +// All rights reserved. + +using System; +using System.IO; +using System.Security.AccessControl; +using System.Security.Principal; +using NUnit.Framework; + +namespace Application.Ringtoets.Storage.Test +{ + [TestFixture] + public class BackedUpFileWriterTest + { + private string testWorkDir = Path.Combine(".", "SafeOverwriteFileHelperTest"); + + [TestFixtureSetUp] + public void SetUpFixture() + { + Directory.CreateDirectory(testWorkDir); + } + + [TestFixtureTearDown] + public void TearDownFixture() + { + Directory.Delete(testWorkDir); + } + + [Test] + [TestCase("")] + [TestCase(null)] + [TestCase("\"")] + [TestCase("/")] + public void Constructor_InvalidPath_ThrowsArgumentException(string path) + { + // Call + TestDelegate test = () => new BackedUpFileWriter(path); + + // Assert + Assert.Throws(test); + } + + [Test] + public void Perform_ValidFile_TemporaryFileCreatedFromOriginalAndDeletedAfterwards() + { + // Setup + var writableDirectory = Path.Combine(testWorkDir, "Writable"); + var filePath = Path.Combine(writableDirectory, "iDoExist.txt"); + var temporaryFilePath = filePath + "~"; + var testContent = "Some test text to write into file."; + + Directory.CreateDirectory(writableDirectory); + File.WriteAllText(filePath, testContent); + + var writer = new BackedUpFileWriter(filePath); + + try + { + // Precondition + Assert.IsTrue(File.Exists(filePath)); + + // Call + writer.Perform(() => + { + // Assert + Assert.IsFalse(File.Exists(filePath)); + Assert.IsTrue(File.Exists(temporaryFilePath)); + Assert.AreEqual(testContent, File.ReadAllText(temporaryFilePath)); + }); + Assert.False(File.Exists(temporaryFilePath)); + } + finally + { + Directory.Delete(writableDirectory, true); + } + } + + [Test] + public void Perform_ActionThrowsExceptionValidPathFileExists_OriginalFileRevertedAndExceptionThrown() + { + // Setup + var writableDirectory = Path.Combine(testWorkDir, "Writable"); + var filePath = Path.Combine(writableDirectory, "iDoExist.txt"); + var temporaryFilePath = filePath + "~"; + var testContent = "Some test text to write into file."; + + Directory.CreateDirectory(writableDirectory); + File.WriteAllText(filePath, testContent); + + var writer = new BackedUpFileWriter(filePath); + var exception = new IOException(); + + // Precondition + Assert.IsTrue(File.Exists(filePath)); + + try + { + // Call + TestDelegate test = () => writer.Perform(() => + { + throw exception; + }); + + var actualException = Assert.Throws(exception.GetType(), test); + Assert.AreSame(exception, actualException); + + Assert.IsFalse(File.Exists(temporaryFilePath)); + Assert.IsTrue(File.Exists(filePath)); + Assert.AreEqual(testContent, File.ReadAllText(filePath)); + } + finally + { + Directory.Delete(writableDirectory, true); + } + } + + [Test] + public void Perform_ValidPathFileExistsTemporaryFileExistsAndCannotBeDeleted_ThrowsIOException() + { + // Setup + var filePath = Path.Combine(testWorkDir, "iDoExist.txt"); + var temporaryFilePath = filePath + "~"; + + using (File.Create(filePath)) { } + var temporaryFileStream = File.Create(temporaryFilePath); + + var writer = new BackedUpFileWriter(filePath); + + // Precondition + Assert.IsTrue(File.Exists(filePath)); + Assert.IsTrue(File.Exists(temporaryFilePath)); + + // Call + TestDelegate test = () => + { + writer.Perform(() => { }); + }; + + try + { + // Assert + var message = Assert.Throws(test).Message; + var expectedMessage = string.Format( + "Er bestaat al een tijdelijk bestand ({0}) dat niet verwijderd kan worden. Het bestaande tijdelijke bestand dient handmatig verwijderd te worden.", + temporaryFilePath); + Assert.AreEqual(message, expectedMessage); + temporaryFileStream.Dispose(); + } + finally + { + File.Delete(filePath); + File.Delete(temporaryFilePath); + } + } + + [Test] + public void Perform_ValidPathFileExistsDirectoryNotWritable_ThrowsIOException() + { + // Setup + var notWritableDirectory = Path.Combine(testWorkDir, "NotWritable"); + var filePath = Path.Combine(notWritableDirectory, "iDoExist.txt"); + + Directory.CreateDirectory(notWritableDirectory); + using (File.Create(filePath)) {} + DenyDirectoryRight(notWritableDirectory, FileSystemRights.Write); + + var writer = new BackedUpFileWriter(filePath); + + // Precondition + Assert.IsTrue(File.Exists(filePath)); + + // Call + TestDelegate test = () => + { + writer.Perform(() => { }); + }; + + try + { + // Assert + var expectedMessage = string.Format( + "Kan geen tijdelijk bestand maken van het originele bestand ({0}).", + filePath); + var message = Assert.Throws(test).Message; + Assert.AreEqual(expectedMessage, message); + } + finally + { + Directory.Delete(notWritableDirectory, true); + } + } + + [Test] + public void Perform_TargetFileDoesNotExistAccessRightsRevoked_DoesNotThrow() + { + // Setup + var noAccessDirectory = Path.Combine(testWorkDir, "NoAccess"); + var filePath = Path.Combine(noAccessDirectory, "iDoNotExist.txt"); + + Directory.CreateDirectory(noAccessDirectory); + var helper = new BackedUpFileWriter(filePath); + + var right = FileSystemRights.FullControl; + DenyDirectoryRight(noAccessDirectory, right); + + // Call + TestDelegate test = () => helper.Perform(() => { }); + + try + { + // Assert + Assert.DoesNotThrow(test); + } + finally + { + RevertDenyDirectoryRight(noAccessDirectory, right); + Directory.Delete(noAccessDirectory, true); + } + } + + [Test] + public void Perform_TargetFileExistsCannotDeleteFile_ThrowsIOException() + { + // Setup + var noAccessDirectory = Path.Combine(testWorkDir, "Access"); + var filePath = Path.Combine(noAccessDirectory, "iDoExist.txt"); + var temporaryFilePath = filePath + "~"; + + Directory.CreateDirectory(noAccessDirectory); + using (File.Create(filePath)) {} + + var helper = new BackedUpFileWriter(filePath); + + FileStream fileStream = null; + + try + { + // Call + TestDelegate test = () => helper.Perform(() => + { + fileStream = File.Open(temporaryFilePath, FileMode.Open); + }); + + // Assert + var expectedMessage = string.Format( + "Kan het tijdelijke bestand ({0}) niet opruimen. Het tijdelijke bestand dient handmatig verwijderd te worden.", + temporaryFilePath); + var message = Assert.Throws(test).Message; + Assert.AreEqual(expectedMessage, message); + } + finally + { + if (fileStream != null) + { + fileStream.Dispose(); + } + Directory.Delete(noAccessDirectory, true); + } + } + + [Test] + public void Perform_ActionThrowsExceptionTargetFileExistsCannotDeleteFile_ThrowsIOException() + { + // Setup + var noAccessDirectory = Path.Combine(testWorkDir, "Access"); + var filePath = Path.Combine(noAccessDirectory, "iDoExist.txt"); + var temporaryFilePath = filePath + "~"; + + Directory.CreateDirectory(noAccessDirectory); + using (File.Create(filePath)) {} + + var helper = new BackedUpFileWriter(filePath); + + FileStream fileStream = null; + + try + { + // Call + TestDelegate test = () => helper.Perform(() => + { + fileStream = File.Open(temporaryFilePath, FileMode.Open); + throw new Exception(); + }); + + // Assert + var expectedMessage = string.Format( + "Kan het originele bestand ({0}) niet herstellen. Het tijdelijke bestand dient handmatig hersteld worden.", + filePath); + var message = Assert.Throws(test).Message; + Assert.AreEqual(expectedMessage, message); + } + finally + { + if (fileStream != null) + { + fileStream.Dispose(); + } + Directory.Delete(noAccessDirectory, true); + } + } + + [Test] + public void Perform_ActionThrowsExceptionTargetFileFileExistsCannotMoveFile_ThrowsIOException() + { + // Setup + var noAccessDirectory = Path.Combine(testWorkDir, "NoAccess"); + var filePath = Path.Combine(noAccessDirectory, "iDoExist.txt"); + + Directory.CreateDirectory(noAccessDirectory); + using (File.Create(filePath)) {} + + var helper = new BackedUpFileWriter(filePath); + + var right = FileSystemRights.FullControl; + + try + { + // Call + TestDelegate test = () => helper.Perform(() => + { + DenyDirectoryRight(noAccessDirectory, right); + throw new Exception(); + }); + + // Assert + var expectedMessage = string.Format( + "Kan het originele bestand ({0}) niet herstellen. Het tijdelijke bestand dient handmatig hersteld worden.", + filePath); + var message = Assert.Throws(test).Message; + Assert.AreEqual(expectedMessage, message); + } + finally + { + RevertDenyDirectoryRight(noAccessDirectory, right); + Directory.Delete(noAccessDirectory, true); + } + } + + /// + /// Adds a of type to the access + /// rule set for the file at . + /// + /// The path of the file to change the right for. + /// The right to deny. + private static void DenyDirectoryRight(string filePath, FileSystemRights rights) + { + var sid = GetSecurityIdentifier(); + + DirectoryInfo directoryInfo = new DirectoryInfo(filePath); + DirectorySecurity directorySecurity = directoryInfo.GetAccessControl(); + + directorySecurity.AddAccessRule(new FileSystemAccessRule(sid, rights, AccessControlType.Deny)); + + directoryInfo.SetAccessControl(directorySecurity); + } + + /// + /// Removes a of type from the + /// access rule set for the file at . + /// + /// The path of the file to change the right for. + /// The right to revert. + private static void RevertDenyDirectoryRight(string filePath, FileSystemRights rights) + { + var sid = GetSecurityIdentifier(); + + DirectoryInfo directoryInfo = new DirectoryInfo(filePath); + DirectorySecurity directorySecurity = directoryInfo.GetAccessControl(); + + directorySecurity.RemoveAccessRule(new FileSystemAccessRule(sid, rights, AccessControlType.Deny)); + + directoryInfo.SetAccessControl(directorySecurity); + } + + private static SecurityIdentifier GetSecurityIdentifier() + { + SecurityIdentifier id = WindowsIdentity.GetCurrent().User.AccountDomainSid; + return new SecurityIdentifier(WellKnownSidType.WorldSid, id); + } + } +} \ No newline at end of file Fisheye: Tag 3456261423844a9f804b352472ac08feb22485ac refers to a dead (removed) revision in file `Application/Ringtoets/test/Application.Ringtoets.Storage.Test/SafeOverwriteFileHelperTest.cs'. Fisheye: No comparison available. Pass `N' to diff? Index: Application/Ringtoets/test/Application.Ringtoets.Storage.Test/StorageSqLiteTest.cs =================================================================== diff -u -r1c18575a6c396cd3c2d9d6079e4ed593871fc5f4 -r3456261423844a9f804b352472ac08feb22485ac --- Application/Ringtoets/test/Application.Ringtoets.Storage.Test/StorageSqLiteTest.cs (.../StorageSqLiteTest.cs) (revision 1c18575a6c396cd3c2d9d6079e4ed593871fc5f4) +++ Application/Ringtoets/test/Application.Ringtoets.Storage.Test/StorageSqLiteTest.cs (.../StorageSqLiteTest.cs) (revision 3456261423844a9f804b352472ac08feb22485ac) @@ -291,7 +291,7 @@ { // Setup var expectedMessage = string.Format( - @"Kan geen tijdelijk bestand maken van het originele bestand ({0}). Probeer ergens anders op te slaan.", + @"Kan geen tijdelijk bestand maken van het originele bestand ({0}).", tempRingtoetsFile); var project = new RingtoetsProject(); var storage = new StorageSqLite(); Index: Application/Ringtoets/test/Application.Ringtoets.Storage.Test/StorageSqliteCreatorTest.cs =================================================================== diff -u -r8293556a04e3650c9978df7a50f8f8ad0d792ed8 -r3456261423844a9f804b352472ac08feb22485ac --- Application/Ringtoets/test/Application.Ringtoets.Storage.Test/StorageSqliteCreatorTest.cs (.../StorageSqliteCreatorTest.cs) (revision 8293556a04e3650c9978df7a50f8f8ad0d792ed8) +++ Application/Ringtoets/test/Application.Ringtoets.Storage.Test/StorageSqliteCreatorTest.cs (.../StorageSqliteCreatorTest.cs) (revision 3456261423844a9f804b352472ac08feb22485ac) @@ -21,7 +21,6 @@ using System; using System.IO; -using Core.Common.Base.Storage; using Core.Common.TestUtil; using NUnit.Framework; @@ -56,8 +55,8 @@ // Precondition Assert.IsFalse(File.Exists(fullPath)); - - using (new FileDisposeHelper(fullPath)) + + try { // Call TestDelegate call = () => StorageSqliteCreator.CreateDatabaseStructure(fullPath); @@ -67,6 +66,13 @@ Assert.IsTrue(File.Exists(fullPath)); } + finally + { + if (File.Exists(fullPath)) + { + File.Delete(fullPath); + } + } } [Test] @@ -80,7 +86,7 @@ // Precondition Assert.IsFalse(File.Exists(fullPath)); - using (new FileDisposeHelper(fullPath)) + try { // Call TestDelegate call = () => StorageSqliteCreator.CreateDatabaseStructure(uncPath); @@ -90,26 +96,31 @@ Assert.IsTrue(File.Exists(fullPath)); } + finally + { + if (File.Exists(fullPath)) + { + File.Delete(fullPath); + } + } } [Test] - public void CreateDatabaseStructure_ValidPathToLockedFile_ThrowsUpdateStorageException() + public void CreateDatabaseStructure_ValidExistingFile_ThrowsStorageException() { using (new FileDisposeHelper(tempRingtoetsFile)) { // Call TestDelegate call = () => StorageSqliteCreator.CreateDatabaseStructure(tempRingtoetsFile); - StorageException exception; + ArgumentException exception; using (File.Create(tempRingtoetsFile)) // Locks file { - exception = Assert.Throws(call); + exception = Assert.Throws(call); } // Assert - Assert.IsInstanceOf(exception.InnerException); - var expectedMessage = String.Format(@"Fout bij het schrijven naar bestand '{0}': " + - "Een fout is opgetreden met schrijven naar het nieuwe Ringtoets bestand.", tempRingtoetsFile); + var expectedMessage = String.Format(@"File '{0}' already exists.", tempRingtoetsFile); Assert.AreEqual(expectedMessage, exception.Message); } }