File: Throw\ThrowTest.cs
Web Access
Project: src\test\Shared\Shared.Tests.csproj (Shared.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Generic;
using Xunit;
 
namespace Microsoft.Shared.Diagnostics.Test;
 
#pragma warning disable S3236 // Caller information arguments should not be provided explicitly
 
public class ThrowTest
{
    #region Exceptions
 
    [Fact]
    public void ThrowInvalidOperationException_ThrowsException_WithMessage()
    {
        var message = "message";
        var exception = Assert.Throws<InvalidOperationException>(() => Throw.InvalidOperationException(message));
        Assert.Contains(message, exception.Message, StringComparison.Ordinal);
    }
 
    [Fact]
    public void ThrowInvalidOperationException_ThrowsException_WithMessageAndInnerException()
    {
        var message = "message";
#pragma warning disable CA2201 // Do not raise reserved exception types
        var innerException = new Exception();
#pragma warning restore CA2201 // Do not raise reserved exception types
        var exception = Assert.Throws<InvalidOperationException>(() => Throw.InvalidOperationException(message, innerException));
        Assert.Contains(message, exception.Message, StringComparison.Ordinal);
        Assert.Equal(innerException, exception.InnerException);
    }
 
    [Fact]
    public void ThrowArgumentException_ThrowsException_WithMessageAndParamName()
    {
        var message = "message";
        var paramName = "paramName";
        var exception = Assert.Throws<ArgumentException>(() => Throw.ArgumentException(paramName, message));
        Assert.Contains(message, exception.Message, StringComparison.Ordinal);
        Assert.Equal(paramName, exception.ParamName);
    }
 
    [Fact]
    public void ThrowArgumentException_ThrowsException_WithMessageAndParamNameAndInnerException()
    {
        var message = "message";
        var paramName = "paramName";
#pragma warning disable CA2201 // Do not raise reserved exception types
        var innerException = new Exception();
#pragma warning restore CA2201 // Do not raise reserved exception types
        var exception = Assert.Throws<ArgumentException>(() => Throw.ArgumentException(paramName, message, innerException));
        Assert.Contains(message, exception.Message, StringComparison.Ordinal);
        Assert.Equal(paramName, exception.ParamName);
        Assert.Equal(innerException, exception.InnerException);
    }
 
    [Fact]
    public void ThrowArgumentNullException_ThrowsException_WithMessage()
    {
        var paramName = "paramName";
        var exception = Assert.Throws<ArgumentNullException>(() => Throw.ArgumentNullException(paramName));
        Assert.Equal(paramName, exception.ParamName);
    }
 
    [Fact]
    public void ThrowArgumentNullException_ThrowsException_WithMessageAndParamName()
    {
        var paramName = "paramName";
        var message = "message";
        var exception = Assert.Throws<ArgumentNullException>(() => Throw.ArgumentNullException(paramName, message));
        Assert.Contains(message, exception.Message, StringComparison.Ordinal);
        Assert.Equal(paramName, exception.ParamName);
    }
 
    [Fact]
    public void ThrowArgumentOutOfRangeException_ThrowsException_WithParamName()
    {
        var paramName = "paramName";
        var exception = Assert.Throws<ArgumentOutOfRangeException>(() => Throw.ArgumentOutOfRangeException(paramName));
        Assert.Equal(paramName, exception.ParamName);
    }
 
    [Fact]
    public void ThrowArgumentOutOfRangeException_ThrowsException_WithMessageAndParamName()
    {
        var paramName = "paramName";
        var message = "message";
        var exception = Assert.Throws<ArgumentOutOfRangeException>(() => Throw.ArgumentOutOfRangeException(paramName, message));
        Assert.Contains(message, exception.Message, StringComparison.Ordinal);
        Assert.Equal(paramName, exception.ParamName);
    }
 
    [Fact]
    public void ThrowArgumentOutOfRangeException_ThrowsException_WithMessageAndActualValue()
    {
        var paramName = "paramName";
        var message = "message";
        var actualValue = 10;
 
        var exception = Assert.Throws<ArgumentOutOfRangeException>(() => Throw.ArgumentOutOfRangeException(paramName, actualValue, message));
        Assert.Contains(message, exception.Message, StringComparison.Ordinal);
        Assert.Equal(paramName, exception.ParamName);
        Assert.Equal(actualValue, exception.ActualValue);
    }
 
    #endregion
 
    #region For Object
 
    [Fact]
    public void ThrowIfNull_ThrowsWhenNullValue_ReferenceType()
    {
        var exception = Assert.Throws<ArgumentNullException>(() => Throw.IfNull<string>(null!, "paramName"));
        Assert.Equal("paramName", exception.ParamName);
    }
 
    [Fact]
    public void ThrowIfNull_ThrowsWhenNullValue_NullableValueType()
    {
        var exception = Assert.Throws<ArgumentNullException>(() => Throw.IfNull<int?>(null!, "paramName"));
        Assert.Equal("paramName", exception.ParamName);
    }
 
    [Fact]
    public void ThrowIfNull_DoesntThrow_WhenNotNullReferenceType()
    {
        var value = string.Empty;
        Assert.Equal(value, Throw.IfNull(value, nameof(value)));
    }
 
    [Fact]
    public void ThrowIfNull_DoesntThrow_WhenNullableValueType()
    {
        int? value = 0;
        Assert.Equal(value, Throw.IfNull(value, nameof(value)));
    }
 
    [Fact]
    public void Shorter_Version_Of_Throws_Get_Correct_Argument_Name()
    {
        Random? somethingThatIsNull = null;
 
        var exceptionImplicitArgumentName = Record.Exception(() => Throw.IfNull(somethingThatIsNull));
        var exceptionExplicitArgumentName = Record.Exception(() => Throw.IfNull(somethingThatIsNull, nameof(somethingThatIsNull)));
 
        Assert.Equal(exceptionExplicitArgumentName.Message, exceptionImplicitArgumentName.Message);
    }
 
    [Fact]
    public void Shorter_Version_Of_Throws_Get_Correct_Argument_Name_For_Object_Checks()
    {
        Random? somethingThatIsNull = null;
        Random? somethingNestedThatIsNull = null;
        object somethingThatIsNotNull = new();
 
        var exceptionImplicitArgumentName = Record.Exception(() => Throw.IfNullOrMemberNull(somethingThatIsNull, somethingNestedThatIsNull));
        var exceptionExplicitArgumentName = Record.Exception(
            () => Throw.IfNullOrMemberNull(somethingThatIsNull, somethingNestedThatIsNull, nameof(somethingThatIsNull), nameof(somethingNestedThatIsNull)));
 
        Assert.Equal(exceptionExplicitArgumentName.Message, exceptionImplicitArgumentName.Message);
    }
 
    [Fact]
    public void Shorter_Version_Of_Throws_Get_Correct_Argument_Name_For_Member_Checks()
    {
        Color red = Color.Red;
        Random? somethingNestedThatIsNull = null;
        object somethingThatIsNotNull = new();
 
        var exceptionImplicitArgumentName = Record.Exception(() => Throw.IfNullOrMemberNull(red, somethingNestedThatIsNull));
        var exceptionExplicitArgumentName = Record.Exception(
            () => Throw.IfNullOrMemberNull(red, somethingNestedThatIsNull, nameof(red), nameof(somethingNestedThatIsNull)));
 
        Assert.Equal(exceptionExplicitArgumentName.Message, exceptionImplicitArgumentName.Message);
 
        var expectedMessage = $"Member {nameof(somethingNestedThatIsNull)} of {nameof(red)} is null";
#if NETCOREAPP3_1_OR_GREATER
        expectedMessage += $" (Parameter '{nameof(red)}')";
#else
        expectedMessage += $"\r\nParameter name: {nameof(red)}";
#endif
        Assert.Equal(expectedMessage, exceptionImplicitArgumentName.Message);
 
        exceptionImplicitArgumentName = Record.Exception(() => Throw.IfMemberNull(somethingThatIsNotNull, somethingNestedThatIsNull));
        exceptionExplicitArgumentName = Record.Exception(
            () => Throw.IfMemberNull(somethingThatIsNotNull, somethingNestedThatIsNull, nameof(somethingThatIsNotNull), nameof(somethingNestedThatIsNull)));
 
        Assert.Equal(exceptionExplicitArgumentName.Message, exceptionImplicitArgumentName.Message);
 
        expectedMessage = $"Member {nameof(somethingNestedThatIsNull)} of {nameof(somethingThatIsNotNull)} is null";
#if NETCOREAPP3_1_OR_GREATER
        expectedMessage += $" (Parameter '{nameof(somethingThatIsNotNull)}')";
#else
        expectedMessage += $"\r\nParameter name: {nameof(somethingThatIsNotNull)}";
#endif
        Assert.Equal(expectedMessage, exceptionImplicitArgumentName.Message);
    }
 
    [Fact]
    public void Shorter_Version_Of_IfNull_Does_Not_Throw_When_Member_Is_Not_Null()
    {
        Color red = Color.Red;
        Color blue = Color.Blue;
 
        var exception = Record.Exception(() => Throw.IfNullOrMemberNull(red, blue));
        Assert.Null(exception);
 
        var resultOfThrows = Throw.IfNullOrMemberNull(red, blue);
        Assert.Equal(resultOfThrows, blue);
 
        resultOfThrows = Throw.IfMemberNull(red, blue);
        Assert.Equal(resultOfThrows, blue);
    }
 
    #endregion
 
    #region For String
    [Fact]
    public void ThrowIfNullOrWhitespace_ThrowsWhenNull()
    {
        var exception = Assert.Throws<ArgumentNullException>(() => Throw.IfNullOrWhitespace(null!, "paramName"));
        Assert.Equal("paramName", exception.ParamName);
    }
 
    [Fact]
    public void ThrowIfNullOrWhitespace_ThrowsWhenWhitespace()
    {
        var exception = Assert.Throws<ArgumentException>(() => Throw.IfNullOrWhitespace("  ", "paramName"));
        Assert.Equal("paramName", exception.ParamName);
        Assert.StartsWith("Argument is whitespace", exception.Message);
    }
 
    [Fact]
    public void ThrowIfNullOrWhitespace_DoesntThrow_WhenNotNullOrWhitespace()
    {
        var exception = Record.Exception(() => Throw.IfNullOrWhitespace("param", "paramName"));
        Assert.Null(exception);
    }
 
    [Fact]
    public void ThrowIfNullOrEmpty_ThrowsWhenNull()
    {
        var exception = Assert.Throws<ArgumentNullException>(() => Throw.IfNullOrEmpty(null, "paramName"));
        Assert.Equal("paramName", exception.ParamName);
    }
 
    [Fact]
    public void ThrowIfNullOrEmpty_ThrowsWhenEmpty()
    {
        var exception = Assert.Throws<ArgumentException>(() => Throw.IfNullOrEmpty("", "paramName"));
        Assert.Equal("paramName", exception.ParamName);
        Assert.StartsWith("Argument is an empty string", exception.Message);
    }
 
    [Fact]
    public void ThrowIfNullOrWhitespace_DoesntThrow_WhenNotNullOrEmpty()
    {
        var exception = Record.Exception(() => Throw.IfNullOrEmpty("param", "paramName"));
        Assert.Null(exception);
    }
 
    [Fact]
    public void Shorter_Version_Of_ThrowIfNullOrWhitespace_Get_Correct_Argument_Name()
    {
        string? somethingThatIsNull = null;
 
        var exceptionImplicitArgumentName = Record.Exception(() => Throw.IfNullOrWhitespace(somethingThatIsNull));
        var exceptionExplicitArgumentName = Record.Exception(() => Throw.IfNullOrWhitespace(somethingThatIsNull, nameof(somethingThatIsNull)));
 
        Assert.Equal(exceptionExplicitArgumentName.Message, exceptionImplicitArgumentName.Message);
    }
 
    [Fact]
    public void Shorter_Version_Of_ThrowIfNullOrEmpty_Get_Correct_Argument_Name()
    {
        string? somethingThatIsNull = null;
 
        var exceptionImplicitArgumentName = Record.Exception(() => Throw.IfNullOrEmpty(somethingThatIsNull));
        var exceptionExplicitArgumentName = Record.Exception(() => Throw.IfNullOrEmpty(somethingThatIsNull, nameof(somethingThatIsNull)));
 
        Assert.Equal(exceptionExplicitArgumentName.Message, exceptionImplicitArgumentName.Message);
    }
 
    #endregion
 
    #region For Buffer
 
    [Fact]
    public void IfBufferTooSmall_ThrowsWhenBufferTooSmall()
    {
        var exception = Assert.Throws<ArgumentException>(() => Throw.IfBufferTooSmall(23, 24, "paramName"));
        Assert.Equal("paramName", exception.ParamName);
        Assert.StartsWith("Buffer", exception.Message);
    }
 
    [Fact]
    public void IfBufferTooSmall_DoesntThrow_WhenBufferIsLargeEnough()
    {
        var exception = Record.Exception(() => Throw.IfBufferTooSmall(23, 23, "paramName"));
        Assert.Null(exception);
    }
    #endregion
 
    #region For Collections
 
    private static IEnumerable<int> GetEmptyEnumerable()
    {
        yield break;
    }
 
    private static IEnumerable<int> GetNonemptyEnumerable()
    {
        yield return 1;
    }
 
    [Fact]
    public void Collection_IfNullOrEmpty()
    {
        ArgumentException exception = Assert.Throws<ArgumentNullException>(() => Throw.IfNullOrEmpty((ICollection<int>?)null, "foo"));
        Assert.Equal("foo", exception.ParamName);
 
        exception = Assert.Throws<ArgumentNullException>(() => Throw.IfNullOrEmpty((IReadOnlyCollection<int>?)null, "foo"));
        Assert.Equal("foo", exception.ParamName);
 
        exception = Assert.Throws<ArgumentNullException>(() => Throw.IfNullOrEmpty((List<int>?)null, "foo"));
        Assert.Equal("foo", exception.ParamName);
 
        exception = Assert.Throws<ArgumentNullException>(() => Throw.IfNullOrEmpty((Queue<int>?)null, "foo"));
        Assert.Equal("foo", exception.ParamName);
 
        exception = Assert.Throws<ArgumentNullException>(() => Throw.IfNullOrEmpty((IEnumerable<int>?)null, "foo"));
        Assert.Equal("foo", exception.ParamName);
 
        var list = new List<int>();
 
        exception = Assert.Throws<ArgumentException>(() => Throw.IfNullOrEmpty((ICollection<int>?)list, "foo"));
        Assert.Equal("foo", exception.ParamName);
        Assert.StartsWith("Collection is empty", exception.Message);
 
        exception = Assert.Throws<ArgumentException>(() => Throw.IfNullOrEmpty((IReadOnlyCollection<int>?)list, "foo"));
        Assert.Equal("foo", exception.ParamName);
        Assert.StartsWith("Collection is empty", exception.Message);
 
        exception = Assert.Throws<ArgumentException>(() => Throw.IfNullOrEmpty(list, "foo"));
        Assert.Equal("foo", exception.ParamName);
        Assert.StartsWith("Collection is empty", exception.Message);
 
        var queue = new Queue<int>();
 
        exception = Assert.Throws<ArgumentException>(() => Throw.IfNullOrEmpty(queue, "foo"));
        Assert.Equal("foo", exception.ParamName);
        Assert.StartsWith("Collection is empty", exception.Message);
 
        var enumerable = GetEmptyEnumerable();
 
        exception = Assert.Throws<ArgumentException>(() => Throw.IfNullOrEmpty(enumerable, "foo"));
        Assert.Equal("foo", exception.ParamName);
        Assert.StartsWith("Collection is empty", exception.Message);
 
        list.Add(42);
        Assert.Equal(list, Throw.IfNullOrEmpty((ICollection<int>?)list, "foo"));
        Assert.Equal(list, Throw.IfNullOrEmpty((IReadOnlyCollection<int>?)list, "foo"));
        Assert.Equal(list, Throw.IfNullOrEmpty(list, "foo"));
 
        queue.Enqueue(42);
        Assert.Equal(queue, Throw.IfNullOrEmpty(queue, "foo"));
 
        enumerable = GetNonemptyEnumerable();
        Assert.Equal(enumerable, Throw.IfNullOrEmpty(enumerable, "foo"));
    }
 
    [Fact]
    public void Shorter_Version_Of_NullOrEmpty_Get_Correct_Argument_Name()
    {
        List<int>? listButActuallyNull = null;
 
        var exceptionImplicitArgumentName = Record.Exception(() => Throw.IfNullOrEmpty(listButActuallyNull!));
 
        Assert.Contains(nameof(listButActuallyNull), exceptionImplicitArgumentName.Message);
    }
 
    #endregion
 
    #region For Enums
 
    internal enum Color
    {
        Red,
        Green,
        Blue,
    }
 
    [Fact]
    public void Enum_OutOfRange()
    {
        Assert.Equal(Color.Red, Throw.IfOutOfRange(Color.Red, "foo"));
        Assert.Equal(Color.Green, Throw.IfOutOfRange(Color.Green, "foo"));
        Assert.Equal(Color.Blue, Throw.IfOutOfRange(Color.Blue, "foo"));
 
        var exception = Assert.Throws<ArgumentOutOfRangeException>(() => Throw.IfOutOfRange((Color)(-1), "foo"));
        Assert.Equal("foo", exception.ParamName);
        Assert.Contains("is an invalid value for enum type", exception.Message);
 
        exception = Assert.Throws<ArgumentOutOfRangeException>(() => Throw.IfOutOfRange((Color)3, "foo"));
        Assert.Equal("foo", exception.ParamName);
        Assert.Contains("is an invalid value for enum type", exception.Message);
    }
 
    [Fact]
    public void Shorter_Version_Of_OutOfRange_Get_Correct_Argument_Name()
    {
        Color? colorButNull = null;
 
        var exceptionImplicitArgumentName = Record.Exception(() => Throw.IfOutOfRange((Color)colorButNull!));
        var exceptionExplicitArgumentName = Record.Exception(() => Throw.IfOutOfRange((Color)colorButNull!, nameof(colorButNull)));
 
        Assert.Equal(exceptionExplicitArgumentName.Message, exceptionImplicitArgumentName.Message);
    }
 
    #endregion
}