Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 30 additions & 5 deletions src/Microsoft.VisualStudio.Validation/Assumes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Runtime;
using System.Runtime.CompilerServices;

Expand All @@ -24,7 +23,10 @@ public static partial class Assumes
public static void NotNull<T>([ValidatedNotNull, NotNull]T? value)
where T : class
{
True(value is object);
if (value is null)
{
FailNotNull<T>();
}
}

/// <summary>
Expand All @@ -36,7 +38,10 @@ public static void NotNull<T>([ValidatedNotNull, NotNull]T? value)
public static void NotNull<T>([ValidatedNotNull, NotNull]T? value)
where T : struct
{
True(value.HasValue);
if (!value.HasValue)
{
FailNotNull<T>();
}
}

/// <summary>
Expand Down Expand Up @@ -97,7 +102,10 @@ public static void NotNullOrEmpty<T>([ValidatedNotNull, NotNull]IEnumerable<T>?
public static void Null<T>(T? value)
where T : class
{
True(value is null);
if (value is not null)
{
FailNull<T>();
}
}

/// <summary>
Expand All @@ -109,7 +117,10 @@ public static void Null<T>(T? value)
public static void Null<T>(T? value)
where T : struct
{
False(value.HasValue);
if (value.HasValue)
{
FailNull<T>();
}
}

/// <summary>
Expand Down Expand Up @@ -356,6 +367,20 @@ public static Exception Fail([Localizable(false)] string? message, Exception? in
}
}

[DoesNotReturn]
[MethodImpl(MethodImplOptions.NoInlining)]
private static void FailNotNull<T>()
{
Fail(Strings.FormatUnexpectedNull(typeof(T).Name));
}

[DoesNotReturn]
[MethodImpl(MethodImplOptions.NoInlining)]
private static void FailNull<T>()
{
Fail(Strings.FormatUnexpectedNonNull(typeof(T).Name));
}

/// <summary>
/// Helper method that formats string arguments.
/// </summary>
Expand Down
6 changes: 6 additions & 0 deletions src/Microsoft.VisualStudio.Validation/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -144,4 +144,10 @@
<data name="ServiceMissing" xml:space="preserve">
<value>Cannot find an instance of the {0} service.</value>
</data>
<data name="UnexpectedNull" xml:space="preserve">
<value>Unexpected null value of type '{0}'.</value>
</data>
<data name="UnexpectedNonNull" xml:space="preserve">
<value>Unexpected non-null value of type '{0}'.</value>
</data>
</root>
25 changes: 20 additions & 5 deletions test/Microsoft.VisualStudio.Validation.Tests/AssumesTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Globalization;
using System.Runtime.Serialization.Formatters.Binary;
using Microsoft;
using Moq;
Expand All @@ -9,11 +10,13 @@
public partial class AssumesTests : IDisposable
{
private const string TestMessage = "Some test message.";
private AssertDialogSuppression suppressAssertUi = new AssertDialogSuppression();
private readonly AssertDialogSuppression suppressAssertUi = new();
private readonly OverrideCulture overrideCulture = new(CultureInfo.InvariantCulture);

public void Dispose()
{
this.suppressAssertUi.Dispose();
this.overrideCulture.Dispose();
}

[Fact]
Expand Down Expand Up @@ -79,28 +82,40 @@ public void Fail()
[Fact]
public void NotNull()
{
Assert.ThrowsAny<Exception>(() => Assumes.NotNull((object?)null));
Exception ex = Assert.ThrowsAny<Exception>(() => Assumes.NotNull((string?)null));

Assert.Equal("Unexpected null value of type 'String'.", ex.Message);
Comment thread
AArnott marked this conversation as resolved.

Assumes.NotNull("success");
}

[Fact]
public void NotNull_NullableStruct()
{
Assert.ThrowsAny<Exception>(() => Assumes.NotNull((int?)null));
Exception ex = Assert.ThrowsAny<Exception>(() => Assumes.NotNull((int?)null));

Assert.Equal("Unexpected null value of type 'Int32'.", ex.Message);

Assumes.NotNull((int?)5);
}

[Fact]
public void Null()
{
Assert.ThrowsAny<Exception>(() => Assumes.Null("not null"));
Exception ex = Assert.ThrowsAny<Exception>(() => Assumes.Null("not null"));

Assert.Equal("Unexpected non-null value of type 'String'.", ex.Message);

Assumes.Null((object?)null);
}

[Fact]
public void Null_NullableStruct()
{
Assert.ThrowsAny<Exception>(() => Assumes.Null((int?)5));
Exception ex = Assert.ThrowsAny<Exception>(() => Assumes.Null((int?)5));

Assert.Equal("Unexpected non-null value of type 'Int32'.", ex.Message);

Assumes.Null((int?)null);
}

Expand Down
28 changes: 28 additions & 0 deletions test/Microsoft.VisualStudio.Validation.Tests/OverrideCulture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Globalization;

/// <summary>
/// Sets a specific culture for a duration, restoring the prior culture upon disposal.
/// </summary>
internal sealed class OverrideCulture : IDisposable
{
private readonly CultureInfo priorCulture;
private readonly CultureInfo priorUICulture;

public OverrideCulture(CultureInfo culture)
{
this.priorCulture = CultureInfo.CurrentCulture;
this.priorUICulture = CultureInfo.CurrentUICulture;

CultureInfo.CurrentCulture = culture;
CultureInfo.CurrentUICulture = culture;
}

public void Dispose()
{
CultureInfo.CurrentCulture = this.priorCulture;
CultureInfo.CurrentUICulture = this.priorUICulture;
}
}