Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

.NET Native causing NullReferenceException in CallerIdentity class #1149

Closed
borrrden opened this issue Sep 24, 2019 · 12 comments
Closed

.NET Native causing NullReferenceException in CallerIdentity class #1149

borrrden opened this issue Sep 24, 2019 · 12 comments
Labels

Comments

@borrrden
Copy link

borrrden commented Sep 24, 2019

Description

When using certain functions in UWP tests, null reference exceptions occur only when compiling with .NET Native (which is a requirement for the Windows Store).

Complete minimal example reproducing the issue

Complete means the code snippet can be copied into a unit test method in a fresh C# project and run.
Minimal means it is stripped from code not related to reproducing the issue.

E.g.

var arr1 = new[] {1, 2, 3, 4, 5};
var arr2 = new[] {1, 2, 3, 4, 5, 6};
arr1.Should().NotBeEquivalentTo(arr2);

Expected behavior:

The assertion should pass

Actual behavior:

Message: Test method ... threw exception:
System.NullReferenceException: Object reference not set to an instance of an object.

Test Name: TestReKey
Test FullName: Test.DatabaseEncryptionTest.TestReKey
Test Source: C:\Development\couchbase-lite-net-ee\couchbase-lite-net\src\Couchbase.Lite.Tests.Shared\DatabaseEncryptionTest.cs : line 169
Test Outcome: Failed
Test Duration: 0:00:00.2356786

Result StackTrace:

at FluentAssertions.CallerIdentifier.DetermineCallerIdentity()
   at FluentAssertions.Execution.AssertionScope.<>c__DisplayClass30_0.<FailWith>b__0()
   at FluentAssertions.Execution.AssertionScope.FailWith(Func<System.String> failReasonFunc)
   at FluentAssertions.Execution.AssertionScope.FailWith(Func<FluentAssertions.Execution.FailReason> failReasonFunc)
   at FluentAssertions.Execution.AssertionScope.FailWith(String message, Object[] args)
   at Continuation FluentAssertions.Equivalency.EnumerableEquivalencyValidatorExtensions.AssertCollectionHasEnoughItems[T](IAssertionScope, ICollection`1, ICollection`1) + 0x100
   at Continuation FluentAssertions.Equivalency.EnumerableEquivalencyValidator.AssertCollectionsHaveSameCount[T](ICollection`1, ICollection`1) + 0xe8
   at Void FluentAssertions.Equivalency.EnumerableEquivalencyValidator.Execute[T](Object[], T[]) + 0x90
   at FluentAssertions.Equivalency.EnumerableEquivalencyStep.Handle(Equivalency.IEquivalencyValidationContext context, Equivalency.IEquivalencyValidator parent, Equivalency.IEquivalencyAssertionOptions config)
   at FluentAssertions.Equivalency.EquivalencyValidator.RunStepsUntilEquivalencyIsProven(Equivalency.IEquivalencyValidationContext context)
   at FluentAssertions.Equivalency.EquivalencyValidator.AssertEqualityUsing(Equivalency.IEquivalencyValidationContext context)
   at FluentAssertions.Equivalency.EquivalencyValidator.AssertEquality(Equivalency.EquivalencyValidationContext context)
   at AndConstraint`1 FluentAssertions.Collections.CollectionAssertions`2.BeEquivalentTo(IEnumerable, Func`2, String, Object[]) + 0x14e
   at AndConstraint`1 FluentAssertions.Collections.CollectionAssertions`2.BeEquivalentTo(IEnumerable, String, Object[]) + 0x97
   at AndConstraint`1 FluentAssertions.Collections.CollectionAssertions`2.NotBeEquivalentTo(IEnumerable, String, Object[]) + 0x2ed

Stack trace remainder omitted (no more FA frames)

Versions

Fluent Assertions 5.9.0
UWP 6.0.6 with .NET Native Compiler enabled

Additional Information

This is probably due to native frames being passed through instead of C# ones as the result of the native compilation. Debug builds, without .NET Native enabled, work as expected. I recently upgraded from 4.x to 5.9 and I don't remember this happening in 4.x. I've confirmed this happens because each StackFrame in StackTrace returns null from GetMethod(). I feel like this method DetermineCallerIdentity should be used quite a bit so I'm not sure why it would only fail from certain conditions.

@borrrden
Copy link
Author

On second thought, why is this trying to fail? The assertion should pass and have no reason to go through FailWith in the first place...

This seems to only have with NotBeEquivalentTo and not BeEquivalentTo

borrrden added a commit to couchbase/couchbase-lite-net that referenced this issue Sep 24, 2019
Unable to run this test without crashing due to fluentassertions/fluentassertions#1149
@dennisdoomen
Copy link
Member

When comparing collections by equivalency, it's trying to find a combination that as equivalent as possible and report that. Because of its recursive nature, it relies on the internal comparison to throw a failure. So we really have two bugs here.

jnyrup added a commit to jnyrup/fluentassertions that referenced this issue Oct 25, 2019
Apparently when compiling an UWP project with .NET Native
`StackFrame.GetMethod` can return `null`. As determination
of caller identity is just a nice-to-have and not a need-to-have
I think we should wrap it in a `try/catch` instead of throwing
exceptions.
Related to fluentassertions#1149
@dennisdoomen
Copy link
Member

This is fixed, right?

@jnyrup
Copy link
Member

jnyrup commented Jan 1, 2020

Yes should be. We null check GetMethod and catch exceptions in any case. So I'll close this for now.
Feel free to reopen if the issue hasn't been fixed.
Could be nice with a regression test to verify the fix though

@jnyrup jnyrup closed this as completed Jan 1, 2020
@Nirmal4G
Copy link

@jnyrup This seems to regress in v6.1 lib.

The affected PR is CommunityToolkit/WindowsCommunityToolkit#4181 in the Windows Community Toolkit project. This happened when I was trying to upgrade v5.10.x to v6.1 of the FluentAssertions library in the UWP test project.

The affected build log is here: Line 2449 of the Build #7.0.0-build.1597+21ac5a2cbb

@dennisdoomen
Copy link
Member

dennisdoomen commented Sep 23, 2021

I can't access that build. Would you mind creating a new issue for that with the stacktrace and as much information as possible?

@jnyrup
Copy link
Member

jnyrup commented Sep 23, 2021

I can access the build.

Failed First_WhenGroupDoesNotExist_ShouldThrow [5 ms]
  Error Message:
   Expected a <System.InvalidOperationException> to be thrown, but found <System.NullReferenceException>: "
"System.NullReferenceException with message "Object reference not set to an instance of an object."
     at FluentAssertions.CallerIdentifier.<>c.<DetermineCallerIdentity>b__4_0(StackFrame) + 0xc
     at System.Func`2.Invoke(T) + 0x23
     at System.Linq.Enumerable.WhereArrayIterator`1.MoveNext() + 0x36
     at System.Collections.Generic.LargeArrayBuilder`1.AddRange(IEnumerable`1) + 0x49
     at FluentAssertions.CallerIdentifier.OnlyOneFluentAssertionScopeOnCallStack() + 0x1e5
     at FluentAssertions.Specialized.DelegateAssertions`2.InvokeSubjectWithInterception() + 0x1a
  
.
  Stack Trace:
     at FluentAssertions.Execution.LateBoundTestFramework.Throw(String) + 0xea
   at FluentAssertions.Execution.TestFrameworkProvider.Throw(String) + 0x35
   at System.Action`1.InvokeOpenStaticThunk(T) + 0x19
   at FluentAssertions.Execution.DefaultAssertionStrategy.HandleFailure(String) + 0x38
   at FluentAssertions.Execution.AssertionScope.FailWith(Func`1) + 0xac
   at FluentAssertions.Execution.AssertionScope.FailWith(Func`1) + 0x5b
   at FluentAssertions.Execution.AssertionScope.FailWith(String, Object[]) + 0x5b
   at FluentAssertions.Specialized.DelegateAssertionsBase`2.ThrowInternal[TException](Exception, String, Object[]) + 0x26e
   at FluentAssertions.Specialized.DelegateAssertions`2.Throw[TException](String, Object[]) + 0xb6
   at UnitTests.Collections.ObservableGroupedCollectionExtensionsTests.First_WhenGroupDoesNotExist_ShouldThrow() + 0x132
   at _$ILCT$.$ILT$ReflectionDynamicInvoke$.InvokeRetV(Object, IntPtr, InvokeUtils.ArgSetupState&, Boolean) + 0x2f
   at System.InvokeUtils.CalliIntrinsics.Call(IntPtr, Object, IntPtr, InvokeUtils.ArgSetupState&, Boolean) + 0x2c
   at System.InvokeUtils.CallDynamicInvokeMethod(Object, IntPtr, Object, IntPtr, IntPtr, Object, Object[], BinderBundle, Boolean, Boolean, Boolean) + 0x119

It looks like IsCompilerServices throws the NRE when calling .Where(frame => !IsCompilerServices(frame)) in OnlyOneFluentAssertionScopeOnCallStack.

GetFrames() nullability annotation indicates that it should not contain nulls, but from what I can infer from the stack trace, that's exactly what happens.

@dennisdoomen
Copy link
Member

Weird that there's no line numbers anymore. I guess the PDBs are dropped when you compile to .NET Native. Although I wonder why you would run your unit tests like that.

@Nirmal4G
Copy link

@michael-hawker @RosarioPulella @XAML-Knight 👆 Do we know something about this?

This is regarding the FluentAsserstions upgrade I did as part of the CPVM feature.

@michael-hawker
Copy link

If it's coming from our CI then it'd be so it matches the runtime environment that we expect the code to run in. Locally, it should just run in whatever configuration VS is set to. Eh, @azchohfi?

@dennisdoomen
Copy link
Member

Regardless, we need a new bug for this specific case.

@michael-hawker
Copy link

@Nirmal4G mind opening a new bug since I think you have all the context here?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants