Index: src/Plugins/Wti/Wti.IO/PipingSurfaceLinesCsvReader.cs =================================================================== diff -u -rf2284364cde43ab7969dd8f0fb038f846138ec41 -recfa21abcbbd04db3963323e308dd0b94c8cb7a0 --- src/Plugins/Wti/Wti.IO/PipingSurfaceLinesCsvReader.cs (.../PipingSurfaceLinesCsvReader.cs) (revision f2284364cde43ab7969dd8f0fb038f846138ec41) +++ src/Plugins/Wti/Wti.IO/PipingSurfaceLinesCsvReader.cs (.../PipingSurfaceLinesCsvReader.cs) (revision ecfa21abcbbd04db3963323e308dd0b94c8cb7a0) @@ -19,12 +19,23 @@ /// public class PipingSurfaceLinesCsvReader : IDisposable { - private readonly string[] acceptableIdNames = + private const char separator = ';'; + + private readonly string[] acceptableLowerCaseIdNames = { - "Profielnaam, LocationID" + "profielnaam", + "locationid" }; private readonly string filePath; + + private readonly string[] expectedFirstCoordinateHeader = + { + "x1", + "y1", + "z1" + }; + private StreamReader fileReader; /// @@ -52,9 +63,9 @@ /// Some other I/O related issue occurred, such as: path includes an incorrect /// or invalid syntax for file name, directory name, or volume label. /// There is insufficient memory to allocate a buffer for the returned string. + /// File incompatible for importing surface lines. /// /// - /// The file in not properly formatted. public int GetSurfaceLinesCount() { int count = 0, lineNumber = 0; @@ -63,9 +74,7 @@ { reader = new StreamReader(filePath); - // Skip header: - // TODO: Make sure header is valid - reader.ReadLine(); + ValidateHeader(reader); lineNumber++; // Count SurfaceLines: @@ -131,7 +140,7 @@ var readText = fileReader.ReadLine(); if (readText != null) { - var tokenizedString = readText.Split(';'); + var tokenizedString = readText.Split(separator); var worldCoordinateValues = tokenizedString.Skip(1) .Select(ts => Double.Parse(ts, CultureInfo.InvariantCulture)) .ToArray(); @@ -167,6 +176,43 @@ } } + private void ValidateHeader(StreamReader reader) + { + var header = reader.ReadLine(); + if (header != null) + { + if (!IsHeaderValid(header)) + { + var expectedMessage = string.Format(Resources.PipingSurfaceLinesCsvReader_File_0_invalid_header, filePath); + throw new CriticalFileReadException(expectedMessage); + } + } + else + { + var expectedMessage = string.Format(Resources.Error_File_0_empty, filePath); + throw new CriticalFileReadException(expectedMessage); + } + } + + private bool IsHeaderValid(string header) + { + var tokenizedHeader = header.Split(separator).Select(s => s.Trim().ToLowerInvariant()).ToArray(); + + // Check for valid id: + if (!acceptableLowerCaseIdNames.Contains(tokenizedHeader[0])) + { + return false; + } + + // CHeck for valid 1st coordinate in header: + bool valid = true; + for (int i = 0; i < expectedFirstCoordinateHeader.Length && valid; i++) + { + valid = tokenizedHeader[1 + i].Equals(expectedFirstCoordinateHeader[i]); + } + return valid; + } + private void CheckIfPathIsValid(string path) { if (string.IsNullOrWhiteSpace(path)) Index: src/Plugins/Wti/Wti.IO/Properties/Resources.Designer.cs =================================================================== diff -u -rf2284364cde43ab7969dd8f0fb038f846138ec41 -recfa21abcbbd04db3963323e308dd0b94c8cb7a0 --- src/Plugins/Wti/Wti.IO/Properties/Resources.Designer.cs (.../Resources.Designer.cs) (revision f2284364cde43ab7969dd8f0fb038f846138ec41) +++ src/Plugins/Wti/Wti.IO/Properties/Resources.Designer.cs (.../Resources.Designer.cs) (revision ecfa21abcbbd04db3963323e308dd0b94c8cb7a0) @@ -88,6 +88,15 @@ } /// + /// Looks up a localized string similar to Het bestand op '{0}' is leeg.. + /// + public static string Error_File_0_empty { + get { + return ResourceManager.GetString("Error_File_0_empty", resourceCulture); + } + } + + /// /// Looks up a localized string similar to Er is een onverwachte inleesfout opgetreden tijdens het lezen van het bestand '{0}': {1}. /// public static string Error_General_IO_File_0_ErrorMessage_1_ { @@ -131,5 +140,14 @@ return ResourceManager.GetString("Error_Unexpected_IOError_File_0_Line_1_", resourceCulture); } } + + /// + /// Looks up a localized string similar to Het bestand op '{0}' is niet geschikt om dwarsdoorsneden uit te lezen (Verwachte header: locationid;X1;Y1;Z1).. + /// + public static string PipingSurfaceLinesCsvReader_File_0_invalid_header { + get { + return ResourceManager.GetString("PipingSurfaceLinesCsvReader_File_0_invalid_header", resourceCulture); + } + } } } Index: src/Plugins/Wti/Wti.IO/Properties/Resources.resx =================================================================== diff -u -rf2284364cde43ab7969dd8f0fb038f846138ec41 -recfa21abcbbd04db3963323e308dd0b94c8cb7a0 --- src/Plugins/Wti/Wti.IO/Properties/Resources.resx (.../Resources.resx) (revision f2284364cde43ab7969dd8f0fb038f846138ec41) +++ src/Plugins/Wti/Wti.IO/Properties/Resources.resx (.../Resources.resx) (revision ecfa21abcbbd04db3963323e308dd0b94c8cb7a0) @@ -141,4 +141,10 @@ Er is een onverwachte inleesfout opgetreden tijdens het lezen van het bestand '{0}': {1} + + Het bestand op '{0}' is leeg. + + + Het bestand op '{0}' is niet geschikt om dwarsdoorsneden uit te lezen (Verwachte header: locationid;X1;Y1;Z1). + \ No newline at end of file Index: test-data/Plugins/Wti/IO/PipingSurfaceLinesCsvReader/InvalidHeader_LacksX1.csv =================================================================== diff -u --- test-data/Plugins/Wti/IO/PipingSurfaceLinesCsvReader/InvalidHeader_LacksX1.csv (revision 0) +++ test-data/Plugins/Wti/IO/PipingSurfaceLinesCsvReader/InvalidHeader_LacksX1.csv (revision ecfa21abcbbd04db3963323e308dd0b94c8cb7a0) @@ -0,0 +1,3 @@ +Profielnaam;Y1;Z1;...;Xn;Yn;Zn +Rotterdam1;94263.0026213;427776.654093;-1.02;94275.9126686;427811.080886;-1.04;94284.0663827;427831.918156;1.25;94294.9380015;427858.191234;1.45;94305.3566362;427889.900123;1.65;94315.0957947;427913.908281;1.66;94325.0614453;427941.766804;1.55;94331.1767309;427960.112661;1.44 +ArtificalLocal;2.3;0;1.0;5.7;0;2.0;4.4;0;1.1 \ No newline at end of file Index: test-data/Plugins/Wti/IO/PipingSurfaceLinesCsvReader/InvalidHeader_LacksY1.csv =================================================================== diff -u --- test-data/Plugins/Wti/IO/PipingSurfaceLinesCsvReader/InvalidHeader_LacksY1.csv (revision 0) +++ test-data/Plugins/Wti/IO/PipingSurfaceLinesCsvReader/InvalidHeader_LacksY1.csv (revision ecfa21abcbbd04db3963323e308dd0b94c8cb7a0) @@ -0,0 +1,3 @@ +Profielnaam;X1;Z1;...;Xn;Yn;Zn +Rotterdam1;94263.0026213;427776.654093;-1.02;94275.9126686;427811.080886;-1.04;94284.0663827;427831.918156;1.25;94294.9380015;427858.191234;1.45;94305.3566362;427889.900123;1.65;94315.0957947;427913.908281;1.66;94325.0614453;427941.766804;1.55;94331.1767309;427960.112661;1.44 +ArtificalLocal;2.3;0;1.0;5.7;0;2.0;4.4;0;1.1 \ No newline at end of file Index: test-data/Plugins/Wti/IO/PipingSurfaceLinesCsvReader/InvalidHeader_LacksZ1.csv =================================================================== diff -u --- test-data/Plugins/Wti/IO/PipingSurfaceLinesCsvReader/InvalidHeader_LacksZ1.csv (revision 0) +++ test-data/Plugins/Wti/IO/PipingSurfaceLinesCsvReader/InvalidHeader_LacksZ1.csv (revision ecfa21abcbbd04db3963323e308dd0b94c8cb7a0) @@ -0,0 +1,3 @@ +Profielnaam;X1;Y1;...;Xn;Yn;Zn +Rotterdam1;94263.0026213;427776.654093;-1.02;94275.9126686;427811.080886;-1.04;94284.0663827;427831.918156;1.25;94294.9380015;427858.191234;1.45;94305.3566362;427889.900123;1.65;94315.0957947;427913.908281;1.66;94325.0614453;427941.766804;1.55;94331.1767309;427960.112661;1.44 +ArtificalLocal;2.3;0;1.0;5.7;0;2.0;4.4;0;1.1 \ No newline at end of file Index: test-data/Plugins/Wti/IO/PipingSurfaceLinesCsvReader/InvalidHeader_UnsupportedId.csv =================================================================== diff -u --- test-data/Plugins/Wti/IO/PipingSurfaceLinesCsvReader/InvalidHeader_UnsupportedId.csv (revision 0) +++ test-data/Plugins/Wti/IO/PipingSurfaceLinesCsvReader/InvalidHeader_UnsupportedId.csv (revision ecfa21abcbbd04db3963323e308dd0b94c8cb7a0) @@ -0,0 +1,3 @@ +"I'm not a valid id";X1;Y1;Z1;...;Xn;Yn;Zn +Rotterdam1;94263.0026213;427776.654093;-1.02;94275.9126686;427811.080886;-1.04;94284.0663827;427831.918156;1.25;94294.9380015;427858.191234;1.45;94305.3566362;427889.900123;1.65;94315.0957947;427913.908281;1.66;94325.0614453;427941.766804;1.55;94331.1767309;427960.112661;1.44 +ArtificalLocal;2.3;0;1.0;5.7;0;2.0;4.4;0;1.1 \ No newline at end of file Index: test-data/Plugins/Wti/IO/PipingSurfaceLinesCsvReader/TwoValidSurfaceLines.csv =================================================================== diff -u -rc42d0c47753135357002db6026ebbefc3603a62b -recfa21abcbbd04db3963323e308dd0b94c8cb7a0 --- test-data/Plugins/Wti/IO/PipingSurfaceLinesCsvReader/TwoValidSurfaceLines.csv (.../TwoValidSurfaceLines.csv) (revision c42d0c47753135357002db6026ebbefc3603a62b) +++ test-data/Plugins/Wti/IO/PipingSurfaceLinesCsvReader/TwoValidSurfaceLines.csv (.../TwoValidSurfaceLines.csv) (revision ecfa21abcbbd04db3963323e308dd0b94c8cb7a0) @@ -1,3 +1,3 @@ -Profielnaam;X1;Y1;Z1...;Xn;Yn;Zn +Profielnaam;X1;Y1;Z1;...;Xn;Yn;Zn Rotterdam1;94263.0026213;427776.654093;-1.02;94275.9126686;427811.080886;-1.04;94284.0663827;427831.918156;1.25;94294.9380015;427858.191234;1.45;94305.3566362;427889.900123;1.65;94315.0957947;427913.908281;1.66;94325.0614453;427941.766804;1.55;94331.1767309;427960.112661;1.44 ArtificalLocal;2.3;0;1.0;5.7;0;2.0;4.4;0;1.1 \ No newline at end of file Index: test-data/Plugins/Wti/IO/PipingSurfaceLinesCsvReader/ValidFileWithoutSurfaceLines.csv =================================================================== diff -u -rc42d0c47753135357002db6026ebbefc3603a62b -recfa21abcbbd04db3963323e308dd0b94c8cb7a0 --- test-data/Plugins/Wti/IO/PipingSurfaceLinesCsvReader/ValidFileWithoutSurfaceLines.csv (.../ValidFileWithoutSurfaceLines.csv) (revision c42d0c47753135357002db6026ebbefc3603a62b) +++ test-data/Plugins/Wti/IO/PipingSurfaceLinesCsvReader/ValidFileWithoutSurfaceLines.csv (.../ValidFileWithoutSurfaceLines.csv) (revision ecfa21abcbbd04db3963323e308dd0b94c8cb7a0) @@ -1,2 +1,2 @@ -Profielnaam;X1;Y1;Z1...;Xn;Yn;Zn +Profielnaam;X1;Y1;Z1;...;Xn;Yn;Zn Index: test-data/Plugins/Wti/IO/PipingSurfaceLinesCsvReader/empty.csv =================================================================== diff -u --- test-data/Plugins/Wti/IO/PipingSurfaceLinesCsvReader/empty.csv (revision 0) +++ test-data/Plugins/Wti/IO/PipingSurfaceLinesCsvReader/empty.csv (revision ecfa21abcbbd04db3963323e308dd0b94c8cb7a0) @@ -0,0 +1 @@ \ No newline at end of file Index: test/Plugins/Wti/Wti.IO.Test/PipingSurfaceLinesCsvReaderTest.cs =================================================================== diff -u -rf2284364cde43ab7969dd8f0fb038f846138ec41 -recfa21abcbbd04db3963323e308dd0b94c8cb7a0 --- test/Plugins/Wti/Wti.IO.Test/PipingSurfaceLinesCsvReaderTest.cs (.../PipingSurfaceLinesCsvReaderTest.cs) (revision f2284364cde43ab7969dd8f0fb038f846138ec41) +++ test/Plugins/Wti/Wti.IO.Test/PipingSurfaceLinesCsvReaderTest.cs (.../PipingSurfaceLinesCsvReaderTest.cs) (revision ecfa21abcbbd04db3963323e308dd0b94c8cb7a0) @@ -191,6 +191,73 @@ } } + [Test] + public void GetSurfaceLinesCount_EmptyFile_ThrowCriticalFileReadException() + { + // Setup + string path = Path.Combine(testDataPath, "empty.csv"); + + // Precondition + Assert.IsTrue(File.Exists(path)); + + using (var reader = new PipingSurfaceLinesCsvReader(path)) + { + // Call + TestDelegate call = () => reader.GetSurfaceLinesCount(); + + // Assert + var exception = Assert.Throws(call); + var expectedMessage = string.Format(IOResources.Error_File_0_empty, path); + Assert.AreEqual(expectedMessage, exception.Message); + } + } + + [Test] + public void GetSurfaceLinesCount_InvalidHeader1_ThrowCriticalFileReadException() + { + // Setup + string path = Path.Combine(testDataPath, "InvalidHeader_UnsupportedId.csv"); + + // Precondition + Assert.IsTrue(File.Exists(path)); + + using (var reader = new PipingSurfaceLinesCsvReader(path)) + { + // Call + TestDelegate call = () => reader.GetSurfaceLinesCount(); + + // Assert + var exception = Assert.Throws(call); + var expectedMessage = string.Format(IOResources.PipingSurfaceLinesCsvReader_File_0_invalid_header, path); + Assert.AreEqual(expectedMessage, exception.Message); + } + } + + [Test] + [TestCase("X")] + [TestCase("Y")] + [TestCase("Z")] + public void GetSurfaceLinesCount_InvalidHeader2_ThrowCriticalFileReadException(string missingVariableName) + { + // Setup + var filename = string.Format("InvalidHeader_Lacks{0}1.csv", missingVariableName); + string path = Path.Combine(testDataPath, filename); + + // Precondition + Assert.IsTrue(File.Exists(path)); + + using (var reader = new PipingSurfaceLinesCsvReader(path)) + { + // Call + TestDelegate call = () => reader.GetSurfaceLinesCount(); + + // Assert + var exception = Assert.Throws(call); + var expectedMessage = string.Format("Het bestand op '{0}' is niet geschikt om dwarsdoorsneden uit te lezen (Verwachte header: locationid;X1;Y1;Z1).", path); + Assert.AreEqual(expectedMessage, exception.Message); + } + } + private void DoReadLine_OpenedValidFileWithHeaderAndTwoSurfaceLines_ReturnCreatedSurfaceLine() { // Setup Index: test/Plugins/Wti/Wti.Plugin.Test/FileImporter/PipingSurfaceLineCsvImporterTest.cs =================================================================== diff -u -rf2284364cde43ab7969dd8f0fb038f846138ec41 -recfa21abcbbd04db3963323e308dd0b94c8cb7a0 --- test/Plugins/Wti/Wti.Plugin.Test/FileImporter/PipingSurfaceLineCsvImporterTest.cs (.../PipingSurfaceLineCsvImporterTest.cs) (revision f2284364cde43ab7969dd8f0fb038f846138ec41) +++ test/Plugins/Wti/Wti.Plugin.Test/FileImporter/PipingSurfaceLineCsvImporterTest.cs (.../PipingSurfaceLineCsvImporterTest.cs) (revision ecfa21abcbbd04db3963323e308dd0b94c8cb7a0) @@ -247,5 +247,69 @@ "No items should be added to collection when import is aborted."); mocks.VerifyAll(); // Expect no calls on 'observer' } + + [Test] + public void ImportItem_FileIsEmpty_AbortImportAndLog() + { + // Setup + string corruptPath = Path.Combine(testDataPath, "empty.csv"); + + var mocks = new MockRepository(); + var observer = mocks.StrictMock(); + mocks.ReplayAll(); + + var importer = new PipingSurfaceLinesCsvImporter(); + + var observableSurfaceLinesList = new ObservableList(); + observableSurfaceLinesList.Attach(observer); + + object importedItem = null; + + // Call + Action call = () => importedItem = importer.ImportItem(corruptPath, observableSurfaceLinesList); + + // Assert + var internalErrorMessage = String.Format(WtiIOResources.Error_File_0_empty, + corruptPath); + var expectedLogMessage = string.Format(ApplicationResources.PipingSurfaceLinesCsvImporter_CriticalErrorReading_0_Cause_1_, + corruptPath, internalErrorMessage); + TestHelper.AssertLogMessageIsGenerated(call, expectedLogMessage, 1); + Assert.AreSame(observableSurfaceLinesList, importedItem); + CollectionAssert.IsEmpty(observableSurfaceLinesList, + "No items should be added to collection when import is aborted."); + mocks.VerifyAll(); // Expect no calls on 'observer' + } + + [Test] + public void ImportItem_InvalidHeader_AbortImportAndLog() + { + // Setup + string corruptPath = Path.Combine(testDataPath, "InvalidHeader_LacksY1.csv"); + + var mocks = new MockRepository(); + var observer = mocks.StrictMock(); + mocks.ReplayAll(); + + var importer = new PipingSurfaceLinesCsvImporter(); + + var observableSurfaceLinesList = new ObservableList(); + observableSurfaceLinesList.Attach(observer); + + object importedItem = null; + + // Call + Action call = () => importedItem = importer.ImportItem(corruptPath, observableSurfaceLinesList); + + // Assert + var internalErrorMessage = String.Format(WtiIOResources.PipingSurfaceLinesCsvReader_File_0_invalid_header, + corruptPath); + var expectedLogMessage = string.Format(ApplicationResources.PipingSurfaceLinesCsvImporter_CriticalErrorReading_0_Cause_1_, + corruptPath, internalErrorMessage); + TestHelper.AssertLogMessageIsGenerated(call, expectedLogMessage, 1); + Assert.AreSame(observableSurfaceLinesList, importedItem); + CollectionAssert.IsEmpty(observableSurfaceLinesList, + "No items should be added to collection when import is aborted."); + mocks.VerifyAll(); // Expect no calls on 'observer' + } } } \ No newline at end of file