Moved all unit test files into a separate directory in preparation for splitting the project
This commit is contained in:
parent
28b96fd557
commit
ba5234f701
58 changed files with 0 additions and 853 deletions
|
|
@ -1,225 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if !NO_SETS
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.Cloning {
|
||||
|
||||
/// <summary>Unit Test for the expression tree-based cloner</summary>
|
||||
[TestFixture]
|
||||
internal class ExpressionTreeClonerTest : CloneFactoryTest {
|
||||
|
||||
/// <summary>Initializes a new unit test suite for the reflection cloner</summary>
|
||||
public ExpressionTreeClonerTest() {
|
||||
this.cloneFactory = new ExpressionTreeCloner();
|
||||
}
|
||||
|
||||
/// <summary>Verifies that cloning a null object simply returns null</summary>
|
||||
[Test]
|
||||
public void CloningNullYieldsNull() {
|
||||
Assert.IsNull(this.cloneFactory.DeepFieldClone<object>(null));
|
||||
Assert.IsNull(this.cloneFactory.DeepPropertyClone<object>(null));
|
||||
Assert.IsNull(this.cloneFactory.ShallowFieldClone<object>(null));
|
||||
Assert.IsNull(this.cloneFactory.ShallowPropertyClone<object>(null));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that clones of objects whose class doesn't possess a default constructor
|
||||
/// can be made
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ClassWithoutDefaultConstructorCanBeCloned() {
|
||||
var original = new ClassWithoutDefaultConstructor(1234);
|
||||
ClassWithoutDefaultConstructor clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
Assert.AreNotSame(original, clone);
|
||||
Assert.AreEqual(original.Dummy, clone.Dummy);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that clones of primitive types can be created</summary>
|
||||
[Test]
|
||||
public void PrimitiveTypesCanBeCloned() {
|
||||
int original = 12345;
|
||||
int clone = this.cloneFactory.DeepFieldClone(original);
|
||||
Assert.AreEqual(original, clone);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that shallow clones of arrays can be made</summary>
|
||||
[Test]
|
||||
public void ReferenceTypesCanBeCloned() {
|
||||
var original = new TestReferenceType() { TestField = 123, TestProperty = 456 };
|
||||
TestReferenceType clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
Assert.AreNotSame(original, clone);
|
||||
Assert.AreEqual(original.TestField, clone.TestField);
|
||||
Assert.AreEqual(original.TestProperty, clone.TestProperty);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that shallow clones of arrays can be made</summary>
|
||||
[Test]
|
||||
public void PrimitiveArraysCanBeCloned() {
|
||||
var original = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
|
||||
int[] clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
Assert.AreNotSame(original, clone);
|
||||
CollectionAssert.AreEqual(original, clone);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that shallow clones of arrays can be made</summary>
|
||||
[Test]
|
||||
public void ShallowClonesOfArraysCanBeMade() {
|
||||
var original = new TestReferenceType[] {
|
||||
new TestReferenceType() { TestField = 123, TestProperty = 456 }
|
||||
};
|
||||
TestReferenceType[] clone = this.cloneFactory.ShallowFieldClone(original);
|
||||
|
||||
Assert.AreSame(original[0], clone[0]);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that deep clones of arrays can be made</summary>
|
||||
[Test]
|
||||
public void DeepClonesOfArraysCanBeMade() {
|
||||
var original = new TestReferenceType[,] {
|
||||
{
|
||||
new TestReferenceType() { TestField = 123, TestProperty = 456 }
|
||||
}
|
||||
};
|
||||
TestReferenceType[,] clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
Assert.AreNotSame(original[0, 0], clone[0, 0]);
|
||||
Assert.AreEqual(original[0, 0].TestField, clone[0, 0].TestField);
|
||||
Assert.AreEqual(original[0, 0].TestProperty, clone[0, 0].TestProperty);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that deep clones of a generic list can be made</summary>
|
||||
[Test]
|
||||
public void GenericListsCanBeCloned() {
|
||||
var original = new List<int>(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 });
|
||||
List<int> clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
CollectionAssert.AreEqual(original, clone);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that deep clones of a generic dictionary can be made</summary>
|
||||
[Test]
|
||||
public void GenericDictionariesCanBeCloned() {
|
||||
var original = new Dictionary<int, string>();
|
||||
original.Add(1, "one");
|
||||
Dictionary<int, string> clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
Assert.AreEqual("one", clone[1]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a field-based shallow clone of a value type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ShallowFieldBasedClonesOfValueTypesCanBeMade() {
|
||||
HierarchicalValueType original = CreateValueType();
|
||||
HierarchicalValueType clone = this.cloneFactory.ShallowFieldClone(original);
|
||||
VerifyClone(ref original, ref clone, isDeepClone: false, isPropertyBasedClone: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a field-based shallow clone of a reference type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ShallowFieldBasedClonesOfReferenceTypesCanBeMade() {
|
||||
HierarchicalReferenceType original = CreateReferenceType();
|
||||
HierarchicalReferenceType clone = this.cloneFactory.ShallowFieldClone(original);
|
||||
VerifyClone(original, clone, isDeepClone: false, isPropertyBasedClone: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a field-based deep clone of a value type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DeepFieldBasedClonesOfValueTypesCanBeMade() {
|
||||
HierarchicalValueType original = CreateValueType();
|
||||
HierarchicalValueType clone = this.cloneFactory.DeepFieldClone(original);
|
||||
VerifyClone(ref original, ref clone, isDeepClone: true, isPropertyBasedClone: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a field-based deep clone of a reference type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DeepFieldBasedClonesOfReferenceTypesCanBeMade() {
|
||||
HierarchicalReferenceType original = CreateReferenceType();
|
||||
HierarchicalReferenceType clone = this.cloneFactory.DeepFieldClone(original);
|
||||
VerifyClone(original, clone, isDeepClone: true, isPropertyBasedClone: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a property-based shallow clone of a value type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ShallowPropertyBasedClonesOfValueTypesCanBeMade() {
|
||||
HierarchicalValueType original = CreateValueType();
|
||||
HierarchicalValueType clone = this.cloneFactory.ShallowPropertyClone(original);
|
||||
VerifyClone(ref original, ref clone, isDeepClone: false, isPropertyBasedClone: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a property-based shallow clone of a reference type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ShallowPropertyBasedClonesOfReferenceTypesCanBeMade() {
|
||||
HierarchicalReferenceType original = CreateReferenceType();
|
||||
HierarchicalReferenceType clone = this.cloneFactory.ShallowPropertyClone(original);
|
||||
VerifyClone(original, clone, isDeepClone: false, isPropertyBasedClone: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a property-based deep clone of a value type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DeepPropertyBasedClonesOfValueTypesCanBeMade() {
|
||||
HierarchicalValueType original = CreateValueType();
|
||||
HierarchicalValueType clone = this.cloneFactory.DeepPropertyClone(original);
|
||||
VerifyClone(ref original, ref clone, isDeepClone: true, isPropertyBasedClone: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a property-based deep clone of a reference type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DeepPropertyBasedClonesOfReferenceTypesCanBeMade() {
|
||||
HierarchicalReferenceType original = CreateReferenceType();
|
||||
HierarchicalReferenceType clone = this.cloneFactory.DeepPropertyClone(original);
|
||||
VerifyClone(original, clone, isDeepClone: true, isPropertyBasedClone: true);
|
||||
}
|
||||
|
||||
/// <summary>Clone factory being tested</summary>
|
||||
private ICloneFactory cloneFactory;
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Cloning
|
||||
|
||||
#endif // UNITTEST
|
||||
|
||||
#endif // !NO_SETS
|
||||
|
|
@ -1,198 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.Cloning {
|
||||
|
||||
/// <summary>Unit Test for the reflection-based cloner</summary>
|
||||
[TestFixture]
|
||||
internal class ReflectionClonerTest : CloneFactoryTest {
|
||||
|
||||
/// <summary>Initializes a new unit test suite for the reflection cloner</summary>
|
||||
public ReflectionClonerTest() {
|
||||
this.cloneFactory = new ReflectionCloner();
|
||||
}
|
||||
|
||||
/// <summary>Verifies that cloning a null object simply returns null</summary>
|
||||
[Test]
|
||||
public void CloningNullYieldsNull() {
|
||||
Assert.IsNull(this.cloneFactory.DeepFieldClone<object>(null));
|
||||
Assert.IsNull(this.cloneFactory.DeepPropertyClone<object>(null));
|
||||
Assert.IsNull(this.cloneFactory.ShallowFieldClone<object>(null));
|
||||
Assert.IsNull(this.cloneFactory.ShallowPropertyClone<object>(null));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that clones of objects whose class doesn't possess a default constructor
|
||||
/// can be made
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ClassWithoutDefaultConstructorCanBeCloned() {
|
||||
var original = new ClassWithoutDefaultConstructor(1234);
|
||||
ClassWithoutDefaultConstructor clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
Assert.AreNotSame(original, clone);
|
||||
Assert.AreEqual(original.Dummy, clone.Dummy);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that clones of primitive types can be created</summary>
|
||||
[Test]
|
||||
public void PrimitiveTypesCanBeCloned() {
|
||||
int original = 12345;
|
||||
int clone = this.cloneFactory.ShallowFieldClone(original);
|
||||
Assert.AreEqual(original, clone);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that shallow clones of arrays can be made</summary>
|
||||
[Test]
|
||||
public void ShallowClonesOfArraysCanBeMade() {
|
||||
var original = new TestReferenceType[] {
|
||||
new TestReferenceType() { TestField = 123, TestProperty = 456 }
|
||||
};
|
||||
TestReferenceType[] clone = this.cloneFactory.ShallowFieldClone(original);
|
||||
|
||||
Assert.AreSame(original[0], clone[0]);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that deep clones of arrays can be made</summary>
|
||||
[Test]
|
||||
public void DeepClonesOfArraysCanBeMade() {
|
||||
var original = new TestReferenceType[] {
|
||||
new TestReferenceType() { TestField = 123, TestProperty = 456 }
|
||||
};
|
||||
TestReferenceType[] clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
Assert.AreNotSame(original[0], clone[0]);
|
||||
Assert.AreEqual(original[0].TestField, clone[0].TestField);
|
||||
Assert.AreEqual(original[0].TestProperty, clone[0].TestProperty);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that deep clones of a generic list can be made</summary>
|
||||
[Test]
|
||||
public void GenericListsCanBeCloned() {
|
||||
var original = new List<int>(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 });
|
||||
List<int> clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
CollectionAssert.AreEqual(original, clone);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that deep clones of a generic dictionary can be made</summary>
|
||||
[Test]
|
||||
public void GenericDictionariesCanBeCloned() {
|
||||
var original = new Dictionary<int, string>();
|
||||
original.Add(1, "one");
|
||||
Dictionary<int, string> clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
Assert.AreEqual("one", clone[1]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a field-based shallow clone of a value type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ShallowFieldBasedClonesOfValueTypesCanBeMade() {
|
||||
HierarchicalValueType original = CreateValueType();
|
||||
HierarchicalValueType clone = this.cloneFactory.ShallowFieldClone(original);
|
||||
VerifyClone(ref original, ref clone, isDeepClone: false, isPropertyBasedClone: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a field-based shallow clone of a reference type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ShallowFieldBasedClonesOfReferenceTypesCanBeMade() {
|
||||
HierarchicalReferenceType original = CreateReferenceType();
|
||||
HierarchicalReferenceType clone = this.cloneFactory.ShallowFieldClone(original);
|
||||
VerifyClone(original, clone, isDeepClone: false, isPropertyBasedClone: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a field-based deep clone of a value type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DeepFieldBasedClonesOfValueTypesCanBeMade() {
|
||||
HierarchicalValueType original = CreateValueType();
|
||||
HierarchicalValueType clone = this.cloneFactory.DeepFieldClone(original);
|
||||
VerifyClone(ref original, ref clone, isDeepClone: true, isPropertyBasedClone: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a field-based deep clone of a reference type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DeepFieldBasedClonesOfReferenceTypesCanBeMade() {
|
||||
HierarchicalReferenceType original = CreateReferenceType();
|
||||
HierarchicalReferenceType clone = this.cloneFactory.DeepFieldClone(original);
|
||||
VerifyClone(original, clone, isDeepClone: true, isPropertyBasedClone: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a property-based shallow clone of a value type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ShallowPropertyBasedClonesOfValueTypesCanBeMade() {
|
||||
HierarchicalValueType original = CreateValueType();
|
||||
HierarchicalValueType clone = this.cloneFactory.ShallowPropertyClone(original);
|
||||
VerifyClone(ref original, ref clone, isDeepClone: false, isPropertyBasedClone: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a property-based shallow clone of a reference type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ShallowPropertyBasedClonesOfReferenceTypesCanBeMade() {
|
||||
HierarchicalReferenceType original = CreateReferenceType();
|
||||
HierarchicalReferenceType clone = this.cloneFactory.ShallowPropertyClone(original);
|
||||
VerifyClone(original, clone, isDeepClone: false, isPropertyBasedClone: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a property-based deep clone of a value type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DeepPropertyBasedClonesOfValueTypesCanBeMade() {
|
||||
HierarchicalValueType original = CreateValueType();
|
||||
HierarchicalValueType clone = this.cloneFactory.DeepPropertyClone(original);
|
||||
VerifyClone(ref original, ref clone, isDeepClone: true, isPropertyBasedClone: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a property-based deep clone of a reference type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DeepPropertyBasedClonesOfReferenceTypesCanBeMade() {
|
||||
HierarchicalReferenceType original = CreateReferenceType();
|
||||
HierarchicalReferenceType clone = this.cloneFactory.DeepPropertyClone(original);
|
||||
VerifyClone(original, clone, isDeepClone: true, isPropertyBasedClone: true);
|
||||
}
|
||||
|
||||
/// <summary>Clone factory being tested</summary>
|
||||
private ICloneFactory cloneFactory;
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Cloning
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,145 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.Cloning {
|
||||
|
||||
/// <summary>Unit Test for the binary serializer-based cloner</summary>
|
||||
[TestFixture]
|
||||
internal class SerializationClonerTest : CloneFactoryTest {
|
||||
|
||||
/// <summary>Initializes a new unit test suite for the reflection cloner</summary>
|
||||
public SerializationClonerTest() {
|
||||
this.cloneFactory = new SerializationCloner();
|
||||
}
|
||||
|
||||
/// <summary>Verifies that cloning a null object simply returns null</summary>
|
||||
[Test]
|
||||
public void CloningNullYieldsNull() {
|
||||
Assert.IsNull(this.cloneFactory.DeepFieldClone<object>(null));
|
||||
Assert.IsNull(this.cloneFactory.DeepPropertyClone<object>(null));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that clones of objects whose class doesn't possess a default constructor
|
||||
/// can be made
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ClassWithoutDefaultConstructorCanBeCloned() {
|
||||
var original = new ClassWithoutDefaultConstructor(1234);
|
||||
ClassWithoutDefaultConstructor clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
Assert.AreNotSame(original, clone);
|
||||
Assert.AreEqual(original.Dummy, clone.Dummy);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that clones of primitive types can be created</summary>
|
||||
[Test]
|
||||
public void PrimitiveTypesCanBeCloned() {
|
||||
int original = 12345;
|
||||
int clone = this.cloneFactory.DeepFieldClone(original);
|
||||
Assert.AreEqual(original, clone);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that deep clones of arrays can be made</summary>
|
||||
[Test]
|
||||
public void DeepClonesOfArraysCanBeMade() {
|
||||
var original = new TestReferenceType[] {
|
||||
new TestReferenceType() { TestField = 123, TestProperty = 456 }
|
||||
};
|
||||
TestReferenceType[] clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
Assert.AreNotSame(original[0], clone[0]);
|
||||
Assert.AreEqual(original[0].TestField, clone[0].TestField);
|
||||
Assert.AreEqual(original[0].TestProperty, clone[0].TestProperty);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that deep clones of a generic list can be made</summary>
|
||||
[Test]
|
||||
public void GenericListsCanBeCloned() {
|
||||
var original = new List<int>(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 });
|
||||
List<int> clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
CollectionAssert.AreEqual(original, clone);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that deep clones of a generic dictionary can be made</summary>
|
||||
[Test]
|
||||
public void GenericDictionariesCanBeCloned() {
|
||||
var original = new Dictionary<int, string>();
|
||||
original.Add(1, "one");
|
||||
Dictionary<int, string> clone = this.cloneFactory.DeepFieldClone(original);
|
||||
|
||||
Assert.AreEqual("one", clone[1]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a field-based deep clone of a value type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DeepFieldBasedClonesOfValueTypesCanBeMade() {
|
||||
HierarchicalValueType original = CreateValueType();
|
||||
HierarchicalValueType clone = this.cloneFactory.DeepFieldClone(original);
|
||||
VerifyClone(ref original, ref clone, isDeepClone: true, isPropertyBasedClone: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a field-based deep clone of a reference type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DeepFieldBasedClonesOfReferenceTypesCanBeMade() {
|
||||
HierarchicalReferenceType original = CreateReferenceType();
|
||||
HierarchicalReferenceType clone = this.cloneFactory.DeepFieldClone(original);
|
||||
VerifyClone(original, clone, isDeepClone: true, isPropertyBasedClone: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a property-based deep clone of a value type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DeepPropertyBasedClonesOfValueTypesCanBeMade() {
|
||||
HierarchicalValueType original = CreateValueType();
|
||||
HierarchicalValueType clone = this.cloneFactory.DeepPropertyClone(original);
|
||||
VerifyClone(ref original, ref clone, isDeepClone: true, isPropertyBasedClone: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a property-based deep clone of a reference type can be performed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DeepPropertyBasedClonesOfReferenceTypesCanBeMade() {
|
||||
HierarchicalReferenceType original = CreateReferenceType();
|
||||
HierarchicalReferenceType clone = this.cloneFactory.DeepPropertyClone(original);
|
||||
VerifyClone(original, clone, isDeepClone: true, isPropertyBasedClone: true);
|
||||
}
|
||||
|
||||
/// <summary>Clone factory being tested</summary>
|
||||
private ICloneFactory cloneFactory;
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Cloning
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.Collections {
|
||||
|
||||
/// <summary>Unit Test for the collection constants</summary>
|
||||
[TestFixture]
|
||||
internal class ConstantsTest {
|
||||
|
||||
#if !NO_SPECIALIZED_COLLECTIONS
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the collection reset event arguments have 'reset' specified as
|
||||
/// their action
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CollectionResetEventArgsHaveResetActionSet() {
|
||||
Assert.AreEqual(
|
||||
NotifyCollectionChangedAction.Reset, Constants.NotifyCollectionResetEventArgs.Action
|
||||
);
|
||||
}
|
||||
|
||||
#endif // !NO_SPECIALIZED_COLLECTIONS
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Collections
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,710 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.Collections {
|
||||
|
||||
/// <summary>Unit Test for the double ended queue</summary>
|
||||
[TestFixture]
|
||||
internal class DequeTest {
|
||||
|
||||
/// <summary>Verifies that the AddLast() method of the deque is working</summary>
|
||||
[Test]
|
||||
public void TestAddLast() {
|
||||
Deque<int> intDeque = new Deque<int>(16);
|
||||
for(int item = 0; item < 48; ++item) {
|
||||
intDeque.AddLast(item);
|
||||
}
|
||||
|
||||
for(int item = 0; item < 48; ++item) {
|
||||
Assert.AreEqual(item, intDeque[item]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the AddFirst() method of the deque is working</summary>
|
||||
[Test]
|
||||
public void TestAddFirst() {
|
||||
Deque<int> intDeque = new Deque<int>(16);
|
||||
for(int item = 0; item < 48; ++item) {
|
||||
intDeque.AddFirst(item);
|
||||
}
|
||||
|
||||
for(int item = 0; item < 48; ++item) {
|
||||
Assert.AreEqual(47 - item, intDeque[item]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the RemoveFirst() method of the deque is working
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestRemoveFirst() {
|
||||
Deque<int> intDeque = new Deque<int>(16);
|
||||
for(int item = 0; item < 48; ++item) {
|
||||
intDeque.AddLast(item);
|
||||
}
|
||||
|
||||
for(int item = 0; item < 48; ++item) {
|
||||
Assert.AreEqual(item, intDeque.First);
|
||||
Assert.AreEqual(48 - item, intDeque.Count);
|
||||
intDeque.RemoveFirst();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the RemoveLast() method of the deque is working
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestRemoveLast() {
|
||||
Deque<int> intDeque = new Deque<int>(16);
|
||||
for(int item = 0; item < 48; ++item) {
|
||||
intDeque.AddLast(item);
|
||||
}
|
||||
|
||||
for(int item = 0; item < 48; ++item) {
|
||||
Assert.AreEqual(47 - item, intDeque.Last);
|
||||
Assert.AreEqual(48 - item, intDeque.Count);
|
||||
intDeque.RemoveLast();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the Insert() method works in all cases</summary>
|
||||
/// <remarks>
|
||||
/// We have several different cases here that will be tested. The deque can
|
||||
/// shift items to the left or right (depending on which end is closer to
|
||||
/// the insertion point) and the insertion point may fall in an only partially
|
||||
/// occupied block, requiring elaborate index calculations
|
||||
/// </remarks>
|
||||
[Test]
|
||||
public void TestInsert() {
|
||||
for(int testedIndex = 0; testedIndex <= 96; ++testedIndex) {
|
||||
Deque<int> intDeque = createDeque(96);
|
||||
|
||||
intDeque.Insert(testedIndex, 12345);
|
||||
|
||||
Assert.AreEqual(97, intDeque.Count);
|
||||
|
||||
for(int index = 0; index < testedIndex; ++index) {
|
||||
Assert.AreEqual(index, intDeque[index]);
|
||||
}
|
||||
Assert.AreEqual(12345, intDeque[testedIndex]);
|
||||
for(int index = testedIndex + 1; index < 97; ++index) {
|
||||
Assert.AreEqual(index - 1, intDeque[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the Insert() method works in all cases when the deque doesn't
|
||||
/// start at a block boundary
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestInsertNonNormalized() {
|
||||
for(int testedIndex = 0; testedIndex <= 96; ++testedIndex) {
|
||||
Deque<int> intDeque = createNonNormalizedDeque(96);
|
||||
|
||||
intDeque.Insert(testedIndex, 12345);
|
||||
|
||||
Assert.AreEqual(97, intDeque.Count);
|
||||
|
||||
for(int index = 0; index < testedIndex; ++index) {
|
||||
Assert.AreEqual(index, intDeque[index]);
|
||||
}
|
||||
Assert.AreEqual(12345, intDeque[testedIndex]);
|
||||
for(int index = testedIndex + 1; index < 97; ++index) {
|
||||
Assert.AreEqual(index - 1, intDeque[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Verifies the the RemoveAt() method works in all cases</summary>
|
||||
[Test]
|
||||
public void TestRemoveAt() {
|
||||
for(int testedIndex = 0; testedIndex < 96; ++testedIndex) {
|
||||
Deque<int> intDeque = new Deque<int>(16);
|
||||
for(int item = 0; item < 96; ++item) {
|
||||
intDeque.AddLast(item);
|
||||
}
|
||||
|
||||
intDeque.RemoveAt(testedIndex);
|
||||
|
||||
Assert.AreEqual(95, intDeque.Count);
|
||||
|
||||
for(int index = 0; index < testedIndex; ++index) {
|
||||
Assert.AreEqual(index, intDeque[index]);
|
||||
}
|
||||
for(int index = testedIndex; index < 95; ++index) {
|
||||
Assert.AreEqual(index + 1, intDeque[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies the the RemoveAt() method works in all cases when the deque doesn't
|
||||
/// start at a block boundary
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestRemoveAtNonNormalized() {
|
||||
for(int testedIndex = 0; testedIndex < 96; ++testedIndex) {
|
||||
Deque<int> intDeque = new Deque<int>(16);
|
||||
for(int item = 4; item < 96; ++item) {
|
||||
intDeque.AddLast(item);
|
||||
}
|
||||
intDeque.AddFirst(3);
|
||||
intDeque.AddFirst(2);
|
||||
intDeque.AddFirst(1);
|
||||
intDeque.AddFirst(0);
|
||||
|
||||
intDeque.RemoveAt(testedIndex);
|
||||
|
||||
Assert.AreEqual(95, intDeque.Count);
|
||||
|
||||
for(int index = 0; index < testedIndex; ++index) {
|
||||
Assert.AreEqual(index, intDeque[index]);
|
||||
}
|
||||
for(int index = testedIndex; index < 95; ++index) {
|
||||
Assert.AreEqual(index + 1, intDeque[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the RemoveAt() method keeps the state of the deque intact when
|
||||
/// it has to remove a block from the left end of the deque
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestRemoveAtEmptiesLeftBlock() {
|
||||
Deque<int> intDeque = new Deque<int>(16);
|
||||
for(int item = 1; item <= 16; ++item) {
|
||||
intDeque.AddLast(item);
|
||||
}
|
||||
intDeque.AddFirst(0);
|
||||
intDeque.RemoveAt(3);
|
||||
|
||||
Assert.AreEqual(16, intDeque.Count);
|
||||
|
||||
for(int index = 0; index < 3; ++index) {
|
||||
Assert.AreEqual(index, intDeque[index]);
|
||||
}
|
||||
for(int index = 3; index < 16; ++index) {
|
||||
Assert.AreEqual(index + 1, intDeque[index]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the RemoveAt() method keeps the state of the deque intact when
|
||||
/// it has to remove a block from the right end of the deque
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestRemoveAtEmptiesRightBlock() {
|
||||
Deque<int> intDeque = new Deque<int>(16);
|
||||
for(int item = 0; item <= 16; ++item) {
|
||||
intDeque.AddLast(item);
|
||||
}
|
||||
intDeque.RemoveAt(13);
|
||||
|
||||
Assert.AreEqual(16, intDeque.Count);
|
||||
|
||||
for(int index = 0; index < 13; ++index) {
|
||||
Assert.AreEqual(index, intDeque[index]);
|
||||
}
|
||||
for(int index = 13; index < 16; ++index) {
|
||||
Assert.AreEqual(index + 1, intDeque[index]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that an exception is thrown if the 'First' property is accessed
|
||||
/// in an empty deque
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnAccessFirstInEmptyDeque() {
|
||||
Deque<int> intDeque = new Deque<int>();
|
||||
Assert.Throws<InvalidOperationException>(
|
||||
delegate() { Console.WriteLine(intDeque.First); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that an exception is thrown if the 'Last' property is accessed
|
||||
/// in an empty deque
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnAccessLastInEmptyDeque() {
|
||||
Deque<int> intDeque = new Deque<int>();
|
||||
Assert.Throws<InvalidOperationException>(
|
||||
delegate() { Console.WriteLine(intDeque.Last); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that an exception is thrown if the first item is attempted to be
|
||||
/// removed from an empty deque
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnRemoveFirstFromEmptyDeque() {
|
||||
Deque<int> intDeque = new Deque<int>();
|
||||
Assert.Throws<InvalidOperationException>(
|
||||
delegate() { intDeque.RemoveFirst(); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that an exception is thrown if the last item is attempted to be
|
||||
/// removed from an empty deque
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnRemoveLastFromEmptyDeque() {
|
||||
Deque<int> intDeque = new Deque<int>();
|
||||
Assert.Throws<InvalidOperationException>(
|
||||
delegate() { intDeque.RemoveLast(); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that items can be assigned by their index
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestIndexAssignment() {
|
||||
Deque<int> intDeque = createDeque(32);
|
||||
intDeque[16] = 12345;
|
||||
intDeque[17] = 54321;
|
||||
|
||||
for(int index = 0; index < 16; ++index) {
|
||||
intDeque.RemoveFirst();
|
||||
}
|
||||
|
||||
Assert.AreEqual(12345, intDeque.First);
|
||||
intDeque.RemoveFirst();
|
||||
Assert.AreEqual(54321, intDeque.First);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that an exception is thrown if an invalid index is accessed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnInvalidIndex() {
|
||||
Deque<int> intDeque = new Deque<int>(16);
|
||||
for(int item = 0; item < 32; ++item) {
|
||||
intDeque.AddLast(item);
|
||||
}
|
||||
|
||||
Assert.Throws<ArgumentOutOfRangeException>(
|
||||
delegate() { Console.WriteLine(intDeque[32]); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Tests the IndexOf() method</summary>
|
||||
[Test, TestCase(0), TestCase(16), TestCase(32), TestCase(48)]
|
||||
public void TestIndexOf(int count) {
|
||||
Deque<int> intDeque = new Deque<int>(16);
|
||||
for(int item = 0; item < count; ++item) {
|
||||
intDeque.AddLast(item);
|
||||
}
|
||||
|
||||
for(int item = 0; item < count; ++item) {
|
||||
Assert.AreEqual(item, intDeque.IndexOf(item));
|
||||
}
|
||||
Assert.AreEqual(-1, intDeque.IndexOf(count));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests the IndexOf() method with the deque not starting at a block boundary
|
||||
/// </summary>
|
||||
[Test, TestCase(0), TestCase(16), TestCase(32), TestCase(48)]
|
||||
public void TestIndexOfNonNormalized(int count) {
|
||||
Deque<int> intDeque = createNonNormalizedDeque(count);
|
||||
|
||||
for(int item = 0; item < count; ++item) {
|
||||
Assert.AreEqual(item, intDeque.IndexOf(item));
|
||||
}
|
||||
Assert.AreEqual(-1, intDeque.IndexOf(count));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the deque's enumerator works</summary>
|
||||
[Test]
|
||||
public void TestEnumerator() {
|
||||
Deque<int> intDeque = createNonNormalizedDeque(40);
|
||||
|
||||
for(int testRun = 0; testRun < 2; ++testRun) {
|
||||
using(IEnumerator<int> enumerator = intDeque.GetEnumerator()) {
|
||||
for(int index = 0; index < intDeque.Count; ++index) {
|
||||
Assert.IsTrue(enumerator.MoveNext());
|
||||
Assert.AreEqual(index, enumerator.Current);
|
||||
}
|
||||
|
||||
Assert.IsFalse(enumerator.MoveNext());
|
||||
|
||||
enumerator.Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the deque's enumerator works</summary>
|
||||
[Test]
|
||||
public void TestObjectEnumerator() {
|
||||
Deque<int> intDeque = createNonNormalizedDeque(40);
|
||||
|
||||
for(int testRun = 0; testRun < 2; ++testRun) {
|
||||
IEnumerator enumerator = ((IEnumerable)intDeque).GetEnumerator();
|
||||
for(int index = 0; index < intDeque.Count; ++index) {
|
||||
Assert.IsTrue(enumerator.MoveNext());
|
||||
Assert.AreEqual(index, enumerator.Current);
|
||||
}
|
||||
|
||||
Assert.IsFalse(enumerator.MoveNext());
|
||||
|
||||
enumerator.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that an exception is thrown if the enumerator is accessed in
|
||||
/// an invalid position
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnInvalidEnumeratorPosition() {
|
||||
Deque<int> intDeque = createNonNormalizedDeque(40);
|
||||
|
||||
using(IEnumerator<int> enumerator = intDeque.GetEnumerator()) {
|
||||
Assert.Throws<InvalidOperationException>(
|
||||
delegate() { Console.WriteLine(enumerator.Current); }
|
||||
);
|
||||
|
||||
while(enumerator.MoveNext()) { }
|
||||
|
||||
Assert.Throws<InvalidOperationException>(
|
||||
delegate() { Console.WriteLine(enumerator.Current); }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Tests whether a small deque can be cleared</summary>
|
||||
[Test]
|
||||
public void TestClearSmallDeque() {
|
||||
Deque<int> intDeque = createDeque(12);
|
||||
intDeque.Clear();
|
||||
Assert.AreEqual(0, intDeque.Count);
|
||||
}
|
||||
|
||||
/// <summary>Tests whether a large deque can be cleared</summary>
|
||||
[Test]
|
||||
public void TestClearLargeDeque() {
|
||||
Deque<int> intDeque = createDeque(40);
|
||||
intDeque.Clear();
|
||||
Assert.AreEqual(0, intDeque.Count);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the non-typesafe Add() method is working</summary>
|
||||
[Test]
|
||||
public void TestAddObject() {
|
||||
Deque<int> intDeque = new Deque<int>();
|
||||
Assert.AreEqual(0, ((IList)intDeque).Add(123));
|
||||
Assert.AreEqual(1, intDeque.Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether an exception is thrown if the non-typesafe Add() method is
|
||||
/// used to add an incompatible object into the deque
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnAddIncompatibleObject() {
|
||||
Deque<int> intDeque = new Deque<int>();
|
||||
Assert.Throws<ArgumentException>(
|
||||
delegate() { ((IList)intDeque).Add("Hello World"); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the Add() method is working</summary>
|
||||
[Test]
|
||||
public void TestAdd() {
|
||||
Deque<int> intDeque = new Deque<int>();
|
||||
((IList<int>)intDeque).Add(123);
|
||||
Assert.AreEqual(1, intDeque.Count);
|
||||
}
|
||||
|
||||
/// <summary>Tests whether the Contains() method is working</summary>
|
||||
[Test]
|
||||
public void TestContains() {
|
||||
Deque<int> intDeque = createDeque(16);
|
||||
Assert.IsTrue(intDeque.Contains(14));
|
||||
Assert.IsFalse(intDeque.Contains(16));
|
||||
}
|
||||
|
||||
/// <summary>Tests the non-typesafe Contains() method</summary>
|
||||
[Test]
|
||||
public void TestContainsObject() {
|
||||
Deque<int> intDeque = createDeque(16);
|
||||
Assert.IsTrue(((IList)intDeque).Contains(14));
|
||||
Assert.IsFalse(((IList)intDeque).Contains(16));
|
||||
Assert.IsFalse(((IList)intDeque).Contains("Hello World"));
|
||||
}
|
||||
|
||||
/// <summary>Tests the non-typesafe Contains() method</summary>
|
||||
[Test]
|
||||
public void TestIndexOfObject() {
|
||||
Deque<int> intDeque = createDeque(16);
|
||||
Assert.AreEqual(14, ((IList)intDeque).IndexOf(14));
|
||||
Assert.AreEqual(-1, ((IList)intDeque).IndexOf(16));
|
||||
Assert.AreEqual(-1, ((IList)intDeque).IndexOf("Hello World"));
|
||||
}
|
||||
|
||||
/// <summary>Tests wether the non-typesafe Insert() method is working</summary>
|
||||
[Test]
|
||||
public void TestInsertObject() {
|
||||
for(int testedIndex = 0; testedIndex <= 96; ++testedIndex) {
|
||||
Deque<int> intDeque = createDeque(96);
|
||||
|
||||
((IList)intDeque).Insert(testedIndex, 12345);
|
||||
|
||||
Assert.AreEqual(97, intDeque.Count);
|
||||
|
||||
for(int index = 0; index < testedIndex; ++index) {
|
||||
Assert.AreEqual(index, intDeque[index]);
|
||||
}
|
||||
Assert.AreEqual(12345, intDeque[testedIndex]);
|
||||
for(int index = testedIndex + 1; index < 97; ++index) {
|
||||
Assert.AreEqual(index - 1, intDeque[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that an exception is thrown if an incompatible object is inserted
|
||||
/// into the deque
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnInsertIncompatibleObject() {
|
||||
Deque<int> intDeque = createDeque(12);
|
||||
Assert.Throws<ArgumentException>(
|
||||
delegate() { ((IList)intDeque).Insert(8, "Hello World"); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Validates that the IsFixedObject property is set to false</summary>
|
||||
[Test]
|
||||
public void TestIsFixedObject() {
|
||||
Deque<int> intDeque = new Deque<int>();
|
||||
Assert.IsFalse(((IList)intDeque).IsFixedSize);
|
||||
}
|
||||
|
||||
/// <summary>Validates that the IsSynchronized property is set to false</summary>
|
||||
[Test]
|
||||
public void TestIsSynchronized() {
|
||||
Deque<int> intDeque = new Deque<int>();
|
||||
Assert.IsFalse(((IList)intDeque).IsSynchronized);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that items can be assigned by their index using the non-typesafe
|
||||
/// IList interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestObjectIndexAssignment() {
|
||||
Deque<int> intDeque = createDeque(32);
|
||||
|
||||
((IList)intDeque)[16] = 12345;
|
||||
((IList)intDeque)[17] = 54321;
|
||||
|
||||
Assert.AreEqual(12345, ((IList)intDeque)[16]);
|
||||
Assert.AreEqual(54321, ((IList)intDeque)[17]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether an exception is thrown if an incompatible object is assigned
|
||||
/// to the deque
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestIncompatibleObjectIndexAssignment() {
|
||||
Deque<int> intDeque = createDeque(2);
|
||||
Assert.Throws<ArgumentException>(
|
||||
delegate() { ((IList)intDeque)[0] = "Hello World"; }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the Remove() method is working correctly</summary>
|
||||
[Test]
|
||||
public void TestRemove() {
|
||||
Deque<int> intDeque = createDeque(16);
|
||||
Assert.AreEqual(16, intDeque.Count);
|
||||
Assert.IsTrue(intDeque.Remove(13));
|
||||
Assert.IsFalse(intDeque.Remove(13));
|
||||
Assert.AreEqual(15, intDeque.Count);
|
||||
}
|
||||
|
||||
/// <summary>Tests the non-typesafe remove method</summary>
|
||||
[Test]
|
||||
public void TestRemoveObject() {
|
||||
Deque<int> intDeque = createDeque(10);
|
||||
Assert.IsTrue(intDeque.Contains(8));
|
||||
Assert.AreEqual(10, intDeque.Count);
|
||||
((IList)intDeque).Remove(8);
|
||||
Assert.IsFalse(intDeque.Contains(8));
|
||||
Assert.AreEqual(9, intDeque.Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests the non-typesafe remove method used to remove an incompatible object
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestRemoveIncompatibleObject() {
|
||||
Deque<int> intDeque = createDeque(10);
|
||||
((IList)intDeque).Remove("Hello World"); // should simply do nothing
|
||||
Assert.AreEqual(10, intDeque.Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the IsSynchronized property and the SyncRoot property are working
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestSynchronization() {
|
||||
Deque<int> intDeque = new Deque<int>();
|
||||
|
||||
if(!(intDeque as ICollection).IsSynchronized) {
|
||||
lock((intDeque as ICollection).SyncRoot) {
|
||||
Assert.AreEqual(0, intDeque.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that the IsReadOnly property of the deque returns false
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestIsReadOnly() {
|
||||
Deque<int> intDeque = new Deque<int>();
|
||||
Assert.IsFalse(((IList)intDeque).IsReadOnly);
|
||||
Assert.IsFalse(((ICollection<int>)intDeque).IsReadOnly);
|
||||
}
|
||||
|
||||
/// <summary>Tests the non-typesafe CopyTo() method</summary>
|
||||
[Test]
|
||||
public void TestCopyToObjectArray() {
|
||||
Deque<int> intDeque = createNonNormalizedDeque(40);
|
||||
|
||||
int[] array = new int[40];
|
||||
((ICollection)intDeque).CopyTo(array, 0);
|
||||
|
||||
Assert.AreEqual(intDeque, array);
|
||||
}
|
||||
|
||||
/// <summary>Tests the CopyTo() method</summary>
|
||||
[Test]
|
||||
public void TestCopyToArray() {
|
||||
Deque<int> intDeque = createDeque(12);
|
||||
intDeque.RemoveFirst();
|
||||
intDeque.RemoveFirst();
|
||||
|
||||
int[] array = new int[14];
|
||||
intDeque.CopyTo(array, 4);
|
||||
|
||||
Assert.AreEqual(
|
||||
new int[] { 0, 0, 0, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 },
|
||||
array
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the non-typesafe CopyTo() method throws an exception if
|
||||
/// the array is of an incompatible type
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnCopyToIncompatibleObjectArray() {
|
||||
Deque<int> intDeque = createDeque(4);
|
||||
|
||||
short[] array = new short[4];
|
||||
Assert.Throws<ArgumentException>(
|
||||
delegate() { ((ICollection)intDeque).CopyTo(array, 4); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the CopyTo() method throws an exception if the target array
|
||||
/// is too small
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnCopyToTooSmallArray() {
|
||||
Deque<int> intDeque = createDeque(8);
|
||||
Assert.Throws<ArgumentException>(
|
||||
delegate() { intDeque.CopyTo(new int[7], 0); }
|
||||
);
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
/// <summary>
|
||||
/// Tests whether the deque enumerator detects when it runs out of sync
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestInvalidatedEnumeratorDetection() {
|
||||
Deque<int> intDeque = createDeque(8);
|
||||
using(IEnumerator<int> enumerator = intDeque.GetEnumerator()) {
|
||||
Assert.IsTrue(enumerator.MoveNext());
|
||||
intDeque.AddFirst(12345);
|
||||
Assert.Throws<InvalidOperationException>(
|
||||
delegate() { enumerator.MoveNext(); }
|
||||
);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Creates a deque whose first element does not coincide with a block boundary
|
||||
/// </summary>
|
||||
/// <param name="count">Number of items the deque will be filled with</param>
|
||||
/// <returns>The newly created deque</returns>
|
||||
private static Deque<int> createNonNormalizedDeque(int count) {
|
||||
Deque<int> intDeque = new Deque<int>(16);
|
||||
|
||||
for(int item = 4; item < count; ++item) {
|
||||
intDeque.AddLast(item);
|
||||
}
|
||||
if(count > 3) { intDeque.AddFirst(3); }
|
||||
if(count > 2) { intDeque.AddFirst(2); }
|
||||
if(count > 1) { intDeque.AddFirst(1); }
|
||||
if(count > 0) { intDeque.AddFirst(0); }
|
||||
|
||||
return intDeque;
|
||||
}
|
||||
|
||||
/// <summary>Creates a deque filled with the specified number of items
|
||||
/// </summary>
|
||||
/// <param name="count">Number of items the deque will be filled with</param>
|
||||
/// <returns>The newly created deque</returns>
|
||||
private static Deque<int> createDeque(int count) {
|
||||
Deque<int> intDeque = new Deque<int>(16);
|
||||
|
||||
for(int item = 0; item < count; ++item) {
|
||||
intDeque.AddLast(item);
|
||||
}
|
||||
|
||||
return intDeque;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Collections
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,136 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.Collections {
|
||||
|
||||
/// <summary>Unit Test for the IList extension methods</summary>
|
||||
[TestFixture]
|
||||
internal class IListExtensionsTest {
|
||||
|
||||
/// <summary>Tests whether the insertion sort algorithm sorts a list correctly</summary>
|
||||
[Test]
|
||||
public void InsertionSortCanSortWholeList() {
|
||||
var testList = new List<int>(capacity: 5) { 1, 5, 2, 4, 3 };
|
||||
var testListAsIList = (IList<int>)testList;
|
||||
|
||||
testListAsIList.InsertionSort();
|
||||
|
||||
CollectionAssert.AreEqual(
|
||||
new List<int>(capacity: 5) { 1, 2, 3, 4, 5 },
|
||||
testList
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Tests whether the insertion sort algorithm works on big lists</summary>
|
||||
[Test]
|
||||
public void InsertionSortCanSortBigList() {
|
||||
const int ListSize = 16384;
|
||||
|
||||
var testList = new List<int>(capacity: ListSize);
|
||||
{
|
||||
var random = new Random();
|
||||
for(int index = 0; index < ListSize; ++index) {
|
||||
testList.Add(random.Next());
|
||||
}
|
||||
}
|
||||
|
||||
var testListAsIList = (IList<int>)testList;
|
||||
testListAsIList.InsertionSort();
|
||||
|
||||
for(int index = 1; index < ListSize; ++index) {
|
||||
Assert.LessOrEqual(testListAsIList[index - 1], testListAsIList[index]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Tests whether the insertion sort algorithm respects custom boundaries</summary>
|
||||
[Test]
|
||||
public void InsertionSortCanSortListSegment() {
|
||||
var testList = new List<int>(capacity: 7) { 9, 1, 5, 2, 4, 3, 0 };
|
||||
var testListAsIList = (IList<int>)testList;
|
||||
|
||||
testListAsIList.InsertionSort(1, 5, Comparer<int>.Default);
|
||||
|
||||
CollectionAssert.AreEqual(
|
||||
new List<int>(capacity: 7) { 9, 1, 2, 3, 4, 5, 0 },
|
||||
testList
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Tests whether the quicksort algorithm sorts a list correctly</summary>
|
||||
[Test]
|
||||
public void QuickSortCanSortWholeList() {
|
||||
var testList = new List<int>(capacity: 5) { 1, 5, 2, 4, 3 };
|
||||
var testListAsIList = (IList<int>)testList;
|
||||
|
||||
testListAsIList.QuickSort();
|
||||
|
||||
CollectionAssert.AreEqual(
|
||||
new List<int>(capacity: 5) { 1, 2, 3, 4, 5 },
|
||||
testList
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Tests whether the quicksort algorithm works on big lists</summary>
|
||||
[Test]
|
||||
public void QuickSortCanSortBigList() {
|
||||
const int ListSize = 16384;
|
||||
|
||||
var testList = new List<int>(capacity: ListSize);
|
||||
{
|
||||
var random = new Random();
|
||||
for(int index = 0; index < ListSize; ++index) {
|
||||
testList.Add(random.Next());
|
||||
}
|
||||
}
|
||||
|
||||
var testListAsIList = (IList<int>)testList;
|
||||
testListAsIList.QuickSort();
|
||||
|
||||
for(int index = 1; index < ListSize; ++index) {
|
||||
Assert.LessOrEqual(testListAsIList[index - 1], testListAsIList[index]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Tests whether the quicksort algorithm respects custom boundaries</summary>
|
||||
[Test]
|
||||
public void QuickSortCanSortListSegment() {
|
||||
var testList = new List<int>(capacity: 7) { 9, 1, 5, 2, 4, 3, 0 };
|
||||
var testListAsIList = (IList<int>)testList;
|
||||
|
||||
testListAsIList.QuickSort(1, 5, Comparer<int>.Default);
|
||||
|
||||
CollectionAssert.AreEqual(
|
||||
new List<int>(capacity: 7) { 9, 1, 2, 3, 4, 5, 0 },
|
||||
testList
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Collections
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.Collections {
|
||||
|
||||
/// <summary>Unit Test for the item event argument container</summary>
|
||||
[TestFixture]
|
||||
internal class ItemEventArgsTest {
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether an integer argument can be stored in the argument container
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void IntegersCanBeCarried() {
|
||||
var test = new ItemEventArgs<int>(12345);
|
||||
Assert.AreEqual(12345, test.Item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether a string argument can be stored in the argument container
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void StringsCanBeCarried() {
|
||||
var test = new ItemEventArgs<string>("hello world");
|
||||
Assert.AreEqual("hello world", test.Item);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Collections
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.Collections {
|
||||
|
||||
/// <summary>Unit Test for the item event argument container</summary>
|
||||
[TestFixture]
|
||||
internal class ItemReplaceEventArgsTest {
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether an integer argument can be stored in the argument container
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void IntegersCanBeCarried() {
|
||||
var test = new ItemReplaceEventArgs<int>(12345, 54321);
|
||||
Assert.AreEqual(12345, test.OldItem);
|
||||
Assert.AreEqual(54321, test.NewItem);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether a string argument can be stored in the argument container
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void StringsCanBeCarried() {
|
||||
var test = new ItemReplaceEventArgs<string>("hello", "world");
|
||||
Assert.AreEqual("hello", test.OldItem);
|
||||
Assert.AreEqual("world", test.NewItem);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Collections
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,256 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.Collections {
|
||||
|
||||
/// <summary>Unit Test for the list segment class</summary>
|
||||
[TestFixture]
|
||||
internal class ListSegmentTest {
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the default constructor of the ListSegment class throws the
|
||||
/// right exception when being passed 'null' instead of a list
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SimpleConstructorThrowsWhenListIsNull() {
|
||||
Assert.Throws<ArgumentNullException>(
|
||||
delegate() { new ListSegment<int>(null); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the simple constructor of the ListSegment class accepts
|
||||
/// an empty list
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SimpleConstructorAcceptsEmptyList() {
|
||||
new ListSegment<int>(new List<int>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the full constructor of the ListSegment class throws the
|
||||
/// right exception when being passed 'null' instead of a string
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ConstructorThrowsWhenListIsNull() {
|
||||
Assert.Throws<ArgumentNullException>(
|
||||
delegate() { new ListSegment<int>(null, 0, 0); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the full constructor of the ListSegment class accepts
|
||||
/// an empty string
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ConstructorAcceptsEmptyList() {
|
||||
new ListSegment<int>(new List<int>(), 0, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the full constructor of the ListSegment class throws the
|
||||
/// right exception when being passed an invalid start offset
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ConstructorThrowsOnInvalidOffset() {
|
||||
Assert.Throws<ArgumentOutOfRangeException>(
|
||||
delegate() { new ListSegment<int>(new List<int>(), -1, 0); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the full constructor of the ListSegment class throws the
|
||||
/// right exception when being passed an invalid element count
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ConstructorThrowsOnInvalidCount() {
|
||||
Assert.Throws<ArgumentOutOfRangeException>(
|
||||
delegate() { new ListSegment<int>(new List<int>(), 0, -1); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the full constructor of the ListSegment class throws the
|
||||
/// right exception when being passed a string length that's too large
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ConstructorThrowsOnListOverrun() {
|
||||
var testList = new List<int>(capacity: 5) { 1, 2, 3, 4, 5 };
|
||||
Assert.Throws<ArgumentException>(
|
||||
delegate() { new ListSegment<int>(testList, 3, 3); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Tests whether the 'Text' property works as expected</summary>
|
||||
[Test]
|
||||
public void ListPropertyStoresOriginalList() {
|
||||
var testList = new List<int>(capacity: 5) { 1, 2, 3, 4, 5 };
|
||||
ListSegment<int> testSegment = new ListSegment<int>(testList, 1, 3);
|
||||
Assert.AreSame(testList, testSegment.List);
|
||||
}
|
||||
|
||||
/// <summary>Tests whether the 'Offset' property works as expected</summary>
|
||||
[Test]
|
||||
public void OffsetPropertyIsStored() {
|
||||
var testList = new List<int>(capacity: 5) { 1, 2, 3, 4, 5 };
|
||||
ListSegment<int> testSegment = new ListSegment<int>(testList, 1, 3);
|
||||
Assert.AreEqual(1, testSegment.Offset);
|
||||
}
|
||||
|
||||
/// <summary>Tests whether the 'Count' property works as expected</summary>
|
||||
[Test]
|
||||
public void CountPropertyIsStored() {
|
||||
var testList = new List<int>(capacity: 5) { 1, 2, 3, 4, 5 };
|
||||
ListSegment<int> testSegment = new ListSegment<int>(testList, 1, 3);
|
||||
Assert.AreEqual(3, testSegment.Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether two differing instances produce different hash codes
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DifferentInstancesHaveDifferentHashCodes_Usually() {
|
||||
var forwardCountSegment = new ListSegment<int>(
|
||||
new List<int>(capacity: 9) { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 2, 7
|
||||
);
|
||||
var reverseCountSegment = new ListSegment<int>(
|
||||
new List<int>(capacity: 9) { 9, 8, 7, 6, 5, 4, 3, 2, 1 }, 1, 8
|
||||
);
|
||||
|
||||
Assert.AreNotEqual(
|
||||
forwardCountSegment.GetHashCode(), reverseCountSegment.GetHashCode()
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether two equivalent instances produce an identical hash code
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void EquivalentInstancesHaveSameHashcode() {
|
||||
var testSegment = new ListSegment<int>(
|
||||
new List<int>(capacity: 9) { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 2, 7
|
||||
);
|
||||
var identicalSegment = new ListSegment<int>(
|
||||
new List<int>(capacity: 9) { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 2, 7
|
||||
);
|
||||
|
||||
Assert.AreEqual(
|
||||
testSegment.GetHashCode(), identicalSegment.GetHashCode()
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Tests the equals method performing a comparison against null</summary>
|
||||
[Test]
|
||||
public void EqualsAgainstNullIsAlwaysFalse() {
|
||||
var testList = new List<int>(capacity: 5) { 1, 2, 3, 4, 5 };
|
||||
ListSegment<int> testSegment = new ListSegment<int>(testList, 1, 3);
|
||||
|
||||
Assert.IsFalse(
|
||||
testSegment.Equals(null)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Tests the equality operator with differing instances</summary>
|
||||
[Test]
|
||||
public void DifferingInstancesAreNotEqual() {
|
||||
var forwardCountSegment = new ListSegment<int>(
|
||||
new List<int>(capacity: 9) { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 2, 7
|
||||
);
|
||||
var reverseCountSegment = new ListSegment<int>(
|
||||
new List<int>(capacity: 9) { 9, 8, 7, 6, 5, 4, 3, 2, 1 }, 1, 8
|
||||
);
|
||||
|
||||
Assert.IsFalse(forwardCountSegment == reverseCountSegment);
|
||||
}
|
||||
|
||||
/// <summary>Tests the equality operator with equivalent instances</summary>
|
||||
[Test]
|
||||
public void EquivalentInstancesAreEqual() {
|
||||
var testSegment = new ListSegment<int>(
|
||||
new List<int>(capacity: 9) { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 2, 7
|
||||
);
|
||||
var identicalSegment = new ListSegment<int>(
|
||||
new List<int>(capacity: 9) { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 2, 7
|
||||
);
|
||||
|
||||
Assert.IsTrue(testSegment == identicalSegment);
|
||||
}
|
||||
|
||||
/// <summary>Tests the inequality operator with differing instances</summary>
|
||||
[Test]
|
||||
public void DifferingInstancesAreUnequal() {
|
||||
var forwardCountSegment = new ListSegment<int>(
|
||||
new List<int>(capacity: 9) { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 2, 7
|
||||
);
|
||||
var reverseCountSegment = new ListSegment<int>(
|
||||
new List<int>(capacity: 9) { 9, 8, 7, 6, 5, 4, 3, 2, 1 }, 1, 8
|
||||
);
|
||||
|
||||
Assert.IsTrue(forwardCountSegment != reverseCountSegment);
|
||||
}
|
||||
|
||||
/// <summary>Tests the inequality operator with equivalent instances</summary>
|
||||
[Test]
|
||||
public void EquivalentInstancesAreNotUnequal() {
|
||||
var testSegment = new ListSegment<int>(
|
||||
new List<int>(capacity: 9) { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 2, 7
|
||||
);
|
||||
var identicalSegment = new ListSegment<int>(
|
||||
new List<int>(capacity: 9) { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, 2, 7
|
||||
);
|
||||
|
||||
Assert.IsFalse(testSegment != identicalSegment);
|
||||
}
|
||||
|
||||
/// <summary>Tests the ToString() method of the string segment</summary>
|
||||
[Test]
|
||||
public void TestToString() {
|
||||
var testList = new List<int>(capacity: 6) { 1, 2, 3, 4, 5, 6 };
|
||||
ListSegment<int> testSegment = new ListSegment<int>(testList, 2, 2);
|
||||
|
||||
string stringRepresentation = testSegment.ToString();
|
||||
StringAssert.Contains("3, 4", stringRepresentation);
|
||||
StringAssert.DoesNotContain("2", stringRepresentation);
|
||||
StringAssert.DoesNotContain("5", stringRepresentation);
|
||||
}
|
||||
|
||||
/// <summary>Tests whether the 'Text' property works as expected</summary>
|
||||
[Test]
|
||||
public void ToListReturnsSubset() {
|
||||
var testList = new List<int>(capacity: 5) { 1, 2, 3, 4, 5 };
|
||||
ListSegment<int> testSegment = new ListSegment<int>(testList, 1, 3);
|
||||
CollectionAssert.AreEqual(
|
||||
new List<int>(capacity: 3) { 2, 3, 4 },
|
||||
testSegment.ToList()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Collections
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,390 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using NUnit.Framework;
|
||||
using NMock;
|
||||
|
||||
namespace Nuclex.Support.Collections {
|
||||
|
||||
/// <summary>Unit tests for the multi dictionary</summary>
|
||||
[TestFixture]
|
||||
internal class MultiDictionaryTest {
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that new instances of the multi dictionary can be created
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CanConstructNewDictionary() {
|
||||
var dictionary = new MultiDictionary<int, string>();
|
||||
Assert.IsNotNull(dictionary); // nonsense, prevents compiler warning
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the count is initialized correctly when building
|
||||
/// a multi dictionary from a dictionary of value collections.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CountIsCalculatedIfInitializedFromDictionary() {
|
||||
var contents = new Dictionary<int, ICollection<string>>();
|
||||
contents.Add(1, new List<string>(new string[] { "one", "eins" }));
|
||||
contents.Add(2, new List<string>(new string[] { "two", "zwei" }));
|
||||
|
||||
var multiDictionary = new MultiDictionary<int, string>(contents);
|
||||
Assert.AreEqual(4, multiDictionary.Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a new multi dictionary based on a read-only dictionary is
|
||||
/// also read-only
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void IsReadOnlyWhenBasedOnReadOnlyContainer() {
|
||||
var readOnly = new ReadOnlyDictionary<int, ICollection<string>>(
|
||||
new Dictionary<int, ICollection<string>>()
|
||||
);
|
||||
var dictionary = new MultiDictionary<int, string>(readOnly);
|
||||
|
||||
Assert.IsTrue(dictionary.IsReadOnly);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the multi dictionary can contain the same key multiple times
|
||||
/// (or in other words, multiple values on the same key)
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CanContainKeyMultipleTimes() {
|
||||
var dictionary = new MultiDictionary<int, string>();
|
||||
dictionary.Add(123, "one two three");
|
||||
dictionary.Add(123, "eins zwei drei");
|
||||
|
||||
Assert.AreEqual(2, dictionary.Count);
|
||||
|
||||
CollectionAssert.AreEquivalent(
|
||||
new KeyValuePair<int, string>[] {
|
||||
new KeyValuePair<int, string>(123, "one two three"),
|
||||
new KeyValuePair<int, string>(123, "eins zwei drei")
|
||||
},
|
||||
dictionary
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that adding values through the indexer still updates the item count
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void AddingValuesFromIndexerUpdatesCount() {
|
||||
var dictionary = new MultiDictionary<int, string>();
|
||||
dictionary.Add(42, "the answer to everything");
|
||||
dictionary[42].Add("21x2");
|
||||
|
||||
Assert.AreEqual(2, dictionary.Count);
|
||||
|
||||
CollectionAssert.AreEquivalent(
|
||||
new KeyValuePair<int, string>[] {
|
||||
new KeyValuePair<int, string>(42, "the answer to everything"),
|
||||
new KeyValuePair<int, string>(42, "21x2")
|
||||
},
|
||||
dictionary
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the collection can count the number of values stored
|
||||
/// under a key
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ValuesWithSameKeyCanBeCounted() {
|
||||
var dictionary = new MultiDictionary<int, string>();
|
||||
dictionary.Add(10, "ten");
|
||||
dictionary.Add(20, "twenty");
|
||||
dictionary.Add(30, "thirty");
|
||||
dictionary.Add(10, "zehn");
|
||||
dictionary.Add(20, "zwanzig");
|
||||
dictionary.Add(10, "dix");
|
||||
|
||||
Assert.AreEqual(6, dictionary.Count);
|
||||
Assert.AreEqual(3, dictionary.CountValues(10));
|
||||
Assert.AreEqual(2, dictionary.CountValues(20));
|
||||
Assert.AreEqual(1, dictionary.CountValues(30));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that counting the values of a non-existing key returns 0
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CountingValuesOfNonExistentKeyReturnsNull() {
|
||||
var dictionary = new MultiDictionary<int, string>();
|
||||
Assert.AreEqual(0, dictionary.CountValues(1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that its possible to remove values individually without affecting
|
||||
/// other values stored under the same key
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ValuesCanBeRemovedIndividually() {
|
||||
var dictionary = new MultiDictionary<int, string>();
|
||||
dictionary.Add(10, "ten");
|
||||
dictionary.Add(10, "zehn");
|
||||
dictionary.Add(10, "dix");
|
||||
|
||||
dictionary.Remove(10, "zehn");
|
||||
|
||||
Assert.AreEqual(2, dictionary.Count);
|
||||
CollectionAssert.AreEquivalent(
|
||||
new KeyValuePair<int, string>[] {
|
||||
new KeyValuePair<int, string>(10, "ten"),
|
||||
new KeyValuePair<int, string>(10, "dix")
|
||||
},
|
||||
dictionary
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the Count property returns the number of unique keys if it is called
|
||||
/// on the collection-of-collections interface implemented by the multi dictionary
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CollectionOfCollectionCountIsUniqueKeyCount() {
|
||||
var dictionary = new MultiDictionary<int, string>();
|
||||
dictionary.Add(10, "ten");
|
||||
dictionary.Add(10, "zehn");
|
||||
|
||||
Assert.AreEqual(2, dictionary.Count);
|
||||
var collectionOfCollections =
|
||||
(ICollection<KeyValuePair<int, ICollection<string>>>)dictionary;
|
||||
Assert.AreEqual(1, collectionOfCollections.Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the multi dictionary can be tested for containment of a specific value
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ContainmentCanBeTested() {
|
||||
var dictionary = new MultiDictionary<int, string>();
|
||||
dictionary.Add(10, "ten");
|
||||
dictionary.Add(10, "zehn");
|
||||
|
||||
Assert.IsTrue(dictionary.Contains(new KeyValuePair<int, string>(10, "ten")));
|
||||
Assert.IsTrue(dictionary.Contains(new KeyValuePair<int, string>(10, "zehn")));
|
||||
Assert.IsFalse(dictionary.Contains(new KeyValuePair<int, string>(10, "dix")));
|
||||
Assert.IsFalse(dictionary.Contains(new KeyValuePair<int, string>(20, "ten")));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the multi dictionary can be tested for containment of a specific key
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void KeyContainmentCanBeTested() {
|
||||
var dictionary = new MultiDictionary<int, string>();
|
||||
dictionary.Add(10, "ten");
|
||||
dictionary.Add(10, "zehn");
|
||||
|
||||
Assert.IsTrue(dictionary.ContainsKey(10));
|
||||
Assert.IsFalse(dictionary.ContainsKey(20));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the key collection can be retrieved from the dictionary
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void KeyCollectionCanBeRetrieved() {
|
||||
var dictionary = new MultiDictionary<int, string>();
|
||||
dictionary.Add(10, "ten");
|
||||
dictionary.Add(10, "zehn");
|
||||
|
||||
ICollection<int> keys = dictionary.Keys;
|
||||
Assert.IsNotNull(keys);
|
||||
Assert.AreEqual(1, keys.Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the key collection can be retrieved from the dictionary
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ValueCollectionCanBeRetrieved() {
|
||||
var dictionary = new MultiDictionary<int, string>();
|
||||
dictionary.Add(10, "ten");
|
||||
dictionary.Add(10, "zehn");
|
||||
dictionary.Add(20, "twenty");
|
||||
|
||||
ICollection<string> values = dictionary.Values;
|
||||
Assert.IsNotNull(values);
|
||||
Assert.AreEqual(3, values.Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that TryGetValue() returns false and doesn't throw if a key
|
||||
/// is not found in the collection
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TryGetValueReturnsFalseOnMissingKey() {
|
||||
var dictionary = new MultiDictionary<int, string>();
|
||||
ICollection<string> values;
|
||||
Assert.IsFalse(dictionary.TryGetValue(123, out values));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that keys can be looked up via TryGetValue()</summary>
|
||||
[Test]
|
||||
public void TryGetValueCanLookUpValues() {
|
||||
var dictionary = new MultiDictionary<int, string>();
|
||||
dictionary.Add(10, "ten");
|
||||
dictionary.Add(10, "zehn");
|
||||
ICollection<string> values;
|
||||
Assert.IsTrue(dictionary.TryGetValue(10, out values));
|
||||
Assert.AreEqual(2, values.Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that assigning null to a key deletes all the values stored
|
||||
/// under it
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void AssigningNullToKeyRemovesAllValues() {
|
||||
var dictionary = new MultiDictionary<int, string>();
|
||||
dictionary.Add(10, "ten");
|
||||
dictionary.Add(10, "zehn");
|
||||
dictionary.Add(20, "twenty");
|
||||
|
||||
Assert.AreEqual(3, dictionary.Count);
|
||||
dictionary[10] = null;
|
||||
Assert.AreEqual(1, dictionary.Count);
|
||||
Assert.IsFalse(dictionary.ContainsKey(10));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that assigning null to a key deletes all the values stored
|
||||
/// under it
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ValueListCanBeAssignedToNewKey() {
|
||||
var dictionary = new MultiDictionary<int, string>();
|
||||
dictionary[3] = new List<string>() { "three", "drei" };
|
||||
|
||||
Assert.AreEqual(2, dictionary.Count);
|
||||
Assert.IsTrue(dictionary.Contains(new KeyValuePair<int, string>(3, "three")));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that assigning null to a key deletes all the values stored
|
||||
/// under it
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ValueListCanOverwriteExistingKey() {
|
||||
var dictionary = new MultiDictionary<int, string>();
|
||||
dictionary.Add(10, "dix");
|
||||
|
||||
Assert.AreEqual(1, dictionary.Count);
|
||||
|
||||
dictionary[10] = new List<string>() { "ten", "zehn" };
|
||||
|
||||
Assert.AreEqual(2, dictionary.Count);
|
||||
Assert.IsFalse(dictionary.Contains(new KeyValuePair<int, string>(10, "dix")));
|
||||
Assert.IsTrue(dictionary.Contains(new KeyValuePair<int, string>(10, "ten")));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that nothing bad happens when a key is removed from the dictionary
|
||||
/// that it doesn't contain
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void NonExistingKeyCanBeRemoved() {
|
||||
var dictionary = new MultiDictionary<int, string>();
|
||||
Assert.AreEqual(0, dictionary.RemoveKey(123));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the remove method returns the number of values that have
|
||||
/// been removed from the dictionary
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void RemoveReturnsNumberOfValuesRemoved() {
|
||||
var dictionary = new MultiDictionary<int, string>();
|
||||
dictionary.Add(10, "ten");
|
||||
dictionary.Add(10, "zehn");
|
||||
Assert.AreEqual(2, dictionary.RemoveKey(10));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the dictionary becomes empty after clearing it
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DictionaryIsEmptyAfterClear() {
|
||||
var dictionary = new MultiDictionary<int, string>();
|
||||
dictionary.Add(10, "ten");
|
||||
dictionary.Add(10, "zehn");
|
||||
dictionary.Add(20, "twenty");
|
||||
Assert.AreEqual(3, dictionary.Count);
|
||||
dictionary.Clear();
|
||||
Assert.AreEqual(0, dictionary.Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that non-existing values can be removed from the dictionary
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void NonExistingValueCanBeRemoved() {
|
||||
var dictionary = new MultiDictionary<int, string>();
|
||||
Assert.IsFalse(dictionary.Remove(123, "test"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that nothing bad happens when the last value under a key is removed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void LastValueOfKeyCanBeRemoved() {
|
||||
var dictionary = new MultiDictionary<int, string>();
|
||||
dictionary.Add(123, "test");
|
||||
dictionary.Remove(123, "test");
|
||||
Assert.AreEqual(0, dictionary.CountValues(123));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the dictionary can be copied into an array
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DictionaryCanBeCopiedIntoArray() {
|
||||
var expected = new List<KeyValuePair<int, string>>() {
|
||||
new KeyValuePair<int, string>(1, "one"),
|
||||
new KeyValuePair<int, string>(1, "eins"),
|
||||
new KeyValuePair<int, string>(2, "two"),
|
||||
new KeyValuePair<int, string>(2, "zwei")
|
||||
};
|
||||
|
||||
var dictionary = new MultiDictionary<int, string>();
|
||||
foreach(KeyValuePair<int, string> entry in expected) {
|
||||
dictionary.Add(entry.Key, entry.Value);
|
||||
}
|
||||
|
||||
var actual = new KeyValuePair<int, string>[4];
|
||||
dictionary.CopyTo(actual, 0);
|
||||
|
||||
CollectionAssert.AreEquivalent(expected, actual);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Collections
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,144 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if !NO_NMOCK
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using NUnit.Framework;
|
||||
using NMock;
|
||||
|
||||
namespace Nuclex.Support.Collections {
|
||||
|
||||
/// <summary>Unit Test for the observable collection class</summary>
|
||||
[TestFixture]
|
||||
internal class ObservableCollectionTest {
|
||||
|
||||
#region interface IObservableCollectionSubscriber
|
||||
|
||||
/// <summary>Interface used to test the observable collection</summary>
|
||||
public interface IObservableCollectionSubscriber {
|
||||
|
||||
/// <summary>Called when the collection is about to clear its contents</summary>
|
||||
/// <param name="sender">Collection that is clearing its contents</param>
|
||||
/// <param name="arguments">Not used</param>
|
||||
void Clearing(object sender, EventArgs arguments);
|
||||
|
||||
/// <summary>Called when the collection has been cleared of its contents</summary>
|
||||
/// <param name="sender">Collection that was cleared of its contents</param>
|
||||
/// <param name="arguments">Not used</param>
|
||||
void Cleared(object sender, EventArgs arguments);
|
||||
|
||||
/// <summary>Called when an item is added to the collection</summary>
|
||||
/// <param name="sender">Collection to which an item is being added</param>
|
||||
/// <param name="arguments">Contains the item that is being added</param>
|
||||
void ItemAdded(object sender, ItemEventArgs<int> arguments);
|
||||
|
||||
/// <summary>Called when an item is removed from the collection</summary>
|
||||
/// <param name="sender">Collection from which an item is being removed</param>
|
||||
/// <param name="arguments">Contains the item that is being removed</param>
|
||||
void ItemRemoved(object sender, ItemEventArgs<int> arguments);
|
||||
|
||||
}
|
||||
|
||||
#endregion // interface IObservableCollectionSubscriber
|
||||
|
||||
/// <summary>Initialization routine executed before each test is run</summary>
|
||||
[SetUp]
|
||||
public void Setup() {
|
||||
this.mockery = new MockFactory();
|
||||
|
||||
this.mockedSubscriber = this.mockery.CreateMock<IObservableCollectionSubscriber>();
|
||||
|
||||
this.observedCollection = new ObservableCollection<int>();
|
||||
this.observedCollection.Clearing += new EventHandler(
|
||||
this.mockedSubscriber.MockObject.Clearing
|
||||
);
|
||||
this.observedCollection.Cleared += new EventHandler(
|
||||
this.mockedSubscriber.MockObject.Cleared
|
||||
);
|
||||
this.observedCollection.ItemAdded += new EventHandler<ItemEventArgs<int>>(
|
||||
this.mockedSubscriber.MockObject.ItemAdded
|
||||
);
|
||||
this.observedCollection.ItemRemoved += new EventHandler<ItemEventArgs<int>>(
|
||||
this.mockedSubscriber.MockObject.ItemRemoved
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Tests whether the Clearing event is fired</summary>
|
||||
[Test]
|
||||
public void TestClearingEvent() {
|
||||
this.mockedSubscriber.Expects.One.Method(m => m.Clearing(null, null)).WithAnyArguments();
|
||||
this.mockedSubscriber.Expects.One.Method(m => m.Cleared(null, null)).WithAnyArguments();
|
||||
this.observedCollection.Clear();
|
||||
|
||||
this.mockery.VerifyAllExpectationsHaveBeenMet();
|
||||
}
|
||||
|
||||
/// <summary>Tests whether the ItemAdded event is fired</summary>
|
||||
[Test]
|
||||
public void TestItemAddedEvent() {
|
||||
this.mockedSubscriber.Expects.One.Method(m => m.ItemAdded(null, null)).WithAnyArguments();
|
||||
|
||||
this.observedCollection.Add(123);
|
||||
|
||||
this.mockery.VerifyAllExpectationsHaveBeenMet();
|
||||
}
|
||||
|
||||
/// <summary>Tests whether the ItemRemoved event is fired</summary>
|
||||
[Test]
|
||||
public void TestItemRemovedEvent() {
|
||||
this.mockedSubscriber.Expects.One.Method(m => m.ItemAdded(null, null)).WithAnyArguments();
|
||||
|
||||
this.observedCollection.Add(123);
|
||||
|
||||
this.mockedSubscriber.Expects.One.Method(m => m.ItemRemoved(null, null)).WithAnyArguments();
|
||||
|
||||
this.observedCollection.Remove(123);
|
||||
|
||||
this.mockery.VerifyAllExpectationsHaveBeenMet();
|
||||
}
|
||||
|
||||
/// <summary>Tests whether a the list constructor is working</summary>
|
||||
[Test]
|
||||
public void TestListConstructor() {
|
||||
int[] integers = new int[] { 12, 34, 56, 78 };
|
||||
|
||||
var testCollection = new ObservableCollection<int>(integers);
|
||||
|
||||
CollectionAssert.AreEqual(integers, testCollection);
|
||||
}
|
||||
|
||||
/// <summary>Mock object factory</summary>
|
||||
private MockFactory mockery;
|
||||
/// <summary>The mocked observable collection subscriber</summary>
|
||||
private Mock<IObservableCollectionSubscriber> mockedSubscriber;
|
||||
/// <summary>An observable collection to which a mock will be subscribed</summary>
|
||||
private ObservableCollection<int> observedCollection;
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Collections
|
||||
|
||||
#endif // UNITTEST
|
||||
|
||||
#endif // !NO_NMOCK
|
||||
|
|
@ -1,589 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if !NO_NMOCK
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
|
||||
using NUnit.Framework;
|
||||
using NMock;
|
||||
|
||||
namespace Nuclex.Support.Collections {
|
||||
|
||||
/// <summary>Unit Test for the observable dictionary wrapper</summary>
|
||||
[TestFixture]
|
||||
internal class ObservableDictionaryTest {
|
||||
|
||||
#region interface IObservableDictionarySubscriber
|
||||
|
||||
/// <summary>Interface used to test the observable dictionary</summary>
|
||||
public interface IObservableDictionarySubscriber {
|
||||
|
||||
/// <summary>Called when the dictionary is about to clear its contents</summary>
|
||||
/// <param name="sender">Dictionary that is clearing its contents</param>
|
||||
/// <param name="arguments">Not used</param>
|
||||
void Clearing(object sender, EventArgs arguments);
|
||||
|
||||
/// <summary>Called when the dictionary has been clear of its contents</summary>
|
||||
/// <param name="sender">Dictionary that was cleared of its contents</param>
|
||||
/// <param name="arguments">Not used</param>
|
||||
void Cleared(object sender, EventArgs arguments);
|
||||
|
||||
/// <summary>Called when an item is added to the dictionary</summary>
|
||||
/// <param name="sender">Dictionary to which an item is being added</param>
|
||||
/// <param name="arguments">Contains the item that is being added</param>
|
||||
void ItemAdded(object sender, ItemEventArgs<KeyValuePair<int, string>> arguments);
|
||||
|
||||
/// <summary>Called when an item is removed from the dictionary</summary>
|
||||
/// <param name="sender">Dictionary from which an item is being removed</param>
|
||||
/// <param name="arguments">Contains the item that is being removed</param>
|
||||
void ItemRemoved(object sender, ItemEventArgs<KeyValuePair<int, string>> arguments);
|
||||
|
||||
/// <summary>Called when an item is replaced in the dictionary</summary>
|
||||
/// <param name="sender">Dictionary in which an item is being replaced</param>
|
||||
/// <param name="arguments">Contains the replaced item and its replacement</param>
|
||||
void ItemReplaced(
|
||||
object sender, ItemReplaceEventArgs<KeyValuePair<int, string>> arguments
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
#endregion // interface IObservableDictionarySubscriber
|
||||
|
||||
/// <summary>Initialization routine executed before each test is run</summary>
|
||||
[SetUp]
|
||||
public void Setup() {
|
||||
this.mockery = new MockFactory();
|
||||
|
||||
this.mockedSubscriber = this.mockery.CreateMock<IObservableDictionarySubscriber>();
|
||||
|
||||
this.observedDictionary = new ObservableDictionary<int, string>();
|
||||
this.observedDictionary.Add(1, "one");
|
||||
this.observedDictionary.Add(2, "two");
|
||||
this.observedDictionary.Add(3, "three");
|
||||
this.observedDictionary.Add(42, "forty-two");
|
||||
|
||||
this.observedDictionary.Clearing +=
|
||||
new EventHandler(this.mockedSubscriber.MockObject.Clearing);
|
||||
this.observedDictionary.Cleared +=
|
||||
new EventHandler(this.mockedSubscriber.MockObject.Cleared);
|
||||
this.observedDictionary.ItemAdded +=
|
||||
new EventHandler<ItemEventArgs<KeyValuePair<int, string>>>(
|
||||
this.mockedSubscriber.MockObject.ItemAdded
|
||||
);
|
||||
this.observedDictionary.ItemRemoved +=
|
||||
new EventHandler<ItemEventArgs<KeyValuePair<int, string>>>(
|
||||
this.mockedSubscriber.MockObject.ItemRemoved
|
||||
);
|
||||
this.observedDictionary.ItemReplaced +=
|
||||
new EventHandler<ItemReplaceEventArgs<KeyValuePair<int, string>>>(
|
||||
this.mockedSubscriber.MockObject.ItemReplaced
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the default constructor of the observable dictionary works
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestDefaultConstructor() {
|
||||
ObservableDictionary<int, string> testDictionary =
|
||||
new ObservableDictionary<int, string>();
|
||||
|
||||
Assert.AreEqual(0, testDictionary.Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the copy constructor of the observable dictionary works
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCopyConstructor() {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ObservableDictionary<int, string> testDictionary = makeObservable(numbers);
|
||||
|
||||
CollectionAssert.AreEqual(numbers, testDictionary);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the IsReadOnly property is working</summary>
|
||||
[Test]
|
||||
public void TestIsReadOnly() {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ObservableDictionary<int, string> testDictionary = makeObservable(numbers);
|
||||
|
||||
Assert.IsFalse(testDictionary.IsReadOnly);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the Contains() method of the observable dictionary is able to
|
||||
/// determine if the dictionary contains an item
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestContains() {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ObservableDictionary<int, string> testDictionary = makeObservable(numbers);
|
||||
|
||||
Assert.IsTrue(
|
||||
testDictionary.Contains(new KeyValuePair<int, string>(42, "forty-two"))
|
||||
);
|
||||
Assert.IsFalse(
|
||||
testDictionary.Contains(new KeyValuePair<int, string>(24, "twenty-four"))
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the Contains() method of the observable dictionary is able to
|
||||
/// determine if the dictionary contains a key
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestContainsKey() {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ObservableDictionary<int, string> testDictionary = makeObservable(numbers);
|
||||
|
||||
Assert.IsTrue(testDictionary.ContainsKey(42));
|
||||
Assert.IsFalse(testDictionary.ContainsKey(24));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the CopyTo() of the observable dictionary works
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCopyToArray() {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ObservableDictionary<int, string> testDictionary = makeObservable(numbers);
|
||||
|
||||
KeyValuePair<int, string>[] items = new KeyValuePair<int, string>[numbers.Count];
|
||||
|
||||
testDictionary.CopyTo(items, 0);
|
||||
|
||||
CollectionAssert.AreEqual(numbers, items);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the typesafe enumerator of the observable dictionary is working
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestTypesafeEnumerator() {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ObservableDictionary<int, string> testDictionary = makeObservable(numbers);
|
||||
|
||||
List<KeyValuePair<int, string>> outputItems = new List<KeyValuePair<int, string>>();
|
||||
foreach(KeyValuePair<int, string> item in testDictionary) {
|
||||
outputItems.Add(item);
|
||||
}
|
||||
|
||||
CollectionAssert.AreEqual(numbers, outputItems);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the keys collection of the observable dictionary can be queried
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestGetKeysCollection() {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ObservableDictionary<int, string> testDictionary = makeObservable(numbers);
|
||||
|
||||
ICollection<int> inputKeys = numbers.Keys;
|
||||
ICollection<int> keys = testDictionary.Keys;
|
||||
CollectionAssert.AreEquivalent(inputKeys, keys);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the values collection of the observable dictionary can be queried
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestGetValuesCollection() {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ObservableDictionary<int, string> testDictionary = makeObservable(numbers);
|
||||
|
||||
ICollection<string> inputValues = numbers.Values;
|
||||
ICollection<string> values = testDictionary.Values;
|
||||
CollectionAssert.AreEquivalent(inputValues, values);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the TryGetValue() method of the observable dictionary is working
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestTryGetValue() {
|
||||
string value;
|
||||
|
||||
Assert.IsTrue(this.observedDictionary.TryGetValue(42, out value));
|
||||
Assert.AreEqual("forty-two", value);
|
||||
|
||||
Assert.IsFalse(this.observedDictionary.TryGetValue(24, out value));
|
||||
Assert.AreEqual(null, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the retrieval of values using the indexer of the observable
|
||||
/// dictionary is working
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestRetrieveValueByIndexer() {
|
||||
Assert.AreEqual("forty-two", this.observedDictionary[42]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether an exception is thrown if the indexer of the observable dictionary
|
||||
/// is used to attempt to retrieve a non-existing value
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestRetrieveNonExistingValueByIndexer() {
|
||||
Assert.Throws<KeyNotFoundException>(
|
||||
delegate() { Console.WriteLine(this.observedDictionary[24]); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the Add() methods works via the generic
|
||||
/// IDictionary<> interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestAddViaGenericIDictionary() {
|
||||
this.mockedSubscriber.Expects.One.Method(m => m.ItemAdded(null, null)).WithAnyArguments();
|
||||
(this.observedDictionary as IDictionary<int, string>).Add(10, "ten");
|
||||
this.mockery.VerifyAllExpectationsHaveBeenMet();
|
||||
|
||||
CollectionAssert.Contains(
|
||||
this.observedDictionary, new KeyValuePair<int, string>(10, "ten")
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the Remove() method works via the generic
|
||||
/// IDictionary<> interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestRemoveViaGenericIDictionary() {
|
||||
this.mockedSubscriber.Expects.One.Method(m => m.ItemRemoved(null, null)).WithAnyArguments();
|
||||
(this.observedDictionary as IDictionary<int, string>).Remove(3);
|
||||
this.mockery.VerifyAllExpectationsHaveBeenMet();
|
||||
|
||||
CollectionAssert.DoesNotContain(this.observedDictionary.Keys, 3);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the TryGetValue() method of the observable dictionary is working
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestRetrieveValueByIndexerViaGenericIDictionary() {
|
||||
Assert.AreEqual(
|
||||
"forty-two", (this.observedDictionary as IDictionary<int, string>)[42]
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the indexer can be used to insert an item via the generic
|
||||
/// IDictionar<> interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestReplaceByIndexerViaGenericIDictionary() {
|
||||
this.mockedSubscriber.Expects.One.Method(
|
||||
m => m.ItemReplaced(null, null)
|
||||
).WithAnyArguments();
|
||||
|
||||
(this.observedDictionary as IDictionary<int, string>)[42] = "two and fourty";
|
||||
this.mockery.VerifyAllExpectationsHaveBeenMet();
|
||||
|
||||
Assert.AreEqual("two and fourty", this.observedDictionary[42]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the Clear() method of observable dictionary is working
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestClearViaIDictionary() {
|
||||
this.mockedSubscriber.Expects.One.Method(
|
||||
m => m.Clearing(null, null)
|
||||
).WithAnyArguments();
|
||||
this.mockedSubscriber.Expects.One.Method(
|
||||
m => m.Cleared(null, null)
|
||||
).WithAnyArguments();
|
||||
(this.observedDictionary as IDictionary).Clear();
|
||||
this.mockery.VerifyAllExpectationsHaveBeenMet();
|
||||
|
||||
Assert.AreEqual(0, this.observedDictionary.Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the Add() method works via the IDictionary interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestAddViaIDictionary() {
|
||||
this.mockedSubscriber.Expects.One.Method(
|
||||
m => m.ItemAdded(null, null)
|
||||
).WithAnyArguments();
|
||||
(this.observedDictionary as IDictionary).Add(24, "twenty-four");
|
||||
this.mockery.VerifyAllExpectationsHaveBeenMet();
|
||||
|
||||
CollectionAssert.Contains(
|
||||
this.observedDictionary, new KeyValuePair<int, string>(24, "twenty-four")
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the Contains() method of the observable dictionary is able to
|
||||
/// determine if the dictionary contains an item via the IDictionary interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestContainsViaIDictionary() {
|
||||
Assert.IsTrue((this.observedDictionary as IDictionary).Contains(42));
|
||||
Assert.IsFalse((this.observedDictionary as IDictionary).Contains(24));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the GetEnumerator() method of the observable dictionary
|
||||
/// returns a working enumerator if accessed via the IDictionary interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestEnumeratorViaIDictionary() {
|
||||
Dictionary<int, string> outputNumbers = new Dictionary<int, string>();
|
||||
foreach(DictionaryEntry entry in (this.observedDictionary as IDictionary)) {
|
||||
(outputNumbers as IDictionary).Add(entry.Key, entry.Value);
|
||||
}
|
||||
|
||||
CollectionAssert.AreEquivalent(this.observedDictionary, outputNumbers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the IsFixedSize property of the observable dictionary returns
|
||||
/// the expected result for a read only dictionary based on a dynamic dictionary
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestIsFixedSizeViaIList() {
|
||||
Assert.IsFalse((this.observedDictionary as IDictionary).IsFixedSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the keys collection of the observable dictionary can be queried
|
||||
/// via the IDictionary interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestGetKeysCollectionViaIDictionary() {
|
||||
ICollection keys = (this.observedDictionary as IDictionary).Keys;
|
||||
Assert.AreEqual(this.observedDictionary.Count, keys.Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the values collection of the observable dictionary can be queried
|
||||
/// via the IDictionary interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestGetValuesCollectionViaIDictionary() {
|
||||
ICollection values = (this.observedDictionary as IDictionary).Values;
|
||||
Assert.AreEqual(this.observedDictionary.Count, values.Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether Remove() method works via the IDictionary interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestRemoveViaIDictionary() {
|
||||
this.mockedSubscriber.Expects.One.Method(m => m.ItemRemoved(null, null)).WithAnyArguments();
|
||||
(this.observedDictionary as IDictionary).Remove(3);
|
||||
this.mockery.VerifyAllExpectationsHaveBeenMet();
|
||||
|
||||
CollectionAssert.DoesNotContain(this.observedDictionary.Keys, 3);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the retrieval of values using the indexer of the observable
|
||||
/// dictionary is working via the IDictionary interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestRetrieveValueByIndexerViaIDictionary() {
|
||||
Assert.AreEqual("forty-two", (this.observedDictionary as IDictionary)[42]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies the indexer can be used to insert an item via the IDictionary interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestReplaceByIndexerViaIDictionary() {
|
||||
this.mockedSubscriber.Expects.One.Method(
|
||||
m => m.ItemRemoved(null, null)
|
||||
).WithAnyArguments();
|
||||
this.mockedSubscriber.Expects.One.Method(
|
||||
m => m.ItemAdded(null, null)
|
||||
).WithAnyArguments();
|
||||
|
||||
(this.observedDictionary as IDictionary)[42] = "two and fourty";
|
||||
this.mockery.VerifyAllExpectationsHaveBeenMet();
|
||||
|
||||
Assert.AreEqual("two and fourty", this.observedDictionary[42]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether Add() method is working via the generic
|
||||
/// ICollection<> interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestAddViaGenericICollection() {
|
||||
this.mockedSubscriber.Expects.One.Method(
|
||||
m => m.ItemAdded(null, null)
|
||||
).WithAnyArguments();
|
||||
|
||||
(this.observedDictionary as ICollection<KeyValuePair<int, string>>).Add(
|
||||
new KeyValuePair<int, string>(24, "twenty-four")
|
||||
);
|
||||
this.mockery.VerifyAllExpectationsHaveBeenMet();
|
||||
|
||||
CollectionAssert.Contains(
|
||||
this.observedDictionary, new KeyValuePair<int, string>(24, "twenty-four")
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the Clear() method is working via the generic
|
||||
/// ICollection<> interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestClearViaGenericICollection() {
|
||||
this.mockedSubscriber.Expects.One.Method(
|
||||
m => m.Clearing(null, null)
|
||||
).WithAnyArguments();
|
||||
this.mockedSubscriber.Expects.One.Method(
|
||||
m => m.Cleared(null, null)
|
||||
).WithAnyArguments();
|
||||
|
||||
(this.observedDictionary as ICollection<KeyValuePair<int, string>>).Clear();
|
||||
this.mockery.VerifyAllExpectationsHaveBeenMet();
|
||||
|
||||
Assert.AreEqual(0, this.observedDictionary.Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the Remove() method is working via the
|
||||
/// generic ICollection<> interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestRemoveViaGenericICollection() {
|
||||
IEnumerator<KeyValuePair<int, string>> enumerator =
|
||||
(this.observedDictionary as ICollection<KeyValuePair<int, string>>).GetEnumerator();
|
||||
enumerator.MoveNext();
|
||||
|
||||
this.mockedSubscriber.Expects.One.Method(
|
||||
m => m.ItemRemoved(null, null)
|
||||
).WithAnyArguments();
|
||||
|
||||
(this.observedDictionary as ICollection<KeyValuePair<int, string>>).Remove(
|
||||
enumerator.Current
|
||||
);
|
||||
this.mockery.VerifyAllExpectationsHaveBeenMet();
|
||||
|
||||
CollectionAssert.DoesNotContain(this.observedDictionary, enumerator.Current);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the CopyTo() of the observable dictionary works when called
|
||||
/// via the the ICollection interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCopyToArrayViaICollection() {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ObservableDictionary<int, string> testDictionary = makeObservable(numbers);
|
||||
|
||||
DictionaryEntry[] entries = new DictionaryEntry[numbers.Count];
|
||||
(testDictionary as ICollection).CopyTo(entries, 0);
|
||||
|
||||
KeyValuePair<int, string>[] items = new KeyValuePair<int, string>[numbers.Count];
|
||||
for(int index = 0; index < entries.Length; ++index) {
|
||||
items[index] = new KeyValuePair<int, string>(
|
||||
(int)entries[index].Key, (string)entries[index].Value
|
||||
);
|
||||
}
|
||||
CollectionAssert.AreEquivalent(numbers, items);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the IsSynchronized property and the SyncRoot property are working
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestSynchronization() {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ObservableDictionary<int, string> testDictionary = makeObservable(numbers);
|
||||
|
||||
if(!(testDictionary as ICollection).IsSynchronized) {
|
||||
lock((testDictionary as ICollection).SyncRoot) {
|
||||
Assert.AreEqual(numbers.Count, testDictionary.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test whether the observable dictionary can be serialized
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestSerialization() {
|
||||
BinaryFormatter formatter = new BinaryFormatter();
|
||||
|
||||
using(MemoryStream memory = new MemoryStream()) {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ObservableDictionary<int, string> testDictionary1 = makeObservable(numbers);
|
||||
|
||||
formatter.Serialize(memory, testDictionary1);
|
||||
memory.Position = 0;
|
||||
object testDictionary2 = formatter.Deserialize(memory);
|
||||
|
||||
CollectionAssert.AreEquivalent(testDictionary1, (IEnumerable)testDictionary2);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new observable dictionary filled with some values for testing
|
||||
/// </summary>
|
||||
/// <returns>The newly created observable dictionary</returns>
|
||||
private static Dictionary<int, string> createTestDictionary() {
|
||||
Dictionary<int, string> numbers = new Dictionary<int, string>();
|
||||
numbers.Add(1, "one");
|
||||
numbers.Add(2, "two");
|
||||
numbers.Add(3, "three");
|
||||
numbers.Add(42, "forty-two");
|
||||
return new Dictionary<int, string>(numbers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new observable dictionary filled with some values for testing
|
||||
/// </summary>
|
||||
/// <returns>The newly created observable dictionary</returns>
|
||||
private static ObservableDictionary<int, string> makeObservable(
|
||||
IDictionary<int, string> dictionary
|
||||
) {
|
||||
return new ObservableDictionary<int, string>(dictionary);
|
||||
}
|
||||
|
||||
/// <summary>Mock object factory</summary>
|
||||
private MockFactory mockery;
|
||||
/// <summary>The mocked observable collection subscriber</summary>
|
||||
private Mock<IObservableDictionarySubscriber> mockedSubscriber;
|
||||
/// <summary>An observable dictionary to which a mock will be subscribed</summary>
|
||||
private ObservableDictionary<int, string> observedDictionary;
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Collections
|
||||
|
||||
#endif // UNITTEST
|
||||
|
||||
#endif // !NO_NMOCK
|
||||
|
|
@ -1,175 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if !NO_NMOCK
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using NUnit.Framework;
|
||||
using NMock;
|
||||
|
||||
namespace Nuclex.Support.Collections {
|
||||
|
||||
/// <summary>Unit Test for the observable list class</summary>
|
||||
[TestFixture]
|
||||
internal class ObservableListTest {
|
||||
|
||||
#region interface IObservableCollectionSubscriber
|
||||
|
||||
/// <summary>Interface used to test the observable collection</summary>
|
||||
public interface IObservableCollectionSubscriber {
|
||||
|
||||
/// <summary>Called when the collection is about to clear its contents</summary>
|
||||
/// <param name="sender">Collection that is clearing its contents</param>
|
||||
/// <param name="arguments">Not used</param>
|
||||
void Clearing(object sender, EventArgs arguments);
|
||||
|
||||
/// <summary>Called when the collection has been cleared of its contents</summary>
|
||||
/// <param name="sender">Collection that was cleared of its contents</param>
|
||||
/// <param name="arguments">Not used</param>
|
||||
void Cleared(object sender, EventArgs arguments);
|
||||
|
||||
/// <summary>Called when an item is added to the collection</summary>
|
||||
/// <param name="sender">Collection to which an item is being added</param>
|
||||
/// <param name="arguments">Contains the item that is being added</param>
|
||||
void ItemAdded(object sender, ItemEventArgs<int> arguments);
|
||||
|
||||
/// <summary>Called when an item is removed from the collection</summary>
|
||||
/// <param name="sender">Collection from which an item is being removed</param>
|
||||
/// <param name="arguments">Contains the item that is being removed</param>
|
||||
void ItemRemoved(object sender, ItemEventArgs<int> arguments);
|
||||
|
||||
/// <summary>Called when an item is replaced in the dictionary</summary>
|
||||
/// <param name="sender">Dictionary in which an item is being replaced</param>
|
||||
/// <param name="arguments">Contains the replaced item and its replacement</param>
|
||||
void ItemReplaced(object sender, ItemReplaceEventArgs<int> arguments);
|
||||
|
||||
}
|
||||
|
||||
#endregion // interface IObservableCollectionSubscriber
|
||||
|
||||
/// <summary>Initialization routine executed before each test is run</summary>
|
||||
[SetUp]
|
||||
public void Setup() {
|
||||
this.mockery = new MockFactory();
|
||||
|
||||
this.mockedSubscriber = this.mockery.CreateMock<IObservableCollectionSubscriber>();
|
||||
|
||||
this.observedList = new ObservableList<int>();
|
||||
this.observedList.Clearing += new EventHandler(
|
||||
this.mockedSubscriber.MockObject.Clearing
|
||||
);
|
||||
this.observedList.Cleared += new EventHandler(
|
||||
this.mockedSubscriber.MockObject.Cleared
|
||||
);
|
||||
this.observedList.ItemAdded += new EventHandler<ItemEventArgs<int>>(
|
||||
this.mockedSubscriber.MockObject.ItemAdded
|
||||
);
|
||||
this.observedList.ItemRemoved += new EventHandler<ItemEventArgs<int>>(
|
||||
this.mockedSubscriber.MockObject.ItemRemoved
|
||||
);
|
||||
this.observedList.ItemReplaced += new EventHandler<ItemReplaceEventArgs<int>>(
|
||||
this.mockedSubscriber.MockObject.ItemReplaced
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Tests whether the Clearing event is fired</summary>
|
||||
[Test]
|
||||
public void TestClearingEvent() {
|
||||
this.mockedSubscriber.Expects.One.Method(m => m.Clearing(null, null)).WithAnyArguments();
|
||||
this.mockedSubscriber.Expects.One.Method(m => m.Cleared(null, null)).WithAnyArguments();
|
||||
this.observedList.Clear();
|
||||
|
||||
this.mockery.VerifyAllExpectationsHaveBeenMet();
|
||||
}
|
||||
|
||||
/// <summary>Tests whether the ItemAdded event is fired</summary>
|
||||
[Test]
|
||||
public void TestItemAddedEvent() {
|
||||
this.mockedSubscriber.Expects.One.Method(m => m.ItemAdded(null, null)).WithAnyArguments();
|
||||
|
||||
this.observedList.Add(123);
|
||||
|
||||
this.mockery.VerifyAllExpectationsHaveBeenMet();
|
||||
}
|
||||
|
||||
/// <summary>Tests whether the ItemRemoved event is fired</summary>
|
||||
[Test]
|
||||
public void TestItemRemovedEvent() {
|
||||
this.mockedSubscriber.Expects.One.Method(m => m.ItemAdded(null, null)).WithAnyArguments();
|
||||
|
||||
this.observedList.Add(123);
|
||||
|
||||
this.mockedSubscriber.Expects.One.Method(m => m.ItemRemoved(null, null)).WithAnyArguments();
|
||||
|
||||
this.observedList.Remove(123);
|
||||
|
||||
this.mockery.VerifyAllExpectationsHaveBeenMet();
|
||||
}
|
||||
|
||||
/// <summary>Tests whether items in the collection can be replaced</summary>
|
||||
[Test]
|
||||
public void TestItemReplacement() {
|
||||
this.mockedSubscriber.Expects.Exactly(3).Method(
|
||||
m => m.ItemAdded(null, null)
|
||||
).WithAnyArguments();
|
||||
|
||||
this.observedList.Add(1);
|
||||
this.observedList.Add(2);
|
||||
this.observedList.Add(3);
|
||||
|
||||
this.mockedSubscriber.Expects.One.Method(m => m.ItemReplaced(null, null)).WithAnyArguments();
|
||||
|
||||
// Replace the middle item with something else
|
||||
this.observedList[1] = 4;
|
||||
|
||||
Assert.AreEqual(
|
||||
1, this.observedList.IndexOf(4)
|
||||
);
|
||||
|
||||
this.mockery.VerifyAllExpectationsHaveBeenMet();
|
||||
}
|
||||
|
||||
/// <summary>Tests whether a the list constructor is working</summary>
|
||||
[Test]
|
||||
public void TestListConstructor() {
|
||||
int[] integers = new int[] { 12, 34, 56, 78 };
|
||||
|
||||
var testList = new ObservableList<int>(integers);
|
||||
|
||||
CollectionAssert.AreEqual(integers, testList);
|
||||
}
|
||||
|
||||
/// <summary>Mock object factory</summary>
|
||||
private MockFactory mockery;
|
||||
/// <summary>The mocked observable collection subscriber</summary>
|
||||
private Mock<IObservableCollectionSubscriber> mockedSubscriber;
|
||||
/// <summary>An observable collection to which a mock will be subscribed</summary>
|
||||
private ObservableList<int> observedList;
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Collections
|
||||
|
||||
#endif // UNITTEST
|
||||
|
||||
#endif // !NO_NMOCK
|
||||
|
|
@ -1,300 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if !NO_SETS
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
|
||||
using NUnit.Framework;
|
||||
using NMock;
|
||||
|
||||
namespace Nuclex.Support.Collections {
|
||||
|
||||
/// <summary>Unit Test for the observable set wrapper</summary>
|
||||
[TestFixture]
|
||||
internal class ObservableSetTest {
|
||||
|
||||
#region interface IObservableCollectionSubscriber<TItem>
|
||||
|
||||
public interface IObservableCollectionSubscriber<TItem> {
|
||||
|
||||
/// <summary>Called when an item has been added to the collection</summary>
|
||||
void ItemAdded(object sender, ItemEventArgs<TItem> arguments);
|
||||
/// <summary>Called when an item is removed from the collection</summary>
|
||||
void ItemRemoved(object sender, ItemEventArgs<TItem> arguments);
|
||||
/// <summary>Called when an item is replaced in the collection</summary>
|
||||
void ItemReplaced(object sender, ItemReplaceEventArgs<TItem> arguments);
|
||||
/// <summary>Called when the collection is about to be cleared</summary>
|
||||
void Clearing(object sender, EventArgs arguments);
|
||||
/// <summary>Called when the collection has been cleared</summary>
|
||||
void Cleared(object sender, EventArgs arguments);
|
||||
|
||||
}
|
||||
|
||||
#endregion // interface IObservableCollectionSubscriber<TItem>
|
||||
|
||||
/// <summary>Called before each test is run</summary>
|
||||
[SetUp]
|
||||
public void Setup() {
|
||||
this.mockFactory = new MockFactory();
|
||||
this.observableSet = new ObservableSet<int>();
|
||||
|
||||
this.subscriber = this.mockFactory.CreateMock<IObservableCollectionSubscriber<int>>();
|
||||
this.observableSet.ItemAdded += this.subscriber.MockObject.ItemAdded;
|
||||
this.observableSet.ItemRemoved += this.subscriber.MockObject.ItemRemoved;
|
||||
this.observableSet.ItemReplaced += this.subscriber.MockObject.ItemReplaced;
|
||||
this.observableSet.Clearing += this.subscriber.MockObject.Clearing;
|
||||
this.observableSet.Cleared += this.subscriber.MockObject.Cleared;
|
||||
}
|
||||
|
||||
/// <summary>Called after each test has run</summary>
|
||||
[TearDown]
|
||||
public void Teardown() {
|
||||
if(this.mockFactory != null) {
|
||||
this.mockFactory.VerifyAllExpectationsHaveBeenMet();
|
||||
|
||||
this.subscriber = null;
|
||||
this.mockFactory.Dispose();
|
||||
this.mockFactory = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the observable set has a default constructor
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void HasDefaultConstructor() {
|
||||
Assert.IsNotNull(new ObservableSet<int>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that adding items to the set triggers the 'ItemAdded' event
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void AddingItemsTriggersEvent() {
|
||||
this.subscriber.Expects.One.Method((s) => s.ItemAdded(null, null)).WithAnyArguments();
|
||||
this.observableSet.Add(123);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that removing items from the set triggers the 'ItemRemoved' event
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void RemovingItemsTriggersEvent() {
|
||||
this.subscriber.Expects.One.Method((s) => s.ItemAdded(null, null)).WithAnyArguments();
|
||||
this.observableSet.Add(123);
|
||||
|
||||
this.subscriber.Expects.One.Method((s) => s.ItemRemoved(null, null)).WithAnyArguments();
|
||||
this.observableSet.Remove(123);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that adding items to the set triggers the 'ItemAdded' event
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void AddingAlreadyContainedItemDoesNotTriggerEvent() {
|
||||
this.subscriber.Expects.One.Method((s) => s.ItemAdded(null, null)).WithAnyArguments();
|
||||
this.observableSet.Add(123);
|
||||
|
||||
this.subscriber.Expects.No.Method((s) => s.ItemAdded(null, null)).WithAnyArguments();
|
||||
this.observableSet.Add(123);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that excepting the set with itself empties the set
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ExceptWithSelfEmptiesSet() {
|
||||
this.subscriber.Expects.Exactly(3).Method(
|
||||
(s) => s.ItemAdded(null, null)
|
||||
).WithAnyArguments();
|
||||
|
||||
this.observableSet.Add(1);
|
||||
this.observableSet.Add(2);
|
||||
this.observableSet.Add(3);
|
||||
|
||||
Assert.AreEqual(3, this.observableSet.Count);
|
||||
|
||||
this.subscriber.Expects.One.Method((s) => s.Clearing(null, null)).WithAnyArguments();
|
||||
this.subscriber.Expects.One.Method((s) => s.Cleared(null, null)).WithAnyArguments();
|
||||
|
||||
this.observableSet.ExceptWith(this.observableSet);
|
||||
Assert.AreEqual(0, this.observableSet.Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a set can be excepted with a collection
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SetCanBeExceptedWithCollection() {
|
||||
this.subscriber.Expects.Exactly(2).Method(
|
||||
(s) => s.ItemAdded(null, null)
|
||||
).WithAnyArguments();
|
||||
|
||||
this.observableSet.Add(1);
|
||||
this.observableSet.Add(2);
|
||||
|
||||
var collection = new List<int>() { 1 };
|
||||
|
||||
this.subscriber.Expects.One.Method((s) => s.ItemRemoved(null, null)).WithAnyArguments();
|
||||
this.observableSet.ExceptWith(collection);
|
||||
Assert.AreEqual(1, this.observableSet.Count);
|
||||
Assert.IsTrue(this.observableSet.Contains(2));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a set can be intersected with a collection
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SetCanBeIntersectedWithCollection() {
|
||||
this.subscriber.Expects.Exactly(2).Method(
|
||||
(s) => s.ItemAdded(null, null)
|
||||
).WithAnyArguments();
|
||||
|
||||
this.observableSet.Add(1);
|
||||
this.observableSet.Add(2);
|
||||
|
||||
var collection = new List<int>() { 1 };
|
||||
|
||||
this.subscriber.Expects.One.Method((s) => s.ItemRemoved(null, null)).WithAnyArguments();
|
||||
this.observableSet.IntersectWith(collection);
|
||||
Assert.AreEqual(1, this.observableSet.Count);
|
||||
Assert.IsTrue(this.observableSet.Contains(1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that it's possible to determine whether a set is a proper subset
|
||||
/// or superset of another set
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CanDetermineProperSubsetAndSuperset() {
|
||||
var set1 = new ObservableSet<int>() { 1, 2, 3 };
|
||||
var set2 = new HashSet<int>() { 1, 3 };
|
||||
|
||||
Assert.IsTrue(set1.IsProperSupersetOf(set2));
|
||||
Assert.IsTrue(set2.IsProperSubsetOf(set1));
|
||||
|
||||
set2.Add(2);
|
||||
|
||||
Assert.IsFalse(set1.IsProperSupersetOf(set2));
|
||||
Assert.IsFalse(set2.IsProperSubsetOf(set1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that it's possible to determine whether a set is a subset
|
||||
/// or a superset of another set
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CanDetermineSubsetAndSuperset() {
|
||||
var set1 = new ObservableSet<int>() { 1, 2, 3 };
|
||||
var set2 = new HashSet<int>() { 1, 2, 3 };
|
||||
|
||||
Assert.IsTrue(set1.IsSupersetOf(set2));
|
||||
Assert.IsTrue(set2.IsSubsetOf(set1));
|
||||
|
||||
set2.Add(4);
|
||||
|
||||
Assert.IsFalse(set1.IsSupersetOf(set2));
|
||||
Assert.IsFalse(set2.IsSubsetOf(set1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a set can determine if another set overlaps with it
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CanDetermineOverlap() {
|
||||
var set1 = new ObservableSet<int>() { 1, 3, 5 };
|
||||
var set2 = new HashSet<int>() { 3 };
|
||||
|
||||
Assert.IsTrue(set1.Overlaps(set2));
|
||||
Assert.IsTrue(set2.Overlaps(set1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a set can determine if another set contains the same elements
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CanDetermineSetEquality() {
|
||||
var set1 = new ObservableSet<int>() { 1, 3, 5 };
|
||||
var set2 = new HashSet<int>() { 3, 1, 5 };
|
||||
|
||||
Assert.IsTrue(set1.SetEquals(set2));
|
||||
Assert.IsTrue(set2.SetEquals(set1));
|
||||
|
||||
set1.Add(7);
|
||||
|
||||
Assert.IsFalse(set1.SetEquals(set2));
|
||||
Assert.IsFalse(set2.SetEquals(set1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a set can be symmetrically excepted with another set
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CanBeSymmetricallyExcepted() {
|
||||
var set1 = new ObservableSet<int>() { 1, 2, 3 };
|
||||
var set2 = new HashSet<int>() { 3, 4, 5 };
|
||||
|
||||
set1.SymmetricExceptWith(set2);
|
||||
|
||||
Assert.AreEqual(4, set1.Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a union of two sets can be built
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CanBeUnioned() {
|
||||
this.subscriber.Expects.Exactly(3).Method(
|
||||
(s) => s.ItemAdded(null, null)
|
||||
).WithAnyArguments();
|
||||
|
||||
this.observableSet.Add(1);
|
||||
this.observableSet.Add(2);
|
||||
this.observableSet.Add(3);
|
||||
|
||||
var set2 = new ObservableSet<int>() { 3, 4, 5 };
|
||||
|
||||
this.subscriber.Expects.Exactly(2).Method(
|
||||
(s) => s.ItemAdded(null, null)
|
||||
).WithAnyArguments();
|
||||
this.observableSet.UnionWith(set2);
|
||||
Assert.AreEqual(5, this.observableSet.Count);
|
||||
}
|
||||
|
||||
/// <summary>Creates mock object for the test</summary>
|
||||
private MockFactory mockFactory;
|
||||
/// <summary>Observable set being tested</summary>
|
||||
private ObservableSet<int> observableSet;
|
||||
/// <summary>Subscriber for the observable set's events</summary>
|
||||
private Mock<IObservableCollectionSubscriber<int>> subscriber;
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Collections
|
||||
|
||||
#endif // UNITTEST
|
||||
|
||||
#endif // !NO_SETS
|
||||
|
|
@ -1,150 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.Collections {
|
||||
|
||||
/// <summary>Unit Test for the priority queue class</summary>
|
||||
[TestFixture]
|
||||
internal class PairPriorityQueueTest {
|
||||
|
||||
/// <summary>Tests to ensure the count property is properly updated</summary>
|
||||
[Test]
|
||||
public void TestCount() {
|
||||
PairPriorityQueue<float, string> testQueue = new PairPriorityQueue<float, string>();
|
||||
|
||||
Assert.AreEqual(0, testQueue.Count);
|
||||
testQueue.Enqueue(12.34f, "a");
|
||||
Assert.AreEqual(1, testQueue.Count);
|
||||
testQueue.Enqueue(56.78f, "b");
|
||||
Assert.AreEqual(2, testQueue.Count);
|
||||
testQueue.Dequeue();
|
||||
Assert.AreEqual(1, testQueue.Count);
|
||||
testQueue.Enqueue(9.0f, "c");
|
||||
Assert.AreEqual(2, testQueue.Count);
|
||||
testQueue.Clear();
|
||||
Assert.AreEqual(0, testQueue.Count);
|
||||
}
|
||||
|
||||
/// <summary>Tests to ensure that the priority collection actually sorts items</summary>
|
||||
[Test]
|
||||
public void TestOrdering() {
|
||||
PairPriorityQueue<float, string> testQueue = new PairPriorityQueue<float, string>();
|
||||
|
||||
testQueue.Enqueue(1.0f, "a");
|
||||
testQueue.Enqueue(9.0f, "i");
|
||||
testQueue.Enqueue(2.0f, "b");
|
||||
testQueue.Enqueue(8.0f, "h");
|
||||
testQueue.Enqueue(3.0f, "c");
|
||||
testQueue.Enqueue(7.0f, "g");
|
||||
testQueue.Enqueue(4.0f, "d");
|
||||
testQueue.Enqueue(6.0f, "f");
|
||||
testQueue.Enqueue(5.0f, "e");
|
||||
|
||||
Assert.AreEqual("i", testQueue.Dequeue().Item);
|
||||
Assert.AreEqual("h", testQueue.Dequeue().Item);
|
||||
Assert.AreEqual("g", testQueue.Dequeue().Item);
|
||||
Assert.AreEqual("f", testQueue.Dequeue().Item);
|
||||
Assert.AreEqual("e", testQueue.Dequeue().Item);
|
||||
Assert.AreEqual("d", testQueue.Dequeue().Item);
|
||||
Assert.AreEqual("c", testQueue.Dequeue().Item);
|
||||
Assert.AreEqual("b", testQueue.Dequeue().Item);
|
||||
Assert.AreEqual("a", testQueue.Dequeue().Item);
|
||||
}
|
||||
|
||||
/// <summary>Tests to ensure that the priority collection's Peek() method works</summary>
|
||||
[Test]
|
||||
public void TestPeek() {
|
||||
PairPriorityQueue<float, string> testQueue = new PairPriorityQueue<float, string>();
|
||||
|
||||
testQueue.Enqueue(1.0f, "a");
|
||||
testQueue.Enqueue(2.0f, "b");
|
||||
testQueue.Enqueue(0.0f, "c");
|
||||
|
||||
Assert.AreEqual("b", testQueue.Peek().Item);
|
||||
}
|
||||
|
||||
/// <summary>Tests whether the priority collection can copy itself into an array</summary>
|
||||
[Test]
|
||||
public void TestCopyTo() {
|
||||
PairPriorityQueue<float, string> testQueue = new PairPriorityQueue<float, string>();
|
||||
|
||||
testQueue.Enqueue(1.0f, "a");
|
||||
testQueue.Enqueue(9.0f, "i");
|
||||
testQueue.Enqueue(2.0f, "b");
|
||||
testQueue.Enqueue(8.0f, "h");
|
||||
testQueue.Enqueue(3.0f, "c");
|
||||
testQueue.Enqueue(7.0f, "g");
|
||||
testQueue.Enqueue(4.0f, "d");
|
||||
testQueue.Enqueue(6.0f, "f");
|
||||
testQueue.Enqueue(5.0f, "e");
|
||||
|
||||
PriorityItemPair<float, string>[] itemArray = new PriorityItemPair<float, string>[9];
|
||||
testQueue.CopyTo(itemArray, 0);
|
||||
|
||||
CollectionAssert.AreEquivalent(testQueue, itemArray);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the priority collection provides a synchronization root
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestSyncRoot() {
|
||||
PairPriorityQueue<int, int> testQueue = new PairPriorityQueue<int, int>();
|
||||
|
||||
// If IsSynchronized returns true, SyncRoot is allowed to be null
|
||||
if(!testQueue.IsSynchronized) {
|
||||
lock(testQueue.SyncRoot) {
|
||||
testQueue.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the priority collection provides a working type-safe enumerator
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestEnumerator() {
|
||||
PairPriorityQueue<float, string> testQueue = new PairPriorityQueue<float, string>();
|
||||
|
||||
testQueue.Enqueue(1.0f, "a");
|
||||
testQueue.Enqueue(2.0f, "b");
|
||||
testQueue.Enqueue(0.0f, "c");
|
||||
|
||||
List<PriorityItemPair<float, string>> testList =
|
||||
new List<PriorityItemPair<float,string>>();
|
||||
|
||||
foreach(PriorityItemPair<float, string> entry in testQueue) {
|
||||
testList.Add(entry);
|
||||
}
|
||||
|
||||
CollectionAssert.AreEquivalent(testQueue, testList);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Collections
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.Collections {
|
||||
|
||||
/// <summary>Unit Test for the Parentable class</summary>
|
||||
[TestFixture]
|
||||
internal class ParentableTest {
|
||||
|
||||
#region class TestParentable
|
||||
|
||||
/// <summary>Parentable object that can be the child of an int</summary>
|
||||
private class TestParentable : Parentable<int> {
|
||||
|
||||
/// <summary>Initializes a new instance of the parentable test class</summary>
|
||||
public TestParentable() { }
|
||||
|
||||
/// <summary>The parent object that owns this instance</summary>
|
||||
public int GetParent() {
|
||||
return base.Parent;
|
||||
}
|
||||
|
||||
/// <summary>Invoked whenever the instance's owner changes</summary>
|
||||
/// <remarks>
|
||||
/// When items are parented for the first time, the oldParent argument will
|
||||
/// be null. Also, if the element is removed from the collection, the
|
||||
/// current parent will be null.
|
||||
/// </remarks>
|
||||
/// <param name="oldParent">Previous owner of the instance</param>
|
||||
protected override void OnParentChanged(int oldParent) {
|
||||
this.parentChangedCalled = true;
|
||||
|
||||
base.OnParentChanged(oldParent); // to satisfy NCover :-/
|
||||
}
|
||||
|
||||
/// <summary>Whether the OnParentChanged method has been called</summary>
|
||||
public bool ParentChangedCalled {
|
||||
get { return this.parentChangedCalled; }
|
||||
}
|
||||
|
||||
/// <summary>Whether the OnParentChanged method has been called</summary>
|
||||
private bool parentChangedCalled;
|
||||
|
||||
}
|
||||
|
||||
#endregion // class TestParentable
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether a parent can be assigned and then retrieved from
|
||||
/// the parentable object
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestParentAssignment() {
|
||||
TestParentable testParentable = new TestParentable();
|
||||
|
||||
testParentable.SetParent(12345);
|
||||
Assert.AreEqual(12345, testParentable.GetParent());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether a parent can be assigned and then retrieved from
|
||||
/// the parentable object
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestParentChangedNotification() {
|
||||
TestParentable testParentable = new TestParentable();
|
||||
|
||||
testParentable.SetParent(12345);
|
||||
|
||||
Assert.IsTrue(testParentable.ParentChangedCalled);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Collections
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,189 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.Collections {
|
||||
|
||||
/// <summary>Unit Test for the Parenting Collection class</summary>
|
||||
[TestFixture]
|
||||
internal class ParentingCollectionTest {
|
||||
|
||||
#region class TestParentable
|
||||
|
||||
/// <summary>Parentable object that can be the child of an int</summary>
|
||||
private class TestParentable : Parentable<int>, IDisposable {
|
||||
|
||||
/// <summary>Initializes a new instance of the parentable test class</summary>
|
||||
public TestParentable() { }
|
||||
|
||||
/// <summary>The parent object that owns this instance</summary>
|
||||
public int GetParent() {
|
||||
return base.Parent;
|
||||
}
|
||||
|
||||
/// <summary>Immediately releases all resources owned by the item</summary>
|
||||
public void Dispose() {
|
||||
this.disposeCalled = true;
|
||||
}
|
||||
|
||||
/// <summary>Whether Dispose() has been called on this item</summary>
|
||||
public bool DisposeCalled {
|
||||
get { return this.disposeCalled; }
|
||||
}
|
||||
|
||||
/// <summary>Whether Dispose() has been called on this item</summary>
|
||||
private bool disposeCalled;
|
||||
|
||||
}
|
||||
|
||||
#endregion // class TestParentable
|
||||
|
||||
#region class TestParentingCollection
|
||||
|
||||
/// <summary>Parentable object that can be the child of an int</summary>
|
||||
private class TestParentingCollection : ParentingCollection<int, TestParentable> {
|
||||
|
||||
/// <summary>Changes the parent of the collection</summary>
|
||||
/// <param name="parent">New parent to assign to the collection</param>
|
||||
public void SetParent(int parent) {
|
||||
base.Reparent(parent);
|
||||
}
|
||||
|
||||
/// <summary>Disposes all items contained in the collection</summary>
|
||||
public new void DisposeItems() {
|
||||
base.DisposeItems();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endregion // class TestParentingCollection
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the parenting collection propagates its parent to an item that
|
||||
/// is added to the collection after the collection's aprent is already assigned
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestPropagatePreassignedParent() {
|
||||
TestParentingCollection testCollection = new TestParentingCollection();
|
||||
TestParentable testParentable = new TestParentable();
|
||||
|
||||
testCollection.SetParent(54321);
|
||||
testCollection.Add(testParentable);
|
||||
|
||||
Assert.AreEqual(54321, testParentable.GetParent());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the parenting collection propagates a new parent to all items
|
||||
/// contained in it when its parent is changed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestPropagateParentChange() {
|
||||
TestParentingCollection testCollection = new TestParentingCollection();
|
||||
TestParentable testParentable = new TestParentable();
|
||||
|
||||
testCollection.Add(testParentable);
|
||||
testCollection.SetParent(54321);
|
||||
|
||||
Assert.AreEqual(54321, testParentable.GetParent());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the parenting collection propagates its parent to an item that
|
||||
/// is added to the collection after the collection's aprent is already assigned
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestPropagateParentOnReplace() {
|
||||
TestParentingCollection testCollection = new TestParentingCollection();
|
||||
TestParentable testParentable1 = new TestParentable();
|
||||
TestParentable testParentable2 = new TestParentable();
|
||||
|
||||
testCollection.SetParent(54321);
|
||||
testCollection.Add(testParentable1);
|
||||
testCollection[0] = testParentable2;
|
||||
|
||||
Assert.AreEqual(0, testParentable1.GetParent());
|
||||
Assert.AreEqual(54321, testParentable2.GetParent());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the parenting collection unsets the parent when an item is removed
|
||||
/// from the collection
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestUnsetParentOnRemoveItem() {
|
||||
TestParentingCollection testCollection = new TestParentingCollection();
|
||||
TestParentable testParentable = new TestParentable();
|
||||
|
||||
testCollection.Add(testParentable);
|
||||
testCollection.SetParent(54321);
|
||||
|
||||
Assert.AreEqual(54321, testParentable.GetParent());
|
||||
|
||||
testCollection.RemoveAt(0);
|
||||
|
||||
Assert.AreEqual(0, testParentable.GetParent());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the parenting collection unsets the parent when all item are
|
||||
/// removed from the collection by clearing it
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestUnsetParentOnClear() {
|
||||
TestParentingCollection testCollection = new TestParentingCollection();
|
||||
TestParentable testParentable = new TestParentable();
|
||||
|
||||
testCollection.Add(testParentable);
|
||||
testCollection.SetParent(54321);
|
||||
|
||||
Assert.AreEqual(54321, testParentable.GetParent());
|
||||
|
||||
testCollection.Clear();
|
||||
|
||||
Assert.AreEqual(0, testParentable.GetParent());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the parenting collection calls Dispose() on all contained items
|
||||
/// that implement IDisposable when its DisposeItems() method is called
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestDisposeItems() {
|
||||
TestParentingCollection testCollection = new TestParentingCollection();
|
||||
TestParentable testParentable = new TestParentable();
|
||||
|
||||
testCollection.Add(testParentable);
|
||||
|
||||
testCollection.DisposeItems();
|
||||
|
||||
Assert.IsTrue(testParentable.DisposeCalled);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Collections
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,117 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.Collections {
|
||||
|
||||
/// <summary>Unit tests for the Pool class</summary>
|
||||
[TestFixture]
|
||||
internal class PoolTest {
|
||||
|
||||
#region class TestClass
|
||||
|
||||
/// <summary>Used to test the pool</summary>
|
||||
private class TestClass : IRecyclable {
|
||||
|
||||
/// <summary>Returns the object to its initial state</summary>
|
||||
public void Recycle() {
|
||||
this.Recycled = true;
|
||||
}
|
||||
|
||||
/// <summary>Whether the instance has been recycled</summary>
|
||||
public bool Recycled;
|
||||
|
||||
}
|
||||
|
||||
#endregion // class TestClass
|
||||
|
||||
#region class NoDefaultConstructor
|
||||
|
||||
/// <summary>Used to test the pool</summary>
|
||||
private class NoDefaultConstructor {
|
||||
|
||||
/// <summary>Private constructor so no instances can be created</summary>
|
||||
private NoDefaultConstructor() { }
|
||||
|
||||
}
|
||||
|
||||
#endregion // class NoDefaultConstructor
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the pool can return newly constructed objects
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void NewInstancesCanBeObtained() {
|
||||
Pool<TestClass> pool = new Pool<TestClass>();
|
||||
Assert.IsNotNull(pool.Get());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that an exception is thrown if the pool's default instance creator is used
|
||||
/// on a type that doesn't have a default constructor
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void UsingDefaultInstanceCreatorRequiresDefaultConstructor() {
|
||||
Assert.Throws<ArgumentException>(
|
||||
delegate() { new Pool<NoDefaultConstructor>(); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the pool can redeem objects that are no longer used
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void InstancesCanBeRedeemed() {
|
||||
Pool<TestClass> pool = new Pool<TestClass>();
|
||||
pool.Redeem(new TestClass());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the Recycle() method is called at the appropriate time
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void RedeemedItemsWillBeRecycled() {
|
||||
Pool<TestClass> pool = new Pool<TestClass>();
|
||||
TestClass x = new TestClass();
|
||||
|
||||
Assert.IsFalse(x.Recycled);
|
||||
pool.Redeem(x);
|
||||
Assert.IsTrue(x.Recycled);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the pool's Capacity is applied correctly</summary>
|
||||
[Test]
|
||||
public void PoolCapacityCanBeAdjusted() {
|
||||
Pool<TestClass> pool = new Pool<TestClass>(123);
|
||||
Assert.AreEqual(123, pool.Capacity);
|
||||
pool.Capacity = 321;
|
||||
Assert.AreEqual(321, pool.Capacity);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Collections
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.Collections {
|
||||
|
||||
/// <summary>Unit Test for the Priority/Item pair class</summary>
|
||||
[TestFixture]
|
||||
internal class PriorityItemPairTest {
|
||||
|
||||
#region class ToStringNullReturner
|
||||
|
||||
/// <summary>Test class in which ToString() can return null</summary>
|
||||
private class ToStringNullReturner {
|
||||
|
||||
/// <summary>
|
||||
/// Returns a System.String that represents the current System.Object
|
||||
/// </summary>
|
||||
/// <returns>A System.String that represents the current System.Object</returns>
|
||||
public override string ToString() { return null; }
|
||||
|
||||
}
|
||||
|
||||
#endregion // class ToStringNullReturner
|
||||
|
||||
/// <summary>Tests whether the pair's default constructor works</summary>
|
||||
[Test]
|
||||
public void TestDefaultConstructor() {
|
||||
new PriorityItemPair<int, string>();
|
||||
}
|
||||
|
||||
/// <summary>Tests whether the priority can be retrieved from the pair</summary>
|
||||
[Test]
|
||||
public void TestPriorityRetrieval() {
|
||||
PriorityItemPair<int, string> testPair = new PriorityItemPair<int, string>(
|
||||
12345, "hello world"
|
||||
);
|
||||
|
||||
Assert.AreEqual(12345, testPair.Priority);
|
||||
}
|
||||
|
||||
/// <summary>Tests whether the item can be retrieved from the pair</summary>
|
||||
[Test]
|
||||
public void TestItemRetrieval() {
|
||||
PriorityItemPair<int, string> testPair = new PriorityItemPair<int, string>(
|
||||
12345, "hello world"
|
||||
);
|
||||
|
||||
Assert.AreEqual("hello world", testPair.Item);
|
||||
}
|
||||
|
||||
/// <summary>Tests whether the ToString() methods works with valid strings</summary>
|
||||
[Test]
|
||||
public void TestToStringWithValidStrings() {
|
||||
PriorityItemPair<string, string> testPair = new PriorityItemPair<string, string>(
|
||||
"hello", "world"
|
||||
);
|
||||
|
||||
Assert.AreEqual("[hello, world]", testPair.ToString());
|
||||
}
|
||||
|
||||
/// <summary>Tests whether the ToString() methods works with null strings</summary>
|
||||
[Test]
|
||||
public void TestToStringWithNullStrings() {
|
||||
PriorityItemPair<ToStringNullReturner, ToStringNullReturner> testPair =
|
||||
new PriorityItemPair<ToStringNullReturner, ToStringNullReturner>(
|
||||
new ToStringNullReturner(), new ToStringNullReturner()
|
||||
);
|
||||
|
||||
Assert.AreEqual("[, ]", testPair.ToString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Collections
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,157 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.Collections {
|
||||
|
||||
/// <summary>Unit Test for the priority queue class</summary>
|
||||
[TestFixture]
|
||||
internal class PriorityQueueTest {
|
||||
|
||||
#region class FloatComparer
|
||||
|
||||
/// <summary>Comparer for two floating point values</summary>
|
||||
private class FloatComparer : IComparer<float> {
|
||||
|
||||
/// <summary>The default instance of this comparer</summary>
|
||||
public static readonly FloatComparer Default = new FloatComparer();
|
||||
|
||||
/// <summary>Compares two floating points against each other</summary>
|
||||
/// <param name="left">First float to compare</param>
|
||||
/// <param name="right">Second float to compare</param>
|
||||
/// <returns>The relationship of the two floats to each other</returns>
|
||||
public int Compare(float left, float right) {
|
||||
return Math.Sign(left - right);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endregion // class FloatComparer
|
||||
|
||||
/// <summary>Tests to ensure the count property is properly updated</summary>
|
||||
[Test]
|
||||
public void TestCount() {
|
||||
PriorityQueue<float> testQueue = new PriorityQueue<float>(FloatComparer.Default);
|
||||
|
||||
Assert.AreEqual(0, testQueue.Count);
|
||||
testQueue.Enqueue(12.34f);
|
||||
Assert.AreEqual(1, testQueue.Count);
|
||||
testQueue.Enqueue(56.78f);
|
||||
Assert.AreEqual(2, testQueue.Count);
|
||||
testQueue.Dequeue();
|
||||
Assert.AreEqual(1, testQueue.Count);
|
||||
testQueue.Enqueue(9.0f);
|
||||
Assert.AreEqual(2, testQueue.Count);
|
||||
testQueue.Clear();
|
||||
Assert.AreEqual(0, testQueue.Count);
|
||||
}
|
||||
|
||||
/// <summary>Tests to ensure that the priority collection actually sorts items</summary>
|
||||
[Test]
|
||||
public void TestOrdering() {
|
||||
PriorityQueue<float> testQueue = new PriorityQueue<float>(FloatComparer.Default);
|
||||
|
||||
testQueue.Enqueue(1.0f);
|
||||
testQueue.Enqueue(9.0f);
|
||||
testQueue.Enqueue(2.0f);
|
||||
testQueue.Enqueue(8.0f);
|
||||
testQueue.Enqueue(3.0f);
|
||||
testQueue.Enqueue(7.0f);
|
||||
testQueue.Enqueue(4.0f);
|
||||
testQueue.Enqueue(6.0f);
|
||||
testQueue.Enqueue(5.0f);
|
||||
|
||||
Assert.AreEqual(9.0f, testQueue.Dequeue());
|
||||
Assert.AreEqual(8.0f, testQueue.Dequeue());
|
||||
Assert.AreEqual(7.0f, testQueue.Dequeue());
|
||||
Assert.AreEqual(6.0f, testQueue.Dequeue());
|
||||
Assert.AreEqual(5.0f, testQueue.Dequeue());
|
||||
Assert.AreEqual(4.0f, testQueue.Dequeue());
|
||||
Assert.AreEqual(3.0f, testQueue.Dequeue());
|
||||
Assert.AreEqual(2.0f, testQueue.Dequeue());
|
||||
Assert.AreEqual(1.0f, testQueue.Dequeue());
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
/// <summary>
|
||||
/// Tests whether the priority queue's enumerators are invalidated when the queue's
|
||||
/// contents are modified
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestEnumeratorInvalidationOnModify() {
|
||||
PriorityQueue<int> testQueue = new PriorityQueue<int>();
|
||||
IEnumerator<int> testQueueEnumerator = testQueue.GetEnumerator();
|
||||
|
||||
testQueue.Enqueue(123);
|
||||
|
||||
Assert.Throws<InvalidOperationException>(
|
||||
delegate() { testQueueEnumerator.MoveNext(); }
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that an exception is thrown when Peek() is called on an empty queue
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestPeekEmptyQueue() {
|
||||
PriorityQueue<int> testQueue = new PriorityQueue<int>();
|
||||
Assert.Throws<InvalidOperationException>(
|
||||
delegate() { testQueue.Peek(); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that an exception is thrown when Dequeue() is called on an empty queue
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestDequeueEmptyQueue() {
|
||||
PriorityQueue<int> testQueue = new PriorityQueue<int>();
|
||||
Assert.Throws<InvalidOperationException>(
|
||||
delegate() { testQueue.Dequeue(); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the priority queue can handle large amounts of data
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestLargeQueue() {
|
||||
PriorityQueue<int> testQueue = new PriorityQueue<int>();
|
||||
List<int> testList = new List<int>();
|
||||
|
||||
for(int index = 0; index < 1000; ++index) {
|
||||
testQueue.Enqueue(index * 2);
|
||||
testList.Add(index * 2);
|
||||
}
|
||||
|
||||
CollectionAssert.AreEquivalent(testList, testQueue);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Collections
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,162 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.Collections {
|
||||
|
||||
/// <summary>Unit Test for the read only collection wrapper</summary>
|
||||
[TestFixture]
|
||||
internal class ReadOnlyCollectionTest {
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the copy constructor of the read only collection works
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCopyConstructor() {
|
||||
int[] integers = new int[] { 12, 34, 56, 78 };
|
||||
ReadOnlyCollection<int> testCollection = new ReadOnlyCollection<int>(integers);
|
||||
|
||||
CollectionAssert.AreEqual(integers, testCollection);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the IsReadOnly property returns true</summary>
|
||||
[Test]
|
||||
public void TestIsReadOnly() {
|
||||
ReadOnlyCollection<int> testCollection = new ReadOnlyCollection<int>(new int[0]);
|
||||
|
||||
Assert.IsTrue(testCollection.IsReadOnly);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the CopyTo() of the read only collection works
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCopyToArray() {
|
||||
int[] inputIntegers = new int[] { 12, 34, 56, 78 };
|
||||
ReadOnlyCollection<int> testCollection = new ReadOnlyCollection<int>(inputIntegers);
|
||||
|
||||
int[] outputIntegers = new int[testCollection.Count];
|
||||
testCollection.CopyTo(outputIntegers, 0);
|
||||
|
||||
CollectionAssert.AreEqual(inputIntegers, outputIntegers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the Contains() method of the read only collection is able to
|
||||
/// determine if the collection contains an item
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestContains() {
|
||||
int[] integers = new int[] { 1234, 6789 };
|
||||
ReadOnlyCollection<int> testCollection = new ReadOnlyCollection<int>(integers);
|
||||
|
||||
Assert.IsTrue(testCollection.Contains(1234));
|
||||
Assert.IsFalse(testCollection.Contains(4321));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the Add() method of the read only collection throws an exception
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnAdd() {
|
||||
ReadOnlyCollection<int> testCollection = new ReadOnlyCollection<int>(new int[0]);
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { (testCollection as ICollection<int>).Add(123); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the Remove() method of the read only collection throws an exception
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnRemove() {
|
||||
ReadOnlyCollection<int> testCollection = new ReadOnlyCollection<int>(new int[0]);
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { (testCollection as ICollection<int>).Remove(123); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the Clear() method of the read only collection throws an exception
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnClear() {
|
||||
ReadOnlyCollection<int> testCollection = new ReadOnlyCollection<int>(new int[0]);
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { (testCollection as ICollection<int>).Clear(); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the typesafe enumerator of the read only collection is working
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestTypesafeEnumerator() {
|
||||
int[] inputIntegers = new int[] { 12, 34, 56, 78 };
|
||||
ReadOnlyCollection<int> testCollection = new ReadOnlyCollection<int>(inputIntegers);
|
||||
|
||||
List<int> outputIntegers = new List<int>();
|
||||
foreach(int value in testCollection) {
|
||||
outputIntegers.Add(value);
|
||||
}
|
||||
|
||||
CollectionAssert.AreEqual(inputIntegers, outputIntegers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the CopyTo() of the read only collection works if invoked via
|
||||
/// the ICollection interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCopyToArrayViaICollection() {
|
||||
int[] inputIntegers = new int[] { 12, 34, 56, 78 };
|
||||
ReadOnlyCollection<int> testCollection = new ReadOnlyCollection<int>(inputIntegers);
|
||||
|
||||
int[] outputIntegers = new int[testCollection.Count];
|
||||
(testCollection as ICollection).CopyTo(outputIntegers, 0);
|
||||
|
||||
CollectionAssert.AreEqual(inputIntegers, outputIntegers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the IsSynchronized property and the SyncRoot property are working
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestSynchronization() {
|
||||
ReadOnlyCollection<int> testCollection = new ReadOnlyCollection<int>(new int[0]);
|
||||
|
||||
if(!(testCollection as ICollection).IsSynchronized) {
|
||||
lock((testCollection as ICollection).SyncRoot) {
|
||||
Assert.AreEqual(0, testCollection.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Collections
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,509 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.Collections {
|
||||
|
||||
/// <summary>Unit Test for the read only dictionary wrapper</summary>
|
||||
[TestFixture]
|
||||
internal class ReadOnlyDictionaryTest {
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the copy constructor of the read only dictionary works
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCopyConstructor() {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
|
||||
|
||||
CollectionAssert.AreEqual(numbers, testDictionary);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the IsReadOnly property returns true</summary>
|
||||
[Test]
|
||||
public void TestIsReadOnly() {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
|
||||
|
||||
Assert.IsTrue(testDictionary.IsReadOnly);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the Contains() method of the read only dictionary is able to
|
||||
/// determine if the dictionary contains an item
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestContains() {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
|
||||
|
||||
Assert.IsTrue(
|
||||
testDictionary.Contains(new KeyValuePair<int, string>(42, "forty-two"))
|
||||
);
|
||||
Assert.IsFalse(
|
||||
testDictionary.Contains(new KeyValuePair<int, string>(24, "twenty-four"))
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the Contains() method of the read only dictionary is able to
|
||||
/// determine if the dictionary contains a key
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestContainsKey() {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
|
||||
|
||||
Assert.IsTrue(testDictionary.ContainsKey(42));
|
||||
Assert.IsFalse(testDictionary.ContainsKey(24));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the CopyTo() of the read only dictionary works
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCopyToArray() {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
|
||||
|
||||
KeyValuePair<int, string>[] items = new KeyValuePair<int, string>[numbers.Count];
|
||||
|
||||
testDictionary.CopyTo(items, 0);
|
||||
|
||||
CollectionAssert.AreEqual(numbers, items);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the typesafe enumerator of the read only dictionary is working
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestTypesafeEnumerator() {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
|
||||
|
||||
List<KeyValuePair<int, string>> outputItems = new List<KeyValuePair<int, string>>();
|
||||
foreach(KeyValuePair<int, string> item in testDictionary) {
|
||||
outputItems.Add(item);
|
||||
}
|
||||
|
||||
CollectionAssert.AreEqual(numbers, outputItems);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the keys collection of the read only dictionary can be queried
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestGetKeysCollection() {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
|
||||
|
||||
ICollection<int> inputKeys = numbers.Keys;
|
||||
ICollection<int> keys = testDictionary.Keys;
|
||||
CollectionAssert.AreEqual(inputKeys, keys);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the values collection of the read only dictionary can be queried
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestGetValuesCollection() {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
|
||||
|
||||
ICollection<string> inputValues = numbers.Values;
|
||||
ICollection<string> values = testDictionary.Values;
|
||||
CollectionAssert.AreEqual(inputValues, values);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the TryGetValue() method of the read only dictionary is working
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestTryGetValue() {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
|
||||
|
||||
string value;
|
||||
|
||||
Assert.IsTrue(testDictionary.TryGetValue(42, out value));
|
||||
Assert.AreEqual("forty-two", value);
|
||||
|
||||
Assert.IsFalse(testDictionary.TryGetValue(24, out value));
|
||||
Assert.AreEqual(null, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the retrieval of values using the indexer of the read only
|
||||
/// dictionary is working
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestRetrieveValueByIndexer() {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
|
||||
|
||||
Assert.AreEqual("forty-two", testDictionary[42]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether an exception is thrown if the indexer of the read only dictionary
|
||||
/// is used to attempt to retrieve a non-existing value
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnRetrieveNonExistingValueByIndexer() {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
|
||||
|
||||
Assert.Throws<KeyNotFoundException>(
|
||||
delegate() { Console.WriteLine(testDictionary[24]); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the read only dictionary will throw an exception if its
|
||||
/// Add() method is called via the generic IDictionary<> interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnAddViaGenericIDictionary() {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
|
||||
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { (testDictionary as IDictionary<int, string>).Add(10, "ten"); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the read only dictionary will throw an exception if its
|
||||
/// Remove() method is called via the generic IDictionary<> interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnRemoveViaGenericIDictionary() {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
|
||||
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { (testDictionary as IDictionary<int, string>).Remove(3); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the TryGetValue() method of the read only dictionary is working
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestRetrieveValueByIndexerViaGenericIDictionary() {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
|
||||
|
||||
Assert.AreEqual("forty-two", (testDictionary as IDictionary<int, string>)[42]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the read only dictionary will throw an exception if its
|
||||
/// indexer is used to insert an item via the generic IDictionar<> interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnReplaceByIndexerViaGenericIDictionary() {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
|
||||
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { (testDictionary as IDictionary<int, string>)[24] = "twenty-four"; }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the read only dictionary will throw an exception if its
|
||||
/// Clear() method is called via the IDictionary interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnClearViaIDictionary() {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
|
||||
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { (testDictionary as IDictionary).Clear(); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the read only dictionary will throw an exception if its
|
||||
/// Add() method is called via the IDictionary interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnAddViaIDictionary() {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
|
||||
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { (testDictionary as IDictionary).Add(24, "twenty-four"); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the Contains() method of the read only dictionary is able to
|
||||
/// determine if the dictionary contains an item via the IDictionary interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestContainsViaIDictionary() {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
|
||||
|
||||
Assert.IsTrue((testDictionary as IDictionary).Contains(42));
|
||||
Assert.IsFalse((testDictionary as IDictionary).Contains(24));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the GetEnumerator() method of the read only dictionary returns
|
||||
/// a working enumerator if accessed via the IDictionary interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestEnumeratorViaIDictionary() {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
|
||||
|
||||
Dictionary<int, string> outputNumbers = new Dictionary<int, string>();
|
||||
foreach(DictionaryEntry entry in (testDictionary as IDictionary)) {
|
||||
(outputNumbers as IDictionary).Add(entry.Key, entry.Value);
|
||||
}
|
||||
|
||||
CollectionAssert.AreEquivalent(numbers, outputNumbers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the IsFixedSize property of the read only dictionary returns
|
||||
/// the expected result for a read only dictionary based on a dynamic dictionary
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestIsFixedSizeViaIList() {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
|
||||
|
||||
Assert.IsFalse((testDictionary as IDictionary).IsFixedSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the keys collection of the read only dictionary can be queried
|
||||
/// via the IDictionary interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestGetKeysCollectionViaIDictionary() {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
|
||||
|
||||
ICollection inputKeys = (numbers as IDictionary).Keys;
|
||||
ICollection keys = (testDictionary as IDictionary).Keys;
|
||||
CollectionAssert.AreEqual(inputKeys, keys);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the values collection of the read only dictionary can be queried
|
||||
/// via the IDictionary interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestGetValuesCollectionViaIDictionary() {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
|
||||
|
||||
ICollection inputValues = (numbers as IDictionary).Values;
|
||||
ICollection values = (testDictionary as IDictionary).Values;
|
||||
CollectionAssert.AreEqual(inputValues, values);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the read only dictionary will throw an exception if its
|
||||
/// Remove() method is called via the IDictionary interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnRemoveViaIDictionary() {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
|
||||
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { (testDictionary as IDictionary).Remove(3); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the retrieval of values using the indexer of the read only
|
||||
/// dictionary is working via the IDictionary interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestRetrieveValueByIndexerViaIDictionary() {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
|
||||
|
||||
Assert.AreEqual("forty-two", (testDictionary as IDictionary)[42]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the read only dictionary will throw an exception if its
|
||||
/// indexer is used to insert an item via the IDictionary interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnReplaceByIndexerViaIDictionary() {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
|
||||
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { (testDictionary as IDictionary)[24] = "twenty-four"; }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the read only dictionary will throw an exception if its
|
||||
/// Add() method is used via the generic ICollection<> interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnAddViaGenericICollection() {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
|
||||
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() {
|
||||
(testDictionary as ICollection<KeyValuePair<int, string>>).Add(
|
||||
new KeyValuePair<int, string>(24, "twenty-four")
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the read only dictionary will throw an exception if its
|
||||
/// Clear() method is used via the generic ICollection<> interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnClearViaGenericICollection() {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
|
||||
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { (testDictionary as ICollection<KeyValuePair<int, string>>).Clear(); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the read only dictionary will throw an exception if its
|
||||
/// Remove() method is used via the generic ICollection<> interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnRemoveViaGenericICollection() {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
|
||||
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() {
|
||||
(testDictionary as ICollection<KeyValuePair<int, string>>).Remove(
|
||||
new KeyValuePair<int, string>(42, "fourty-two")
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the CopyTo() of the read only dictionary works when called
|
||||
/// via the the ICollection interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCopyToArrayViaICollection() {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
|
||||
|
||||
DictionaryEntry[] entries = new DictionaryEntry[numbers.Count];
|
||||
(testDictionary as ICollection).CopyTo(entries, 0);
|
||||
|
||||
KeyValuePair<int, string>[] items = new KeyValuePair<int, string>[numbers.Count];
|
||||
for(int index = 0; index < entries.Length; ++index) {
|
||||
items[index] = new KeyValuePair<int, string>(
|
||||
(int)entries[index].Key, (string)entries[index].Value
|
||||
);
|
||||
}
|
||||
CollectionAssert.AreEquivalent(numbers, items);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the IsSynchronized property and the SyncRoot property are working
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestSynchronization() {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ReadOnlyDictionary<int, string> testDictionary = makeReadOnly(numbers);
|
||||
|
||||
if(!(testDictionary as ICollection).IsSynchronized) {
|
||||
lock((testDictionary as ICollection).SyncRoot) {
|
||||
Assert.AreEqual(numbers.Count, testDictionary.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test whether the read only dictionary can be serialized
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestSerialization() {
|
||||
BinaryFormatter formatter = new BinaryFormatter();
|
||||
|
||||
using(MemoryStream memory = new MemoryStream()) {
|
||||
Dictionary<int, string> numbers = createTestDictionary();
|
||||
ReadOnlyDictionary<int, string> testDictionary1 = makeReadOnly(numbers);
|
||||
|
||||
formatter.Serialize(memory, testDictionary1);
|
||||
memory.Position = 0;
|
||||
object testDictionary2 = formatter.Deserialize(memory);
|
||||
|
||||
CollectionAssert.AreEquivalent(testDictionary1, (IEnumerable)testDictionary2);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new read-only dictionary filled with some values for testing
|
||||
/// </summary>
|
||||
/// <returns>The newly created read-only dictionary</returns>
|
||||
private static Dictionary<int, string> createTestDictionary() {
|
||||
Dictionary<int, string> numbers = new Dictionary<int, string>();
|
||||
numbers.Add(1, "one");
|
||||
numbers.Add(2, "two");
|
||||
numbers.Add(3, "three");
|
||||
numbers.Add(42, "forty-two");
|
||||
return new Dictionary<int, string>(numbers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new read-only dictionary filled with some values for testing
|
||||
/// </summary>
|
||||
/// <returns>The newly created read-only dictionary</returns>
|
||||
private static ReadOnlyDictionary<int, string> makeReadOnly(
|
||||
IDictionary<int, string> dictionary
|
||||
) {
|
||||
return new ReadOnlyDictionary<int, string>(dictionary);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Collections
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,380 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.Collections {
|
||||
|
||||
/// <summary>Unit Test for the read only list wrapper</summary>
|
||||
[TestFixture]
|
||||
internal class ReadOnlyListTest {
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the copy constructor of the read only list works
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCopyConstructor() {
|
||||
int[] integers = new int[] { 12, 34, 56, 78 };
|
||||
ReadOnlyList<int> testList = new ReadOnlyList<int>(integers);
|
||||
|
||||
CollectionAssert.AreEqual(integers, testList);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the IsReadOnly property returns true</summary>
|
||||
[Test]
|
||||
public void TestIsReadOnly() {
|
||||
ReadOnlyList<int> testList = new ReadOnlyList<int>(new int[0]);
|
||||
|
||||
Assert.IsTrue(testList.IsReadOnly);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the CopyTo() of the read only list works
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCopyToArray() {
|
||||
int[] inputIntegers = new int[] { 12, 34, 56, 78 };
|
||||
ReadOnlyList<int> testList = new ReadOnlyList<int>(inputIntegers);
|
||||
|
||||
int[] outputIntegers = new int[testList.Count];
|
||||
testList.CopyTo(outputIntegers, 0);
|
||||
|
||||
CollectionAssert.AreEqual(inputIntegers, outputIntegers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the Contains() method of the read only list is able to
|
||||
/// determine if the list contains an item
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestContains() {
|
||||
int[] integers = new int[] { 1234, 6789 };
|
||||
ReadOnlyList<int> testList = new ReadOnlyList<int>(integers);
|
||||
|
||||
Assert.IsTrue(testList.Contains(1234));
|
||||
Assert.IsFalse(testList.Contains(4321));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the IndexOf() method of the read only list is able to
|
||||
/// determine if the index of an item in the list
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestIndexOf() {
|
||||
int[] integers = new int[] { 12, 34, 67, 89 };
|
||||
ReadOnlyList<int> testList = new ReadOnlyList<int>(integers);
|
||||
|
||||
Assert.AreEqual(0, testList.IndexOf(12));
|
||||
Assert.AreEqual(1, testList.IndexOf(34));
|
||||
Assert.AreEqual(2, testList.IndexOf(67));
|
||||
Assert.AreEqual(3, testList.IndexOf(89));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the indexer method of the read only list is able to
|
||||
/// retrieve items from the list
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestRetrieveByIndexer() {
|
||||
int[] integers = new int[] { 12, 34, 67, 89 };
|
||||
ReadOnlyList<int> testList = new ReadOnlyList<int>(integers);
|
||||
|
||||
Assert.AreEqual(12, testList[0]);
|
||||
Assert.AreEqual(34, testList[1]);
|
||||
Assert.AreEqual(67, testList[2]);
|
||||
Assert.AreEqual(89, testList[3]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the read only list will throw an exception if its Insert() method
|
||||
/// is called via the generic IList<> interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnInsertViaGenericIList() {
|
||||
ReadOnlyList<int> testList = new ReadOnlyList<int>(new int[0]);
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { (testList as IList<int>).Insert(0, 12345); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the read only list will throw an exception if its RemoveAt() method
|
||||
/// is called via the generic IList<> interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnRemoveViaGenericIList() {
|
||||
ReadOnlyList<int> testList = new ReadOnlyList<int>(new int[1]);
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { (testList as IList<int>).RemoveAt(0); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the indexer method of the read only list will throw an exception
|
||||
/// if it is attempted to be used for replacing an item
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestRetrieveByIndexerViaGenericIList() {
|
||||
int[] integers = new int[] { 12, 34, 67, 89 };
|
||||
ReadOnlyList<int> testList = new ReadOnlyList<int>(integers);
|
||||
|
||||
Assert.AreEqual(12, (testList as IList<int>)[0]);
|
||||
Assert.AreEqual(34, (testList as IList<int>)[1]);
|
||||
Assert.AreEqual(67, (testList as IList<int>)[2]);
|
||||
Assert.AreEqual(89, (testList as IList<int>)[3]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the indexer method of the read only list will throw an exception
|
||||
/// if it is attempted to be used for replacing an item
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnReplaceByIndexerViaGenericIList() {
|
||||
ReadOnlyList<int> testList = new ReadOnlyList<int>(new int[1]);
|
||||
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { (testList as IList<int>)[0] = 12345; }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the read only list will throw an exception if its Add() method
|
||||
/// is called via the generic ICollection<> interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnAddViaGenericICollection() {
|
||||
ReadOnlyList<int> testList = new ReadOnlyList<int>(new int[0]);
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { (testList as ICollection<int>).Add(12345); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the read only list will throw an exception if its Clear() method
|
||||
/// is called via the generic ICollection<> interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnClearViaGenericICollection() {
|
||||
ReadOnlyList<int> testList = new ReadOnlyList<int>(new int[1]);
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { (testList as ICollection<int>).Clear(); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the read only list will throw an exception if its Remove() method
|
||||
/// is called via the generic ICollection<> interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnRemoveViaGenericICollection() {
|
||||
int[] integers = new int[] { 12, 34, 67, 89 };
|
||||
ReadOnlyList<int> testList = new ReadOnlyList<int>(integers);
|
||||
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { (testList as ICollection<int>).Remove(89); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the typesafe enumerator of the read only list is working
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestTypesafeEnumerator() {
|
||||
int[] inputIntegers = new int[] { 12, 34, 56, 78 };
|
||||
ReadOnlyList<int> testList = new ReadOnlyList<int>(inputIntegers);
|
||||
|
||||
List<int> outputIntegers = new List<int>();
|
||||
foreach(int value in testList) {
|
||||
outputIntegers.Add(value);
|
||||
}
|
||||
|
||||
CollectionAssert.AreEqual(inputIntegers, outputIntegers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the read only list will throw an exception if its Clear() method
|
||||
/// is called via the IList interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnClearViaIList() {
|
||||
ReadOnlyList<int> testList = new ReadOnlyList<int>(new int[1]);
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { (testList as IList).Clear(); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the read only list will throw an exception if its Add() method
|
||||
/// is called via the IList interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnAddViaIList() {
|
||||
ReadOnlyList<int> testList = new ReadOnlyList<int>(new int[0]);
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { (testList as IList).Add(12345); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the Contains() method of the read only list is able to
|
||||
/// determine if the list contains an item
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestContainsViaIList() {
|
||||
int[] integers = new int[] { 1234, 6789 };
|
||||
ReadOnlyList<int> testList = new ReadOnlyList<int>(integers);
|
||||
|
||||
Assert.IsTrue((testList as IList).Contains(1234));
|
||||
Assert.IsFalse((testList as IList).Contains(4321));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the IndexOf() method of the read only list is able to
|
||||
/// determine if the index of an item in the list
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestIndexOfViaIList() {
|
||||
int[] integers = new int[] { 12, 34, 67, 89 };
|
||||
ReadOnlyList<int> testList = new ReadOnlyList<int>(integers);
|
||||
|
||||
Assert.AreEqual(0, (testList as IList).IndexOf(12));
|
||||
Assert.AreEqual(1, (testList as IList).IndexOf(34));
|
||||
Assert.AreEqual(2, (testList as IList).IndexOf(67));
|
||||
Assert.AreEqual(3, (testList as IList).IndexOf(89));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the read only list will throw an exception if its Insert() method
|
||||
/// is called via the IList interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnInsertViaIList() {
|
||||
ReadOnlyList<int> testList = new ReadOnlyList<int>(new int[0]);
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { (testList as IList).Insert(0, 12345); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the IsFixedSize property of the read only list returns the
|
||||
/// expected result for a read only list based on a fixed array
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestIsFixedSizeViaIList() {
|
||||
int[] integers = new int[] { 12, 34, 67, 89 };
|
||||
ReadOnlyList<int> testList = new ReadOnlyList<int>(integers);
|
||||
|
||||
Assert.IsTrue((testList as IList).IsFixedSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the read only list will throw an exception if its Remove() method
|
||||
/// is called via the IList interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnRemoveViaIList() {
|
||||
int[] integers = new int[] { 1234, 6789 };
|
||||
ReadOnlyList<int> testList = new ReadOnlyList<int>(integers);
|
||||
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { (testList as IList).Remove(6789); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the read only list will throw an exception if its Remove() method
|
||||
/// is called via the IList interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnRemoveAtViaIList() {
|
||||
ReadOnlyList<int> testList = new ReadOnlyList<int>(new int[1]);
|
||||
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { (testList as IList).RemoveAt(0); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the indexer method of the read only list will throw an exception
|
||||
/// if it is attempted to be used for replacing an item
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestRetrieveByIndexerViaIList() {
|
||||
int[] integers = new int[] { 12, 34, 67, 89 };
|
||||
ReadOnlyList<int> testList = new ReadOnlyList<int>(integers);
|
||||
|
||||
Assert.AreEqual(12, (testList as IList)[0]);
|
||||
Assert.AreEqual(34, (testList as IList)[1]);
|
||||
Assert.AreEqual(67, (testList as IList)[2]);
|
||||
Assert.AreEqual(89, (testList as IList)[3]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the indexer method of the read only list will throw an exception
|
||||
/// if it is attempted to be used for replacing an item
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnReplaceByIndexerViaIList() {
|
||||
ReadOnlyList<int> testList = new ReadOnlyList<int>(new int[1]);
|
||||
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { (testList as IList)[0] = 12345; }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the CopyTo() of the read only list works if invoked via
|
||||
/// the ICollection interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCopyToArrayViaICollection() {
|
||||
int[] inputIntegers = new int[] { 12, 34, 56, 78 };
|
||||
ReadOnlyList<int> testList = new ReadOnlyList<int>(inputIntegers);
|
||||
|
||||
int[] outputIntegers = new int[testList.Count];
|
||||
(testList as ICollection).CopyTo(outputIntegers, 0);
|
||||
|
||||
CollectionAssert.AreEqual(inputIntegers, outputIntegers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the IsSynchronized property and the SyncRoot property are working
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestSynchronization() {
|
||||
ReadOnlyList<int> testList = new ReadOnlyList<int>(new int[0]);
|
||||
|
||||
if(!(testList as ICollection).IsSynchronized) {
|
||||
lock((testList as ICollection).SyncRoot) {
|
||||
Assert.AreEqual(0, testList.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Collections
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,207 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if !NO_SETS
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
|
||||
using NUnit.Framework;
|
||||
using NMock;
|
||||
|
||||
namespace Nuclex.Support.Collections {
|
||||
|
||||
/// <summary>Unit Test for the observable set wrapper</summary>
|
||||
[TestFixture]
|
||||
internal class ReadOnlySetTest {
|
||||
|
||||
/// <summary>Called before each test is run</summary>
|
||||
[SetUp]
|
||||
public void Setup() {
|
||||
this.set = new HashSet<int>();
|
||||
this.readOnlySet = new ReadOnlySet<int>(this.set);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the observable set has a default constructor
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void HasDefaultConstructor() {
|
||||
Assert.IsNotNull(new ReadOnlySet<int>(new HashSet<int>()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that an exception is thrown upon any attempt to add items
|
||||
/// to a read-only set
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void AddingThrowsException() {
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { ((ISet<int>)this.readOnlySet).Add(123); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that an exception is thrown upon any attempt to remove items
|
||||
/// from a read-only set
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void RemovingThrowsException() {
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { ((ISet<int>)this.readOnlySet).Remove(123); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that an exception is thrown upon any attempt to except
|
||||
/// the set with another set
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ExceptingThrowsException() {
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { ((ISet<int>)this.readOnlySet).ExceptWith(null); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that an exception is thrown upon any attempt to intersect
|
||||
/// the set with another set
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void InsersectThrowsException() {
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { ((ISet<int>)this.readOnlySet).IntersectWith(null); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that it's possible to determine whether a set is a proper subset
|
||||
/// or superset of another set
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CanDetermineProperSubsetAndSuperset() {
|
||||
this.set.Add(1);
|
||||
this.set.Add(2);
|
||||
this.set.Add(3);
|
||||
|
||||
var set2 = new HashSet<int>() { 1, 3 };
|
||||
|
||||
Assert.IsTrue(this.readOnlySet.IsProperSupersetOf(set2));
|
||||
Assert.IsTrue(set2.IsProperSubsetOf(this.readOnlySet));
|
||||
|
||||
set2.Add(2);
|
||||
|
||||
Assert.IsFalse(this.readOnlySet.IsProperSupersetOf(set2));
|
||||
Assert.IsFalse(set2.IsProperSubsetOf(this.readOnlySet));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that it's possible to determine whether a set is a subset
|
||||
/// or a superset of another set
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CanDetermineSubsetAndSuperset() {
|
||||
this.set.Add(1);
|
||||
this.set.Add(2);
|
||||
this.set.Add(3);
|
||||
|
||||
var set2 = new HashSet<int>() { 1, 2, 3 };
|
||||
|
||||
Assert.IsTrue(this.readOnlySet.IsSupersetOf(set2));
|
||||
Assert.IsTrue(set2.IsSubsetOf(this.readOnlySet));
|
||||
|
||||
set2.Add(4);
|
||||
|
||||
Assert.IsFalse(this.readOnlySet.IsSupersetOf(set2));
|
||||
Assert.IsFalse(set2.IsSubsetOf(this.readOnlySet));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a set can determine if another set overlaps with it
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CanDetermineOverlap() {
|
||||
this.set.Add(1);
|
||||
this.set.Add(3);
|
||||
this.set.Add(5);
|
||||
|
||||
var set2 = new HashSet<int>() { 3 };
|
||||
|
||||
Assert.IsTrue(this.readOnlySet.Overlaps(set2));
|
||||
Assert.IsTrue(set2.Overlaps(this.readOnlySet));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a set can determine if another set contains the same elements
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CanDetermineSetEquality() {
|
||||
this.set.Add(1);
|
||||
this.set.Add(3);
|
||||
this.set.Add(5);
|
||||
|
||||
var set2 = new HashSet<int>() { 3, 1, 5 };
|
||||
|
||||
Assert.IsTrue(this.readOnlySet.SetEquals(set2));
|
||||
Assert.IsTrue(set2.SetEquals(this.readOnlySet));
|
||||
|
||||
this.set.Add(7);
|
||||
|
||||
Assert.IsFalse(this.readOnlySet.SetEquals(set2));
|
||||
Assert.IsFalse(set2.SetEquals(this.readOnlySet));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that any attempt to symmetrically except a read-only set
|
||||
/// causes an exception
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SymmetricallyExceptingThrowsException() {
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { ((ISet<int>)this.readOnlySet).SymmetricExceptWith(null); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that any attempt to union a read-only set causes an exception
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void UnioningThrowsException() {
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { ((ISet<int>)this.readOnlySet).UnionWith(null); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Set being wrapped in a read-only set</summary>
|
||||
private ISet<int> set;
|
||||
/// <summary>Read-only wrapper around the set</summary>
|
||||
private ReadOnlySet<int> readOnlySet;
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Collections
|
||||
|
||||
#endif // UNITTEST
|
||||
|
||||
#endif // !NO_SETS
|
||||
|
|
@ -1,116 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.Collections {
|
||||
|
||||
/// <summary>Unit Test for the ReverseComparer helper class</summary>
|
||||
[TestFixture]
|
||||
internal class ReverseComparerTest {
|
||||
|
||||
#region class FortyTwoComparer
|
||||
|
||||
/// <summary>Special comparer in which 42 is larger than everything</summary>
|
||||
private class FortyTwoComparer : IComparer<int> {
|
||||
|
||||
/// <summary>Compares the left value to the right value</summary>
|
||||
/// <param name="left">Value on the left side</param>
|
||||
/// <param name="right">Value on the right side</param>
|
||||
/// <returns>The relationship of the two values</returns>
|
||||
public int Compare(int left, int right) {
|
||||
|
||||
// Is there a 42 in the arguments?
|
||||
if(left == 42) {
|
||||
if(right == 42) {
|
||||
return 0; // both are equal
|
||||
} else {
|
||||
return +1; // left is larger
|
||||
}
|
||||
} else if(right == 42) {
|
||||
return -1; // right is larger
|
||||
}
|
||||
|
||||
// No 42 encountered, proceed as normal
|
||||
return Math.Sign(left - right);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endregion // class FortyTwoComparer
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the default constructor of the reverse comparer works
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestDefaultConstructor() {
|
||||
new ReverseComparer<int>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the full constructor of the reverse comparer works
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestFullConstructor() {
|
||||
new ReverseComparer<int>(new FortyTwoComparer());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the full constructor of the reverse comparer works
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestReversedDefaultComparer() {
|
||||
Comparer<int> comparer = Comparer<int>.Default;
|
||||
ReverseComparer<int> reverseComparer = new ReverseComparer<int>(comparer);
|
||||
|
||||
Assert.Greater(0, comparer.Compare(10, 20));
|
||||
Assert.Less(0, comparer.Compare(20, 10));
|
||||
|
||||
Assert.Less(0, reverseComparer.Compare(10, 20));
|
||||
Assert.Greater(0, reverseComparer.Compare(20, 10));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the full constructor of the reverse comparer works
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestReversedCustomComparer() {
|
||||
FortyTwoComparer fortyTwoComparer = new FortyTwoComparer();
|
||||
ReverseComparer<int> reverseFortyTwoComparer = new ReverseComparer<int>(
|
||||
fortyTwoComparer
|
||||
);
|
||||
|
||||
Assert.Less(0, fortyTwoComparer.Compare(42, 84));
|
||||
Assert.Greater(0, fortyTwoComparer.Compare(84, 42));
|
||||
|
||||
Assert.Greater(0, reverseFortyTwoComparer.Compare(42, 84));
|
||||
Assert.Less(0, reverseFortyTwoComparer.Compare(84, 42));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Collections
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,119 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Reflection;
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.Collections {
|
||||
|
||||
/// <summary>Unit Test for the SortableBindingList class</summary>
|
||||
[TestFixture]
|
||||
internal class SortableBindingListTest {
|
||||
|
||||
#region class TestRecord
|
||||
|
||||
/// <summary>Dummy record used to test the sortable binding list</summary>
|
||||
private class TestRecord {
|
||||
|
||||
/// <summary>A property of type integer</summary>
|
||||
public int IntegerValue { get; set; }
|
||||
|
||||
/// <summary>A property of type string</summary>
|
||||
public string StringValue { get; set; }
|
||||
|
||||
/// <summary>A property of type float</summary>
|
||||
public float FloatValue { get; set; }
|
||||
|
||||
}
|
||||
|
||||
#endregion // class TestRecord
|
||||
|
||||
/// <summary>Verifies that the sortable binding list is default constructible</summary>
|
||||
[Test]
|
||||
public void HasDefaultConstructor() {
|
||||
Assert.DoesNotThrow(
|
||||
delegate () { new SortableBindingList<TestRecord>(); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the sortable binding list can copy an existing list
|
||||
/// when being constructed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void HasEnumerableConstructor() {
|
||||
var items = new List<TestRecord>() {
|
||||
new TestRecord() { IntegerValue = 123 },
|
||||
new TestRecord() { IntegerValue = 456 }
|
||||
};
|
||||
|
||||
var testList = new SortableBindingList<TestRecord>(items);
|
||||
|
||||
Assert.AreEqual(2, testList.Count);
|
||||
Assert.AreSame(items[0], testList[0]);
|
||||
Assert.AreSame(items[1], testList[1]);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the sortable binding list supports sorting</summary>
|
||||
[Test]
|
||||
public void SupportsSorting() {
|
||||
var testList = new SortableBindingList<TestRecord>();
|
||||
IBindingList testListAsBindingList = testList;
|
||||
|
||||
Assert.IsTrue(testListAsBindingList.SupportsSorting);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the sortable binding list can sort its elements by different properties
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CanSortItems() {
|
||||
var items = new List<TestRecord>() {
|
||||
new TestRecord() { IntegerValue = 456 },
|
||||
new TestRecord() { IntegerValue = 789 },
|
||||
new TestRecord() { IntegerValue = 123 }
|
||||
};
|
||||
|
||||
var testList = new SortableBindingList<TestRecord>(items);
|
||||
IBindingList testListAsBindingList = testList;
|
||||
|
||||
PropertyDescriptor integerValuePropertyDescriptor = (
|
||||
TypeDescriptor.GetProperties(typeof(TestRecord))[nameof(TestRecord.IntegerValue)]
|
||||
);
|
||||
testListAsBindingList.ApplySort(
|
||||
integerValuePropertyDescriptor, ListSortDirection.Ascending
|
||||
);
|
||||
|
||||
Assert.AreEqual(3, testList.Count);
|
||||
Assert.AreEqual(123, testList[0].IntegerValue);
|
||||
Assert.AreEqual(456, testList[1].IntegerValue);
|
||||
Assert.AreEqual(789, testList[2].IntegerValue);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Collections
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,500 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if !NO_NMOCK
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using NUnit.Framework;
|
||||
using NMock;
|
||||
|
||||
namespace Nuclex.Support.Collections {
|
||||
|
||||
/// <summary>Unit Test for the transforming read only collection wrapper</summary>
|
||||
[TestFixture]
|
||||
internal class TransformingReadOnlyCollectionTest {
|
||||
|
||||
#region class StringTransformer
|
||||
|
||||
/// <summary>Test implementation of a transforming collection</summary>
|
||||
private class StringTransformer : TransformingReadOnlyCollection<int, string> {
|
||||
|
||||
/// <summary>Initializes a new int-to-string transforming collection</summary>
|
||||
/// <param name="items">Items the transforming collection will contain</param>
|
||||
public StringTransformer(IList<int> items) : base(items) { }
|
||||
|
||||
/// <summary>Transforms an item into the exposed type</summary>
|
||||
/// <param name="item">Item to be transformed</param>
|
||||
/// <returns>The transformed item</returns>
|
||||
/// <remarks>
|
||||
/// This method is used to transform an item in the wrapped collection into
|
||||
/// the exposed item type whenever the user accesses an item. Expect it to
|
||||
/// be called frequently, because the TransformingReadOnlyCollection does
|
||||
/// not cache or otherwise store the transformed items.
|
||||
/// </remarks>
|
||||
protected override string Transform(int item) {
|
||||
if(item == 42) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return item.ToString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endregion // class StringTransformer
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the copy constructor of the transforming read only collection works
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCopyConstructor() {
|
||||
int[] integers = new int[] { 12, 34, 56, 78 };
|
||||
StringTransformer testCollection = new StringTransformer(integers);
|
||||
|
||||
string[] strings = new string[] { "12", "34", "56", "78" };
|
||||
CollectionAssert.AreEqual(strings, testCollection);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the IsReadOnly property returns true</summary>
|
||||
[Test]
|
||||
public void TestIsReadOnly() {
|
||||
StringTransformer testCollection = new StringTransformer(new int[0]);
|
||||
|
||||
Assert.IsTrue(testCollection.IsReadOnly);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the CopyTo() method of the transforming read only collection works
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCopyToArray() {
|
||||
int[] inputIntegers = new int[] { 12, 34, 56, 78 };
|
||||
StringTransformer testCollection = new StringTransformer(inputIntegers);
|
||||
|
||||
string[] inputStrings = new string[] { "12", "34", "56", "78" };
|
||||
string[] outputStrings = new string[testCollection.Count];
|
||||
testCollection.CopyTo(outputStrings, 0);
|
||||
|
||||
CollectionAssert.AreEqual(inputStrings, outputStrings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the CopyTo() method of the transforming read only collection throws
|
||||
/// an exception if the target array is too small to hold the collection's contents
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnCopyToTooSmallArray() {
|
||||
int[] inputIntegers = new int[] { 12, 34, 56, 78 };
|
||||
StringTransformer testCollection = new StringTransformer(inputIntegers);
|
||||
|
||||
string[] outputStrings = new string[testCollection.Count - 1];
|
||||
Assert.Throws<ArgumentException>(
|
||||
delegate() { testCollection.CopyTo(outputStrings, 0); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the Contains() method of the transforming read only collection
|
||||
/// is able to determine if the collection contains an item
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestContains() {
|
||||
int[] integers = new int[] { 1234, 6789 };
|
||||
StringTransformer testCollection = new StringTransformer(integers);
|
||||
|
||||
Assert.IsTrue(testCollection.Contains("1234"));
|
||||
Assert.IsFalse(testCollection.Contains("4321"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the IndexOf() method of the transforming read only collection
|
||||
/// is able to determine if the index of an item in the collection
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestIndexOf() {
|
||||
int[] integers = new int[] { 12, 34, 67, 89 };
|
||||
StringTransformer testCollection = new StringTransformer(integers);
|
||||
|
||||
Assert.AreEqual(0, testCollection.IndexOf("12"));
|
||||
Assert.AreEqual(1, testCollection.IndexOf("34"));
|
||||
Assert.AreEqual(2, testCollection.IndexOf("67"));
|
||||
Assert.AreEqual(3, testCollection.IndexOf("89"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the IndexOf() method of the transforming read only collection
|
||||
/// can cope with queries for 'null' when no 'null' item is contained on it
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestIndexOfWithNullItemNotContained() {
|
||||
int[] integers = new int[] { 12, 34, 67, 89 };
|
||||
StringTransformer testCollection = new StringTransformer(integers);
|
||||
|
||||
Assert.AreEqual(-1, testCollection.IndexOf(null));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the IndexOf() method of the transforming read only collection
|
||||
/// can cope with queries for 'null' when a 'null' item is contained on it
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestIndexOfWithNullItemContained() {
|
||||
int[] integers = new int[] { 12, 34, 67, 89, 42 };
|
||||
StringTransformer testCollection = new StringTransformer(integers);
|
||||
|
||||
Assert.AreEqual(4, testCollection.IndexOf(null));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the Enumerator of the transforming read only collection correctly
|
||||
/// implements the Reset() method
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestEnumeratorReset() {
|
||||
int[] integers = new int[] { 1234, 6789 };
|
||||
StringTransformer testCollection = new StringTransformer(integers);
|
||||
|
||||
IEnumerator<string> stringEnumerator = testCollection.GetEnumerator();
|
||||
Assert.IsTrue(stringEnumerator.MoveNext());
|
||||
Assert.IsTrue(stringEnumerator.MoveNext());
|
||||
Assert.IsFalse(stringEnumerator.MoveNext());
|
||||
|
||||
stringEnumerator.Reset();
|
||||
|
||||
Assert.IsTrue(stringEnumerator.MoveNext());
|
||||
Assert.IsTrue(stringEnumerator.MoveNext());
|
||||
Assert.IsFalse(stringEnumerator.MoveNext());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the indexer method of the transforming read only collection
|
||||
/// is able to retrieve items from the collection
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestRetrieveByIndexer() {
|
||||
int[] integers = new int[] { 12, 34, 67, 89 };
|
||||
StringTransformer testCollection = new StringTransformer(integers);
|
||||
|
||||
Assert.AreEqual("12", testCollection[0]);
|
||||
Assert.AreEqual("34", testCollection[1]);
|
||||
Assert.AreEqual("67", testCollection[2]);
|
||||
Assert.AreEqual("89", testCollection[3]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the transforming read only collection will throw an exception
|
||||
/// if its Insert() method is called via the generic IList<> interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnInsertViaGenericIList() {
|
||||
StringTransformer testCollection = new StringTransformer(new int[0]);
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { (testCollection as IList<string>).Insert(0, "12345"); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the transforming read only collection will throw an exception
|
||||
/// if its RemoveAt() method is called via the generic IList<> interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnRemoveViaGenericIList() {
|
||||
StringTransformer testCollection = new StringTransformer(new int[1]);
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { (testCollection as IList<string>).RemoveAt(0); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the indexer method of the transforming read only collection will
|
||||
/// throw an exception if it is attempted to be used for replacing an item
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestRetrieveByIndexerViaGenericIList() {
|
||||
int[] integers = new int[] { 12, 34, 67, 89 };
|
||||
StringTransformer testCollection = new StringTransformer(integers);
|
||||
|
||||
Assert.AreEqual("12", (testCollection as IList<string>)[0]);
|
||||
Assert.AreEqual("34", (testCollection as IList<string>)[1]);
|
||||
Assert.AreEqual("67", (testCollection as IList<string>)[2]);
|
||||
Assert.AreEqual("89", (testCollection as IList<string>)[3]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the indexer method of the transforming read only collection
|
||||
/// will throw an exception if it is attempted to be used for replacing an item
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnReplaceByIndexerViaGenericIList() {
|
||||
StringTransformer testCollection = new StringTransformer(new int[1]);
|
||||
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { (testCollection as IList<string>)[0] = "12345"; }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the transforming read only collection will throw an exception
|
||||
/// if its Add() method is called via the generic ICollection<> interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnAddViaGenericICollection() {
|
||||
StringTransformer testCollection = new StringTransformer(new int[0]);
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { (testCollection as ICollection<string>).Add("12345"); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the transforming read only collection will throw an exception
|
||||
/// if its Clear() method is called via the generic ICollection<> interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnClearViaGenericICollection() {
|
||||
StringTransformer testCollection = new StringTransformer(new int[1]);
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { (testCollection as ICollection<string>).Clear(); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the transforming read only collection will throw an exception
|
||||
/// if its Remove() method is called via the generic ICollection<> interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnRemoveViaGenericICollection() {
|
||||
int[] integers = new int[] { 12, 34, 67, 89 };
|
||||
StringTransformer testCollection = new StringTransformer(integers);
|
||||
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { (testCollection as ICollection<string>).Remove("89"); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the typesafe enumerator of the read only collection is working
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestTypesafeEnumerator() {
|
||||
int[] inputIntegers = new int[] { 12, 34, 56, 78 };
|
||||
StringTransformer testCollection = new StringTransformer(inputIntegers);
|
||||
|
||||
List<string> outputStrings = new List<string>();
|
||||
foreach(string value in testCollection) {
|
||||
outputStrings.Add(value);
|
||||
}
|
||||
|
||||
string[] inputStrings = new string[] { "12", "34", "56", "78" };
|
||||
CollectionAssert.AreEqual(inputStrings, outputStrings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the transforming read only collection will throw an exception
|
||||
/// if its Clear() method is called via the IList interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnClearViaIList() {
|
||||
StringTransformer testCollection = new StringTransformer(new int[1]);
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { (testCollection as IList).Clear(); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the transforming read only collection will throw an exception
|
||||
/// if its Add() method is called via the IList interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnAddViaIList() {
|
||||
StringTransformer testCollection = new StringTransformer(new int[0]);
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { (testCollection as IList).Add("12345"); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the Contains() method of the transforming read only collection
|
||||
/// is able to determine if the collection contains an item
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestContainsViaIList() {
|
||||
int[] integers = new int[] { 1234, 6789 };
|
||||
StringTransformer testCollection = new StringTransformer(integers);
|
||||
|
||||
Assert.IsTrue((testCollection as IList).Contains("1234"));
|
||||
Assert.IsFalse((testCollection as IList).Contains("4321"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the IndexOf() method of the transforming read only collection
|
||||
/// is able to determine if the index of an item in the collection
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestIndexOfViaIList() {
|
||||
int[] integers = new int[] { 12, 34, 67, 89 };
|
||||
StringTransformer testCollection = new StringTransformer(integers);
|
||||
|
||||
Assert.AreEqual(0, (testCollection as IList).IndexOf("12"));
|
||||
Assert.AreEqual(1, (testCollection as IList).IndexOf("34"));
|
||||
Assert.AreEqual(2, (testCollection as IList).IndexOf("67"));
|
||||
Assert.AreEqual(3, (testCollection as IList).IndexOf("89"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the transforming read only collection will throw an exception
|
||||
/// if its Insert() method is called via the IList interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnInsertViaIList() {
|
||||
StringTransformer testCollection = new StringTransformer(new int[0]);
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { (testCollection as IList).Insert(0, "12345"); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the IsFixedSize property of the transforming read only collection
|
||||
/// returns the expected result for a transforming read only collection based on
|
||||
/// a fixed array
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestIsFixedSizeViaIList() {
|
||||
int[] integers = new int[] { 12, 34, 67, 89 };
|
||||
StringTransformer testCollection = new StringTransformer(integers);
|
||||
|
||||
Assert.IsTrue((testCollection as IList).IsFixedSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the transforming read only collection will throw an exception
|
||||
/// if its Remove() method is called via the IList interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnRemoveViaIList() {
|
||||
int[] integers = new int[] { 1234, 6789 };
|
||||
StringTransformer testCollection = new StringTransformer(integers);
|
||||
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { (testCollection as IList).Remove("6789"); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the transforming read only collection will throw an exception
|
||||
/// if its Remove() method is called via the IList interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnRemoveAtViaIList() {
|
||||
StringTransformer testCollection = new StringTransformer(new int[1]);
|
||||
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { (testCollection as IList).RemoveAt(0); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the indexer method of the transforming read only collection
|
||||
/// will throw an exception if it is attempted to be used for replacing an item
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestRetrieveByIndexerViaIList() {
|
||||
int[] integers = new int[] { 12, 34, 67, 89 };
|
||||
StringTransformer testCollection = new StringTransformer(integers);
|
||||
|
||||
Assert.AreEqual("12", (testCollection as IList)[0]);
|
||||
Assert.AreEqual("34", (testCollection as IList)[1]);
|
||||
Assert.AreEqual("67", (testCollection as IList)[2]);
|
||||
Assert.AreEqual("89", (testCollection as IList)[3]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the indexer method of the transforming read only collection
|
||||
/// will throw an exception if it is attempted to be used for replacing an item
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnReplaceByIndexerViaIList() {
|
||||
StringTransformer testCollection = new StringTransformer(new int[1]);
|
||||
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { (testCollection as IList)[0] = "12345"; }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the CopyTo() method of the transforming read only collection
|
||||
/// works if invoked via the ICollection interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCopyToArrayViaICollection() {
|
||||
int[] inputIntegers = new int[] { 12, 34, 56, 78 };
|
||||
StringTransformer testCollection = new StringTransformer(inputIntegers);
|
||||
|
||||
string[] outputStrings = new string[testCollection.Count];
|
||||
(testCollection as ICollection).CopyTo(outputStrings, 0);
|
||||
|
||||
string[] inputStrings = new string[] { "12", "34", "56", "78" };
|
||||
CollectionAssert.AreEqual(inputStrings, outputStrings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the IsSynchronized property and the SyncRoot property are working
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestSynchronization() {
|
||||
StringTransformer testCollection = new StringTransformer(new int[0]);
|
||||
|
||||
if(!(testCollection as ICollection).IsSynchronized) {
|
||||
lock((testCollection as ICollection).SyncRoot) {
|
||||
Assert.AreEqual(0, testCollection.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the IsSynchronized property and the SyncRoot property are working
|
||||
/// on transforming read only collections based on IList<>s that do not
|
||||
/// implement the ICollection interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestSynchronizationOfIListWithoutICollection() {
|
||||
MockFactory mockery = new MockFactory();
|
||||
Mock<IList<int>> mockedIList = mockery.CreateMock<IList<int>>();
|
||||
StringTransformer testCollection = new StringTransformer(mockedIList.MockObject);
|
||||
|
||||
if(!(testCollection as ICollection).IsSynchronized) {
|
||||
lock((testCollection as ICollection).SyncRoot) {
|
||||
mockedIList.Expects.One.GetProperty(p => p.Count).WillReturn(12345);
|
||||
int count = testCollection.Count;
|
||||
Assert.AreEqual(12345, count); // ;-)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Collections
|
||||
|
||||
#endif // UNITTEST
|
||||
|
||||
#endif // !NO_NMOCK
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if !NO_SETS
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.Collections {
|
||||
|
||||
/// <summary>Unit Test for the Variegator multi dictionary</summary>
|
||||
[TestFixture]
|
||||
internal class VariegatorTest {
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the default constructor of the reverse comparer works
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void InstancesCanBeCreated() {
|
||||
new Variegator<int, string>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that querying for a missing value leads to an exception being thrown
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void QueryingMissingValueThrowsException() {
|
||||
var variegator = new Variegator<int, string>();
|
||||
Assert.Throws<KeyNotFoundException>(
|
||||
() => {
|
||||
variegator.Get(123);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the variegator resolves ambiguous matches according to its design
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void AmbiguityResolvesToLeastRecentValue() {
|
||||
var variegator = new Variegator<int, string>();
|
||||
variegator.Add(1, "one");
|
||||
variegator.Add(1, "eins");
|
||||
|
||||
string first = variegator.Get(1);
|
||||
string second = variegator.Get(1);
|
||||
|
||||
// The variegator should have selected the first value by random and then
|
||||
// returned the other value on the second query
|
||||
Assert.AreNotEqual(first, second);
|
||||
|
||||
// Now the variegator should return the first value again because it is
|
||||
// the least recently used value
|
||||
Assert.AreEqual(first, variegator.Get(1));
|
||||
|
||||
// Repeating the query, the second should be returned again because now
|
||||
// it has become the least recently used value
|
||||
Assert.AreEqual(second, variegator.Get(1));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Collections
|
||||
|
||||
#endif // UNITTEST
|
||||
|
||||
#endif // !NO_SETS
|
||||
|
|
@ -1,668 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.Collections {
|
||||
|
||||
/// <summary>Unit Test for the weak collection wrapper</summary>
|
||||
[TestFixture]
|
||||
internal class WeakCollectionTest {
|
||||
|
||||
#region class Dummy
|
||||
|
||||
/// <summary>Dummy class used to test the weakly referencing collection</summary>
|
||||
private class Dummy {
|
||||
|
||||
/// <summary>Initializes a new dummy</summary>
|
||||
/// <param name="value">Value that will be stored by the dummy</param>
|
||||
public Dummy(int value) {
|
||||
this.Value = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified System.Object is equal to
|
||||
/// the current Dummy object.
|
||||
/// </summary>
|
||||
/// <param name="otherAsObject">
|
||||
/// The System.Object to compare with the current Dummy object
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// True if the specified System.Object is equal to the current Dummy object;
|
||||
/// otherwise, false.
|
||||
/// </returns>
|
||||
public override bool Equals(object otherAsObject) {
|
||||
Dummy other = otherAsObject as Dummy;
|
||||
if(other == null) {
|
||||
return false;
|
||||
}
|
||||
return this.Value.Equals(other.Value);
|
||||
}
|
||||
|
||||
/// <summary>Serves as a hash function for a particular type.</summary>
|
||||
/// <returns>A hash code for the current System.Object.</returns>
|
||||
public override int GetHashCode() {
|
||||
return this.Value.GetHashCode();
|
||||
}
|
||||
|
||||
/// <summary>Some value that can be used for testing</summary>
|
||||
public int Value;
|
||||
|
||||
}
|
||||
|
||||
#endregion // class Dummy
|
||||
|
||||
#region class ListWithoutICollection
|
||||
|
||||
private class ListWithoutICollection : IList<WeakReference<Dummy>> {
|
||||
public int IndexOf(WeakReference<Dummy> item) { throw new NotImplementedException(); }
|
||||
public void Insert(int index, WeakReference<Dummy> item) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public void RemoveAt(int index) { throw new NotImplementedException(); }
|
||||
public WeakReference<Dummy> this[int index] {
|
||||
get { throw new NotImplementedException(); }
|
||||
set { throw new NotImplementedException(); }
|
||||
}
|
||||
public void Add(WeakReference<Dummy> item) { throw new NotImplementedException(); }
|
||||
public void Clear() { throw new NotImplementedException(); }
|
||||
public bool Contains(WeakReference<Dummy> item) { throw new NotImplementedException(); }
|
||||
public void CopyTo(WeakReference<Dummy>[] array, int arrayIndex) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public int Count { get { return 12345; } }
|
||||
public bool IsReadOnly { get { throw new NotImplementedException(); } }
|
||||
public bool Remove(WeakReference<Dummy> item) { throw new NotImplementedException(); }
|
||||
public IEnumerator<WeakReference<Dummy>> GetEnumerator() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
#endregion // class ListWithoutICollection
|
||||
|
||||
/// <summary>Verifies that the constructor of the weak collection is working</summary>
|
||||
[Test]
|
||||
public void TestConstructor() {
|
||||
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
|
||||
new List<WeakReference<Dummy>>()
|
||||
);
|
||||
Assert.IsNotNull(dummies);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test whether the non-typesafe Add() method of the weak collection works
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestAddAsObject() {
|
||||
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
|
||||
new List<WeakReference<Dummy>>()
|
||||
);
|
||||
|
||||
Dummy oneTwoThreeDummy = new Dummy(12345);
|
||||
(dummies as IList).Add((object)oneTwoThreeDummy);
|
||||
|
||||
CollectionAssert.Contains(dummies, oneTwoThreeDummy);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test whether the non-typesafe Add() method throws an exception if an object is
|
||||
/// added that is not compatible to the collection's item type
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnAddIncompatibleObject() {
|
||||
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
|
||||
new List<WeakReference<Dummy>>()
|
||||
);
|
||||
|
||||
Assert.Throws<ArgumentException>(
|
||||
delegate() { (dummies as IList).Add(new object()); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test whether the generic Add() method of the weak collection works
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestAdd() {
|
||||
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
|
||||
new List<WeakReference<Dummy>>()
|
||||
);
|
||||
|
||||
Dummy oneTwoThreeDummy = new Dummy(12345);
|
||||
dummies.Add(oneTwoThreeDummy);
|
||||
|
||||
CollectionAssert.Contains(dummies, oneTwoThreeDummy);
|
||||
}
|
||||
|
||||
/// <summary>Tests whether the Clear() method works</summary>
|
||||
[Test]
|
||||
public void TestClear() {
|
||||
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
|
||||
new List<WeakReference<Dummy>>()
|
||||
);
|
||||
|
||||
Dummy oneTwoThreeDummy = new Dummy(12345);
|
||||
dummies.Add(oneTwoThreeDummy);
|
||||
Dummy threeTwoOneDummy = new Dummy(54321);
|
||||
dummies.Add(threeTwoOneDummy);
|
||||
|
||||
Assert.AreEqual(2, dummies.Count);
|
||||
|
||||
dummies.Clear();
|
||||
|
||||
Assert.AreEqual(0, dummies.Count);
|
||||
}
|
||||
|
||||
/// <summary>Tests whether the Contains() method works</summary>
|
||||
[Test]
|
||||
public void TestContains() {
|
||||
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
|
||||
new List<WeakReference<Dummy>>()
|
||||
);
|
||||
|
||||
Dummy oneTwoThreeDummy = new Dummy(12345);
|
||||
dummies.Add(oneTwoThreeDummy);
|
||||
Dummy threeTwoOneDummy = new Dummy(54321);
|
||||
|
||||
Assert.IsTrue(dummies.Contains(oneTwoThreeDummy));
|
||||
Assert.IsFalse(dummies.Contains(threeTwoOneDummy));
|
||||
}
|
||||
|
||||
/// <summary>Tests whether the non-typesafe Contains() method works</summary>
|
||||
[Test]
|
||||
public void TestContainsWithObject() {
|
||||
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
|
||||
new List<WeakReference<Dummy>>()
|
||||
);
|
||||
|
||||
Dummy oneTwoThreeDummy = new Dummy(12345);
|
||||
dummies.Add(oneTwoThreeDummy);
|
||||
Dummy threeTwoOneDummy = new Dummy(54321);
|
||||
|
||||
Assert.IsTrue((dummies as IList).Contains((object)oneTwoThreeDummy));
|
||||
Assert.IsFalse((dummies as IList).Contains((object)threeTwoOneDummy));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the Enumerator of the dummy collection correctly
|
||||
/// implements the Reset() method
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestEnumeratorReset() {
|
||||
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
|
||||
new List<WeakReference<Dummy>>()
|
||||
);
|
||||
Dummy oneTwoThreeDummy = new Dummy(123);
|
||||
dummies.Add(oneTwoThreeDummy);
|
||||
Dummy fourFiveSixDummy = new Dummy(456);
|
||||
dummies.Add(fourFiveSixDummy);
|
||||
|
||||
IEnumerator<Dummy> dummyEnumerator = dummies.GetEnumerator();
|
||||
Assert.IsTrue(dummyEnumerator.MoveNext());
|
||||
Assert.IsTrue(dummyEnumerator.MoveNext());
|
||||
Assert.IsFalse(dummyEnumerator.MoveNext());
|
||||
|
||||
dummyEnumerator.Reset();
|
||||
|
||||
Assert.IsTrue(dummyEnumerator.MoveNext());
|
||||
Assert.IsTrue(dummyEnumerator.MoveNext());
|
||||
Assert.IsFalse(dummyEnumerator.MoveNext());
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the IndexOf() method is working as intended</summary>
|
||||
[Test]
|
||||
public void TestIndexOf() {
|
||||
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
|
||||
new List<WeakReference<Dummy>>()
|
||||
);
|
||||
Dummy oneTwoThreeDummy = new Dummy(123);
|
||||
dummies.Add(oneTwoThreeDummy);
|
||||
Dummy fourFiveSixDummy = new Dummy(456);
|
||||
dummies.Add(fourFiveSixDummy);
|
||||
Dummy sevenEightNineDummy = new Dummy(789);
|
||||
|
||||
Assert.AreEqual(0, dummies.IndexOf(oneTwoThreeDummy));
|
||||
Assert.AreEqual(1, dummies.IndexOf(fourFiveSixDummy));
|
||||
Assert.AreEqual(-1, dummies.IndexOf(sevenEightNineDummy));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the non-typesafe IndexOf() method is working as intended
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestIndexOfWithObject() {
|
||||
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
|
||||
new List<WeakReference<Dummy>>()
|
||||
);
|
||||
Dummy oneTwoThreeDummy = new Dummy(123);
|
||||
dummies.Add(oneTwoThreeDummy);
|
||||
Dummy fourFiveSixDummy = new Dummy(456);
|
||||
dummies.Add(fourFiveSixDummy);
|
||||
Dummy sevenEightNineDummy = new Dummy(789);
|
||||
|
||||
Assert.AreEqual(0, (dummies as IList).IndexOf((object)oneTwoThreeDummy));
|
||||
Assert.AreEqual(1, (dummies as IList).IndexOf((object)fourFiveSixDummy));
|
||||
Assert.AreEqual(-1, (dummies as IList).IndexOf((object)sevenEightNineDummy));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that an exception is thrown if an incompatible object is passed to
|
||||
/// the non-typesafe variant of the IndexOf() method
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnIndexOfWithIncompatibleObject() {
|
||||
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
|
||||
new List<WeakReference<Dummy>>()
|
||||
);
|
||||
|
||||
Assert.Throws<ArgumentException>(
|
||||
delegate() { Assert.IsNull((dummies as IList).IndexOf(new object())); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Test whether the IndexOf() method can cope with null references</summary>
|
||||
[Test]
|
||||
public void TestIndexOfNull() {
|
||||
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
|
||||
new List<WeakReference<Dummy>>()
|
||||
);
|
||||
|
||||
Assert.AreEqual(-1, dummies.IndexOf(null));
|
||||
dummies.Add(null);
|
||||
Assert.AreEqual(0, dummies.IndexOf(null));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the CopyTo() method of the weak collection works
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCopyToArray() {
|
||||
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
|
||||
new List<WeakReference<Dummy>>()
|
||||
);
|
||||
Dummy oneTwoThreeDummy = new Dummy(123);
|
||||
dummies.Add(oneTwoThreeDummy);
|
||||
Dummy fourFiveSixDummy = new Dummy(456);
|
||||
dummies.Add(fourFiveSixDummy);
|
||||
|
||||
Dummy[] inputDummies = new Dummy[] { oneTwoThreeDummy, fourFiveSixDummy };
|
||||
Dummy[] outputDummies = new Dummy[dummies.Count];
|
||||
|
||||
dummies.CopyTo(outputDummies, 0);
|
||||
|
||||
CollectionAssert.AreEqual(inputDummies, outputDummies);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the CopyTo() method of the weak collection throws an exception
|
||||
/// if the target array is too small to hold the collection's contents
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnCopyToTooSmallArray() {
|
||||
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
|
||||
new List<WeakReference<Dummy>>()
|
||||
);
|
||||
Dummy oneTwoThreeDummy = new Dummy(123);
|
||||
dummies.Add(oneTwoThreeDummy);
|
||||
Dummy fourFiveSixDummy = new Dummy(456);
|
||||
dummies.Add(fourFiveSixDummy);
|
||||
|
||||
Dummy[] outputStrings = new Dummy[dummies.Count - 1];
|
||||
Assert.Throws<ArgumentException>(
|
||||
delegate() { dummies.CopyTo(outputStrings, 0); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the CopyTo() method of the transforming read only collection
|
||||
/// works if invoked via the ICollection interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCopyToArrayViaICollection() {
|
||||
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
|
||||
new List<WeakReference<Dummy>>()
|
||||
);
|
||||
Dummy oneTwoThreeDummy = new Dummy(123);
|
||||
dummies.Add(oneTwoThreeDummy);
|
||||
Dummy fourFiveSixDummy = new Dummy(456);
|
||||
dummies.Add(fourFiveSixDummy);
|
||||
|
||||
Dummy[] inputDummies = new Dummy[] { oneTwoThreeDummy, fourFiveSixDummy };
|
||||
Dummy[] outputDummies = new Dummy[dummies.Count];
|
||||
|
||||
(dummies as ICollection).CopyTo(outputDummies, 0);
|
||||
|
||||
CollectionAssert.AreEqual(inputDummies, outputDummies);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the Insert() method correctly shifts items in the collection
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestInsert() {
|
||||
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
|
||||
new List<WeakReference<Dummy>>()
|
||||
);
|
||||
Dummy oneTwoThreeDummy = new Dummy(123);
|
||||
dummies.Add(oneTwoThreeDummy);
|
||||
|
||||
Dummy fourFiveSixDummy = new Dummy(456);
|
||||
dummies.Insert(0, fourFiveSixDummy);
|
||||
|
||||
Assert.AreEqual(2, dummies.Count);
|
||||
Assert.AreSame(fourFiveSixDummy, dummies[0]);
|
||||
Assert.AreSame(oneTwoThreeDummy, dummies[1]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the non-typesafe Insert() method correctly shifts items in
|
||||
/// the collection
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestInsertObject() {
|
||||
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
|
||||
new List<WeakReference<Dummy>>()
|
||||
);
|
||||
Dummy oneTwoThreeDummy = new Dummy(123);
|
||||
dummies.Add(oneTwoThreeDummy);
|
||||
|
||||
Dummy fourFiveSixDummy = new Dummy(456);
|
||||
(dummies as IList).Insert(0, (object)fourFiveSixDummy);
|
||||
|
||||
Assert.AreEqual(2, dummies.Count);
|
||||
Assert.AreSame(fourFiveSixDummy, dummies[0]);
|
||||
Assert.AreSame(oneTwoThreeDummy, dummies[1]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the non-typesafe Insert() method correctly shifts items in
|
||||
/// the collection
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnInsertIncompatibleObject() {
|
||||
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
|
||||
new List<WeakReference<Dummy>>()
|
||||
);
|
||||
Dummy oneTwoThreeDummy = new Dummy(123);
|
||||
dummies.Add(oneTwoThreeDummy);
|
||||
|
||||
Assert.Throws<ArgumentException>(
|
||||
delegate() { (dummies as IList).Insert(0, new object()); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the IsFixedSize property of the weak collection returns
|
||||
/// the expected result for a weak collection based on a fixed array
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestIsFixedSizeViaIList() {
|
||||
Dummy oneTwoThreeDummy = new Dummy(123);
|
||||
Dummy fourFiveSixDummy = new Dummy(456);
|
||||
|
||||
WeakReference<Dummy>[] dummyReferences = new WeakReference<Dummy>[] {
|
||||
new WeakReference<Dummy>(oneTwoThreeDummy),
|
||||
new WeakReference<Dummy>(fourFiveSixDummy)
|
||||
};
|
||||
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(dummyReferences);
|
||||
|
||||
Assert.IsTrue((dummies as IList).IsFixedSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the IsReadOnly property of the weak collection works
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestIsReadOnly() {
|
||||
Dummy oneTwoThreeDummy = new Dummy(123);
|
||||
Dummy fourFiveSixDummy = new Dummy(456);
|
||||
|
||||
List<WeakReference<Dummy>> dummyReferences = new List<WeakReference<Dummy>>();
|
||||
dummyReferences.Add(new WeakReference<Dummy>(oneTwoThreeDummy));
|
||||
dummyReferences.Add(new WeakReference<Dummy>(fourFiveSixDummy));
|
||||
|
||||
ReadOnlyList<WeakReference<Dummy>> readOnlyDummyReferences =
|
||||
new ReadOnlyList<WeakReference<Dummy>>(dummyReferences);
|
||||
|
||||
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(dummyReferences);
|
||||
WeakCollection<Dummy> readOnlydummies = new WeakCollection<Dummy>(
|
||||
readOnlyDummyReferences
|
||||
);
|
||||
|
||||
Assert.IsFalse(dummies.IsReadOnly);
|
||||
Assert.IsTrue(readOnlydummies.IsReadOnly);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the IsSynchronized property of the weak collection works
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestIsSynchronized() {
|
||||
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
|
||||
new List<WeakReference<Dummy>>()
|
||||
);
|
||||
|
||||
Assert.IsFalse((dummies as IList).IsSynchronized);
|
||||
}
|
||||
|
||||
/// <summary>Tests the indexer of the weak collection</summary>
|
||||
[Test]
|
||||
public void TestIndexer() {
|
||||
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
|
||||
new List<WeakReference<Dummy>>()
|
||||
);
|
||||
Dummy oneTwoThreeDummy = new Dummy(123);
|
||||
dummies.Add(oneTwoThreeDummy);
|
||||
Dummy fourFiveSixDummy = new Dummy(456);
|
||||
dummies.Add(fourFiveSixDummy);
|
||||
|
||||
Assert.AreSame(oneTwoThreeDummy, dummies[0]);
|
||||
Assert.AreSame(fourFiveSixDummy, dummies[1]);
|
||||
|
||||
dummies[0] = fourFiveSixDummy;
|
||||
|
||||
Assert.AreSame(fourFiveSixDummy, dummies[0]);
|
||||
}
|
||||
|
||||
/// <summary>Tests the non-typesafe indexer of the weak collection</summary>
|
||||
[Test]
|
||||
public void TestIndexerWithObject() {
|
||||
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
|
||||
new List<WeakReference<Dummy>>()
|
||||
);
|
||||
Dummy oneTwoThreeDummy = new Dummy(123);
|
||||
dummies.Add(oneTwoThreeDummy);
|
||||
Dummy fourFiveSixDummy = new Dummy(456);
|
||||
dummies.Add(fourFiveSixDummy);
|
||||
|
||||
Assert.AreSame((object)oneTwoThreeDummy, (dummies as IList)[0]);
|
||||
Assert.AreSame((object)fourFiveSixDummy, (dummies as IList)[1]);
|
||||
|
||||
(dummies as IList)[0] = (object)fourFiveSixDummy;
|
||||
|
||||
Assert.AreSame((object)fourFiveSixDummy, (dummies as IList)[0]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the non-typesafe indexer of the weak collection throws
|
||||
/// the correct exception if an incompatible object is assigned
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnIndexerWithIncompatibleObject() {
|
||||
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
|
||||
new List<WeakReference<Dummy>>()
|
||||
);
|
||||
Dummy oneTwoThreeDummy = new Dummy(123);
|
||||
dummies.Add(oneTwoThreeDummy);
|
||||
|
||||
Assert.Throws<ArgumentException>(
|
||||
delegate() { (dummies as IList)[0] = new object(); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Tests the Remove() method of the weak collection</summary>
|
||||
[Test]
|
||||
public void TestRemove() {
|
||||
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
|
||||
new List<WeakReference<Dummy>>()
|
||||
);
|
||||
Dummy oneTwoThreeDummy = new Dummy(123);
|
||||
dummies.Add(oneTwoThreeDummy);
|
||||
Dummy fourFiveSixDummy = new Dummy(456);
|
||||
dummies.Add(fourFiveSixDummy);
|
||||
|
||||
Assert.AreEqual(2, dummies.Count);
|
||||
Assert.IsTrue(dummies.Remove(oneTwoThreeDummy));
|
||||
Assert.AreEqual(1, dummies.Count);
|
||||
Assert.IsFalse(dummies.Remove(oneTwoThreeDummy));
|
||||
}
|
||||
|
||||
/// <summary>Tests the non-typesafe Remove() method of the weak collection</summary>
|
||||
[Test]
|
||||
public void TestRemoveObject() {
|
||||
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
|
||||
new List<WeakReference<Dummy>>()
|
||||
);
|
||||
Dummy oneTwoThreeDummy = new Dummy(123);
|
||||
dummies.Add(oneTwoThreeDummy);
|
||||
Dummy fourFiveSixDummy = new Dummy(456);
|
||||
dummies.Add(fourFiveSixDummy);
|
||||
|
||||
Assert.AreEqual(2, dummies.Count);
|
||||
(dummies as IList).Remove((object)oneTwoThreeDummy);
|
||||
Assert.AreEqual(1, dummies.Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether a null object can be managed by and removed from the weak collection
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestRemoveNull() {
|
||||
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
|
||||
new List<WeakReference<Dummy>>()
|
||||
);
|
||||
dummies.Add(null);
|
||||
|
||||
Assert.AreEqual(1, dummies.Count);
|
||||
Assert.IsTrue(dummies.Remove(null));
|
||||
Assert.AreEqual(0, dummies.Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the non-typesafe Remove() method of the weak collection throws
|
||||
/// an exception if an object is tried to be removed that is incompatible with
|
||||
/// the collection's item type
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnRemoveIncompatibleObject() {
|
||||
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
|
||||
new List<WeakReference<Dummy>>()
|
||||
);
|
||||
Assert.Throws<ArgumentException>(
|
||||
delegate() { (dummies as IList).Remove(new object()); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Tests the RemoveAt() method of the weak collection</summary>
|
||||
[Test]
|
||||
public void TestRemoveAt() {
|
||||
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
|
||||
new List<WeakReference<Dummy>>()
|
||||
);
|
||||
Dummy oneTwoThreeDummy = new Dummy(123);
|
||||
dummies.Add(oneTwoThreeDummy);
|
||||
Dummy fourFiveSixDummy = new Dummy(456);
|
||||
dummies.Add(fourFiveSixDummy);
|
||||
|
||||
Assert.AreSame(oneTwoThreeDummy, dummies[0]);
|
||||
dummies.RemoveAt(0);
|
||||
Assert.AreSame(fourFiveSixDummy, dummies[0]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the IsSynchronized property and the SyncRoot property are working
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestSynchronization() {
|
||||
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
|
||||
new List<WeakReference<Dummy>>()
|
||||
);
|
||||
|
||||
if(!(dummies as ICollection).IsSynchronized) {
|
||||
lock((dummies as ICollection).SyncRoot) {
|
||||
Assert.AreEqual(0, dummies.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the IsSynchronized property and the SyncRoot property are working
|
||||
/// on transforming read only collections based on IList<>s that do not
|
||||
/// implement the ICollection interface
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestSynchronizationOfIListWithoutICollection() {
|
||||
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(
|
||||
new ListWithoutICollection()
|
||||
);
|
||||
|
||||
if(!(dummies as ICollection).IsSynchronized) {
|
||||
lock((dummies as ICollection).SyncRoot) {
|
||||
int count = dummies.Count;
|
||||
Assert.AreEqual(12345, count); // ;-)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Tests the RemoveDeadItems() method</summary>
|
||||
[Test]
|
||||
public void TestRemoveDeadItems() {
|
||||
List<WeakReference<Dummy>> dummyReferences = new List<WeakReference<Dummy>>();
|
||||
|
||||
Dummy oneTwoThreeDummy = new Dummy(123);
|
||||
dummyReferences.Add(new WeakReference<Dummy>(oneTwoThreeDummy));
|
||||
|
||||
dummyReferences.Add(new WeakReference<Dummy>(null));
|
||||
|
||||
Dummy fourFiveSixDummy = new Dummy(456);
|
||||
dummyReferences.Add(new WeakReference<Dummy>(fourFiveSixDummy));
|
||||
|
||||
WeakCollection<Dummy> dummies = new WeakCollection<Dummy>(dummyReferences);
|
||||
|
||||
Assert.AreEqual(3, dummies.Count);
|
||||
|
||||
dummies.RemoveDeadItems();
|
||||
|
||||
Assert.AreEqual(2, dummies.Count);
|
||||
Assert.AreSame(oneTwoThreeDummy, dummies[0]);
|
||||
Assert.AreSame(fourFiveSixDummy, dummies[1]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Collections
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,123 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support {
|
||||
|
||||
/// <summary>Unit Test for the enumeration helper class</summary>
|
||||
[TestFixture]
|
||||
internal class EnumHelperTest {
|
||||
|
||||
#region enum TestEnumeration
|
||||
|
||||
/// <summary>An enumeration used for unit testing</summary>
|
||||
internal enum TestEnumeration {
|
||||
/// <summary>First arbitrary enumeration value</summary>
|
||||
One = -2,
|
||||
/// <summary>Third arbitrary enumeration value</summary>
|
||||
Three = 33,
|
||||
/// <summary>Second arbitrary enumeration value</summary>
|
||||
Two = 23
|
||||
}
|
||||
|
||||
#endregion // enum TestEnumeration
|
||||
|
||||
#region enum EmptyEnumeration
|
||||
|
||||
internal enum EmptyEnumeration { }
|
||||
|
||||
#endregion // enum EmptyEnumeration
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the enum helper can list the members of an enumeration
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestGetValues() {
|
||||
CollectionAssert.AreEquivalent(
|
||||
new TestEnumeration[] {
|
||||
TestEnumeration.One, TestEnumeration.Two, TestEnumeration.Three
|
||||
},
|
||||
EnumHelper.GetValues<TestEnumeration>()
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the enum helper can locate the highest value in an enumeration
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestGetHighestValue() {
|
||||
Assert.AreEqual(
|
||||
TestEnumeration.Three, EnumHelper.GetHighestValue<TestEnumeration>()
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the enum helper can locate the lowest value in an enumeration
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestGetLowestValue() {
|
||||
Assert.AreEqual(
|
||||
TestEnumeration.One, EnumHelper.GetLowestValue<TestEnumeration>()
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether an exception is thrown if the GetValues() method is used on
|
||||
/// a non-enumeration type
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnNonEnumType() {
|
||||
Assert.Throws<ArgumentException>(
|
||||
delegate() { EnumHelper.GetValues<int>(); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the default value for an enumeration is returned if
|
||||
/// the GetLowestValue() method is used on an empty enumeration
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestLowestValueInEmptyEnumeration() {
|
||||
Assert.AreEqual(
|
||||
default(EmptyEnumeration), EnumHelper.GetLowestValue<EmptyEnumeration>()
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the default value for an enumeration is returned if
|
||||
/// the GetHighestValue() method is used on an empty enumeration
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestHighestValueInEmptyEnumeration() {
|
||||
Assert.AreEqual(
|
||||
default(EmptyEnumeration), EnumHelper.GetHighestValue<EmptyEnumeration>()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,265 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support {
|
||||
|
||||
/// <summary>Unit Test for the FloatHelper class</summary>
|
||||
[TestFixture]
|
||||
internal class FloatHelperTest {
|
||||
|
||||
/// <summary>Tests the floating point value comparison helper</summary>
|
||||
[Test]
|
||||
public void UlpDistancesOnFloatsCompareAsEqual() {
|
||||
Assert.IsTrue(
|
||||
FloatHelper.AreAlmostEqual(0.00000001f, 0.0000000100000008f, 1),
|
||||
"Minimal difference between very small floating point numbers is considered equal"
|
||||
);
|
||||
Assert.IsFalse(
|
||||
FloatHelper.AreAlmostEqual(0.00000001f, 0.0000000100000017f, 1),
|
||||
"Larger difference between very small floating point numbers is not considered equal"
|
||||
);
|
||||
|
||||
Assert.IsTrue(
|
||||
FloatHelper.AreAlmostEqual(1000000.00f, 1000000.06f, 1),
|
||||
"Minimal difference between very large floating point numbers is considered equal"
|
||||
);
|
||||
Assert.IsFalse(
|
||||
FloatHelper.AreAlmostEqual(1000000.00f, 1000000.13f, 1),
|
||||
"Larger difference between very large floating point numbers is not considered equal"
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Tests the double precision floating point value comparison helper</summary>
|
||||
[Test]
|
||||
public void UlpDistancesOnDoublesCompareAsEqual() {
|
||||
Assert.IsTrue(
|
||||
FloatHelper.AreAlmostEqual(0.00000001, 0.000000010000000000000002, 1),
|
||||
"Minimal difference between very small double precision floating point " +
|
||||
"numbers is considered equal"
|
||||
);
|
||||
Assert.IsFalse(
|
||||
FloatHelper.AreAlmostEqual(0.00000001, 0.000000010000000000000004, 1),
|
||||
"Larger difference between very small double precision floating point " +
|
||||
"numbers is not considered equal"
|
||||
);
|
||||
|
||||
Assert.IsTrue(
|
||||
FloatHelper.AreAlmostEqual(1000000.00, 1000000.0000000001, 1),
|
||||
"Minimal difference between very large double precision floating point " +
|
||||
"numbers is considered equal"
|
||||
);
|
||||
Assert.IsFalse(
|
||||
FloatHelper.AreAlmostEqual(1000000.00, 1000000.0000000002, 1),
|
||||
"Larger difference between very large double precision floating point " +
|
||||
"numbers is not considered equal"
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Tests the integer reinterpretation functions</summary>
|
||||
[Test]
|
||||
public void IntegersCanBeReinterpretedAsFloats() {
|
||||
Assert.AreEqual(
|
||||
12345.0f,
|
||||
FloatHelper.ReinterpretAsFloat(FloatHelper.ReinterpretAsInt(12345.0f)),
|
||||
"Number hasn't changed after mirrored reinterpretation"
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Tests the long reinterpretation functions</summary>
|
||||
[Test]
|
||||
public void LongsCanBeReinterpretedAsDoubles() {
|
||||
Assert.AreEqual(
|
||||
12345.67890,
|
||||
FloatHelper.ReinterpretAsDouble(FloatHelper.ReinterpretAsLong(12345.67890)),
|
||||
"Number hasn't changed after mirrored reinterpretation"
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Tests the floating point reinterpretation functions</summary>
|
||||
[Test]
|
||||
public void FloatsCanBeReinterpretedAsIntegers() {
|
||||
Assert.AreEqual(
|
||||
12345,
|
||||
FloatHelper.ReinterpretAsInt(FloatHelper.ReinterpretAsFloat(12345)),
|
||||
"Number hasn't changed after mirrored reinterpretation"
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the IsZero() method can distinguish zero from very small values
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CanDetermineIfFloatIsZero() {
|
||||
Assert.IsTrue(FloatHelper.IsZero(FloatHelper.PositiveZeroFloat));
|
||||
Assert.IsTrue(FloatHelper.IsZero(FloatHelper.NegativeZeroFloat));
|
||||
Assert.IsFalse(FloatHelper.IsZero(1.401298E-45f));
|
||||
Assert.IsFalse(FloatHelper.IsZero(-1.401298E-45f));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the IsZero() method can distinguish zero from very small values
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CanDetermineIfDoubleIsZero() {
|
||||
Assert.IsTrue(FloatHelper.IsZero(FloatHelper.PositiveZeroDouble));
|
||||
Assert.IsTrue(FloatHelper.IsZero(FloatHelper.NegativeZeroDouble));
|
||||
Assert.IsFalse(FloatHelper.IsZero(4.94065645841247E-324));
|
||||
Assert.IsFalse(FloatHelper.IsZero(-4.94065645841247E-324));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests the double prevision floating point reinterpretation functions
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DoublesCanBeReinterpretedAsLongs() {
|
||||
Assert.AreEqual(
|
||||
1234567890,
|
||||
FloatHelper.ReinterpretAsLong(FloatHelper.ReinterpretAsDouble(1234567890)),
|
||||
"Number hasn't changed after mirrored reinterpretation"
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that two denormalized floats can be compared in ulps
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DenormalizedFloatsCanBeCompared() {
|
||||
float zero = 0.0f;
|
||||
float zeroPlusOneUlp = FloatHelper.ReinterpretAsFloat(
|
||||
FloatHelper.ReinterpretAsInt(zero) + 1
|
||||
);
|
||||
float zeroMinusOneUlp = -zeroPlusOneUlp;
|
||||
|
||||
// Across zero
|
||||
Assert.IsFalse(FloatHelper.AreAlmostEqual(zeroMinusOneUlp, zeroPlusOneUlp, 1));
|
||||
Assert.IsTrue(FloatHelper.AreAlmostEqual(zeroPlusOneUlp, zeroMinusOneUlp, 2));
|
||||
|
||||
// Against zero
|
||||
Assert.IsFalse(FloatHelper.AreAlmostEqual(zero, zeroPlusOneUlp, 0));
|
||||
Assert.IsTrue(FloatHelper.AreAlmostEqual(zero, zeroPlusOneUlp, 1));
|
||||
Assert.IsFalse(FloatHelper.AreAlmostEqual(zero, zeroMinusOneUlp, 0));
|
||||
Assert.IsTrue(FloatHelper.AreAlmostEqual(zero, zeroMinusOneUlp, 1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the negative floating point zero is within one ulp of the positive
|
||||
/// floating point zero and vice versa
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void NegativeZeroFloatEqualsPositiveZero() {
|
||||
Assert.IsTrue(
|
||||
FloatHelper.AreAlmostEqual(
|
||||
FloatHelper.NegativeZeroFloat, FloatHelper.PositiveZeroFloat, 0
|
||||
)
|
||||
);
|
||||
Assert.IsTrue(
|
||||
FloatHelper.AreAlmostEqual(
|
||||
FloatHelper.PositiveZeroFloat, FloatHelper.NegativeZeroFloat, 0
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that floats can be compared across the zero boundary</summary>
|
||||
[Test]
|
||||
public void FloatsCanBeComparedAcrossZeroInUlps() {
|
||||
float tenUlps = float.Epsilon * 10.0f;
|
||||
|
||||
Assert.IsTrue(FloatHelper.AreAlmostEqual(-tenUlps, tenUlps, 20));
|
||||
Assert.IsTrue(FloatHelper.AreAlmostEqual(tenUlps, -tenUlps, 20));
|
||||
Assert.IsFalse(FloatHelper.AreAlmostEqual(-tenUlps, tenUlps, 19));
|
||||
|
||||
Assert.IsTrue(FloatHelper.AreAlmostEqual(-tenUlps, 0, 10));
|
||||
Assert.IsTrue(FloatHelper.AreAlmostEqual(0, -tenUlps, 10));
|
||||
Assert.IsFalse(FloatHelper.AreAlmostEqual(-tenUlps, 0, 9));
|
||||
|
||||
Assert.IsTrue(FloatHelper.AreAlmostEqual(0, tenUlps, 10));
|
||||
Assert.IsTrue(FloatHelper.AreAlmostEqual(tenUlps, 0, 10));
|
||||
Assert.IsFalse(FloatHelper.AreAlmostEqual(0, tenUlps, 9));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that two denormalized doubles can be compared in ulps
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DenormalizedDoublesCanBeCompared() {
|
||||
double zero = 0.0;
|
||||
double zeroPlusOneUlp = FloatHelper.ReinterpretAsDouble(
|
||||
FloatHelper.ReinterpretAsLong(zero) + 1
|
||||
);
|
||||
double zeroMinusOneUlp = -zeroPlusOneUlp;
|
||||
|
||||
// Across zero
|
||||
Assert.IsFalse(FloatHelper.AreAlmostEqual(zeroMinusOneUlp, zeroPlusOneUlp, 1));
|
||||
Assert.IsTrue(FloatHelper.AreAlmostEqual(zeroPlusOneUlp, zeroMinusOneUlp, 2));
|
||||
|
||||
// Against zero
|
||||
Assert.IsFalse(FloatHelper.AreAlmostEqual(zero, zeroPlusOneUlp, 0));
|
||||
Assert.IsTrue(FloatHelper.AreAlmostEqual(zero, zeroPlusOneUlp, 1));
|
||||
Assert.IsFalse(FloatHelper.AreAlmostEqual(zero, zeroMinusOneUlp, 0));
|
||||
Assert.IsTrue(FloatHelper.AreAlmostEqual(zero, zeroMinusOneUlp, 1));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the negative double precision floating point zero is within one ulp
|
||||
/// of the positive double precision floating point zero and vice versa
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void NegativeZeroDoubleEqualsPositiveZero() {
|
||||
Assert.IsTrue(
|
||||
FloatHelper.AreAlmostEqual(
|
||||
FloatHelper.NegativeZeroDouble, FloatHelper.NegativeZeroDouble, 0
|
||||
)
|
||||
);
|
||||
Assert.IsTrue(
|
||||
FloatHelper.AreAlmostEqual(
|
||||
FloatHelper.NegativeZeroDouble, FloatHelper.NegativeZeroDouble, 0
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that doubles can be compared across the zero boundary</summary>
|
||||
[Test]
|
||||
public void DoublesCanBeComparedAcrossZeroInUlps() {
|
||||
double tenUlps = double.Epsilon * 10.0;
|
||||
|
||||
Assert.IsTrue(FloatHelper.AreAlmostEqual(-tenUlps, tenUlps, 20));
|
||||
Assert.IsTrue(FloatHelper.AreAlmostEqual(tenUlps, -tenUlps, 20));
|
||||
Assert.IsFalse(FloatHelper.AreAlmostEqual(-tenUlps, tenUlps, 19));
|
||||
|
||||
Assert.IsTrue(FloatHelper.AreAlmostEqual(-tenUlps, 0, 10));
|
||||
Assert.IsTrue(FloatHelper.AreAlmostEqual(0, -tenUlps, 10));
|
||||
Assert.IsFalse(FloatHelper.AreAlmostEqual(-tenUlps, 0, 9));
|
||||
|
||||
Assert.IsTrue(FloatHelper.AreAlmostEqual(0, tenUlps, 10));
|
||||
Assert.IsTrue(FloatHelper.AreAlmostEqual(tenUlps, 0, 10));
|
||||
Assert.IsFalse(FloatHelper.AreAlmostEqual(0, tenUlps, 9));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,561 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.IO {
|
||||
|
||||
/// <summary>Unit Test for the stream chainer</summary>
|
||||
[TestFixture]
|
||||
internal class ChainStreamTest {
|
||||
|
||||
#region class TestStream
|
||||
|
||||
/// <summary>Testing stream that allows specific features to be disabled</summary>
|
||||
private class TestStream : Stream {
|
||||
|
||||
/// <summary>Initializes a new test stream</summary>
|
||||
/// <param name="wrappedStream">Stream that will be wrapped</param>
|
||||
/// <param name="allowRead">Whether to allow reading from the stream</param>
|
||||
/// <param name="allowWrite">Whether to allow writing to the stream</param>
|
||||
/// <param name="allowSeek">Whether to allow seeking within the stream</param>
|
||||
public TestStream(
|
||||
Stream wrappedStream, bool allowRead, bool allowWrite, bool allowSeek
|
||||
) {
|
||||
this.stream = wrappedStream;
|
||||
this.readAllowed = allowRead;
|
||||
this.writeAllowed = allowWrite;
|
||||
this.seekAllowed = allowSeek;
|
||||
}
|
||||
|
||||
/// <summary>Whether data can be read from the stream</summary>
|
||||
public override bool CanRead {
|
||||
get { return this.readAllowed; }
|
||||
}
|
||||
|
||||
/// <summary>Whether the stream supports seeking</summary>
|
||||
public override bool CanSeek {
|
||||
get { return this.seekAllowed; }
|
||||
}
|
||||
|
||||
/// <summary>Whether data can be written into the stream</summary>
|
||||
public override bool CanWrite {
|
||||
get { return this.writeAllowed; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all buffers for this stream and causes any buffered data to be written
|
||||
/// to the underlying device.
|
||||
/// </summary>
|
||||
public override void Flush() {
|
||||
++this.flushCallCount;
|
||||
this.stream.Flush();
|
||||
}
|
||||
|
||||
/// <summary>Length of the stream in bytes</summary>
|
||||
public override long Length {
|
||||
get {
|
||||
enforceSeekAllowed();
|
||||
return this.stream.Length;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Absolute position of the file pointer within the stream</summary>
|
||||
/// <exception cref="NotSupportedException">
|
||||
/// At least one of the chained streams does not support seeking
|
||||
/// </exception>
|
||||
public override long Position {
|
||||
get {
|
||||
enforceSeekAllowed();
|
||||
return this.stream.Position;
|
||||
}
|
||||
set {
|
||||
enforceSeekAllowed();
|
||||
this.stream.Position = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a sequence of bytes from the stream and advances the position of
|
||||
/// the file pointer by the number of bytes read.
|
||||
/// </summary>
|
||||
/// <param name="buffer">Buffer that will receive the data read from the stream</param>
|
||||
/// <param name="offset">
|
||||
/// Offset in the buffer at which the stream will place the data read
|
||||
/// </param>
|
||||
/// <param name="count">Maximum number of bytes that will be read</param>
|
||||
/// <returns>
|
||||
/// The number of bytes that were actually read from the stream and written into
|
||||
/// the provided buffer
|
||||
/// </returns>
|
||||
public override int Read(byte[] buffer, int offset, int count) {
|
||||
enforceReadAllowed();
|
||||
return this.stream.Read(buffer, offset, count);
|
||||
}
|
||||
|
||||
/// <summary>Changes the position of the file pointer</summary>
|
||||
/// <param name="offset">
|
||||
/// Offset to move the file pointer by, relative to the position indicated by
|
||||
/// the <paramref name="origin" /> parameter.
|
||||
/// </param>
|
||||
/// <param name="origin">
|
||||
/// Reference point relative to which the file pointer is placed
|
||||
/// </param>
|
||||
/// <returns>The new absolute position within the stream</returns>
|
||||
public override long Seek(long offset, SeekOrigin origin) {
|
||||
enforceSeekAllowed();
|
||||
return this.stream.Seek(offset, origin);
|
||||
}
|
||||
|
||||
/// <summary>Changes the length of the stream</summary>
|
||||
/// <param name="value">New length the stream shall have</param>
|
||||
public override void SetLength(long value) {
|
||||
enforceSeekAllowed();
|
||||
this.stream.SetLength(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a sequence of bytes to the stream and advances the position of
|
||||
/// the file pointer by the number of bytes written.
|
||||
/// </summary>
|
||||
/// <param name="buffer">
|
||||
/// Buffer containing the data that will be written to the stream
|
||||
/// </param>
|
||||
/// <param name="offset">
|
||||
/// Offset in the buffer at which the data to be written starts
|
||||
/// </param>
|
||||
/// <param name="count">Number of bytes that will be written into the stream</param>
|
||||
public override void Write(byte[] buffer, int offset, int count) {
|
||||
enforceWriteAllowed();
|
||||
this.stream.Write(buffer, offset, count);
|
||||
}
|
||||
|
||||
/// <summary>Number of times the Flush() method has been called</summary>
|
||||
public int FlushCallCount {
|
||||
get { return this.flushCallCount; }
|
||||
}
|
||||
|
||||
/// <summary>Throws an exception if reading is not allowed</summary>
|
||||
private void enforceReadAllowed() {
|
||||
if(!this.readAllowed) {
|
||||
throw new NotSupportedException("Reading has been disabled");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Throws an exception if writing is not allowed</summary>
|
||||
private void enforceWriteAllowed() {
|
||||
if(!this.writeAllowed) {
|
||||
throw new NotSupportedException("Writing has been disabled");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Throws an exception if seeking is not allowed</summary>
|
||||
private void enforceSeekAllowed() {
|
||||
if(!this.seekAllowed) {
|
||||
throw new NotSupportedException("Seeking has been disabled");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Stream being wrapped for testing</summary>
|
||||
private Stream stream;
|
||||
/// <summary>whether to allow reading from the wrapped stream</summary>
|
||||
private bool readAllowed;
|
||||
/// <summary>Whether to allow writing to the wrapped stream</summary>
|
||||
private bool writeAllowed;
|
||||
/// <summary>Whether to allow seeking within the wrapped stream</summary>
|
||||
private bool seekAllowed;
|
||||
/// <summary>Number of times the Flush() method has been called</summary>
|
||||
private int flushCallCount;
|
||||
|
||||
}
|
||||
|
||||
#endregion // class TestStream
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the stream chainer correctly partitions a long write request
|
||||
/// over its chained streams and appends any remaining data to the end of
|
||||
/// the last chained stream.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestPartitionedWrite() {
|
||||
ChainStream chainer = chainTwoStreamsOfTenBytes();
|
||||
|
||||
byte[] testData = new byte[20];
|
||||
for(int index = 0; index < testData.Length; ++index) {
|
||||
testData[index] = (byte)(index + 1);
|
||||
}
|
||||
|
||||
chainer.Position = 5;
|
||||
chainer.Write(testData, 0, testData.Length);
|
||||
|
||||
Assert.AreEqual(
|
||||
new byte[] { 0, 0, 0, 0, 0, 1, 2, 3, 4, 5 },
|
||||
((MemoryStream)chainer.ChainedStreams[0]).ToArray()
|
||||
);
|
||||
Assert.AreEqual(
|
||||
new byte[] { 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 },
|
||||
((MemoryStream)chainer.ChainedStreams[1]).ToArray()
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the stream chainer correctly partitions a long read request
|
||||
/// over its chained streams.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestPartitionedRead() {
|
||||
ChainStream chainer = chainTwoStreamsOfTenBytes();
|
||||
|
||||
((MemoryStream)chainer.ChainedStreams[0]).Write(
|
||||
new byte[] { 1, 2, 3, 4, 5 }, 0, 5
|
||||
);
|
||||
((MemoryStream)chainer.ChainedStreams[1]).Write(
|
||||
new byte[] { 6, 7, 8, 9, 10 }, 0, 5
|
||||
);
|
||||
|
||||
chainer.Position = 3;
|
||||
byte[] buffer = new byte[15];
|
||||
int bytesRead = chainer.Read(buffer, 0, 14);
|
||||
|
||||
Assert.AreEqual(14, bytesRead);
|
||||
Assert.AreEqual(new byte[] { 4, 5, 0, 0, 0, 0, 0, 6, 7, 8, 9, 10, 0, 0, 0 }, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the stream chainer can handle a stream resize
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestWriteAfterResize() {
|
||||
ChainStream chainer = chainTwoStreamsOfTenBytes();
|
||||
|
||||
// The first stream has a size of 10 bytes, so this goes into the second stream
|
||||
chainer.Position = 11;
|
||||
chainer.Write(new byte[] { 12, 34 }, 0, 2);
|
||||
|
||||
// Now we resize the first stream to 15 bytes, so this goes into the first stream
|
||||
((MemoryStream)chainer.ChainedStreams[0]).SetLength(15);
|
||||
chainer.Write(new byte[] { 56, 78, 11, 22 }, 0, 4);
|
||||
|
||||
Assert.AreEqual(
|
||||
new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 78 },
|
||||
((MemoryStream)chainer.ChainedStreams[0]).ToArray()
|
||||
);
|
||||
Assert.AreEqual(
|
||||
new byte[] { 11, 22, 34, 0, 0, 0, 0, 0, 0, 0 },
|
||||
((MemoryStream)chainer.ChainedStreams[1]).ToArray()
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests writing to a stream chainer that contains an unseekable stream
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestWriteToUnseekableStream() {
|
||||
MemoryStream firstStream = new MemoryStream();
|
||||
|
||||
// Now the second stream _does_ support seeking. If the stream chainer ignores
|
||||
// that, it would overwrite data in the second stream.
|
||||
MemoryStream secondStream = new MemoryStream();
|
||||
secondStream.Write(new byte[] { 0, 9, 8, 7, 6 }, 0, 5);
|
||||
secondStream.Position = 0;
|
||||
|
||||
TestStream testStream = new TestStream(firstStream, true, true, false);
|
||||
ChainStream chainer = new ChainStream(new Stream[] { testStream, secondStream });
|
||||
|
||||
chainer.Write(new byte[] { 1, 2, 3, 4, 5 }, 0, 5);
|
||||
Assert.IsFalse(chainer.CanSeek);
|
||||
Assert.AreEqual(0, firstStream.Length);
|
||||
Assert.AreEqual(new byte[] { 0, 9, 8, 7, 6, 1, 2, 3, 4, 5 }, secondStream.ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests reading from a stream chainer that contains an unseekable stream
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestReadFromUnseekableStream() {
|
||||
MemoryStream firstStream = new MemoryStream();
|
||||
|
||||
// Now the second stream _does_ support seeking. If the stream chainer ignores
|
||||
// that, it would overwrite data in the second stream.
|
||||
MemoryStream secondStream = new MemoryStream();
|
||||
secondStream.Write(new byte[] { 0, 9, 8, 7, 6 }, 0, 5);
|
||||
secondStream.Position = 3;
|
||||
|
||||
TestStream testStream = new TestStream(firstStream, true, true, false);
|
||||
ChainStream chainer = new ChainStream(new Stream[] { testStream, secondStream });
|
||||
|
||||
Assert.IsFalse(chainer.CanSeek);
|
||||
|
||||
byte[] buffer = new byte[5];
|
||||
int readByteCount = chainer.Read(buffer, 0, 3);
|
||||
|
||||
Assert.AreEqual(3, readByteCount);
|
||||
Assert.AreEqual(new byte[] { 0, 9, 8, 0, 0 }, buffer);
|
||||
|
||||
readByteCount = chainer.Read(buffer, 0, 3);
|
||||
|
||||
Assert.AreEqual(2, readByteCount);
|
||||
Assert.AreEqual(new byte[] { 7, 6, 8, 0, 0 }, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests reading from a stream chainer that contains an unreadable stream
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnReadFromUnreadableStream() {
|
||||
MemoryStream memoryStream = new MemoryStream();
|
||||
TestStream testStream = new TestStream(memoryStream, false, true, true);
|
||||
ChainStream chainer = new ChainStream(new Stream[] { testStream });
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { chainer.Read(new byte[5], 0, 5); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests writing to a stream chainer that contains an unwriteable stream
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnWriteToUnwriteableStream() {
|
||||
MemoryStream memoryStream = new MemoryStream();
|
||||
TestStream testStream = new TestStream(memoryStream, true, false, true);
|
||||
ChainStream chainer = new ChainStream(new Stream[] { testStream });
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { chainer.Write(new byte[] { 1, 2, 3, 4, 5 }, 0, 5); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the stream chainer throws an exception if the attempt is
|
||||
/// made to change the length of the stream
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnLengthChange() {
|
||||
ChainStream chainer = chainTwoStreamsOfTenBytes();
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { chainer.SetLength(123); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the CanRead property is correctly determined by the stream chainer
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCanRead() {
|
||||
MemoryStream yesStream = new MemoryStream();
|
||||
TestStream noStream = new TestStream(yesStream, false, true, true);
|
||||
|
||||
Stream[] yesGroup = new Stream[] { yesStream, yesStream, yesStream, yesStream };
|
||||
Stream[] partialGroup = new Stream[] { yesStream, yesStream, noStream, yesStream };
|
||||
Stream[] noGroup = new Stream[] { noStream, noStream, noStream, noStream };
|
||||
|
||||
Assert.IsTrue(new ChainStream(yesGroup).CanRead);
|
||||
Assert.IsFalse(new ChainStream(partialGroup).CanRead);
|
||||
Assert.IsFalse(new ChainStream(noGroup).CanRead);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the CanRead property is correctly determined by the stream chainer
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCanWrite() {
|
||||
MemoryStream yesStream = new MemoryStream();
|
||||
TestStream noStream = new TestStream(yesStream, true, false, true);
|
||||
|
||||
Stream[] yesGroup = new Stream[] { yesStream, yesStream, yesStream, yesStream };
|
||||
Stream[] partialGroup = new Stream[] { yesStream, yesStream, noStream, yesStream };
|
||||
Stream[] noGroup = new Stream[] { noStream, noStream, noStream, noStream };
|
||||
|
||||
Assert.IsTrue(new ChainStream(yesGroup).CanWrite);
|
||||
Assert.IsFalse(new ChainStream(partialGroup).CanWrite);
|
||||
Assert.IsFalse(new ChainStream(noGroup).CanWrite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the CanSeek property is correctly determined by the stream chainer
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCanSeek() {
|
||||
MemoryStream yesStream = new MemoryStream();
|
||||
TestStream noStream = new TestStream(yesStream, true, true, false);
|
||||
|
||||
Stream[] yesGroup = new Stream[] { yesStream, yesStream, yesStream, yesStream };
|
||||
Stream[] partialGroup = new Stream[] { yesStream, yesStream, noStream, yesStream };
|
||||
Stream[] noGroup = new Stream[] { noStream, noStream, noStream, noStream };
|
||||
|
||||
Assert.IsTrue(new ChainStream(yesGroup).CanSeek);
|
||||
Assert.IsFalse(new ChainStream(partialGroup).CanSeek);
|
||||
Assert.IsFalse(new ChainStream(noGroup).CanSeek);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether an exception is thrown if the Seek() method is called on
|
||||
/// a stream chainer with streams that do not support seeking
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnSeekWithUnseekableStream() {
|
||||
MemoryStream memoryStream = new MemoryStream();
|
||||
TestStream testStream = new TestStream(memoryStream, true, true, false);
|
||||
|
||||
ChainStream chainer = new ChainStream(new Stream[] { testStream });
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { chainer.Seek(123, SeekOrigin.Begin); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether an exception is thrown if the Position property is retrieved
|
||||
/// on a stream chainer with streams that do not support seeking
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnGetPositionWithUnseekableStream() {
|
||||
MemoryStream memoryStream = new MemoryStream();
|
||||
TestStream testStream = new TestStream(memoryStream, true, true, false);
|
||||
|
||||
ChainStream chainer = new ChainStream(new Stream[] { testStream });
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { Console.WriteLine(chainer.Position); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether an exception is thrown if the Position property is set
|
||||
/// on a stream chainer with streams that do not support seeking
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnSetPositionWithUnseekableStream() {
|
||||
MemoryStream memoryStream = new MemoryStream();
|
||||
TestStream testStream = new TestStream(memoryStream, true, true, false);
|
||||
|
||||
ChainStream chainer = new ChainStream(new Stream[] { testStream });
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { chainer.Position = 123; }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether an exception is thrown if the Length property is retrieved
|
||||
/// on a stream chainer with streams that do not support seeking
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnGetLengthWithUnseekableStream() {
|
||||
MemoryStream memoryStream = new MemoryStream();
|
||||
TestStream testStream = new TestStream(memoryStream, true, true, false);
|
||||
|
||||
ChainStream chainer = new ChainStream(new Stream[] { testStream });
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { Assert.IsTrue(chainer.Length != chainer.Length); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the Seek() method of the stream chainer is working
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestSeeking() {
|
||||
ChainStream chainer = chainTwoStreamsOfTenBytes();
|
||||
|
||||
Assert.AreEqual(7, chainer.Seek(-13, SeekOrigin.End));
|
||||
Assert.AreEqual(14, chainer.Seek(7, SeekOrigin.Current));
|
||||
Assert.AreEqual(11, chainer.Seek(11, SeekOrigin.Begin));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the stream behaves correctly if data is read from beyond its end
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestReadBeyondEndOfStream() {
|
||||
ChainStream chainer = chainTwoStreamsOfTenBytes();
|
||||
chainer.Seek(10, SeekOrigin.End);
|
||||
|
||||
// This is how the MemoryStream behaves: it returns 0 bytes.
|
||||
int readByteCount = chainer.Read(new byte[1], 0, 1);
|
||||
Assert.AreEqual(0, readByteCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the Seek() method throws an exception if an invalid
|
||||
/// reference point is provided
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnInvalidSeekReferencePoint() {
|
||||
ChainStream chainer = chainTwoStreamsOfTenBytes();
|
||||
Assert.Throws<ArgumentException>(
|
||||
delegate() { chainer.Seek(1, (SeekOrigin)12345); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the position property works correctly</summary>
|
||||
[Test]
|
||||
public void TestPositionChange() {
|
||||
ChainStream chainer = chainTwoStreamsOfTenBytes();
|
||||
|
||||
chainer.Position = 7;
|
||||
Assert.AreEqual(chainer.Position, 7);
|
||||
chainer.Position = 14;
|
||||
Assert.AreEqual(chainer.Position, 14);
|
||||
}
|
||||
|
||||
/// <summary>Tests the Flush() method of the stream chainer</summary>
|
||||
[Test]
|
||||
public void TestFlush() {
|
||||
MemoryStream firstStream = new MemoryStream();
|
||||
TestStream firstTestStream = new TestStream(firstStream, true, true, true);
|
||||
|
||||
MemoryStream secondStream = new MemoryStream();
|
||||
TestStream secondTestStream = new TestStream(secondStream, true, true, true);
|
||||
|
||||
ChainStream chainer = new ChainStream(
|
||||
new Stream[] { firstTestStream, secondTestStream }
|
||||
);
|
||||
|
||||
Assert.AreEqual(0, firstTestStream.FlushCallCount);
|
||||
Assert.AreEqual(0, secondTestStream.FlushCallCount);
|
||||
|
||||
chainer.Flush();
|
||||
|
||||
Assert.AreEqual(1, firstTestStream.FlushCallCount);
|
||||
Assert.AreEqual(1, secondTestStream.FlushCallCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a stream chainer with two streams that each have a size of 10 bytes
|
||||
/// </summary>
|
||||
/// <returns>The new stream chainer with two chained 10-byte streams</returns>
|
||||
private static ChainStream chainTwoStreamsOfTenBytes() {
|
||||
MemoryStream firstStream = new MemoryStream(10);
|
||||
MemoryStream secondStream = new MemoryStream(10);
|
||||
|
||||
firstStream.SetLength(10);
|
||||
secondStream.SetLength(10);
|
||||
|
||||
return new ChainStream(
|
||||
new Stream[] { firstStream, secondStream }
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.IO
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,526 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.IO {
|
||||
|
||||
/// <summary>Unit Test for the partial stream</summary>
|
||||
[TestFixture]
|
||||
internal class PartialStreamTest {
|
||||
|
||||
#region class TestStream
|
||||
|
||||
/// <summary>Testing stream that allows specific features to be disabled</summary>
|
||||
private class TestStream : Stream {
|
||||
|
||||
/// <summary>Initializes a new test stream</summary>
|
||||
/// <param name="wrappedStream">Stream that will be wrapped</param>
|
||||
/// <param name="allowRead">Whether to allow reading from the stream</param>
|
||||
/// <param name="allowWrite">Whether to allow writing to the stream</param>
|
||||
/// <param name="allowSeek">Whether to allow seeking within the stream</param>
|
||||
public TestStream(
|
||||
Stream wrappedStream, bool allowRead, bool allowWrite, bool allowSeek
|
||||
) {
|
||||
this.stream = wrappedStream;
|
||||
this.readAllowed = allowRead;
|
||||
this.writeAllowed = allowWrite;
|
||||
this.seekAllowed = allowSeek;
|
||||
}
|
||||
|
||||
/// <summary>Whether data can be read from the stream</summary>
|
||||
public override bool CanRead {
|
||||
get { return this.readAllowed; }
|
||||
}
|
||||
|
||||
/// <summary>Whether the stream supports seeking</summary>
|
||||
public override bool CanSeek {
|
||||
get { return this.seekAllowed; }
|
||||
}
|
||||
|
||||
/// <summary>Whether data can be written into the stream</summary>
|
||||
public override bool CanWrite {
|
||||
get { return this.writeAllowed; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all buffers for this stream and causes any buffered data to be written
|
||||
/// to the underlying device.
|
||||
/// </summary>
|
||||
public override void Flush() {
|
||||
++this.flushCallCount;
|
||||
this.stream.Flush();
|
||||
}
|
||||
|
||||
/// <summary>Length of the stream in bytes</summary>
|
||||
public override long Length {
|
||||
get {
|
||||
enforceSeekAllowed();
|
||||
return this.stream.Length;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Absolute position of the file pointer within the stream</summary>
|
||||
/// <exception cref="NotSupportedException">
|
||||
/// At least one of the chained streams does not support seeking
|
||||
/// </exception>
|
||||
public override long Position {
|
||||
get {
|
||||
enforceSeekAllowed();
|
||||
return this.stream.Position;
|
||||
}
|
||||
set {
|
||||
enforceSeekAllowed();
|
||||
this.stream.Position = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a sequence of bytes from the stream and advances the position of
|
||||
/// the file pointer by the number of bytes read.
|
||||
/// </summary>
|
||||
/// <param name="buffer">Buffer that will receive the data read from the stream</param>
|
||||
/// <param name="offset">
|
||||
/// Offset in the buffer at which the stream will place the data read
|
||||
/// </param>
|
||||
/// <param name="count">Maximum number of bytes that will be read</param>
|
||||
/// <returns>
|
||||
/// The number of bytes that were actually read from the stream and written into
|
||||
/// the provided buffer
|
||||
/// </returns>
|
||||
public override int Read(byte[] buffer, int offset, int count) {
|
||||
enforceReadAllowed();
|
||||
return this.stream.Read(buffer, offset, count);
|
||||
}
|
||||
|
||||
/// <summary>Changes the position of the file pointer</summary>
|
||||
/// <param name="offset">
|
||||
/// Offset to move the file pointer by, relative to the position indicated by
|
||||
/// the <paramref name="origin" /> parameter.
|
||||
/// </param>
|
||||
/// <param name="origin">
|
||||
/// Reference point relative to which the file pointer is placed
|
||||
/// </param>
|
||||
/// <returns>The new absolute position within the stream</returns>
|
||||
public override long Seek(long offset, SeekOrigin origin) {
|
||||
enforceSeekAllowed();
|
||||
return this.stream.Seek(offset, origin);
|
||||
}
|
||||
|
||||
/// <summary>Changes the length of the stream</summary>
|
||||
/// <param name="value">New length the stream shall have</param>
|
||||
public override void SetLength(long value) {
|
||||
enforceSeekAllowed();
|
||||
this.stream.SetLength(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a sequence of bytes to the stream and advances the position of
|
||||
/// the file pointer by the number of bytes written.
|
||||
/// </summary>
|
||||
/// <param name="buffer">
|
||||
/// Buffer containing the data that will be written to the stream
|
||||
/// </param>
|
||||
/// <param name="offset">
|
||||
/// Offset in the buffer at which the data to be written starts
|
||||
/// </param>
|
||||
/// <param name="count">Number of bytes that will be written into the stream</param>
|
||||
public override void Write(byte[] buffer, int offset, int count) {
|
||||
enforceWriteAllowed();
|
||||
this.stream.Write(buffer, offset, count);
|
||||
}
|
||||
|
||||
/// <summary>Number of times the Flush() method has been called</summary>
|
||||
public int FlushCallCount {
|
||||
get { return this.flushCallCount; }
|
||||
}
|
||||
|
||||
/// <summary>Throws an exception if reading is not allowed</summary>
|
||||
private void enforceReadAllowed() {
|
||||
if(!this.readAllowed) {
|
||||
throw new NotSupportedException("Reading has been disabled");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Throws an exception if writing is not allowed</summary>
|
||||
private void enforceWriteAllowed() {
|
||||
if(!this.writeAllowed) {
|
||||
throw new NotSupportedException("Writing has been disabled");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Throws an exception if seeking is not allowed</summary>
|
||||
private void enforceSeekAllowed() {
|
||||
if(!this.seekAllowed) {
|
||||
throw new NotSupportedException("Seeking has been disabled");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Stream being wrapped for testing</summary>
|
||||
private Stream stream;
|
||||
/// <summary>whether to allow reading from the wrapped stream</summary>
|
||||
private bool readAllowed;
|
||||
/// <summary>Whether to allow writing to the wrapped stream</summary>
|
||||
private bool writeAllowed;
|
||||
/// <summary>Whether to allow seeking within the wrapped stream</summary>
|
||||
private bool seekAllowed;
|
||||
/// <summary>Number of times the Flush() method has been called</summary>
|
||||
private int flushCallCount;
|
||||
|
||||
}
|
||||
|
||||
#endregion // class TestStream
|
||||
|
||||
/// <summary>Tests whether the partial stream constructor is working</summary>
|
||||
[Test]
|
||||
public void TestConstructor() {
|
||||
using(MemoryStream memoryStream = new MemoryStream()) {
|
||||
memoryStream.SetLength(123);
|
||||
PartialStream partialStream = new PartialStream(memoryStream, 23, 100);
|
||||
Assert.AreEqual(100, partialStream.Length);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the partial stream constructor throws an exception if
|
||||
/// it's invoked with an invalid start offset
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnInvalidStartInConstructor() {
|
||||
using(MemoryStream memoryStream = new MemoryStream()) {
|
||||
memoryStream.SetLength(123);
|
||||
Assert.Throws<ArgumentException>(
|
||||
delegate() { Console.WriteLine(new PartialStream(memoryStream, -1, 10)); }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the partial stream constructor throws an exception if
|
||||
/// it's invoked with an invalid start offset
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnInvalidLengthInConstructor() {
|
||||
using(MemoryStream memoryStream = new MemoryStream()) {
|
||||
memoryStream.SetLength(123);
|
||||
Assert.Throws<ArgumentException>(
|
||||
delegate() { Console.WriteLine(new PartialStream(memoryStream, 100, 24)); }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the partial stream constructor throws an exception if
|
||||
/// it's invoked with a start offset on an unseekable stream
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnUnseekableStreamWithOffsetInConstructor() {
|
||||
using(MemoryStream memoryStream = new MemoryStream()) {
|
||||
memoryStream.SetLength(123);
|
||||
TestStream testStream = new TestStream(memoryStream, true, true, false);
|
||||
Assert.Throws<ArgumentException>(
|
||||
delegate() { Console.WriteLine(new PartialStream(testStream, 23, 100)); }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the CanRead property reports its status correctly
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCanReadProperty() {
|
||||
using(MemoryStream memoryStream = new MemoryStream()) {
|
||||
TestStream yesStream = new TestStream(memoryStream, true, true, true);
|
||||
TestStream noStream = new TestStream(memoryStream, false, true, true);
|
||||
|
||||
Assert.IsTrue(new PartialStream(yesStream, 0, 0).CanRead);
|
||||
Assert.IsFalse(new PartialStream(noStream, 0, 0).CanRead);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the CanWrite property reports its status correctly
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCanWriteProperty() {
|
||||
using(MemoryStream memoryStream = new MemoryStream()) {
|
||||
TestStream yesStream = new TestStream(memoryStream, true, true, true);
|
||||
TestStream noStream = new TestStream(memoryStream, true, false, true);
|
||||
|
||||
Assert.IsTrue(new PartialStream(yesStream, 0, 0).CanWrite);
|
||||
Assert.IsFalse(new PartialStream(noStream, 0, 0).CanWrite);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the CanSeek property reports its status correctly
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCanSeekProperty() {
|
||||
using(MemoryStream memoryStream = new MemoryStream()) {
|
||||
TestStream yesStream = new TestStream(memoryStream, true, true, true);
|
||||
TestStream noStream = new TestStream(memoryStream, true, true, false);
|
||||
|
||||
Assert.IsTrue(new PartialStream(yesStream, 0, 0).CanSeek);
|
||||
Assert.IsFalse(new PartialStream(noStream, 0, 0).CanSeek);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the CompleteStream property returns the original stream
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCompleteStreamProperty() {
|
||||
using(MemoryStream memoryStream = new MemoryStream()) {
|
||||
PartialStream partialStream = new PartialStream(memoryStream, 0, 0);
|
||||
Assert.AreSame(memoryStream, partialStream.CompleteStream);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Tests whether the Flush() method can be called</summary>
|
||||
[Test]
|
||||
public void TestFlush() {
|
||||
using(MemoryStream memoryStream = new MemoryStream()) {
|
||||
PartialStream partialStream = new PartialStream(memoryStream, 0, 0);
|
||||
partialStream.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the Position property correctly reports the file pointer position
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestGetPosition() {
|
||||
using(MemoryStream memoryStream = new MemoryStream()) {
|
||||
memoryStream.SetLength(123);
|
||||
PartialStream partialStream = new PartialStream(memoryStream, 23, 100);
|
||||
|
||||
Assert.AreEqual(0, partialStream.Position);
|
||||
|
||||
byte[] test = new byte[10];
|
||||
int bytesRead = partialStream.Read(test, 0, 10);
|
||||
|
||||
Assert.AreEqual(10, bytesRead);
|
||||
Assert.AreEqual(10, partialStream.Position);
|
||||
|
||||
bytesRead = partialStream.Read(test, 0, 10);
|
||||
|
||||
Assert.AreEqual(10, bytesRead);
|
||||
Assert.AreEqual(20, partialStream.Position);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the Position property is correctly updated
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestSetPosition() {
|
||||
using(MemoryStream memoryStream = new MemoryStream()) {
|
||||
memoryStream.SetLength(123);
|
||||
PartialStream partialStream = new PartialStream(memoryStream, 23, 100);
|
||||
|
||||
Assert.AreEqual(0, partialStream.Position);
|
||||
partialStream.Position = 7;
|
||||
Assert.AreEqual(partialStream.Position, 7);
|
||||
partialStream.Position = 14;
|
||||
Assert.AreEqual(partialStream.Position, 14);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the Position property throws an exception if the stream does
|
||||
/// not support seeking.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnGetPositionOnUnseekableStream() {
|
||||
using(MemoryStream memoryStream = new MemoryStream()) {
|
||||
TestStream testStream = new TestStream(memoryStream, true, true, false);
|
||||
|
||||
PartialStream partialStream = new PartialStream(testStream, 0, 0);
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { Console.WriteLine(partialStream.Position); }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the Position property throws an exception if the stream does
|
||||
/// not support seeking.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnSetPositionOnUnseekableStream() {
|
||||
using(MemoryStream memoryStream = new MemoryStream()) {
|
||||
TestStream testStream = new TestStream(memoryStream, true, true, false);
|
||||
|
||||
PartialStream partialStream = new PartialStream(testStream, 0, 0);
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { partialStream.Position = 0; }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the Read() method throws an exception if the stream does
|
||||
/// not support reading
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnReadFromUnreadableStream() {
|
||||
using(MemoryStream memoryStream = new MemoryStream()) {
|
||||
TestStream testStream = new TestStream(memoryStream, false, true, true);
|
||||
PartialStream partialStream = new PartialStream(testStream, 0, 0);
|
||||
|
||||
byte[] test = new byte[10];
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { Console.WriteLine(partialStream.Read(test, 0, 10)); }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the Seek() method of the partial stream is working
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestSeeking() {
|
||||
using(MemoryStream memoryStream = new MemoryStream()) {
|
||||
memoryStream.SetLength(20);
|
||||
PartialStream partialStream = new PartialStream(memoryStream, 0, 20);
|
||||
|
||||
Assert.AreEqual(7, partialStream.Seek(-13, SeekOrigin.End));
|
||||
Assert.AreEqual(14, partialStream.Seek(7, SeekOrigin.Current));
|
||||
Assert.AreEqual(11, partialStream.Seek(11, SeekOrigin.Begin));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the Seek() method throws an exception if an invalid
|
||||
/// reference point is provided
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnInvalidSeekReferencePoint() {
|
||||
using(MemoryStream memoryStream = new MemoryStream()) {
|
||||
PartialStream partialStream = new PartialStream(memoryStream, 0, 0);
|
||||
Assert.Throws<ArgumentException>(
|
||||
delegate() { partialStream.Seek(1, (SeekOrigin)12345); }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the partial stream throws an exception if the attempt is
|
||||
/// made to change the length of the stream
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnLengthChange() {
|
||||
using(MemoryStream memoryStream = new MemoryStream()) {
|
||||
PartialStream partialStream = new PartialStream(memoryStream, 0, 0);
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { partialStream.SetLength(123); }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the Read() method returns 0 bytes if the attempt is made
|
||||
/// to read data from an invalid position
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestReadFromInvalidPosition() {
|
||||
using(MemoryStream memoryStream = new MemoryStream()) {
|
||||
memoryStream.SetLength(123);
|
||||
|
||||
memoryStream.Position = 1123;
|
||||
byte[] test = new byte[10];
|
||||
|
||||
Assert.AreEqual(0, memoryStream.Read(test, 0, 10));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the Read() method is working</summary>
|
||||
[Test]
|
||||
public void TestReadFromPartialStream() {
|
||||
using(MemoryStream memoryStream = new MemoryStream()) {
|
||||
memoryStream.SetLength(123);
|
||||
|
||||
memoryStream.Position = 100;
|
||||
memoryStream.Write(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, 0, 10);
|
||||
|
||||
PartialStream partialStream = new PartialStream(memoryStream, 95, 10);
|
||||
|
||||
byte[] buffer = new byte[15];
|
||||
int bytesRead = partialStream.Read(buffer, 0, 15);
|
||||
|
||||
Assert.AreEqual(10, bytesRead);
|
||||
Assert.AreEqual(
|
||||
new byte[] { 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 0, 0, 0, 0, 0 }, buffer
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the Write() method is working</summary>
|
||||
[Test]
|
||||
public void TestWriteToPartialStream() {
|
||||
using(MemoryStream memoryStream = new MemoryStream()) {
|
||||
memoryStream.SetLength(123);
|
||||
|
||||
memoryStream.Position = 60;
|
||||
memoryStream.Write(new byte[] { 11, 12, 13, 14, 15 }, 0, 5);
|
||||
|
||||
PartialStream partialStream = new PartialStream(memoryStream, 50, 15);
|
||||
partialStream.Position = 3;
|
||||
partialStream.Write(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, 0, 10);
|
||||
|
||||
byte[] buffer = new byte[17];
|
||||
memoryStream.Position = 49;
|
||||
int bytesRead = memoryStream.Read(buffer, 0, 17);
|
||||
|
||||
Assert.AreEqual(17, bytesRead);
|
||||
Assert.AreEqual(
|
||||
new byte[] { 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 14, 15, 0 },
|
||||
buffer
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that an exception is thrown if the Write() method of the partial stream
|
||||
/// is attempted to be used to extend the partial stream's length
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnExtendPartialStream() {
|
||||
using(MemoryStream memoryStream = new MemoryStream()) {
|
||||
memoryStream.SetLength(25);
|
||||
|
||||
PartialStream partialStream = new PartialStream(memoryStream, 10, 10);
|
||||
partialStream.Position = 5;
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { partialStream.Write(new byte[] { 1, 2, 3, 4, 5, 6 }, 0, 6); }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.IO
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,329 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.IO {
|
||||
|
||||
/// <summary>Unit Test for the ring buffer class</summary>
|
||||
[TestFixture]
|
||||
internal class RingMemoryStreamTest {
|
||||
|
||||
/// <summary>Prepares some test data for the units test methods</summary>
|
||||
[TestFixtureSetUp]
|
||||
public void Setup() {
|
||||
this.testBytes = new byte[20];
|
||||
for(int i = 0; i < 20; ++i)
|
||||
this.testBytes[i] = (byte)i;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the ring buffer blocks write attempts that would exceed its capacity
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestWriteTooLargeChunk() {
|
||||
Assert.Throws<OverflowException>(
|
||||
delegate() { new RingMemoryStream(10).Write(this.testBytes, 0, 11); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the ring buffer still accepts write attempts that would fill the
|
||||
/// entire buffer in one go.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestWriteBarelyFittingChunk() {
|
||||
new RingMemoryStream(10).Write(this.testBytes, 0, 10);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the ring buffer correctly manages write attempts that have to
|
||||
/// be split at the end of the ring buffer
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestWriteSplitBlock() {
|
||||
RingMemoryStream testRing = new RingMemoryStream(10);
|
||||
testRing.Write(this.testBytes, 0, 8);
|
||||
testRing.Read(this.testBytes, 0, 5);
|
||||
testRing.Write(this.testBytes, 0, 7);
|
||||
|
||||
byte[] actual = new byte[10];
|
||||
testRing.Read(actual, 0, 10);
|
||||
Assert.AreEqual(new byte[] { 5, 6, 7, 0, 1, 2, 3, 4, 5, 6 }, actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the ring buffer correctly manages write attempts that write into
|
||||
/// the gap after the ring buffer's data has become split
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestWriteSplitAndLinearBlock() {
|
||||
RingMemoryStream testRing = new RingMemoryStream(10);
|
||||
testRing.Write(this.testBytes, 0, 8);
|
||||
testRing.Read(this.testBytes, 0, 5);
|
||||
testRing.Write(this.testBytes, 0, 5);
|
||||
testRing.Write(this.testBytes, 0, 2);
|
||||
|
||||
byte[] actual = new byte[10];
|
||||
testRing.Read(actual, 0, 10);
|
||||
Assert.AreEqual(new byte[] { 5, 6, 7, 0, 1, 2, 3, 4, 0, 1 }, actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the ring buffer still detects write that would exceed its capacity
|
||||
/// if they write into the gap after the ring buffer's data has become split
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestWriteSplitAndLinearTooLargeBlock() {
|
||||
RingMemoryStream testRing = new RingMemoryStream(10);
|
||||
testRing.Write(this.testBytes, 0, 8);
|
||||
testRing.Read(this.testBytes, 0, 5);
|
||||
testRing.Write(this.testBytes, 0, 5);
|
||||
Assert.Throws<OverflowException>(
|
||||
delegate() { testRing.Write(this.testBytes, 0, 3); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Tests whether the ring buffer correctly handles fragmentation</summary>
|
||||
[Test]
|
||||
public void TestSplitBlockWrappedRead() {
|
||||
RingMemoryStream testRing = new RingMemoryStream(10);
|
||||
testRing.Write(this.testBytes, 0, 10);
|
||||
testRing.Read(this.testBytes, 0, 5);
|
||||
testRing.Write(this.testBytes, 0, 5);
|
||||
|
||||
byte[] actual = new byte[10];
|
||||
testRing.Read(actual, 0, 10);
|
||||
Assert.AreEqual(new byte[] { 5, 6, 7, 8, 9, 0, 1, 2, 3, 4 }, actual);
|
||||
}
|
||||
|
||||
/// <summary>Tests whether the ring buffer correctly handles fragmentation</summary>
|
||||
[Test]
|
||||
public void TestSplitBlockLinearRead() {
|
||||
RingMemoryStream testRing = new RingMemoryStream(10);
|
||||
testRing.Write(this.testBytes, 0, 10);
|
||||
testRing.Read(this.testBytes, 0, 5);
|
||||
testRing.Write(this.testBytes, 0, 5);
|
||||
|
||||
byte[] actual = new byte[5];
|
||||
testRing.Read(actual, 0, 5);
|
||||
Assert.AreEqual(new byte[] { 5, 6, 7, 8, 9 }, actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the ring buffer correctly returns partial data if more
|
||||
/// data is requested than is contained in it.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestEndOfStream() {
|
||||
byte[] tempBytes = new byte[10];
|
||||
|
||||
RingMemoryStream testRing = new RingMemoryStream(10);
|
||||
Assert.AreEqual(0, testRing.Read(tempBytes, 0, 5));
|
||||
|
||||
testRing.Write(this.testBytes, 0, 5);
|
||||
Assert.AreEqual(5, testRing.Read(tempBytes, 0, 10));
|
||||
|
||||
testRing.Write(this.testBytes, 0, 6);
|
||||
testRing.Read(tempBytes, 0, 5);
|
||||
testRing.Write(this.testBytes, 0, 9);
|
||||
Assert.AreEqual(10, testRing.Read(tempBytes, 0, 20));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that the ring buffer can extend its capacity without loosing data
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCapacityIncrease() {
|
||||
RingMemoryStream testRing = new RingMemoryStream(10);
|
||||
testRing.Write(this.testBytes, 0, 10);
|
||||
|
||||
testRing.Capacity = 20;
|
||||
byte[] actual = new byte[10];
|
||||
testRing.Read(actual, 0, 10);
|
||||
|
||||
Assert.AreEqual(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that the ring buffer can reduce its capacity without loosing data
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCapacityDecrease() {
|
||||
RingMemoryStream testRing = new RingMemoryStream(20);
|
||||
testRing.Write(this.testBytes, 0, 10);
|
||||
|
||||
testRing.Capacity = 10;
|
||||
byte[] actual = new byte[10];
|
||||
testRing.Read(actual, 0, 10);
|
||||
|
||||
Assert.AreEqual(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, actual);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks that an exception is thrown when the ring buffer's capacity is
|
||||
/// reduced so much it would have to give up some of its contained data
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCapacityDecreaseException() {
|
||||
RingMemoryStream testRing = new RingMemoryStream(20);
|
||||
testRing.Write(this.testBytes, 0, 20);
|
||||
|
||||
Assert.Throws<ArgumentOutOfRangeException>(
|
||||
delegate() { testRing.Capacity = 10; }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Tests whether the Capacity property returns the current capacity</summary>
|
||||
[Test]
|
||||
public void TestCapacity() {
|
||||
RingMemoryStream testRing = new RingMemoryStream(123);
|
||||
|
||||
Assert.AreEqual(123, testRing.Capacity);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the CanRead property returns true</summary>
|
||||
[Test]
|
||||
public void TestCanRead() {
|
||||
Assert.IsTrue(new RingMemoryStream(10).CanRead);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the CanSeek property returns false</summary>
|
||||
[Test]
|
||||
public void TestCanSeek() {
|
||||
Assert.IsFalse(new RingMemoryStream(10).CanSeek);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the CanWrite property returns true</summary>
|
||||
[Test]
|
||||
public void TestCanWrite() {
|
||||
Assert.IsTrue(new RingMemoryStream(10).CanWrite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the auto reset feature works (resets the buffer pointer to the
|
||||
/// left end of the buffer when it gets empty; mainly a performance feature).
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestAutoReset() {
|
||||
byte[] tempBytes = new byte[10];
|
||||
RingMemoryStream testRing = new RingMemoryStream(10);
|
||||
|
||||
testRing.Write(this.testBytes, 0, 8);
|
||||
testRing.Read(tempBytes, 0, 2);
|
||||
testRing.Read(tempBytes, 0, 2);
|
||||
testRing.Read(tempBytes, 0, 1);
|
||||
testRing.Read(tempBytes, 0, 1);
|
||||
|
||||
Assert.AreEqual(2, testRing.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that an exception is thrown when the Position property of the ring
|
||||
/// memory stream is used to retrieve the current file pointer position
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnRetrievePosition() {
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { Console.WriteLine(new RingMemoryStream(10).Position); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that an exception is thrown when the Position property of the ring
|
||||
/// memory stream is used to modify the current file pointer position
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnAssignPosition() {
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { new RingMemoryStream(10).Position = 0; }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that an exception is thrown when the Seek() method of the ring memory
|
||||
/// stream is attempted to be used
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnSeek() {
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { new RingMemoryStream(10).Seek(0, SeekOrigin.Begin); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that an exception is thrown when the SetLength() method of the ring
|
||||
/// memory stream is attempted to be used
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestThrowOnSetLength() {
|
||||
Assert.Throws<NotSupportedException>(
|
||||
delegate() { new RingMemoryStream(10).SetLength(10); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests the Flush() method of the ring memory stream, which is either a dummy
|
||||
/// implementation or has no side effects
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestFlush() {
|
||||
new RingMemoryStream(10).Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the length property is updated in accordance to the data written
|
||||
/// into the ring memory stream
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestLengthOnLinearBlock() {
|
||||
RingMemoryStream testRing = new RingMemoryStream(10);
|
||||
testRing.Write(new byte[10], 0, 10);
|
||||
|
||||
Assert.AreEqual(10, testRing.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the length property is updated in accordance to the data written
|
||||
/// into the ring memory stream when the data is split within the stream
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestLengthOnSplitBlock() {
|
||||
RingMemoryStream testRing = new RingMemoryStream(10);
|
||||
|
||||
testRing.Write(new byte[10], 0, 10);
|
||||
testRing.Read(new byte[5], 0, 5);
|
||||
testRing.Write(new byte[5], 0, 5);
|
||||
|
||||
Assert.AreEqual(10, testRing.Length);
|
||||
}
|
||||
|
||||
/// <summary>Test data for the ring buffer unit tests</summary>
|
||||
private byte[] testBytes;
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.IO
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,129 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support {
|
||||
|
||||
/// <summary>Contains unit tests for the integer helper class</summary>
|
||||
[TestFixture]
|
||||
internal class IntegerHelperTest {
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the next power of 2 calculation works for long integers
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestNextPowerOf2ULong() {
|
||||
Assert.AreEqual(1UL, IntegerHelper.NextPowerOf2(0UL));
|
||||
Assert.AreEqual(1UL, IntegerHelper.NextPowerOf2(1UL));
|
||||
Assert.AreEqual(2UL, IntegerHelper.NextPowerOf2(2UL));
|
||||
Assert.AreEqual(4UL, IntegerHelper.NextPowerOf2(3UL));
|
||||
Assert.AreEqual(4UL, IntegerHelper.NextPowerOf2(4UL));
|
||||
Assert.AreEqual(
|
||||
9223372036854775808UL, IntegerHelper.NextPowerOf2(4611686018427387905UL)
|
||||
);
|
||||
Assert.AreEqual(
|
||||
9223372036854775808UL, IntegerHelper.NextPowerOf2(9223372036854775807UL)
|
||||
);
|
||||
Assert.AreEqual(
|
||||
9223372036854775808UL, IntegerHelper.NextPowerOf2(9223372036854775808UL)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the next power of 2 calculation works for long integers
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestNextPowerOf2Long() {
|
||||
Assert.AreEqual(1L, IntegerHelper.NextPowerOf2(0L));
|
||||
Assert.AreEqual(1L, IntegerHelper.NextPowerOf2(1L));
|
||||
Assert.AreEqual(2L, IntegerHelper.NextPowerOf2(2L));
|
||||
Assert.AreEqual(4L, IntegerHelper.NextPowerOf2(3L));
|
||||
Assert.AreEqual(4L, IntegerHelper.NextPowerOf2(4L));
|
||||
Assert.AreEqual(4611686018427387904L, IntegerHelper.NextPowerOf2(2305843009213693953L));
|
||||
Assert.AreEqual(4611686018427387904L, IntegerHelper.NextPowerOf2(4611686018427387903L));
|
||||
Assert.AreEqual(4611686018427387904L, IntegerHelper.NextPowerOf2(4611686018427387904L));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the next power of 2 calculation works for integers
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestNextPowerOf2UInt() {
|
||||
Assert.AreEqual(1U, IntegerHelper.NextPowerOf2(0U));
|
||||
Assert.AreEqual(1U, IntegerHelper.NextPowerOf2(1U));
|
||||
Assert.AreEqual(2U, IntegerHelper.NextPowerOf2(2U));
|
||||
Assert.AreEqual(4U, IntegerHelper.NextPowerOf2(3U));
|
||||
Assert.AreEqual(4U, IntegerHelper.NextPowerOf2(4U));
|
||||
Assert.AreEqual(2147483648U, IntegerHelper.NextPowerOf2(1073741825U));
|
||||
Assert.AreEqual(2147483648U, IntegerHelper.NextPowerOf2(2147483647U));
|
||||
Assert.AreEqual(2147483648U, IntegerHelper.NextPowerOf2(2147483648U));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the next power of 2 calculation works for integers
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestNextPowerOf2Int() {
|
||||
Assert.AreEqual(1, IntegerHelper.NextPowerOf2(0));
|
||||
Assert.AreEqual(1, IntegerHelper.NextPowerOf2(1));
|
||||
Assert.AreEqual(2, IntegerHelper.NextPowerOf2(2));
|
||||
Assert.AreEqual(4, IntegerHelper.NextPowerOf2(3));
|
||||
Assert.AreEqual(4, IntegerHelper.NextPowerOf2(4));
|
||||
Assert.AreEqual(1073741824, IntegerHelper.NextPowerOf2(536870913));
|
||||
Assert.AreEqual(1073741824, IntegerHelper.NextPowerOf2(1073741823));
|
||||
Assert.AreEqual(1073741824, IntegerHelper.NextPowerOf2(1073741824));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the bit counting method for integers works</summary>
|
||||
[Test]
|
||||
public void TestCountBitsInInteger() {
|
||||
Assert.AreEqual(0, IntegerHelper.CountBits(0));
|
||||
Assert.AreEqual(32, IntegerHelper.CountBits(-1));
|
||||
Assert.AreEqual(16, IntegerHelper.CountBits(0x55555555));
|
||||
Assert.AreEqual(16, IntegerHelper.CountBits(0xAAAAAAAA));
|
||||
|
||||
for (int bitIndex = 0; bitIndex < 32; ++bitIndex) {
|
||||
Assert.AreEqual(1, IntegerHelper.CountBits(1 << bitIndex));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the bit counting method for long integers works</summary>
|
||||
[Test]
|
||||
public void TestCountBitsInLongInteger() {
|
||||
Assert.AreEqual(0, IntegerHelper.CountBits(0L));
|
||||
Assert.AreEqual(64, IntegerHelper.CountBits(-1L));
|
||||
Assert.AreEqual(32, IntegerHelper.CountBits(0x5555555555555555));
|
||||
Assert.AreEqual(32, IntegerHelper.CountBits(0xAAAAAAAAAAAAAAAA));
|
||||
|
||||
for (int bitIndex = 0; bitIndex < 64; ++bitIndex) {
|
||||
Assert.AreEqual(1, IntegerHelper.CountBits(1 << bitIndex));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,141 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.Licensing {
|
||||
|
||||
/// <summary>Unit test for the license key class</summary>
|
||||
[TestFixture]
|
||||
internal class LicenseKeyTest {
|
||||
|
||||
/// <summary>Tests the default constructor of the license key class</summary>
|
||||
[Test]
|
||||
public void DefaultConstructorCanBeUsed() {
|
||||
Assert.IsNotNull(new LicenseKey()); // Nonsense, prevents compiler warning
|
||||
}
|
||||
|
||||
/// <summary>Validates the correct translation of keys to GUIDs and back</summary>
|
||||
[Test]
|
||||
public void LicenseKeysCanBeConvertedToGuidsAndBack() {
|
||||
for(int i = 0; i < 128; ++i) {
|
||||
|
||||
// Create a new BitArray with the n.th bit set
|
||||
BitArray guidBits = new BitArray(128);
|
||||
guidBits[i] = true;
|
||||
|
||||
// Create a GUID from this Bitarray
|
||||
byte[] guidBytes = new byte[16];
|
||||
guidBits.CopyTo(guidBytes, 0);
|
||||
Guid originalGuid = new Guid(guidBytes);
|
||||
|
||||
// Convert the GUID into a license key and back to a GUID
|
||||
string licenseKey = new LicenseKey(originalGuid).ToString();
|
||||
Guid rebuiltGuid = LicenseKey.Parse(licenseKey).ToGuid();
|
||||
|
||||
// Verify that the original GUID matches the fore-and-back converted one
|
||||
Assert.AreEqual(originalGuid, rebuiltGuid, "Test for GUID bit " + i);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Tests whether license keys can be modified without destroying them</summary>
|
||||
[Test]
|
||||
public void LicenseKeysCanBeModified() {
|
||||
|
||||
for(int i = 0; i < 4; ++i) {
|
||||
for(int j = 0; j < 8; ++j) {
|
||||
|
||||
LicenseKey testKey = new LicenseKey(
|
||||
new Guid(-1, -1, -1, 255, 255, 255, 255, 255, 255, 255, 255)
|
||||
);
|
||||
|
||||
string originalString = testKey.ToString();
|
||||
testKey[i] &= ~(1 << j);
|
||||
string modifiedString = testKey.ToString();
|
||||
|
||||
Assert.IsTrue(
|
||||
originalString != modifiedString, "Modified string differs from original"
|
||||
);
|
||||
|
||||
testKey[i] |= (1 << j);
|
||||
string revertedString = testKey.ToString();
|
||||
|
||||
Assert.AreEqual(
|
||||
originalString, revertedString, "Original state restorable"
|
||||
);
|
||||
|
||||
} // for j
|
||||
} // for i
|
||||
|
||||
}
|
||||
|
||||
/// <summary>Tests whether license keys can be modified without destroying them</summary>
|
||||
[Test]
|
||||
public void ParsingInvalidLicenseKeyThrowsArgumentException() {
|
||||
Assert.Throws<ArgumentException>(
|
||||
delegate() { LicenseKey.Parse("hello world"); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether an exception is thrown if the indexer of a license key is used
|
||||
/// with an invalid index to retrieve a component of the key
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ReadingInvalidIndexThrowsIndexOutOfRangeException() {
|
||||
LicenseKey key = new LicenseKey();
|
||||
Assert.Throws<IndexOutOfRangeException>(
|
||||
delegate() { Console.WriteLine(key[-1]); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether an exception is thrown if the indexer of a license key is used
|
||||
/// with an invalid index to set a component of the key
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WritingInvalidIndexThrowsIndexOutOfRangeException() {
|
||||
LicenseKey key = new LicenseKey();
|
||||
Assert.Throws<IndexOutOfRangeException>(
|
||||
delegate() { key[-1] = 0; }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a license key can be converted into a byte array
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void LicenseKeyCanBeConvertedToByteArray() {
|
||||
Guid someGuid = Guid.NewGuid();
|
||||
LicenseKey someKey = new LicenseKey(someGuid);
|
||||
|
||||
CollectionAssert.AreEqual(someGuid.ToByteArray(), someKey.ToByteArray());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Licensing
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,171 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
|
||||
using NUnit.Framework;
|
||||
using NMock;
|
||||
|
||||
namespace Nuclex.Support {
|
||||
|
||||
/// <summary>Unit tests for observable class</summary>
|
||||
[TestFixture]
|
||||
internal class ObservableTest {
|
||||
|
||||
#region class TestObservable
|
||||
|
||||
/// <summary>Example class on which unit test generates change notifications</summary>
|
||||
public class TestObservable : Observable {
|
||||
|
||||
/// <summary>Triggers the property changed event for the specified property</summary>
|
||||
/// <param name="propertyName">
|
||||
/// Name of the property that will be reported as changed
|
||||
/// </param>
|
||||
public void FirePropertyChanged(string propertyName) {
|
||||
OnPropertyChanged(propertyName);
|
||||
}
|
||||
|
||||
/// <summary>Fires the property changed event for the 'SomePropety' property</summary>
|
||||
public void FireSomePropertyChanged() {
|
||||
#pragma warning disable 0618
|
||||
OnPropertyChanged(() => SomeProperty);
|
||||
#pragma warning restore 0618
|
||||
}
|
||||
|
||||
/// <summary>Example property that will be reported to have changed</summary>
|
||||
public int SomeProperty { get; set; }
|
||||
|
||||
}
|
||||
|
||||
#endregion // class TestObservable
|
||||
|
||||
#region class MockedSubscriber
|
||||
|
||||
/// <summary>Mocked change notification subscriber</summary>
|
||||
public class MockedSubscriber {
|
||||
|
||||
/// <summary>Called when the value of a property has changed</summary>
|
||||
/// <param name="sender">Object of which a property has changed</param>
|
||||
/// <param name="arguments">Contains the name of the changed property</param>
|
||||
public void PropertyChanged(object sender, PropertyChangedEventArgs arguments) {
|
||||
this.wasNotified = true;
|
||||
this.changedPropertyName = arguments.PropertyName;
|
||||
}
|
||||
|
||||
/// <summary>Whether the subscriber was notified of a property change</summary>
|
||||
public bool WasNotified {
|
||||
get { return this.wasNotified; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether a change notification for the specified property was received
|
||||
/// </summary>
|
||||
/// <param name="propertyName">Name of the property that will be checked for</param>
|
||||
/// <returns>
|
||||
/// True if a change notification for the specified property was received
|
||||
/// </returns>
|
||||
public bool WasNotifiedOfChangeTo(string propertyName) {
|
||||
if(!this.wasNotified) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(string.IsNullOrEmpty(propertyName)) {
|
||||
return string.IsNullOrEmpty(this.changedPropertyName);
|
||||
}
|
||||
|
||||
return (propertyName == this.changedPropertyName);
|
||||
}
|
||||
|
||||
/// <summary>Whether a change notification was received</summary>
|
||||
private bool wasNotified;
|
||||
/// <summary>Name of the property for which a change notification was received</summary>
|
||||
private string changedPropertyName;
|
||||
|
||||
}
|
||||
|
||||
#endregion // class MockedSubscriber
|
||||
|
||||
/// <summary>Called before each unit test is run</summary>
|
||||
[SetUp]
|
||||
public void Setup() {
|
||||
this.testObservable = new TestObservable();
|
||||
this.subscriber = new MockedSubscriber();
|
||||
|
||||
this.testObservable.PropertyChanged += this.subscriber.PropertyChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the name of the changed property can be specified manually
|
||||
/// when triggering the PropertyChanged event
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void PropertyNameCanBeSpecifiedManually() {
|
||||
this.testObservable.FirePropertyChanged("SomeProperty");
|
||||
Assert.IsTrue(this.subscriber.WasNotifiedOfChangeTo("SomeProperty"));
|
||||
}
|
||||
|
||||
#if DEBUG // The check is conditionally performed only in debug mode
|
||||
/// <summary>
|
||||
/// Verifies that specifying the name of a property that doesn't exist
|
||||
/// causes an ArgumentException to be thrown
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SpecifyingInvalidPropertyNameThrowsArgumentException() {
|
||||
Assert.Throws<ArgumentException>(
|
||||
delegate() { this.testObservable.FirePropertyChanged("DoesntExist"); }
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the observable is capable of deducing the name of the property
|
||||
/// from a lambda expression
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void PropertyNameCanBeDeducedFromLambdaExpression() {
|
||||
this.testObservable.FireSomePropertyChanged();
|
||||
Assert.IsTrue(this.subscriber.WasNotifiedOfChangeTo("SomeProperty"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that change notifications for all properties of a type can
|
||||
/// be generated
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void WildcardChangeNotificationsCanBeSent() {
|
||||
this.testObservable.FirePropertyChanged(string.Empty);
|
||||
Assert.IsTrue(this.subscriber.WasNotifiedOfChangeTo(null));
|
||||
|
||||
this.testObservable.FirePropertyChanged(null);
|
||||
Assert.IsTrue(this.subscriber.WasNotifiedOfChangeTo(string.Empty));
|
||||
}
|
||||
|
||||
/// <summary>Observable object being tested</summary>
|
||||
private TestObservable testObservable;
|
||||
/// <summary>Subscriber to the observable object being tested</summary>
|
||||
private MockedSubscriber subscriber;
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support {
|
||||
|
||||
/// <summary>Unit tests for the observable helper</summary>
|
||||
[TestFixture]
|
||||
internal class ObservableHelperTest {
|
||||
|
||||
#region class TestReferenceType
|
||||
|
||||
/// <summary>Example class on which unit test generates change notifications</summary>
|
||||
public class TestReferenceType {
|
||||
|
||||
/// <summary>Example property that will be reported to have changed</summary>
|
||||
public int SomeProperty { get; set; }
|
||||
|
||||
}
|
||||
|
||||
#endregion // class TestReferenceType
|
||||
|
||||
#region struct TestValueType
|
||||
|
||||
/// <summary>Example class on which unit test generates change notifications</summary>
|
||||
public struct TestValueType {
|
||||
|
||||
/// <summary>Example property that will be reported to have changed</summary>
|
||||
public int SomeProperty { get; set; }
|
||||
|
||||
}
|
||||
|
||||
#endregion // struct TestValueType
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the name of a property accessed in a lambda expression
|
||||
/// can be obtained.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CanObtainPropertyNameFromLambdaExpression() {
|
||||
string propertyName = ObservableHelper.GetPropertyName(
|
||||
() => SomeReferenceType.SomeProperty
|
||||
);
|
||||
Assert.AreEqual("SomeProperty", propertyName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the name of a property assigned in a lambda expression
|
||||
/// can be obtained.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CanObtainPropertyNameFromBoxedLambdaExpression() {
|
||||
string propertyName = ObservableHelper.GetPropertyName(
|
||||
() => (object)(SomeValueType.SomeProperty)
|
||||
);
|
||||
Assert.AreEqual("SomeProperty", propertyName);
|
||||
}
|
||||
|
||||
/// <summary>Helper used to construct lambda expressions</summary>
|
||||
protected static TestReferenceType SomeReferenceType { get; set; }
|
||||
|
||||
/// <summary>Helper used to construct lambda expressions</summary>
|
||||
protected static TestValueType SomeValueType { get; set; }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,657 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
using System;
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.Parsing {
|
||||
|
||||
/// <summary>Ensures that the command line parser is working properly</summary>
|
||||
[TestFixture]
|
||||
internal class CommandLineTest {
|
||||
|
||||
#region class ArgumentTest
|
||||
|
||||
/// <summary>Unit test for the command line option class</summary>
|
||||
[TestFixture]
|
||||
public class ArgumentTest {
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the name of a command line argument without a value can
|
||||
/// be extracted
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestNameExtraction() {
|
||||
CommandLine.Argument argument = CommandLine.Argument.OptionOnly(
|
||||
new StringSegment("--test"), 2, 4
|
||||
);
|
||||
|
||||
Assert.AreEqual("--test", argument.Raw);
|
||||
Assert.AreEqual("--", argument.Initiator);
|
||||
Assert.AreEqual("test", argument.Name);
|
||||
Assert.IsNull(argument.Associator);
|
||||
Assert.IsNull(argument.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the name of a command line argument without a value can be
|
||||
/// extracted when the argument is contained in a substring of a larger string
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestNameExtractionFromSubstring() {
|
||||
CommandLine.Argument argument = CommandLine.Argument.OptionOnly(
|
||||
new StringSegment("||--test||", 2, 6), 4, 4
|
||||
);
|
||||
|
||||
Assert.AreEqual("--test", argument.Raw);
|
||||
Assert.AreEqual("--", argument.Initiator);
|
||||
Assert.AreEqual("test", argument.Name);
|
||||
Assert.IsNull(argument.Associator);
|
||||
Assert.IsNull(argument.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Varifies that the name and value of a command line argument can be extracted
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestValueExtraction() {
|
||||
CommandLine.Argument argument = new CommandLine.Argument(
|
||||
new StringSegment("--test=123"), 2, 4, 7, 3
|
||||
);
|
||||
|
||||
Assert.AreEqual("--test=123", argument.Raw);
|
||||
Assert.AreEqual("--", argument.Initiator);
|
||||
Assert.AreEqual("test", argument.Name);
|
||||
Assert.AreEqual("=", argument.Associator);
|
||||
Assert.AreEqual("123", argument.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Varifies that the name and value of a command line argument can be extracted
|
||||
/// when the argument is contained in a substring of a larger string
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestValueExtractionFromSubstring() {
|
||||
CommandLine.Argument argument = new CommandLine.Argument(
|
||||
new StringSegment("||--test=123||", 2, 10), 4, 4, 9, 3
|
||||
);
|
||||
|
||||
Assert.AreEqual("--test=123", argument.Raw);
|
||||
Assert.AreEqual("--", argument.Initiator);
|
||||
Assert.AreEqual("test", argument.Name);
|
||||
Assert.AreEqual("=", argument.Associator);
|
||||
Assert.AreEqual("123", argument.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Varifies that the name and value of a command line argument can be extracted
|
||||
/// when the option is assigned a quoted value
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestQuotedValueExtraction() {
|
||||
CommandLine.Argument argument = new CommandLine.Argument(
|
||||
new StringSegment("--test=\"123\"", 0, 12), 2, 4, 8, 3
|
||||
);
|
||||
|
||||
Assert.AreEqual("--test=\"123\"", argument.Raw);
|
||||
Assert.AreEqual("--", argument.Initiator);
|
||||
Assert.AreEqual("test", argument.Name);
|
||||
Assert.AreEqual("=", argument.Associator);
|
||||
Assert.AreEqual("123", argument.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Varifies that the associator of a command line argument with an open ended
|
||||
/// value assignment can be retrieved
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestValuelessAssociatorRetrieval() {
|
||||
CommandLine.Argument argument = CommandLine.Argument.OptionOnly(
|
||||
new StringSegment("--test="), 2, 4
|
||||
);
|
||||
|
||||
Assert.AreEqual("--test=", argument.Raw);
|
||||
Assert.AreEqual("--", argument.Initiator);
|
||||
Assert.AreEqual("test", argument.Name);
|
||||
Assert.AreEqual("=", argument.Associator);
|
||||
Assert.IsNull(argument.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Varifies that the associator of a command line option with an open ended value
|
||||
/// assignment can be retrieved when the option is contained in a substring of
|
||||
/// a larger string
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestValuelessAssociatorRetrievalFromSubstring() {
|
||||
CommandLine.Argument option = CommandLine.Argument.OptionOnly(
|
||||
new StringSegment("||--test=||", 2, 7), 4, 4
|
||||
);
|
||||
|
||||
Assert.AreEqual("--test=", option.Raw);
|
||||
Assert.AreEqual("--", option.Initiator);
|
||||
Assert.AreEqual("test", option.Name);
|
||||
Assert.AreEqual("=", option.Associator);
|
||||
Assert.IsNull(option.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Varifies that a command line argument without an option name can be retrieved
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestNamelessValueRetrieval() {
|
||||
CommandLine.Argument argument = CommandLine.Argument.ValueOnly(
|
||||
new StringSegment("\"hello world\""), 1, 11
|
||||
);
|
||||
|
||||
Assert.AreEqual("\"hello world\"", argument.Raw);
|
||||
Assert.IsNull(argument.Initiator);
|
||||
Assert.IsNull(argument.Name);
|
||||
Assert.IsNull(argument.Associator);
|
||||
Assert.AreEqual("hello world", argument.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Varifies that a command line argument without an option name can be retrieved
|
||||
/// that is contained in a substring of larger string
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestNamelessValueRetrievalFromSubstring() {
|
||||
CommandLine.Argument argument = CommandLine.Argument.ValueOnly(
|
||||
new StringSegment("||\"hello world\"||", 2, 13), 3, 11
|
||||
);
|
||||
|
||||
Assert.AreEqual("\"hello world\"", argument.Raw);
|
||||
Assert.IsNull(argument.Initiator);
|
||||
Assert.IsNull(argument.Name);
|
||||
Assert.IsNull(argument.Associator);
|
||||
Assert.AreEqual("hello world", argument.Value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endregion // class ArgumentTest
|
||||
|
||||
/// <summary>Verifies that the default constructor is working</summary>
|
||||
[Test]
|
||||
public void TestDefaultConstructor() {
|
||||
CommandLine commandLine = new CommandLine();
|
||||
|
||||
Assert.AreEqual(0, commandLine.Arguments.Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that the parser can handle an argument initiator with an
|
||||
/// assignment that is missing a name
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestParseAmbiguousNameResolution() {
|
||||
CommandLine commandLine = CommandLine.Parse("--:test");
|
||||
|
||||
// Without a name, this is not a valid command line option, so it will
|
||||
// be parsed as a loose value instead.
|
||||
Assert.AreEqual(1, commandLine.Arguments.Count);
|
||||
Assert.AreEqual("--:test", commandLine.Arguments[0].Raw);
|
||||
Assert.IsNull(commandLine.Arguments[0].Initiator);
|
||||
Assert.IsNull(commandLine.Arguments[0].Name);
|
||||
Assert.IsNull(commandLine.Arguments[0].Associator);
|
||||
Assert.AreEqual("--:test", commandLine.Arguments[0].Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a lone short argument initiator without anything behind
|
||||
/// can be parsed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestParseShortArgumentInitiatorOnly() {
|
||||
CommandLine commandLine = CommandLine.Parse("-");
|
||||
|
||||
Assert.AreEqual(1, commandLine.Arguments.Count);
|
||||
Assert.AreEqual("-", commandLine.Arguments[0].Raw);
|
||||
Assert.IsNull(commandLine.Arguments[0].Initiator);
|
||||
Assert.IsNull(commandLine.Arguments[0].Name);
|
||||
Assert.IsNull(commandLine.Arguments[0].Associator);
|
||||
Assert.AreEqual("-", commandLine.Arguments[0].Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a lone long argument initiator without anything behind
|
||||
/// can be parsed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestParseLongArgumentInitiatorOnly() {
|
||||
CommandLine commandLine = CommandLine.Parse("--");
|
||||
|
||||
Assert.AreEqual(1, commandLine.Arguments.Count);
|
||||
Assert.AreEqual("--", commandLine.Arguments[0].Raw);
|
||||
Assert.IsNull(commandLine.Arguments[0].Initiator);
|
||||
Assert.IsNull(commandLine.Arguments[0].Name);
|
||||
Assert.IsNull(commandLine.Arguments[0].Associator);
|
||||
Assert.AreEqual("--", commandLine.Arguments[0].Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that the parser can handle multiple lone argument initators without
|
||||
/// a following argument
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestParseArgumentInitiatorAtEnd() {
|
||||
CommandLine commandLine = CommandLine.Parse("-hello:-world -");
|
||||
|
||||
Assert.AreEqual(2, commandLine.Arguments.Count);
|
||||
|
||||
Assert.AreEqual("-hello:-world", commandLine.Arguments[0].Raw);
|
||||
Assert.AreEqual("-", commandLine.Arguments[0].Initiator);
|
||||
Assert.AreEqual("hello", commandLine.Arguments[0].Name);
|
||||
Assert.AreEqual(":", commandLine.Arguments[0].Associator);
|
||||
Assert.AreEqual("-world", commandLine.Arguments[0].Value);
|
||||
|
||||
Assert.AreEqual("-", commandLine.Arguments[1].Raw);
|
||||
Assert.IsNull(commandLine.Arguments[1].Initiator);
|
||||
Assert.IsNull(commandLine.Arguments[1].Name);
|
||||
Assert.IsNull(commandLine.Arguments[1].Associator);
|
||||
Assert.AreEqual("-", commandLine.Arguments[1].Value);
|
||||
}
|
||||
|
||||
/// <summary>Validates that quoted arguments can be parsed</summary>
|
||||
[Test]
|
||||
public void TestParseQuotedValue() {
|
||||
CommandLine commandLine = CommandLine.Parse("hello -world --this -is=\"a test\"");
|
||||
|
||||
Assert.AreEqual(4, commandLine.Arguments.Count);
|
||||
|
||||
Assert.AreEqual("hello", commandLine.Arguments[0].Raw);
|
||||
Assert.IsNull(commandLine.Arguments[0].Initiator);
|
||||
Assert.IsNull(commandLine.Arguments[0].Name);
|
||||
Assert.IsNull(commandLine.Arguments[0].Associator);
|
||||
Assert.AreEqual("hello", commandLine.Arguments[0].Value);
|
||||
|
||||
Assert.AreEqual("-world", commandLine.Arguments[1].Raw);
|
||||
Assert.AreEqual("-", commandLine.Arguments[1].Initiator);
|
||||
Assert.AreEqual("world", commandLine.Arguments[1].Name);
|
||||
Assert.IsNull(commandLine.Arguments[1].Associator);
|
||||
Assert.IsNull(commandLine.Arguments[1].Value);
|
||||
|
||||
Assert.AreEqual("--this", commandLine.Arguments[2].Raw);
|
||||
Assert.AreEqual("--", commandLine.Arguments[2].Initiator);
|
||||
Assert.AreEqual("this", commandLine.Arguments[2].Name);
|
||||
Assert.IsNull(commandLine.Arguments[2].Associator);
|
||||
Assert.IsNull(commandLine.Arguments[2].Value);
|
||||
|
||||
Assert.AreEqual("-is=\"a test\"", commandLine.Arguments[3].Raw);
|
||||
Assert.AreEqual("-", commandLine.Arguments[3].Initiator);
|
||||
Assert.AreEqual("is", commandLine.Arguments[3].Name);
|
||||
Assert.AreEqual("=", commandLine.Arguments[3].Associator);
|
||||
Assert.AreEqual("a test", commandLine.Arguments[3].Value);
|
||||
}
|
||||
|
||||
/// <summary>Validates that null can be parsed</summary>
|
||||
[Test]
|
||||
public void TestParseNull() {
|
||||
CommandLine commandLine = CommandLine.Parse((string)null);
|
||||
|
||||
Assert.AreEqual(0, commandLine.Arguments.Count);
|
||||
}
|
||||
|
||||
/// <summary>Validates that a single argument without quotes can be parsed</summary>
|
||||
[Test]
|
||||
public void TestParseSingleNakedValue() {
|
||||
CommandLine commandLine = CommandLine.Parse("hello");
|
||||
|
||||
Assert.AreEqual(1, commandLine.Arguments.Count);
|
||||
Assert.AreEqual("hello", commandLine.Arguments[0].Raw);
|
||||
Assert.IsNull(commandLine.Arguments[0].Initiator);
|
||||
Assert.IsNull(commandLine.Arguments[0].Name);
|
||||
Assert.IsNull(commandLine.Arguments[0].Associator);
|
||||
Assert.AreEqual("hello", commandLine.Arguments[0].Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that the parser can handle a quoted argument that's missing
|
||||
/// the closing quote
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestParseQuotedArgumentWithoutClosingQuote() {
|
||||
CommandLine commandLine = CommandLine.Parse("\"Quoted argument");
|
||||
|
||||
Assert.AreEqual(1, commandLine.Arguments.Count);
|
||||
Assert.AreEqual("\"Quoted argument", commandLine.Arguments[0].Raw);
|
||||
Assert.IsNull(commandLine.Arguments[0].Initiator);
|
||||
Assert.IsNull(commandLine.Arguments[0].Name);
|
||||
Assert.IsNull(commandLine.Arguments[0].Associator);
|
||||
Assert.AreEqual("Quoted argument", commandLine.Arguments[0].Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that the parser correctly handles a quoted value assignment that's
|
||||
/// missing the closing quote
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestParseQuotedValueWithoutClosingQuote() {
|
||||
CommandLine commandLine = CommandLine.Parse("--test=\"Quoted argument");
|
||||
|
||||
Assert.AreEqual(1, commandLine.Arguments.Count);
|
||||
Assert.AreEqual("--test=\"Quoted argument", commandLine.Arguments[0].Raw);
|
||||
Assert.AreEqual("--", commandLine.Arguments[0].Initiator);
|
||||
Assert.AreEqual("test", commandLine.Arguments[0].Name);
|
||||
Assert.AreEqual("=", commandLine.Arguments[0].Associator);
|
||||
Assert.AreEqual("Quoted argument", commandLine.Arguments[0].Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that the parser can handle an command line consisting of only spaces
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestParseSpacesOnly() {
|
||||
CommandLine commandLine = CommandLine.Parse(" \t ");
|
||||
|
||||
Assert.AreEqual(0, commandLine.Arguments.Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that the parser can handle a quoted option
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestParseQuotedOption() {
|
||||
CommandLine commandLine = CommandLine.Parse("--\"hello\"");
|
||||
|
||||
// Quoted options are not supported, so this becomes a loose value
|
||||
Assert.AreEqual(1, commandLine.Arguments.Count);
|
||||
Assert.AreEqual("--\"hello\"", commandLine.Arguments[0].Raw);
|
||||
Assert.IsNull(commandLine.Arguments[0].Initiator);
|
||||
Assert.IsNull(commandLine.Arguments[0].Name);
|
||||
Assert.IsNull(commandLine.Arguments[0].Associator);
|
||||
Assert.AreEqual("--\"hello\"", commandLine.Arguments[0].Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that the parser can handle multiple lone argument initators without
|
||||
/// a following argument
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestParseMultipleLoneArgumentInitiators() {
|
||||
CommandLine commandLine = CommandLine.Parse("--- --");
|
||||
|
||||
Assert.AreEqual(2, commandLine.Arguments.Count);
|
||||
|
||||
Assert.AreEqual("---", commandLine.Arguments[0].Raw);
|
||||
Assert.IsNull(commandLine.Arguments[0].Initiator);
|
||||
Assert.IsNull(commandLine.Arguments[0].Name);
|
||||
Assert.IsNull(commandLine.Arguments[0].Associator);
|
||||
Assert.AreEqual("---", commandLine.Arguments[0].Value);
|
||||
|
||||
Assert.AreEqual("--", commandLine.Arguments[1].Raw);
|
||||
Assert.IsNull(commandLine.Arguments[1].Initiator);
|
||||
Assert.IsNull(commandLine.Arguments[1].Name);
|
||||
Assert.IsNull(commandLine.Arguments[1].Associator);
|
||||
Assert.AreEqual("--", commandLine.Arguments[1].Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the parser correctly handles options with embedded option initiators
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestParseOptionWithEmbeddedInitiator() {
|
||||
CommandLine commandLine = CommandLine.Parse("-hello/world=123 -test-case");
|
||||
|
||||
Assert.AreEqual(2, commandLine.Arguments.Count);
|
||||
|
||||
Assert.AreEqual("-hello/world=123", commandLine.Arguments[0].Raw);
|
||||
Assert.AreEqual("-", commandLine.Arguments[0].Initiator);
|
||||
Assert.AreEqual("hello/world", commandLine.Arguments[0].Name);
|
||||
Assert.AreEqual("=", commandLine.Arguments[0].Associator);
|
||||
Assert.AreEqual("123", commandLine.Arguments[0].Value);
|
||||
|
||||
Assert.AreEqual("-test-case", commandLine.Arguments[1].Raw);
|
||||
Assert.AreEqual("-", commandLine.Arguments[1].Initiator);
|
||||
Assert.AreEqual("test-case", commandLine.Arguments[1].Name);
|
||||
Assert.IsNull(commandLine.Arguments[1].Associator);
|
||||
Assert.IsNull(commandLine.Arguments[1].Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that arguments and values without spaces inbetween can be parsed
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestParseOptionAndValueWithoutSpaces() {
|
||||
CommandLine commandLine = CommandLine.Parse("\"value\"-option\"value\"");
|
||||
|
||||
Assert.AreEqual(3, commandLine.Arguments.Count);
|
||||
|
||||
Assert.AreEqual("\"value\"", commandLine.Arguments[0].Raw);
|
||||
Assert.IsNull(commandLine.Arguments[0].Initiator);
|
||||
Assert.IsNull(commandLine.Arguments[0].Name);
|
||||
Assert.IsNull(commandLine.Arguments[0].Associator);
|
||||
Assert.AreEqual("value", commandLine.Arguments[0].Value);
|
||||
|
||||
Assert.AreEqual("-option", commandLine.Arguments[1].Raw);
|
||||
Assert.AreEqual("-", commandLine.Arguments[1].Initiator);
|
||||
Assert.AreEqual("option", commandLine.Arguments[1].Name);
|
||||
Assert.IsNull(commandLine.Arguments[1].Associator);
|
||||
Assert.IsNull(commandLine.Arguments[1].Value);
|
||||
|
||||
Assert.AreEqual("\"value\"", commandLine.Arguments[2].Raw);
|
||||
Assert.IsNull(commandLine.Arguments[2].Initiator);
|
||||
Assert.IsNull(commandLine.Arguments[2].Name);
|
||||
Assert.IsNull(commandLine.Arguments[2].Associator);
|
||||
Assert.AreEqual("value", commandLine.Arguments[2].Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that options with modifiers at the end of the command line
|
||||
/// are parsed successfully
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestParseOptionWithModifierAtEnd() {
|
||||
CommandLine commandLine = CommandLine.Parse("--test-value- -test+");
|
||||
|
||||
Assert.AreEqual(2, commandLine.Arguments.Count);
|
||||
|
||||
Assert.AreEqual("--test-value-", commandLine.Arguments[0].Raw);
|
||||
Assert.AreEqual("--", commandLine.Arguments[0].Initiator);
|
||||
Assert.AreEqual("test-value", commandLine.Arguments[0].Name);
|
||||
Assert.IsNull(commandLine.Arguments[0].Associator);
|
||||
Assert.AreEqual("-", commandLine.Arguments[0].Value);
|
||||
|
||||
Assert.AreEqual("-test+", commandLine.Arguments[1].Raw);
|
||||
Assert.AreEqual("-", commandLine.Arguments[1].Initiator);
|
||||
Assert.AreEqual("test", commandLine.Arguments[1].Name);
|
||||
Assert.IsNull(commandLine.Arguments[1].Associator);
|
||||
Assert.AreEqual("+", commandLine.Arguments[1].Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that options with values assigned to them are parsed successfully
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestParseOptionWithAssignment() {
|
||||
CommandLine commandLine = CommandLine.Parse("-hello: -world=321");
|
||||
|
||||
Assert.AreEqual(2, commandLine.Arguments.Count);
|
||||
|
||||
Assert.AreEqual("-hello:", commandLine.Arguments[0].Raw);
|
||||
Assert.AreEqual("-", commandLine.Arguments[0].Initiator);
|
||||
Assert.AreEqual("hello", commandLine.Arguments[0].Name);
|
||||
Assert.AreEqual(":", commandLine.Arguments[0].Associator);
|
||||
Assert.IsNull(commandLine.Arguments[0].Value);
|
||||
|
||||
Assert.AreEqual("-world=321", commandLine.Arguments[1].Raw);
|
||||
Assert.AreEqual("-", commandLine.Arguments[1].Initiator);
|
||||
Assert.AreEqual("world", commandLine.Arguments[1].Name);
|
||||
Assert.AreEqual("=", commandLine.Arguments[1].Associator);
|
||||
Assert.AreEqual("321", commandLine.Arguments[1].Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that options with an empty value at the end of the command line
|
||||
/// string are parsed successfully
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestParseOptionAtEndOfString() {
|
||||
CommandLine commandLine = CommandLine.Parse("--test:");
|
||||
|
||||
Assert.AreEqual(1, commandLine.Arguments.Count);
|
||||
Assert.AreEqual("--test:", commandLine.Arguments[0].Raw);
|
||||
Assert.AreEqual("--", commandLine.Arguments[0].Initiator);
|
||||
Assert.AreEqual("test", commandLine.Arguments[0].Name);
|
||||
Assert.AreEqual(":", commandLine.Arguments[0].Associator);
|
||||
Assert.IsNull(commandLine.Arguments[0].Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the parser can recognize windows command line options if
|
||||
/// configured to windows mode
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestWindowsOptionInitiator() {
|
||||
CommandLine commandLine = CommandLine.Parse("/hello //world", true);
|
||||
|
||||
Assert.AreEqual(2, commandLine.Arguments.Count);
|
||||
|
||||
Assert.AreEqual("/hello", commandLine.Arguments[0].Raw);
|
||||
Assert.AreEqual("/", commandLine.Arguments[0].Initiator);
|
||||
Assert.AreEqual("hello", commandLine.Arguments[0].Name);
|
||||
Assert.IsNull(commandLine.Arguments[0].Associator);
|
||||
Assert.IsNull(commandLine.Arguments[0].Value);
|
||||
|
||||
Assert.AreEqual("//world", commandLine.Arguments[1].Raw);
|
||||
Assert.IsNull(commandLine.Arguments[1].Initiator);
|
||||
Assert.IsNull(commandLine.Arguments[1].Name);
|
||||
Assert.IsNull(commandLine.Arguments[1].Associator);
|
||||
Assert.AreEqual("//world", commandLine.Arguments[1].Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the parser ignores windows command line options if
|
||||
/// configured to non-windows mode
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestNonWindowsOptionValues() {
|
||||
CommandLine commandLine = CommandLine.Parse("/hello //world", false);
|
||||
|
||||
Assert.AreEqual(2, commandLine.Arguments.Count);
|
||||
|
||||
Assert.AreEqual("/hello", commandLine.Arguments[0].Raw);
|
||||
Assert.IsNull(commandLine.Arguments[0].Initiator);
|
||||
Assert.IsNull(commandLine.Arguments[0].Name);
|
||||
Assert.IsNull(commandLine.Arguments[0].Associator);
|
||||
Assert.AreEqual("/hello", commandLine.Arguments[0].Value);
|
||||
|
||||
Assert.AreEqual("//world", commandLine.Arguments[1].Raw);
|
||||
Assert.IsNull(commandLine.Arguments[1].Initiator);
|
||||
Assert.IsNull(commandLine.Arguments[1].Name);
|
||||
Assert.IsNull(commandLine.Arguments[1].Associator);
|
||||
Assert.AreEqual("//world", commandLine.Arguments[1].Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the existence of named arguments can be checked
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void HasArgumentWorksForWindowsStyleArguments() {
|
||||
CommandLine test = CommandLine.Parse("/first:x /second:y /second:z third", true);
|
||||
|
||||
Assert.IsTrue(test.HasArgument("first"));
|
||||
Assert.IsTrue(test.HasArgument("second"));
|
||||
Assert.IsFalse(test.HasArgument("third"));
|
||||
Assert.IsFalse(test.HasArgument("fourth"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the existence of named arguments can be checked
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void HasArgumentWorksForUnixStyleArguments() {
|
||||
CommandLine test = CommandLine.Parse("--first=x --second=y --second=z third", false);
|
||||
|
||||
Assert.IsTrue(test.HasArgument("first"));
|
||||
Assert.IsTrue(test.HasArgument("second"));
|
||||
Assert.IsFalse(test.HasArgument("third"));
|
||||
Assert.IsFalse(test.HasArgument("fourth"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether a command line can be built with the command line class
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestCommandLineFormatting() {
|
||||
CommandLine commandLine = new CommandLine(true);
|
||||
|
||||
commandLine.AddValue("single");
|
||||
commandLine.AddValue("with space");
|
||||
commandLine.AddOption("option");
|
||||
commandLine.AddOption("@@", "extravagant-option");
|
||||
commandLine.AddAssignment("name", "value");
|
||||
commandLine.AddAssignment("name", "value with spaces");
|
||||
commandLine.AddAssignment("@@", "name", "value");
|
||||
commandLine.AddAssignment("@@", "name", "value with spaces");
|
||||
|
||||
Assert.AreEqual(8, commandLine.Arguments.Count);
|
||||
Assert.AreEqual("single", commandLine.Arguments[0].Value);
|
||||
Assert.AreEqual("with space", commandLine.Arguments[1].Value);
|
||||
Assert.AreEqual("option", commandLine.Arguments[2].Name);
|
||||
Assert.AreEqual("@@", commandLine.Arguments[3].Initiator);
|
||||
Assert.AreEqual("extravagant-option", commandLine.Arguments[3].Name);
|
||||
Assert.AreEqual("name", commandLine.Arguments[4].Name);
|
||||
Assert.AreEqual("value", commandLine.Arguments[4].Value);
|
||||
Assert.AreEqual("name", commandLine.Arguments[5].Name);
|
||||
Assert.AreEqual("value with spaces", commandLine.Arguments[5].Value);
|
||||
Assert.AreEqual("@@", commandLine.Arguments[6].Initiator);
|
||||
Assert.AreEqual("name", commandLine.Arguments[6].Name);
|
||||
Assert.AreEqual("value", commandLine.Arguments[6].Value);
|
||||
Assert.AreEqual("name", commandLine.Arguments[7].Name);
|
||||
Assert.AreEqual("@@", commandLine.Arguments[7].Initiator);
|
||||
Assert.AreEqual("value with spaces", commandLine.Arguments[7].Value);
|
||||
|
||||
string commandLineString = commandLine.ToString();
|
||||
Assert.AreEqual(
|
||||
"single \"with space\" " +
|
||||
"-option @@extravagant-option " +
|
||||
"-name=value -name=\"value with spaces\" " +
|
||||
"@@name=value @@name=\"value with spaces\"",
|
||||
commandLineString
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether a command line can be built that contains empty arguments
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestNullArgumentFormatting() {
|
||||
CommandLine commandLine = new CommandLine(false);
|
||||
|
||||
commandLine.AddValue(string.Empty);
|
||||
commandLine.AddValue("hello");
|
||||
commandLine.AddValue(null);
|
||||
commandLine.AddValue("-test");
|
||||
|
||||
Assert.AreEqual(4, commandLine.Arguments.Count);
|
||||
string commandLineString = commandLine.ToString();
|
||||
Assert.AreEqual("\"\" hello \"\" \"-test\"", commandLineString);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Parsing
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,232 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.Parsing {
|
||||
|
||||
/// <summary>Verifies that the parser helper methods are correct</summary>
|
||||
[TestFixture]
|
||||
internal class ParserHelperTest {
|
||||
|
||||
/// <summary>Ensures that the SkipSpaces() method can handle null strings</summary>
|
||||
[Test]
|
||||
public void CanSkipSpacesInNullString() {
|
||||
int index = 0;
|
||||
Assert.DoesNotThrow(
|
||||
delegate() { ParserHelper.SkipSpaces((string)null, ref index); }
|
||||
);
|
||||
Assert.AreEqual(0, index);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the SkipSpaces() method can handle empty strings</summary>
|
||||
[Test]
|
||||
public void CanSkipSpacesInEmptyString() {
|
||||
int index = 0;
|
||||
Assert.DoesNotThrow(
|
||||
delegate() { ParserHelper.SkipSpaces(string.Empty, ref index); }
|
||||
);
|
||||
Assert.AreEqual(0, index);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the SkipSpaces() method can skip spaces</summary>
|
||||
[Test]
|
||||
public void SpacesCanBeSkipped() {
|
||||
int index = 7;
|
||||
ParserHelper.SkipSpaces(" Test Test ", ref index);
|
||||
Assert.AreEqual(10, index);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the SkipNonSpaces() method can handle null strings</summary>
|
||||
[Test]
|
||||
public void CanSkipNonSpacesInNullString() {
|
||||
int index = 0;
|
||||
Assert.DoesNotThrow(
|
||||
delegate() { ParserHelper.SkipNonSpaces((string)null, ref index); }
|
||||
);
|
||||
Assert.AreEqual(0, index);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the SkipNonSpaces() method can handle empty strings</summary>
|
||||
[Test]
|
||||
public void CanSkipNonSpacesInEmptyString() {
|
||||
int index = 0;
|
||||
Assert.DoesNotThrow(
|
||||
delegate() { ParserHelper.SkipNonSpaces(string.Empty, ref index); }
|
||||
);
|
||||
Assert.AreEqual(0, index);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the SkipNonSpaces() method can skip non-space characters</summary>
|
||||
[Test]
|
||||
public void NonSpacesCanBeSkipped() {
|
||||
int index = 7;
|
||||
ParserHelper.SkipNonSpaces("Test Test Test", ref index);
|
||||
Assert.AreEqual(11, index);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the SkipNumbers() method can handle null strings</summary>
|
||||
[Test]
|
||||
public void CanSkipNumbersInNullString() {
|
||||
int index = 0;
|
||||
Assert.DoesNotThrow(
|
||||
delegate() { ParserHelper.SkipNumericals((string)null, ref index); }
|
||||
);
|
||||
Assert.AreEqual(0, index);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the SkipNumbers() method can handle empty strings</summary>
|
||||
[Test]
|
||||
public void CanSkipNumbersInEmptyString() {
|
||||
int index = 0;
|
||||
Assert.DoesNotThrow(
|
||||
delegate() { ParserHelper.SkipNumericals(string.Empty, ref index); }
|
||||
);
|
||||
Assert.AreEqual(0, index);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the SkipNumbers() method can skip numbers</summary>
|
||||
[Test]
|
||||
public void NumbersCanBeSkipped() {
|
||||
int index = 6;
|
||||
ParserHelper.SkipNumericals("123abc456def789", ref index);
|
||||
Assert.AreEqual(9, index);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the SkipIntegers() method can handle null strings</summary>
|
||||
[Test]
|
||||
public void CanSkipIntegersInNullString() {
|
||||
int index = 0;
|
||||
Assert.IsFalse(ParserHelper.SkipInteger((string)null, ref index));
|
||||
Assert.AreEqual(0, index);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the SkipNumbers() method can handle empty strings</summary>
|
||||
[Test]
|
||||
public void CanSkipIntegersInEmptyString() {
|
||||
int index = 0;
|
||||
Assert.IsFalse(ParserHelper.SkipInteger(string.Empty, ref index));
|
||||
Assert.AreEqual(0, index);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a prefix alone can not be skipped as an integer</summary>
|
||||
[Test]
|
||||
public void PrefixAloneIsNotAnInteger() {
|
||||
int index = 0;
|
||||
Assert.IsFalse(ParserHelper.SkipInteger("+Test", ref index));
|
||||
Assert.AreEqual(0, index);
|
||||
Assert.IsFalse(ParserHelper.SkipInteger("-", ref index));
|
||||
Assert.AreEqual(0, index);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a prefixed integer can be skipped</summary>
|
||||
[Test]
|
||||
public void PrefixedIntegersCanBeSkipped() {
|
||||
int index = 0;
|
||||
Assert.IsTrue(ParserHelper.SkipInteger("+123", ref index));
|
||||
Assert.AreEqual(4, index);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that an integer without a prefix can be skipped</summary>
|
||||
[Test]
|
||||
public void PlainIntegersCanBeSkipped() {
|
||||
int index = 0;
|
||||
Assert.IsTrue(ParserHelper.SkipInteger("12345", ref index));
|
||||
Assert.AreEqual(5, index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that trying to skip text as if it was an integer skips nothing
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SkippingTextAsIntegerReturnsFalse() {
|
||||
int index = 0;
|
||||
Assert.IsFalse(ParserHelper.SkipInteger("hello", ref index));
|
||||
Assert.AreEqual(0, index);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the SkipIntegers() method can handle null strings</summary>
|
||||
[Test]
|
||||
public void CanSkipStringInNullString() {
|
||||
int index = 0;
|
||||
Assert.IsFalse(ParserHelper.SkipString((string)null, ref index));
|
||||
Assert.AreEqual(0, index);
|
||||
}
|
||||
|
||||
/// <summary>Ensures that the SkipNumbers() method can handle empty strings</summary>
|
||||
[Test]
|
||||
public void CanSkipStringInEmptyString() {
|
||||
int index = 0;
|
||||
Assert.IsFalse(ParserHelper.SkipString(string.Empty, ref index));
|
||||
Assert.AreEqual(0, index);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a string consisting of a single word can be skipped</summary>
|
||||
[Test]
|
||||
public void SingleWordStringsCanBeSkipped() {
|
||||
int index = 0;
|
||||
Assert.IsTrue(ParserHelper.SkipString("hello", ref index));
|
||||
Assert.AreEqual(5, index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a space character is not skipped over when skipping a string
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SpaceTerminatesUnquotedStrings() {
|
||||
int index = 0;
|
||||
Assert.IsTrue(ParserHelper.SkipString("hello world", ref index));
|
||||
Assert.AreEqual(5, index);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a string in quotes continues until the closing quote</summary>
|
||||
[Test]
|
||||
public void QuotedStringsCanBeSkipped() {
|
||||
int index = 0;
|
||||
Assert.IsTrue(ParserHelper.SkipString("\"This is a test\"", ref index));
|
||||
Assert.AreEqual(16, index);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a string in quotes continues until the closing quote</summary>
|
||||
[Test]
|
||||
public void QuotedStringsStopAtClosingQuote() {
|
||||
int index = 0;
|
||||
Assert.IsTrue(ParserHelper.SkipString("\"This is a test\" but this not.", ref index));
|
||||
Assert.AreEqual(16, index);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a string in quotes continues until the closing quote</summary>
|
||||
[Test]
|
||||
public void QuotedStringRequiresClosingQuote() {
|
||||
int index = 0;
|
||||
Assert.IsFalse(ParserHelper.SkipString("\"This is missing the closing quote", ref index));
|
||||
Assert.AreEqual(0, index);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Parsing
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,246 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support {
|
||||
|
||||
/// <summary>Unit Test for the path helper class</summary>
|
||||
[TestFixture]
|
||||
internal class PathHelperTest {
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the relative path creator keeps the absolute path if
|
||||
/// the location being passed is not relative to the base path.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestRelativeWindowsPathOfNonRelativePath() {
|
||||
Assert.That(
|
||||
PathHelper.MakeRelative(
|
||||
platformify("C:/Folder1/Folder2"),
|
||||
platformify("D:/Folder1/Folder2")
|
||||
),
|
||||
Is.EqualTo(platformify("D:/Folder1/Folder2"))
|
||||
);
|
||||
Assert.That(
|
||||
PathHelper.MakeRelative(
|
||||
platformify("C:/Folder1/Folder2/"),
|
||||
platformify("D:/Folder1/Folder2/")
|
||||
),
|
||||
Is.EqualTo(platformify("D:/Folder1/Folder2/"))
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the relative path creator correctly builds the relative
|
||||
/// path to the parent folder of the base path for windows paths.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestRelativeWindowsPathToParentFolder() {
|
||||
Assert.That(
|
||||
PathHelper.MakeRelative(
|
||||
platformify("C:/Folder1/Folder2"),
|
||||
platformify("C:/Folder1")
|
||||
),
|
||||
Is.EqualTo(platformify(".."))
|
||||
);
|
||||
Assert.That(
|
||||
PathHelper.MakeRelative(
|
||||
platformify("C:/Folder1/Folder2/"),
|
||||
platformify("C:/Folder1/")
|
||||
),
|
||||
Is.EqualTo(platformify("../"))
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the relative path creator correctly builds the relative path to
|
||||
/// the parent folder of the base path for windows paths with more than one level.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestRelativeWindowsPathToParentFolderTwoLevels() {
|
||||
Assert.That(
|
||||
PathHelper.MakeRelative(
|
||||
platformify("C:/Folder1/Folder2/Folder3"),
|
||||
platformify("C:/Folder1")
|
||||
),
|
||||
Is.EqualTo(platformify("../.."))
|
||||
);
|
||||
Assert.That(
|
||||
PathHelper.MakeRelative(
|
||||
platformify("C:/Folder1/Folder2/Folder3/"),
|
||||
platformify("C:/Folder1/")
|
||||
),
|
||||
Is.EqualTo(platformify("../../"))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the relative path creator correctly builds the relative
|
||||
/// path to the parent folder of the base path for unix paths.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestRelativeUnixPathToParentFolder() {
|
||||
Assert.That(
|
||||
PathHelper.MakeRelative(
|
||||
platformify("/Folder1/Folder2"),
|
||||
platformify("/Folder1")
|
||||
),
|
||||
Is.EqualTo(platformify(".."))
|
||||
);
|
||||
Assert.That(
|
||||
PathHelper.MakeRelative(
|
||||
platformify("/Folder1/Folder2/"),
|
||||
platformify("/Folder1/")
|
||||
),
|
||||
Is.EqualTo(platformify("../"))
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the relative path creator correctly builds the relative path to
|
||||
/// the parent folder of the base path for unix paths with more than one level.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestRelativeUnixPathToParentFolderTwoLevels() {
|
||||
Assert.That(
|
||||
PathHelper.MakeRelative(
|
||||
platformify("/Folder1/Folder2/Folder3"),
|
||||
platformify("/Folder1")
|
||||
),
|
||||
Is.EqualTo(platformify("../.."))
|
||||
);
|
||||
Assert.That(
|
||||
PathHelper.MakeRelative(
|
||||
platformify("/Folder1/Folder2/Folder3/"),
|
||||
platformify("/Folder1/")
|
||||
),
|
||||
Is.EqualTo(platformify("../../"))
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the relative path creator correctly builds the relative
|
||||
/// path to a nested folder in the base path for windows paths.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestRelativeWindowsPathToNestedFolder() {
|
||||
Assert.That(
|
||||
PathHelper.MakeRelative(
|
||||
platformify("C:/Folder1"),
|
||||
platformify("C:/Folder1/Folder2")
|
||||
),
|
||||
Is.EqualTo(platformify("Folder2"))
|
||||
);
|
||||
Assert.That(
|
||||
PathHelper.MakeRelative(
|
||||
platformify("C:/Folder1/"),
|
||||
platformify("C:/Folder1/Folder2/")
|
||||
),
|
||||
Is.EqualTo(platformify("Folder2/"))
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the relative path creator correctly builds the relative
|
||||
/// path to a nested folder in the base path for unix paths.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestRelativeUnixPathToNestedFolder() {
|
||||
Assert.That(
|
||||
PathHelper.MakeRelative(
|
||||
platformify("/Folder1"),
|
||||
platformify("/Folder1/Folder2")
|
||||
),
|
||||
Is.EqualTo(platformify("Folder2"))
|
||||
);
|
||||
Assert.That(
|
||||
PathHelper.MakeRelative(
|
||||
platformify("/Folder1/"),
|
||||
platformify("/Folder1/Folder2/")
|
||||
),
|
||||
Is.EqualTo(platformify("Folder2/"))
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the relative path creator correctly builds the relative
|
||||
/// path to another folder on the same level as base path for windows paths.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestRelativeWindowsPathToSiblingFolder() {
|
||||
Assert.That(
|
||||
PathHelper.MakeRelative(
|
||||
platformify("C:/Folder1/Folder2/"),
|
||||
platformify("C:/Folder1/Folder2345")
|
||||
),
|
||||
Is.EqualTo(platformify("../Folder2345"))
|
||||
);
|
||||
Assert.That(
|
||||
PathHelper.MakeRelative(
|
||||
platformify("C:/Folder1/Folder2345/"),
|
||||
platformify("C:/Folder1/Folder2")
|
||||
),
|
||||
Is.EqualTo(platformify("../Folder2"))
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the relative path creator correctly builds the relative
|
||||
/// path to another folder on the same level as base path for unix paths.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestRelativeUnixPathToSiblingFolder() {
|
||||
Assert.That(
|
||||
PathHelper.MakeRelative(
|
||||
platformify("/Folder1/Folder2/"),
|
||||
platformify("/Folder1/Folder2345")
|
||||
),
|
||||
Is.EqualTo(platformify("../Folder2345"))
|
||||
);
|
||||
Assert.That(
|
||||
PathHelper.MakeRelative(
|
||||
platformify("/Folder1/Folder2345/"),
|
||||
platformify("/Folder1/Folder2")
|
||||
),
|
||||
Is.EqualTo(platformify("../Folder2"))
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts unix-style directory separators into the format used by the current platform
|
||||
/// </summary>
|
||||
/// <param name="path">Path to converts into the platform-dependent format</param>
|
||||
/// <returns>Platform-specific version of the provided unix-style path</returns>
|
||||
private string platformify(string path) {
|
||||
return path.Replace('/', Path.DirectorySeparatorChar);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,124 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support {
|
||||
|
||||
/// <summary>Unit tests for the property change event argument helper</summary>
|
||||
[TestFixture]
|
||||
internal class PropertyChangedEventArgsHelperTest {
|
||||
|
||||
#region class TestViewModel
|
||||
|
||||
/// <summary>Example class on which unit test generates change notifications</summary>
|
||||
public class TestViewModel {
|
||||
|
||||
/// <summary>Example property that will be reported to have changed</summary>
|
||||
public int SomeProperty { get; set; }
|
||||
|
||||
}
|
||||
|
||||
#endregion // class TestViewModel
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a property change notification matching the property
|
||||
/// passed to the AreAffecting() method is recognized
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void MatchingPropertyChangeNotificationIsRecognized() {
|
||||
var arguments = new PropertyChangedEventArgs("SomeProperty");
|
||||
#pragma warning disable 0618
|
||||
Assert.IsTrue(arguments.AreAffecting(() => ViewModel.SomeProperty));
|
||||
#pragma warning restore 0618
|
||||
Assert.IsTrue(arguments.AreAffecting("SomeProperty"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that a mismatching property change notification will
|
||||
/// not report the property as being affected.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void MismatchingPropertyIsReportedAsUnaffected() {
|
||||
var arguments = new PropertyChangedEventArgs("AnotherProperty");
|
||||
#pragma warning disable 0618
|
||||
Assert.IsFalse(arguments.AreAffecting(() => ViewModel.SomeProperty));
|
||||
#pragma warning restore 0618
|
||||
Assert.IsFalse(arguments.AreAffecting("SomeProperty"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that any specific property is reported as being affected
|
||||
/// when the property change notification is a null wildcard
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SpecificPropertyIsAffectedByNullWildcard() {
|
||||
var nullArguments = new PropertyChangedEventArgs(null);
|
||||
#pragma warning disable 0618
|
||||
Assert.IsTrue(nullArguments.AreAffecting(() => ViewModel.SomeProperty));
|
||||
#pragma warning restore 0618
|
||||
Assert.IsTrue(nullArguments.AreAffecting("SomeProperty"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that any specific property is reported as being affected
|
||||
/// when the property change notification is an empty wildcard
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SpecificPropertyIsAffectedByEmptyWildcard() {
|
||||
var emptyArguments = new PropertyChangedEventArgs(string.Empty);
|
||||
#pragma warning disable 0618
|
||||
Assert.IsTrue(emptyArguments.AreAffecting(() => ViewModel.SomeProperty));
|
||||
#pragma warning disable 0618
|
||||
Assert.IsTrue(emptyArguments.AreAffecting("SomeProperty"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the helper can recognize a wildcard property change
|
||||
/// notification using null as the wildcard.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void NullWildcardIsRecognized() {
|
||||
var nullArguments = new PropertyChangedEventArgs(null);
|
||||
Assert.IsTrue(nullArguments.AffectAllProperties());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the helper can recognize a wildcard property change
|
||||
/// notification using an empty string as the wildcard.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void EmptyWildcardIsRecognized() {
|
||||
var emptyArguments = new PropertyChangedEventArgs(string.Empty);
|
||||
Assert.IsTrue(emptyArguments.AffectAllProperties());
|
||||
}
|
||||
|
||||
/// <summary>Helper used to construct lambda expressions</summary>
|
||||
protected static TestViewModel ViewModel { get; set; }
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,466 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.Settings {
|
||||
|
||||
/// <summary>Unit tests for the configuration file store</summary>
|
||||
[TestFixture]
|
||||
internal class ConfigurationFileStoreTest {
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that loading an empty file doesn't lead to an exception
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CanParseEmptyFile() {
|
||||
Assert.That(() => load(string.Empty), Throws.Nothing);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that categories can be parsed from a configuration file
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CanParseCategories() {
|
||||
string[] categoryNames = new string[] { "Category1", "Category 2" };
|
||||
string fileContents =
|
||||
"[" + categoryNames[0] + "]\r\n" +
|
||||
" [ " + categoryNames[1] + " ] \r\n";
|
||||
ConfigurationFileStore configurationFile = load(fileContents);
|
||||
|
||||
Assert.That(configurationFile.EnumerateCategories(), Is.EquivalentTo(categoryNames));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that malformed categories can be handled by the parser
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void MalformedCategoriesAreIgnored() {
|
||||
string fileContents =
|
||||
"[ Not a category\r\n" +
|
||||
" [";
|
||||
ConfigurationFileStore configurationFile = load(fileContents);
|
||||
|
||||
Assert.That(configurationFile.EnumerateCategories(), Is.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that empty lines in the configuration file have no meaning
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void EmptyLinesAreSkipped() {
|
||||
string fileContents =
|
||||
"\r\n" +
|
||||
" ";
|
||||
ConfigurationFileStore configurationFile = load(fileContents);
|
||||
Assert.That(configurationFile.EnumerateCategories(), Is.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that category definitions after a comment sign are ignored
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CommentedOutCategoriesAreIgnored() {
|
||||
string fileContents =
|
||||
"#[NotACategory]\r\n" +
|
||||
"; [ Also Not A Category ]\r\n";
|
||||
ConfigurationFileStore configurationFile = load(fileContents);
|
||||
Assert.That(configurationFile.EnumerateCategories(), Is.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that assignments without an option name are ignored by the parser
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void NamelessAssignmentsAreIgnored() {
|
||||
string fileContents =
|
||||
"=\r\n" +
|
||||
" = \r\n" +
|
||||
" = hello";
|
||||
ConfigurationFileStore configurationFile = load(fileContents);
|
||||
Assert.That(configurationFile.EnumerateCategories(), Is.Empty);
|
||||
Assert.That(configurationFile.EnumerateOptions(), Is.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that assignments without an option name are ignored by the parser
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void OptionsCanHaveEmptyValues() {
|
||||
string fileContents =
|
||||
"a =\r\n" +
|
||||
"b = \r\n" +
|
||||
"c = ; hello";
|
||||
ConfigurationFileStore configurationFile = load(fileContents);
|
||||
Assert.That(configurationFile.EnumerateCategories(), Is.Empty);
|
||||
|
||||
var options = new List<OptionInfo>(configurationFile.EnumerateOptions());
|
||||
Assert.That(options.Count, Is.EqualTo(3));
|
||||
|
||||
for(int index = 0; index < options.Count; ++index) {
|
||||
Assert.That(
|
||||
configurationFile.Get<string>(null, options[index].Name), Is.Null.Or.Empty
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that values assigned to options can contain space charcters
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void OptionValuesCanContainSpaces() {
|
||||
string fileContents =
|
||||
"test = hello world";
|
||||
ConfigurationFileStore configurationFile = load(fileContents);
|
||||
|
||||
Assert.That(configurationFile.Get<string>(null, "test"), Is.EqualTo("hello world"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that values enclosed in quotes can embed comment characters
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void OptionValuesWithQuotesCanEmbedComments() {
|
||||
string fileContents =
|
||||
"test = \"This ; is # not a comment\" # but this is";
|
||||
ConfigurationFileStore configurationFile = load(fileContents);
|
||||
|
||||
Assert.That(
|
||||
configurationFile.Get<string>(null, "test"),
|
||||
Is.EqualTo("\"This ; is # not a comment\"")
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that values can end on a quote without causing trouble
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CommentsCanEndWithAQuote() {
|
||||
string fileContents =
|
||||
"test = \"This value ends with a quote\"";
|
||||
ConfigurationFileStore configurationFile = load(fileContents);
|
||||
|
||||
Assert.That(
|
||||
configurationFile.Get<string>(null, "test"),
|
||||
Is.EqualTo("\"This value ends with a quote\"")
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that values can forget the closing quote without causing trouble
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ClosingQuoteCanBeOmmitted() {
|
||||
string fileContents =
|
||||
"test = \"No closing quote";
|
||||
ConfigurationFileStore configurationFile = load(fileContents);
|
||||
|
||||
Assert.That(
|
||||
configurationFile.Get<string>(null, "test"),
|
||||
Is.EqualTo("\"No closing quote")
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that text placed after the closing quote will also be part of
|
||||
/// an option's value
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TextAfterClosingQuoteBecomesPartOfValue() {
|
||||
string fileContents =
|
||||
"test = \"Begins here\" end ends here";
|
||||
ConfigurationFileStore configurationFile = load(fileContents);
|
||||
|
||||
Assert.That(
|
||||
configurationFile.Get<string>(null, "test"),
|
||||
Is.EqualTo("\"Begins here\" end ends here")
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that text placed after the closing quote will also be part of
|
||||
/// an option's value
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void OptionValuesCanBeChanged() {
|
||||
string fileContents = "test = 123 ; comment";
|
||||
ConfigurationFileStore configurationFile = load(fileContents);
|
||||
|
||||
configurationFile.Set(null, "test", "hello world");
|
||||
|
||||
Assert.That(
|
||||
save(configurationFile),
|
||||
Contains.Substring("hello world").And.ContainsSubstring("comment")
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that options can be added to the configuration file
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void OptionsCanBeAdded() {
|
||||
var configurationFile = new ConfigurationFileStore();
|
||||
|
||||
configurationFile.Set<string>(null, "test", "123");
|
||||
Assert.That(configurationFile.Get<string>(null, "test"), Is.EqualTo("123"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that options can be added to the configuration file
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CategoriesCanBeAdded() {
|
||||
var configurationFile = new ConfigurationFileStore();
|
||||
|
||||
configurationFile.Set<string>("general", "sol", "42");
|
||||
|
||||
Assert.That(
|
||||
configurationFile.EnumerateCategories(), Is.EquivalentTo(new string[] { "general" })
|
||||
);
|
||||
Assert.That(save(configurationFile), Contains.Substring("[general]"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that accessing an option that doesn't exist throws an exception
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void AccessingNonExistingOptionThrowsException() {
|
||||
var configurationFile = new ConfigurationFileStore();
|
||||
|
||||
Assert.That(
|
||||
() => configurationFile.Get<string>(null, "doesn't exist"),
|
||||
Throws.Exception.AssignableTo<KeyNotFoundException>()
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that accessing a category that doesn't exist throws an exception
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void AccessingNonExistingCategoryThrowsException() {
|
||||
var configurationFile = new ConfigurationFileStore();
|
||||
configurationFile.Set<string>(null, "test", "123");
|
||||
|
||||
Assert.That(
|
||||
() => configurationFile.Get<string>("doesn't exist", "test"),
|
||||
Throws.Exception.AssignableTo<KeyNotFoundException>()
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that it's possible to enumerate a category that doesn't exist
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void NonExistingCategoryCanBeEnumerated() {
|
||||
var configurationFile = new ConfigurationFileStore();
|
||||
|
||||
Assert.That(configurationFile.EnumerateOptions("doesn't exist"), Is.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that it's possible to create an option without a value
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ValuelessOptionsCanBeCreated() {
|
||||
var configurationFile = new ConfigurationFileStore();
|
||||
|
||||
configurationFile.Set<string>(null, "test", null);
|
||||
Assert.That(configurationFile.Get<string>(null, "test"), Is.Null.Or.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that it's possible to assign an empty value to an option
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void OptionValueCanBeCleared() {
|
||||
string fileContents = "test = 123 ; comment";
|
||||
ConfigurationFileStore configurationFile = load(fileContents);
|
||||
|
||||
configurationFile.Set<string>(null, "test", null);
|
||||
Assert.That(configurationFile.Get<string>(null, "test"), Is.Null.Or.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that it's possible to remove options from the configuration file
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void OptionsCanBeRemoved() {
|
||||
var configurationFile = new ConfigurationFileStore();
|
||||
configurationFile.Set<string>(null, "test", null);
|
||||
|
||||
Assert.That(configurationFile.Remove(null, "test"), Is.True);
|
||||
|
||||
string value;
|
||||
Assert.That(configurationFile.TryGet<string>(null, "test", out value), Is.False);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that options are removed from the configuration file correctly
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void RemovingOptionShiftsFollowingOptionsUp() {
|
||||
string fileContents =
|
||||
"first = 1\r\n" +
|
||||
"second = 2";
|
||||
ConfigurationFileStore configurationFile = load(fileContents);
|
||||
|
||||
Assert.That(configurationFile.Remove(null, "first"), Is.True);
|
||||
configurationFile.Set<string>(null, "second", "yay! first!");
|
||||
|
||||
Assert.That(save(configurationFile), Has.No.ContainsSubstring("1"));
|
||||
Assert.That(save(configurationFile), Contains.Substring("second"));
|
||||
Assert.That(save(configurationFile), Contains.Substring("yay! first!"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that it's not an error to remove an option from a non-existing category
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CanRemoveOptionFromNonExistingCategory() {
|
||||
var configurationFile = new ConfigurationFileStore();
|
||||
Assert.That(configurationFile.Remove("nothing", "first"), Is.False);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that it's not an error to remove a non-existing option
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CanRemoveNonExistingOption() {
|
||||
var configurationFile = new ConfigurationFileStore();
|
||||
Assert.That(configurationFile.Remove(null, "first"), Is.False);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the configuration file store can identify various types of values
|
||||
/// </summary>
|
||||
[
|
||||
Test,
|
||||
TestCase("nothing=", typeof(string)),
|
||||
TestCase("text = world", typeof(string)),
|
||||
TestCase("short=9", typeof(int)),
|
||||
TestCase("integer = 123", typeof(int)),
|
||||
TestCase("integer = 123 ", typeof(int)),
|
||||
TestCase("string=x", typeof(string)),
|
||||
TestCase("string = 123s", typeof(string)),
|
||||
TestCase("float = 123.45", typeof(float)),
|
||||
TestCase("float = 123.45 ", typeof(float)),
|
||||
TestCase("boolean = true", typeof(bool)),
|
||||
TestCase("boolean = false", typeof(bool)),
|
||||
TestCase("boolean = yes", typeof(bool)),
|
||||
TestCase("boolean = no", typeof(bool))
|
||||
]
|
||||
public void OptionTypeCanBeIdentified(string assignment, Type expectedType) {
|
||||
ConfigurationFileStore configurationFile = load(assignment);
|
||||
|
||||
OptionInfo info;
|
||||
using(
|
||||
IEnumerator<OptionInfo> enumerator = configurationFile.EnumerateOptions().GetEnumerator()
|
||||
) {
|
||||
Assert.That(enumerator.MoveNext(), Is.True);
|
||||
info = enumerator.Current;
|
||||
Assert.That(enumerator.MoveNext(), Is.False);
|
||||
}
|
||||
|
||||
Assert.That(info.OptionType, Is.EqualTo(expectedType));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that configuration files containing duplicate option names can not
|
||||
/// be used with the configuration file store
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void FilesWithDuplicateOptionNamesCannotBeProcessed() {
|
||||
string fileContents =
|
||||
"duplicate name = 1\r\n" +
|
||||
"duplicate name = 2";
|
||||
|
||||
Assert.That(() => load(fileContents), Throws.Exception);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that attempting to cast a value to an incompatible data type causes
|
||||
/// a FormatException to be thrown
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ImpossibleCastCausesFormatException() {
|
||||
string fileContents = "fail = yesnomaybe";
|
||||
ConfigurationFileStore configurationFile = load(fileContents);
|
||||
|
||||
Assert.That(
|
||||
() => configurationFile.Get<bool>(null, "fail"),
|
||||
Throws.Exception.AssignableTo<FormatException>()
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that configuration files containing duplicate option names can not
|
||||
/// be used with the configuration file store
|
||||
/// </summary>
|
||||
[
|
||||
Test,
|
||||
TestCase("value = yes", true),
|
||||
TestCase("value = true", true),
|
||||
TestCase("value = no", false),
|
||||
TestCase("value = false", false)
|
||||
]
|
||||
public void BooleanLiteralsAreUnderstood(string fileContents, bool expectedValue) {
|
||||
ConfigurationFileStore configurationFile = load(fileContents);
|
||||
|
||||
if(expectedValue) {
|
||||
Assert.That(configurationFile.Get<bool>(null, "value"), Is.True);
|
||||
} else {
|
||||
Assert.That(configurationFile.Get<bool>(null, "value"), Is.False);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Loads a configuration file from a string</summary>
|
||||
/// <param name="fileContents">Contents of the configuration file</param>
|
||||
/// <returns>The configuration file loaded from the string</returns>
|
||||
private static ConfigurationFileStore load(string fileContents) {
|
||||
using(var reader = new StringReader(fileContents)) {
|
||||
return ConfigurationFileStore.Parse(reader);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Saves a configuration file into a string</summary>
|
||||
/// <param name="configurationFile">Configuration file that will be saved</param>
|
||||
/// <returns>Contents of the configuration file</returns>
|
||||
private static string save(ConfigurationFileStore configurationFile) {
|
||||
var builder = new StringBuilder();
|
||||
|
||||
using(var writer = new StringWriter(builder)) {
|
||||
configurationFile.Save(writer);
|
||||
writer.Flush();
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Settings
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,164 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.Settings {
|
||||
|
||||
/// <summary>Unit tests for the memory settings store</summary>
|
||||
[TestFixture]
|
||||
internal class MemoryStoreTest {
|
||||
|
||||
/// <summary>Verifies that constructed a memory store throws an exception</summary>
|
||||
[Test]
|
||||
public void CanBeCreated() {
|
||||
Assert.That(() => new MemoryStore(), Throws.Nothing);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that it's possible to enumerate the options in a non-existing category
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void NonExistingCategoriesCanBeEnumerated() {
|
||||
var memoryStore = new MemoryStore();
|
||||
Assert.That(memoryStore.EnumerateOptions("doesn't exist"), Is.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that accessing an option that doesn't exist throws an exception
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void AccessingNonExistingOptionThrowsException() {
|
||||
var memoryStore = new MemoryStore();
|
||||
|
||||
Assert.That(
|
||||
() => memoryStore.Get<string>(null, "doesn't exist"),
|
||||
Throws.Exception.AssignableTo<KeyNotFoundException>()
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that accessing a category that doesn't exist throws an exception
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void AccessingNonExistingCategoryThrowsException() {
|
||||
var memoryStore = new MemoryStore();
|
||||
memoryStore.Set<string>(null, "test", "123");
|
||||
|
||||
Assert.That(
|
||||
() => memoryStore.Get<string>("doesn't exist", "test"),
|
||||
Throws.Exception.AssignableTo<KeyNotFoundException>()
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that settings can be stored in the memory store
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SettingsCanBeAssignedAndRetrieved() {
|
||||
var memoryStore = new MemoryStore();
|
||||
memoryStore.Set<string>("general", "sol", "42");
|
||||
|
||||
Assert.That(memoryStore.Get<string>("general", "sol"), Is.EqualTo("42"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that it's possible to remove options from the memory store
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void OptionsCanBeRemoved() {
|
||||
var memoryStore = new MemoryStore();
|
||||
memoryStore.Set<string>(null, "test", null);
|
||||
|
||||
Assert.That(memoryStore.Remove(null, "test"), Is.True);
|
||||
|
||||
string value;
|
||||
Assert.That(memoryStore.TryGet<string>(null, "test", out value), Is.False);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that it's not an error to remove an option from a non-existing category
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CanRemoveOptionFromNonExistingCategory() {
|
||||
var memoryStore = new MemoryStore();
|
||||
Assert.That(memoryStore.Remove("nothing", "first"), Is.False);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that it's not an error to remove a non-existing option
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CanRemoveNonExistingOption() {
|
||||
var memoryStore = new MemoryStore();
|
||||
Assert.That(memoryStore.Remove(null, "first"), Is.False);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the root category is not part of the enumerated categories
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void RootCategoryIsNotEnumerated() {
|
||||
var memoryStore = new MemoryStore();
|
||||
Assert.That(memoryStore.EnumerateCategories(), Is.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the root category is not part of the enumerated categories
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void OptionsInRootCategoryCanBeEnumerated() {
|
||||
var memoryStore = new MemoryStore();
|
||||
|
||||
string[] optionNames = new string[] { "first", "second" };
|
||||
memoryStore.Set<int>(null, optionNames[0], 1);
|
||||
memoryStore.Set<int>(null, optionNames[1], 2);
|
||||
|
||||
var optionInfos = new List<OptionInfo>(memoryStore.EnumerateOptions());
|
||||
Assert.That(optionInfos.Count, Is.EqualTo(2));
|
||||
|
||||
var enumeratedOptionNames = new List<string>() {
|
||||
optionInfos[0].Name, optionInfos[1].Name
|
||||
};
|
||||
Assert.That(enumeratedOptionNames, Is.EquivalentTo(optionNames));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the root category is not part of the enumerated categories
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CategoriesCanBeCreated() {
|
||||
var memoryStore = new MemoryStore();
|
||||
|
||||
memoryStore.Set<string>(null, "not", "used");
|
||||
memoryStore.Set<string>("test", "message", "hello world");
|
||||
|
||||
Assert.That(memoryStore.EnumerateCategories(), Is.EquivalentTo(new string[] { "test" }));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Settings
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,365 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if UNITTEST && WINDOWS
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
|
||||
using Microsoft.Win32;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.Settings {
|
||||
|
||||
/// <summary>Unit tests for the windows registry settings store</summary>
|
||||
[TestFixture]
|
||||
internal class WindowsRegistryStoreTest {
|
||||
|
||||
#region class TestContext
|
||||
|
||||
/// <summary>Sets up a temporary registry key for the unit test</summary>
|
||||
private class TestContext : IDisposable {
|
||||
|
||||
/// <summary>Initializes a new test context</summary>
|
||||
public TestContext() {
|
||||
this.keyName = Guid.NewGuid().ToString();
|
||||
this.registryKey = Registry.CurrentUser.CreateSubKey(this.keyName);
|
||||
this.store = new WindowsRegistryStore(this.registryKey, writable: true);
|
||||
}
|
||||
|
||||
/// <summary>Immediately frees all resources owned by the test context</summary>
|
||||
public void Dispose() {
|
||||
if(this.store != null) {
|
||||
this.store.Dispose();
|
||||
this.store = null;
|
||||
this.registryKey = null;
|
||||
} else if(this.registryKey != null) {
|
||||
this.registryKey.Dispose();
|
||||
this.registryKey = null;
|
||||
}
|
||||
if(this.keyName != null) {
|
||||
Registry.CurrentUser.DeleteSubKeyTree(this.keyName);
|
||||
this.keyName = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Store created on a temporary registry key</summary>
|
||||
public WindowsRegistryStore Store {
|
||||
get { return this.store; }
|
||||
}
|
||||
|
||||
/// <summary>Name of the temporary registry key</summary>
|
||||
private string keyName;
|
||||
/// <summary>Registry key (ownership transfered to the store)</summary>
|
||||
private RegistryKey registryKey;
|
||||
/// <summary>Store that is accessing the registry key</summary>
|
||||
private WindowsRegistryStore store;
|
||||
|
||||
}
|
||||
|
||||
#endregion // class TestContext
|
||||
|
||||
/// <summary>Verifies that new instances of the registry store can be created</summary>
|
||||
[Test]
|
||||
public void CanBeCreated() {
|
||||
Assert.That(
|
||||
() => { using(var context = new TestContext()) { } }, Throws.Nothing
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that new instances of the registry store can be created</summary>
|
||||
[Test]
|
||||
public void RegistryHivesCanBeOpened() {
|
||||
Assert.That(
|
||||
() => {
|
||||
using(
|
||||
var store = new WindowsRegistryStore(
|
||||
RegistryHive.CurrentUser, "", writable: false
|
||||
)
|
||||
) { }
|
||||
},
|
||||
Throws.Nothing
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that booleans can be stored in the registry</summary>
|
||||
[Test]
|
||||
public void BooleansCanBeStored() {
|
||||
using(var context = new TestContext()) {
|
||||
context.Store.Set(null, "test", true);
|
||||
Assert.That(context.Store.Get<bool>(null, "test"), Is.True);
|
||||
|
||||
context.Store.Set(null, "test", false);
|
||||
Assert.That(context.Store.Get<bool>(null, "test"), Is.False);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Verifies that integers can be stored in the registry</summary>
|
||||
[Test]
|
||||
public void IntegersCanBeStored() {
|
||||
using(var context = new TestContext()) {
|
||||
context.Store.Set(null, "test", 123);
|
||||
Assert.That(context.Store.Get<int>(null, "test"), Is.EqualTo(123));
|
||||
|
||||
context.Store.Set(null, "test", 456);
|
||||
Assert.That(context.Store.Get<int>(null, "test"), Is.EqualTo(456));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Verifies that floats can be stored in the registry</summary>
|
||||
[Test]
|
||||
public void FloatsCanBeStored() {
|
||||
float testValue = float.Parse("123.456", CultureInfo.InvariantCulture);
|
||||
|
||||
using(var context = new TestContext()) {
|
||||
context.Store.Set(null, "test", testValue);
|
||||
Assert.That(context.Store.Get<float>(null, "test"), Is.EqualTo(testValue));
|
||||
|
||||
testValue = float.Parse("654.321", CultureInfo.InvariantCulture);
|
||||
|
||||
context.Store.Set(null, "test", testValue);
|
||||
Assert.That(context.Store.Get<float>(null, "test"), Is.EqualTo(testValue));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Verifies that strings can be stored in the registry</summary>
|
||||
[Test]
|
||||
public void StringsCanBeStored() {
|
||||
using(var context = new TestContext()) {
|
||||
context.Store.Set(null, "test", "hello world");
|
||||
Assert.That(context.Store.Get<string>(null, "test"), Is.EqualTo("hello world"));
|
||||
|
||||
context.Store.Set(null, "test", "world hello");
|
||||
Assert.That(context.Store.Get<string>(null, "test"), Is.EqualTo("world hello"));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Verifies that long integers can be stored in the registry</summary>
|
||||
[Test]
|
||||
public void LongIntegersCanBeStored() {
|
||||
using(var context = new TestContext()) {
|
||||
context.Store.Set(null, "test", long.MaxValue);
|
||||
Assert.That(context.Store.Get<long>(null, "test"), Is.EqualTo(long.MaxValue));
|
||||
|
||||
context.Store.Set(null, "test", long.MinValue);
|
||||
Assert.That(context.Store.Get<long>(null, "test"), Is.EqualTo(long.MinValue));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Verifies that string arrays can be stored in the registry</summary>
|
||||
[Test]
|
||||
public void StringArraysCanBeStored() {
|
||||
string[] english = new string[] { "one", "two", "three" };
|
||||
string[] german = new string[] { "eins", "zwei", "drei" };
|
||||
|
||||
using(var context = new TestContext()) {
|
||||
context.Store.Set(null, "test", english);
|
||||
Assert.That(context.Store.Get<string[]>(null, "test"), Is.EquivalentTo(english));
|
||||
|
||||
context.Store.Set(null, "test", german);
|
||||
Assert.That(context.Store.Get<string[]>(null, "test"), Is.EquivalentTo(german));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that it's possible to enumerate a category that doesn't exist
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void NonExistingCategoryCanBeEnumerated() {
|
||||
using(var context = new TestContext()) {
|
||||
Assert.That(context.Store.EnumerateOptions("doesn't exist"), Is.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Verifies that byte arrays can be stored in the registry</summary>
|
||||
[Test]
|
||||
public void ByteArraysCanBeStored() {
|
||||
byte[] ascending = new byte[] { 1, 2, 3 };
|
||||
byte[] descending = new byte[] { 9, 8, 7 };
|
||||
|
||||
using(var context = new TestContext()) {
|
||||
context.Store.Set(null, "test", ascending);
|
||||
Assert.That(context.Store.Get<byte[]>(null, "test"), Is.EquivalentTo(ascending));
|
||||
|
||||
context.Store.Set(null, "test", descending);
|
||||
Assert.That(context.Store.Get<byte[]>(null, "test"), Is.EquivalentTo(descending));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Verifies that strings can be stored in the registry</summary>
|
||||
[Test]
|
||||
public void ValuesCanBeStoredInCategories() {
|
||||
using(var context = new TestContext()) {
|
||||
context.Store.Set("main", "test", "hello world");
|
||||
|
||||
string value;
|
||||
Assert.That(context.Store.TryGet<string>(null, "test", out value), Is.False);
|
||||
Assert.That(context.Store.Get<string>("main", "test"), Is.EqualTo("hello world"));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the subkeys of a registry key can be enumerated</summary>
|
||||
[Test]
|
||||
public void CategoriesCanBeEnumerated() {
|
||||
string[] names = new string[] { "one", "two", "three" };
|
||||
|
||||
using(var context = new TestContext()) {
|
||||
context.Store.Set(names[0], "sol", 21);
|
||||
context.Store.Set(names[1], "sol", 42);
|
||||
context.Store.Set(names[2], "sol", 84);
|
||||
|
||||
Assert.That(context.Store.EnumerateCategories(), Is.EquivalentTo(names));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the values under a registry subkey can be enumerated</summary>
|
||||
[Test]
|
||||
public void OptionsInCategoryCanBeEnumerated() {
|
||||
string[] names = new string[] { "one", "two", "three" };
|
||||
|
||||
using(var context = new TestContext()) {
|
||||
context.Store.Set("test", names[0], 1);
|
||||
context.Store.Set("test", names[1], 2);
|
||||
context.Store.Set("test", names[2], 3);
|
||||
|
||||
var optionInfos = new List<OptionInfo>(context.Store.EnumerateOptions("test"));
|
||||
Assert.That(optionInfos.Count, Is.EqualTo(3));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the values under a registry key can be enumerated</summary>
|
||||
[Test]
|
||||
public void RootOptionsCanBeEnumerated() {
|
||||
string[] names = new string[] { "one", "two", "three" };
|
||||
|
||||
using(var context = new TestContext()) {
|
||||
context.Store.Set(null, names[0], 1);
|
||||
context.Store.Set(null, names[1], 2);
|
||||
context.Store.Set(null, names[2], 3);
|
||||
|
||||
var optionInfos = new List<OptionInfo>(context.Store.EnumerateOptions(null));
|
||||
Assert.That(optionInfos.Count, Is.EqualTo(3));
|
||||
|
||||
string[] actualNames = new string[] {
|
||||
optionInfos[0].Name, optionInfos[1].Name, optionInfos[2].Name
|
||||
};
|
||||
Assert.That(actualNames, Is.EquivalentTo(names));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that accessing an option that doesn't exist throws an exception
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void AccessingNonExistingOptionThrowsException() {
|
||||
using(var context = new TestContext()) {
|
||||
Assert.That(
|
||||
() => context.Store.Get<string>(null, "doesn't exist"),
|
||||
Throws.Exception.AssignableTo<KeyNotFoundException>()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that accessing a category that doesn't exist throws an exception
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void AccessingNonExistingCategoryThrowsException() {
|
||||
using(var context = new TestContext()) {
|
||||
Assert.That(
|
||||
() => context.Store.Get<string>("doesn't exist", "test"),
|
||||
Throws.Exception.AssignableTo<KeyNotFoundException>()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that values can be removed from a registry key
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ValuesCanBeRemovedFromRoot() {
|
||||
using(var context = new TestContext()) {
|
||||
context.Store.Set(null, "nothing", "short-lived");
|
||||
Assert.That(context.Store.Remove(null, "nothing"), Is.True);
|
||||
Assert.That(context.Store.Remove(null, "nothing"), Is.False);
|
||||
|
||||
Assert.That(context.Store.EnumerateOptions(), Is.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that values can be removed from the subkey of a registry key
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ValuesCanBeRemovedFromCategory() {
|
||||
using(var context = new TestContext()) {
|
||||
context.Store.Set("limbo", "nothing", "short-lived");
|
||||
Assert.That(context.Store.Remove("limbo", "nothing"), Is.True);
|
||||
Assert.That(context.Store.Remove("limbo", "nothing"), Is.False);
|
||||
|
||||
Assert.That(context.Store.EnumerateOptions("limbo"), Is.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that values can be removed from a non-existing subkey without
|
||||
/// causing an error
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void RemovingValueFromNonExistingCategoryCanBeHandled() {
|
||||
using(var context = new TestContext()) {
|
||||
Assert.That(context.Store.Remove("empty", "nothing"), Is.False);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the store identifies the types of values stored in
|
||||
/// a registry when they are enumerated
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ValueTypesAreIdentifiedWhenEnumerating() {
|
||||
Type[] types = new Type[] {
|
||||
typeof(int),
|
||||
typeof(long),
|
||||
typeof(byte[]),
|
||||
typeof(string),
|
||||
typeof(string[])
|
||||
};
|
||||
using(var context = new TestContext()) {
|
||||
context.Store.Set<int>(null, "0", 123);
|
||||
context.Store.Set<long>(null, "1", 456L);
|
||||
context.Store.Set<byte[]>(null, "2", new byte[] { 7, 8, 9 });
|
||||
context.Store.Set<string>(null, "3", "text");
|
||||
context.Store.Set<string[]>(null, "4", new string[] { "many", "words" });
|
||||
|
||||
var optionInfos = new List<OptionInfo>(context.Store.EnumerateOptions());
|
||||
for(int index = 0; index < optionInfos.Count; ++index) {
|
||||
int typeIndex = int.Parse(optionInfos[index].Name);
|
||||
Assert.That(optionInfos[index].OptionType, Is.EqualTo(types[typeIndex]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Settings
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support {
|
||||
|
||||
/// <summary>Unit Test for the shared instance provider class</summary>
|
||||
[TestFixture]
|
||||
internal class SharedTest {
|
||||
|
||||
#region class Dummy
|
||||
|
||||
/// <summary>Dummy class for testing the shared instance provider</summary>
|
||||
private class Dummy {
|
||||
/// <summary>Initializes a new dummy</summary>
|
||||
public Dummy() {}
|
||||
}
|
||||
|
||||
#endregion // class Dummy
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the shared instance provider returns the same instance of a class
|
||||
/// when asked for the same class twice.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestSameInstance() {
|
||||
#pragma warning disable 0618
|
||||
Dummy dummyInstance = Shared<Dummy>.Instance;
|
||||
Dummy otherDummyInstance = Shared<Dummy>.Instance;
|
||||
#pragma warning restore 0618
|
||||
|
||||
// Make sure they're the same instance. We could have put an instance counter in
|
||||
// the dummy class, but this might or might not work well across multiple tests
|
||||
// because the order in which tests are executed is undefined and Shared<> changes
|
||||
// its global state when the first test is run by remembering the instance.
|
||||
//
|
||||
// Maybe this really is a defect in Shared<> and the class should be equipped with
|
||||
// a method such as Discard() or Dispose() to get rid of the instance?
|
||||
Assert.AreSame(dummyInstance, otherDummyInstance);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,374 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support {
|
||||
|
||||
/// <summary>
|
||||
/// Unit test for the helper class to .NET's string builder
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
internal class StringBuilderHelperTest {
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that bytes are correctly appended to a string builder
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestAppendByte() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.Append((byte)255, GarbagePolicy.Avoid);
|
||||
Assert.AreEqual(((byte)255).ToString(), builder.ToString());
|
||||
|
||||
builder.Clear();
|
||||
|
||||
builder.Append((byte)255, GarbagePolicy.Accept);
|
||||
Assert.AreEqual(((byte)255).ToString(), builder.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a byte with value 0 is correctly appended to a string builder
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestAppendNullByte() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.Append((byte)0, GarbagePolicy.Avoid);
|
||||
Assert.AreEqual(((byte)0).ToString(), builder.ToString());
|
||||
|
||||
builder.Clear();
|
||||
|
||||
builder.Append((byte)0, GarbagePolicy.Accept);
|
||||
Assert.AreEqual(((byte)0).ToString(), builder.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a positive integer is correctly appended to a string builder
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestAppendPositiveInteger() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.Append(12345, GarbagePolicy.Avoid);
|
||||
Assert.AreEqual((12345).ToString(), builder.ToString());
|
||||
|
||||
builder.Clear();
|
||||
|
||||
builder.Append(12345, GarbagePolicy.Accept);
|
||||
Assert.AreEqual((12345).ToString(), builder.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that an integer with value 0 is correctly appended to a string builder
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestAppendNullInteger() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.Append(0, GarbagePolicy.Avoid);
|
||||
Assert.AreEqual((0).ToString(), builder.ToString());
|
||||
|
||||
builder.Clear();
|
||||
|
||||
builder.Append(0, GarbagePolicy.Accept);
|
||||
Assert.AreEqual((0).ToString(), builder.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a negative integer is correctly appended to a string builder
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestAppendNegativeInteger() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.Append(-12345, GarbagePolicy.Avoid);
|
||||
Assert.AreEqual((-12345).ToString(), builder.ToString());
|
||||
|
||||
builder.Clear();
|
||||
|
||||
builder.Append(-12345, GarbagePolicy.Accept);
|
||||
Assert.AreEqual((-12345).ToString(), builder.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a positive long integer is correctly appended to a string builder
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestAppendPositiveLong() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.Append(12345L, GarbagePolicy.Avoid);
|
||||
Assert.AreEqual((12345L).ToString(), builder.ToString());
|
||||
|
||||
builder.Clear();
|
||||
|
||||
builder.Append(12345L, GarbagePolicy.Accept);
|
||||
Assert.AreEqual((12345L).ToString(), builder.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a long integer with value 0 is correctly appended to a string builder
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestAppendNullLong() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.Append(0L, GarbagePolicy.Avoid);
|
||||
Assert.AreEqual((0L).ToString(), builder.ToString());
|
||||
|
||||
builder.Clear();
|
||||
|
||||
builder.Append(0L, GarbagePolicy.Accept);
|
||||
Assert.AreEqual((0L).ToString(), builder.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a negative long integer is correctly appended to a string builder
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestAppendNegativeLong() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.Append(-12345L, GarbagePolicy.Avoid);
|
||||
Assert.AreEqual((-12345L).ToString(), builder.ToString());
|
||||
|
||||
builder.Clear();
|
||||
|
||||
builder.Append(-12345L, GarbagePolicy.Accept);
|
||||
Assert.AreEqual((-12345L).ToString(), builder.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that negative floating point values are correctly converted
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestAppendNegativeFloat() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.Append(-0.125f, GarbagePolicy.Avoid);
|
||||
Assert.AreEqual((-0.125f).ToString(), builder.ToString());
|
||||
|
||||
builder.Clear();
|
||||
|
||||
builder.Append(-0.125f, GarbagePolicy.Accept);
|
||||
Assert.AreEqual((-0.125f).ToString(), builder.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that positive floating point values are correctly converted
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestAppendPositiveFloat() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.Append(10.0625f, GarbagePolicy.Avoid);
|
||||
Assert.AreEqual((10.0625f).ToString(), builder.ToString());
|
||||
|
||||
builder.Clear();
|
||||
|
||||
builder.Append(10.0625f, GarbagePolicy.Accept);
|
||||
Assert.AreEqual((10.0625f).ToString(), builder.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that very small floating point values are correctly converted
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestAppendSmallFloat() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.Append(0.00390625f, GarbagePolicy.Avoid);
|
||||
Assert.AreEqual((0.00390625f).ToString(), builder.ToString());
|
||||
|
||||
builder.Clear();
|
||||
|
||||
builder.Append(0.00390625f, GarbagePolicy.Accept);
|
||||
Assert.AreEqual((0.00390625f).ToString(), builder.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that very large floating point values are correctly converted
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestAppendHugeFloat() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.Append(1000000000.0f, GarbagePolicy.Avoid);
|
||||
Assert.AreEqual((1000000000.0f).ToString("F1"), builder.ToString());
|
||||
|
||||
builder.Clear();
|
||||
|
||||
builder.Append(1000000000.0f, GarbagePolicy.Accept);
|
||||
Assert.AreEqual((1000000000.0f).ToString(), builder.ToString());
|
||||
}
|
||||
|
||||
/// <summary>Tests whether the number of decimal places can be restricted</summary>
|
||||
[Test]
|
||||
public void TestAppendFloatLimitDecimalPlaces() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.Append(0.00390625f, 3);
|
||||
Assert.AreEqual((0.003f).ToString(), builder.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a float with no decimal places is correctly appended
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestAppendFloatWithoutDecimalPlaces() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.Append(0.00390625f, 0);
|
||||
Assert.AreEqual((0.0f).ToString(), builder.ToString()); // Note: no rounding!
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies the behavior of the helper with unsupported floating point values
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestAppendOutOfRangeFloat() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
Assert.IsFalse(builder.Append(float.PositiveInfinity, GarbagePolicy.Avoid));
|
||||
Assert.IsFalse(builder.Append(float.NegativeInfinity, GarbagePolicy.Avoid));
|
||||
Assert.IsFalse(builder.Append(float.NaN, GarbagePolicy.Avoid));
|
||||
Assert.IsFalse(builder.Append(0.000000059604644775390625f, GarbagePolicy.Avoid));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that negative double precision floating point values are
|
||||
/// correctly converted
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestAppendNegativeDouble() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.Append(-32.015625, GarbagePolicy.Avoid);
|
||||
Assert.AreEqual((-32.015625).ToString(), builder.ToString());
|
||||
|
||||
builder.Clear();
|
||||
|
||||
builder.Append(-32.015625, GarbagePolicy.Accept);
|
||||
Assert.AreEqual((-32.015625).ToString(), builder.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that positive double precision floating point values are
|
||||
/// correctly converted
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestAppendPositiveDouble() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.Append(10.0625, GarbagePolicy.Avoid);
|
||||
Assert.AreEqual((10.0625).ToString(), builder.ToString());
|
||||
|
||||
builder.Clear();
|
||||
|
||||
builder.Append(10.0625, GarbagePolicy.Accept);
|
||||
Assert.AreEqual((10.0625).ToString(), builder.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that very small double precision floating point values are
|
||||
/// correctly converted
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestAppendSmallDouble() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.Append(0.00390625, GarbagePolicy.Avoid);
|
||||
Assert.AreEqual((0.00390625).ToString(), builder.ToString());
|
||||
|
||||
builder.Clear();
|
||||
|
||||
builder.Append(0.00390625, GarbagePolicy.Accept);
|
||||
Assert.AreEqual((0.00390625).ToString(), builder.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that very large double precision floating point values are
|
||||
/// correctly converted
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestAppendHugeDouble() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
builder.Append(1000000000000000000.0, GarbagePolicy.Avoid);
|
||||
Assert.AreEqual((1000000000000000000.0).ToString("F1"), builder.ToString());
|
||||
|
||||
builder.Clear();
|
||||
|
||||
builder.Append(1000000000000000000.0, GarbagePolicy.Accept);
|
||||
Assert.AreEqual((1000000000000000000.0).ToString(), builder.ToString());
|
||||
}
|
||||
|
||||
/// <summary>Tests whether the number of decimal places can be restricted</summary>
|
||||
[Test]
|
||||
public void TestAppendDoubleLimitDecimalPlaces() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
StringBuilderHelper.Append(builder, 0.00390625, 3);
|
||||
|
||||
Assert.AreEqual((0.003).ToString(), builder.ToString()); // Note: no rounding!
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a double with no decimal places is correctly appended
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestAppendDoubleWithoutDecimalPlaces() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
StringBuilderHelper.Append(builder, 0.00390625, 0);
|
||||
|
||||
Assert.AreEqual((0.0).ToString(), builder.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies the behavior of the helper with unsupported double precision
|
||||
/// floating point values
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestAppendOutOfRangeDouble() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
Assert.IsFalse(builder.Append(double.PositiveInfinity, GarbagePolicy.Avoid));
|
||||
Assert.IsFalse(builder.Append(double.NegativeInfinity, GarbagePolicy.Avoid));
|
||||
Assert.IsFalse(builder.Append(double.NaN, GarbagePolicy.Avoid));
|
||||
Assert.IsFalse(builder.Append(1.1102230246251565404236316680908e-16, GarbagePolicy.Avoid));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the contents of a string builder can be cleared
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestClear() {
|
||||
StringBuilder builder = new StringBuilder("Hello World");
|
||||
StringBuilderHelper.Clear(builder);
|
||||
|
||||
Assert.AreEqual(string.Empty, builder.ToString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,216 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support {
|
||||
|
||||
/// <summary>Unit Test for the string helper class</summary>
|
||||
[TestFixture]
|
||||
internal class StringHelperTest {
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the IndexNotOfAny() method works identical to the framework's
|
||||
/// implementation of the IndexOfAny() method, only inverted.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestIndexNotOfAny() {
|
||||
string positive = "xxxxxOOOOO";
|
||||
string negative = "OOOOOxxxxx";
|
||||
|
||||
Assert.AreEqual(
|
||||
positive.IndexOfAny(new char[] { 'O' }),
|
||||
StringHelper.IndexNotOfAny(negative, new char[] { 'O' })
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the IndexNotOfAny() method works identical to the framework's
|
||||
/// implementation of the IndexOfAny() method, only inverted, using a start index.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestIndexNotOfAnyWithStartIndex() {
|
||||
string positive = "OOOOOxxxxxOOOOO";
|
||||
string negative = "xxxxxOOOOOxxxxx";
|
||||
|
||||
Assert.AreEqual(
|
||||
positive.IndexOfAny(new char[] { 'O' }, 5),
|
||||
StringHelper.IndexNotOfAny(negative, new char[] { 'O' }, 5)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the LastIndexNotOfAny() method works identical to the framework's
|
||||
/// implementation of the LastIndexOfAny() method, only inverted.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestLastIndexNotOfAny() {
|
||||
string positive = "xxxxxOOOOO";
|
||||
string negative = "OOOOOxxxxx";
|
||||
|
||||
Assert.AreEqual(
|
||||
positive.LastIndexOfAny(new char[] { 'x' }),
|
||||
StringHelper.LastIndexNotOfAny(negative, new char[] { 'x' })
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the LastIndexNotOfAny() method works identical to the framework's
|
||||
/// implementation of the LastIndexOfAny() method, only inverted, using a start index.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestLastIndexNotOfAnyWithStartIndex() {
|
||||
string positive = "OOOOOxxxxxOOOOO";
|
||||
string negative = "xxxxxOOOOOxxxxx";
|
||||
|
||||
Assert.AreEqual(
|
||||
positive.LastIndexOfAny(new char[] { 'x' }, 5),
|
||||
StringHelper.LastIndexNotOfAny(negative, new char[] { 'x' }, 5)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the IndexNotOfAny() method works with multiple characters
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestMultipleCharIndexNotOfAny() {
|
||||
string positive = "abcde12345";
|
||||
string negative = "12345abcde";
|
||||
|
||||
Assert.AreEqual(
|
||||
positive.IndexOfAny(new char[] { '1', '2', '3', '4', '5' }),
|
||||
StringHelper.IndexNotOfAny(negative, new char[] { '1', '2', '3', '4', '5' })
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the IndexNotOfAny() method works with multiple characters,
|
||||
/// using a start index
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestMultipleCharIndexNotOfAnyWithStartIndex() {
|
||||
string positive = "12345abcde12345";
|
||||
string negative = "abcde12345abcde";
|
||||
|
||||
Assert.AreEqual(
|
||||
positive.IndexOfAny(new char[] { '1', '2', '3', '4', '5' }, 5),
|
||||
StringHelper.IndexNotOfAny(negative, new char[] { '1', '2', '3', '4', '5' }, 5)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the LastIndexNotOfAny() method works with multiple characters
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestMultipleCharLastIndexNotOfAny() {
|
||||
string positive = "abcde12345";
|
||||
string negative = "12345abcde";
|
||||
|
||||
Assert.AreEqual(
|
||||
positive.LastIndexOfAny(new char[] { 'a', 'b', 'c', 'd', 'e' }),
|
||||
StringHelper.LastIndexNotOfAny(negative, new char[] { 'a', 'b', 'c', 'd', 'e' })
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the LastIndexNotOfAny() method works with multiple characters,
|
||||
/// using a start index
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestMultipleCharLastIndexNotOfAnyWithStartIndex() {
|
||||
string positive = "12345abcde12345";
|
||||
string negative = "abcde12345abcde";
|
||||
|
||||
Assert.AreEqual(
|
||||
positive.LastIndexOfAny(new char[] { 'a', 'b', 'c', 'd', 'e' }, 5),
|
||||
StringHelper.LastIndexNotOfAny(negative, new char[] { 'a', 'b', 'c', 'd', 'e' }, 5)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the IndexNotOfAny() method fails when only matches are found
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestIndexNotOfAnyMatchesOnly() {
|
||||
string positive = "1234512345";
|
||||
string negative = "abcdeabcde";
|
||||
|
||||
Assert.AreEqual(
|
||||
positive.IndexOfAny(new char[] { 'a', 'b', 'c', 'd', 'e' }),
|
||||
StringHelper.IndexNotOfAny(negative, new char[] { 'a', 'b', 'c', 'd', 'e' })
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the IndexNotOfAny() method fails when only matches are found,
|
||||
/// using a start index
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestIndexNotOfAnyMatchesOnlyWithStartIndex() {
|
||||
string positive = "abcde1234512345";
|
||||
string negative = "12345abcdeabcde";
|
||||
|
||||
Assert.AreEqual(
|
||||
positive.IndexOfAny(new char[] { 'a', 'b', 'c', 'd', 'e' }, 5),
|
||||
StringHelper.IndexNotOfAny(negative, new char[] { 'a', 'b', 'c', 'd', 'e' }, 5)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the LastIndexNotOfAny() method fails when only matches are found
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestLastIndexNotOfAnyMatchesOnly() {
|
||||
string positive = "1234512345";
|
||||
string negative = "abcdeabcde";
|
||||
|
||||
Assert.AreEqual(
|
||||
positive.LastIndexOfAny(new char[] { 'a', 'b', 'c', 'd', 'e' }),
|
||||
StringHelper.LastIndexNotOfAny(negative, new char[] { 'a', 'b', 'c', 'd', 'e' })
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the LastIndexNotOfAny() method fails when only matches are found,
|
||||
/// using a start index
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestLastIndexNotOfAnyMatchesOnlyWithStartIndex() {
|
||||
string positive = "abcde1234512345";
|
||||
string negative = "12345abcdeabcde";
|
||||
|
||||
Assert.AreEqual(
|
||||
positive.LastIndexOfAny(new char[] { 'a', 'b', 'c', 'd', 'e' }, 5),
|
||||
StringHelper.LastIndexNotOfAny(negative, new char[] { 'a', 'b', 'c', 'd', 'e' }, 5)
|
||||
);
|
||||
}
|
||||
|
||||
// TODO: Also need unit tests for the 'length' argument
|
||||
// to guarantee the methods stop searching at the exact character
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,211 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support {
|
||||
|
||||
/// <summary>Unit Test for the string segment class</summary>
|
||||
[TestFixture]
|
||||
internal class StringSegmentTest {
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the default constructor of the StringSegment class throws the
|
||||
/// right exception when being passed 'null' instead of a string
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SimpleConstructorThrowsWhenStringIsNull() {
|
||||
Assert.Throws<ArgumentNullException>(
|
||||
delegate() { new StringSegment(null); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the simple constructor of the StringSegment class accepts
|
||||
/// an empty string
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SimpleConstructorAcceptsEmptyString() {
|
||||
new StringSegment(string.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the full constructor of the StringSegment class throws the
|
||||
/// right exception when being passed 'null' instead of a string
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ConstructorThrowsWhenStringIsNull() {
|
||||
Assert.Throws<ArgumentNullException>(
|
||||
delegate() { new StringSegment(null, 0, 0); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the full constructor of the StringSegment class accepts
|
||||
/// an empty string
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ConstructorAcceptsEmptyString() {
|
||||
new StringSegment(string.Empty, 0, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the full constructor of the StringSegment class throws the
|
||||
/// right exception when being passed an invalid start offset
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ConstructorThrowsOnInvalidOffset() {
|
||||
Assert.Throws<ArgumentOutOfRangeException>(
|
||||
delegate() { new StringSegment(string.Empty, -1, 0); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the full constructor of the StringSegment class throws the
|
||||
/// right exception when being passed an invalid string length
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ConstructorThrowsOnInvalidLength() {
|
||||
Assert.Throws<ArgumentOutOfRangeException>(
|
||||
delegate() { new StringSegment(string.Empty, 0, -1); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the full constructor of the StringSegment class throws the
|
||||
/// right exception when being passed a string length that's too large
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ConstructorThrowsOnLengthOverrun() {
|
||||
Assert.Throws<ArgumentException>(
|
||||
delegate() { new StringSegment("hello", 3, 3); }
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Tests whether the 'Text' property works as expected</summary>
|
||||
[Test]
|
||||
public void TextPropertyStoresOriginalString() {
|
||||
StringSegment testSegment = new StringSegment("hello", 1, 3);
|
||||
Assert.AreEqual("hello", testSegment.Text);
|
||||
}
|
||||
|
||||
/// <summary>Tests whether the 'Offset' property works as expected</summary>
|
||||
[Test]
|
||||
public void OffsetPropertyIsStored() {
|
||||
StringSegment testSegment = new StringSegment("hello", 1, 3);
|
||||
Assert.AreEqual(1, testSegment.Offset);
|
||||
}
|
||||
|
||||
/// <summary>Tests whether the 'Count' property works as expected</summary>
|
||||
[Test]
|
||||
public void CountPropertyIsStored() {
|
||||
StringSegment testSegment = new StringSegment("hello", 1, 3);
|
||||
Assert.AreEqual(3, testSegment.Count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether two differing instances produce different hash codes
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void DifferentInstancesHaveDifferentHashCodes_Usually() {
|
||||
StringSegment helloWorldSegment = new StringSegment("hello world", 2, 7);
|
||||
StringSegment howAreYouSegment = new StringSegment("how are you", 1, 9);
|
||||
|
||||
Assert.AreNotEqual(
|
||||
helloWorldSegment.GetHashCode(), howAreYouSegment.GetHashCode()
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether two equivalent instances produce an identical hash code
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void EquivalentInstancesHaveSameHashcode() {
|
||||
StringSegment helloWorld1Segment = new StringSegment("hello world", 2, 7);
|
||||
StringSegment helloWorld2Segment = new StringSegment("hello world", 2, 7);
|
||||
|
||||
Assert.AreEqual(
|
||||
helloWorld1Segment.GetHashCode(), helloWorld2Segment.GetHashCode()
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Tests the equals method performing a comparison against null</summary>
|
||||
[Test]
|
||||
public void EqualsAgainstNullIsAlwaysFalse() {
|
||||
StringSegment helloWorldSegment = new StringSegment("hello world", 2, 7);
|
||||
|
||||
Assert.IsFalse(
|
||||
helloWorldSegment.Equals(null)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Tests the equality operator with differing instances</summary>
|
||||
[Test]
|
||||
public void DifferingInstancesAreNotEqual() {
|
||||
StringSegment helloWorldSegment = new StringSegment("hello world", 2, 7);
|
||||
StringSegment howAreYouSegment = new StringSegment("how are you", 1, 9);
|
||||
|
||||
Assert.IsFalse(helloWorldSegment == howAreYouSegment);
|
||||
}
|
||||
|
||||
/// <summary>Tests the equality operator with equivalent instances</summary>
|
||||
[Test]
|
||||
public void EquivalentInstancesAreEqual() {
|
||||
StringSegment helloWorld1Segment = new StringSegment("hello world", 2, 7);
|
||||
StringSegment helloWorld2Segment = new StringSegment("hello world", 2, 7);
|
||||
|
||||
Assert.IsTrue(helloWorld1Segment == helloWorld2Segment);
|
||||
}
|
||||
|
||||
/// <summary>Tests the inequality operator with differing instances</summary>
|
||||
[Test]
|
||||
public void DifferingInstancesAreUnequal() {
|
||||
StringSegment helloWorldSegment = new StringSegment("hello world", 2, 7);
|
||||
StringSegment howAreYouSegment = new StringSegment("how are you", 1, 9);
|
||||
|
||||
Assert.IsTrue(helloWorldSegment != howAreYouSegment);
|
||||
}
|
||||
|
||||
/// <summary>Tests the inequality operator with equivalent instances</summary>
|
||||
[Test]
|
||||
public void EquivalentInstancesAreNotUnequal() {
|
||||
StringSegment helloWorld1Segment = new StringSegment("hello world", 2, 7);
|
||||
StringSegment helloWorld2Segment = new StringSegment("hello world", 2, 7);
|
||||
|
||||
Assert.IsFalse(helloWorld1Segment != helloWorld2Segment);
|
||||
}
|
||||
|
||||
/// <summary>Tests the ToString() method of the string segment</summary>
|
||||
[Test]
|
||||
public void TestToString() {
|
||||
StringSegment helloWorldSegment = new StringSegment("hello world", 4, 3);
|
||||
|
||||
Assert.AreEqual("o w", helloWorldSegment.ToString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,355 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.Threading {
|
||||
|
||||
/// <summary>Unit Test for the CPU core-affine thread pool</summary>
|
||||
[TestFixture]
|
||||
internal class AffineThreadPoolTest {
|
||||
|
||||
#region class TestTask
|
||||
|
||||
/// <summary>ThreadPool task that can be used for testing</summary>
|
||||
private class TestTask : IDisposable {
|
||||
|
||||
/// <summary>Initializes a new test task</summary>
|
||||
public TestTask() {
|
||||
this.callbackEvent = new ManualResetEvent(false);
|
||||
}
|
||||
|
||||
/// <summary>Immediately releases all resources owned by the instance</summary>
|
||||
public void Dispose() {
|
||||
if(this.callbackEvent != null) {
|
||||
this.callbackEvent.Close();
|
||||
this.callbackEvent = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Callback that can be added to the thread pool as a task</summary>
|
||||
/// <param name="state">User defined state</param>
|
||||
public void Callback(object state) {
|
||||
this.LastCallbackState = state;
|
||||
this.callbackEvent.Set();
|
||||
}
|
||||
|
||||
/// <summary>Event that will be set when the callback is executed</summary>
|
||||
public ManualResetEvent CallbackEvent {
|
||||
get { return this.callbackEvent; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// State parameter that was provide when the callback was called
|
||||
/// </summary>
|
||||
public volatile object LastCallbackState;
|
||||
|
||||
/// <summary>Event that will be set when the callback is invoked</summary>
|
||||
private ManualResetEvent callbackEvent;
|
||||
|
||||
}
|
||||
|
||||
#endregion // class TestTask
|
||||
|
||||
#region class WaitTask
|
||||
|
||||
/// <summary>ThreadPool task that can be used for testing</summary>
|
||||
private class WaitTask : IDisposable {
|
||||
|
||||
/// <summary>Initializes a new test task</summary>
|
||||
public WaitTask() {
|
||||
this.startEvent = new ManualResetEvent(false);
|
||||
this.finishEvent = new ManualResetEvent(false);
|
||||
this.waitEvent = new ManualResetEvent(false);
|
||||
}
|
||||
|
||||
/// <summary>Immediately releases all resources owned by the instance</summary>
|
||||
public void Dispose() {
|
||||
if(this.waitEvent != null) {
|
||||
this.waitEvent.Close();
|
||||
this.waitEvent = null;
|
||||
}
|
||||
if(this.finishEvent != null) {
|
||||
this.finishEvent.Close();
|
||||
this.finishEvent = null;
|
||||
}
|
||||
if(this.startEvent != null) {
|
||||
this.startEvent.Close();
|
||||
this.startEvent = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Callback that can be added to the thread pool as a task</summary>
|
||||
/// <param name="state">User defined state</param>
|
||||
public void Callback(object state) {
|
||||
this.LastCallbackState = state;
|
||||
this.startEvent.Set();
|
||||
this.waitEvent.WaitOne();
|
||||
this.finishEvent.Set();
|
||||
}
|
||||
|
||||
/// <summary>Event that will be set when the callback has started</summary>
|
||||
public ManualResetEvent StartEvent {
|
||||
get { return this.startEvent; }
|
||||
}
|
||||
|
||||
/// <summary>Event that will be set when the callback has finished</summary>
|
||||
public ManualResetEvent FinishEvent {
|
||||
get { return this.finishEvent; }
|
||||
}
|
||||
|
||||
/// <summary>Event that blocks the callback</summary>
|
||||
public ManualResetEvent WaitEvent {
|
||||
get { return this.waitEvent; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// State parameter that was provide when the callback was called
|
||||
/// </summary>
|
||||
public volatile object LastCallbackState;
|
||||
|
||||
/// <summary>Event that will be set when the callback has started</summary>
|
||||
private ManualResetEvent startEvent;
|
||||
/// <summary>Event that will be set when the callback has finished</summary>
|
||||
private ManualResetEvent finishEvent;
|
||||
/// <summary>Event used to block the callback</summary>
|
||||
private ManualResetEvent waitEvent;
|
||||
|
||||
}
|
||||
|
||||
#endregion // class WaitTask
|
||||
|
||||
#if false
|
||||
#region class ThrowingDisposable
|
||||
|
||||
/// <summary>Throws an exception when it is disposed</summary>
|
||||
private class ThrowingDisposable : IDisposable {
|
||||
|
||||
/// <summary>Immediately releases all resources owned by the instance</summary>
|
||||
public void Dispose() {
|
||||
throw new ArithmeticException("Simulated exception for unit testing");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endregion // class ThrowingDisposable
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the Thread Pool's default assertion handler is working
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestDefaultAssertionHandler() {
|
||||
|
||||
// We can't test a failing assertion because our tests need to run
|
||||
// unattended on a build server without blocking for user input.
|
||||
AffineThreadPool.DefaultAssertionHandler(
|
||||
true, "Unit test", "This should not fail"
|
||||
);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>Tests whether the QueueUserWorkItem() method is working</summary>
|
||||
[Test]
|
||||
public void TestQueueUserWorkItem() {
|
||||
using(TestTask task = new TestTask()) {
|
||||
AffineThreadPool.QueueUserWorkItem(task.Callback);
|
||||
Assert.IsTrue(task.CallbackEvent.WaitOne(1000));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the QueueUserWorkItem() method is passing the state parameter
|
||||
/// on to the callback
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestQueueUserWorkItemWithState() {
|
||||
using(TestTask task = new TestTask()) {
|
||||
object state = new object();
|
||||
|
||||
AffineThreadPool.QueueUserWorkItem(task.Callback, state);
|
||||
|
||||
Assert.IsTrue(task.CallbackEvent.WaitOne(1000));
|
||||
Assert.AreSame(state, task.LastCallbackState);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the thread pool can handle an exception from a user work item
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestExceptionFromUserWorkItem() {
|
||||
using(ManualResetEvent exceptionEvent = new ManualResetEvent(false)) {
|
||||
AffineThreadPool.ExceptionDelegate oldExceptionHandler =
|
||||
AffineThreadPool.ExceptionHandler;
|
||||
|
||||
AffineThreadPool.ExceptionHandler = delegate(Exception exception) {
|
||||
exceptionEvent.Set();
|
||||
};
|
||||
try {
|
||||
AffineThreadPool.QueueUserWorkItem(
|
||||
delegate(object state) { throw new KeyNotFoundException(); }
|
||||
);
|
||||
Assert.IsTrue(exceptionEvent.WaitOne(1000));
|
||||
}
|
||||
finally {
|
||||
AffineThreadPool.ExceptionHandler = oldExceptionHandler;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the affine thread pool's maximum thread count equals
|
||||
/// the number of logical processors in the system
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestMaxThreadsProperty() {
|
||||
Assert.AreEqual(Environment.ProcessorCount, AffineThreadPool.MaxThreads);
|
||||
}
|
||||
|
||||
#if WINDOWS
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the ProcessThread instance for a system thread id can
|
||||
/// be determined using the GetProcessThread() method
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CanGetProcessThreadForManagedThread() {
|
||||
if(Environment.OSVersion.Platform == PlatformID.Win32NT) {
|
||||
Thread.BeginThreadAffinity();
|
||||
try {
|
||||
int threadId = AffineThreadPool.GetCurrentThreadId();
|
||||
|
||||
Assert.IsNotNull(AffineThreadPool.GetProcessThread(threadId));
|
||||
Assert.IsNull(AffineThreadPool.GetProcessThread(0));
|
||||
}
|
||||
finally {
|
||||
Thread.EndThreadAffinity();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // WINDOWS
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the afine thread pool's default exception handler works
|
||||
/// as expected
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestDefaultExceptionHandler() {
|
||||
Assert.Throws<ArrayTypeMismatchException>(
|
||||
delegate() {
|
||||
AffineThreadPool.ExceptionHandler(new ArrayTypeMismatchException("Test"));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the waiting work items count and active thread count are
|
||||
/// updated by the thread pool.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestWaitingWorkItemsProperty() {
|
||||
int eventCount = AffineThreadPool.Processors;
|
||||
WaitTask[] tasks = new WaitTask[eventCount];
|
||||
|
||||
int createdTasks = 0;
|
||||
try {
|
||||
// CHECK: Is there danger that the thread pool still has not finished
|
||||
// queued items for other unit tests, thereby failing to meet
|
||||
// our expected task counts?
|
||||
|
||||
// Create the tasks, counting up the created task counter. If an exception
|
||||
// occurs, we will roll back from there.
|
||||
for(createdTasks = 0; createdTasks < eventCount; ++createdTasks) {
|
||||
tasks[createdTasks] = new WaitTask();
|
||||
}
|
||||
|
||||
// Schedule the blocking tasks in the thread pool so it will not be able
|
||||
// to process the next task we add to the queue
|
||||
for(int index = 0; index < eventCount; ++index) {
|
||||
AffineThreadPool.QueueUserWorkItem(tasks[index].Callback);
|
||||
}
|
||||
|
||||
// Wait for the tasks to start so they aren't preempted by the tasks we're
|
||||
// going to add (which would finish immediately). The affine thread pool
|
||||
// works on a first come first serve basis, but we don't want to rely on this
|
||||
// implementation detail in the unit test.
|
||||
for(int index = 0; index < eventCount; ++index) {
|
||||
Assert.IsTrue(
|
||||
tasks[index].StartEvent.WaitOne(10000),
|
||||
"Task " + index.ToString() + " was started"
|
||||
);
|
||||
}
|
||||
|
||||
// All Thread should now be active and no work items should be waiting
|
||||
Assert.AreEqual(
|
||||
createdTasks, AffineThreadPool.ActiveThreads,
|
||||
"ActiveThreads property equals number of tasks"
|
||||
);
|
||||
Assert.AreEqual(
|
||||
0, AffineThreadPool.WaitingWorkItems,
|
||||
"No waiting work items are in the queue"
|
||||
);
|
||||
|
||||
// Add a task to the queue and make sure the waiting work item count goes up
|
||||
AffineThreadPool.QueueUserWorkItem(delegate(object state) { });
|
||||
Assert.AreEqual(
|
||||
1, AffineThreadPool.WaitingWorkItems,
|
||||
"Added work item is waiting in the queue"
|
||||
);
|
||||
|
||||
// The same again. Now we should have 2 work items sitting in the queue
|
||||
AffineThreadPool.QueueUserWorkItem(delegate(object state) { });
|
||||
Assert.AreEqual(
|
||||
2, AffineThreadPool.WaitingWorkItems,
|
||||
"Both added work items are waiting in the queue"
|
||||
);
|
||||
|
||||
// Let the WaitTasks finish so we're not blocking the thread pool any longer
|
||||
for(int index = 0; index < eventCount; ++index) {
|
||||
tasks[index].WaitEvent.Set();
|
||||
}
|
||||
|
||||
// Wait for the tasks to end before we get rid of them
|
||||
for(int index = 0; index < eventCount; ++index) {
|
||||
Assert.IsTrue(
|
||||
tasks[index].FinishEvent.WaitOne(1000),
|
||||
"Task " + index.ToString() + " has finished"
|
||||
);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
for(--createdTasks; createdTasks >= 0; --createdTasks) {
|
||||
tasks[createdTasks].Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Threading
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,257 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if !NO_CONCURRENT_COLLECTIONS
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.Threading {
|
||||
|
||||
/// <summary>Unit Test for the parallel background worker class</summary>
|
||||
[TestFixture]
|
||||
internal class ParallelBackgroundWorkerTest {
|
||||
|
||||
#region class TestWorker
|
||||
|
||||
/// <summary>Implementation of a background worker used for unit testing</summary>
|
||||
#pragma warning disable 0618
|
||||
private class TestWorker : ParallelBackgroundWorker<object> {
|
||||
#pragma warning restore 0618
|
||||
|
||||
/// <summary>Initializes a new parallel background worker with unlimited threads</summary>
|
||||
public TestWorker() : base() { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new parallel background worker running the specified number
|
||||
/// of tasks in parallel
|
||||
/// </summary>
|
||||
/// <param name="threadCount">
|
||||
/// Number of tasks to run in parallel (if positive) or number of CPU cores to leave
|
||||
/// unused (if negative).
|
||||
/// </param>
|
||||
/// <remarks>
|
||||
/// If a negative number of threads is used, at least one thread will be always
|
||||
/// be created, so specifying -2 on a single-core system will still occupy
|
||||
/// the only core.
|
||||
/// </remarks>
|
||||
public TestWorker(int threadCount) : base(threadCount) { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new parallel background worker that uses the specified name for
|
||||
/// its worker threads.
|
||||
/// </summary>
|
||||
/// <param name="name">Name that will be assigned to the worker threads</param>
|
||||
public TestWorker(string name) : base(name) { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new parallel background worker that uses the specified name for
|
||||
/// its worker threads and running the specified number of tasks in parallel.
|
||||
/// </summary>
|
||||
/// <param name="name">Name that will be assigned to the worker threads</param>
|
||||
/// <param name="threadCount">
|
||||
/// Number of tasks to run in parallel (if positive) or number of CPU cores to leave
|
||||
/// unused (if negative).
|
||||
/// </param>
|
||||
/// <remarks>
|
||||
/// If a negative number of threads is used, at least one thread will be always
|
||||
/// be created, so specifying -2 on a single-core system will still occupy
|
||||
/// the only core.
|
||||
/// </remarks>
|
||||
public TestWorker(string name, int threadCount) : base(name, threadCount) { }
|
||||
|
||||
/// <summary>Called in a thread to execute a single task</summary>
|
||||
/// <param name="task">Task that should be executed</param>
|
||||
/// <param name="cancellationToken">
|
||||
/// Cancellation token through which the method can be signalled to cancel
|
||||
/// </param>
|
||||
protected override void Run(object task, CancellationToken cancellationToken) {
|
||||
if(this.ThrowException) {
|
||||
throw new Exception("Something went wrong");
|
||||
}
|
||||
|
||||
if(this.WaitEvent != null) {
|
||||
this.WaitEvent.WaitOne();
|
||||
}
|
||||
|
||||
this.WasCancelled = cancellationToken.IsCancellationRequested;
|
||||
|
||||
if(this.Tasks != null) {
|
||||
lock(this.Tasks) {
|
||||
this.Tasks.Add(task);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Whether the work tasks should throw exceptions</summary>
|
||||
public bool ThrowException;
|
||||
/// <summary>Event that can be used to stop work tasks from completing</summary>
|
||||
public ManualResetEvent WaitEvent;
|
||||
|
||||
/// <summary>Set by work tasks if they have been cancelled</summary>
|
||||
public bool WasCancelled;
|
||||
/// <summary>Work tasks that have reached execution</summary>
|
||||
public ICollection<object> Tasks;
|
||||
|
||||
}
|
||||
|
||||
#endregion // class TestWorker
|
||||
|
||||
/// <summary>Verifies that the background worker has a default constructor</summary>
|
||||
[Test]
|
||||
public void CanBeDefaultConstructed() {
|
||||
using(new TestWorker()) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a background worker can be constructed that uses a fixed number
|
||||
/// of threads
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CanUseFixedNumberOfThreads() {
|
||||
using(new TestWorker(4)) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a background worker can be constructed that leaves free a fixed
|
||||
/// number of CPU cores
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CanPreserveFixedNumberOfCores() {
|
||||
using(new TestWorker(-2)) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a background worker can be constructed using a specific name
|
||||
/// for its worker threads
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CanUseNamedThreads() {
|
||||
using(new TestWorker("Test Task Thread")) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a background worker can be constructed that uses a fixed number
|
||||
/// of threads using a specific name
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CanUseFixedNumberOfNamedThreads() {
|
||||
using(new TestWorker("Test Task Thread", 4)) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that a background worker can be constructed that leaves free a fixed
|
||||
/// number of CPU cores and uses a specific name for its worker threads.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CanPreserveFixedNumberOfCoresAndUseNamedThreads() {
|
||||
using(new TestWorker("Test Task Thread", -2)) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that exceptions happening inside the tasks are collected and re-thrown
|
||||
/// in the Join() method.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ExceptionsAreReThrownInJoin() {
|
||||
using(var testWorker = new TestWorker()) {
|
||||
testWorker.ThrowException = true;
|
||||
testWorker.AddTask(new object());
|
||||
testWorker.AddTask(new object());
|
||||
|
||||
Assert.Throws<AggregateException>(
|
||||
() => {
|
||||
testWorker.Join();
|
||||
}
|
||||
);
|
||||
|
||||
try {
|
||||
testWorker.Join();
|
||||
Assert.Fail(
|
||||
"Calling ParallelBackgroundWorker.Join() multiple times should re-throw " +
|
||||
"exceptions multiple times"
|
||||
);
|
||||
}
|
||||
catch(AggregateException aggregateException) {
|
||||
Assert.AreEqual(2, aggregateException.InnerExceptions.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that tasks can be cancelled while they are running
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TasksCanBeCancelled() {
|
||||
using(var waitEvent = new ManualResetEvent(false)) {
|
||||
using(var testWorker = new TestWorker()) {
|
||||
testWorker.WaitEvent = waitEvent;
|
||||
|
||||
testWorker.AddTask(new object());
|
||||
testWorker.CancelRunningTasks();
|
||||
|
||||
waitEvent.Set();
|
||||
|
||||
Assert.IsTrue(testWorker.Wait(1000));
|
||||
|
||||
Assert.IsTrue(testWorker.WasCancelled);
|
||||
}
|
||||
} // disposes waitEvent
|
||||
}
|
||||
|
||||
/// <summary>Verifies that calling Join() waits for all queued tasks</summary>
|
||||
[Test]
|
||||
public void JoinWaitsForQueuedTasks() {
|
||||
var tasks = new List<object>(100);
|
||||
for(int index = 0; index < 100; ++index) {
|
||||
tasks.Add(new object());
|
||||
}
|
||||
|
||||
using(var waitEvent = new ManualResetEvent(false)) {
|
||||
using(var testWorker = new TestWorker(2)) {
|
||||
testWorker.WaitEvent = waitEvent;
|
||||
testWorker.Tasks = new List<object>();
|
||||
for(int index = 0; index < 100; ++index) {
|
||||
testWorker.AddTask(tasks[index]);
|
||||
}
|
||||
|
||||
CollectionAssert.IsEmpty(testWorker.Tasks);
|
||||
|
||||
waitEvent.Set();
|
||||
testWorker.Join();
|
||||
|
||||
lock(testWorker.Tasks) {
|
||||
CollectionAssert.AreEquivalent(tasks, testWorker.Tasks);
|
||||
}
|
||||
}
|
||||
} // disposes waitEvent
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Threading
|
||||
|
||||
#endif // UNITTEST
|
||||
|
||||
#endif // !NO_CONCURRENT_COLLECTIONS
|
||||
|
|
@ -1,459 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if !NO_CONCURRENT_COLLECTIONS
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support.Threading {
|
||||
|
||||
/// <summary>Unit Test for the thread runner class</summary>
|
||||
[TestFixture]
|
||||
internal class ThreadRunnerTest {
|
||||
|
||||
#region class DefaultDisposeRunner
|
||||
|
||||
/// <summary>Implementation of a thread runner to check default dispose behavior</summary>
|
||||
private class DefaultDisposeRunner : ThreadRunner {
|
||||
|
||||
/// <summary>Reports an error</summary>
|
||||
/// <param name="exception">Error that will be reported</param>
|
||||
protected override void ReportError(Exception exception) { }
|
||||
|
||||
/// <summary>Called when the status of the busy flag changes</summary>
|
||||
protected override void BusyChanged() { }
|
||||
|
||||
}
|
||||
|
||||
#endregion // class DefaultDisposeRunner
|
||||
|
||||
#region class DummyRunner
|
||||
|
||||
/// <summary>Implementation of a thread runner used for unit testing</summary>
|
||||
private class DummyRunner : ThreadRunner {
|
||||
|
||||
/// <summary>Initializes a new dummy thread runner</summary>
|
||||
public DummyRunner() : base() {
|
||||
this.completionGate = new ManualResetEvent(initialState: false);
|
||||
}
|
||||
|
||||
/// <summary>Immediately frees all resources used by the instance</summary>
|
||||
public new void Dispose() {
|
||||
base.Dispose(100);
|
||||
|
||||
if(this.completionGate != null) {
|
||||
this.completionGate.Dispose();
|
||||
this.completionGate = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Waits for the task for complete (all of 100 milliseconds)</summary>
|
||||
/// <returns>True if the task completed, false if it continues running</returns>
|
||||
public bool WaitForCompletion() {
|
||||
return this.completionGate.WaitOne(100);
|
||||
}
|
||||
|
||||
/// <summary>How often the status of the busy flag has changed</summary>
|
||||
public int BusyChangeCount {
|
||||
get { return this.busyChangeCount; }
|
||||
}
|
||||
|
||||
/// <summary>Error that has been reported the last time a task was run</summary>
|
||||
public Exception ReportedError {
|
||||
get { return this.reportedError; }
|
||||
}
|
||||
|
||||
/// <summary>Reports an error</summary>
|
||||
/// <param name="exception">Error that will be reported</param>
|
||||
protected override void ReportError(Exception exception) {
|
||||
this.reportedError = exception;
|
||||
}
|
||||
|
||||
/// <summary>Called when the status of the busy flag changes</summary>
|
||||
protected override void BusyChanged() {
|
||||
++busyChangeCount;
|
||||
if((busyChangeCount >= 2) && (base.IsBusy == false)) {
|
||||
this.completionGate.Set();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Last error that was reported in the thread</summary>
|
||||
private Exception reportedError;
|
||||
/// <summary>Number of times the busy state of the runner has changed</summary>
|
||||
private int busyChangeCount;
|
||||
/// <summary>Triggered when the busy event has performed a double flank</summary>
|
||||
private ManualResetEvent completionGate;
|
||||
|
||||
}
|
||||
|
||||
#endregion // class DummyRunner
|
||||
|
||||
#region class DummyTask
|
||||
|
||||
/// <summary>Dummy task that can be executed by a thread runner</summary>
|
||||
private class DummyTask : IDisposable {
|
||||
|
||||
/// <summary>Initializes a new dummy task</summary>
|
||||
/// <param name="delayMilliseconds">How long the task shoudl take to execute</param>
|
||||
public DummyTask(int delayMilliseconds) {
|
||||
this.startGate = new ManualResetEvent(initialState: false);
|
||||
this.delayMilliseconds = delayMilliseconds;
|
||||
}
|
||||
|
||||
/// <summary>Immediately releases all resources owned by the instance</summary>
|
||||
public void Dispose() {
|
||||
if(this.startGate != null) {
|
||||
this.startGate.Dispose();
|
||||
this.startGate = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Waits for the task to start (all of 100 milliseconds)</summary>
|
||||
/// <returns>True if the start started, false if it didn't</returns>
|
||||
public bool WaitForStart() {
|
||||
return this.startGate.WaitOne(100);
|
||||
}
|
||||
|
||||
/// <summary>Sets the task up to fail with the specified error</summary>
|
||||
/// <param name="error">Error the task will fail with</param>
|
||||
public void FailWith(Exception error) {
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
/// <summary>Runs the task with no arguments</summary>
|
||||
public void Run() {
|
||||
this.startGate.Set();
|
||||
|
||||
++this.executionCount;
|
||||
Thread.Sleep(this.delayMilliseconds);
|
||||
if(this.error != null) {
|
||||
throw this.error;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Runs the task with one argument</summary>
|
||||
/// <param name="firstArgument">First argument passed from the runner</param>
|
||||
public void Run(float firstArgument) {
|
||||
this.startGate.Set();
|
||||
|
||||
++this.executionCount;
|
||||
this.firstArgument = firstArgument;
|
||||
Thread.Sleep(this.delayMilliseconds);
|
||||
if(this.error != null) {
|
||||
throw this.error;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Runs the task with two argument</summary>
|
||||
/// <param name="firstArgument">First argument passed from the runner</param>
|
||||
/// <param name="secondArgument">Second argument passed from the runner</param>
|
||||
public void Run(float firstArgument, string secondArgument) {
|
||||
this.startGate.Set();
|
||||
|
||||
++this.executionCount;
|
||||
this.firstArgument = firstArgument;
|
||||
this.secondArgument = secondArgument;
|
||||
Thread.Sleep(this.delayMilliseconds);
|
||||
if(this.error != null) {
|
||||
throw this.error;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Runs the task with no arguments</summary>
|
||||
/// <param name="cancellationToken">Token by which cancellation can be signalled</param>
|
||||
public void RunCancellable(CancellationToken cancellationToken) {
|
||||
this.startGate.Set();
|
||||
|
||||
++this.executionCount;
|
||||
|
||||
if(delayMilliseconds == 0) {
|
||||
Thread.Sleep(0);
|
||||
} else {
|
||||
if(cancellationToken.WaitHandle.WaitOne(delayMilliseconds)) {
|
||||
this.wasCancelled = cancellationToken.IsCancellationRequested;
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
}
|
||||
}
|
||||
if(this.error != null) {
|
||||
throw this.error;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Runs the task with one argument</summary>
|
||||
/// <param name="firstArgument">First argument passed from the runner</param>
|
||||
/// <param name="cancellationToken">Token by which cancellation can be signalled</param>
|
||||
public void RunCancellable(float firstArgument, CancellationToken cancellationToken) {
|
||||
this.startGate.Set();
|
||||
|
||||
++this.executionCount;
|
||||
this.firstArgument = firstArgument;
|
||||
|
||||
if(delayMilliseconds == 0) {
|
||||
Thread.Sleep(0);
|
||||
} else {
|
||||
if(cancellationToken.WaitHandle.WaitOne(delayMilliseconds)) {
|
||||
this.wasCancelled = cancellationToken.IsCancellationRequested;
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
}
|
||||
}
|
||||
if(this.error != null) {
|
||||
throw this.error;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Runs the task with two argument</summary>
|
||||
/// <param name="firstArgument">First argument passed from the runner</param>
|
||||
/// <param name="secondArgument">Second argument passed from the runner</param>
|
||||
/// <param name="cancellationToken">Token by which cancellation can be signalled</param>
|
||||
public void RunCancellable(
|
||||
float firstArgument, string secondArgument, CancellationToken cancellationToken
|
||||
) {
|
||||
this.startGate.Set();
|
||||
|
||||
++this.executionCount;
|
||||
this.firstArgument = firstArgument;
|
||||
this.secondArgument = secondArgument;
|
||||
|
||||
if(delayMilliseconds == 0) {
|
||||
Thread.Sleep(0);
|
||||
} else {
|
||||
if(cancellationToken.WaitHandle.WaitOne(delayMilliseconds)) {
|
||||
this.wasCancelled = cancellationToken.IsCancellationRequested;
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
}
|
||||
}
|
||||
if(this.error != null) {
|
||||
throw this.error;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>How many times the task was run</summary>
|
||||
public int ExecutionCount {
|
||||
get { return this.executionCount; }
|
||||
}
|
||||
|
||||
/// <summary>Whether the task was cancelled by the runner itself</summary>
|
||||
public bool WasCancelled {
|
||||
get { return this.wasCancelled; }
|
||||
}
|
||||
|
||||
/// <summary>What the first argument was during the last call</summary>
|
||||
public float FirstArgument {
|
||||
get { return this.firstArgument; }
|
||||
}
|
||||
|
||||
/// <summary>What the second argument was during the last call</summary>
|
||||
public string SecondArgument {
|
||||
get { return this.secondArgument; }
|
||||
}
|
||||
|
||||
/// <summary>Last error that was reported in the thread</summary>
|
||||
private Exception error;
|
||||
/// <summary>Triggered when the task has started</summary>
|
||||
private ManualResetEvent startGate;
|
||||
/// <summary>How long the task should take to execute in milliseconds</summary>
|
||||
private int delayMilliseconds;
|
||||
/// <summary>How many times the task has been executed</summary>
|
||||
private volatile int executionCount;
|
||||
/// <summary>Whether the task has been cancelled</summary>
|
||||
private volatile bool wasCancelled;
|
||||
/// <summary>First argument that was passed to the task</summary>
|
||||
private volatile float firstArgument;
|
||||
/// <summary>Second argument that was passed to the task</summary>
|
||||
private volatile string secondArgument;
|
||||
|
||||
}
|
||||
|
||||
#endregion // class DummyRunner
|
||||
|
||||
/// <summary>Verifies that the thread runner has a default constructor</summary>
|
||||
[Test]
|
||||
public void CanBeDefaultConstructed() {
|
||||
using(new DummyRunner()) { }
|
||||
}
|
||||
|
||||
/// <summary>Checks that the runner sets and unsets its busy flag</summary>
|
||||
[Test]
|
||||
public void BusyFlagIsToggled() {
|
||||
using(var runner = new DummyRunner()) {
|
||||
int busyFlagChangeCount = runner.BusyChangeCount;
|
||||
Assert.IsFalse(runner.IsBusy);
|
||||
|
||||
runner.RunInBackground((Action)delegate() { });
|
||||
Assert.IsTrue(runner.WaitForCompletion());
|
||||
|
||||
Assert.GreaterOrEqual(busyFlagChangeCount + 2, runner.BusyChangeCount);
|
||||
Assert.IsFalse(runner.IsBusy);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Lets the thread runner run a simple task in the background</summary>
|
||||
[Test]
|
||||
public void CanRunSimpleTaskInBackground() {
|
||||
using(var task = new DummyTask(0)) {
|
||||
using(var runner = new DummyRunner()) {
|
||||
runner.RunInBackground(new Action(task.Run));
|
||||
|
||||
Assert.IsTrue(runner.WaitForCompletion());
|
||||
Assert.IsNull(runner.ReportedError);
|
||||
}
|
||||
|
||||
Assert.AreEqual(1, task.ExecutionCount);
|
||||
Assert.IsFalse(task.WasCancelled);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks that the thread runner is able to pass a single argument to a task
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CanPassSingleArgumentToSimpleTask() {
|
||||
using(var task = new DummyTask(0)) {
|
||||
using(var runner = new DummyRunner()) {
|
||||
runner.RunInBackground(new Action<float>(task.Run), 12.43f);
|
||||
|
||||
Assert.IsTrue(runner.WaitForCompletion());
|
||||
Assert.IsNull(runner.ReportedError);
|
||||
}
|
||||
|
||||
Assert.AreEqual(1, task.ExecutionCount);
|
||||
Assert.AreEqual(12.43f, task.FirstArgument);
|
||||
Assert.IsFalse(task.WasCancelled);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks that the thread runner is able to pass two arguments to a task
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CanPassTwoArgumentsToSimpleTask() {
|
||||
using(var task = new DummyTask(0)) {
|
||||
using(var runner = new DummyRunner()) {
|
||||
runner.RunInBackground(new Action<float, string>(task.Run), 98.67f, "Hello");
|
||||
|
||||
Assert.IsTrue(runner.WaitForCompletion());
|
||||
Assert.IsNull(runner.ReportedError);
|
||||
}
|
||||
|
||||
Assert.AreEqual(1, task.ExecutionCount);
|
||||
Assert.AreEqual(98.67f, task.FirstArgument);
|
||||
Assert.AreEqual("Hello", task.SecondArgument);
|
||||
Assert.IsFalse(task.WasCancelled);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that an error happening in a simple task is reported correctly
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SimpleTaskErrorIsReported() {
|
||||
using(var task = new DummyTask(0)) {
|
||||
var error = new InvalidOperationException("Mooh!");
|
||||
task.FailWith(error);
|
||||
|
||||
using(var runner = new DummyRunner()) {
|
||||
runner.RunInBackground(new Action(task.Run));
|
||||
|
||||
Assert.IsTrue(runner.WaitForCompletion());
|
||||
Assert.AreSame(error, runner.ReportedError);
|
||||
}
|
||||
|
||||
Assert.AreEqual(1, task.ExecutionCount);
|
||||
Assert.IsFalse(task.WasCancelled);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Lets the thread runner run a cancellable task in the background</summary>
|
||||
[Test]
|
||||
public void CanRunCancellableTaskInBackground() {
|
||||
using(var task = new DummyTask(100)) {
|
||||
using(var runner = new DummyRunner()) {
|
||||
runner.RunInBackground(new CancellableAction(task.RunCancellable));
|
||||
Assert.IsTrue(task.WaitForStart());
|
||||
runner.CancelAllBackgroundOperations();
|
||||
|
||||
Assert.IsTrue(runner.WaitForCompletion());
|
||||
Assert.IsNull(runner.ReportedError);
|
||||
}
|
||||
|
||||
Assert.AreEqual(1, task.ExecutionCount);
|
||||
Assert.IsTrue(task.WasCancelled);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks that the thread runner is able to pass a single argument to a task
|
||||
/// that can be cancelled
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CanPassSingleArgumentToCancellableTask() {
|
||||
using(var task = new DummyTask(100)) {
|
||||
using(var runner = new DummyRunner()) {
|
||||
runner.RunInBackground(new CancellableAction<float>(task.RunCancellable), 12.43f);
|
||||
Assert.IsTrue(task.WaitForStart());
|
||||
runner.CancelAllBackgroundOperations();
|
||||
|
||||
Assert.IsTrue(runner.WaitForCompletion());
|
||||
Assert.IsNull(runner.ReportedError);
|
||||
}
|
||||
|
||||
Assert.AreEqual(1, task.ExecutionCount);
|
||||
Assert.AreEqual(12.43f, task.FirstArgument);
|
||||
Assert.IsTrue(task.WasCancelled);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks that the thread runner is able to pass two arguments to a task
|
||||
/// that can be cancelled
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CanPassTwoArgumentsToCancellableTask() {
|
||||
using(var task = new DummyTask(100)) {
|
||||
using(var runner = new DummyRunner()) {
|
||||
runner.RunInBackground(
|
||||
new CancellableAction<float, string>(task.RunCancellable), 98.67f, "Hello"
|
||||
);
|
||||
Assert.IsTrue(task.WaitForStart());
|
||||
runner.CancelAllBackgroundOperations();
|
||||
|
||||
Assert.IsTrue(runner.WaitForCompletion());
|
||||
Assert.IsNull(runner.ReportedError);
|
||||
}
|
||||
|
||||
Assert.AreEqual(1, task.ExecutionCount);
|
||||
Assert.AreEqual(98.67f, task.FirstArgument);
|
||||
Assert.AreEqual("Hello", task.SecondArgument);
|
||||
Assert.IsTrue(task.WasCancelled);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Threading
|
||||
|
||||
#endif // UNITTEST
|
||||
|
||||
#endif // !NO_CONCURRENT_COLLECTIONS
|
||||
|
|
@ -1,141 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support {
|
||||
|
||||
/// <summary>Unit Test for the strign segment class</summary>
|
||||
[TestFixture]
|
||||
internal class TypeHelperTest {
|
||||
|
||||
#region class NoDefaultConstructor
|
||||
|
||||
/// <summary>Test class that doesn't have a default constructor</summary>
|
||||
private class NoDefaultConstructor {
|
||||
/// <summary>Initializes a new instance of the test class</summary>
|
||||
/// <param name="dummy">Dummy argument so this is no default constructor</param>
|
||||
public NoDefaultConstructor(int dummy) { }
|
||||
}
|
||||
|
||||
#endregion // class NoDefaultConstructor
|
||||
|
||||
#region class NonPublicDefaultConstructor
|
||||
|
||||
/// <summary>Test class that has a non-public default constructor</summary>
|
||||
private class NonPublicDefaultConstructor {
|
||||
/// <summary>Initializes a new instance of the test class</summary>
|
||||
protected NonPublicDefaultConstructor() { }
|
||||
}
|
||||
|
||||
#endregion // class NonPublicDefaultConstructor
|
||||
|
||||
#region class PublicDefaultConstructor
|
||||
|
||||
/// <summary>Test class that has a public default constructor</summary>
|
||||
private class PublicDefaultConstructor {
|
||||
/// <summary>Initializes a new instance of the test class</summary>
|
||||
public PublicDefaultConstructor() { }
|
||||
}
|
||||
|
||||
#endregion // class PublicDefaultConstructor
|
||||
|
||||
#region class Base
|
||||
|
||||
/// <summary>Base class used to test the helper methods</summary>
|
||||
private class Base {
|
||||
/// <summary>A simple public field</summary>
|
||||
public int PublicBaseField;
|
||||
/// <summary>An automatic property with a hidden backing field</summary>
|
||||
public int PublicBaseProperty { get; set; }
|
||||
}
|
||||
|
||||
#endregion // class Base
|
||||
|
||||
#region class Derived
|
||||
|
||||
/// <summary>Derived class used to test the helper methods</summary>
|
||||
private class Derived : Base {
|
||||
/// <summary>A simple public field</summary>
|
||||
public int PublicDerivedField;
|
||||
/// <summary>An automatic property with a hidden backing field</summary>
|
||||
public int PublicDerivedProperty { get; set; }
|
||||
}
|
||||
|
||||
#endregion // class Derived
|
||||
|
||||
#region class HasIgnoreAttribute
|
||||
|
||||
/// <summary>Class that carries an IgnoreAttribute</summary>
|
||||
[Ignore]
|
||||
private class HasIgnoreAttribute { }
|
||||
|
||||
#endregion // class HasIgnoreAttribute
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the type helper can determine whether a class is carrying an attribute
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CanDetermineIfTypeHasAttribute() {
|
||||
Assert.IsTrue(typeof(HasIgnoreAttribute).HasAttribute<IgnoreAttribute>());
|
||||
Assert.IsFalse(typeof(HasIgnoreAttribute).HasAttribute<TestAttribute>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the GetFieldInfosIncludingBaseClasses() will include the backing
|
||||
/// fields of automatically implemented properties in base classes
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CanGetBackingFieldsForPropertiesInBaseClasses() {
|
||||
FieldInfo[] fieldInfos = typeof(Derived).GetFieldInfosIncludingBaseClasses(
|
||||
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
|
||||
);
|
||||
Assert.AreEqual(4, fieldInfos.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Useless test that avoids a compile warning about unused fields
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void AvoidCompilerWarnings() {
|
||||
var derived = new Derived() {
|
||||
PublicBaseField = 123,
|
||||
PublicBaseProperty = 321,
|
||||
PublicDerivedField = 456
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>Tests whether the default constructor detection works as expected</summary>
|
||||
[Test]
|
||||
public void TestDefaultConstructorDetection() {
|
||||
Assert.IsFalse(typeof(NoDefaultConstructor).HasDefaultConstructor());
|
||||
Assert.IsFalse(typeof(NonPublicDefaultConstructor).HasDefaultConstructor());
|
||||
Assert.IsTrue(typeof(PublicDefaultConstructor).HasDefaultConstructor());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,113 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support {
|
||||
|
||||
/// <summary>Unit Test for the strongly typed weak reference class</summary>
|
||||
[TestFixture]
|
||||
internal class WeakReferenceTest {
|
||||
|
||||
#region class Dummy
|
||||
|
||||
/// <summary>Dummy class for testing the shared instance provider</summary>
|
||||
[Serializable]
|
||||
private class Dummy {
|
||||
/// <summary>Initializes a new dummy</summary>
|
||||
public Dummy() { }
|
||||
}
|
||||
|
||||
#endregion // class Dummy
|
||||
|
||||
/// <summary>Tests whether the simple constructor works</summary>
|
||||
[Test]
|
||||
public void TestSimpleConstructor() {
|
||||
new WeakReference<Dummy>(new Dummy());
|
||||
}
|
||||
|
||||
/// <summary>Test whether the full constructor works</summary>
|
||||
[Test]
|
||||
public void TestFullConstructor() {
|
||||
new WeakReference<Dummy>(new Dummy(), false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test whether the target object can be retrieved from the weak reference
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestTargetRetrieval() {
|
||||
Dummy strongReference = new Dummy();
|
||||
WeakReference<Dummy> weakReference = new WeakReference<Dummy>(strongReference);
|
||||
|
||||
// We can not just call GC.Collect() and base our test on the assumption that
|
||||
// the garbage collector will actually collect the Dummy instance. This is up
|
||||
// to the garbage collector to decide. But we can keep a strong reference in
|
||||
// parallel and safely assume that the WeakReference will not be invalidated!
|
||||
Assert.AreSame(strongReference, weakReference.Target);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test whether the target object can be reassigned in the weak reference
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestTargetReassignment() {
|
||||
Dummy strongReference1 = new Dummy();
|
||||
Dummy strongReference2 = new Dummy();
|
||||
WeakReference<Dummy> weakReference = new WeakReference<Dummy>(strongReference1);
|
||||
|
||||
Assert.AreSame(strongReference1, weakReference.Target);
|
||||
weakReference.Target = strongReference2;
|
||||
Assert.AreSame(strongReference2, weakReference.Target);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test whether the target object can be reassigned in the weak reference
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestSerialization() {
|
||||
BinaryFormatter formatter = new BinaryFormatter();
|
||||
|
||||
using(MemoryStream memory = new MemoryStream()) {
|
||||
WeakReference<Dummy> weakReference1 = new WeakReference<Dummy>(new Dummy());
|
||||
|
||||
formatter.Serialize(memory, weakReference1);
|
||||
memory.Position = 0;
|
||||
object weakReference2 = formatter.Deserialize(memory);
|
||||
|
||||
// We cannot make any more predictions but for the type of the weak reference.
|
||||
// The pointee might have been garbage collected just now or the serializer
|
||||
// might have decided not to serialize the pointee at all (which is a valid
|
||||
// decision if the serializer found no strong reference to the pointee) in
|
||||
// another of the object graph.
|
||||
Assert.IsNotNull(weakReference2 as WeakReference<Dummy>);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support
|
||||
|
||||
#endif // UNITTEST
|
||||
|
|
@ -1,313 +0,0 @@
|
|||
#region Apache License 2.0
|
||||
/*
|
||||
Nuclex .NET Framework
|
||||
Copyright (C) 2002-2024 Markus Ewald / Nuclex Development Labs
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#endregion // Apache License 2.0
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
#if !USE_XMLDOCUMENT
|
||||
using System.Xml.Linq;
|
||||
#endif
|
||||
using System.Xml.Schema;
|
||||
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Nuclex.Support {
|
||||
|
||||
/// <summary>Unit Test for the XML helper class</summary>
|
||||
[TestFixture]
|
||||
internal class XmlHelperTest {
|
||||
|
||||
/// <summary>A broken XML schema</summary>
|
||||
private const string brokenSchemaXml =
|
||||
"This is not a valid schema";
|
||||
|
||||
/// <summary>An XML schema with a syntax error</summary>
|
||||
private const string syntaxErrorSchemaXml =
|
||||
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
|
||||
"<xs:schema" +
|
||||
" elementFormDefault=\"qualified\"" +
|
||||
" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">" +
|
||||
" <xs:attribute minOccurs=\"0\" maxOccurs=\"2\" name=\"x\" type=\"xs:double\" />" +
|
||||
" <xs:attribute minOccurs=\"0\" maxOccurs=\"2\" name=\"y\" type=\"xs:double\" />" +
|
||||
"</xs:schema>";
|
||||
|
||||
/// <summary>A valid XML schema for a list of 2D points</summary>
|
||||
private const string pointsSchemaXml =
|
||||
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
|
||||
"<xs:schema" +
|
||||
" elementFormDefault=\"qualified\"" +
|
||||
" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">" +
|
||||
" <xs:complexType name=\"vectorType\">" +
|
||||
" <xs:attribute name=\"x\" type=\"xs:double\" />" +
|
||||
" <xs:attribute name=\"y\" type=\"xs:double\" />" +
|
||||
" </xs:complexType>" +
|
||||
" <xs:complexType name=\"pointsType\">" +
|
||||
" <xs:sequence minOccurs=\"0\" maxOccurs=\"unbounded\">" +
|
||||
" <xs:element name=\"point\" type=\"vectorType\" />" +
|
||||
" </xs:sequence>" +
|
||||
" </xs:complexType>" +
|
||||
" <xs:element name=\"points\" type=\"pointsType\" />" +
|
||||
"</xs:schema>";
|
||||
|
||||
/// <summary>A broken XML document</summary>
|
||||
private const string brokenXml =
|
||||
"This is not a valid XML file";
|
||||
|
||||
/// <summary>
|
||||
/// Well-formed XML document that is not conformant to the schema above
|
||||
/// </summary>
|
||||
private const string unconformantXml =
|
||||
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
|
||||
"<points" +
|
||||
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" +
|
||||
" xsi:noNamespaceSchemaLocation=\"skin.xsd\">" +
|
||||
" <point x=\"10\" y=\"20\" z=\"30\" />" +
|
||||
" <point x=\"1\" y=\"2\" />" +
|
||||
"</points>";
|
||||
|
||||
/// <summary>Well-formed XML document that is conformant to the schema</summary>
|
||||
private const string conformantXml =
|
||||
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
|
||||
"<points" +
|
||||
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"" +
|
||||
" xsi:noNamespaceSchemaLocation=\"skin.xsd\">" +
|
||||
" <point x=\"1\" y=\"2\" />" +
|
||||
" <point x=\"10\" y=\"20\" />" +
|
||||
"</points>";
|
||||
|
||||
#region TempFileKeeper
|
||||
|
||||
/// <summary>
|
||||
/// Creates a temporary file and automatically deletes it on dispose
|
||||
/// </summary>
|
||||
private class TempFileKeeper : IDisposable {
|
||||
|
||||
/// <summary>
|
||||
/// Creates a temporary file with the specified contents using the UTF8 encoding
|
||||
/// </summary>
|
||||
/// <param name="fileContents">
|
||||
/// Contents that will be written into the temporary file
|
||||
/// </param>
|
||||
public TempFileKeeper(string fileContents) : this(fileContents, Encoding.UTF8) { }
|
||||
|
||||
/// <summary>Creates a temporary file with the specified contents</summary>
|
||||
/// <param name="fileContents">
|
||||
/// Contents that will be written into the temporary file
|
||||
/// </param>
|
||||
/// <param name="encoding">
|
||||
/// Encoding to use for writing the contents into the file
|
||||
/// </param>
|
||||
public TempFileKeeper(string fileContents, Encoding encoding) {
|
||||
string tempFile = Path.GetTempFileName();
|
||||
try {
|
||||
using(
|
||||
FileStream tempFileStream = new FileStream(
|
||||
tempFile, FileMode.Truncate, FileAccess.Write, FileShare.None
|
||||
)
|
||||
) {
|
||||
StreamWriter writer = new StreamWriter(tempFileStream, encoding);
|
||||
writer.Write(fileContents);
|
||||
writer.Flush();
|
||||
}
|
||||
}
|
||||
catch(Exception) {
|
||||
File.Delete(tempFile);
|
||||
throw;
|
||||
}
|
||||
|
||||
this.tempFilePath = tempFile;
|
||||
}
|
||||
|
||||
/// <summary>Called when the instance is collected by the GC</summary>
|
||||
~TempFileKeeper() {
|
||||
Dispose();
|
||||
}
|
||||
|
||||
/// <summary>Immediately releases all resources used by the instance</summary>
|
||||
public void Dispose() {
|
||||
if(this.tempFilePath != null) {
|
||||
File.Delete(this.tempFilePath);
|
||||
this.tempFilePath = null;
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Implicitely converts a TempFileKeeper into a file path</summary>
|
||||
/// <param name="tempFileKeeper">TempFileKeeper that will be converted</param>
|
||||
/// <returns>The path to the temporary file managed by the TempFileKeeper</returns>
|
||||
public static implicit operator string(TempFileKeeper tempFileKeeper) {
|
||||
return tempFileKeeper.tempFilePath;
|
||||
}
|
||||
|
||||
/// <summary>Path to the temporary file the TempFileKeeper is managing</summary>
|
||||
private string tempFilePath;
|
||||
|
||||
}
|
||||
|
||||
#endregion // class TempFileKeeper
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that an exception is thrown when a schema fails to load
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void LoadSchemaThrowsOnInvalidSchema() {
|
||||
using(
|
||||
TempFileKeeper tempFile = new TempFileKeeper(brokenSchemaXml)
|
||||
) {
|
||||
Assert.Throws<XmlException>(delegate() { XmlHelper.LoadSchema(tempFile); });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that an exception is thrown when a schema contains a syntax error
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void LoadSchemaThrowsOnSyntaxErrors() {
|
||||
using(
|
||||
TempFileKeeper tempFile = new TempFileKeeper(syntaxErrorSchemaXml)
|
||||
) {
|
||||
Assert.Throws<XmlSchemaException>(delegate() { XmlHelper.LoadSchema(tempFile); });
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verfifies that TryLoadSchema() can fail without throwing an exception
|
||||
/// when the schema is not a valid XML document
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TryLoadSchemaHandlesMissingFiles() {
|
||||
XmlSchema schema;
|
||||
Assert.IsFalse(XmlHelper.TryLoadSchema("-- hello world --", out schema));
|
||||
Assert.IsNull(schema);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verfifies that TryLoadSchema() can fail without throwing an exception
|
||||
/// when the schema is not a valid XML document
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TryLoadSchemaHandlesBrokenSchemas() {
|
||||
using(
|
||||
TempFileKeeper tempFile = new TempFileKeeper(brokenSchemaXml)
|
||||
) {
|
||||
XmlSchema schema;
|
||||
Assert.IsFalse(XmlHelper.TryLoadSchema(tempFile, out schema));
|
||||
Assert.IsNull(schema);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verfifies that TryLoadSchema() can fail without throwing an exception
|
||||
/// when the schema contains a syntax error
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TryLoadSchemaHandlesSyntaxErrors() {
|
||||
using(
|
||||
TempFileKeeper tempFile = new TempFileKeeper(syntaxErrorSchemaXml)
|
||||
) {
|
||||
XmlSchema schema;
|
||||
Assert.IsFalse(XmlHelper.TryLoadSchema(tempFile, out schema));
|
||||
Assert.IsNull(schema);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Tests whether a normal, valid schema can be loaded successfully</summary>
|
||||
[Test]
|
||||
public void SchemasCanBeLoadedFromFiles() {
|
||||
using(
|
||||
TempFileKeeper tempFile = new TempFileKeeper(pointsSchemaXml)
|
||||
) {
|
||||
XmlHelper.LoadSchema(tempFile);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Tests whether a normal, valid schema can be loaded successfully</summary>
|
||||
[Test]
|
||||
public void TryLoadSchemaCanLoadSchemas() {
|
||||
using(
|
||||
TempFileKeeper tempFile = new TempFileKeeper(pointsSchemaXml)
|
||||
) {
|
||||
XmlSchema schema;
|
||||
Assert.IsTrue(XmlHelper.TryLoadSchema(tempFile, out schema));
|
||||
Assert.NotNull(schema);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that an exception is thrown when an invalid XML document is loaded
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void LoadingInvalidDocumentThrows() {
|
||||
using(TextReader schemaReader = new StringReader(pointsSchemaXml)) {
|
||||
XmlSchema schema = XmlHelper.LoadSchema(schemaReader);
|
||||
using(
|
||||
TempFileKeeper tempFile = new TempFileKeeper(brokenXml)
|
||||
) {
|
||||
Assert.Throws<XmlException>(
|
||||
delegate() { XmlHelper.LoadDocument(schema, tempFile); }
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that an exception is thrown when a nonconformant XML document is loaded
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SchemaValidationFailureCausesException() {
|
||||
using(TextReader schemaReader = new StringReader(pointsSchemaXml)) {
|
||||
XmlSchema schema = XmlHelper.LoadSchema(schemaReader);
|
||||
using(
|
||||
TempFileKeeper tempFile = new TempFileKeeper(unconformantXml)
|
||||
) {
|
||||
Assert.Throws<XmlSchemaValidationException>(
|
||||
delegate() { XmlHelper.LoadDocument(schema, tempFile); }
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether a normal, conformant XML document can be loaded successfully
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void LoadedDocumentIsValidatedAgainstSchema() {
|
||||
using(TextReader schemaReader = new StringReader(pointsSchemaXml)) {
|
||||
XmlSchema schema = XmlHelper.LoadSchema(schemaReader);
|
||||
using(
|
||||
TempFileKeeper tempFile = new TempFileKeeper(conformantXml)
|
||||
) {
|
||||
#if USE_XMLDOCUMENT
|
||||
XmlDocument document = XmlHelper.LoadDocument(schema, tempFile);
|
||||
#else
|
||||
XDocument document = XmlHelper.LoadDocument(schema, tempFile);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support
|
||||
|
||||
#endif // UNITTEST
|
||||
Loading…
Add table
Add a link
Reference in a new issue