Skip to content

Commit bb4d985

Browse files
authored
feat: add string length range assertions (#4935)
* feat: add string length range assertions (HasMinLength, HasMaxLength, HasLengthBetween) Add three new string assertion methods for validating string length ranges: - HasMinLength(int) - asserts string length >= minLength - HasMaxLength(int) - asserts string length <= maxLength - HasLengthBetween(int, int) - asserts min <= string length <= max Closes #4868 * chore: update source generator snapshot files after rebase * refactor: use [GenerateAssertion] for string length assertions and add bounds guard Convert HasMinLength, HasMaxLength, HasLengthBetween from manual assertion classes to [GenerateAssertion] source-generated assertions, reducing ~170 lines of boilerplate. Add ArgumentOutOfRangeException guard when minLength > maxLength in HasLengthBetween. * test: add tests for HasMinLength, HasMaxLength, and HasLengthBetween assertions Cover pass/fail cases, null input handling, bounds guard validation, and And-chaining for the new string length range assertions.
1 parent e140fb7 commit bb4d985

15 files changed

Lines changed: 440 additions & 72 deletions

TUnit.Assertions.SourceGenerator.Tests/ExceptionAssertionGeneratorTests.GeneratesExceptionAssertions.DotNet8_0.verified.txt

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -406,93 +406,113 @@ public static partial class ExceptionAssertionExtensions
406406
/// <summary>
407407
/// Generated extension method for HasInnerException
408408
/// </summary>
409-
public static Exception_HasInnerException_Assertion HasInnerException(this IAssertionSource<System.Exception> source)
409+
[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2091", Justification = "Generic type parameter is only used for property access, not instantiation")]
410+
public static Exception_HasInnerException_Assertion HasInnerException<TActual>(this IAssertionSource<TActual> source)
411+
where TActual : System.Exception
410412
{
411413
source.Context.ExpressionBuilder.Append(".HasInnerException()");
412-
return new Exception_HasInnerException_Assertion(source.Context);
414+
return new Exception_HasInnerException_Assertion(source.Context.Map<System.Exception>(static x => (System.Exception?)x));
413415
}
414416

415417
/// <summary>
416418
/// Generated extension method for HasNoInnerException
417419
/// </summary>
418-
public static Exception_HasNoInnerException_Assertion HasNoInnerException(this IAssertionSource<System.Exception> source)
420+
[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2091", Justification = "Generic type parameter is only used for property access, not instantiation")]
421+
public static Exception_HasNoInnerException_Assertion HasNoInnerException<TActual>(this IAssertionSource<TActual> source)
422+
where TActual : System.Exception
419423
{
420424
source.Context.ExpressionBuilder.Append(".HasNoInnerException()");
421-
return new Exception_HasNoInnerException_Assertion(source.Context);
425+
return new Exception_HasNoInnerException_Assertion(source.Context.Map<System.Exception>(static x => (System.Exception?)x));
422426
}
423427

424428
/// <summary>
425429
/// Generated extension method for HasStackTrace
426430
/// </summary>
427-
public static Exception_HasStackTrace_Assertion HasStackTrace(this IAssertionSource<System.Exception> source)
431+
[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2091", Justification = "Generic type parameter is only used for property access, not instantiation")]
432+
public static Exception_HasStackTrace_Assertion HasStackTrace<TActual>(this IAssertionSource<TActual> source)
433+
where TActual : System.Exception
428434
{
429435
source.Context.ExpressionBuilder.Append(".HasStackTrace()");
430-
return new Exception_HasStackTrace_Assertion(source.Context);
436+
return new Exception_HasStackTrace_Assertion(source.Context.Map<System.Exception>(static x => (System.Exception?)x));
431437
}
432438

433439
/// <summary>
434440
/// Generated extension method for HasNoData
435441
/// </summary>
436-
public static Exception_HasNoData_Assertion HasNoData(this IAssertionSource<System.Exception> source)
442+
[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2091", Justification = "Generic type parameter is only used for property access, not instantiation")]
443+
public static Exception_HasNoData_Assertion HasNoData<TActual>(this IAssertionSource<TActual> source)
444+
where TActual : System.Exception
437445
{
438446
source.Context.ExpressionBuilder.Append(".HasNoData()");
439-
return new Exception_HasNoData_Assertion(source.Context);
447+
return new Exception_HasNoData_Assertion(source.Context.Map<System.Exception>(static x => (System.Exception?)x));
440448
}
441449

442450
/// <summary>
443451
/// Generated extension method for HasHelpLink
444452
/// </summary>
445-
public static Exception_HasHelpLink_Assertion HasHelpLink(this IAssertionSource<System.Exception> source)
453+
[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2091", Justification = "Generic type parameter is only used for property access, not instantiation")]
454+
public static Exception_HasHelpLink_Assertion HasHelpLink<TActual>(this IAssertionSource<TActual> source)
455+
where TActual : System.Exception
446456
{
447457
source.Context.ExpressionBuilder.Append(".HasHelpLink()");
448-
return new Exception_HasHelpLink_Assertion(source.Context);
458+
return new Exception_HasHelpLink_Assertion(source.Context.Map<System.Exception>(static x => (System.Exception?)x));
449459
}
450460

451461
/// <summary>
452462
/// Generated extension method for HasNoHelpLink
453463
/// </summary>
454-
public static Exception_HasNoHelpLink_Assertion HasNoHelpLink(this IAssertionSource<System.Exception> source)
464+
[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2091", Justification = "Generic type parameter is only used for property access, not instantiation")]
465+
public static Exception_HasNoHelpLink_Assertion HasNoHelpLink<TActual>(this IAssertionSource<TActual> source)
466+
where TActual : System.Exception
455467
{
456468
source.Context.ExpressionBuilder.Append(".HasNoHelpLink()");
457-
return new Exception_HasNoHelpLink_Assertion(source.Context);
469+
return new Exception_HasNoHelpLink_Assertion(source.Context.Map<System.Exception>(static x => (System.Exception?)x));
458470
}
459471

460472
/// <summary>
461473
/// Generated extension method for HasSource
462474
/// </summary>
463-
public static Exception_HasSource_Assertion HasSource(this IAssertionSource<System.Exception> source)
475+
[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2091", Justification = "Generic type parameter is only used for property access, not instantiation")]
476+
public static Exception_HasSource_Assertion HasSource<TActual>(this IAssertionSource<TActual> source)
477+
where TActual : System.Exception
464478
{
465479
source.Context.ExpressionBuilder.Append(".HasSource()");
466-
return new Exception_HasSource_Assertion(source.Context);
480+
return new Exception_HasSource_Assertion(source.Context.Map<System.Exception>(static x => (System.Exception?)x));
467481
}
468482

469483
/// <summary>
470484
/// Generated extension method for HasNoSource
471485
/// </summary>
472-
public static Exception_HasNoSource_Assertion HasNoSource(this IAssertionSource<System.Exception> source)
486+
[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2091", Justification = "Generic type parameter is only used for property access, not instantiation")]
487+
public static Exception_HasNoSource_Assertion HasNoSource<TActual>(this IAssertionSource<TActual> source)
488+
where TActual : System.Exception
473489
{
474490
source.Context.ExpressionBuilder.Append(".HasNoSource()");
475-
return new Exception_HasNoSource_Assertion(source.Context);
491+
return new Exception_HasNoSource_Assertion(source.Context.Map<System.Exception>(static x => (System.Exception?)x));
476492
}
477493

478494
/// <summary>
479495
/// Generated extension method for HasTargetSite
480496
/// </summary>
497+
[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2091", Justification = "Generic type parameter is only used for property access, not instantiation")]
481498
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Exception.TargetSite uses reflection which may be trimmed in AOT scenarios")]
482-
public static Exception_HasTargetSite_Assertion HasTargetSite(this IAssertionSource<System.Exception> source)
499+
public static Exception_HasTargetSite_Assertion HasTargetSite<TActual>(this IAssertionSource<TActual> source)
500+
where TActual : System.Exception
483501
{
484502
source.Context.ExpressionBuilder.Append(".HasTargetSite()");
485-
return new Exception_HasTargetSite_Assertion(source.Context);
503+
return new Exception_HasTargetSite_Assertion(source.Context.Map<System.Exception>(static x => (System.Exception?)x));
486504
}
487505

488506
/// <summary>
489507
/// Generated extension method for HasNoTargetSite
490508
/// </summary>
509+
[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2091", Justification = "Generic type parameter is only used for property access, not instantiation")]
491510
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Exception.TargetSite uses reflection which may be trimmed in AOT scenarios")]
492-
public static Exception_HasNoTargetSite_Assertion HasNoTargetSite(this IAssertionSource<System.Exception> source)
511+
public static Exception_HasNoTargetSite_Assertion HasNoTargetSite<TActual>(this IAssertionSource<TActual> source)
512+
where TActual : System.Exception
493513
{
494514
source.Context.ExpressionBuilder.Append(".HasNoTargetSite()");
495-
return new Exception_HasNoTargetSite_Assertion(source.Context);
515+
return new Exception_HasNoTargetSite_Assertion(source.Context.Map<System.Exception>(static x => (System.Exception?)x));
496516
}
497517

498518
}

TUnit.Assertions.SourceGenerator.Tests/ExceptionAssertionGeneratorTests.GeneratesExceptionAssertions.DotNet9_0.verified.txt

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -406,93 +406,113 @@ public static partial class ExceptionAssertionExtensions
406406
/// <summary>
407407
/// Generated extension method for HasInnerException
408408
/// </summary>
409-
public static Exception_HasInnerException_Assertion HasInnerException(this IAssertionSource<System.Exception> source)
409+
[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2091", Justification = "Generic type parameter is only used for property access, not instantiation")]
410+
public static Exception_HasInnerException_Assertion HasInnerException<TActual>(this IAssertionSource<TActual> source)
411+
where TActual : System.Exception
410412
{
411413
source.Context.ExpressionBuilder.Append(".HasInnerException()");
412-
return new Exception_HasInnerException_Assertion(source.Context);
414+
return new Exception_HasInnerException_Assertion(source.Context.Map<System.Exception>(static x => (System.Exception?)x));
413415
}
414416

415417
/// <summary>
416418
/// Generated extension method for HasNoInnerException
417419
/// </summary>
418-
public static Exception_HasNoInnerException_Assertion HasNoInnerException(this IAssertionSource<System.Exception> source)
420+
[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2091", Justification = "Generic type parameter is only used for property access, not instantiation")]
421+
public static Exception_HasNoInnerException_Assertion HasNoInnerException<TActual>(this IAssertionSource<TActual> source)
422+
where TActual : System.Exception
419423
{
420424
source.Context.ExpressionBuilder.Append(".HasNoInnerException()");
421-
return new Exception_HasNoInnerException_Assertion(source.Context);
425+
return new Exception_HasNoInnerException_Assertion(source.Context.Map<System.Exception>(static x => (System.Exception?)x));
422426
}
423427

424428
/// <summary>
425429
/// Generated extension method for HasStackTrace
426430
/// </summary>
427-
public static Exception_HasStackTrace_Assertion HasStackTrace(this IAssertionSource<System.Exception> source)
431+
[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2091", Justification = "Generic type parameter is only used for property access, not instantiation")]
432+
public static Exception_HasStackTrace_Assertion HasStackTrace<TActual>(this IAssertionSource<TActual> source)
433+
where TActual : System.Exception
428434
{
429435
source.Context.ExpressionBuilder.Append(".HasStackTrace()");
430-
return new Exception_HasStackTrace_Assertion(source.Context);
436+
return new Exception_HasStackTrace_Assertion(source.Context.Map<System.Exception>(static x => (System.Exception?)x));
431437
}
432438

433439
/// <summary>
434440
/// Generated extension method for HasNoData
435441
/// </summary>
436-
public static Exception_HasNoData_Assertion HasNoData(this IAssertionSource<System.Exception> source)
442+
[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2091", Justification = "Generic type parameter is only used for property access, not instantiation")]
443+
public static Exception_HasNoData_Assertion HasNoData<TActual>(this IAssertionSource<TActual> source)
444+
where TActual : System.Exception
437445
{
438446
source.Context.ExpressionBuilder.Append(".HasNoData()");
439-
return new Exception_HasNoData_Assertion(source.Context);
447+
return new Exception_HasNoData_Assertion(source.Context.Map<System.Exception>(static x => (System.Exception?)x));
440448
}
441449

442450
/// <summary>
443451
/// Generated extension method for HasHelpLink
444452
/// </summary>
445-
public static Exception_HasHelpLink_Assertion HasHelpLink(this IAssertionSource<System.Exception> source)
453+
[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2091", Justification = "Generic type parameter is only used for property access, not instantiation")]
454+
public static Exception_HasHelpLink_Assertion HasHelpLink<TActual>(this IAssertionSource<TActual> source)
455+
where TActual : System.Exception
446456
{
447457
source.Context.ExpressionBuilder.Append(".HasHelpLink()");
448-
return new Exception_HasHelpLink_Assertion(source.Context);
458+
return new Exception_HasHelpLink_Assertion(source.Context.Map<System.Exception>(static x => (System.Exception?)x));
449459
}
450460

451461
/// <summary>
452462
/// Generated extension method for HasNoHelpLink
453463
/// </summary>
454-
public static Exception_HasNoHelpLink_Assertion HasNoHelpLink(this IAssertionSource<System.Exception> source)
464+
[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2091", Justification = "Generic type parameter is only used for property access, not instantiation")]
465+
public static Exception_HasNoHelpLink_Assertion HasNoHelpLink<TActual>(this IAssertionSource<TActual> source)
466+
where TActual : System.Exception
455467
{
456468
source.Context.ExpressionBuilder.Append(".HasNoHelpLink()");
457-
return new Exception_HasNoHelpLink_Assertion(source.Context);
469+
return new Exception_HasNoHelpLink_Assertion(source.Context.Map<System.Exception>(static x => (System.Exception?)x));
458470
}
459471

460472
/// <summary>
461473
/// Generated extension method for HasSource
462474
/// </summary>
463-
public static Exception_HasSource_Assertion HasSource(this IAssertionSource<System.Exception> source)
475+
[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2091", Justification = "Generic type parameter is only used for property access, not instantiation")]
476+
public static Exception_HasSource_Assertion HasSource<TActual>(this IAssertionSource<TActual> source)
477+
where TActual : System.Exception
464478
{
465479
source.Context.ExpressionBuilder.Append(".HasSource()");
466-
return new Exception_HasSource_Assertion(source.Context);
480+
return new Exception_HasSource_Assertion(source.Context.Map<System.Exception>(static x => (System.Exception?)x));
467481
}
468482

469483
/// <summary>
470484
/// Generated extension method for HasNoSource
471485
/// </summary>
472-
public static Exception_HasNoSource_Assertion HasNoSource(this IAssertionSource<System.Exception> source)
486+
[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2091", Justification = "Generic type parameter is only used for property access, not instantiation")]
487+
public static Exception_HasNoSource_Assertion HasNoSource<TActual>(this IAssertionSource<TActual> source)
488+
where TActual : System.Exception
473489
{
474490
source.Context.ExpressionBuilder.Append(".HasNoSource()");
475-
return new Exception_HasNoSource_Assertion(source.Context);
491+
return new Exception_HasNoSource_Assertion(source.Context.Map<System.Exception>(static x => (System.Exception?)x));
476492
}
477493

478494
/// <summary>
479495
/// Generated extension method for HasTargetSite
480496
/// </summary>
497+
[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2091", Justification = "Generic type parameter is only used for property access, not instantiation")]
481498
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Exception.TargetSite uses reflection which may be trimmed in AOT scenarios")]
482-
public static Exception_HasTargetSite_Assertion HasTargetSite(this IAssertionSource<System.Exception> source)
499+
public static Exception_HasTargetSite_Assertion HasTargetSite<TActual>(this IAssertionSource<TActual> source)
500+
where TActual : System.Exception
483501
{
484502
source.Context.ExpressionBuilder.Append(".HasTargetSite()");
485-
return new Exception_HasTargetSite_Assertion(source.Context);
503+
return new Exception_HasTargetSite_Assertion(source.Context.Map<System.Exception>(static x => (System.Exception?)x));
486504
}
487505

488506
/// <summary>
489507
/// Generated extension method for HasNoTargetSite
490508
/// </summary>
509+
[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming", "IL2091", Justification = "Generic type parameter is only used for property access, not instantiation")]
491510
[System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("Exception.TargetSite uses reflection which may be trimmed in AOT scenarios")]
492-
public static Exception_HasNoTargetSite_Assertion HasNoTargetSite(this IAssertionSource<System.Exception> source)
511+
public static Exception_HasNoTargetSite_Assertion HasNoTargetSite<TActual>(this IAssertionSource<TActual> source)
512+
where TActual : System.Exception
493513
{
494514
source.Context.ExpressionBuilder.Append(".HasNoTargetSite()");
495-
return new Exception_HasNoTargetSite_Assertion(source.Context);
515+
return new Exception_HasNoTargetSite_Assertion(source.Context.Map<System.Exception>(static x => (System.Exception?)x));
496516
}
497517

498518
}

0 commit comments

Comments
 (0)