Nailed the unit test coverage for all classes in the root namespace at 100%; fixed ugly german comment in ReadOnlyDictionary; moved AbstractFactory interface for FactoryEmployer in Nuclex.Support.Plugins into its own file; wrote unit tests for the PluginHelper, Shared, StringSegment and WeakReference classes
git-svn-id: file:///srv/devel/repo-conversion/nusu@93 d2e56fa2-650e-0410-a79f-9358c0239efd
This commit is contained in:
parent
ddff1d8353
commit
cb0355193d
|
@ -95,11 +95,15 @@
|
|||
<Compile Include="Source\PathHelper.Test.cs">
|
||||
<DependentUpon>PathHelper.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Source\Plugins\Attributes.cs" />
|
||||
<Compile Include="Source\Plugins\AbstractFactory.cs" />
|
||||
<Compile Include="Source\Plugins\NoPluginAttribute.cs" />
|
||||
<Compile Include="Source\Plugins\Employer.cs" />
|
||||
<Compile Include="Source\Plugins\FactoryEmployer.cs" />
|
||||
<Compile Include="Source\Plugins\InstanceEmployer.cs" />
|
||||
<Compile Include="Source\Plugins\PluginHelper.cs" />
|
||||
<Compile Include="Source\Plugins\PluginHelper.Test.cs">
|
||||
<DependentUpon>PluginHelper.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Source\Plugins\PluginHost.cs" />
|
||||
<Compile Include="Source\Plugins\PluginRepository.cs" />
|
||||
<Compile Include="Source\Scheduling\AbortedException.cs" />
|
||||
|
@ -112,11 +116,17 @@
|
|||
<Compile Include="Source\Scheduling\ThreadCallbackOperation.cs" />
|
||||
<Compile Include="Source\Scheduling\ThreadOperation.cs" />
|
||||
<Compile Include="Source\Shared.cs" />
|
||||
<Compile Include="Source\Shared.Test.cs">
|
||||
<DependentUpon>Shared.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Source\StringHelper.cs" />
|
||||
<Compile Include="Source\StringHelper.Test.cs">
|
||||
<DependentUpon>StringHelper.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Source\StringSegment.cs" />
|
||||
<Compile Include="Source\StringSegment.Test.cs">
|
||||
<DependentUpon>StringSegment.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Source\Tracking\IdleStateEventArgs.cs" />
|
||||
<Compile Include="Source\Tracking\Internal\ObservedWeightedWaitable.cs" />
|
||||
<Compile Include="Source\Tracking\Internal\WeightedWaitableWrapperCollection.cs" />
|
||||
|
@ -136,6 +146,9 @@
|
|||
<Compile Include="Source\Tracking\Waitable.cs" />
|
||||
<Compile Include="Source\Tracking\WeightedWaitable.cs" />
|
||||
<Compile Include="Source\WeakReference.cs" />
|
||||
<Compile Include="Source\WeakReference.Test.cs">
|
||||
<DependentUpon>WeakReference.cs</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Documents\Nuclex.Support.txt" />
|
||||
|
|
|
@ -98,8 +98,8 @@ namespace Nuclex.Support.Collections {
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>Collection aller Werte im Dictionary</summary>
|
||||
public ICollection<ValueType> Values { // TODO: RO-Wrappen!
|
||||
/// <summary>Collection of all values contained in the Dictionary</summary>
|
||||
public ICollection<ValueType> Values {
|
||||
get {
|
||||
if(this.readonlyValueCollection == null) {
|
||||
this.readonlyValueCollection = new ReadOnlyCollection<ValueType>(
|
||||
|
|
|
@ -77,6 +77,29 @@ namespace Nuclex.Support {
|
|||
);
|
||||
}
|
||||
|
||||
/// <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.
|
||||
|
@ -99,6 +122,28 @@ namespace Nuclex.Support {
|
|||
);
|
||||
}
|
||||
|
||||
/// <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.
|
||||
|
|
39
Source/Plugins/AbstractFactory.cs
Normal file
39
Source/Plugins/AbstractFactory.cs
Normal file
|
@ -0,0 +1,39 @@
|
|||
#region CPL License
|
||||
/*
|
||||
Nuclex Framework
|
||||
Copyright (C) 2002-2008 Nuclex Development Labs
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the IBM Common Public License as
|
||||
published by the IBM Corporation; either version 1.0 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
IBM Common Public License for more details.
|
||||
|
||||
You should have received a copy of the IBM Common Public
|
||||
License along with this library
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Nuclex.Support.Plugins {
|
||||
|
||||
/// <summary>Abstract factory for a concrete type</summary>
|
||||
/// <typeparam name="ProductType">Interface or base class of the product of the factory</typeparam>
|
||||
public interface IAbstractFactory<ProductType> {
|
||||
|
||||
/// <summary>The concrete type as implemented by the factory instance</summary>
|
||||
Type ConcreteType { get; }
|
||||
|
||||
/// <summary>Creates a new instance of the type to which the factory is specialized</summary>
|
||||
/// <returns>The newly created instance</returns>
|
||||
ProductType CreateInstance();
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Plugins
|
|
@ -34,7 +34,7 @@ namespace Nuclex.Support.Plugins {
|
|||
|
||||
/// <summary>Determines whether the type suites the employer's requirements</summary>
|
||||
/// <param name="type">Type which will be assessed</param>
|
||||
/// <returns>True if the type can be employed</returns>
|
||||
/// <returns>True if the type can be employed, otherwise false</returns>
|
||||
public virtual bool CanEmploy(Type type) {
|
||||
return PluginHelper.HasDefaultConstructor(type);
|
||||
}
|
||||
|
|
|
@ -23,21 +23,10 @@ using System.Collections.Generic;
|
|||
|
||||
namespace Nuclex.Support.Plugins {
|
||||
|
||||
/// <summary>Abstract factory</summary>
|
||||
/// <typeparam name="T">Interface or base class of the product of the factory</typeparam>
|
||||
public interface IFactory<T> {
|
||||
|
||||
/// <summary>The concrete type as implemented by the factory instance</summary>
|
||||
Type ConcreteType { get; }
|
||||
|
||||
/// <summary>Creates a new instance of the type to which the factory is specialized</summary>
|
||||
/// <returns>The newly created instance</returns>
|
||||
T CreateInstance();
|
||||
|
||||
}
|
||||
|
||||
/// <summary>Employer to create factories of suiting types found in plugins</summary>
|
||||
/// <typeparam name="T">Interface or base class that the types need to implement</typeparam>
|
||||
/// <typeparam name="ProductType">
|
||||
/// Interface or base class that the types need to implement
|
||||
/// </typeparam>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This employer will not directly instanciate any compatible types found in
|
||||
|
@ -52,16 +41,16 @@ namespace Nuclex.Support.Plugins {
|
|||
/// a human-readable name, capabilities or an icon.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class FactoryEmployer<T> : Employer {
|
||||
public class FactoryEmployer<ProductType> : Employer {
|
||||
|
||||
#region class Factory
|
||||
#region class ConcreteFactory
|
||||
|
||||
/// <summary>Concrete factory for the types in a plugin assembly</summary>
|
||||
private class Factory : IFactory<T> {
|
||||
private class ConcreteFactory : IAbstractFactory<ProductType> {
|
||||
|
||||
/// <summary>Initializes a factory and configures it for the specified product</summary>
|
||||
/// <param name="type">Type of which the factory creates instances</param>
|
||||
public Factory(Type type) {
|
||||
public ConcreteFactory(Type type) {
|
||||
this.concreteType = type;
|
||||
}
|
||||
|
||||
|
@ -72,8 +61,8 @@ namespace Nuclex.Support.Plugins {
|
|||
|
||||
/// <summary>Create a new instance of the type that the factory is configured to</summary>
|
||||
/// <returns>The newly created instance</returns>
|
||||
public T CreateInstance() {
|
||||
return (T)Activator.CreateInstance(this.concreteType);
|
||||
public ProductType CreateInstance() {
|
||||
return (ProductType)Activator.CreateInstance(this.concreteType);
|
||||
}
|
||||
|
||||
/// <summary>Concrete product which the factory instance creates</summary>
|
||||
|
@ -85,11 +74,11 @@ namespace Nuclex.Support.Plugins {
|
|||
|
||||
/// <summary>Initializes a new FactoryEmployer</summary>
|
||||
public FactoryEmployer() {
|
||||
this.employedFactories = new List<IFactory<T>>();
|
||||
this.employedFactories = new List<IAbstractFactory<ProductType>>();
|
||||
}
|
||||
|
||||
/// <summary>List of all factories that the instance employer has created</summary>
|
||||
public List<IFactory<T>> Factories {
|
||||
public List<IAbstractFactory<ProductType>> Factories {
|
||||
get { return this.employedFactories; }
|
||||
}
|
||||
|
||||
|
@ -99,17 +88,17 @@ namespace Nuclex.Support.Plugins {
|
|||
public override bool CanEmploy(Type type) {
|
||||
return
|
||||
PluginHelper.HasDefaultConstructor(type) &&
|
||||
typeof(T).IsAssignableFrom(type);
|
||||
typeof(ProductType).IsAssignableFrom(type);
|
||||
}
|
||||
|
||||
/// <summary>Employs the specified plugin type</summary>
|
||||
/// <param name="type">Type to be employed</param>
|
||||
public override void Employ(Type type) {
|
||||
this.employedFactories.Add(new Factory(type));
|
||||
this.employedFactories.Add(new ConcreteFactory(type));
|
||||
}
|
||||
|
||||
/// <summary>All factories that the instance employer has created</summary>
|
||||
private List<IFactory<T>> employedFactories;
|
||||
private List<IAbstractFactory<ProductType>> employedFactories;
|
||||
|
||||
}
|
||||
|
||||
|
|
84
Source/Plugins/PluginHelper.Test.cs
Normal file
84
Source/Plugins/PluginHelper.Test.cs
Normal file
|
@ -0,0 +1,84 @@
|
|||
#region CPL License
|
||||
/*
|
||||
Nuclex Framework
|
||||
Copyright (C) 2002-2008 Nuclex Development Labs
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the IBM Common Public License as
|
||||
published by the IBM Corporation; either version 1.0 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
IBM Common Public License for more details.
|
||||
|
||||
You should have received a copy of the IBM Common Public
|
||||
License along with this library
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.SyntaxHelpers;
|
||||
|
||||
namespace Nuclex.Support.Plugins {
|
||||
|
||||
/// <summary>Unit Test for the plugin helper class</summary>
|
||||
[TestFixture]
|
||||
public class PluginHelperTest {
|
||||
|
||||
#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
|
||||
|
||||
/// <summary>Tests whether the default constructor detection works as expected</summary>
|
||||
[Test]
|
||||
public void TestDefaultConstructorDetection() {
|
||||
Assert.IsFalse(
|
||||
PluginHelper.HasDefaultConstructor(typeof(NoDefaultConstructor))
|
||||
);
|
||||
Assert.IsFalse(
|
||||
PluginHelper.HasDefaultConstructor(typeof(NonPublicDefaultConstructor))
|
||||
);
|
||||
Assert.IsTrue(
|
||||
PluginHelper.HasDefaultConstructor(typeof(PublicDefaultConstructor))
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support.Plugins
|
||||
|
||||
#endif // UNITTEST
|
|
@ -19,20 +19,21 @@ License along with this library
|
|||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Nuclex.Support.Plugins {
|
||||
|
||||
/// <summary>Supporting functions for the plugin classes</summary>
|
||||
internal static class PluginHelper {
|
||||
public static class PluginHelper {
|
||||
|
||||
/// <summary>Determines whether the given type has a default constructor</summary>
|
||||
/// <param name="type">Type which is to be checked</param>
|
||||
/// <returns>True if the type has a default constructor</returns>
|
||||
public static bool HasDefaultConstructor(Type type) {
|
||||
System.Reflection.ConstructorInfo[] constructors = type.GetConstructors();
|
||||
ConstructorInfo[] constructors = type.GetConstructors();
|
||||
|
||||
foreach(System.Reflection.ConstructorInfo constructor in constructors)
|
||||
if(constructor.IsPublic && (constructor.GetParameters().Length != 0))
|
||||
foreach(ConstructorInfo constructor in constructors)
|
||||
if(constructor.IsPublic && (constructor.GetParameters().Length == 0))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
|
70
Source/Shared.Test.cs
Normal file
70
Source/Shared.Test.cs
Normal file
|
@ -0,0 +1,70 @@
|
|||
#region CPL License
|
||||
/*
|
||||
Nuclex Framework
|
||||
Copyright (C) 2002-2008 Nuclex Development Labs
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the IBM Common Public License as
|
||||
published by the IBM Corporation; either version 1.0 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
IBM Common Public License for more details.
|
||||
|
||||
You should have received a copy of the IBM Common Public
|
||||
License along with this library
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.SyntaxHelpers;
|
||||
|
||||
namespace Nuclex.Support {
|
||||
|
||||
/// <summary>Unit Test for the shared instance provider class</summary>
|
||||
[TestFixture]
|
||||
public 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() {
|
||||
Dummy dummyInstance = Shared<Dummy>.Instance;
|
||||
Dummy otherDummyInstance = Shared<Dummy>.Instance;
|
||||
|
||||
// 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.IsTrue(
|
||||
ReferenceEquals(dummyInstance, otherDummyInstance)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support
|
||||
|
||||
#endif // UNITTEST
|
|
@ -19,6 +19,7 @@ License along with this library
|
|||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Nuclex.Support {
|
||||
|
||||
|
@ -30,7 +31,7 @@ namespace Nuclex.Support {
|
|||
|
||||
/// <summary>Returns the global instance of the class</summary>
|
||||
public static SharedType Instance {
|
||||
[System.Diagnostics.DebuggerStepThrough]
|
||||
[DebuggerStepThrough]
|
||||
get {
|
||||
return instance;
|
||||
}
|
||||
|
|
|
@ -47,6 +47,21 @@ namespace Nuclex.Support {
|
|||
);
|
||||
}
|
||||
|
||||
/// <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.
|
||||
|
@ -63,14 +78,17 @@ namespace Nuclex.Support {
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that the IndexNotOfAny() method works with multiple characters
|
||||
/// 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 TestMultipleCharIndexNotOfAny() {
|
||||
string haystack = "0123456789";
|
||||
public void TestLastIndexNotOfAnyWithStartIndex() {
|
||||
string positive = "OOOOOxxxxxOOOOO";
|
||||
string negative = "xxxxxOOOOOxxxxx";
|
||||
|
||||
Assert.AreEqual(
|
||||
5, StringHelper.IndexNotOfAny(haystack, new char[] { '4', '3', '2', '1', '0' })
|
||||
positive.LastIndexOfAny(new char[] { 'x' }, 5),
|
||||
StringHelper.LastIndexNotOfAny(negative, new char[] { 'x' }, 5)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -78,14 +96,121 @@ namespace Nuclex.Support {
|
|||
/// Verifies that the IndexNotOfAny() method works with multiple characters
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestMultipleCharLastIndexNotOfAny() {
|
||||
string haystack = "0123456789";
|
||||
public void TestMultipleCharIndexNotOfAny() {
|
||||
string positive = "abcde12345";
|
||||
string negative = "12345abcde";
|
||||
|
||||
Assert.AreEqual(
|
||||
4, StringHelper.LastIndexNotOfAny(haystack, new char[] { '9', '8', '7', '6', '5' })
|
||||
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
|
||||
|
|
195
Source/StringSegment.Test.cs
Normal file
195
Source/StringSegment.Test.cs
Normal file
|
@ -0,0 +1,195 @@
|
|||
#region CPL License
|
||||
/*
|
||||
Nuclex Framework
|
||||
Copyright (C) 2002-2008 Nuclex Development Labs
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the IBM Common Public License as
|
||||
published by the IBM Corporation; either version 1.0 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
IBM Common Public License for more details.
|
||||
|
||||
You should have received a copy of the IBM Common Public
|
||||
License along with this library
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.SyntaxHelpers;
|
||||
|
||||
namespace Nuclex.Support {
|
||||
|
||||
/// <summary>Unit Test for the strign segment class</summary>
|
||||
[TestFixture]
|
||||
public 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, ExpectedException(typeof(ArgumentNullException))]
|
||||
public void TestNullStringInSimpleConstructor() {
|
||||
new StringSegment(null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the simple constructor of the StringSegment class accepts
|
||||
/// an empty string
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestEmptyStringInSimpleConstructor() {
|
||||
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, ExpectedException(typeof(ArgumentNullException))]
|
||||
public void TestNullStringInFullConstructor() {
|
||||
new StringSegment(null, 0, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests whether the full constructor of the StringSegment class accepts
|
||||
/// an empty string
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestEmptyStringInFullConstructor() {
|
||||
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, ExpectedException(typeof(ArgumentOutOfRangeException))]
|
||||
public void TestInvalidOffsetInConstructor() {
|
||||
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, ExpectedException(typeof(ArgumentOutOfRangeException))]
|
||||
public void TestInvalidLengthInConstructor() {
|
||||
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, ExpectedException(typeof(ArgumentException))]
|
||||
public void TestExcessiveLengthInConstructor() {
|
||||
new StringSegment("hello", 3, 3);
|
||||
}
|
||||
|
||||
/// <summary>Tests whether the 'Text' property works as expected</summary>
|
||||
[Test]
|
||||
public void TestTextProperty() {
|
||||
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 TestOffsetProperty() {
|
||||
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 TestCountProperty() {
|
||||
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 TestHashCodeOnDifferingInstances() {
|
||||
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 TestHashCodeOnEquivalentInstances() {
|
||||
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 TestEqualsOnNull() {
|
||||
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 TestEqualityOnDifferingInstances() {
|
||||
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 TestEqualityOnEquivalentInstances() {
|
||||
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 TestInequalityOnDifferingInstances() {
|
||||
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 TestInequalityOnEquivalentInstances() {
|
||||
StringSegment helloWorld1Segment = new StringSegment("hello world", 2, 7);
|
||||
StringSegment helloWorld2Segment = new StringSegment("hello world", 2, 7);
|
||||
|
||||
Assert.IsFalse(helloWorld1Segment != helloWorld2Segment);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Nuclex.Support
|
||||
|
||||
#endif // UNITTEST
|
|
@ -25,8 +25,22 @@ using System.Runtime.InteropServices;
|
|||
namespace Nuclex.Support {
|
||||
|
||||
/// <summary>Delimits a section of a string</summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The design of this class pretty much mirrors that of the
|
||||
/// <see cref="T:System.ArraySegment" /> class found in the .NET framework, but is
|
||||
/// specialized to be used for strings, which can not be expressed as arrays but
|
||||
/// share a lot of the characteristics of an array.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// In certain situations, passing a StringSegment instead of the the actual
|
||||
/// section from a string is useful. For example, the caller might want to know
|
||||
/// from which index of the original string the substring was taken. Used internally
|
||||
/// in parsers, it can also prevent needless string copying and garbage generation.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
[Serializable, StructLayout(LayoutKind.Sequential)]
|
||||
internal struct StringSegment {
|
||||
public struct StringSegment {
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="StringSegment" /> class that delimits
|
||||
|
@ -35,7 +49,7 @@ namespace Nuclex.Support {
|
|||
/// <param name="text">String that will be wrapped</param>
|
||||
/// <exception cref="System.ArgumentNullException">String is null</exception>
|
||||
public StringSegment(string text) {
|
||||
if(text == null) {
|
||||
if(text == null) { // questionable, but matches behavior of ArraySegment class
|
||||
throw new ArgumentNullException("text");
|
||||
}
|
||||
|
||||
|
@ -59,8 +73,8 @@ namespace Nuclex.Support {
|
|||
/// </exception>
|
||||
/// <exception cref="System.ArgumentNullException">String is null</exception>
|
||||
public StringSegment(string text, int offset, int count) {
|
||||
if(text == null) {
|
||||
throw new ArgumentNullException("array");
|
||||
if(text == null) { // questionable, but matches behavior of ArraySegment class
|
||||
throw new ArgumentNullException("text");
|
||||
}
|
||||
if(offset < 0) {
|
||||
throw new ArgumentOutOfRangeException(
|
||||
|
@ -72,7 +86,7 @@ namespace Nuclex.Support {
|
|||
"count", "Argument out of range, non-negative number required"
|
||||
);
|
||||
}
|
||||
if((text.Length - offset) < count) {
|
||||
if(count > (text.Length - offset)) {
|
||||
throw new ArgumentException(
|
||||
"Invalid argument, specified offset and count exceed string length"
|
||||
);
|
||||
|
|
115
Source/WeakReference.Test.cs
Normal file
115
Source/WeakReference.Test.cs
Normal file
|
@ -0,0 +1,115 @@
|
|||
#region CPL License
|
||||
/*
|
||||
Nuclex Framework
|
||||
Copyright (C) 2002-2008 Nuclex Development Labs
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the IBM Common Public License as
|
||||
published by the IBM Corporation; either version 1.0 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
IBM Common Public License for more details.
|
||||
|
||||
You should have received a copy of the IBM Common Public
|
||||
License along with this library
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
|
||||
#if UNITTEST
|
||||
|
||||
using NUnit.Framework;
|
||||
using NUnit.Framework.SyntaxHelpers;
|
||||
|
||||
namespace Nuclex.Support {
|
||||
|
||||
/// <summary>Unit Test for the strongly typed weak reference class</summary>
|
||||
[TestFixture]
|
||||
public 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
|
|
@ -28,6 +28,7 @@ namespace Nuclex.Support {
|
|||
/// Type-safe weak reference, referencing an object while still allowing
|
||||
/// that object to be garbage collected.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class WeakReference<ReferencedType> : WeakReference
|
||||
where ReferencedType : class {
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user