#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.Cloning {
/// Base class for unit tests verifying the clone factory
internal abstract class CloneFactoryTest {
#region class DerivedReferenceType
/// A derived reference type being used for testing
protected class DerivedReferenceType : TestReferenceType {
/// Field holding an integer value for testing
public int DerivedField;
/// Property holding an integer value for testing
public int DerivedProperty { get; set; }
}
#endregion // class DerivedReferenceType
#region class TestReferenceType
/// A reference type being used for testing
protected class TestReferenceType {
/// Field holding an integer value for testing
public int TestField;
/// Property holding an integer value for testing
public int TestProperty { get; set; }
}
#endregion // class TestReferenceType
#region struct TestValueType
/// A value type being used for testing
protected struct TestValueType {
/// Field holding an integer value for testing
public int TestField;
/// Property holding an integer value for testing
public int TestProperty { get; set; }
}
#endregion // struct TestValueType
#region struct HierarchicalValueType
/// A value type containiner other complex types used for testing
protected struct HierarchicalValueType {
/// Field holding an integer value for testing
public int TestField;
/// Property holding an integer value for testing
public int TestProperty { get; set; }
/// Value type field for testing
public TestValueType ValueTypeField;
/// Value type property for testing
public TestValueType ValueTypeProperty { get; set; }
/// Reference type field for testing
public TestReferenceType ReferenceTypeField;
/// Reference type property for testing
public TestReferenceType ReferenceTypeProperty { get; set; }
/// An array field of reference types
public TestReferenceType[,][] ReferenceTypeArrayField;
/// An array property of reference types
public TestReferenceType[,][] ReferenceTypeArrayProperty { get; set; }
/// A reference type field that's always null
public TestReferenceType AlwaysNullField;
/// A reference type property that's always null
public TestReferenceType AlwaysNullProperty { get; set; }
/// A property that only has a getter
public TestReferenceType GetOnlyProperty { get { return null; } }
/// A property that only has a setter
public TestReferenceType SetOnlyProperty { set { } }
/// A read-only field
public readonly TestValueType ReadOnlyField;
/// Field typed as base class holding a derived instance
public TestReferenceType DerivedField;
/// Field typed as base class holding a derived instance
public TestReferenceType DerivedProperty { get; set; }
}
#endregion // struct HierarchicalValueType
#region struct HierarchicalReferenceType
/// A value type containiner other complex types used for testing
protected class HierarchicalReferenceType {
/// Field holding an integer value for testing
public int TestField;
/// Property holding an integer value for testing
public int TestProperty { get; set; }
/// Value type field for testing
public TestValueType ValueTypeField;
/// Value type property for testing
public TestValueType ValueTypeProperty { get; set; }
/// Reference type field for testing
public TestReferenceType ReferenceTypeField;
/// Reference type property for testing
public TestReferenceType ReferenceTypeProperty { get; set; }
/// An array field of reference types
public TestReferenceType[,][] ReferenceTypeArrayField;
/// An array property of reference types
public TestReferenceType[,][] ReferenceTypeArrayProperty { get; set; }
/// A reference type field that's always null
public TestReferenceType AlwaysNullField;
/// A reference type property that's always null
public TestReferenceType AlwaysNullProperty { get; set; }
/// A property that only has a getter
public TestReferenceType GetOnlyProperty { get { return null; } }
/// A property that only has a setter
public TestReferenceType SetOnlyProperty { set { } }
/// A read-only field
public readonly TestValueType ReadOnlyField;
/// Field typed as base class holding a derived instance
public TestReferenceType DerivedField;
/// Field typed as base class holding a derived instance
public TestReferenceType DerivedProperty { get; set; }
}
#endregion // struct HierarchicalReferenceType
#region class ClassWithoutDefaultConstructor
/// A class that does not have a default constructor
public class ClassWithoutDefaultConstructor {
///
/// Initializes a new instance of the class without default constructor
///
/// Dummy value that will be saved by the instance
public ClassWithoutDefaultConstructor(int dummy) {
this.dummy = dummy;
}
/// Dummy value that has been saved by the instance
public int Dummy {
get { return this.dummy; }
}
/// Dummy value that has been saved by the instance
private int dummy;
}
#endregion // class ClassWithoutDefaultConstructor
///
/// Verifies that a cloned object exhibits the expected state for the type of
/// clone that has been performed
///
/// Original instance the clone was created from
/// Cloned instance that will be checked for correctness
/// Whether the cloned instance is a deep clone
///
/// Whether a property-based clone was performed
///
protected static void VerifyClone(
HierarchicalReferenceType original, HierarchicalReferenceType clone,
bool isDeepClone, bool isPropertyBasedClone
) {
Assert.AreNotSame(original, clone);
if(isPropertyBasedClone) {
Assert.AreEqual(0, clone.TestField);
Assert.AreEqual(0, clone.ValueTypeField.TestField);
Assert.AreEqual(0, clone.ValueTypeField.TestProperty);
Assert.AreEqual(0, clone.ValueTypeProperty.TestField);
Assert.IsNull(clone.ReferenceTypeField);
Assert.IsNull(clone.DerivedField);
if(isDeepClone) {
Assert.AreNotSame(original.ReferenceTypeProperty, clone.ReferenceTypeProperty);
Assert.AreNotSame(
original.ReferenceTypeArrayProperty, clone.ReferenceTypeArrayProperty
);
Assert.AreNotSame(original.DerivedProperty, clone.DerivedProperty);
Assert.IsInstanceOf(clone.DerivedProperty);
var originalDerived = (DerivedReferenceType)original.DerivedProperty;
var clonedDerived = (DerivedReferenceType)clone.DerivedProperty;
Assert.AreEqual(originalDerived.TestProperty, clonedDerived.TestProperty);
Assert.AreEqual(originalDerived.DerivedProperty, clonedDerived.DerivedProperty);
Assert.AreEqual(0, clone.ReferenceTypeProperty.TestField);
Assert.AreNotSame(
original.ReferenceTypeArrayProperty[1, 3][0],
clone.ReferenceTypeArrayProperty[1, 3][0]
);
Assert.AreNotSame(
original.ReferenceTypeArrayProperty[1, 3][2],
clone.ReferenceTypeArrayProperty[1, 3][2]
);
Assert.AreEqual(0, clone.ReferenceTypeArrayProperty[1, 3][0].TestField);
Assert.AreEqual(0, clone.ReferenceTypeArrayProperty[1, 3][2].TestField);
} else {
Assert.AreSame(original.ReferenceTypeProperty, clone.ReferenceTypeProperty);
Assert.AreSame(original.ReferenceTypeProperty, clone.ReferenceTypeProperty);
Assert.AreSame(
original.ReferenceTypeArrayProperty, clone.ReferenceTypeArrayProperty
);
}
} else {
Assert.AreEqual(original.TestField, clone.TestField);
Assert.AreEqual(original.ValueTypeField.TestField, clone.ValueTypeField.TestField);
Assert.AreEqual(original.ValueTypeField.TestProperty, clone.ValueTypeField.TestProperty);
Assert.AreEqual(
original.ValueTypeProperty.TestField, clone.ValueTypeProperty.TestField
);
Assert.AreEqual(
original.ReferenceTypeField.TestField, clone.ReferenceTypeField.TestField
);
Assert.AreEqual(
original.ReferenceTypeField.TestProperty, clone.ReferenceTypeField.TestProperty
);
Assert.AreEqual(
original.ReferenceTypeProperty.TestField, clone.ReferenceTypeProperty.TestField
);
if(isDeepClone) {
Assert.AreNotSame(original.ReferenceTypeField, clone.ReferenceTypeField);
Assert.AreNotSame(original.ReferenceTypeProperty, clone.ReferenceTypeProperty);
Assert.AreNotSame(original.DerivedField, clone.DerivedField);
Assert.AreNotSame(original.DerivedProperty, clone.DerivedProperty);
Assert.IsInstanceOf(clone.DerivedField);
Assert.IsInstanceOf(clone.DerivedProperty);
var originalDerived = (DerivedReferenceType)original.DerivedField;
var clonedDerived = (DerivedReferenceType)clone.DerivedField;
Assert.AreEqual(originalDerived.TestField, clonedDerived.TestField);
Assert.AreEqual(originalDerived.TestProperty, clonedDerived.TestProperty);
Assert.AreEqual(originalDerived.DerivedField, clonedDerived.DerivedField);
Assert.AreEqual(originalDerived.DerivedProperty, clonedDerived.DerivedProperty);
originalDerived = (DerivedReferenceType)original.DerivedProperty;
clonedDerived = (DerivedReferenceType)clone.DerivedProperty;
Assert.AreEqual(originalDerived.TestField, clonedDerived.TestField);
Assert.AreEqual(originalDerived.TestProperty, clonedDerived.TestProperty);
Assert.AreEqual(originalDerived.DerivedField, clonedDerived.DerivedField);
Assert.AreEqual(originalDerived.DerivedProperty, clonedDerived.DerivedProperty);
Assert.AreNotSame(
original.ReferenceTypeArrayField, clone.ReferenceTypeArrayField
);
Assert.AreNotSame(
original.ReferenceTypeArrayProperty, clone.ReferenceTypeArrayProperty
);
Assert.AreNotSame(
original.ReferenceTypeArrayProperty[1, 3][0],
clone.ReferenceTypeArrayProperty[1, 3][0]
);
Assert.AreNotSame(
original.ReferenceTypeArrayProperty[1, 3][2],
clone.ReferenceTypeArrayProperty[1, 3][2]
);
Assert.AreEqual(
original.ReferenceTypeArrayProperty[1, 3][0].TestField,
clone.ReferenceTypeArrayProperty[1, 3][0].TestField
);
Assert.AreEqual(
original.ReferenceTypeArrayProperty[1, 3][2].TestField,
clone.ReferenceTypeArrayProperty[1, 3][2].TestField
);
} else {
Assert.AreSame(original.DerivedField, clone.DerivedField);
Assert.AreSame(original.DerivedProperty, clone.DerivedProperty);
Assert.AreSame(original.ReferenceTypeField, clone.ReferenceTypeField);
Assert.AreSame(original.ReferenceTypeProperty, clone.ReferenceTypeProperty);
Assert.AreSame(
original.ReferenceTypeArrayField, clone.ReferenceTypeArrayField
);
Assert.AreSame(
original.ReferenceTypeArrayProperty, clone.ReferenceTypeArrayProperty
);
}
}
}
///
/// Verifies that a cloned object exhibits the expected state for the type of
/// clone that has been performed
///
/// Original instance the clone was created from
/// Cloned instance that will be checked for correctness
/// Whether the cloned instance is a deep clone
///
/// Whether a property-based clone was performed
///
protected static void VerifyClone(
ref HierarchicalValueType original, ref HierarchicalValueType clone,
bool isDeepClone, bool isPropertyBasedClone
) {
if(isPropertyBasedClone) {
Assert.AreEqual(0, clone.TestField);
Assert.AreEqual(0, clone.ValueTypeField.TestField);
Assert.AreEqual(0, clone.ValueTypeField.TestProperty);
Assert.AreEqual(0, clone.ValueTypeProperty.TestField);
Assert.IsNull(clone.ReferenceTypeField);
Assert.IsNull(clone.DerivedField);
if(isDeepClone) {
Assert.AreNotSame(original.ReferenceTypeProperty, clone.ReferenceTypeProperty);
Assert.AreNotSame(
original.ReferenceTypeArrayProperty, clone.ReferenceTypeArrayProperty
);
Assert.AreNotSame(original.DerivedProperty, clone.DerivedProperty);
Assert.IsInstanceOf(clone.DerivedProperty);
var originalDerived = (DerivedReferenceType)original.DerivedProperty;
var clonedDerived = (DerivedReferenceType)clone.DerivedProperty;
Assert.AreEqual(originalDerived.TestProperty, clonedDerived.TestProperty);
Assert.AreEqual(originalDerived.DerivedProperty, clonedDerived.DerivedProperty);
Assert.AreEqual(0, clone.ReferenceTypeProperty.TestField);
Assert.AreNotSame(
original.ReferenceTypeArrayProperty[1, 3][0],
clone.ReferenceTypeArrayProperty[1, 3][0]
);
Assert.AreNotSame(
original.ReferenceTypeArrayProperty[1, 3][2],
clone.ReferenceTypeArrayProperty[1, 3][2]
);
Assert.AreEqual(0, clone.ReferenceTypeArrayProperty[1, 3][0].TestField);
Assert.AreEqual(0, clone.ReferenceTypeArrayProperty[1, 3][2].TestField);
} else {
Assert.AreSame(original.ReferenceTypeProperty, clone.ReferenceTypeProperty);
Assert.AreSame(original.ReferenceTypeProperty, clone.ReferenceTypeProperty);
Assert.AreSame(
original.ReferenceTypeArrayProperty, clone.ReferenceTypeArrayProperty
);
}
} else {
Assert.AreEqual(original.TestField, clone.TestField);
Assert.AreEqual(original.ValueTypeField.TestField, clone.ValueTypeField.TestField);
Assert.AreEqual(original.ValueTypeField.TestProperty, clone.ValueTypeField.TestProperty);
Assert.AreEqual(
original.ValueTypeProperty.TestField, clone.ValueTypeProperty.TestField
);
Assert.AreEqual(
original.ReferenceTypeField.TestField, clone.ReferenceTypeField.TestField
);
Assert.AreEqual(
original.ReferenceTypeField.TestProperty, clone.ReferenceTypeField.TestProperty
);
Assert.AreEqual(
original.ReferenceTypeProperty.TestField, clone.ReferenceTypeProperty.TestField
);
if(isDeepClone) {
Assert.AreNotSame(original.ReferenceTypeField, clone.ReferenceTypeField);
Assert.AreNotSame(original.ReferenceTypeProperty, clone.ReferenceTypeProperty);
Assert.AreNotSame(original.DerivedField, clone.DerivedField);
Assert.AreNotSame(original.DerivedProperty, clone.DerivedProperty);
Assert.IsInstanceOf(clone.DerivedField);
Assert.IsInstanceOf(clone.DerivedProperty);
var originalDerived = (DerivedReferenceType)original.DerivedField;
var clonedDerived = (DerivedReferenceType)clone.DerivedField;
Assert.AreEqual(originalDerived.TestField, clonedDerived.TestField);
Assert.AreEqual(originalDerived.TestProperty, clonedDerived.TestProperty);
Assert.AreEqual(originalDerived.DerivedField, clonedDerived.DerivedField);
Assert.AreEqual(originalDerived.DerivedProperty, clonedDerived.DerivedProperty);
originalDerived = (DerivedReferenceType)original.DerivedProperty;
clonedDerived = (DerivedReferenceType)clone.DerivedProperty;
Assert.AreEqual(originalDerived.TestField, clonedDerived.TestField);
Assert.AreEqual(originalDerived.TestProperty, clonedDerived.TestProperty);
Assert.AreEqual(originalDerived.DerivedField, clonedDerived.DerivedField);
Assert.AreEqual(originalDerived.DerivedProperty, clonedDerived.DerivedProperty);
Assert.AreNotSame(
original.ReferenceTypeArrayField, clone.ReferenceTypeArrayField
);
Assert.AreNotSame(
original.ReferenceTypeArrayProperty, clone.ReferenceTypeArrayProperty
);
Assert.AreNotSame(
original.ReferenceTypeArrayProperty[1, 3][0],
clone.ReferenceTypeArrayProperty[1, 3][0]
);
Assert.AreNotSame(
original.ReferenceTypeArrayProperty[1, 3][2],
clone.ReferenceTypeArrayProperty[1, 3][2]
);
Assert.AreEqual(
original.ReferenceTypeArrayProperty[1, 3][0].TestField,
clone.ReferenceTypeArrayProperty[1, 3][0].TestField
);
Assert.AreEqual(
original.ReferenceTypeArrayProperty[1, 3][2].TestField,
clone.ReferenceTypeArrayProperty[1, 3][2].TestField
);
} else {
Assert.AreSame(original.DerivedField, clone.DerivedField);
Assert.AreSame(original.DerivedProperty, clone.DerivedProperty);
Assert.AreSame(original.ReferenceTypeField, clone.ReferenceTypeField);
Assert.AreSame(original.ReferenceTypeProperty, clone.ReferenceTypeProperty);
Assert.AreSame(
original.ReferenceTypeArrayField, clone.ReferenceTypeArrayField
);
Assert.AreSame(
original.ReferenceTypeArrayProperty, clone.ReferenceTypeArrayProperty
);
}
}
Assert.AreEqual(original.TestProperty, clone.TestProperty);
Assert.AreEqual(
original.ValueTypeProperty.TestProperty, clone.ValueTypeProperty.TestProperty
);
Assert.AreEqual(
original.ReferenceTypeProperty.TestProperty, clone.ReferenceTypeProperty.TestProperty
);
Assert.AreEqual(
original.ReferenceTypeArrayProperty[1, 3][0].TestProperty,
clone.ReferenceTypeArrayProperty[1, 3][0].TestProperty
);
Assert.AreEqual(
original.ReferenceTypeArrayProperty[1, 3][2].TestProperty,
clone.ReferenceTypeArrayProperty[1, 3][2].TestProperty
);
}
/// Creates a value type with random data for testing
/// A new value type with random data
protected static HierarchicalValueType CreateValueType() {
return new HierarchicalValueType() {
TestField = 123,
TestProperty = 321,
ReferenceTypeArrayField = new TestReferenceType[2, 4][] {
{
null, null, null, null
},
{
null, null, null,
new TestReferenceType[3] {
new TestReferenceType() { TestField = 101, TestProperty = 202 },
null,
new TestReferenceType() { TestField = 909, TestProperty = 808 }
}
},
},
ReferenceTypeArrayProperty = new TestReferenceType[2, 4][] {
{
null, null, null, null
},
{
null, null, null,
new TestReferenceType[3] {
new TestReferenceType() { TestField = 303, TestProperty = 404 },
null,
new TestReferenceType() { TestField = 707, TestProperty = 606 }
}
},
},
ValueTypeField = new TestValueType() {
TestField = 456,
TestProperty = 654
},
ValueTypeProperty = new TestValueType() {
TestField = 789,
TestProperty = 987,
},
ReferenceTypeField = new TestReferenceType() {
TestField = 135,
TestProperty = 531
},
ReferenceTypeProperty = new TestReferenceType() {
TestField = 246,
TestProperty = 642,
},
DerivedField = new DerivedReferenceType() {
DerivedField = 100,
DerivedProperty = 200,
TestField = 300,
TestProperty = 400
},
DerivedProperty = new DerivedReferenceType() {
DerivedField = 500,
DerivedProperty = 600,
TestField = 700,
TestProperty = 800
}
};
}
/// Creates a reference type with random data for testing
/// A new reference type with random data
protected static HierarchicalReferenceType CreateReferenceType() {
return new HierarchicalReferenceType() {
TestField = 123,
TestProperty = 321,
ReferenceTypeArrayField = new TestReferenceType[2, 4][] {
{
null, null, null, null
},
{
null, null, null,
new TestReferenceType[3] {
new TestReferenceType() { TestField = 101, TestProperty = 202 },
null,
new TestReferenceType() { TestField = 909, TestProperty = 808 }
}
},
},
ReferenceTypeArrayProperty = new TestReferenceType[2, 4][] {
{
null, null, null, null
},
{
null, null, null,
new TestReferenceType[3] {
new TestReferenceType() { TestField = 303, TestProperty = 404 },
null,
new TestReferenceType() { TestField = 707, TestProperty = 606 }
}
},
},
ValueTypeField = new TestValueType() {
TestField = 456,
TestProperty = 654
},
ValueTypeProperty = new TestValueType() {
TestField = 789,
TestProperty = 987,
},
ReferenceTypeField = new TestReferenceType() {
TestField = 135,
TestProperty = 531
},
ReferenceTypeProperty = new TestReferenceType() {
TestField = 246,
TestProperty = 642,
},
DerivedField = new DerivedReferenceType() {
DerivedField = 100,
DerivedProperty = 200,
TestField = 300,
TestProperty = 400
},
DerivedProperty = new DerivedReferenceType() {
DerivedField = 500,
DerivedProperty = 600,
TestField = 700,
TestProperty = 800
}
};
}
///
/// Useless method that avoids a compile warnings due to unused fields
///
protected void AvoidCompilerWarnings() {
var reference = new HierarchicalReferenceType() {
AlwaysNullField = new TestReferenceType()
};
var value = new HierarchicalValueType() {
AlwaysNullField = new TestReferenceType()
};
}
}
} // namespace Nuclex.Support.Cloning
#endif // UNITTEST