Commit 16901687 authored by An Ionescu's avatar An Ionescu

Fixing everything was wrong with the unit test setup before. All tests passing.

parent 670049e8
......@@ -16,6 +16,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.Common", "src\Tests\Common\Tests.Common.csproj", "{BEAB10EF-BD0D-4144-9AD0-D3BD1C052246}"
ProjectSection(ProjectDependencies) = postProject
{CA98F1BE-E77B-4B55-9B87-4F4F6F7BF412} = {CA98F1BE-E77B-4B55-9B87-4F4F6F7BF412}
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
......
......@@ -81,6 +81,6 @@ namespace Anvoker.Collections.Maps
/// </summary>
/// <param name="key">The key of the value to replace.</param>
/// <param name="value">The new value.</param>
void Replace(TKey key, TVal value);
bool Replace(TKey key, TVal value);
}
}
\ No newline at end of file
......@@ -72,7 +72,7 @@ namespace Anvoker.Collections.Maps
/// <returns>true if the <see cref="IFixedKeysMultiMap{TKey, TVal}"/>
/// contains an element with the specified key and value; otherwise,
/// false.</returns>
bool ContainsValue(TKey key, TVal value);
bool ContainsKeyWithValue(TKey key, TVal value);
/// <summary>
/// Removes the value associated with the specified key from the
......
......@@ -51,25 +51,25 @@ namespace Anvoker.Collections.Maps
void AddKey(TKey key, IEnumerable<TVal> values);
/// <summary>
/// Adds the value to the specified key in the
/// Adds the value to an existing specified key in the
/// <see cref="IMultiMap{TKey, TVal}"/>.
/// </summary>
/// <param name="key">The key of the element.</param>
/// <param name="value">The value to add to the element. The value can
/// be null for reference types.</param>
/// <returns>true if the value didn't exist already; otherwise,
/// false.</returns>
/// <returns>true if the key was found and the value didn't exist
/// already; otherwise, false.</returns>
bool AddValue(TKey key, TVal value);
/// <summary>
/// Adds the values to the specified key in the
/// Adds the values to an existing specified key in the
/// <see cref="IMultiMap{TKey, TVal}"/>.
/// </summary>
/// <param name="key">The key of the element.</param>
/// <param name="values">The values to add to the element. The value can
/// be null for reference types.</param>
/// <returns>true if at least one value didn't exist already and was
/// added; otherwise, false.</returns>
/// <returns>true if the key was found and at least one value didn't
/// exist already; otherwise, false.</returns>
bool AddValues(TKey key, IEnumerable<TVal> values);
/// <summary>
......@@ -96,7 +96,21 @@ namespace Anvoker.Collections.Maps
/// <returns>true if the <see cref="IMultiMap{TKey, TVal}"/> contains
/// an element with the specified key and value; otherwise, false.
/// </returns>
bool ContainsValue(TKey key, TVal value);
bool ContainsKeyWithValue(TKey key, TVal value);
/// <summary>
/// Determines whether the <see cref="IMultiMap{TKey, TVal}"/> contains
/// all of the specified values at the specified key.
/// </summary>
/// <param name="key">The key to locate in the
/// <see cref="IMultiMap{TKey, TVal}"/>.</param>
/// <param name="values">The values to locate in the
/// <see cref="IMultiMap{TKey, TVal}"/>. The value can be null for
/// reference types.</param>
/// <returns>true if the <see cref="IMultiMap{TKey, TVal}"/> contains
/// an element with the specified key and values; otherwise, false.
/// </returns>
bool ContainsKeyWithValues(TKey key, IEnumerable<TVal> values);
/// <summary>
/// Removes the element with the specified key from the
......
......@@ -49,6 +49,6 @@ namespace Anvoker.Collections.Maps
/// <returns>true if the <see cref="IReadOnlyMultiMap{TKey, TVal}"/>
/// contains an element with the specified key and value; otherwise,
/// false.</returns>
bool ContainsValue(TKey key, TVal value);
bool ContainsKeyWithValue(TKey key, TVal value);
}
}
\ No newline at end of file
......@@ -611,9 +611,24 @@ namespace Anvoker.Collections.Maps
/// <returns>true if the <see cref="MultiBiMap{TKey, TVal}"/> contains
/// an element with the specified key and value; otherwise, false.
/// </returns>
public bool ContainsValue(TKey key, TVal value)
public bool ContainsKeyWithValue(TKey key, TVal value)
=> dictFwd.ContainsKey(key) && dictFwd[key].Contains(value);
/// <summary>
/// Determines whether the <see cref="MultiBiMap{TKey, TVal}"/> contains
/// all of the specified values at the specified key.
/// </summary>
/// <param name="key">The key to locate in the
/// <see cref="MultiBiMap{TKey, TVal}"/>.</param>
/// <param name="values">The values to locate in the
/// <see cref="MultiBiMap{TKey, TVal}"/>. The value can be null for
/// reference types.</param>
/// <returns>true if the <see cref="MultiMap{TKey, TVal}"/> contains
/// an element with the specified key and values; otherwise, false.
/// </returns>
public bool ContainsKeyWithValues(TKey key, IEnumerable<TVal> values)
=> dictFwd.ContainsKey(key) && dictFwd[key].SetEquals(values);
/// <summary>
/// Determines whether the <see cref="MultiBiMap{TKey, TVal}"/> contains
/// a specific value.
......
......@@ -390,11 +390,7 @@ namespace Anvoker.Collections.Maps
/// <param name="value">The value of the element to add. The value can
/// be null for reference types.</param>
public void AddKey(TKey key, TVal value)
{
#pragma warning disable IDE0022 // Use expression body for methods
multiDict.Add(key, new HashSet<TVal>(ComparerValue) { value });
#pragma warning restore IDE0022 // Use expression body for methods
}
=> multiDict.Add(key, new HashSet<TVal>(ComparerValue) { value });
/// <summary>
/// Adds the specified key and its associated values to the
......@@ -430,60 +426,49 @@ namespace Anvoker.Collections.Maps
}
/// <summary>
/// Adds the value to the specified key in the
/// Adds the value to an existing specified key in the
/// <see cref="MultiMap{TKey, TVal}"/>.
/// </summary>
/// <param name="key">The key of the element.</param>
/// <param name="value">The values to add to the element. The value can
/// <param name="value">The value to add to the element. The value can
/// be null for reference types.</param>
/// <returns>true if the value didn't exist already; otherwise,
/// false.</returns>
/// <returns>true if the key was found and the value didn't exist
/// already; otherwise, false.</returns>
public bool AddValue(TKey key, TVal value)
{
try
if (key == null)
{
return multiDict[key].Add(value);
throw new
ArgumentNullException(nameof(key), "Keys cannot be null.");
}
catch (ArgumentNullException)
{
if (key == null)
{
throw new ArgumentNullException(
nameof(key), "Keys cannot be null.");
}
throw;
}
return ContainsKey(key) && multiDict[key].Add(value);
}
/// <summary>
/// Adds the values to the specified key in the
/// Adds the values to an existing specified key in the
/// <see cref="MultiMap{TKey, TVal}"/>.
/// </summary>
/// <param name="key">The key of the element.</param>
/// <param name="values">The values to add to the element.</param>
/// <returns>true if at least one value didn't exist already and was
/// added; otherwise, false.</returns>
/// <param name="values">The values to add to the element. The value can
/// be null for reference types.</param>
/// <returns>true if the key was found and at least one value didn't
/// exist already; otherwise, false.</returns>
public bool AddValues(TKey key, IEnumerable<TVal> values)
{
if (values == null)
if (key == null)
{
throw new ArgumentNullException(nameof(values));
throw new
ArgumentNullException(nameof(key), "Keys cannot be null.");
}
bool atLeastOneValueAdded = false;
HashSet<TVal> hashSetValues = null;
try
{
hashSetValues = multiDict[key];
}
catch (ArgumentNullException)
if (values == null)
{
throw new ArgumentNullException(
nameof(key), "Keys cannot be null.");
throw new ArgumentNullException(nameof(values));
}
bool atLeastOneValueAdded = false;
HashSet<TVal> hashSetValues = multiDict[key];
foreach (TVal value in values)
{
atLeastOneValueAdded |= hashSetValues.Add(value);
......@@ -522,9 +507,24 @@ namespace Anvoker.Collections.Maps
/// <returns>true if the <see cref="MultiMap{TKey, TVal}"/> contains
/// an element with the specified key and value; otherwise, false.
/// </returns>
public bool ContainsValue(TKey key, TVal value)
public bool ContainsKeyWithValue(TKey key, TVal value)
=> multiDict.ContainsKey(key) && multiDict[key].Contains(value);
/// <summary>
/// Determines whether the <see cref="MultiMap{TKey, TVal}"/> contains
/// all of the specified values at the specified key.
/// </summary>
/// <param name="key">The key to locate in the
/// <see cref="MultiMap{TKey, TVal}"/>.</param>
/// <param name="values">The values to locate in the
/// <see cref="MultiMap{TKey, TVal}"/>. The value can be null for
/// reference types.</param>
/// <returns>true if the <see cref="MultiMap{TKey, TVal}"/> contains
/// an element with the specified key and values; otherwise, false.
/// </returns>
public bool ContainsKeyWithValues(TKey key, IEnumerable<TVal> values)
=> multiDict.ContainsKey(key) && multiDict[key].SetEquals(values);
/// <summary>
/// Determines whether the <see cref="MultiMap{TKey, TVal}"/> contains
/// a specific value.
......
using System;
using System.Text;
namespace Anvoker.Collections.Tests.Common
{
/// <summary>
/// A delegate to a method that can build an assert message
/// appropriate for reporting failure when a method is called with various
/// arguments and all of the calls are expected to return one specific
/// value.
/// </summary>
/// <param name="args">The text representations of the arguments
/// used in the call.</param>
/// <param name="argsDisplayName">A title that can be prefixed
/// to the arguments.</param>
/// <param name="methodDisplayName">The name of the method call that
/// generated the returns.</param>
/// <param name="expectedReturn">The text representations of the
/// values of the expected returns.</param>
/// <param name="differingReturns">An array of bools where true values
/// indicate indices at which the return differed from the expected
/// value.</param>
/// <param name="actualReturns">The text representations of the actual
/// return values generated by the method.</param>
/// <returns>The built assert message.</returns>
public delegate string ManyArgsOneReturnMsgBuild(
string[] args,
string argsDisplayName,
string methodDisplayName,
string expectedReturn,
bool[] differingReturns,
string[] actualReturns);
/// <summary>
/// Encapsulates the data and method required to build an assert message
/// appropriate for reporting failure when a method is called with various
/// arguments and all of the calls are expected to return one specific
/// value.
/// </summary>
public class ManyArgsOneReturnMsgBuilder<TReturn>
{
private ManyArgsOneReturnMsgBuild buildMethod;
/// <summary>
/// Initializes a new instance of the
/// <see cref="CollectionCallAssertMsgBuilder{TElement, TReturn}"/>
/// class with the default message builder and the specified
/// element and return string conversion methods.
/// </summary>
/// <param name="argsToString">An array of methods that can output the
/// text representation of the arguments.</param>
/// <param name="returnToString">A method that can output the text
/// representation of the return value.</param>
public ManyArgsOneReturnMsgBuilder(
Func<object, string>[] argsToString,
Func<TReturn, string> returnToString)
{
buildMethod = DefaultBuildMethod;
ArgsToString = argsToString;
ReturnToString = returnToString;
}
/// <summary>
/// Initializes a new instance of the
/// <see cref="CollectionCallAssertMsgBuilder{TElement, TReturn}"/>
/// class with the specified message builder and the specified
/// element and return string conversion methods.
/// </summary>
/// <param name="buildMethod">The method that builds the assert
/// message.</param>
/// <param name="argsToString">An array of methods that can output the
/// text representation of the arguments.</param>
/// <param name="returnToString">A method that can output the text
/// representation of the return value.</param>
public ManyArgsOneReturnMsgBuilder(
ManyArgsOneReturnMsgBuild buildMethod,
Func<object, string>[] argsToString,
Func<TReturn, string> returnToString)
{
this.buildMethod = buildMethod;
ArgsToString = argsToString;
ReturnToString = returnToString;
}
private ManyArgsOneReturnMsgBuilder()
{
}
/// <summary>
/// Gets the default builder.
/// </summary>
public static ManyArgsOneReturnMsgBuilder<TReturn>
Default
{ get; }
= new ManyArgsOneReturnMsgBuilder<TReturn>()
{
buildMethod = DefaultBuildMethod,
ArgsToString = null,
ReturnToString = null
};
/// <summary>
/// Gets a delegate containing a method that can output the text
/// representation of the arguments.
/// </summary>
public Func<object, string>[] ArgsToString { get; private set; }
/// <summary>
/// Gets a delegate containing a method that can output the text
/// representation of a return value of type
/// <typeparamref name="TReturn"/>.
/// </summary>
public Func<TReturn, string> ReturnToString { get; private set; }
/// <summary>
/// Builds an assert message appropriate for reporting failure when a
/// method is called with various arguments and all of the calls are
/// expected to return a specific value.
/// </summary>
/// <param name="args">The text representations of the arguments
/// used in the call.</param>
/// <param name="argsDisplayName">A title that can be prefixed
/// to the arguments.</param>
/// <param name="methodDisplayName">The name of the method call that
/// generated the returns.</param>
/// <param name="expectedReturn">The text representations of the
/// values of the expected returns.</param>
/// <param name="differingReturns">An array of bools where true
/// values indicate indices at which the return differed from the
/// expected value.</param>
/// <param name="actualReturns">The text representations of the
/// actual return values generated by the method.</param>
/// <returns>The built assert message.</returns>
public string Build(
string[] args,
string argsDisplayName,
string methodDisplayName,
string expectedReturn,
bool[] differingReturns,
string[] actualReturns)
=> buildMethod(
args,
argsDisplayName,
methodDisplayName,
expectedReturn,
differingReturns,
actualReturns);
private static string DefaultBuildMethod(
string[] args,
string elementDisplayName,
string methodDisplayName,
string expectedReturn,
bool[] differingReturns,
string[] actualReturns)
{
if (args.Length != differingReturns.Length)
{
throw new ArgumentException("Count of args cannot differ " +
"from count of differing returns. " +
$"{args.Length} vs {differingReturns.Length}.");
}
if (args.Length != actualReturns.Length)
{
throw new ArgumentException("Count of args cannot differ " +
"from count of actual returns. " +
$"{args.Length} vs {actualReturns.Length}.");
}
var stringBuilder = new StringBuilder();
stringBuilder.AppendLine().Append("{");
for (int i = 0; i < args.Length; i++)
{
stringBuilder.AppendLine()
.Append(" ")
.Append(differingReturns[i] ? "FAILED " : "PASSED ")
.Append(elementDisplayName).Append(": ")
.Append(args[i]);
stringBuilder.Append(" ACTUAL: ").Append(actualReturns[i]);
}
stringBuilder.AppendLine().Append("}");
return $"{methodDisplayName} method should have " +
$"returned {expectedReturn} but didn't on at least one of " +
$"the inputs: {stringBuilder}.";
}
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using NUnit.Framework;
using System.Reflection;
using NUnit.Framework.Interfaces;
namespace Anvoker.Collections.Tests.Common
{
......@@ -13,6 +19,111 @@ namespace Anvoker.Collections.Tests.Common
/// </summary>
public static class HelperMethods
{
/// <summary>
/// Pretty prints a key-value pair.
/// </summary>
/// <typeparam name="T">The type of the key.</typeparam>
/// <typeparam name="K">The type of the value.</typeparam>
/// <param name="kvp">The key-value pair to pretty print.</param>
/// <returns>A text representation of the specified key-value pair.
/// </returns>
public static string KVPToString<T, K>(KeyValuePair<T, K> kvp)
{
string strT;
if (kvp.Key is IEnumerable keyEnum)
{
strT = $"{typeof(T).Name}{{ ";
bool ran = false;
foreach (object keyItem in keyEnum)
{
strT += keyItem.ToString() + ", ";
ran = true;
}
if (ran)
{
strT = strT.Remove(strT.Length - 2, 2);
}
strT += " }";
}
else
{
if (kvp.Key != null)
{
strT = kvp.Key.ToString();
}
else
{
strT = "null";
}
}
string strK;
if (kvp.Value is IEnumerable valEnum)
{
strK = $"{typeof(K).Name}{{ ";
bool ran = false;
foreach (object valItem in valEnum)
{
strK += valItem.ToString() + ", ";
ran = true;
}
if (ran)
{
strT = strT.Remove(strT.Length - 2, 2);
}
strK += " }";
}
else
{
if (kvp.Value != null)
{
strK = kvp.Value.ToString();
}
else
{
strK = "null";
}
}
return $"{{ Key: {strT} | Value: {strK} }}";
}
public static string ObjectsToString(
Func<object, string>[] toStringMethods,
IEnumerable args,
string delimiter = ", ")
{
if (args == null)
{
throw new ArgumentNullException(nameof(args));
}
var sb = new StringBuilder();
int i = 0;
foreach (object arg in args)
{
if (toStringMethods != null && toStringMethods[i] != null)
{
sb.Append(toStringMethods[i](arg)).Append(delimiter);
}
else
{
sb.Append(arg.ToString()).Append(delimiter);
}
i++;
}
sb.Remove(sb.Length - 2, 2);
return sb.ToString();
}
/// <summary>
/// Instantiates an array of collections where each collection is
/// constructed from a pairwise union between the collections in the
......@@ -68,5 +179,179 @@ namespace Anvoker.Collections.Tests.Common
return values;
}
/// <summary>
/// Instantiates an array of collections where each collection is
/// constructed from a pairwise union between the collections in the
/// <paramref name="first"/> and <paramref name="second"/> arrays.
/// </summary>
/// <typeparam name="TCollection">Type of the collection.
/// </typeparam>
/// <typeparam name="TValue">Type of the element in the collection.
/// </typeparam>
/// <param name="first">The first array of collections.</param>
/// <param name="second">The second array of collections.</param>
/// <param name="comparer">The comparer for
/// <typeparamref name="TValue"/></param>
/// <param name="collectionConstructor">A delegate pointing to a
/// constructor of <typeparamref name="TCollection"/> that takes
/// <see cref="IEnumerable{T}"/> as its parameter.</param>
/// <returns>An array with new collections, created from pairwise
/// unions.</returns>
public static TCollection[] UnionValues<TCollection, TValue>(
TCollection[] first,
IEnumerable<TValue> second,
Func<IEnumerable<TValue>, TCollection> collectionConstructor)
where TCollection : IEnumerable<TValue>
{
var values = new TCollection[first.Length];
var enumerator = second.GetEnumerator();
for (int i = 0; i < first.Length; i++, enumerator.MoveNext())
{
var union = new List<TValue>(first[i]);
union.Add(enumerator.Current);
values[i] = collectionConstructor(union);
}
return values;
}
public static object[] CombineArraysFromSources(
ITestFixtureData[][] testFixtureSources)