diff --git a/.BinaryPrefs/Packages/manifest.json b/.BinaryPrefs/Packages/manifest.json
index d53a375..07cd7a3 100644
--- a/.BinaryPrefs/Packages/manifest.json
+++ b/.BinaryPrefs/Packages/manifest.json
@@ -2,13 +2,11 @@
"dependencies": {
"com.appegy.binary-prefs": "file:../..",
"com.boundfoxstudios.fluentassertions": "6.8.0",
- "com.dbrizov.naughtyattributes": "https://github.com/dbrizov/NaughtyAttributes.git#upm",
"com.unity.2d.sprite": "1.0.0",
- "com.unity.ide.rider": "3.0.28",
- "com.unity.ide.visualstudio": "2.0.22",
+ "com.unity.ide.rider": "3.0.31",
"com.unity.mobile.android-logcat": "1.4.2",
"com.unity.test-framework": "2.0.1-pre.18",
- "com.unity.textmeshpro": "3.0.6",
+ "com.unity.textmeshpro": "3.0.9",
"com.unity.ugui": "1.0.0",
"com.yasirkula.ingamedebugconsole": "https://github.com/yasirkula/UnityIngameDebugConsole.git",
"net.tnrd.nsubstitute": "5.1.0",
diff --git a/.BinaryPrefs/Packages/packages-lock.json b/.BinaryPrefs/Packages/packages-lock.json
index 9f22339..cd68fa1 100644
--- a/.BinaryPrefs/Packages/packages-lock.json
+++ b/.BinaryPrefs/Packages/packages-lock.json
@@ -13,13 +13,6 @@
"dependencies": {},
"url": "https://package.openupm.com"
},
- "com.dbrizov.naughtyattributes": {
- "version": "https://github.com/dbrizov/NaughtyAttributes.git#upm",
- "depth": 0,
- "source": "git",
- "dependencies": {},
- "hash": "8a8fa5a9659a6d63f196391c71e06c4286c8acd7"
- },
"com.unity.2d.sprite": {
"version": "1.0.0",
"depth": 0,
@@ -34,7 +27,7 @@
"url": "https://packages.unity.com"
},
"com.unity.ide.rider": {
- "version": "3.0.28",
+ "version": "3.0.31",
"depth": 0,
"source": "registry",
"dependencies": {
@@ -42,15 +35,6 @@
},
"url": "https://packages.unity.com"
},
- "com.unity.ide.visualstudio": {
- "version": "2.0.22",
- "depth": 0,
- "source": "registry",
- "dependencies": {
- "com.unity.test-framework": "1.1.9"
- },
- "url": "https://packages.unity.com"
- },
"com.unity.mobile.android-logcat": {
"version": "1.4.2",
"depth": 0,
@@ -70,7 +54,7 @@
"url": "https://packages.unity.com"
},
"com.unity.textmeshpro": {
- "version": "3.0.6",
+ "version": "3.0.9",
"depth": 0,
"source": "registry",
"dependencies": {
diff --git a/Runtime/BinaryStorage.Builder.cs b/Runtime/BinaryStorage.Builder.cs
index a361241..4295fa9 100644
--- a/Runtime/BinaryStorage.Builder.cs
+++ b/Runtime/BinaryStorage.Builder.cs
@@ -11,9 +11,7 @@ public partial class BinaryStorage
static partial void ThrowIfFilePathLocked(string filePath);
static partial void UnlockFilePathInEditor(string filePath);
- ///
- /// Creates and configures a new instance of with default settings.
- ///
+ /// Creates and configures a new instance of with default settings.
/// The file path for the storage.
/// A configured instance.
public static BinaryStorage Get(string filePath)
@@ -24,9 +22,7 @@ public static BinaryStorage Get(string filePath)
.Build();
}
- ///
- /// Begins the construction of a new instance.
- ///
+ /// Begins the construction of a new instance.
/// The file path for the storage.
/// A for configuring the instance.
public static Builder Construct(string filePath)
@@ -36,9 +32,7 @@ public static Builder Construct(string filePath)
return new Builder(filePath);
}
- ///
- /// Deletes the storage file at the specified path.
- ///
+ /// Deletes the storage file at the specified path.
/// The path to the storage file.
internal static void Delete(string storagePath)
{
@@ -48,9 +42,7 @@ internal static void Delete(string storagePath)
}
}
- ///
- /// Provides a fluent interface for configuring and building a instance.
- ///
+ /// Provides a fluent interface for configuring and building a instance.
public class Builder
{
private readonly string _filePath;
@@ -64,9 +56,7 @@ internal Builder(string filePath)
_filePath = filePath;
}
- ///
- /// Enables automatic saving of changes to the storage.
- ///
+ /// Enables automatic saving of changes to the storage.
/// The current instance for method chaining.
public Builder EnableAutoSaveOnChange()
{
@@ -74,18 +64,14 @@ public Builder EnableAutoSaveOnChange()
return this;
}
- ///
- /// Specifies the behavior when a requested key is not found in the storage.
- ///
+ /// Specifies the behavior when a requested key is not found in the storage.
/// The current instance for method chaining.
public Builder SetMissingKeyBehaviour(MissingKeyBehavior behavior)
{
_missingKeyBehavior = behavior;
return this;
}
- ///
- /// Specifies the behavior when the type of value associated with a key does not match the expected type.
- ///
+ /// Specifies the behavior when the type of value associated with a key does not match the expected type.
/// The type mismatch behavior.
/// The current instance for method chaining.
public Builder SetTypeMismatchBehaviour(TypeMismatchBehaviour behavior)
@@ -94,9 +80,7 @@ public Builder SetTypeMismatchBehaviour(TypeMismatchBehaviour behavior)
return this;
}
- ///
- /// Adds serializers for primitive types to the storage configuration.
- ///
+ /// Adds serializers for primitive types to the storage configuration.
/// The current instance for method chaining.
public Builder AddPrimitiveTypes()
{
@@ -124,9 +108,7 @@ public Builder AddPrimitiveTypes()
.AddTypeSerializer(Vector3IntSerializer.Shared);
}
- ///
- /// Adds a serializer for a specified type to the storage configuration.
- ///
+ /// Adds a serializer for a specified type to the storage configuration.
/// The type to be serialized.
/// The serializer for the specified type.
/// The current instance for method chaining.
@@ -141,9 +123,7 @@ public Builder AddTypeSerializer(TypeSerializer typeSerializer)
return this;
}
- ///
- /// Adds support for a specified enum type to the storage configuration.
- ///
+ /// Adds support for a specified enum type to the storage configuration.
/// The enum type to be supported.
/// Whether to use the full name of the enum type.
/// The current instance for method chaining.
@@ -185,9 +165,7 @@ public Builder SupportEnum(bool useFullName = false)
return this;
}
- ///
- /// Adds support for lists of a specified type to the storage configuration.
- ///
+ /// Adds support for lists of a specified type to the storage configuration.
/// The type of elements in the list.
/// The current instance for method chaining.
/// Thrown if the specified type is not supported.
@@ -201,9 +179,7 @@ public Builder SupportListsOf()
return AddTypeSerializer(new CollectionTypeSerializer>(section.Serializer));
}
- ///
- /// Adds support for sets of a specified type to the storage configuration.
- ///
+ /// Adds support for sets of a specified type to the storage configuration.
/// The type of elements in the set.
/// The current instance for method chaining.
/// Thrown if the specified type is not supported.
@@ -217,9 +193,7 @@ public Builder SupportSetsOf()
return AddTypeSerializer(new CollectionTypeSerializer>(section.Serializer));
}
- ///
- /// Adds support for dictionaries of specified key and value types to the storage configuration.
- ///
+ /// Adds support for dictionaries of specified key and value types to the storage configuration.
/// The type of the dictionary keys.
/// The type of the dictionary values.
/// The current instance for method chaining.
@@ -236,11 +210,10 @@ public Builder SupportDictionariesOf()
return AddTypeSerializer(new CollectionTypeSerializer, ReactiveDictionary>(kvSerializer));
}
- ///
- /// Builds and returns the configured instance.
- ///
+ /// Builds and returns the configured instance.
/// The configured instance.
- /// Thrown if the storage fails to load data from disk.
+ /// Thrown if the storage is disposed.
+ /// An I/O error occurred
public BinaryStorage Build()
{
try
diff --git a/Runtime/BinaryStorage.cs b/Runtime/BinaryStorage.cs
index 7be8053..6952b45 100644
--- a/Runtime/BinaryStorage.cs
+++ b/Runtime/BinaryStorage.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
using JetBrains.Annotations;
using UnityEngine;
@@ -7,44 +8,31 @@
namespace Appegy.Storage
{
- ///
- /// Manages a binary storage system for saving, retrieving, and managing records of various types.
- ///
+ /// Manages a binary storage system for saving, retrieving, and managing records of various types.
public partial class BinaryStorage : IDisposable
{
private readonly string _storageFilePath;
private readonly IReadOnlyList _supportedTypes;
private readonly Dictionary _data = new();
+ private readonly Dictionary _collections = new();
private int _changeScopeCounter;
- ///
- /// Gets or sets a value indicating whether data should be saved automatically.
- ///
+ /// Gets or sets a value indicating whether data should be saved automatically.
public bool AutoSave { get; set; }
- ///
- /// Gets or sets the behavior when a requested key is not found in the storage.
- ///
+ /// Gets or sets the behavior when a requested key is not found in the storage.
public MissingKeyBehavior MissingKeyBehavior { get; set; } = MissingKeyBehavior.ReturnDefaultValueOnly;
- ///
- /// Gets or sets the behavior when the type of a value associated with a key does not match the expected type.
- ///
+ /// Gets or sets the behavior when the type of value associated with a key does not match the expected type.
public TypeMismatchBehaviour TypeMismatchBehaviour { get; set; } = TypeMismatchBehaviour.OverrideValueAndType;
- ///
- /// Gets a value indicating whether there are unsaved changes.
- ///
+ /// Gets a value indicating whether there are unsaved changes.
public bool IsDirty { get; private set; }
- ///
- /// Gets a value indicating whether the storage has been disposed.
- ///
+ /// Gets a value indicating whether the storage has been disposed.
public bool IsDisposed { get; private set; }
- ///
- /// Initializes a new instance of the class.
- ///
+ /// Initializes a new instance of the class.
/// The file path for storing data.
/// The list of supported types for storage.
internal BinaryStorage(string storageFilePath, IReadOnlyList supportedTypes)
@@ -53,24 +41,35 @@ internal BinaryStorage(string storageFilePath, IReadOnlyList supp
_supportedTypes = supportedTypes;
}
+ #region Events
+
+ /// Occurs when a key is added to the storage.
+ public event Action OnKeyAdded;
+
+ /// Occurs when a key is changed in the storage.
+ public event Action OnKeyChanged;
+
+ /// Occurs when a key is removed from the storage.
+ public event Action OnKeyRemoved;
+
+ #endregion
+
#region Public API
- ///
- /// Determines whether the specified key exists in the storage.
- ///
+ /// Determines whether the specified key exists in the storage.
/// The key to check for existence.
/// True if the key exists; otherwise, false.
+ /// Thrown if the storage is disposed.
public virtual bool Has(string key)
{
ThrowIfDisposed();
return _data.ContainsKey(key);
}
- ///
- /// Gets the type of the value associated with the specified key.
- ///
+ /// Gets the type of the value associated with the specified key.
/// The key to get the type for.
/// The type of the value associated with the key, or null if the key does not exist.
+ /// Thrown if the storage is disposed.
[CanBeNull]
public virtual Type TypeOf(string key)
{
@@ -78,11 +77,11 @@ public virtual Type TypeOf(string key)
return _data.TryGetValue(key, out var record) ? record.Type : null;
}
- ///
- /// Determines whether the storage supports the specified type.
- ///
+ /// Determines whether the storage supports the specified type.
/// The type to check for support.
/// True if the type is supported; otherwise, false.
+ /// Thrown if the storage is disposed.
+ /// Thrown if the type is a collection.
public virtual bool Supports()
{
ThrowIfDisposed();
@@ -90,43 +89,45 @@ public virtual bool Supports()
return _supportedTypes.Any(c => c is TypedBinarySection);
}
- ///
- /// Gets the value associated with the specified key.
- ///
+ /// Gets the value associated with the specified key.
/// The type of the value.
/// The key to get the value for.
/// The default value to use if the key does not exist.
+ /// Override default behavior when a requested key is not found in the storage.
/// The value associated with the key.
+ /// Thrown if the storage is disposed.
+ /// Thrown if the type is a collection.
+ /// Thrown if the type is not registered.
+ /// Thrown if the type of the value associated with the key does not match the expected type.
public virtual T Get(string key, T defaultValue = default, MissingKeyBehavior? overrideMissingKeyBehavior = null)
{
ThrowIfDisposed();
ThrowIfCollection();
var record = GetRecord(key);
var missingKeyBehavior = overrideMissingKeyBehavior ?? MissingKeyBehavior;
- switch (record)
+ return record switch
{
- case Record typedRecord:
- return typedRecord.Value;
- case not null:
- throw new UnexpectedTypeException(key, nameof(Get), record.Type, typeof(T));
- case null:
- return missingKeyBehavior switch
- {
- MissingKeyBehavior.InitializeWithDefaultValue => AddRecord(key, defaultValue).Value,
- MissingKeyBehavior.ReturnDefaultValueOnly => defaultValue,
- _ => throw new UnexpectedEnumException(typeof(MissingKeyBehavior), missingKeyBehavior)
- };
- }
+ Record typedRecord => typedRecord.Value,
+ not null => throw new UnexpectedTypeException(key, nameof(Get), record.Type, typeof(T)),
+ null => missingKeyBehavior switch
+ {
+ MissingKeyBehavior.InitializeWithDefaultValue => AddRecord(key, defaultValue).Value,
+ MissingKeyBehavior.ReturnDefaultValueOnly => defaultValue,
+ _ => throw new UnexpectedEnumException(typeof(MissingKeyBehavior), missingKeyBehavior)
+ }
+ };
}
- ///
- /// Sets the value for the specified key.
- ///
+ /// Sets the value for the specified key.
/// The type of the value.
/// The key to set the value for.
/// The value to set.
/// Whether to override the value if the key already exists but with another type.
/// True if the value was set; otherwise, false.
+ /// Thrown if the storage is disposed.
+ /// Thrown if the type is a collection.
+ /// Thrown if the type is not registered.
+ /// Thrown if the type of the value associated with the key does not match the expected type.
public virtual bool Set(string key, T value, TypeMismatchBehaviour? overrideTypeMismatchBehaviour = null)
{
ThrowIfDisposed();
@@ -141,7 +142,7 @@ public virtual bool Set(string key, T value, TypeMismatchBehaviour? overrideT
if (record is Record typedRecord)
{
- return ChangeRecord(typedRecord, value);
+ return ChangeRecord(key, typedRecord, value);
}
var mismatchBehaviour = overrideTypeMismatchBehaviour ?? TypeMismatchBehaviour;
@@ -208,18 +209,16 @@ public virtual int RemoveAll()
return count;
}
- ///
- /// Saves the current data to disk.
- ///
+ /// Saves the current data to disk.
+ /// Thrown if the storage is disposed.
public virtual void Save()
{
SaveDataFromDisk();
}
- ///
- /// Begins a scope for making multiple changes.
- ///
+ /// Begins a scope for making multiple changes.
/// An IDisposable to end the scope.
+ /// Thrown if the storage is disposed.
public IDisposable MultipleChangeScope()
{
ThrowIfDisposed();
@@ -229,72 +228,67 @@ public IDisposable MultipleChangeScope()
#region Collections
- ///
- /// Determines whether the storage supports lists of the specified type.
- ///
+ /// Determines whether the storage supports lists of the specified type.
/// The type to check for support.
/// True if lists of the type are supported; otherwise, false.
+ /// Thrown if the storage is disposed.
public virtual bool SupportsListsOf() => SupportsCollectionOf>();
- ///
- /// Determines whether the storage supports sets of the specified type.
- ///
+ /// Determines whether the storage supports sets of the specified type.
/// The type to check for support.
/// True if sets of the type are supported; otherwise, false.
+ /// Thrown if the storage is disposed.
public virtual bool SupportsSetsOf() => SupportsCollectionOf>();
- ///
- /// Determines whether the storage supports dictionaries of the specified key and value types.
- ///
+ /// Determines whether the storage supports dictionaries of the specified key and value types.
/// The type of the dictionary keys.
/// The type of the dictionary values.
/// True if dictionaries of the key and value types are supported; otherwise, false.
public virtual bool SupportsDictionariesOf() => SupportsCollectionOf, ReactiveDictionary>();
- ///
- /// Gets the list associated with the specified key.
- ///
+ /// Gets the list associated with the specified key.
/// The type of the list elements.
/// The key to get the list for.
/// The list associated with the key.
+ /// Thrown if the storage is disposed.
+ /// Thrown if the type is not registered.
public IList GetListOf(string key) => GetCollectionOf>(key);
- ///
- /// Gets the set associated with the specified key.
- ///
+ /// Gets the set associated with the specified key.
/// The type of the set elements.
/// The key to get the set for.
/// The set associated with the key.
+ /// Thrown if the storage is disposed.
+ /// Thrown if the type is not registered.
public ISet GetSetOf(string key) => GetCollectionOf>(key);
- ///
- /// Gets the dictionary associated with the specified key.
- ///
+ /// Gets the dictionary associated with the specified key.
/// The type of the dictionary keys.
/// The type of the dictionary values.
/// The key to get the dictionary for.
/// The dictionary associated with the key.
+ /// Thrown if the storage is disposed.
+ /// Thrown if the type is not registered.
public IDictionary GetDictionaryOf(string key) => GetCollectionOf, ReactiveDictionary>(key);
- ///
- /// Determines whether the specified collection type is supported.
- ///
+ /// Determines whether the specified collection type is supported.
/// The type of elements in the collection.
/// The type of the collection.
/// true if the specified collection type is supported; otherwise, false.
+ /// Thrown if the storage is disposed.
private bool SupportsCollectionOf() where TCollection : IReactiveCollection
{
ThrowIfDisposed();
return _supportedTypes.Any(c => c is TypedBinarySection);
}
- ///
- /// Gets the collection associated with the specified key.
- ///
+ /// Gets the collection associated with the specified key.
/// The type of the collection elements.
/// The type of the collection.
/// The key to get the collection for.
/// The collection associated with the key.
+ /// Thrown if the storage is disposed.
+ /// Thrown if the type is not registered.
private TCollection GetCollectionOf(string key)
where TCollection : ICollection, IReactiveCollection, new()
{
@@ -313,13 +307,13 @@ private TCollection GetCollectionOf(string key)
#region Mutable methods
- ///
- /// Adds a new record with the specified key and value.
- ///
+ /// Adds a new record with the specified key and value.
/// The type of the value.
/// The key to add the record for.
/// The value to add.
/// The added record.
+ /// Thrown if the type is not registered.
+ /// Thrown if the storage is disposed.
private Record AddRecord(string key, T value)
{
var typeIndex = _supportedTypes.FindIndex(static c => c is TypedBinarySection);
@@ -334,36 +328,38 @@ private Record AddRecord(string key, T value)
_data.Add(key, record);
if (value is IReactiveCollection rc)
{
- rc.OnChanged += MarkChanged;
+ _collections.Add(rc, key);
+ rc.OnChanged += ReactiveCollectionChanged;
}
MarkChanged();
+ OnKeyAdded?.Invoke(key);
return record;
}
- ///
- /// Changes the value of an existing record.
- ///
+ /// Changes the value of an existing record.
/// The type of the value.
+ /// The key to change the record for.
/// The record to change.
/// The new value.
/// True if the value was changed; otherwise, false.
- private bool ChangeRecord(Record record, T value)
+ /// Thrown if the storage is disposed.
+ private bool ChangeRecord(string key, Record record, T value)
{
var serializer = ((TypedBinarySection)_supportedTypes[record.TypeIndex]).Serializer;
- var result = serializer.Equals(record.Value, value);
- if (!result)
+ var equals = serializer.Equals(record.Value, value);
+ if (!equals)
{
record.Value = value;
MarkChanged();
+ OnKeyChanged?.Invoke(key);
}
- return !result;
+ return !equals;
}
- ///
- /// Removes the record associated with the specified key.
- ///
+ /// Removes the record associated with the specified key.
/// The key to remove the record for.
/// True if the record was removed; otherwise, false.
+ /// Thrown if the storage is disposed.
private bool RemoveRecord(string key)
{
if (!_data.TryGetValue(key, out var value))
@@ -372,26 +368,28 @@ private bool RemoveRecord(string key)
}
if (value.Object is IReactiveCollection rc)
{
- rc.OnChanged -= MarkChanged;
+ rc.OnChanged -= ReactiveCollectionChanged;
rc.Dispose();
+ _collections.Remove(rc);
}
_supportedTypes[value.TypeIndex].Count--;
_data.Remove(key);
MarkChanged();
+ OnKeyRemoved?.Invoke(key);
return true;
}
- ///
- /// Removes all records from the storage.
- ///
+ /// Removes all records from the storage.
+ /// Thrown if the storage is disposed.
private void RemoveAllRecords()
{
using (MultipleChangeScope())
{
foreach (var rc in _data.Values.Select(c => c.Object).OfType())
{
- rc.OnChanged -= MarkChanged;
+ rc.OnChanged -= ReactiveCollectionChanged;
rc.Dispose();
+ _collections.Remove(rc);
}
_data.Clear();
foreach (var section in _supportedTypes)
@@ -406,9 +404,7 @@ private void RemoveAllRecords()
#region Private methods
- ///
- /// Gets the record associated with the specified key.
- ///
+ /// Gets the record associated with the specified key.
/// The key to get the record for.
/// The record associated with the key, or null if the key does not exist.
[CanBeNull]
@@ -420,6 +416,7 @@ private Record GetRecord(string key)
///
/// Decreases the change scope counter and saves data if necessary.
///
+ /// Thrown if the storage is disposed.
private void DecreaseCounter()
{
if (_changeScopeCounter == 0)
@@ -438,9 +435,19 @@ private void DecreaseCounter()
}
}
- ///
- /// Marks the storage as changed and saves data if necessary.
- ///
+ /// Reacts to a change in a reactive collection.
+ /// Thrown if the storage is disposed.
+ private void ReactiveCollectionChanged(IReactiveCollection collection)
+ {
+ if (_collections.TryGetValue(collection, out var key))
+ {
+ MarkChanged();
+ OnKeyChanged?.Invoke(key);
+ }
+ }
+
+ /// Marks the storage as changed and saves data if necessary.
+ /// Thrown if the storage is disposed.
private void MarkChanged()
{
if (!AutoSave || _changeScopeCounter > 0)
@@ -451,21 +458,17 @@ private void MarkChanged()
SaveDataFromDisk();
}
- ///
- /// Throws an exception if the storage has been disposed.
- ///
- /// Thrown if the storage is disposed.
+ /// Throws an exception if the storage has been disposed.
+ /// Thrown if the storage is disposed.
private void ThrowIfDisposed()
{
if (IsDisposed)
{
- throw new StorageDisposedException(_storageFilePath);
+ throw new ObjectDisposedException($"{nameof(BinaryStorage)}: {_storageFilePath}");
}
}
- ///
- /// Throws an exception if the specified type is a collection.
- ///
+ /// Throws an exception if the specified type is a collection.
/// The type to check.
/// Thrown if the type is a collection.
private void ThrowIfCollection()
@@ -481,26 +484,20 @@ private void ThrowIfCollection()
#region Dispose Pattern
- ///
- /// Finalizer
- ///
+ /// Finalizer
~BinaryStorage()
{
Dispose(false);
}
- ///
- /// Disposes the resources used by the storage.
- ///
+ /// Disposes the resources used by the storage.
public virtual void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
- ///
- /// Disposes the resources used by the storage.
- ///
+ /// Disposes the resources used by the storage.
/// Whether managed resources should be disposed.
private void Dispose(bool disposing)
{
@@ -512,10 +509,15 @@ private void Dispose(bool disposing)
// Always dispose IReactiveCollection instances
foreach (var rc in _data.Values.Select(c => c.Object).OfType())
{
- rc.OnChanged -= MarkChanged;
+ rc.OnChanged -= ReactiveCollectionChanged;
rc.Dispose();
+ _collections.Remove(rc);
}
+ OnKeyAdded = null;
+ OnKeyChanged = null;
+ OnKeyRemoved = null;
+
if (disposing)
{
_data.Clear();
@@ -530,22 +532,26 @@ private void Dispose(bool disposing)
#region File System IO
- ///
- /// Loads the data from disk into memory.
- ///
+ /// Loads the data from disk into memory.
+ /// Thrown if the storage is disposed.
+ /// An I/O error occurred
private void LoadDataFromDisk()
{
ThrowIfDisposed();
BinaryStorageIO.LoadDataFromDisk(_storageFilePath, _supportedTypes, _data);
- foreach (var rc in _data.Values.Select(c => c.Object).OfType())
+ foreach (var pair in _data)
{
- rc.OnChanged += MarkChanged;
+ if (pair.Value.Object is IReactiveCollection rc)
+ {
+ _collections.Add(rc, pair.Key);
+ rc.OnChanged += ReactiveCollectionChanged;
+ }
}
}
- ///
- /// Saves the data from memory to disk.
- ///
+ /// Saves the data from memory to disk.
+ /// Thrown if the storage is disposed.
+ /// An I/O error occurred
private void SaveDataFromDisk()
{
ThrowIfDisposed();
diff --git a/Runtime/BinaryStorageIO.cs b/Runtime/BinaryStorageIO.cs
index fd16574..81b7629 100644
--- a/Runtime/BinaryStorageIO.cs
+++ b/Runtime/BinaryStorageIO.cs
@@ -8,6 +8,11 @@ namespace Appegy.Storage
{
internal static class BinaryStorageIO
{
+ /// Save data from memory to disk.
+ /// Path to the storage file
+ /// List of sections
+ /// Dictionary to store data
+ /// An I/O error occurred
internal static void SaveDataOnDisk(string storageFilePath, IReadOnlyList sections, IReadOnlyDictionary data)
{
// make sure there is no temp file from previous (most likely failed) save try
@@ -80,6 +85,11 @@ internal static void SaveDataOnDisk(string storageFilePath, IReadOnlyList Load data from disk to memory.
+ /// Path to the storage file
+ /// List of sections
+ /// Dictionary to store data
+ /// An I/O error occurred
internal static void LoadDataFromDisk(string storageFilePath, IReadOnlyList sections, IDictionary data)
{
data.Clear();
diff --git a/Runtime/Collections/IReactiveCollection.cs b/Runtime/Collections/IReactiveCollection.cs
index 9a4a0cb..95d7501 100644
--- a/Runtime/Collections/IReactiveCollection.cs
+++ b/Runtime/Collections/IReactiveCollection.cs
@@ -4,6 +4,6 @@ namespace Appegy.Storage
{
internal interface IReactiveCollection : IDisposable
{
- public event Action OnChanged;
+ public event Action OnChanged;
}
}
\ No newline at end of file
diff --git a/Runtime/Collections/ReactiveDictionary.cs b/Runtime/Collections/ReactiveDictionary.cs
index 26639e1..cd84ba8 100644
--- a/Runtime/Collections/ReactiveDictionary.cs
+++ b/Runtime/Collections/ReactiveDictionary.cs
@@ -4,24 +4,24 @@
namespace Appegy.Storage
{
- public class ReactiveDictionary : IReactiveCollection, IDictionary, IReadOnlyDictionary
+ internal class ReactiveDictionary : IReactiveCollection, IDictionary, IReadOnlyDictionary
{
private readonly Dictionary _dictionary = new();
public bool IsDisposed { get; private set; }
- public event Action OnChanged;
+ public event Action OnChanged;
private void SetDirty()
{
- OnChanged?.Invoke();
+ OnChanged?.Invoke(this);
}
private void ThrowIfDisposed()
{
if (IsDisposed)
{
- throw new CollectionDisposedException();
+ throw new ObjectDisposedException(nameof(ReactiveDictionary));
}
}
diff --git a/Runtime/Collections/ReactiveList.cs b/Runtime/Collections/ReactiveList.cs
index 55f977c..0db5d3c 100644
--- a/Runtime/Collections/ReactiveList.cs
+++ b/Runtime/Collections/ReactiveList.cs
@@ -4,24 +4,24 @@
namespace Appegy.Storage
{
- public class ReactiveList : IReactiveCollection, IList, IReadOnlyList
+ internal class ReactiveList : IReactiveCollection, IList, IReadOnlyList
{
private readonly List _list = new();
public bool IsDisposed { get; private set; }
- public event Action OnChanged;
+ public event Action OnChanged;
private void SetDirty()
{
- OnChanged?.Invoke();
+ OnChanged?.Invoke(this);
}
private void ThrowIfDisposed()
{
if (IsDisposed)
{
- throw new CollectionDisposedException();
+ throw new ObjectDisposedException(nameof(ReactiveList));
}
}
diff --git a/Runtime/Collections/ReactiveSet.cs b/Runtime/Collections/ReactiveSet.cs
index d5a2be6..d5ecda1 100644
--- a/Runtime/Collections/ReactiveSet.cs
+++ b/Runtime/Collections/ReactiveSet.cs
@@ -5,24 +5,24 @@
namespace Appegy.Storage
{
- public class ReactiveSet : IReactiveCollection, ISet, IReadOnlyCollection
+ internal class ReactiveSet : IReactiveCollection, ISet, IReadOnlyCollection
{
private readonly HashSet _set = new();
public bool IsDisposed { get; private set; }
- public event Action OnChanged;
+ public event Action OnChanged;
private void SetDirty()
{
- OnChanged?.Invoke();
+ OnChanged?.Invoke(this);
}
private void ThrowIfDisposed()
{
if (IsDisposed)
{
- throw new CollectionDisposedException();
+ throw new ObjectDisposedException(nameof(ReactiveSet));
}
}
diff --git a/Runtime/Exceptions/CollectionDisposedException.cs b/Runtime/Exceptions/CollectionDisposedException.cs
deleted file mode 100644
index 12086e3..0000000
--- a/Runtime/Exceptions/CollectionDisposedException.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System;
-
-namespace Appegy.Storage
-{
- public class CollectionDisposedException : Exception
- {
- public CollectionDisposedException()
- : base("Collection already disposed and can't be used anymore.")
- {
- }
- }
-}
\ No newline at end of file
diff --git a/Runtime/Exceptions/CollectionDisposedException.cs.meta b/Runtime/Exceptions/CollectionDisposedException.cs.meta
deleted file mode 100644
index dad2af2..0000000
--- a/Runtime/Exceptions/CollectionDisposedException.cs.meta
+++ /dev/null
@@ -1,3 +0,0 @@
-fileFormatVersion: 2
-guid: bcd1185209924629ab8d16c26a98be27
-timeCreated: 1718201930
\ No newline at end of file
diff --git a/Runtime/Exceptions/StorageDisposedException.cs b/Runtime/Exceptions/StorageDisposedException.cs
deleted file mode 100644
index 59f3b7e..0000000
--- a/Runtime/Exceptions/StorageDisposedException.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System;
-
-namespace Appegy.Storage
-{
- public class StorageDisposedException : Exception
- {
- public StorageDisposedException(string storageFilePath)
- : base($"Storage already disposed and can't be used anymore. File path: {storageFilePath}")
- {
- }
- }
-}
\ No newline at end of file
diff --git a/Runtime/Exceptions/StorageDisposedException.cs.meta b/Runtime/Exceptions/StorageDisposedException.cs.meta
deleted file mode 100644
index ff1ad0a..0000000
--- a/Runtime/Exceptions/StorageDisposedException.cs.meta
+++ /dev/null
@@ -1,3 +0,0 @@
-fileFormatVersion: 2
-guid: 0dc468d1621f4470b4e1840e203e57dc
-timeCreated: 1694037130
\ No newline at end of file
diff --git a/Runtime/Utilities/CollectionExtensions.cs b/Runtime/Utilities/CollectionExtensions.cs
index 2fad70d..1b16f1b 100644
--- a/Runtime/Utilities/CollectionExtensions.cs
+++ b/Runtime/Utilities/CollectionExtensions.cs
@@ -5,6 +5,63 @@ namespace Appegy.Storage
{
internal static class CollectionExtensions
{
+ #region AddRange
+
+ public static void AddRange(this ICollection source, T item1, T item2)
+ {
+ source.Add(item1);
+ source.Add(item2);
+ }
+
+ public static void AddRange(this ICollection source, T item1, T item2, T item3)
+ {
+ source.Add(item1);
+ source.Add(item2);
+ source.Add(item3);
+ }
+
+ public static void AddRange(this ICollection source, T item1, T item2, T item3, T item4)
+ {
+ source.Add(item1);
+ source.Add(item2);
+ source.Add(item3);
+ source.Add(item4);
+ }
+
+ public static void AddRange(this ICollection source, params T[] items)
+ {
+ items.ForEach(source.Add);
+ }
+
+ public static void AddRange(this IDictionary source, (TKey Key, TValue Value) item1, (TKey Key, TValue Value) item2)
+ {
+ source.Add(item1.Key, item1.Value);
+ source.Add(item2.Key, item2.Value);
+ }
+
+ public static void AddRange(this IDictionary source, (TKey Key, TValue Value) item1, (TKey Key, TValue Value) item2, (TKey Key, TValue Value) item3)
+ {
+ source.Add(item1.Key, item1.Value);
+ source.Add(item2.Key, item2.Value);
+ source.Add(item3.Key, item3.Value);
+ }
+
+ public static void AddRange(this IDictionary source, (TKey Key, TValue Value) item1, (TKey Key, TValue Value) item2, (TKey Key, TValue Value) item3,
+ (TKey Key, TValue Value) item4)
+ {
+ source.Add(item1.Key, item1.Value);
+ source.Add(item2.Key, item2.Value);
+ source.Add(item3.Key, item3.Value);
+ source.Add(item4.Key, item4.Value);
+ }
+
+ public static void AddRange(this IDictionary source, params (TKey Key, TValue Value)[] items)
+ {
+ items.ForEach(item => source.Add(item.Key, item.Value));
+ }
+
+ #endregion
+
public static bool IsCollection(this Type type)
{
if (!type.IsGenericType)
@@ -15,6 +72,14 @@ public static bool IsCollection(this Type type)
return typeof(ICollection<>).IsAssignableFrom(genericTypeDefinition);
}
+ public static void ForEach(this Span source, Action predicate)
+ {
+ foreach (var item in source)
+ {
+ predicate(item);
+ }
+ }
+
public static void ForEach(this IEnumerable source, Action predicate)
{
foreach (var item in source)
diff --git a/Tests/BinaryStorageTests.cs b/Tests/BinaryStorageTests.cs
index a2dcefa..c33f167 100644
--- a/Tests/BinaryStorageTests.cs
+++ b/Tests/BinaryStorageTests.cs
@@ -99,7 +99,7 @@ public void WhenStorageDisposed_AndHasCalled_ThenExceptionOccured()
// Assert
Action action = () => storage.Has("key");
- action.Should().Throw();
+ action.Should().Throw();
}
[Test]
@@ -115,7 +115,7 @@ public void WhenStorageDisposed_AndTypeOfCalled_ThenExceptionOccured()
// Assert
Action action = () => storage.TypeOf("key");
- action.Should().Throw();
+ action.Should().Throw();
}
[Test]
@@ -131,7 +131,7 @@ public void WhenStorageDisposed_AndSupportsCalled_ThenExceptionOccured()
// Assert
Action action = () => storage.Supports();
- action.Should().Throw();
+ action.Should().Throw();
}
#region Reactive Lists
@@ -389,6 +389,129 @@ public void WhenReactiveDictionaryChanged_AndStorageReloaded_ThenValuesInStorage
#endregion
+ #region Events
+
+ [Test]
+ public void WhenKeyAddedToStorage_ThenOnKeyAddedEventRaised()
+ {
+ // Arrange
+ using var storage = BinaryStorage.Construct(StoragePath)
+ .AddTypeSerializer(Int32Serializer.Shared)
+ .Build();
+
+ var raised = false;
+ storage.OnKeyAdded += s => { raised = s == "key"; };
+
+ // Act
+ storage.Set("key", 10);
+
+ // Assert
+ raised.Should().BeTrue("OnKeyAdded should be raised when Set is called.");
+ }
+
+ [Test]
+ public void WhenKeyRemovedFromStorage_ThenOnKeyRemovedEventRaised()
+ {
+ // Arrange
+ using var storage = BinaryStorage.Construct(StoragePath)
+ .AddTypeSerializer(Int32Serializer.Shared)
+ .Build();
+
+ storage.Set("key", 10);
+
+ var raised = false;
+ storage.OnKeyRemoved += s => { raised = s == "key"; };
+
+ // Act
+ storage.Remove("key");
+
+ // Assert
+ raised.Should().BeTrue("OnKeyRemoved should be raised when Remove is called.");
+ }
+
+ [Test]
+ public void WhenKeyChangedInStorage_ThenOnKeyChangedEventRaised()
+ {
+ // Arrange
+ using var storage = BinaryStorage.Construct(StoragePath)
+ .AddTypeSerializer(Int32Serializer.Shared)
+ .Build();
+
+ storage.Set("key", 10);
+
+ var raised = false;
+ storage.OnKeyChanged += s => { raised = s == "key"; };
+
+ // Act
+ storage.Set("key", 20);
+
+ // Assert
+ raised.Should().BeTrue("OnKeyChanged should be raised when Set is called.");
+ }
+
+ [Test]
+ public void WhenCollectionAddedToStorage_ThenOnKeyAddedEventRaised()
+ {
+ // Arrange
+ using var storage = BinaryStorage.Construct(StoragePath)
+ .AddTypeSerializer(Int32Serializer.Shared)
+ .SupportListsOf()
+ .Build();
+
+ var raised = false;
+ storage.OnKeyAdded += s => { raised = s == "key"; };
+
+ // Act
+ storage.GetListOf("key").Add(10);
+
+ // Assert
+ raised.Should().BeTrue("OnKeyAdded should be raised when Set is called.");
+ }
+
+ [Test]
+ public void WhenCollectionRemovedFromStorage_ThenOnKeyRemovedEventRaised()
+ {
+ // Arrange
+ using var storage = BinaryStorage.Construct(StoragePath)
+ .AddTypeSerializer(Int32Serializer.Shared)
+ .SupportListsOf()
+ .Build();
+
+ storage.GetListOf("key").Add(10);
+
+ var raised = false;
+ storage.OnKeyRemoved += s => { raised = s == "key"; };
+
+ // Act
+ storage.Remove("key");
+
+ // Assert
+ raised.Should().BeTrue("OnKeyRemoved should be raised when Remove is called.");
+ }
+
+ [Test]
+ public void WhenCollectionChangedInStorage_ThenOnKeyChangedEventRaised()
+ {
+ // Arrange
+ using var storage = BinaryStorage.Construct(StoragePath)
+ .AddTypeSerializer(Int32Serializer.Shared)
+ .SupportListsOf()
+ .Build();
+
+ storage.GetListOf("key").Add(10);
+
+ var raised = false;
+ storage.OnKeyChanged += s => { raised = s == "key"; };
+
+ // Act
+ storage.GetListOf("key").Add(20);
+
+ // Assert
+ raised.Should().BeTrue("OnKeyChanged should be raised when Set is called.");
+ }
+
+ #endregion
+
#region TypeMismatchBehaviour Tests
[Test]
diff --git a/Tests/CollectionTests/ReactiveDictionaryTests.cs b/Tests/CollectionTests/ReactiveDictionaryTests.cs
index 274f6c5..4dcc785 100644
--- a/Tests/CollectionTests/ReactiveDictionaryTests.cs
+++ b/Tests/CollectionTests/ReactiveDictionaryTests.cs
@@ -12,7 +12,7 @@ public class ReactiveDictionaryTests
public void WhenItemIsAdded_AndDictionaryIsNotDisposed_ThenItemShouldBeInDictionary()
{
// Arrange
- var dictionary = new ReactiveDictionary();
+ using var dictionary = new ReactiveDictionary();
// Act
dictionary.Add(1, "one");
@@ -25,7 +25,8 @@ public void WhenItemIsAdded_AndDictionaryIsNotDisposed_ThenItemShouldBeInDiction
public void WhenItemIsRemoved_AndItemExistsInDictionary_ThenItemShouldNotBeInDictionary()
{
// Arrange
- var dictionary = new ReactiveDictionary { { 1, "one" }, { 2, "two" } };
+ using var dictionary = new ReactiveDictionary();
+ dictionary.AddRange((1, "one"), (2, "two"));
// Act
dictionary.Remove(2);
@@ -38,7 +39,8 @@ public void WhenItemIsRemoved_AndItemExistsInDictionary_ThenItemShouldNotBeInDic
public void WhenClearIsCalled_AndDictionaryHasItems_ThenDictionaryShouldBeEmpty()
{
// Arrange
- var dictionary = new ReactiveDictionary { { 1, "one" }, { 2, "two" } };
+ using var dictionary = new ReactiveDictionary();
+ dictionary.AddRange((1, "one"), (2, "two"));
// Act
dictionary.Clear();
@@ -51,7 +53,8 @@ public void WhenClearIsCalled_AndDictionaryHasItems_ThenDictionaryShouldBeEmpty(
public void WhenGettingItemByKey_AndKeyIsValid_ThenShouldReturnCorrectItem()
{
// Arrange
- var dictionary = new ReactiveDictionary { { 1, "one" }, { 2, "two" } };
+ using var dictionary = new ReactiveDictionary();
+ dictionary.AddRange((1, "one"), (2, "two"));
// Act
var item = dictionary[1];
@@ -64,7 +67,8 @@ public void WhenGettingItemByKey_AndKeyIsValid_ThenShouldReturnCorrectItem()
public void WhenSettingItemByKey_AndKeyIsValid_ThenShouldUpdateItem()
{
// Arrange
- var dictionary = new ReactiveDictionary { { 1, "one" }, { 2, "two" } };
+ using var dictionary = new ReactiveDictionary();
+ dictionary.AddRange((1, "one"), (2, "two"));
// Act
dictionary[1] = "uno";
@@ -84,7 +88,7 @@ public void WhenItemIsAdded_AndDictionaryIsDisposed_ThenShouldThrowException()
Action action = () => dictionary.Add(1, "one");
// Assert
- action.Should().Throw();
+ action.Should().Throw();
}
[Test]
@@ -98,7 +102,7 @@ public void WhenItemIsRemoved_AndDictionaryIsDisposed_ThenShouldThrowException()
Action action = () => dictionary.Remove(1);
// Assert
- action.Should().Throw();
+ action.Should().Throw();
}
[Test]
@@ -112,7 +116,7 @@ public void WhenClearIsCalled_AndDictionaryIsDisposed_ThenShouldThrowException()
Action action = () => dictionary.Clear();
// Assert
- action.Should().Throw();
+ action.Should().Throw();
}
[Test]
@@ -126,7 +130,7 @@ public void WhenSetItemByKey_AndDictionaryIsDisposed_ThenShouldThrowException()
Action action = () => dictionary[1] = "uno";
// Assert
- action.Should().Throw();
+ action.Should().Throw();
}
[Test]
@@ -159,9 +163,9 @@ public void WhenDictionaryIsDisposed_ThenDictionaryShouldBeEmpty()
public void WhenOnChangedIsSubscribed_AndDictionaryIsModified_ThenOnChangedShouldBeTriggered()
{
// Arrange
- var dictionary = new ReactiveDictionary();
+ using var dictionary = new ReactiveDictionary();
var wasTriggered = false;
- dictionary.OnChanged += () => wasTriggered = true;
+ dictionary.OnChanged += (_) => wasTriggered = true;
// Act
dictionary.Add(1, "one");
@@ -174,9 +178,10 @@ public void WhenOnChangedIsSubscribed_AndDictionaryIsModified_ThenOnChangedShoul
public void WhenOnChangedIsSubscribed_AndDictionaryIsCleared_ThenOnChangedShouldBeTriggered()
{
// Arrange
- var dictionary = new ReactiveDictionary { { 1, "one" }, { 2, "two" } };
+ using var dictionary = new ReactiveDictionary();
+ dictionary.AddRange((1, "one"), (2, "two"));
var wasTriggered = false;
- dictionary.OnChanged += () => wasTriggered = true;
+ dictionary.OnChanged += (_) => wasTriggered = true;
// Act
dictionary.Clear();
@@ -191,7 +196,7 @@ public void WhenOnChangedIsSubscribed_AndDictionaryIsDisposed_ThenOnChangedShoul
// Arrange
var dictionary = new ReactiveDictionary { { 1, "one" }, { 2, "two" } };
var wasTriggered = false;
- dictionary.OnChanged += () => wasTriggered = true;
+ dictionary.OnChanged += (_) => wasTriggered = true;
// Act
dictionary.Dispose();
@@ -204,7 +209,8 @@ public void WhenOnChangedIsSubscribed_AndDictionaryIsDisposed_ThenOnChangedShoul
public void WhenKeyExists_AndTryGetValueIsCalled_ThenShouldReturnTrueAndCorrectValue()
{
// Arrange
- var dictionary = new ReactiveDictionary { { 1, "one" } };
+ using var dictionary = new ReactiveDictionary();
+ dictionary.Add(1, "one");
// Act
var result = dictionary.TryGetValue(1, out var value);
@@ -218,7 +224,7 @@ public void WhenKeyExists_AndTryGetValueIsCalled_ThenShouldReturnTrueAndCorrectV
public void WhenKeyDoesNotExist_AndTryGetValueIsCalled_ThenShouldReturnFalse()
{
// Arrange
- var dictionary = new ReactiveDictionary();
+ using var dictionary = new ReactiveDictionary();
// Act
var result = dictionary.TryGetValue(1, out var value);
@@ -232,7 +238,8 @@ public void WhenKeyDoesNotExist_AndTryGetValueIsCalled_ThenShouldReturnFalse()
public void WhenContainsKeyIsCalled_AndKeyExists_ThenShouldReturnTrue()
{
// Arrange
- var dictionary = new ReactiveDictionary { { 1, "one" } };
+ using var dictionary = new ReactiveDictionary();
+ dictionary.Add(1, "one");
// Act
var result = dictionary.ContainsKey(1);
@@ -245,7 +252,7 @@ public void WhenContainsKeyIsCalled_AndKeyExists_ThenShouldReturnTrue()
public void WhenContainsKeyIsCalled_AndKeyDoesNotExist_ThenShouldReturnFalse()
{
// Arrange
- var dictionary = new ReactiveDictionary();
+ using var dictionary = new ReactiveDictionary();
// Act
var result = dictionary.ContainsKey(1);
@@ -258,7 +265,8 @@ public void WhenContainsKeyIsCalled_AndKeyDoesNotExist_ThenShouldReturnFalse()
public void WhenCopyToIsCalled_ThenDictionaryShouldBeCopiedToArray()
{
// Arrange
- var dictionary = new ReactiveDictionary { { 1, "one" }, { 2, "two" } };
+ using var dictionary = new ReactiveDictionary();
+ dictionary.AddRange((1, "one"), (2, "two"));
var array = new KeyValuePair[2];
// Act
@@ -269,4 +277,4 @@ public void WhenCopyToIsCalled_ThenDictionaryShouldBeCopiedToArray()
array.Should().Contain(new KeyValuePair(2, "two"));
}
}
-}
+}
\ No newline at end of file
diff --git a/Tests/CollectionTests/ReactiveListTests.cs b/Tests/CollectionTests/ReactiveListTests.cs
index 3a002c5..bc7d970 100644
--- a/Tests/CollectionTests/ReactiveListTests.cs
+++ b/Tests/CollectionTests/ReactiveListTests.cs
@@ -11,7 +11,7 @@ public class ReactiveListTests
public void WhenItemIsAdded_AndListIsNotDisposed_ThenItemShouldBeInList()
{
// Arrange
- var list = new ReactiveList();
+ using var list = new ReactiveList();
// Act
list.Add(1);
@@ -24,7 +24,8 @@ public void WhenItemIsAdded_AndListIsNotDisposed_ThenItemShouldBeInList()
public void WhenItemIsRemoved_AndItemExistsInList_ThenItemShouldNotBeInList()
{
// Arrange
- var list = new ReactiveList { 1, 2, 3 };
+ using var list = new ReactiveList();
+ list.AddRange(1, 2, 3);
// Act
list.Remove(2);
@@ -37,7 +38,8 @@ public void WhenItemIsRemoved_AndItemExistsInList_ThenItemShouldNotBeInList()
public void WhenClearIsCalled_AndListHasItems_ThenListShouldBeEmpty()
{
// Arrange
- var list = new ReactiveList { 1, 2, 3 };
+ using var list = new ReactiveList();
+ list.AddRange(1, 2, 3);
// Act
list.Clear();
@@ -50,7 +52,8 @@ public void WhenClearIsCalled_AndListHasItems_ThenListShouldBeEmpty()
public void WhenGettingItemByIndex_AndIndexIsValid_ThenShouldReturnCorrectItem()
{
// Arrange
- var list = new ReactiveList { 1, 2, 3 };
+ using var list = new ReactiveList();
+ list.AddRange(1, 2, 3);
// Act
var item = list[1];
@@ -63,7 +66,8 @@ public void WhenGettingItemByIndex_AndIndexIsValid_ThenShouldReturnCorrectItem()
public void WhenSettingItemByIndex_AndIndexIsValid_ThenShouldUpdateItem()
{
// Arrange
- var list = new ReactiveList { 1, 2, 3 };
+ using var list = new ReactiveList();
+ list.AddRange(1, 2, 3);
// Act
list[1] = 5;
@@ -76,7 +80,8 @@ public void WhenSettingItemByIndex_AndIndexIsValid_ThenShouldUpdateItem()
public void WhenItemIsInserted_AndIndexIsValid_ThenShouldInsertItemAtCorrectPosition()
{
// Arrange
- var list = new ReactiveList { 1, 2, 3 };
+ using var list = new ReactiveList();
+ list.AddRange(1, 2, 3);
// Act
list.Insert(1, 5);
@@ -90,7 +95,8 @@ public void WhenItemIsInserted_AndIndexIsValid_ThenShouldInsertItemAtCorrectPosi
public void WhenItemIsRemovedByIndex_AndIndexIsValid_ThenShouldRemoveItemAtCorrectPosition()
{
// Arrange
- var list = new ReactiveList { 1, 2, 3 };
+ using var list = new ReactiveList();
+ list.AddRange(1, 2, 3);
// Act
list.RemoveAt(1);
@@ -136,7 +142,7 @@ public void WhenItemIsAdded_AndListIsDisposed_ThenShouldThrowException()
Action action = () => list.Add(1);
// Assert
- action.Should().Throw();
+ action.Should().Throw();
}
[Test]
@@ -147,10 +153,10 @@ public void WhenGettingItemByIndex_AndListIsDisposed_ThenShouldThrowException()
list.Dispose();
// Act
- Action action = () => { var item = list[1]; };
+ Action action = () => { _ = list[1]; };
// Assert
- action.Should().Throw();
+ action.Should().Throw();
}
[Test]
@@ -164,7 +170,7 @@ public void WhenSettingItemByIndex_AndListIsDisposed_ThenShouldThrowException()
Action action = () => list[1] = 5;
// Assert
- action.Should().Throw();
+ action.Should().Throw();
}
[Test]
@@ -173,7 +179,7 @@ public void WhenItemIsAdded_AndListIsNotDisposed_ThenOnChangedShouldBeTriggered(
// Arrange
var list = new ReactiveList();
var wasTriggered = false;
- list.OnChanged += () => wasTriggered = true;
+ list.OnChanged += (_) => wasTriggered = true;
// Act
list.Add(1);
@@ -186,9 +192,10 @@ public void WhenItemIsAdded_AndListIsNotDisposed_ThenOnChangedShouldBeTriggered(
public void WhenItemIsRemoved_AndListIsNotDisposed_ThenOnChangedShouldBeTriggered()
{
// Arrange
- var list = new ReactiveList { 1, 2, 3 };
+ using var list = new ReactiveList();
+ list.AddRange(1, 2, 3);
var wasTriggered = false;
- list.OnChanged += () => wasTriggered = true;
+ list.OnChanged += (_) => wasTriggered = true;
// Act
list.Remove(2);
@@ -201,9 +208,10 @@ public void WhenItemIsRemoved_AndListIsNotDisposed_ThenOnChangedShouldBeTriggere
public void WhenListIsCleared_AndListIsNotDisposed_ThenOnChangedShouldBeTriggered()
{
// Arrange
- var list = new ReactiveList { 1, 2, 3 };
+ using var list = new ReactiveList();
+ list.AddRange(1, 2, 3);
var wasTriggered = false;
- list.OnChanged += () => wasTriggered = true;
+ list.OnChanged += (_) => wasTriggered = true;
// Act
list.Clear();
@@ -212,4 +220,4 @@ public void WhenListIsCleared_AndListIsNotDisposed_ThenOnChangedShouldBeTriggere
wasTriggered.Should().BeTrue();
}
}
-}
+}
\ No newline at end of file
diff --git a/Tests/CollectionTests/ReactiveSetTests.cs b/Tests/CollectionTests/ReactiveSetTests.cs
index 9b95e6b..d2cf8d8 100644
--- a/Tests/CollectionTests/ReactiveSetTests.cs
+++ b/Tests/CollectionTests/ReactiveSetTests.cs
@@ -11,7 +11,7 @@ public class ReactiveSetTests
public void WhenItemIsAdded_AndSetIsNotDisposed_ThenItemShouldBeInSet()
{
// Arrange
- var set = new ReactiveSet();
+ using var set = new ReactiveSet();
// Act
set.Add(1);
@@ -24,7 +24,8 @@ public void WhenItemIsAdded_AndSetIsNotDisposed_ThenItemShouldBeInSet()
public void WhenItemIsRemoved_AndItemExistsInSet_ThenItemShouldNotBeInSet()
{
// Arrange
- var set = new ReactiveSet { 1, 2, 3 };
+ using var set = new ReactiveSet();
+ set.AddRange(1, 2, 3);
// Act
set.Remove(2);
@@ -37,7 +38,8 @@ public void WhenItemIsRemoved_AndItemExistsInSet_ThenItemShouldNotBeInSet()
public void WhenClearIsCalled_AndSetHasItems_ThenSetShouldBeEmpty()
{
// Arrange
- var set = new ReactiveSet { 1, 2, 3 };
+ using var set = new ReactiveSet();
+ set.AddRange(1, 2, 3);
// Act
set.Clear();
@@ -57,7 +59,7 @@ public void WhenItemIsAdded_AndSetIsDisposed_ThenShouldThrowException()
Action action = () => set.Add(1);
// Assert
- action.Should().Throw();
+ action.Should().Throw();
}
[Test]
@@ -71,7 +73,7 @@ public void WhenItemIsRemoved_AndSetIsDisposed_ThenShouldThrowException()
Action action = () => set.Remove(1);
// Assert
- action.Should().Throw();
+ action.Should().Throw();
}
[Test]
@@ -85,14 +87,15 @@ public void WhenClearIsCalled_AndSetIsDisposed_ThenShouldThrowException()
Action action = () => set.Clear();
// Assert
- action.Should().Throw();
+ action.Should().Throw();
}
[Test]
public void WhenExceptWithIsCalled_AndSetIsNotDisposed_ThenShouldRemoveItems()
{
// Arrange
- var set = new ReactiveSet { 1, 2, 3 };
+ using var set = new ReactiveSet();
+ set.AddRange(1, 2, 3);
// Act
set.ExceptWith(new[] { 2, 3, 4 });
@@ -106,7 +109,8 @@ public void WhenExceptWithIsCalled_AndSetIsNotDisposed_ThenShouldRemoveItems()
public void WhenIntersectWithIsCalled_AndSetIsNotDisposed_ThenShouldRetainOnlyCommonItems()
{
// Arrange
- var set = new ReactiveSet { 1, 2, 3 };
+ using var set = new ReactiveSet();
+ set.AddRange(1, 2, 3);
// Act
set.IntersectWith(new[] { 2, 3, 4 });
@@ -120,7 +124,8 @@ public void WhenIntersectWithIsCalled_AndSetIsNotDisposed_ThenShouldRetainOnlyCo
public void WhenSymmetricExceptWithIsCalled_AndSetIsNotDisposed_ThenShouldRetainUniqueItems()
{
// Arrange
- var set = new ReactiveSet { 1, 2, 3 };
+ using var set = new ReactiveSet();
+ set.AddRange(1, 2, 3);
// Act
set.SymmetricExceptWith(new[] { 2, 3, 4 });
@@ -134,7 +139,8 @@ public void WhenSymmetricExceptWithIsCalled_AndSetIsNotDisposed_ThenShouldRetain
public void WhenUnionWithIsCalled_AndSetIsNotDisposed_ThenShouldIncludeAllUniqueItems()
{
// Arrange
- var set = new ReactiveSet { 1, 2, 3 };
+ using var set = new ReactiveSet();
+ set.AddRange(1, 2, 3);
// Act
set.UnionWith(new[] { 2, 3, 4 });
@@ -173,9 +179,9 @@ public void WhenSetIsDisposed_ThenSetShouldBeEmpty()
public void WhenOnChangedIsSubscribed_AndSetIsModified_ThenOnChangedShouldBeTriggered()
{
// Arrange
- var set = new ReactiveSet();
+ using var set = new ReactiveSet();
var wasTriggered = false;
- set.OnChanged += () => wasTriggered = true;
+ set.OnChanged += (_) => wasTriggered = true;
// Act
set.Add(1);
@@ -188,9 +194,10 @@ public void WhenOnChangedIsSubscribed_AndSetIsModified_ThenOnChangedShouldBeTrig
public void WhenOnChangedIsSubscribed_AndSetIsCleared_ThenOnChangedShouldBeTriggered()
{
// Arrange
- var set = new ReactiveSet { 1, 2, 3 };
+ using var set = new ReactiveSet();
+ set.AddRange(1, 2, 3);
var wasTriggered = false;
- set.OnChanged += () => wasTriggered = true;
+ set.OnChanged += (_) => wasTriggered = true;
// Act
set.Clear();
@@ -205,7 +212,7 @@ public void WhenOnChangedIsSubscribed_AndSetIsDisposed_ThenOnChangedShouldBeTrig
// Arrange
var set = new ReactiveSet { 1, 2, 3 };
var wasTriggered = false;
- set.OnChanged += () => wasTriggered = true;
+ set.OnChanged += (_) => wasTriggered = true;
// Act
set.Dispose();
@@ -214,4 +221,4 @@ public void WhenOnChangedIsSubscribed_AndSetIsDisposed_ThenOnChangedShouldBeTrig
wasTriggered.Should().BeTrue();
}
}
-}
+}
\ No newline at end of file