Open FantasticFiasco opened 6 years ago
Hey, guys!
I've found similar behaviour in some tests of mine, so I decided to investigate this.
It looks like on xUnit 2.4.0.4010 this works well, at least Resharper isn't hanging. The error is the same as reported by @FantasticFiasco.
But if I take 2.3.1 — everything will freeze. And I actually couldn't be able to start any other tests before shutting down dotnet
process through Task Manager.
I've also found that there are other cases when ReSharper hangs, for example:
[Theory]
[InlineData(double.MaxValue)]
public void Test(decimal a) { }
Which in, as I mentioned above, breaks in 2.3.1 but works in 2.4.0.4010 as:
System.OverflowException
Value was either too large or too small for a Decimal.
at System.Decimal..ctor(Double value)
at System.Decimal.op_Explicit(Double value)
So it looks like any exception during some prewarm phase can cause freeze no matter of concrete semantics. Good news are that it is probably already fixed in 2.4.0.
@JoshuaLight Thanks for the information! I don't think it's the same problem as the original (in your case, the values "look" convertible to one another once they've been serialized, but don't fit in the destination data type).
@bradwilson
Maybe I'm wrong, but I think in both cases we have exceptional situtations:
1) System.ArgumentException: There is at least one object in this array that cannot be serialized
for case when you use custom type in method parameters.
2) System.OverflowException: Value was either too large or too small for a Decimal
for case, when there is actually uncompilable situation, because you cannot cast double
to decimal
neither implicitly nor explicitly.
They should be reported as they are (and they're reported in some cases), but it looks like sometimes both exceptions kinda stuck in the throat of ReSharper runner. ))
It can be worth considering to check whether any other exception can hang an environment.
The original bug here (1) is an actual bug.
Your new issue (2) is by design (that is supposed to throw an exception).
It can be worth considering to check whether any other exception can hang an environment.
If you find anything which does, please open a new issue. This one is not about exceptions hanging environments, but rather about a feature that should work, that isn't working.
Ah now I see, thanks!
The original bug here (1) is an actual bug.
Hi, Any update on this issue (bug).
Hi All,
I've had a quick look and I think the problem lies in the CanSerializeObject method of the XUnitSerializationInfo class. I wonder if adding code similar to below would help solve the problem. What do you think?
using System;
using System.Linq;
using System.Reflection;
using Shouldly;
using Xunit;
namespace XunitIssue1742
{
public class DetectImplicitConversion
{
public class Id
{
public Id(string value)
{
Value = value;
}
public string Value { get; }
public static implicit operator Id(string id)
{
return id != null ? new Id(id) : null;
}
public static implicit operator string(Id id)
{
return id?.Value;
}
}
public class NoImplicitConversions
{
}
[Fact]
public void DetectsImplicitConversionFromString()
{
var otherType =
typeof(string);
HasImplicitConversionFromOtherType<Id>(otherType)
.ShouldBeTrue();
HasImplicitConversionFromOtherType<NoImplicitConversions>(otherType)
.ShouldBeFalse();
}
[Fact]
public void DetectsImplicitConversionToString()
{
var otherType =
typeof(string);
HasImplicitConversionToOtherType<Id>(otherType)
.ShouldBeTrue();
HasImplicitConversionToOtherType<NoImplicitConversions>(otherType)
.ShouldBeFalse();
}
/// <summary>
/// Based on https://stackoverflow.com/questions/32025201/how-can-i-determine-if-an-implicit-cast-exists-in-c/32025393#32025393
/// </summary>
private static bool HasImplicitConversionFromOtherType<T>(Type otherType) where T : class
{
var typeToConvertTo = typeof(T);
return typeToConvertTo.GetMethods(BindingFlags.Public | BindingFlags.Static)
.Where(mi => mi.Name == "op_Implicit" && mi.ReturnType == typeToConvertTo)
.Any(mi =>
{
ParameterInfo pi = mi.GetParameters().FirstOrDefault();
return pi != null && pi.ParameterType == otherType;
});
}
private static bool HasImplicitConversionToOtherType<T>(Type otherType) where T : class
{
var typeToConvertFrom = typeof(T);
return typeToConvertFrom.GetMethods(BindingFlags.Public | BindingFlags.Static)
.Where(mi => mi.Name == "op_Implicit" && mi.ReturnType == otherType)
.Any(mi =>
{
ParameterInfo pi = mi.GetParameters().FirstOrDefault();
return pi != null && pi.ParameterType == typeToConvertFrom;
});
}
}
}
I believe the custom serialization system is being removed in v3 (and there are no more planned releases for v2), so this would become unnecessary.
OK, cool. I couldn't find a release roadmap. Do you know when v3 is scheduled for release?
As a workaround for unit test. I have added support of IXunitSerializable to help XUnit to build the correct test case data.
public sealed record CountryCode(string Value) : IXunitSerializable
{
// It's required by IXunitSerializable
public CountryCode(): this(string.Empty) { }
public override string ToString()
{
return Value;
}
public static implicit operator CountryCode(string self)
{
return new CountryCode(self);
}
public static implicit operator string(CountryCode self)
{
return self.Value;
}
void IXunitSerializable.Serialize(IXunitSerializationInfo info)
{
info.AddValue(nameof(Value), Value, typeof(string));
}
void IXunitSerializable.Deserialize(IXunitSerializationInfo info)
{
throw new NotSupportedException("This should not be used.");
}
}
@kolbasik You're throwing in Deserialize. That would never work in Test Explorer (where serialization comes into play).
Hi, @bradwilson. You might be right that in some cases it might not work. It have described a case when it helped me. I have made a small demo app.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>
using Xunit;
using Xunit.Abstractions;
namespace XUnitRecordsDemo
{
public sealed class XUnitTest
{
[Theory]
[InlineData("BE")]
[InlineData("NL")]
[InlineData("LU")]
public void ShouldAcceptRecordsAsArguments(CountryCode countryCode)
{
Assert.NotNull(countryCode);
}
}
public sealed record CountryCode(string Value) : IXunitSerializable
{
// It's required by IXunitSerializable
public CountryCode(): this(string.Empty) { }
public override string ToString()
{
return Value;
}
public static implicit operator CountryCode(string self)
{
return new CountryCode(self);
}
public static implicit operator string(CountryCode self)
{
return self.Value;
}
void IXunitSerializable.Serialize(IXunitSerializationInfo info)
{
info.AddValue(nameof(Value), Value, typeof(string));
}
void IXunitSerializable.Deserialize(IXunitSerializationInfo info)
{
throw new NotSupportedException("It should not be used.");
}
}
}
Using cli dotnet test --nologo --logger "console;verbosity=detailed"
Using ReSharper Unit Test Session
Using Visual Studio 2022 Test Explorer
I would be glad if it would help anyone.
Hi all,
I want to resurrect this just because it was a huge pain to try and debug. I accidentally relied on an implicit cast without knowing the side effects. I used a ReadOnlyMemory<byte>
instead of a byte[]
dotnet test
reported all tests passed by still failed with exit code 1:
Passed! - Failed: 0, Passed: 1622, Skipped: 124, Total: 1746, Duration: 3 m 32 s - Emu.Tests.dll (net7.0)
Error: Process completed with exit code 1.
I found the following entries in the output of dotnet test:
[xUnit.net 00:00:03.60] [FATAL ERROR] System.ArgumentException
[xUnit.net 00:00:03.60] Catastrophic failure: System.ArgumentException : There is at least one object in this array that cannot be serialized (Parameter 'array')
[xUnit.net 00:00:03.60] [FATAL ERROR] System.ArgumentException
[xUnit.net 00:00:03.60] Catastrophic failure: System.ArgumentException : There is at least one object in this array that cannot be serialized (Parameter 'array')
[xUnit.net 00:00:03.60] [FATAL ERROR] System.ArgumentException
[xUnit.net 00:00:03.60] Catastrophic failure: System.ArgumentException : There is at least one object in this array that cannot be serialized (Parameter 'array')
[xUnit.net 00:00:03.60] [FATAL ERROR] System.ArgumentException
[xUnit.net 00:00:03.60] Catastrophic failure: System.ArgumentException : There is at least one object in this array that cannot be serialized (Parameter 'array')
[xUnit.net 00:00:03.60] [FATAL ERROR] System.ArgumentException
[xUnit.net 00:00:03.60] Catastrophic failure: System.ArgumentException : There is at least one object in this array that cannot be serialized (Parameter 'array')
[xUnit.net 00:00:03.60] [FATAL ERROR] System.ArgumentException
[xUnit.net 00:00:03.60] Catastrophic failure: System.ArgumentException : There is at least one object in this array that cannot be serialized (Parameter 'array')
[xUnit.net 00:00:03.60] [FATAL ERROR] System.ArgumentException
[xUnit.net 00:00:03.60] Catastrophic failure: System.ArgumentException : There is at least one object in this array that cannot be serialized (Parameter 'array')
[xUnit.net 00:00:03.60] [FATAL ERROR] System.ArgumentException
[xUnit.net 00:00:03.60] Catastrophic failure: System.ArgumentException : There is at least one object in this array that cannot be serialized (Parameter 'array')
[xUnit.net 00:00:03.60] [FATAL ERROR] System.ArgumentException
[xUnit.net 00:00:03.60] Catastrophic failure: System.ArgumentException : There is at least one object in this array that cannot be serialized (Parameter 'array')
[xUnit.net 00:00:03.60] [FATAL ERROR] System.ArgumentException
[xUnit.net 00:00:03.60] Catastrophic failure: System.ArgumentException : There is at least one object in this array that cannot be serialized (Parameter 'array')
[xUnit.net 00:00:03.60] [FATAL ERROR] System.ArgumentException
[xUnit.net 00:00:03.60] Catastrophic failure: System.ArgumentException : There is at least one object in this array that cannot be serialized (Parameter 'array')
[xUnit.net 00:00:03.61] [FATAL ERROR] System.ArgumentException
[xUnit.net 00:00:03.61] Catastrophic failure: System.ArgumentException : There is at least one object in this array that cannot be serialized (Parameter 'array')
[xUnit.net 00:00:03.61] [FATAL ERROR] System.ArgumentException
[xUnit.net 00:00:03.61] Catastrophic failure: System.ArgumentException : There is at least one object in this array that cannot be serialized (Parameter 'array')
[xUnit.net 00:00:03.61] [FATAL ERROR] System.ArgumentException
[xUnit.net 00:00:03.61] Catastrophic failure: System.ArgumentException : There is at least one object in this array that cannot be serialized (Parameter 'array')
[xUnit.net 00:00:03.61] [FATAL ERROR] System.ArgumentException
[xUnit.net 00:00:03.61] Catastrophic failure: System.ArgumentException : There is at least one object in this array that cannot be serialized (Parameter 'array')
BUT:
diagnosticMessages
and internalDiagnosticMessages
enabled only a full stack trace in the runner is shown but no context for the problem test is providedThe only way I managed to track this down is by disabling every theory and then reenabling them one-by-one.
I'm mainly writing this because searches for the problem led me to this issue and I'm hoping documenting this will help someone else.
Expected Behavior
dotnet test
should pass ifdotnet xunit
passActual Behavior
dotnet xunit
will successfully run the test whiledotnet test
will report the following error, which actually doesn't describe the real problem, i.e. that the implicit conversion for some reason cannot be handled.Steps to Reproduce the Problem
dotnet new xunit
public class Id { public Id(string value) { Value = value; }
}