Skip to content

Commit

Permalink
Fix determining caller identity for NET Native
Browse files Browse the repository at this point in the history
  • Loading branch information
jnyrup committed Jan 15, 2022
1 parent b02abf4 commit 287f4b5
Show file tree
Hide file tree
Showing 17 changed files with 236 additions and 18 deletions.
6 changes: 6 additions & 0 deletions FluentAssertions.sln
Expand Up @@ -42,6 +42,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Approval.Tests", "Tests\App
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FluentAssertions.Specs", "Tests\FluentAssertions.Specs\FluentAssertions.Specs.csproj", "{6D31FFF8-E7FD-41D2-996C-CA8DDFDAE4FD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UWP.Specs", "Tests\UWP.Specs\UWP.Specs.csproj", "{08087654-2C32-4818-95E4-45362373441D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
CI|Any CPU = CI|Any CPU
Expand Down Expand Up @@ -117,6 +119,9 @@ Global
{6D31FFF8-E7FD-41D2-996C-CA8DDFDAE4FD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6D31FFF8-E7FD-41D2-996C-CA8DDFDAE4FD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6D31FFF8-E7FD-41D2-996C-CA8DDFDAE4FD}.Release|Any CPU.Build.0 = Release|Any CPU
{08087654-2C32-4818-95E4-45362373441D}.CI|Any CPU.ActiveCfg = Debug|x64
{08087654-2C32-4818-95E4-45362373441D}.Debug|Any CPU.ActiveCfg = Debug|x64
{08087654-2C32-4818-95E4-45362373441D}.Release|Any CPU.ActiveCfg = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -133,6 +138,7 @@ Global
{FCAFB0F1-79EA-4D49-813B-188D4BC4BE71} = {963262D0-9FD5-4741-8C0E-E2F34F110EF3}
{F5115158-A038-4D14-A04E-46E7863E40B9} = {963262D0-9FD5-4741-8C0E-E2F34F110EF3}
{6D31FFF8-E7FD-41D2-996C-CA8DDFDAE4FD} = {963262D0-9FD5-4741-8C0E-E2F34F110EF3}
{08087654-2C32-4818-95E4-45362373441D} = {963262D0-9FD5-4741-8C0E-E2F34F110EF3}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {75DDA3D8-9D6F-4865-93F4-DDE11DEE8290}
Expand Down
36 changes: 18 additions & 18 deletions Src/FluentAssertions/CallerIdentifier.cs
Expand Up @@ -27,7 +27,7 @@ public static string DetermineCallerIdentity()
var stack = new StackTrace(fNeedFileInfo: true);

var allStackFrames = stack.GetFrames()
.Where(frame => !IsCompilerServices(frame))
.Where(frame => frame is not null && !IsCompilerServices(frame))
.ToArray();

int searchStart = allStackFrames.Length - 1;
Expand All @@ -37,20 +37,20 @@ public static string DetermineCallerIdentity()
searchStart = Array.FindLastIndex(
allStackFrames,
allStackFrames.Length - StartStackSearchAfterStackFrame.Value.SkipStackFrameCount,
frame => !IsCurrentAssembly(frame));
frame => !IsCurrentAssembly(frame!));
}

int lastUserStackFrameBeforeFluentAssertionsCodeIndex = Array.FindIndex(
allStackFrames,
startIndex: 0,
count: searchStart + 1,
frame => !IsCurrentAssembly(frame) && !IsDotNet(frame));
frame => !IsCurrentAssembly(frame!) && !IsDotNet(frame!));

for (int i = lastUserStackFrameBeforeFluentAssertionsCodeIndex; i < allStackFrames.Length; i++)
{
var frame = allStackFrames[i];

Logger(frame.ToString());
Logger(frame!.ToString());

if (frame.GetMethod() is not null
&& !IsDynamic(frame)
Expand Down Expand Up @@ -82,13 +82,13 @@ public StackFrameReference()
var stack = new StackTrace();

var allStackFrames = stack.GetFrames()
.Where(frame => !IsCompilerServices(frame))
.Where(frame => frame is not null && !IsCompilerServices(frame))
.ToArray();

int firstUserCodeFrameIndex = 0;

while ((firstUserCodeFrameIndex < allStackFrames.Length)
&& IsCurrentAssembly(allStackFrames[firstUserCodeFrameIndex]))
&& IsCurrentAssembly(allStackFrames[firstUserCodeFrameIndex]!))
{
firstUserCodeFrameIndex++;
}
Expand All @@ -115,12 +115,12 @@ internal static IDisposable OverrideStackSearchUsingCurrentScope()
internal static bool OnlyOneFluentAssertionScopeOnCallStack()
{
var allStackFrames = new StackTrace().GetFrames()
.Where(frame => !IsCompilerServices(frame))
.Where(frame => frame is not null && !IsCompilerServices(frame))
.ToArray();

int firstNonFluentAssertionsStackFrameIndex = Array.FindIndex(
allStackFrames,
frame => !IsCurrentAssembly(frame));
frame => !IsCurrentAssembly(frame!));

if (firstNonFluentAssertionsStackFrameIndex < 0)
{
Expand All @@ -130,29 +130,29 @@ internal static bool OnlyOneFluentAssertionScopeOnCallStack()
int startOfSecondFluentAssertionsScopeStackFrameIndex = Array.FindIndex(
allStackFrames,
startIndex: firstNonFluentAssertionsStackFrameIndex + 1,
IsCurrentAssembly);
frame => IsCurrentAssembly(frame!));

return startOfSecondFluentAssertionsScopeStackFrameIndex < 0;
}

private static bool IsCustomAssertion(StackFrame frame)
{
return frame.GetMethod().IsDecoratedWithOrInherit<CustomAssertionAttribute>();
return frame.GetMethod()?.IsDecoratedWithOrInherit<CustomAssertionAttribute>() == true;
}

private static bool IsDynamic(StackFrame frame)
{
return frame.GetMethod().DeclaringType is null;
return frame.GetMethod() is { DeclaringType: null };
}

private static bool IsCurrentAssembly(StackFrame frame)
{
return frame.GetMethod().DeclaringType?.Assembly == typeof(CallerIdentifier).Assembly;
return frame.GetMethod()?.DeclaringType?.Assembly == typeof(CallerIdentifier).Assembly;
}

private static bool IsDotNet(StackFrame frame)
{
var frameNamespace = frame.GetMethod().DeclaringType.Namespace;
var frameNamespace = frame.GetMethod()?.DeclaringType?.Namespace;
var comparisonType = StringComparison.OrdinalIgnoreCase;

return frameNamespace?.StartsWith("system.", comparisonType) == true ||
Expand All @@ -161,7 +161,7 @@ private static bool IsDotNet(StackFrame frame)

private static bool IsCompilerServices(StackFrame frame)
{
return frame.GetMethod().DeclaringType?.Namespace == "System.Runtime.CompilerServices";
return frame.GetMethod()?.DeclaringType?.Namespace == "System.Runtime.CompilerServices";
}

private static string ExtractVariableNameFrom(StackFrame frame)
Expand All @@ -171,9 +171,9 @@ private static string ExtractVariableNameFrom(StackFrame frame)

if (!string.IsNullOrEmpty(statement))
{
Logger(statement);
if (!IsBooleanLiteral(statement) && !IsNumeric(statement) && !IsStringLiteral(statement) &&
!UsesNewKeyword(statement))
Logger(statement!);
if (!IsBooleanLiteral(statement!) && !IsNumeric(statement!) && !IsStringLiteral(statement!) &&
!UsesNewKeyword(statement!))
{
caller = statement;
}
Expand Down Expand Up @@ -228,7 +228,7 @@ private static string GetSourceCodeStatementFrom(StackFrame frame, StreamReader
{
sb.Append(line);
}
while (!sb.IsDone() && (line = reader.ReadLine()) != null);
while (!sb.IsDone() && (line = reader.ReadLine()!) != null);

return sb.ToString();
}
Expand Down
4 changes: 4 additions & 0 deletions Tests/UWP.Specs/App.xaml
@@ -0,0 +1,4 @@
<Application
x:Class="UWP.Specs.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" />
37 changes: 37 additions & 0 deletions Tests/UWP.Specs/App.xaml.cs
@@ -0,0 +1,37 @@
using System;
using Microsoft.VisualStudio.TestPlatform.TestExecutor;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;

namespace UWP.Specs
{
internal sealed partial class App : Application
{
public App()
{
InitializeComponent();
Suspending += OnSuspending;
}

protected override void OnLaunched(LaunchActivatedEventArgs e)
{
if (Window.Current.Content is not Frame rootFrame)
{
rootFrame = new Frame();
rootFrame.NavigationFailed += OnNavigationFailed;
Window.Current.Content = rootFrame;
}

UnitTestClient.Run(e.Arguments);
}

private void OnNavigationFailed(object sender, NavigationFailedEventArgs e) =>
throw new Exception("Failed to load Page " + e.SourcePageType.FullName);

private void OnSuspending(object sender, SuspendingEventArgs e) =>
e.SuspendingOperation.GetDeferral().Complete();
}
}
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Tests/UWP.Specs/Assets/SplashScreen.scale-200.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Tests/UWP.Specs/Assets/StoreLogo.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 25 additions & 0 deletions Tests/UWP.Specs/Package.appxmanifest
@@ -0,0 +1,25 @@
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" IgnorableNamespaces="uap mp">
<Identity Name="17e912aa-d754-4239-acf6-e6863cff80b9" Publisher="CN=FluentAssertions" Version="1.0.0.0" />
<mp:PhoneIdentity PhoneProductId="17e912aa-d754-4239-acf6-e6863cff80b9" PhonePublisherId="00000000-0000-0000-0000-000000000000" />
<Properties>
<DisplayName>UWP.Specs</DisplayName>
<PublisherDisplayName>FluentAssertions</PublisherDisplayName>
<Logo>Assets\StoreLogo.png</Logo>
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0" />
</Dependencies>
<Resources>
<Resource Language="x-generate" />
</Resources>
<Applications>
<Application Id="vstest.executionengine.universal.App" Executable="$targetnametoken$.exe" EntryPoint="UWP.Specs.App">
<uap:VisualElements DisplayName="UWP.Specs" Square150x150Logo="Assets\Square150x150Logo.png" Square44x44Logo="Assets\Square44x44Logo.png" Description="UWP.Specs" BackgroundColor="transparent">
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png" />
<uap:SplashScreen Image="Assets\SplashScreen.png" />
</uap:VisualElements>
</Application>
</Applications>
<Capabilities>
</Capabilities>
</Package>
17 changes: 17 additions & 0 deletions Tests/UWP.Specs/Properties/AssemblyInfo.cs
@@ -0,0 +1,17 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

[assembly: AssemblyTitle("UWP.Specs")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("UWP.Specs")]
[assembly: AssemblyCopyright("Copyright © 2022")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: AssemblyMetadata("TargetPlatform","UAP")]

[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: ComVisible(false)]
5 changes: 5 additions & 0 deletions Tests/UWP.Specs/Properties/Default.rd.xml
@@ -0,0 +1,5 @@
<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
<Application>
<Assembly Name="*Application*" Dynamic="Required All" />
</Application>
</Directives>
100 changes: 100 additions & 0 deletions Tests/UWP.Specs/UWP.Specs.csproj
@@ -0,0 +1,100 @@
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProjectGuid>{08087654-2C32-4818-95E4-45362373441D}</ProjectGuid>
<OutputType>AppContainerExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>UWP.Specs</RootNamespace>
<AssemblyName>UWP.Specs</AssemblyName>
<LangVersion>9.0</LangVersion>
<DefaultLanguage>en-US</DefaultLanguage>
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
<TargetPlatformVersion>10.0.19041.0</TargetPlatformVersion>
<TargetPlatformMinVersion>10.0.18362.0</TargetPlatformMinVersion>
<MinimumVisualStudioVersion>14</MinimumVisualStudioVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<UnitTestPlatformVersion Condition="'$(UnitTestPlatformVersion)' == ''">$(VisualStudioVersion)</UnitTestPlatformVersion>
<AppxPackageSigningEnabled>false</AppxPackageSigningEnabled>
<NoWarn>$(NoWarn);2008</NoWarn>
<UseDotNetNativeToolchain>true</UseDotNetNativeToolchain>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE;NETFX_CORE;WINDOWS_UWP</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup>
<ItemGroup>
<SDKReference Include="TestPlatform.Universal, Version=$(UnitTestPlatformVersion)" />
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
</Compile>
<Compile Include="UwpSpecs.cs" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="App.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
</ItemGroup>
<ItemGroup>
<AppxManifest Include="Package.appxmanifest">
<SubType>Designer</SubType>
</AppxManifest>
</ItemGroup>
<ItemGroup>
<Content Include="Properties\Default.rd.xml" />
<Content Include="Assets\LockScreenLogo.scale-200.png" />
<Content Include="Assets\SplashScreen.scale-200.png" />
<Content Include="Assets\Square150x150Logo.scale-200.png" />
<Content Include="Assets\Square44x44Logo.scale-200.png" />
<Content Include="Assets\Square44x44Logo.targetsize-24_altform-unplated.png" />
<Content Include="Assets\StoreLogo.png" />
<Content Include="Assets\Wide310x150Logo.scale-200.png" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Src\FluentAssertions\FluentAssertions.csproj">
<HintPath>Src\FluentAssertions\bin\$(Configuration)\netstandard2.0\FluentAssertions.dll</HintPath>
<Project>{34E3713D-C02F-4868-BBE7-47DAD2C7F03D}</Project>
<Name>FluentAssertions</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NETCore.UniversalWindowsPlatform">
<Version>6.2.13</Version>
</PackageReference>
<PackageReference Include="MSTest.TestAdapter">
<Version>2.2.8</Version>
</PackageReference>
<PackageReference Include="MSTest.TestFramework">
<Version>2.2.8</Version>
</PackageReference>
<PackageReference Include="System.Xml.XPath.XmlDocument">
<Version>4.3.0</Version>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
</Project>
23 changes: 23 additions & 0 deletions Tests/UWP.Specs/UwpSpecs.cs
@@ -0,0 +1,23 @@
using System;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace UWP.Specs
{
[TestClass]
public class UwpSpecs
{
[TestMethod]
public void Determining_caller_identity_should_not_throw_for_native_programs()
{
// Arrange
Action someAction = () => throw new Exception();

// Act
Action act = () => someAction.Should().NotThrow();

// Assert
act.Should().Throw<AssertFailedException>();
}
}
}
1 change: 1 addition & 0 deletions docs/_pages/releases.md
Expand Up @@ -18,6 +18,7 @@ sidebar:
* `ContainItemsAssignableTo` now expects at least one item assignable to `T` - [#1765](https://github.com/fluentassertions/fluentassertions/pull/1765)
* Querying methods on classes, e.g. `typeof(MyController).Methods()`, now also includes static methods - [#1740](https://github.com/fluentassertions/fluentassertions/pull/1740)
* Variable name is not captured after await assertion - [#1770](https://github.com/fluentassertions/fluentassertions/pull/1770)
* Avoid a `NullReferenceException` when testing an application compiled with .NET Native - [#1776](https://github.com/fluentassertions/fluentassertions/pull/1776)

## 6.3.0

Expand Down

0 comments on commit 287f4b5

Please sign in to comment.