Index: Ringtoets/Piping/src/Ringtoets.Piping.Data/StochasticSoilModelCollection.cs =================================================================== diff -u -r203d0f03d922b6dac20080dad8e82fe92c7bec86 -r5a453b9ac4ccaa5fc6f767036f0bcbf87ad05ff4 --- Ringtoets/Piping/src/Ringtoets.Piping.Data/StochasticSoilModelCollection.cs (.../StochasticSoilModelCollection.cs) (revision 203d0f03d922b6dac20080dad8e82fe92c7bec86) +++ Ringtoets/Piping/src/Ringtoets.Piping.Data/StochasticSoilModelCollection.cs (.../StochasticSoilModelCollection.cs) (revision 5a453b9ac4ccaa5fc6f767036f0bcbf87ad05ff4) @@ -19,19 +19,123 @@ // Stichting Deltares and remain full property of Stichting Deltares at all times. // All rights reserved. +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; using Core.Common.Base; namespace Ringtoets.Piping.Data { /// - /// A collection of that was imported from a file. + /// A collection to store the and the source path + /// they were imported from. /// - public class StochasticSoilModelCollection : ObservableList + public class StochasticSoilModelCollection : IObservable, IEnumerable { + private readonly List stochasticSoilModels = new List(); + private readonly ICollection observers = new Collection(); + /// + /// Gets or sets the element at index in the collection. + /// + /// The index. + /// The element at index in the collection. + /// Thrown when is not + /// between [0, ) + public StochasticSoilModel this[int i] + { + get + { + return stochasticSoilModels[i]; + } + set + { + stochasticSoilModels[i] = value; + } + } + + /// + /// Removes the first occurence of in the collection. + /// + /// The to be removed. + /// True if the was successfully removed from the collection; + /// False if otherwise or if the was not found in the collection. + public bool Remove(StochasticSoilModel model) + { + return stochasticSoilModels.Remove(model); + } + + /// + /// Clears the imported items in the collection and + /// the . + /// + public void Clear() + { + SourcePath = null; + stochasticSoilModels.Clear(); + } + + /// + /// Adds a item to the end of the collection. + /// + /// The to be added. + public void Add(StochasticSoilModel model) + { + stochasticSoilModels.Add(model); + } + + /// + /// Gets the amount of items stored in the collection. + /// + public int Count + { + get + { + return stochasticSoilModels.Count; + } + } + + /// /// Gets or sets the last known file path from which the /// elements were imported. /// public string SourcePath { get; set; } + + public void Attach(IObserver observer) + { + observers.Add(observer); + } + + public void Detach(IObserver observer) + { + observers.Remove(observer); + } + + public void NotifyObservers() + { + // Iterate through a copy of the list of observers; an update of one observer might result in detaching another observer (which will result in a "list modified" exception over here otherwise) + foreach (var observer in observers.ToArray()) + { + // Ensure the observer is still part of the original list of observers + if (!observers.Contains(observer)) + { + continue; + } + + observer.UpdateObserver(); + } + } + + public IEnumerator GetEnumerator() + { + return stochasticSoilModels.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } } } \ No newline at end of file Index: Ringtoets/Piping/src/Ringtoets.Piping.Forms/Views/PipingFailureMechanismView.cs =================================================================== diff -u -r2cafb330e0b90d1103bc9329eafbcba24836787f -r5a453b9ac4ccaa5fc6f767036f0bcbf87ad05ff4 --- Ringtoets/Piping/src/Ringtoets.Piping.Forms/Views/PipingFailureMechanismView.cs (.../PipingFailureMechanismView.cs) (revision 2cafb330e0b90d1103bc9329eafbcba24836787f) +++ Ringtoets/Piping/src/Ringtoets.Piping.Forms/Views/PipingFailureMechanismView.cs (.../PipingFailureMechanismView.cs) (revision 5a453b9ac4ccaa5fc6f767036f0bcbf87ad05ff4) @@ -195,7 +195,7 @@ { ReferenceLine referenceLine = data.Parent.ReferenceLine; IEnumerable failureMechanismSections = data.WrappedData.Sections; - ObservableList stochasticSoilModels = data.WrappedData.StochasticSoilModels; + StochasticSoilModelCollection stochasticSoilModels = data.WrappedData.StochasticSoilModels; ObservableList ringtoetsPipingSurfaceLines = data.WrappedData.SurfaceLines; HydraulicBoundaryDatabase hydraulicBoundaryDatabase = data.Parent.HydraulicBoundaryDatabase; IEnumerable calculations = Index: Ringtoets/Piping/test/Ringtoets.Piping.Data.Test/StochasticSoilModelCollectionTest.cs =================================================================== diff -u -r203d0f03d922b6dac20080dad8e82fe92c7bec86 -r5a453b9ac4ccaa5fc6f767036f0bcbf87ad05ff4 --- Ringtoets/Piping/test/Ringtoets.Piping.Data.Test/StochasticSoilModelCollectionTest.cs (.../StochasticSoilModelCollectionTest.cs) (revision 203d0f03d922b6dac20080dad8e82fe92c7bec86) +++ Ringtoets/Piping/test/Ringtoets.Piping.Data.Test/StochasticSoilModelCollectionTest.cs (.../StochasticSoilModelCollectionTest.cs) (revision 5a453b9ac4ccaa5fc6f767036f0bcbf87ad05ff4) @@ -19,8 +19,11 @@ // Stichting Deltares and remain full property of Stichting Deltares at all times. // All rights reserved. +using System; +using System.Collections.Generic; using Core.Common.Base; using NUnit.Framework; +using Rhino.Mocks; namespace Ringtoets.Piping.Data.Test { @@ -34,8 +37,343 @@ var collection = new StochasticSoilModelCollection(); // Assert - Assert.IsInstanceOf>(collection); + Assert.IsInstanceOf(collection); Assert.IsNull(collection.SourcePath); + Assert.AreEqual(0, collection.Count); + CollectionAssert.IsEmpty(collection); } + + [Test] + public void Add_AddNewStochasticSoilModel_CollectionContainsModel() + { + // Setup + var collection = new StochasticSoilModelCollection(); + var soilModel = new StochasticSoilModel(1, string.Empty, string.Empty); + + // Call + collection.Add(soilModel); + + // Assert + CollectionAssert.Contains(collection, soilModel); + } + + [Test] + public void GivenCollection_WhenAddingNewModels_ThenCollectionContainsExpectedElements() + { + // Given + var collection = new StochasticSoilModelCollection(); + var expectedSoilModelCollection = new[] + { + new StochasticSoilModel(1, string.Empty, string.Empty), + new StochasticSoilModel(2, string.Empty, string.Empty), + new StochasticSoilModel(3, string.Empty, string.Empty), + new StochasticSoilModel(4, string.Empty, string.Empty) + }; + + // When + foreach (var model in expectedSoilModelCollection) + { + collection.Add(model); + } + + // Then + CollectionAssert.AreEqual(expectedSoilModelCollection, collection); + } + + [Test] + public void Count_CollectionFilledWithElements_ReturnsExpectedNumberOfElements() + { + // Setup + var collection = new StochasticSoilModelCollection + { + new StochasticSoilModel(1, string.Empty, string.Empty), + new StochasticSoilModel(2, string.Empty, string.Empty), + new StochasticSoilModel(3, string.Empty, string.Empty), + new StochasticSoilModel(4, string.Empty, string.Empty) + }; + + // Call + int nrOfElements = collection.Count; + + // Assert + Assert.AreEqual(4, nrOfElements); + } + + [Test] + [TestCase(-1)] + [TestCase(0)] + public void Indexer_GetItemAtIndexOutOfRange_ThrowsArgumentOutOfRangeException(int invalidIndex) + { + // Setup + var collection = new StochasticSoilModelCollection(); + + // Call + TestDelegate call = () => + { + StochasticSoilModel model = collection[invalidIndex]; + }; + + // Assert + Assert.Throws(call); + } + + [Test] + public void Indexer_GetElementAtIndex_ReturnsExpectedElement() + { + // Setup + var elementToRetrieve = new StochasticSoilModel(0, string.Empty, string.Empty); + var collection = new StochasticSoilModelCollection + { + new StochasticSoilModel(1, string.Empty, string.Empty), + new StochasticSoilModel(2, string.Empty, string.Empty), + new StochasticSoilModel(3, string.Empty, string.Empty), + new StochasticSoilModel(4, string.Empty, string.Empty), + elementToRetrieve + }; + + // Call + StochasticSoilModel retrievedElement = collection[4]; + + // Assert + Assert.AreSame(elementToRetrieve, retrievedElement); + } + + [Test] + [TestCase(-1)] + [TestCase(0)] + public void Indexer_SetItemAtIndexOutOfRange_ThrowsArgumentOutOfRangeException(int invalidIndex) + { + // Setup + var collection = new StochasticSoilModelCollection(); + + // Call + TestDelegate call = () => { collection[invalidIndex] = new StochasticSoilModel(5, string.Empty, string.Empty); }; + + // Assert + Assert.Throws(call); + } + + [Test] + public void Indexer_SetElementAtIndex_SetsExpectedElement() + { + // Setup + var elementToSet = new StochasticSoilModel(0, string.Empty, string.Empty); + var soilModel1 = new StochasticSoilModel(1, string.Empty, string.Empty); + var soilModel2 = new StochasticSoilModel(2, string.Empty, string.Empty); + var soilModel3 = new StochasticSoilModel(3, string.Empty, string.Empty); + + var collection = new StochasticSoilModelCollection + { + soilModel1, + soilModel2, + soilModel3 + }; + + // Call + collection[1] = elementToSet; + + // Assert + IEnumerable expectedCollection = new[] + { + soilModel1, + elementToSet, + soilModel3 + }; + CollectionAssert.AreEqual(expectedCollection, collection); + } + + [Test] + public void Remove_ElementNotInList_ReturnsFalse() + { + // Setup + var element = new StochasticSoilModel(0, string.Empty, string.Empty); + + var collection = new StochasticSoilModelCollection(); + var expectedSoilModelCollection = new[] + { + new StochasticSoilModel(1, string.Empty, string.Empty), + new StochasticSoilModel(2, string.Empty, string.Empty), + new StochasticSoilModel(3, string.Empty, string.Empty), + new StochasticSoilModel(4, string.Empty, string.Empty) + }; + + foreach (var model in expectedSoilModelCollection) + { + collection.Add(model); + } + + // Call + bool removeSuccessful = collection.Remove(element); + + // Assert + Assert.IsFalse(removeSuccessful); + CollectionAssert.AreEqual(expectedSoilModelCollection, collection); + } + + [Test] + public void Remove_ElementInCollection_ReturnsTrue() + { + // Setup + var elementToBeRemoved = new StochasticSoilModel(0, string.Empty, string.Empty); + + var collection = new StochasticSoilModelCollection(); + var expectedSoilModelCollection = new[] + { + new StochasticSoilModel(1, string.Empty, string.Empty), + new StochasticSoilModel(2, string.Empty, string.Empty), + new StochasticSoilModel(3, string.Empty, string.Empty), + new StochasticSoilModel(4, string.Empty, string.Empty) + }; + + foreach (var model in expectedSoilModelCollection) + { + collection.Add(model); + } + + collection.Add(elementToBeRemoved); + + // Call + bool removeSuccessful = collection.Remove(elementToBeRemoved); + + // Assert + Assert.IsTrue(removeSuccessful); + CollectionAssert.AreEqual(expectedSoilModelCollection, collection); + } + + [Test] + public void Remove_ElementToRemoveMultiplesInCollection_ReturnsTrueAndRemovesFirstOccurence() + { + // Setup + var elementToBeRemoved = new StochasticSoilModel(0, string.Empty, string.Empty); + + var collection = new StochasticSoilModelCollection + { + elementToBeRemoved + }; + var expectedSoilModelCollection = new[] + { + new StochasticSoilModel(1, string.Empty, string.Empty), + new StochasticSoilModel(2, string.Empty, string.Empty), + new StochasticSoilModel(3, string.Empty, string.Empty), + new StochasticSoilModel(4, string.Empty, string.Empty), + elementToBeRemoved + }; + + foreach (var model in expectedSoilModelCollection) + { + collection.Add(model); + } + + // Call + bool removeSuccessful = collection.Remove(elementToBeRemoved); + + // Assert + Assert.IsTrue(removeSuccessful); + CollectionAssert.AreEqual(expectedSoilModelCollection, collection); + } + + [Test] + public void Clear_CollectionFullyDefined_ClearsSourcePathAndCollection() + { + // Setup + var collection = new StochasticSoilModelCollection + { + SourcePath = "I am a source path for my models" + }; + var expectedSoilModelCollection = new[] + { + new StochasticSoilModel(1, string.Empty, string.Empty), + new StochasticSoilModel(2, string.Empty, string.Empty), + new StochasticSoilModel(3, string.Empty, string.Empty), + new StochasticSoilModel(4, string.Empty, string.Empty) + }; + + foreach (var model in expectedSoilModelCollection) + { + collection.Add(model); + } + + // Call + collection.Clear(); + + // Assert + Assert.IsNull(collection.SourcePath); + Assert.AreEqual(0, collection.Count); + CollectionAssert.IsEmpty(collection); + } + + [Test] + public void NotifyObservers_WithObserverAttached_ObserverIsNotified() + { + // Setup + var mocks = new MockRepository(); + var observer = mocks.StrictMock(); + observer.Expect(o => o.UpdateObserver()); // Expect to be called once + mocks.ReplayAll(); + + var stochasticSoilModelCollection = new StochasticSoilModelCollection(); + stochasticSoilModelCollection.Attach(observer); + + // Call + stochasticSoilModelCollection.NotifyObservers(); + + // Assert + mocks.VerifyAll(); + } + + [Test] + public void NotifyObserver_AttachedObserverDetachedAgain_ObserverNoLongerNotified() + { + // Setup + var mocks = new MockRepository(); + var observer = mocks.StrictMock(); + mocks.ReplayAll(); + + var stochasticSoilModelCollection = new StochasticSoilModelCollection(); + stochasticSoilModelCollection.Attach(observer); + stochasticSoilModelCollection.Detach(observer); + + // Call + stochasticSoilModelCollection.NotifyObservers(); + + // Assert + mocks.VerifyAll(); // Expect no calls on 'observer' + } + + [Test] + public void NotifyObservers_MultipleObserversDetachingOrAttachingOthers_NoUpdatesForAttachedAndDetachedObservers() + { + // Setup + var mocks = new MockRepository(); + var stochasticSoilModelCollection = new StochasticSoilModelCollection(); + + var observer1 = mocks.Stub(); + var observer2 = mocks.Stub(); + var observer3 = mocks.Stub(); + var observer4 = mocks.Stub(); + var observer5 = mocks.Stub(); + var observer6 = mocks.Stub(); + + stochasticSoilModelCollection.Attach(observer1); + stochasticSoilModelCollection.Attach(observer2); + stochasticSoilModelCollection.Attach(observer3); + stochasticSoilModelCollection.Attach(observer4); + stochasticSoilModelCollection.Attach(observer6); + + observer1.Expect(o => o.UpdateObserver()); + observer2.Expect(o => o.UpdateObserver()).Do((Action) (() => stochasticSoilModelCollection.Detach(observer3))); + observer3.Expect(o => o.UpdateObserver()).Repeat.Never(); // A detached observer should no longer be updated + observer4.Expect(o => o.UpdateObserver()).Do((Action) (() => stochasticSoilModelCollection.Attach(observer5))); + observer5.Expect(o => o.UpdateObserver()).Repeat.Never(); // An attached observer should not be updated too + observer6.Expect(o => o.UpdateObserver()); + + mocks.ReplayAll(); + + // Call + stochasticSoilModelCollection.NotifyObservers(); + + // Assert + mocks.VerifyAll(); + } } } \ No newline at end of file