/// <summary> /// <paramref name="value"/> is used to connect the caller with the underlying /// <see cref="Keyed.Flags.Enumeration{T}"/> type only. Additionally, we are making /// the assumption that <see cref="Enumeration.GetValues{T}"/> are unique not only /// in <see cref="Enumeration.Name"/>, but also in /// <see cref="Keyed.Enumeration{TKey}.Key"/> value. In truth, this could go either /// way, but for now I will expect that there must be uniqueness throughout. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="value"></param> /// <param name="reporter"></param> /// <param name="outputHelper"></param> /// <see cref="!:http://en.wikipedia.org/wiki/Enumeration"/> public static void KeysShallBeUniquelyAssigned <T>(this T value , IEnumerationCoverageReporter <T> reporter = null , ITestOutputHelper outputHelper = null) where T : Keyed.Flags.Enumeration <T> => value.KeysShallBeUniquelyAssigned <ImmutableBitArray, uint, T>( x => x.ToInts().Single(), reporter, outputHelper );
/// <summary> /// Connect the verification with the <see cref="Enumeration"/> <paramref name="_"/> only. /// We do not require the connection with the test class itself. In fact, we want it to be /// separate, so we can do some independent verification of different good, bad, or ugly /// use cases. We expect there to be no <see cref="Public"/>, <see cref="Instance"/> /// Constructors. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="_">An anchor value rooting the Type <typeparamref name="T"/> only.</param> /// <param name="reporter">Disables the Reported following this invocation.</param> /// <see cref="Public"/> /// <see cref="Instance"/> /// <see cref="Type.GetConstructors(BindingFlags)"/> internal static void ShallNotHaveAnyPublicCtors <T>(this T _, IEnumerationCoverageReporter <T> reporter = null) where T : Enumeration { if (reporter != null) { reporter.Enabled = false; } // We do not care about the Value apart from identifying the underlying Enumeration Type. typeof(T).GetConstructors(Public | Instance).AssertEmpty(); }
/// <summary> /// Connect the verification with the <see cref="Keyed.Flags.Enumeration{T}"/> /// <paramref name="_"/> only. We do not require the connection with the test class /// itself. In fact, we want that to be separate and distinct in order so that we may /// do some independent verification of good, bad, or ugly test cases. Returns whether /// the Type <typeparamref name="T"/> Has the Expected Flags Constructors. That is, /// whether there is One <see cref="NonPublic"/>, <see cref="Instance"/> Constructor /// accepting a single <see cref="byte"/> Array. The Constructor must also be /// <see cref="MethodBase.IsPrivate"/>. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="_">Given a root value in order to inform the <typeparamref name="T"/> Type.</param> /// <param name="reporter"></param> /// <see cref="ConstructorInfo"/> /// <see cref="MethodBase.IsPrivate"/> /// <see cref="NonPublic"/> /// <see cref="Instance"/> /// <see cref="T:byte[]"/> internal static void HasExpectedFlagsCtors <T>(this T _, IEnumerationCoverageReporter <T> reporter = null) where T : Keyed.Flags.Enumeration <T> { if (reporter != null) { reporter.Enabled = false; } const BindingFlags nonPublicInstance = NonPublic | Instance; var types = new[] { typeof(byte[]) }; var ctor = typeof(T).GetConstructor(nonPublicInstance, DefaultBinder, types, null); ctor.AssertNotNull().AssertTrue(x => x.IsPrivate); }
/// <summary> /// Verifies that the type <typeparamref name="T"/> Shall Have At Least One Value. We do /// not care about the <paramref name="_"/> itself apart from its connection with the /// generic type <typeparamref name="T"/>. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="_">Given a pass through anchor value rooting the Type <typeparamref name="T"/>.</param> /// <param name="reporter">Reporter is disabled following this invocation.</param> /// <returns>The pass through <paramref name="_"/> value.</returns> /// <see cref="Enumeration.GetValues{T}"/> internal static T ShallHaveAtLeastOneValue <T>(this T _, IEnumerationCoverageReporter <T> reporter = null) where T : Enumeration { if (reporter != null) { reporter.Enabled = false; } /* Do not Report any Names at this level; this is intentional. Rather, do leave that * level of reporting for the actual unit test coverage. This ensures that we enforce * appropriate Enumeration Test Coverage at appropriate levels. */ Enumeration.GetValues <T>().AssertNotNull().AssertNotEmpty(); return(_); }
/// <summary> /// <typeparamref name="T"/> <see cref="Keyed.Flags.Enumeration{T}"/> values Shall All /// have Consistent <see cref="Keyed.Enumeration{TKey}.Key"/> lengths. Rules out false /// positives in the form of empty <see cref="Enumeration.GetValues{T}"/>. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="value"></param> /// <param name="ignoreNulls"></param> /// <param name="reporter"></param> internal static void ShallAllHaveConsistentBitLengths <T>(this T value, bool ignoreNulls = true , IEnumerationCoverageReporter <T> reporter = null) where T : Keyed.Flags.Enumeration <T> { var values = Enumeration.GetValues <T>(ignoreNulls).AssertNotNull().AssertNotEmpty(); // ReSharper disable once UnusedVariable var distinct = values.Select(x => { Assert.NotNull(x); /* There must be some Bits for this to work... * Yes, while we could Assert NotEmpty here, I am trying to keep the thrown * Exceptions as distinct as possible, not least of which for unit test purposes. */ var length = x.Key.AssertNotNull().AssertNotEmpty().Length; reporter?.Report(x.Name); return(length); }).Distinct().AssertEqual(1, x => x.Count()); }
/// <summary> /// Protected Constructor. /// </summary> /// <param name="outputHelper"></param> /// <param name="reporter"></param> /// <inheritdoc /> protected EnumerationTestFixtureBase(ITestOutputHelper outputHelper, IEnumerationCoverageReporter <T> reporter) : base(outputHelper) { Reporter = reporter; }
/// <summary> /// <paramref name="value"/> is used to connect the caller with the underlying /// <see cref="Keyed.Enumeration{TKey,T}"/> type only. Additionally, we are making /// the assumption that <see cref="Enumeration.GetValues{T}"/> are unique not only /// in <see cref="Enumeration.Name"/>, but also in /// <see cref="Keyed.Enumeration{TKey}.Key"/> value. In truth, this could go either /// way, but for now I will expect that there must be uniqueness throughout. /// </summary> /// <typeparam name="TKey"></typeparam> /// <typeparam name="T"></typeparam> /// <param name="value"></param> /// <param name="reporter"></param> /// <param name="outputHelper"></param> /// <see cref="!:http://en.wikipedia.org/wiki/Enumeration"/> public static void KeysShallBeUniquelyAssigned <TKey, T>(this T value , IEnumerationCoverageReporter <T> reporter = null , ITestOutputHelper outputHelper = null) where T : Keyed.Enumeration <TKey, T> where TKey : IComparable <TKey>, IEquatable <TKey> => value.KeysShallBeUniquelyAssigned <TKey, TKey, T>(x => x, reporter, outputHelper);
/// <summary> /// <paramref name="value"/> is used to connect the caller with the underlying /// <see cref="Keyed.Enumeration{TKey,T}"/> type only. Additionally, we are making /// the assumption that <see cref="Enumeration.GetValues{T}"/> are unique not only /// in <see cref="Enumeration.Name"/>, but also in /// <see cref="Keyed.Enumeration{TKey}.Key"/> value. In truth, this could go either /// way, but for now I will expect that there must be uniqueness throughout. /// </summary> /// <typeparam name="TKey"></typeparam> /// <typeparam name="TGroupByKey"></typeparam> /// <typeparam name="T"></typeparam> /// <param name="value"></param> /// <param name="groupByKey"></param> /// <param name="reporter"></param> /// <param name="outputHelper"></param> /// <see cref="!:http://en.wikipedia.org/wiki/Enumeration"/> public static void KeysShallBeUniquelyAssigned <TKey, TGroupByKey, T>(this T value , Func <TKey, TGroupByKey> groupByKey, IEnumerationCoverageReporter <T> reporter = null , ITestOutputHelper outputHelper = null) where T : Keyed.Enumeration <TKey, T> where TKey : IComparable <TKey>, IEquatable <TKey> { var values = Enumeration.GetValues <T>().Select(x => { var result = x; reporter?.Report(x.Name); return(result); }).ToList(); // This step is key, must all be Uniquely Assigned, that is, in Groups of One. //var grouped = values.GroupBy(x => x.Key).ToArray(); var grouped = values.GroupBy(x => groupByKey.Invoke(x.Key)).ToArray(); try { values.AssertEqual(grouped.Length, x => x.Count); } catch (EqualException) { string ListDupes() => Join(", ", grouped.Where(g => g.Count() > 1).Select(g => $"`{g.Key}´")); outputHelper?.WriteLine($"Some Enumerated values have Duplicate Bits: {ListDupes()}"); throw; } }
/// <summary> /// Protected Constructor. /// </summary> /// <param name="outputHelper"></param> /// <param name="reporter"></param> /// <inheritdoc /> protected OrdinalEnumerationTestFixtureBase(ITestOutputHelper outputHelper, IEnumerationCoverageReporter <T> reporter) : base(outputHelper, reporter) { }