From f0a6b70ae3aa7dba39d0f2fbd06996e7042d3727 Mon Sep 17 00:00:00 2001
From: CoderGamester <game.gamester@gmail.com>
Date: Thu, 10 Oct 2024 22:25:17 +0100
Subject: [PATCH] Improved the *ObservableResolverList* and
 *ObservableResolverDictionary* data types to properly resolve lists and
 dictionaries with different data types from the original collection. Fixed
 Unit tests for floatPTests that were failing. Added new unit tests for
 *ObservableResolverList* and *ObservableResolverDictionary* data types

---
 CHANGELOG.md                                  |  29 +-
 Runtime/ObservableDictionary.cs               | 188 +++++++++++-
 Runtime/ObservableList.cs                     | 149 ++++++++--
 Tests/Editor/OberservableResolverListTest.cs  |  70 +++++
 .../OberservableResolverListTest.cs.meta      |  11 +
 Tests/Editor/ObservableDictionaryTest.cs      | 275 ++++++++++--------
 Tests/Editor/ObservableListTest.cs            | 137 +++------
 .../ObservableResolverDictionaryTest.cs       |  89 ++++++
 .../ObservableResolverDictionaryTest.cs.meta  |  11 +
 Tests/Editor/floatPTests.cs                   |  41 ++-
 package.json                                  |   4 +-
 11 files changed, 742 insertions(+), 262 deletions(-)
 create mode 100644 Tests/Editor/OberservableResolverListTest.cs
 create mode 100644 Tests/Editor/OberservableResolverListTest.cs.meta
 create mode 100644 Tests/Editor/ObservableResolverDictionaryTest.cs
 create mode 100644 Tests/Editor/ObservableResolverDictionaryTest.cs.meta

diff --git a/CHANGELOG.md b/CHANGELOG.md
index a93a613..07b5f96 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,13 +4,38 @@ All notable changes to this package will be documented in this file.
 The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
 and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
 
+## [0.6.0] - 2023-08-05
+
+- Improved the *ObservableResolverList* and *ObservableResolverDictionary* data types to properly resolve lists and dictionaries with different data types from the original collection.
+
+## [0.5.1] - 2023-09-04
+
+- Added StructPair data type to support both object and struct type containers, improving memory usage performance.
+
+**Fix**:
+- Fixed the dispose extension methods for GameObject and Object, removing pragma directives and adding null reference check in GetValid method to avoid unwanted exceptions
+
+## [0.5.0] - 2023-08-05
+
+- Added **floatP**, a deterministic floating-point number type, enhancing precision and predictability in mathematical operations. Including arithmetic and comparison operators for floatP to support complex calculations and conversion methods between floatP and float types.
+
+## [0.4.0] - 2023-07-30
+
+- Added utility methods and extensions for Unity's Object and GameObject types, enhancing the codebase's functionality.
+- Introduced a SerializableType struct for viewing, modifying, and saving types from the inspector, with serialization support and compatibility with filter attributes.
+
+## [0.3.0] - 2023-07-28
+
+- Added support for observing field updates with previous and current values in the ObservableField class.
+- Introduced a UnitySerializedDictionary class that allows serialization of dictionaries in Unity.
+
 ## [0.2.0] - 2020-09-28
 
-- Removed *ObservableIdList* because it's behaviour was too confusing and the same result can be obtained with *ObservableList* or *ObservableDictionary*
 - Added new *ObservableResolverList*, *ObservableResolverDictionary* & *ObservableResolverField* to allow to create observable types without referencing the collection directly
-- Added Unit tests to all types
+- Added Unit tests to all data types in the project
 
 **Changed**:
+- Removed *ObservableIdList* because it's behaviour was too confusing and the same result can be obtained with *ObservableList* or *ObservableDictionary*
 - Removed all Pair Data and moved them to new *Pair<Key,Value>* serialized type that can now be serializable on Unity 2020.1
 - Moved all Vector2, Vector3 & Vector4 extensions to the ValueData file
 
diff --git a/Runtime/ObservableDictionary.cs b/Runtime/ObservableDictionary.cs
index 83e07f8..94ce25b 100644
--- a/Runtime/ObservableDictionary.cs
+++ b/Runtime/ObservableDictionary.cs
@@ -1,7 +1,9 @@
+using NUnit.Framework;
 using System;
 using System.Collections;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
+using UnityEngine.UIElements;
 
 // ReSharper disable once CheckNamespace
 
@@ -19,6 +21,9 @@ public interface IObservableDictionary : IEnumerable
 	}
 
 	/// <inheritdoc cref="IObservableDictionary"/>
+	/// <remarks>
+	/// This dictionary only allows to read the elements in it and not to modify it
+	/// </remarks>
 	public interface IObservableDictionaryReader<TKey, TValue> : IObservableDictionary, IEnumerable<KeyValuePair<TKey, TValue>>
 	{
 		/// <summary>
@@ -71,7 +76,7 @@ public interface IObservableDictionaryReader<TKey, TValue> : IObservableDictiona
 		void StopObservingAll(object subscriber = null);
 	}
 
-	/// <inheritdoc />
+	/// <inheritdoc cref="IObservableDictionary"/>
 	public interface IObservableDictionary<TKey, TValue> : IObservableDictionaryReader<TKey, TValue>
 	{
 		/// <summary>
@@ -86,12 +91,77 @@ public interface IObservableDictionary<TKey, TValue> : IObservableDictionaryRead
 		/// <inheritdoc cref="Dictionary{TKey,TValue}.Remove" />
 		bool Remove(TKey key);
 
+		/// <inheritdoc cref="Dictionary{TKey,TValue}.Clear"/>
+		void Clear();
+
 		/// <remarks>
 		/// It invokes any update method that is observing to the given <paramref name="key"/> on this dictionary
 		/// </remarks>
 		void InvokeUpdate(TKey key);
 	}
 
+	/// <inheritdoc />
+	/// <remarks>
+	/// This interface resolves between 2 dictionaries with different types of keys and values
+	/// </remarks>
+	public interface IObservableResolverDictionaryReader<TKey, TValue, TKeyOrigin, TValueOrigin> :
+		IObservableDictionaryReader<TKey, TValue>
+	{
+		/// <summary>
+		/// The Original Dictionary that is being resolved across the entire interface
+		/// </summary>
+		ReadOnlyDictionary<TKeyOrigin, TValueOrigin> OriginDictionary { get; }
+
+		/// <summary>
+		/// Gets the value from the origin dictionary corresponding to the specified key.
+		/// </summary>
+		/// <param name="key">The key to locate in the origin dictionary.</param>
+		/// <returns>The value from the origin dictionary corresponding to the specified key.</returns>
+		TValueOrigin GetOriginValue(TKey key);
+
+		/// <summary>
+		/// Attempts to get the value from the origin dictionary corresponding to the specified key.
+		/// </summary>
+		/// <param name="key">The key to locate in the origin dictionary.</param>
+		/// <param name="value">When this method returns, contains the value from the origin dictionary corresponding to the specified key, if the key is found; otherwise, the default value for the type of the value parameter. This parameter is passed uninitialized.</param>
+		/// <returns>true if the origin dictionary contains an element with the specified key; otherwise, false.</returns>
+		bool TryGetOriginValue(TKey key, out TValueOrigin value);
+	}
+
+	/// <inheritdoc cref="IObservableDictionary{TKey,TValue}"/>
+	/// <remarks>
+	/// This interface resolves between 2 dictionaries with different types of keys and values
+	/// </remarks>
+	public interface IObservableResolverDictionary<TKey, TValue, TKeyOrigin, TValueOrigin> :
+		IObservableResolverDictionaryReader<TKey, TValue, TKeyOrigin, TValueOrigin>,
+		IObservableDictionary<TKey, TValue>
+	{
+		/// <summary>
+		/// Updates the value in the origin dictionary corresponding to the specified origin key.
+		/// </summary>
+		/// <param name="key">The key of the value to update in the origin dictionary.</param>
+		/// <param name="value">The new value to set in the origin dictionary.</param>
+		void UpdateOrigin(TKeyOrigin key, TValueOrigin value);
+
+		/// <inheritdoc cref="Dictionary{TKey,TValue}.Add" />
+		/// <remarks>
+		/// Add's to the origin dictionary
+		/// </remarks>
+		void AddOrigin(TKeyOrigin key, TValueOrigin value);
+
+		/// <inheritdoc cref="Dictionary{TKey,TValue}.Remove" />
+		/// <remarks>
+		/// Remove's to the origin dictionary
+		/// </remarks>
+		bool RemoveOrigin(TKeyOrigin key);
+
+		/// <inheritdoc cref="Dictionary{TKey,TValue}.Clear" />
+		/// <remarks>
+		/// Clear's to the origin dictionary
+		/// </remarks>
+		void ClearOrigin();
+	}
+
 	/// <inheritdoc />
 	public class ObservableDictionary<TKey, TValue> : IObservableDictionary<TKey, TValue>
 	{
@@ -153,7 +223,7 @@ public bool ContainsKey(TKey key)
 		}
 
 		/// <inheritdoc />
-		public void Add(TKey key, TValue value)
+		public virtual void Add(TKey key, TValue value)
 		{
 			Dictionary.Add(key, value);
 
@@ -172,7 +242,7 @@ public void Add(TKey key, TValue value)
 		}
 
 		/// <inheritdoc />
-		public bool Remove(TKey key)
+		public virtual bool Remove(TKey key)
 		{
 			if (!Dictionary.TryGetValue(key, out var value))
 			{
@@ -197,6 +267,22 @@ public bool Remove(TKey key)
 			return true;
 		}
 
+		/// <inheritdoc />
+		public virtual void Clear()
+		{
+			var dictionary = new Dictionary<TKey, TValue>(Dictionary);
+
+			Dictionary.Clear();
+
+			for (var i = 0; i < _updateActions.Count; i++)
+			{
+				foreach (var data in dictionary)
+				{
+					_updateActions[i](data.Key, data.Value, default, ObservableUpdateType.Removed);
+				}
+			}
+		}
+
 		/// <inheritdoc />
 		public void InvokeUpdate(TKey key)
 		{
@@ -310,16 +396,100 @@ protected void InvokeUpdate(TKey key, TValue previousValue)
 	}
 
 	/// <inheritdoc />
-	public class ObservableResolverDictionary<TKey, TValue> : ObservableDictionary<TKey, TValue>
-		where TValue : struct
+	public class ObservableResolverDictionary<TKey, TValue, TKeyOrigin, TValueOrigin> : 
+		ObservableDictionary<TKey, TValue>,
+		IObservableResolverDictionary<TKey, TValue, TKeyOrigin, TValueOrigin>
 	{
-		private readonly Func<IDictionary<TKey, TValue>> _dictionaryResolver;
+		private readonly IDictionary<TKeyOrigin, TValueOrigin> _dictionary;
+		private readonly Func<TKey, TValue, KeyValuePair<TKeyOrigin, TValueOrigin>> _toOrignResolver;
+		private readonly Func<KeyValuePair<TKeyOrigin, TValueOrigin>, KeyValuePair<TKey, TValue>> _fromOrignResolver;
 
-		protected override IDictionary<TKey, TValue> Dictionary => _dictionaryResolver();
+		/// <inheritdoc />
+		public ReadOnlyDictionary<TKeyOrigin, TValueOrigin> OriginDictionary => new ReadOnlyDictionary<TKeyOrigin, TValueOrigin>(_dictionary);
 
-		public ObservableResolverDictionary(Func<IDictionary<TKey, TValue>> dictionaryResolver)
+		public ObservableResolverDictionary(IDictionary<TKeyOrigin, TValueOrigin> dictionary,
+			Func<KeyValuePair<TKeyOrigin, TValueOrigin>, KeyValuePair<TKey, TValue>> fromOrignResolver,
+			Func<TKey, TValue, KeyValuePair<TKeyOrigin, TValueOrigin>> toOrignResolver)
+			: base(new Dictionary<TKey, TValue>(dictionary.Count))
+		{
+			_dictionary = dictionary;
+			_toOrignResolver = toOrignResolver;
+			_fromOrignResolver = fromOrignResolver;
+
+			foreach (var pair in dictionary)
+			{
+				Dictionary.Add(fromOrignResolver(pair));
+			}
+		}
+
+		/// <inheritdoc />
+		public TValueOrigin GetOriginValue(TKey key)
+		{
+			return _dictionary[_toOrignResolver(key, default).Key];
+		}
+
+		/// <inheritdoc />
+		public bool TryGetOriginValue(TKey key, out TValueOrigin value)
+		{
+			return _dictionary.TryGetValue(_toOrignResolver(key, default).Key, out value);
+		}
+
+		/// <inheritdoc />
+		public void UpdateOrigin(TKeyOrigin key, TValueOrigin value)
+		{
+			var convertPair = _fromOrignResolver(new KeyValuePair<TKeyOrigin, TValueOrigin>(key, value));
+
+			_dictionary[key] = value;
+			this[convertPair.Key] = convertPair.Value;
+		}
+
+		/// <inheritdoc />
+		public override void Add(TKey key, TValue value)
+		{
+			_dictionary.Add(_toOrignResolver(key, value));
+			base.Add(key, value);
+		}
+
+		/// <inheritdoc />
+		public override bool Remove(TKey key)
+		{
+			var pair = _toOrignResolver(key, Dictionary[key]);
+
+			_dictionary.Remove(pair.Key);
+
+			return base.Remove(key);
+		}
+
+		/// <inheritdoc />
+		public override void Clear()
+		{
+			_dictionary.Clear();
+			base.Clear(); ;
+		}
+
+		/// <inheritdoc />
+		public void AddOrigin(TKeyOrigin key, TValueOrigin value)
+		{
+			var convertPair = _fromOrignResolver(new KeyValuePair<TKeyOrigin, TValueOrigin>(key, value));
+
+			_dictionary.Add(key, value);
+			base.Add(convertPair.Key, convertPair.Value);
+		}
+
+		/// <inheritdoc />
+		public bool RemoveOrigin(TKeyOrigin key)
+		{
+			var convertPair = _fromOrignResolver(new KeyValuePair<TKeyOrigin, TValueOrigin>(key, OriginDictionary[key]));
+
+			_dictionary.Remove(key);
+			return base.Remove(convertPair.Key);
+		}
+
+		/// <inheritdoc />
+		public void ClearOrigin()
 		{
-			_dictionaryResolver = dictionaryResolver;
+			_dictionary.Clear();
+			base.Clear();
 		}
 	}
 }
\ No newline at end of file
diff --git a/Runtime/ObservableList.cs b/Runtime/ObservableList.cs
index f6e39bb..354278a 100644
--- a/Runtime/ObservableList.cs
+++ b/Runtime/ObservableList.cs
@@ -1,6 +1,8 @@
 using System;
 using System.Collections;
 using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
 
 // ReSharper disable once CheckNamespace
 
@@ -70,11 +72,11 @@ public interface IObservableList<T> : IObservableListReader<T>
 		/// </summary>
 		new T this[int index] { get; set; }
 
-		/// <inheritdoc cref="List{T}.Remove"/>
+		/// <inheritdoc cref="List{T}.Add"/>
 		void Add(T data);
 
 		/// <inheritdoc cref="List{T}.Remove"/>
-		void Remove(T data);
+		bool Remove(T data);
 
 		/// <inheritdoc cref="List{T}.RemoveAt"/>
 		void RemoveAt(int index);
@@ -88,6 +90,52 @@ public interface IObservableList<T> : IObservableListReader<T>
 		void InvokeUpdate(int index);
 	}
 
+	/// <inheritdoc />
+	/// <remarks>
+	/// This interface resolves between 2 lists with different types of values
+	/// </remarks>
+	public interface IObservableResolverListReader<T, TOrigin> : IObservableListReader<T>
+	{
+		/// <summary>
+		/// The Original List that is being resolved across the entire interface
+		/// </summary>
+		IReadOnlyList<TOrigin> OriginList { get; }
+	}
+
+	/// <inheritdoc />
+	/// <remarks>
+	/// This interface resolves between 2 lists with different types of values
+	/// </remarks>
+	public interface IObservableResolverList<T, TOrigin> :
+		IObservableResolverListReader<T, TOrigin>,
+		IObservableList<T>
+	{
+		/// <summary>
+		/// Updates the value in the origin list corresponding to the specified index.
+		/// </summary>
+		/// <param name="index">The index of the value to update in the origin list.</param>
+		/// <param name="value">The new value to set in the origin list.</param>
+		void UpdateOrigin(TOrigin value, int index);
+
+		/// <inheritdoc cref="List{T}.Add"/>
+		/// <remarks>
+		/// Add's the value to the origin list
+		/// </remarks>
+		void AddOrigin(TOrigin value);
+
+		/// <inheritdoc cref="List{T}.Remove"/>
+		/// <remarks>
+		/// Remove's the value to the origin list
+		/// </remarks>
+		bool RemoveOrigin(TOrigin value);
+
+		/// <inheritdoc cref="List{T}.Clear"/>
+		/// <remarks>
+		/// Clear's to the origin list
+		/// </remarks>
+		void ClearOrigin();
+	}
+
 	/// <inheritdoc />
 	public class ObservableList<T> : IObservableList<T>
 	{
@@ -110,15 +158,15 @@ public T this[int index]
 		/// <inheritdoc />
 		public int Count => List.Count;
 		/// <inheritdoc />
-		public IReadOnlyList<T> ReadOnlyList => List;
+		public IReadOnlyList<T> ReadOnlyList => new List<T>(List);
 
 		protected virtual List<T> List { get; }
 
 		protected ObservableList() { }
 
-		public ObservableList(List<T> list)
+		public ObservableList(IList<T> list)
 		{
-			List = list;
+			List = list as List<T> ?? list.ToList();
 		}
 
 		/// <inheritdoc cref="List{T}.GetEnumerator"/>
@@ -152,7 +200,7 @@ public int IndexOf(T value)
 		}
 
 		/// <inheritdoc />
-		public void Add(T data)
+		public virtual void Add(T data)
 		{
 			List.Add(data);
 
@@ -163,18 +211,22 @@ public void Add(T data)
 		}
 
 		/// <inheritdoc />
-		public void Remove(T data)
+		public bool Remove(T data)
 		{
 			var idx = List.IndexOf(data);
 
 			if (idx >= 0)
 			{
 				RemoveAt(idx);
+
+				return true;
 			}
+
+			return false;
 		}
 
 		/// <inheritdoc />
-		public void RemoveAt(int index)
+		public virtual void RemoveAt(int index)
 		{
 			var data = List[index];
 
@@ -187,17 +239,17 @@ public void RemoveAt(int index)
 		}
 
 		/// <inheritdoc />
-		public void Clear()
+		public virtual void Clear()
 		{
-			var data = new List<T>(List);
+			var list = new List<T>(List);
 
 			List.Clear();
 
 			for (var i = 0; i < _updateActions.Count; i++)
 			{
-				for (var j = 0; j < data.Count; j++)
+				for (var j = 0; j < list.Count; j++)
 				{
-					_updateActions[i](j, data[j], default, ObservableUpdateType.Removed);
+					_updateActions[i](j, list[j], default, ObservableUpdateType.Removed);
 				}
 			}
 		}
@@ -257,15 +309,78 @@ protected void InvokeUpdate(int index, T previousValue)
 	}
 
 	/// <inheritdoc />
-	public class ObservableResolverList<T> : ObservableList<T>
+	public class ObservableResolverList<T, TOrigin> : ObservableList<T>, IObservableResolverList<T, TOrigin>
 	{
-		private readonly Func<List<T>> _listResolver;
+		private readonly IList<TOrigin> _originList;
+		private readonly Func<TOrigin, T> _fromOrignResolver;
+		private readonly Func<T, TOrigin> _toOrignResolver;
 
-		protected override List<T> List => _listResolver();
+		/// <inheritdoc />
+		public IReadOnlyList<TOrigin> OriginList => new List<TOrigin>(_originList);
 
-		public ObservableResolverList(Func<List<T>> listResolver)
+		public ObservableResolverList(IList<TOrigin> originList, 
+			Func<TOrigin, T> fromOrignResolver, 
+			Func<T, TOrigin> toOrignResolver) :
+			base(new List<T>(originList.Count))
+		{
+			_originList = originList;
+			_fromOrignResolver = fromOrignResolver;
+			_toOrignResolver = toOrignResolver;
+
+			for (var i = 0; i < originList.Count; i++)
+			{
+				List.Add(fromOrignResolver(originList[i]));
+			}
+		}
+
+		/// <inheritdoc />
+		public override void Add(T data)
+		{
+			_originList.Add(_toOrignResolver(data));
+			base.Add(data);
+		}
+
+		/// <inheritdoc />
+		public override void RemoveAt(int index)
+		{
+			_originList.RemoveAt(index);
+			base.RemoveAt(index);
+		}
+
+		/// <inheritdoc />
+		public override void Clear()
+		{
+			_originList.Clear();
+			base.Clear();
+		}
+
+		/// <inheritdoc />
+		public void UpdateOrigin(TOrigin value, int index)
+		{
+			_originList[index] = value;
+			List[index] = _fromOrignResolver(value);
+		}
+
+		/// <inheritdoc />
+		public void AddOrigin(TOrigin value)
+		{
+			_originList.Add(value);
+			List.Add(_fromOrignResolver(value));
+		}
+
+		/// <inheritdoc />
+		public bool RemoveOrigin(TOrigin value)
+		{
+			_originList.Remove(value);
+
+			return base.Remove(_fromOrignResolver(value));
+		}
+
+		/// <inheritdoc />
+		public void ClearOrigin()
 		{
-			_listResolver = listResolver;
+			_originList.Clear();
+			base.Clear();
 		}
 	}
 }
\ No newline at end of file
diff --git a/Tests/Editor/OberservableResolverListTest.cs b/Tests/Editor/OberservableResolverListTest.cs
new file mode 100644
index 0000000..453f248
--- /dev/null
+++ b/Tests/Editor/OberservableResolverListTest.cs
@@ -0,0 +1,70 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Reflection;
+using GameLovers;
+using NSubstitute;
+using NUnit.Framework;
+
+// ReSharper disable once CheckNamespace
+
+namespace GameLoversEditor.DataExtensions.Tests
+{
+	[TestFixture]
+	public class ObservableResolverListTest
+	{
+		private int _index = 0;
+		private IObservableResolverList<int, string> _list;
+		private IList<string> _mockList;
+
+		[SetUp]
+		public void SetUp()
+		{
+			_mockList = Substitute.For<IList<string>>();
+			_list = new ObservableResolverList<int, string>(_mockList,
+				origin => int.Parse(origin),
+				value => value.ToString());
+		}
+
+		[Test]
+		public void AddOrigin_AddsValueToOriginList()
+		{
+			var value = "1";
+
+			_list.AddOrigin(value);
+
+			_mockList.Received().Add(value);
+		}
+
+		[Test]
+		public void UpdateOrigin_UpdatesOriginList()
+		{
+			var value = "1";
+
+			_list.AddOrigin(value);
+			_list.UpdateOrigin(value, _index);
+
+			_mockList.Received()[_index] = value;
+		}
+
+		[Test]
+		public void RemoveOrigin_RemovesValueFromOriginList()
+		{
+			var value = "1";
+
+			_list.AddOrigin(value);
+
+			Assert.IsTrue(_list.RemoveOrigin(value));
+			_mockList.Received().Remove(value);
+		}
+
+		[Test]
+		public void ClearOrigin_ClearsOriginList()
+		{
+			_list.ClearOrigin();
+
+			_mockList.Received().Clear();
+		}
+	}
+}
\ No newline at end of file
diff --git a/Tests/Editor/OberservableResolverListTest.cs.meta b/Tests/Editor/OberservableResolverListTest.cs.meta
new file mode 100644
index 0000000..7e1207b
--- /dev/null
+++ b/Tests/Editor/OberservableResolverListTest.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 339332fedbfe038408ad12655029d0a1
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Tests/Editor/ObservableDictionaryTest.cs b/Tests/Editor/ObservableDictionaryTest.cs
index 23ece15..f62a2a0 100644
--- a/Tests/Editor/ObservableDictionaryTest.cs
+++ b/Tests/Editor/ObservableDictionaryTest.cs
@@ -1,7 +1,9 @@
+using System;
 using System.Collections.Generic;
 using GameLovers;
 using NSubstitute;
 using NUnit.Framework;
+using UnityEngine;
 
 // ReSharper disable once CheckNamespace
 
@@ -11,8 +13,6 @@ namespace GameLoversEditor.DataExtensions.Tests
 	public class ObservableDictionaryTest
 	{
 		private const int _key = 0;
-		private const int _previousValue = 5;
-		private const int _newValue = 10;
 
 		/// <summary>
 		/// Mocking interface to check method calls received
@@ -22,8 +22,7 @@ public interface IMockCaller<in TKey, in TValue>
 			void Call(TKey key, TValue previousValue, TValue newValue, ObservableUpdateType updateType);
 		}
 
-		private ObservableDictionary<int, int> _observableDictionary;
-		private ObservableResolverDictionary<int, int> _observableResolverDictionary;
+		private ObservableDictionary<int, int> _dictionary;
 		private IDictionary<int, int> _mockDictionary;
 		private IMockCaller<int, int> _caller;
 
@@ -31,110 +30,176 @@ public interface IMockCaller<in TKey, in TValue>
 		public void Init()
 		{
 			_caller = Substitute.For<IMockCaller<int, int>>();
-			_mockDictionary = Substitute.For<IDictionary<int, int>>();
-			_observableDictionary = new ObservableDictionary<int, int>(_mockDictionary);
-			_observableResolverDictionary = new ObservableResolverDictionary<int, int>(() => _mockDictionary);
+			_mockDictionary = new Dictionary<int, int>();
+			_dictionary = new ObservableDictionary<int, int>(_mockDictionary);
+		}
+
+		[Test]
+		public void TryGetValue_ReturnsFalse_WhenKeyDoesNotExist()
+		{
+			bool result = _dictionary.TryGetValue(1, out int value);
 
-			_mockDictionary.TryGetValue(_key, out _).Returns(callInfo =>
-			{
-				callInfo[1] = _mockDictionary[_key];
-				return true;
-			});
+			Assert.IsFalse(result);
 		}
 
 		[Test]
-		public void ValueCheck()
+		public void TryGetValue_ReturnsTrue_WhenKeyExists()
 		{
-			_mockDictionary[_key].Returns(_previousValue);
+			_dictionary.Add(1, 100);
 
-			Assert.AreEqual(_previousValue, _observableDictionary[_key]);
-			Assert.AreEqual(_previousValue, _observableResolverDictionary[_key]);
+			bool result = _dictionary.TryGetValue(1, out int value);
+
+			Assert.IsTrue(result);
+			Assert.AreEqual(100, value);
 		}
 
 		[Test]
-		public void ValueSetCheck()
+		public void ContainsKey_ReturnsFalse_WhenKeyDoesNotExist()
 		{
-			const int valueCheck1 = 5;
-			const int valueCheck2 = 6;
-			const int valueCheck3 = 7;
+			Assert.IsFalse(_dictionary.ContainsKey(1));
+		}
 
-			_mockDictionary[_key] = valueCheck1;
+		[Test]
+		public void ContainsKey_ReturnsTrue_WhenKeyExists()
+		{
+			_dictionary.Add(1, 100);
 
-			Assert.AreEqual(valueCheck1, _observableDictionary[_key]);
-			Assert.AreEqual(valueCheck1, _observableResolverDictionary[_key]);
+			Assert.IsTrue(_dictionary.ContainsKey(1));
+		}
 
-			_observableDictionary[_key] = valueCheck2;
+		[Test]
+		public void Indexer_ReturnsValue_WhenKeyExists()
+		{
+			_dictionary.Add(1, 100);
 
-			Assert.AreEqual(valueCheck2, _observableDictionary[_key]);
-			Assert.AreEqual(valueCheck2, _observableResolverDictionary[_key]);
+			Assert.AreEqual(100, _dictionary[1]);
+		}
 
-			_observableResolverDictionary[_key] = valueCheck3;
+		[Test]
+		public void Indexer_SetsValue_WhenKeyExists()
+		{
+			_dictionary.Add(1, 100);
+			_dictionary[1] = 200;
 
-			Assert.AreEqual(valueCheck3, _observableDictionary[_key]);
-			Assert.AreEqual(valueCheck3, _observableResolverDictionary[_key]);
+			Assert.AreEqual(200, _dictionary[1]);
 		}
 
 		[Test]
-		public void ObserveCheck()
+		public void Add_AddsKeyValuePair_WhenKeyDoesNotExist()
 		{
-			_observableDictionary.Observe(_key, _caller.Call);
-			_observableDictionary.Observe(_caller.Call);
-			_observableResolverDictionary.Observe(_key, _caller.Call);
-			_observableResolverDictionary.Observe(_caller.Call);
+			_dictionary.Add(1, 100);
 
-			// _caller.DidNotReceive().Call(Arg.Any<int>(), Arg.Any<int>(), Arg.Any<ObservableUpdateType>());
+			Assert.AreEqual(100, _dictionary[1]);
+		}
 
-			_observableDictionary.Add(_key, _previousValue);
-			_observableResolverDictionary.Add(_key, _previousValue);
+		[Test]
+		public void Add_ThrowsException_WhenKeyAlreadyExists()
+		{
+			_dictionary.Add(1, 100);
 
-			_mockDictionary[_key].Returns(_previousValue);
+			Assert.Throws<ArgumentException>(() => _dictionary.Add(1, 200));
+		}
 
-			_observableDictionary[_key] = _newValue;
+		[Test]
+		public void Remove_RemovesKeyValuePair_WhenKeyExists()
+		{
+			_dictionary.Add(1, 100);
 
-			_mockDictionary[_key].Returns(_previousValue);
+			Assert.IsTrue(_dictionary.Remove(1));
+			Assert.AreEqual(0, _dictionary.Count);
+		}
 
-			_observableResolverDictionary[_key] = _newValue;
+		[Test]
+		public void Remove_ReturnsFalse_WhenKeyDoesNotExist()
+		{
+			Assert.IsFalse(_dictionary.Remove(1));
+		}
 
-			_observableDictionary.Remove(_key);
-			_observableResolverDictionary.Remove(_key);
+		[Test]
+		public void Clear_RemovesAllKeyValuePairs()
+		{
+			_dictionary.Add(1, 100);
+			_dictionary.Add(2, 200);
+			_dictionary.Clear();
 
-			_caller.Received(4).Call(_key, 0, _previousValue, ObservableUpdateType.Added);
-			_caller.Received(4).Call(_key, _previousValue, _newValue, ObservableUpdateType.Updated);
-			_caller.Received(4).Call(_key, _newValue, 0, ObservableUpdateType.Removed);
+			Assert.AreEqual(0, _dictionary.Count);
+		}
+
+		[Test]
+		public void ValueSetCheck()
+		{
+			const int valueCheck1 = 5;
+			const int valueCheck2 = 6;
+
+			_mockDictionary.Add(_key, valueCheck1);
+			_dictionary[_key] = valueCheck2;
+
+			Assert.AreNotEqual(valueCheck1, _mockDictionary[_key]);
+			Assert.AreEqual(valueCheck2, _dictionary[_key]);
+		}
+
+		[Test]
+		public void ObserveCheck()
+		{
+			var startValue = 0;
+			var newValue = 1;
+
+			_dictionary.Observe(_key, _caller.Call);
+			_dictionary.Observe(_caller.Call);
+
+			_dictionary.Add(_key, startValue);
+
+			_dictionary[_key] = newValue;
+
+			_dictionary.Remove(_key);
+
+			_caller.Received().Call(_key, 0, startValue, ObservableUpdateType.Added);
+			_caller.Received().Call(_key, startValue, newValue, ObservableUpdateType.Updated);
+			_caller.Received().Call(_key, newValue, 0, ObservableUpdateType.Removed);
 		}
 
 		[Test]
 		public void InvokeObserveCheck()
 		{
-			_observableDictionary.InvokeObserve(_key, _caller.Call);
-			_observableResolverDictionary.InvokeObserve(_key, _caller.Call);
+			_dictionary.Add(_key, 0);
+			_dictionary.InvokeObserve(_key, _caller.Call);
 
 			_caller.DidNotReceive().Call(Arg.Any<int>(), Arg.Any<int>(), Arg.Any<int>(), ObservableUpdateType.Added);
-			_caller.Received(2).Call(_key, 0, 0, ObservableUpdateType.Updated);
+			_caller.Received().Call(_key, 0, 0, ObservableUpdateType.Updated);
 			_caller.DidNotReceive().Call(Arg.Any<int>(), Arg.Any<int>(), Arg.Any<int>(), ObservableUpdateType.Removed);
 		}
 
 		[Test]
-		public void InvokeCheck()
+		public void InvokeUpdate_MissingKey_ThrowsException()
+		{
+			Assert.Throws<KeyNotFoundException>(() => _dictionary.InvokeUpdate(_key));
+		}
+
+		[Test]
+		public void InvokeObserve_MissingKey_ThrowsException()
 		{
-			_observableDictionary.Observe(_key, _caller.Call);
-			_observableDictionary.Observe(_caller.Call);
-			_observableResolverDictionary.Observe(_key, _caller.Call);
-			_observableResolverDictionary.Observe(_caller.Call);
+			Assert.Throws<KeyNotFoundException>(() => _dictionary.InvokeObserve(_key, _caller.Call));
+		}
 
-			_observableDictionary.InvokeUpdate(_key);
-			_observableResolverDictionary.InvokeUpdate(_key);
+		[Test]
+		public void InvokeUpdateCheck()
+		{
+			_dictionary.Add(_key, 0);
+			_dictionary.Observe(_key, _caller.Call);
+			_dictionary.Observe(_caller.Call);
+
+			_dictionary.InvokeUpdate(_key);
 
 			_caller.DidNotReceive().Call(Arg.Any<int>(), Arg.Any<int>(), Arg.Any<int>(), ObservableUpdateType.Added);
-			_caller.Received(4).Call(_key, 0, 0, ObservableUpdateType.Updated);
+			_caller.Received(2).Call(_key, 0, 0, ObservableUpdateType.Updated);
 			_caller.DidNotReceive().Call(Arg.Any<int>(), Arg.Any<int>(), Arg.Any<int>(), ObservableUpdateType.Removed);
 		}
 
 		[Test]
-		public void InvokeCheck_NotObserving_DoesNothing()
+		public void InvokeUpdate_NotObserving_DoesNothing()
 		{
-			_observableDictionary.InvokeUpdate(_key);
-			_observableResolverDictionary.InvokeUpdate(_key);
+			_dictionary.Add(_key, 0);
+			_dictionary.InvokeUpdate(_key);
 
 			_caller.DidNotReceive().Call(Arg.Any<int>(), Arg.Any<int>(), Arg.Any<int>(), Arg.Any<ObservableUpdateType>());
 		}
@@ -142,17 +207,12 @@ public void InvokeCheck_NotObserving_DoesNothing()
 		[Test]
 		public void StopObserveCheck()
 		{
-			_observableDictionary.Observe(_caller.Call);
-			_observableResolverDictionary.Observe(_caller.Call);
-			_observableDictionary.StopObserving(_caller.Call);
-			_observableResolverDictionary.StopObserving(_caller.Call);
+			_dictionary.Observe(_caller.Call);
+			_dictionary.StopObserving(_caller.Call);
 
-			_observableDictionary.Add(_key, _previousValue);
-			_observableResolverDictionary.Add(_key, _previousValue);
-			_observableDictionary[_key] = _previousValue;
-			_observableResolverDictionary[_key] = _previousValue;
-			_observableDictionary.Remove(_key);
-			_observableResolverDictionary.Remove(_key);
+			_dictionary.Add(_key, 0);
+			_dictionary[_key] = 0;
+			_dictionary.Remove(_key);
 
 			_caller.DidNotReceive().Call(Arg.Any<int>(), Arg.Any<int>(), Arg.Any<int>(), Arg.Any<ObservableUpdateType>());
 		}
@@ -160,17 +220,12 @@ public void StopObserveCheck()
 		[Test]
 		public void StopObserve_KeyCheck()
 		{
-			_observableDictionary.Observe(_key, _caller.Call);
-			_observableResolverDictionary.Observe(_key, _caller.Call);
-			_observableDictionary.StopObserving(_key);
-			_observableResolverDictionary.StopObserving(_key);
+			_dictionary.Observe(_key, _caller.Call);
+			_dictionary.StopObserving(_key);
 
-			_observableDictionary.Add(_key, _previousValue);
-			_observableResolverDictionary.Add(_key, _previousValue);
-			_observableDictionary[_key] = _previousValue;
-			_observableResolverDictionary[_key] = _previousValue;
-			_observableDictionary.Remove(_key);
-			_observableResolverDictionary.Remove(_key);
+			_dictionary.Add(_key, 0);
+			_dictionary[_key] = 0;
+			_dictionary.Remove(_key);
 
 			_caller.DidNotReceive().Call(Arg.Any<int>(), Arg.Any<int>(), Arg.Any<int>(), Arg.Any<ObservableUpdateType>());
 		}
@@ -178,17 +233,12 @@ public void StopObserve_KeyCheck()
 		[Test]
 		public void StopObservingAllCheck()
 		{
-			_observableDictionary.Observe(_caller.Call);
-			_observableResolverDictionary.Observe(_caller.Call);
-			_observableDictionary.StopObservingAll(_caller);
-			_observableResolverDictionary.StopObservingAll(_caller);
+			_dictionary.Observe(_caller.Call);
+			_dictionary.StopObservingAll(_caller);
 
-			_observableDictionary.Add(_key, _previousValue);
-			_observableResolverDictionary.Add(_key, _previousValue);
-			_observableDictionary[_key] = _previousValue;
-			_observableResolverDictionary[_key] = _previousValue;
-			_observableDictionary.Remove(_key);
-			_observableResolverDictionary.Remove(_key);
+			_dictionary.Add(_key, 0);
+			_dictionary[_key] = 0;
+			_dictionary.Remove(_key);
 
 			_caller.DidNotReceive().Call(Arg.Any<int>(), Arg.Any<int>(), Arg.Any<int>(), Arg.Any<ObservableUpdateType>());
 		}
@@ -196,19 +246,13 @@ public void StopObservingAllCheck()
 		[Test]
 		public void StopObservingAll_MultipleCalls_Check()
 		{
-			_observableDictionary.Observe(_caller.Call);
-			_observableDictionary.Observe(_caller.Call);
-			_observableResolverDictionary.Observe(_caller.Call);
-			_observableResolverDictionary.Observe(_caller.Call);
-			_observableDictionary.StopObservingAll(_caller);
-			_observableResolverDictionary.StopObservingAll(_caller);
+			_dictionary.Observe(_caller.Call);
+			_dictionary.Observe(_caller.Call);
+			_dictionary.StopObservingAll(_caller);
 
-			_observableDictionary.Add(_key, _previousValue);
-			_observableResolverDictionary.Add(_key, _previousValue);
-			_observableDictionary[_key] = _previousValue;
-			_observableResolverDictionary[_key] = _previousValue;
-			_observableDictionary.Remove(_key);
-			_observableResolverDictionary.Remove(_key);
+			_dictionary.Add(_key, 0);
+			_dictionary[_key] = 0;
+			_dictionary.Remove(_key);
 
 			_caller.DidNotReceive().Call(Arg.Any<int>(), Arg.Any<int>(), Arg.Any<int>(), Arg.Any<ObservableUpdateType>());
 		}
@@ -216,17 +260,12 @@ public void StopObservingAll_MultipleCalls_Check()
 		[Test]
 		public void StopObservingAll_Everything_Check()
 		{
-			_observableDictionary.Observe(_caller.Call);
-			_observableResolverDictionary.Observe(_caller.Call);
-			_observableDictionary.StopObservingAll();
-			_observableResolverDictionary.StopObservingAll();
+			_dictionary.Observe(_caller.Call);
+			_dictionary.StopObservingAll();
 
-			_observableDictionary.Add(_key, _previousValue);
-			_observableResolverDictionary.Add(_key, _previousValue);
-			_observableDictionary[_key] = _previousValue;
-			_observableResolverDictionary[_key] = _previousValue;
-			_observableDictionary.Remove(_key);
-			_observableResolverDictionary.Remove(_key);
+			_dictionary.Add(_key, 0);
+			_dictionary[_key] = 0;
+			_dictionary.Remove(_key);
 
 			_caller.DidNotReceive().Call(Arg.Any<int>(), Arg.Any<int>(), Arg.Any<int>(), Arg.Any<ObservableUpdateType>());
 		}
@@ -234,15 +273,11 @@ public void StopObservingAll_Everything_Check()
 		[Test]
 		public void StopObservingAll_NotObserving_DoesNothing()
 		{
-			_observableDictionary.StopObservingAll(_caller);
-			_observableResolverDictionary.StopObservingAll(_caller);
+			_dictionary.StopObservingAll(_caller);
 
-			_observableDictionary.Add(_key, _previousValue);
-			_observableResolverDictionary.Add(_key, _previousValue);
-			_observableDictionary[_key] = _previousValue;
-			_observableResolverDictionary[_key] = _previousValue;
-			_observableDictionary.Remove(_key);
-			_observableResolverDictionary.Remove(_key);
+			_dictionary.Add(_key, 0);
+			_dictionary[_key] = 0;
+			_dictionary.Remove(_key);
 
 			_caller.DidNotReceive().Call(Arg.Any<int>(), Arg.Any<int>(), Arg.Any<int>(), Arg.Any<ObservableUpdateType>());
 		}
diff --git a/Tests/Editor/ObservableListTest.cs b/Tests/Editor/ObservableListTest.cs
index caa312d..be9b939 100644
--- a/Tests/Editor/ObservableListTest.cs
+++ b/Tests/Editor/ObservableListTest.cs
@@ -22,74 +22,57 @@ public interface IMockCaller<in T>
 		private const int _previousValue = 5;
 		private const int _newValue = 10;
 
-		private ObservableList<int> _observableList;
-		private ObservableResolverList<int> _observableResolverList;
-		private List<int> _list;
+		private ObservableList<int> _list;
+		private IList<int> _mockList;
 		private IMockCaller<int> _caller;
 
 		[SetUp]
 		public void Init()
 		{
 			_caller = Substitute.For<IMockCaller<int>>();
-			_list = new List<int>();
-			_observableList = new ObservableList<int>(_list);
-			_observableResolverList = new ObservableResolverList<int>(() => _list);
+			_mockList = Substitute.For<IList<int>>();
+			_list = new ObservableList<int>(_mockList);
 		}
 
 		[Test]
-		public void ValueCheck()
+		public void AddValue_AddsValueToList()
 		{
 			_list.Add(_previousValue);
 
-			Assert.AreEqual(_previousValue, _observableList[_index]);
-			Assert.AreEqual(_previousValue, _observableResolverList[_index]);
+			Assert.AreEqual(_previousValue, _list[_index]);
 		}
 
 		[Test]
-		public void ValueSetCheck()
+		public void SetValue_UpdatesValue()
 		{
 			const int valueCheck1 = 5;
 			const int valueCheck2 = 6;
-			const int valueCheck3 = 7;
 
 			_list.Add(valueCheck1);
 
-			Assert.AreEqual(valueCheck1, _observableList[_index]);
-			Assert.AreEqual(valueCheck1, _observableResolverList[_index]);
+			Assert.AreEqual(valueCheck1, _list[_index]);
 
-			_observableList[_index] = valueCheck2;
+			_list[_index] = valueCheck2;
 
-			Assert.AreEqual(valueCheck2, _observableList[_index]);
-			Assert.AreEqual(valueCheck2, _observableResolverList[_index]);
-
-			_observableResolverList[_index] = valueCheck3;
-
-			Assert.AreEqual(valueCheck3, _observableList[_index]);
-			Assert.AreEqual(valueCheck3, _observableResolverList[_index]);
+			Assert.AreEqual(valueCheck2, _list[_index]);
 		}
 
 		[Test]
 		public void ObserveCheck()
 		{
-			_observableList.Observe(_caller.Call);
-			_observableResolverList.Observe(_caller.Call);
+			_list.Observe(_caller.Call);
 
 			_caller.DidNotReceive().Call(Arg.Any<int>(), Arg.Any<int>(), Arg.Any<int>(), Arg.Any<ObservableUpdateType>());
 
-			_observableList.Add(_previousValue);
-			_observableResolverList.Add(_previousValue);
-
-			_observableList[_index] = _newValue;
-			_list[_index] = _previousValue;
-			_observableResolverList[_index] = _newValue;
+			_list.Add(_previousValue);
 
-			_observableList.RemoveAt(_index);
 			_list[_index] = _newValue;
-			_observableResolverList.RemoveAt(_index);
 
-			_caller.Received(2).Call(Arg.Any<int>(), Arg.Is(0), Arg.Is(_previousValue), ObservableUpdateType.Added);
-			_caller.Received(2).Call(_index, _previousValue, _newValue, ObservableUpdateType.Updated);
-			_caller.Received(2).Call(_index, _newValue, 0, ObservableUpdateType.Removed);
+			_list.RemoveAt(_index);
+
+			_caller.Received().Call(Arg.Any<int>(), Arg.Is(0), Arg.Is(_previousValue), ObservableUpdateType.Added);
+			_caller.Received().Call(_index, _previousValue, _newValue, ObservableUpdateType.Updated);
+			_caller.Received().Call(_index, _newValue, 0, ObservableUpdateType.Removed);
 		}
 
 		[Test]
@@ -97,11 +80,10 @@ public void InvokeObserveCheck()
 		{
 			_list.Add(_previousValue);
 
-			_observableList.InvokeObserve(_index, _caller.Call);
-			_observableResolverList.InvokeObserve(_index, _caller.Call);
+			_list.InvokeObserve(_index, _caller.Call);
 
 			_caller.DidNotReceive().Call(_index, _previousValue, _previousValue, ObservableUpdateType.Added);
-			_caller.Received(2).Call(_index, _previousValue, _previousValue, ObservableUpdateType.Updated);
+			_caller.Received().Call(_index, _previousValue, _previousValue, ObservableUpdateType.Updated);
 			_caller.DidNotReceive().Call(_index, _previousValue, _previousValue, ObservableUpdateType.Removed);
 		}
 
@@ -109,17 +91,14 @@ public void InvokeObserveCheck()
 		public void InvokeCheck()
 		{
 			_list.Add(_previousValue);
-
-			_observableList.Observe(_caller.Call);
-			_observableResolverList.Observe(_caller.Call);
+			_list.Observe(_caller.Call);
 
 			_caller.DidNotReceive().Call(Arg.Any<int>(), Arg.Any<int>(), Arg.Any<int>(), Arg.Any<ObservableUpdateType>());
 
-			_observableList.InvokeUpdate(_index);
-			_observableResolverList.InvokeUpdate(_index);
+			_list.InvokeUpdate(_index);
 
 			_caller.DidNotReceive().Call(Arg.Any<int>(), Arg.Any<int>(), Arg.Any<int>(), ObservableUpdateType.Added);
-			_caller.Received(2).Call(_index, _previousValue, _previousValue, ObservableUpdateType.Updated);
+			_caller.Received().Call(_index, _previousValue, _previousValue, ObservableUpdateType.Updated);
 			_caller.DidNotReceive().Call(Arg.Any<int>(), Arg.Any<int>(), Arg.Any<int>(), ObservableUpdateType.Removed);
 		}
 
@@ -127,9 +106,7 @@ public void InvokeCheck()
 		public void InvokeCheck_NotObserving_DoesNothing()
 		{
 			_list.Add(_previousValue);
-
-			_observableList.InvokeUpdate(_index);
-			_observableResolverList.InvokeUpdate(_index);
+			_list.InvokeUpdate(_index);
 
 			_caller.DidNotReceive().Call(Arg.Any<int>(), Arg.Any<int>(), Arg.Any<int>(), Arg.Any<ObservableUpdateType>());
 		}
@@ -137,17 +114,13 @@ public void InvokeCheck_NotObserving_DoesNothing()
 		[Test]
 		public void StopObserveCheck()
 		{
-			_observableList.Observe(_caller.Call);
-			_observableResolverList.Observe(_caller.Call);
-			_observableList.StopObserving(_caller.Call);
-			_observableResolverList.StopObserving(_caller.Call);
-
-			_observableList.Add(_previousValue);
-			_observableResolverList.Add(_previousValue);
-			_observableList[_index] = _previousValue;
-			_observableResolverList[_index] = _previousValue;
-			_observableList.RemoveAt(_index);
-			_observableResolverList.RemoveAt(_index);
+			_list.Observe(_caller.Call);
+			_list.StopObserving(_caller.Call);
+			_list.Add(_previousValue);
+
+			_list[_index] = _previousValue;
+
+			_list.RemoveAt(_index);
 
 			_caller.DidNotReceive().Call(Arg.Any<int>(), Arg.Any<int>(), Arg.Any<int>(), Arg.Any<ObservableUpdateType>());
 		}
@@ -155,15 +128,10 @@ public void StopObserveCheck()
 		[Test]
 		public void StopObservingAllCheck()
 		{
-			_observableList.Observe(_caller.Call);
-			_observableResolverList.Observe(_caller.Call);
-			_observableList.StopObservingAll(_caller);
-			_observableResolverList.StopObservingAll(_caller);
-
-			_observableList.Add(_previousValue);
-			_observableResolverList.Add(_previousValue);
-			_observableList.InvokeUpdate(_index);
-			_observableResolverList.InvokeUpdate(_index);
+			_list.Observe(_caller.Call);
+			_list.StopObservingAll(_caller);
+			_list.Add(_previousValue);
+			_list.InvokeUpdate(_index);
 
 			_caller.DidNotReceive().Call(Arg.Any<int>(), Arg.Any<int>(), Arg.Any<int>(), Arg.Any<ObservableUpdateType>());
 		}
@@ -171,17 +139,11 @@ public void StopObservingAllCheck()
 		[Test]
 		public void StopObservingAll_MultipleCalls_Check()
 		{
-			_observableList.Observe(_caller.Call);
-			_observableList.Observe(_caller.Call);
-			_observableResolverList.Observe(_caller.Call);
-			_observableResolverList.Observe(_caller.Call);
-			_observableList.StopObservingAll(_caller);
-			_observableResolverList.StopObservingAll(_caller);
-
-			_observableList.Add(_previousValue);
-			_observableResolverList.Add(_previousValue);
-			_observableList.InvokeUpdate(_index);
-			_observableResolverList.InvokeUpdate(_index);
+			_list.Observe(_caller.Call);
+			_list.Observe(_caller.Call);
+			_list.StopObservingAll(_caller);
+			_list.Add(_previousValue);
+			_list.InvokeUpdate(_index);
 
 			_caller.DidNotReceive().Call(Arg.Any<int>(), Arg.Any<int>(), Arg.Any<int>(), Arg.Any<ObservableUpdateType>());
 		}
@@ -189,15 +151,11 @@ public void StopObservingAll_MultipleCalls_Check()
 		[Test]
 		public void StopObservingAll_Everything_Check()
 		{
-			_observableList.Observe(_caller.Call);
-			_observableResolverList.Observe(_caller.Call);
-			_observableList.StopObservingAll();
-			_observableResolverList.StopObservingAll();
+			_list.Observe(_caller.Call);
+			_list.StopObservingAll();
 
-			_observableList.Add(_previousValue);
-			_observableResolverList.Add(_previousValue);
-			_observableList.InvokeUpdate(_index);
-			_observableResolverList.InvokeUpdate(_index);
+			_list.Add(_previousValue);
+			_list.InvokeUpdate(_index);
 
 			_caller.DidNotReceive().Call(Arg.Any<int>(), Arg.Any<int>(), Arg.Any<int>(), Arg.Any<ObservableUpdateType>());
 		}
@@ -205,13 +163,10 @@ public void StopObservingAll_Everything_Check()
 		[Test]
 		public void StopObservingAll_NotObserving_DoesNothing()
 		{
-			_observableList.StopObservingAll();
-			_observableResolverList.StopObservingAll();
+			_list.StopObservingAll();
 
-			_observableList.Add(_previousValue);
-			_observableResolverList.Add(_previousValue);
-			_observableList.InvokeUpdate(_index);
-			_observableResolverList.InvokeUpdate(_index);
+			_list.Add(_previousValue);
+			_list.InvokeUpdate(_index);
 
 			_caller.DidNotReceive().Call(Arg.Any<int>(), Arg.Any<int>(), Arg.Any<int>(), Arg.Any<ObservableUpdateType>());
 		}
diff --git a/Tests/Editor/ObservableResolverDictionaryTest.cs b/Tests/Editor/ObservableResolverDictionaryTest.cs
new file mode 100644
index 0000000..d3e7be9
--- /dev/null
+++ b/Tests/Editor/ObservableResolverDictionaryTest.cs
@@ -0,0 +1,89 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Reflection;
+using GameLovers;
+using NSubstitute;
+using NUnit.Framework;
+using UnityEngine;
+
+// ReSharper disable once CheckNamespace
+
+namespace GameLoversEditor.DataExtensions.Tests
+{
+	[TestFixture]
+	public class ObservableResolverDictionaryTest
+	{
+		private int _key = 0;
+		private string _value = "1";
+		private ObservableResolverDictionary<int, int, int, string> _dictionary;
+		private IDictionary<int, string> _mockDictionary;
+
+		[SetUp]
+		public void Init()
+		{
+			_mockDictionary = Substitute.For<IDictionary<int, string>>();
+			_dictionary = new ObservableResolverDictionary<int, int, int, string>(
+				_mockDictionary,
+				origin => new KeyValuePair<int, int>(origin.Key, int.Parse(origin.Value)),
+				(key, value) => new KeyValuePair<int, string>(key, key.ToString()));
+
+			_mockDictionary[_key].Returns(_value);
+			_mockDictionary.TryGetValue(_key, out _).Returns(callInfo =>
+			{
+				callInfo[1] = _mockDictionary[_key];
+				return true;
+			});
+		}
+
+		[Test]
+		public void TryGetOriginValue_KeyExists_ReturnsTrueAndOutValue()
+		{
+			Assert.IsTrue(_dictionary.TryGetOriginValue(_key, out var value));
+		}
+
+		[Test]
+		public void TryGetOriginValue_KeyDoesNotExist_ReturnsFalseAndOutDefault()
+		{
+			var result = _dictionary.TryGetOriginValue(999, out var value);
+
+			Assert.IsFalse(result);
+			Assert.IsNull(value);
+		}
+
+		[Test]
+		public void AddOrigin_AddsValueToOriginDictionary()
+		{
+			_dictionary.AddOrigin(_key, _value);
+
+			_mockDictionary.Received().Add(_key, _value);
+		}
+
+		[Test]
+		public void UpdateOrigin_UpdatesValueInOriginDictionary()
+		{
+			_dictionary.AddOrigin(_key, _value);
+			_dictionary.UpdateOrigin(_key, _value);
+
+			_mockDictionary.Received()[_key] = _value;
+		}
+
+		[Test]
+		public void RemoveOrigin_RemovesValueFromOriginDictionary()
+		{
+			_dictionary.AddOrigin(_key, _value);
+
+			Assert.IsTrue(_dictionary.RemoveOrigin(_key));
+			_mockDictionary.Received().Remove(_key);
+		}
+
+		[Test]
+		public void ClearOrigin_ClearsOriginDictionary()
+		{
+			_dictionary.ClearOrigin();
+
+			_mockDictionary.Received().Clear();
+		}
+	}
+}
\ No newline at end of file
diff --git a/Tests/Editor/ObservableResolverDictionaryTest.cs.meta b/Tests/Editor/ObservableResolverDictionaryTest.cs.meta
new file mode 100644
index 0000000..a08312e
--- /dev/null
+++ b/Tests/Editor/ObservableResolverDictionaryTest.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 4caa0f2c6d7acb043a06b1edc62fbb91
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Tests/Editor/floatPTests.cs b/Tests/Editor/floatPTests.cs
index 95ed1a6..44e9a1a 100644
--- a/Tests/Editor/floatPTests.cs
+++ b/Tests/Editor/floatPTests.cs
@@ -11,17 +11,16 @@ public class floatPTests
 		[Test]
 		public void Representation()
 		{
-			Assert.AreEqual(floatP.Zero, 0f);
-			Assert.AreNotEqual(-floatP.Zero, -0f);
+			Assert.AreEqual(floatP.Zero, (floatP)0f);
+			Assert.AreEqual(-floatP.Zero, (floatP) (-0f));
 			Assert.AreEqual(floatP.Zero, -floatP.Zero);
-			Assert.AreEqual(floatP.NaN, float.NaN);
-			Assert.AreEqual(floatP.One, 1f);
-			Assert.AreEqual(floatP.MinusOne, -1f);
-			Assert.AreEqual(floatP.PositiveInfinity, float.PositiveInfinity);
-			Assert.AreEqual(floatP.NegativeInfinity, float.NegativeInfinity);
-			Assert.AreEqual(floatP.Epsilon, float.Epsilon);
-			Assert.AreEqual(floatP.MaxValue, float.MaxValue);
-			Assert.AreEqual(floatP.MinValue, float.MinValue);
+			Assert.AreEqual(floatP.NaN, (floatP) float.NaN);
+			Assert.AreEqual(floatP.MinusOne, (floatP)  (- 1f));
+			Assert.AreEqual(floatP.PositiveInfinity, (floatP)float.PositiveInfinity);
+			Assert.AreEqual(floatP.NegativeInfinity, (floatP)float.NegativeInfinity);
+			Assert.AreEqual(floatP.Epsilon, (floatP)float.Epsilon);
+			Assert.AreEqual(floatP.MaxValue, (floatP)float.MaxValue);
+			Assert.AreEqual(floatP.MinValue, (floatP)float.MinValue);
 		}
 
 		[Test]
@@ -43,24 +42,24 @@ public void Equality()
 		[Test]
 		public void Addition()
 		{
-			Assert.AreEqual(floatP.One + floatP.One, 2f);
-			Assert.AreEqual(floatP.One - floatP.One, 0f);
+			Assert.AreEqual(floatP.One + floatP.One, (floatP)2f);
+			Assert.AreEqual(floatP.One - floatP.One, (floatP)0f);
 		}
 
 		[Test]
 		public void Multiplication()
 		{
-			Assert.AreEqual(floatP.PositiveInfinity * floatP.Zero, float.PositiveInfinity * 0f);
-			Assert.AreEqual(floatP.PositiveInfinity * (-floatP.Zero), float.PositiveInfinity * (-0f));
-			Assert.AreEqual(floatP.PositiveInfinity * floatP.One, float.PositiveInfinity * 1f);
-			Assert.AreEqual(floatP.PositiveInfinity * floatP.MinusOne, float.PositiveInfinity * -1f);
+			Assert.AreEqual(floatP.PositiveInfinity * floatP.Zero, (floatP) (float.PositiveInfinity * 0f));
+			Assert.AreEqual(floatP.PositiveInfinity * (-floatP.Zero), (floatP)(float.PositiveInfinity * (-0f)));
+			Assert.AreEqual(floatP.PositiveInfinity * floatP.One, (floatP)(float.PositiveInfinity * 1f));
+			Assert.AreEqual(floatP.PositiveInfinity * floatP.MinusOne, (floatP)(float.PositiveInfinity * -1f));
 
-			Assert.AreEqual(floatP.NegativeInfinity * floatP.Zero, float.NegativeInfinity * 0f);
-			Assert.AreEqual(floatP.NegativeInfinity * (-floatP.Zero), float.NegativeInfinity * (-0f));
-			Assert.AreEqual(floatP.NegativeInfinity * floatP.One, float.NegativeInfinity * 1f);
-			Assert.AreEqual(floatP.NegativeInfinity * floatP.MinusOne, float.NegativeInfinity * -1f);
+			Assert.AreEqual(floatP.NegativeInfinity * floatP.Zero, (floatP)(float.NegativeInfinity * 0f));
+			Assert.AreEqual(floatP.NegativeInfinity * (-floatP.Zero), (floatP)(float.NegativeInfinity * (-0f)));
+			Assert.AreEqual(floatP.NegativeInfinity * floatP.One, (floatP)(float.NegativeInfinity * 1f));
+			Assert.AreEqual(floatP.NegativeInfinity * floatP.MinusOne, (floatP)(float.NegativeInfinity * -1f));
 
-			Assert.AreEqual(floatP.One * floatP.One, 1f);
+			Assert.AreEqual(floatP.One * floatP.One, (floatP)1f);
 		}
 	}
 }
diff --git a/package.json b/package.json
index 10d2dfc..af7dfed 100644
--- a/package.json
+++ b/package.json
@@ -2,8 +2,8 @@
   "name": "com.gamelovers.dataextensions",
   "displayName": "Unity Data Type Extensions",
   "author": "Miguel Tomas",
-  "version": "0.5.1",
-  "unity": "2021.4",
+  "version": "0.6.0",
+  "unity": "2022.4",
   "license": "MIT",
   "description": "This package extends various sets of data types to be used in any type of data containers or persistent serializable data",
   "type": "library",