|
| 1 | +using System.Diagnostics.CodeAnalysis; |
| 2 | + |
| 3 | +namespace TUnit.Core.Helpers; |
| 4 | + |
| 5 | +/// <summary> |
| 6 | +/// Utility for detecting and unwrapping <see cref="TestDataRow{T}"/> instances. |
| 7 | +/// </summary> |
| 8 | +internal static class TestDataRowUnwrapper |
| 9 | +{ |
| 10 | + private static readonly Type TestDataRowGenericType = typeof(TestDataRow<>); |
| 11 | + |
| 12 | + /// <summary> |
| 13 | + /// Checks if the value is a <see cref="TestDataRow{T}"/> and extracts metadata and data. |
| 14 | + /// </summary> |
| 15 | + /// <param name="value">The value to check.</param> |
| 16 | + /// <param name="data">The extracted data if unwrapped, otherwise the original value.</param> |
| 17 | + /// <param name="metadata">The extracted metadata if unwrapped, otherwise null.</param> |
| 18 | + /// <returns>True if the value was a TestDataRow and was unwrapped.</returns> |
| 19 | + public static bool TryUnwrap(object? value, out object? data, [NotNullWhen(true)] out TestDataRowMetadata? metadata) |
| 20 | + { |
| 21 | + if (value is null) |
| 22 | + { |
| 23 | + data = null; |
| 24 | + metadata = null; |
| 25 | + return false; |
| 26 | + } |
| 27 | + |
| 28 | + // Use interface-based access for AOT compatibility (avoids reflection) |
| 29 | + if (value is ITestDataRow testDataRow) |
| 30 | + { |
| 31 | + data = testDataRow.GetData(); |
| 32 | + metadata = new TestDataRowMetadata(testDataRow.DisplayName, testDataRow.Skip, testDataRow.Categories); |
| 33 | + return true; |
| 34 | + } |
| 35 | + |
| 36 | + data = value; |
| 37 | + metadata = null; |
| 38 | + return false; |
| 39 | + } |
| 40 | + |
| 41 | + /// <summary> |
| 42 | + /// Checks if a type is a <see cref="TestDataRow{T}"/>. |
| 43 | + /// </summary> |
| 44 | + public static bool IsTestDataRowType(Type? type) |
| 45 | + { |
| 46 | + return type is not null && type.IsGenericType && type.GetGenericTypeDefinition() == TestDataRowGenericType; |
| 47 | + } |
| 48 | + |
| 49 | + /// <summary> |
| 50 | + /// Gets the inner data type from a <see cref="TestDataRow{T}"/> type. |
| 51 | + /// </summary> |
| 52 | + public static Type? GetInnerDataType(Type testDataRowType) |
| 53 | + { |
| 54 | + if (!IsTestDataRowType(testDataRowType)) |
| 55 | + { |
| 56 | + return null; |
| 57 | + } |
| 58 | + |
| 59 | + return testDataRowType.GetGenericArguments()[0]; |
| 60 | + } |
| 61 | + |
| 62 | + /// <summary> |
| 63 | + /// Unwraps an array of values, extracting TestDataRow metadata from single-element arrays. |
| 64 | + /// </summary> |
| 65 | + /// <param name="values">The array of values to unwrap.</param> |
| 66 | + /// <returns>A tuple of the unwrapped data array and any extracted metadata.</returns> |
| 67 | + public static (object?[] Data, TestDataRowMetadata? Metadata) UnwrapArray(object?[] values) |
| 68 | + { |
| 69 | + if (values.Length == 1 && TryUnwrap(values[0], out var data, out var metadata)) |
| 70 | + { |
| 71 | + // Single TestDataRow<T> - unwrap it |
| 72 | + // If the inner data is already an array, use it directly |
| 73 | + if (data is object?[] dataArray) |
| 74 | + { |
| 75 | + return (dataArray, metadata); |
| 76 | + } |
| 77 | + |
| 78 | + // Check if the data is a tuple that should be expanded |
| 79 | + if (DataSourceHelpers.IsTuple(data)) |
| 80 | + { |
| 81 | + return (data.ToObjectArray(), metadata); |
| 82 | + } |
| 83 | + |
| 84 | + // Otherwise wrap the single value in an array |
| 85 | + return ([data], metadata); |
| 86 | + } |
| 87 | + |
| 88 | + return (values, null); |
| 89 | + } |
| 90 | +} |
0 commit comments