Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/corefx.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/building/code-coverage.md6
-rw-r--r--Documentation/building/cross-building.md13
-rw-r--r--Tools-Override/publishtest.targets4
-rw-r--r--Tools-Override/tests.targets26
-rw-r--r--buildpipeline/pipeline.json43
-rw-r--r--dependencies.props18
-rw-r--r--dir.props11
-rw-r--r--external/coreclr/project.json.template10
-rw-r--r--external/test-runtime/project.json52
-rw-r--r--netci.groovy2
-rw-r--r--src/Common/src/Interop/Windows/kernel32/Interop.SetFileCompletionNotificationModes.cs23
-rw-r--r--src/Common/src/System/Net/CompletionPortHelper.Uap.cs19
-rw-r--r--src/Common/src/System/Net/CompletionPortHelper.Windows.cs31
-rw-r--r--src/Common/src/System/Net/SafeCloseSocket.Windows.cs27
-rw-r--r--src/Common/tests/System/Collections/DelegateEqualityComparer.cs36
-rw-r--r--src/System.CodeDom/ref/System.CodeDom.cs2
-rw-r--r--src/System.CodeDom/ref/System.CodeDom.csproj1
-rw-r--r--src/System.CodeDom/src/System/CodeDom/Compiler/CodeDomProvider.cs2
-rw-r--r--src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.cs2
-rw-r--r--src/System.Collections.Immutable/tests/ImmutableArrayTest.cs2478
-rw-r--r--src/System.Collections.Immutable/tests/System.Collections.Immutable.Tests.csproj3
-rw-r--r--src/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceEventSource.cs4
-rw-r--r--src/System.Diagnostics.DiagnosticSource/tests/DiagnosticSourceEventSourceBridgeTests.cs16
-rw-r--r--src/System.Net.NetworkInformation/src/System.Net.NetworkInformation.csproj6
-rw-r--r--src/System.Net.Sockets/src/System.Net.Sockets.csproj9
-rw-r--r--src/System.Net.Sockets/src/System/Net/Sockets/BaseOverlappedAsyncResult.Unix.cs8
-rw-r--r--src/System.Net.Sockets/src/System/Net/Sockets/BaseOverlappedAsyncResult.Windows.cs74
-rw-r--r--src/System.Net.Sockets/src/System/Net/Sockets/BaseOverlappedAsyncResult.cs25
-rw-r--r--src/System.Net.Sockets/src/System/Net/Sockets/Socket.Windows.cs17
-rw-r--r--src/System.Net.Sockets/src/System/Net/Sockets/Socket.cs367
-rw-r--r--src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs167
-rw-r--r--src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Unix.cs135
-rw-r--r--src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs133
-rw-r--r--src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.cs28
-rw-r--r--src/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Unix.cs88
-rw-r--r--src/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs343
-rw-r--r--src/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFromAsync.cs12
-rw-r--r--src/System.Net.Sockets/tests/FunctionalTests/Shutdown.cs19
-rw-r--r--src/System.Net.Sockets/tests/FunctionalTests/UnixDomainSocketTest.cs8
-rw-r--r--src/System.Threading.Tasks.Extensions/System.Threading.Tasks.Extensions.sln22
-rw-r--r--src/System.Threading.Tasks.Extensions/src/System.Threading.Tasks.Extensions.csproj4
-rw-r--r--src/System.Threading.Tasks.Extensions/tests/System.Threading.Tasks.Extensions.Tests.csproj4
-rw-r--r--src/System.Threading.Thread/tests/ThreadTests.netstandard.cs30
43 files changed, 2657 insertions, 1671 deletions
diff --git a/Documentation/building/code-coverage.md b/Documentation/building/code-coverage.md
index 35c2a98a5e..2d27ad0ba9 100644
--- a/Documentation/building/code-coverage.md
+++ b/Documentation/building/code-coverage.md
@@ -35,10 +35,12 @@ Code coverage runs are performed by Jenkins approximately twice a day. The resu
You can perform code coverage runs locally on your own machine. Normally to build your entire CoreFX repo, from the root of your repo you'd run:
build
+ build-tests
-To include code coverage in this run, augment it with the `coverage` argument:
+To include code coverage in this run, augment the `build-tests` call with the `coverage` argument:
- build -coverage
+ build
+ build-tests -coverage
This will do the build and testing as with the normal ```build```, but it will run the tests using the OpenCover tool. A resulting index.htm file providing the results of the run will be available at:
diff --git a/Documentation/building/cross-building.md b/Documentation/building/cross-building.md
index 7431c26a62..cc35b1b6aa 100644
--- a/Documentation/building/cross-building.md
+++ b/Documentation/building/cross-building.md
@@ -1,7 +1,7 @@
Cross Compilation for ARM on Linux
==================================
-It is possible to build CoreFx on Linux for arm, arm-softft, or arm64 by cross compiling.
+It is possible to build CoreFx on Linux for arm, armel, or arm64 by cross compiling.
It is very similar to the cross compilation procedure of CoreCLR.
Requirements
@@ -15,7 +15,7 @@ In addition, to cross compile CoreFX, the binutils for the target are required.
lgs@ubuntu ~/git/corefx/ $ sudo apt-get install binutils-arm-linux-gnueabihf
-for arm-softfp:
+for armel:
lgs@ubuntu ~/git/corefx/ $ sudo apt-get install binutils-arm-linux-gnueabi
@@ -28,8 +28,9 @@ Generating the rootfs
---------------------
The `cross\build-rootfs.sh` script can be used to download the files needed for cross compilation. It will generate an Ubuntu 14.04 rootfs as this is what CoreFX targets.
- Usage: build-rootfs.sh [BuildArch]
- BuildArch can be: arm, arm-softfp, arm64
+ Usage: ./cross/build-rootfs.sh [BuildArch] [UbuntuCodeName]
+ BuildArch can be: arm, armel, arm64, x86
+ UbuntuCodeName - optional, Code name for Ubuntu, can be: trusty(default), vivid, wily, xenial. If BuildArch is armel, jessie(default) or tizen.
The `build-rootfs.sh` script must be run as root, as it has to make some symlinks to the system. It will, by default, generate the rootfs in `cross\rootfs\<BuildArch>` however this can be changed by setting the `ROOTFS_DIR` environment variable.
@@ -73,7 +74,7 @@ As usual the generated binaries will be found in `bin/BuildOS.BuildArch.BuildTyp
Compiling for managed CoreFX
============================
-The managed components of CoreFX are architecture-independent and thus do not require a special build for arm, arm-softfp or arm64.
+The managed components of CoreFX are architecture-independent and thus do not require a special build for arm, armel or arm64.
Many of the managed binaries are also OS-independent, e.g. System.Linq.dll, while some are OS-specific, e.g. System.IO.FileSystem.dll, with different builds for Windows and Linux.
@@ -101,4 +102,4 @@ prajwal@ubuntu ~/corefx $ ./scripts/arm32_ci_script.sh \
--buildConfig=Release
```
-The Linux ARM Emulator is based on the soft floating point and thus the native binaries are generated for the arm-softfp architecture. The corefx binaries generated by the above command can be found at `~/corefx/bin/Linux.arm-softfp.Release`, `~/corefx/bin/Linux.AnyCPU.Release`, `~/corefx/bin/Unix.AnyCPU.Release`, and `~/corefx/bin/AnyOS.AnyCPU.Release`.
+The Linux ARM Emulator is based on the soft floating point and thus the native binaries are generated for the armel architecture. The corefx binaries generated by the above command can be found at `~/corefx/bin/Linux.armel.Release`, `~/corefx/bin/Linux.AnyCPU.Release`, `~/corefx/bin/Unix.AnyCPU.Release`, and `~/corefx/bin/AnyOS.AnyCPU.Release`.
diff --git a/Tools-Override/publishtest.targets b/Tools-Override/publishtest.targets
index 4dcec00c82..249d8a9002 100644
--- a/Tools-Override/publishtest.targets
+++ b/Tools-Override/publishtest.targets
@@ -15,7 +15,7 @@
Temporary until we have proper nuget support to deploy content files.
Copies supplemental test data to the build output and test directories.
-->
- <Target Name="CopySupplementalTestData">
+ <Target Name="CopySupplementalTestData" DependsOnTargets="DiscoverTestInputs">
<!-- coalesce supplemental test data items with and without DestinationDir metadata -->
<ItemGroup>
<_SupplementalTestData Include="@(SupplementalTestData)" Condition="'%(DestinationDir)' != ''">
@@ -59,7 +59,7 @@
<PropertyGroup Condition="'$(BuildingInsideVisualStudio)'=='true'">
<PrepareForRunDependsOn>$(PrepareForRunDependsOn);CopyDefaultTestAssetsForVS</PrepareForRunDependsOn>
</PropertyGroup>
- <Target Name="CopyDefaultTestAssetsForVS" DependsOnTargets="CopyTestToTestDirectory;CopySupplementalTestData">
+ <Target Name="CopyDefaultTestAssetsForVS" DependsOnTargets="DiscoverTestInputs;CopySupplementalTestData">
<!-- This was copied from RunTestsForProject in tests.targets
The RunTestsForProject target does not execute in VS context and would be confused by the script based runner.
_TestCopyLocalByFileNameWithoutDuplicates are the precise items that are fed to the runner script generation code.
diff --git a/Tools-Override/tests.targets b/Tools-Override/tests.targets
index e5482e16cb..8c4c99b259 100644
--- a/Tools-Override/tests.targets
+++ b/Tools-Override/tests.targets
@@ -31,6 +31,7 @@
<TestHostExecutablePath Condition="'$(OS)'=='Windows_NT' AND '$(TestHostExecutablePath)' == ''">$(TestSharedFxDir)\dotnet.exe</TestHostExecutablePath>
<TestHostExecutablePath Condition="'$(OS)'!='Windows_NT' AND '$(TestHostExecutablePath)' == ''">$(TestSharedFxDir)\dotnet</TestHostExecutablePath>
<XunitExecutable Condition="'$(XunitExecutable)' == ''">$(TestPath)\xunit.console.netcore.exe</XunitExecutable>
+ <DebugEngines>{2E36F1D4-B23C-435D-AB41-18E608940038}</DebugEngines>
</PropertyGroup>
<!-- General xunit options -->
@@ -60,15 +61,16 @@
<!-- The Code Coverage targets will override TestHost and TestCommandLine if coverage is enabled -->
<Import Project="$(MSBuildThisFileDirectory)CodeCoverage.targets" />
-
- <Target Name="AddRuntimeConfig" >
- <ItemGroup>
- <SupplementalTestData Include="$(XunitRuntimeConfig)" />
- <SupplementalTestData Include="$(RuntimePath)xunit.console.netcore.exe" />
- </ItemGroup>
- </Target>
- <Target Name="DiscoverTestInputs" DependsOnTargets="ResolveReferences;GetCopyToOutputDirectoryItems;AddRuntimeConfig">
+ <!-- In VS (2015 Preview or later currently required): Debug to run unit tests on CoreCLR. -->
+ <PropertyGroup Condition="'$(IsTestProject)'=='true'">
+ <StartWorkingDirectory Condition="'$(StartWorkingDirectory)'==''">$(TestPath)</StartWorkingDirectory>
+ <StartAction Condition="'$(StartAction)'==''">Program</StartAction>
+ <StartProgram Condition="'$(StartProgram)'==''">$(TestProgram)</StartProgram>
+ <StartArguments Condition="'$(StartArguments)'==''">$(TestArguments) -wait -parallel none</StartArguments>
+ </PropertyGroup>
+
+ <Target Name="DiscoverTestInputs" DependsOnTargets="ResolveReferences;GetCopyToOutputDirectoryItems">
<ItemGroup>
<RunTestsForProjectInputs Include="@(ReferenceCopyLocalPaths)" />
<RunTestsForProjectInputs Include="@(Content)" />
@@ -76,6 +78,10 @@
<RunTestsForProjectInputs Include="@(_DebugSymbolsIntermediatePath)" />
<RunTestsForProjectInputs Include="@(AllItemsFullPathWithTargetPath)" />
</ItemGroup>
+ <ItemGroup>
+ <SupplementalTestData Include="$(XunitRuntimeConfig)" />
+ <SupplementalTestData Include="$(RuntimePath)xunit.console.netcore.exe" />
+ </ItemGroup>
</Target>
<!-- Generate the script to run the tests. The script performs two high-level steps:
@@ -84,7 +90,7 @@
directory.
2. Runs the tests. -->
<Target Name="GenerateTestExecutionScripts"
- DependsOnTargets="DiscoverTestInputs;CheckTestCategories">
+ DependsOnTargets="DiscoverTestInputs;SetupTestProperties">
<PropertyGroup>
<TargetOSTrait Condition="'$(TargetOS)'=='Windows_NT'">nonwindowstests</TargetOSTrait>
<TargetOSTrait Condition="'$(TargetOS)'=='Linux'">nonlinuxtests</TargetOSTrait>
@@ -239,8 +245,6 @@
<PropertyGroup>
<TestDependsOn>
$(TestDependsOn);
- DiscoverTestInputs;
- SetupTestProperties;
CopySupplementalTestData;
GenerateTestExecutionScripts;
RunTestsForProject;
diff --git a/buildpipeline/pipeline.json b/buildpipeline/pipeline.json
index 3ae7eefac1..e571c1717c 100644
--- a/buildpipeline/pipeline.json
+++ b/buildpipeline/pipeline.json
@@ -18,18 +18,6 @@
{
"Name": "DotNet-CoreFx-Trusted-Linux",
"Parameters": {
- "PB_DockerTag": "ubuntu1404_prereqs_v3",
- "PB_AdditionalArguments": "-portableLinux"
- },
- "ReportingParameters": {
- "OperatingSystem": "Ubuntu 14.04",
- "Type": "build/product/",
- "ConfigurationGroup": "Release"
- }
- },
- {
- "Name": "DotNet-CoreFx-Trusted-Linux",
- "Parameters": {
"PB_DockerTag": "debian82_prereqs_2"
},
"ReportingParameters": {
@@ -96,12 +84,26 @@
{
"Name": "DotNet-CoreFx-Trusted-Linux",
"Parameters": {
+ "PB_DockerTag": "ubuntu1404_prereqs_v3",
+ "PB_AdditionalArguments": "-portableLinux"
+ },
+ "ReportingParameters": {
+ "OperatingSystem": "Ubuntu 14.04",
+ "Type": "build/product/",
+ "ConfigurationGroup": "Release",
+ "SubType": "PortableLinux"
+ }
+ },
+ {
+ "Name": "DotNet-CoreFx-Trusted-Linux",
+ "Parameters": {
"PB_DockerTag": "ubuntu1404_prereqs_v3"
},
"ReportingParameters": {
"OperatingSystem": "Ubuntu 14.04",
"Type": "build/product/",
- "ConfigurationGroup": "Release"
+ "ConfigurationGroup": "Release",
+ "SubType": "Native"
}
},
{
@@ -278,7 +280,20 @@
"ReportingParameters": {
"OperatingSystem": "Ubuntu 14.04",
"Type": "build/product/",
- "ConfigurationGroup": "Debug"
+ "ConfigurationGroup": "Debug",
+ "SubType": "Native"
+ }
+ },
+ {
+ "Name": "DotNet-CoreFx-Trusted-Linux",
+ "Parameters": {
+ "PB_DockerTag": "ubuntu1404_prereqs_v3"
+ },
+ "ReportingParameters": {
+ "OperatingSystem": "Ubuntu 14.04",
+ "Type": "build/product/",
+ "ConfigurationGroup": "Debug",
+ "SubType": "PortableLinux"
}
},
{
diff --git a/dependencies.props b/dependencies.props
index 5258dcbc7c..9e8507a5ef 100644
--- a/dependencies.props
+++ b/dependencies.props
@@ -17,18 +17,18 @@
These ref versions are pulled from https://github.com/dotnet/versions.
-->
<PropertyGroup>
- <CoreFxCurrentRef>0e4d68ff48a8be0a0bc9aed6d18d71196a1640bb</CoreFxCurrentRef>
- <CoreClrCurrentRef>0e4d68ff48a8be0a0bc9aed6d18d71196a1640bb</CoreClrCurrentRef>
+ <CoreFxCurrentRef>4866f6c9fc81dc6b5b3c56177c6769436d906b93</CoreFxCurrentRef>
+ <CoreClrCurrentRef>3ae2fce20a0a2c098ad787baf1353034d1d0e393</CoreClrCurrentRef>
<ExternalCurrentRef>4e2952b5114bfa90cc45eef908204006a771a62b</ExternalCurrentRef>
- <ProjectNTfsCurrentRef>1724f6ecb49edd2ba33bb1d478c6f146369d10e6</ProjectNTfsCurrentRef>
+ <ProjectNTfsCurrentRef>3ae2fce20a0a2c098ad787baf1353034d1d0e393</ProjectNTfsCurrentRef>
</PropertyGroup>
<!-- Auto-upgraded properties for each build info dependency. -->
<PropertyGroup>
- <CoreFxExpectedPrerelease>beta-24914-13</CoreFxExpectedPrerelease>
- <CoreClrExpectedPrerelease>beta-24915-03</CoreClrExpectedPrerelease>
+ <CoreFxExpectedPrerelease>beta-24917-02</CoreFxExpectedPrerelease>
+ <CoreClrExpectedPrerelease>beta-24917-01</CoreClrExpectedPrerelease>
<ExternalExpectedPrerelease>beta-24727-00</ExternalExpectedPrerelease>
- <ProjectNTfsExpectedPrerelease>beta-24913-00</ProjectNTfsExpectedPrerelease>
+ <ProjectNTfsExpectedPrerelease>beta-24917-00</ProjectNTfsExpectedPrerelease>
</PropertyGroup>
<!-- Full package version strings that are used in other parts of the build. -->
@@ -110,7 +110,7 @@
<StaticDependency Include="@(XUnitDependency)">
<Version>2.2.0-beta2-build3300</Version>
</StaticDependency>
-
+
<StaticDependency Include="Microsoft.xunit.netcore.extensions;Microsoft.DotNet.BuildTools.TestSuite">
<Version>1.0.1-prerelease-01001-04</Version>
</StaticDependency>
@@ -134,5 +134,9 @@
<Version>$(AppXRunnerVersion)</Version>
</DependencyBuildInfo>
+ <!-- project.json files to update -->
+ <ProjectJsonFiles Include="external\**\project.json" />
+ <ProjectJsonFiles Include="external\**\project.json.template" />
+
</ItemGroup>
</Project>
diff --git a/dir.props b/dir.props
index 789d1f9a31..e17443de02 100644
--- a/dir.props
+++ b/dir.props
@@ -135,6 +135,12 @@
<RunApiCompat>true</RunApiCompat>
</PropertyGroup>
+ <PropertyGroup Condition="'$(BuildingInsideVisualStudio)' == 'true' and '$(Configuration)' != ''">
+ <!-- When building in VS setup the ConfigurationGroup based on the given Configuration -->
+ <ConfigurationGroup Condition="$(Configuration.EndsWith('Debug'))">Debug</ConfigurationGroup>
+ <ConfigurationGroup Condition="$(Configuration.EndsWith('Release'))">Release</ConfigurationGroup>
+ </PropertyGroup>
+
<PropertyGroup>
<!--
These *Group properties are only intended to enable passing them individually at the command line to initialize
@@ -148,9 +154,6 @@
<ConfigurationGroup Condition="'$(ConfigurationGroup)' == ''">Debug</ConfigurationGroup>
<ArchGroup Condition="'$(ArchGroup)' == ''">x64</ArchGroup>
- <!-- For VS support if Configuration is set we default BuildConfiguration based on that property -->
- <BuildConfiguration Condition="'$(BuildConfiguration)' == '' and '$(Configuration)' != ''">$(Configuration)-$(ArchGroup)</BuildConfiguration>
-
<!-- Initialize BuildConfiguration from the individual properties if it wasn't already explicitly set -->
<BuildConfiguration Condition="'$(BuildConfiguration)' == ''">$(TargetGroup)-$(OSGroup)-$(ConfigurationGroup)-$(ArchGroup)</BuildConfiguration>
<BuildConfigurationImportFile>$(ToolsDir)/configuration/configuration.props</BuildConfigurationImportFile>
@@ -249,7 +252,7 @@
<RefPath>$(RefRootPath)netcoreapp/</RefPath>
<NetStandardRefPath>$(RefRootPath)netstandard/</NetStandardRefPath>
<NetFxRefPath>$(RefRootPath)netfx/</NetFxRefPath>
-
+
<!-- Constructed shared fx path for testing -->
<TestSharedFxDir>$(ToolsDir)testdotnetcli</TestSharedFxDir>
diff --git a/external/coreclr/project.json.template b/external/coreclr/project.json.template
index efed6b8526..bae9ceb322 100644
--- a/external/coreclr/project.json.template
+++ b/external/coreclr/project.json.template
@@ -2,16 +2,16 @@
"frameworks": {
"{TFM}": {
"dependencies": {
- "Microsoft.NETCore.Platforms": "1.2.0-beta-24721-02",
- "Microsoft.NETCore.Runtime.CoreCLR": "1.2.0-beta-24904-03",
- "Microsoft.NETCore.TestHost": "1.2.0-beta-24904-03",
- "runtime.native.System.Data.SqlClient.sni": "4.4.0-beta-24814-01",
+ "Microsoft.NETCore.Platforms": "2.0.0-beta-24916-02",
+ "Microsoft.NETCore.Runtime.CoreCLR": "1.2.0-beta-24916-02",
+ "Microsoft.NETCore.TestHost": "1.2.0-beta-24916-02",
+ "runtime.native.System.Data.SqlClient.sni": "4.4.0-beta-24913-02",
"Microsoft.NETCore.DotNetHost": "1.2.0-beta-001259-00",
"Microsoft.NETCore.DotNetHostPolicy": "1.2.0-beta-001259-00"
}
}
},
"runtimes": {
- "{RID}": { },
+ "{RID}": {}
}
}
diff --git a/external/test-runtime/project.json b/external/test-runtime/project.json
index e4ab7c7b74..87cbc18f94 100644
--- a/external/test-runtime/project.json
+++ b/external/test-runtime/project.json
@@ -1,29 +1,27 @@
{
- "dependencies": {
- "xunit.console.netcore": "1.0.3-prerelease-00921-01",
- "Microsoft.DotNet.BuildTools.TestSuite": "1.0.1-prerelease-01001-04",
- "Microsoft.xunit.netcore.extensions": "1.0.1-prerelease-01001-04",
- "Microsoft.DotNet.xunit.performance.analysis": "1.0.0-alpha-build0040",
- "Microsoft.DotNet.xunit.performance.analysis.cli": "1.0.0-alpha-build0040",
- "Microsoft.DotNet.xunit.performance.runner.cli": "1.0.0-alpha-build0040",
- "Microsoft.DotNet.xunit.performance.runner.Windows": "1.0.0-alpha-build0040",
- "Microsoft.NETCore.Runtime.CoreCLR": "1.2.0-beta-24728-02",
- "Microsoft.NETCore.TestHost": "1.2.0-beta-24728-02",
- "Newtonsoft.Json": "9.0.1",
- "coveralls.io": "1.4",
- "OpenCover": "4.6.519",
- "ReportGenerator": "2.5.0",
-
- // Supplemental test data
- "System.IO.Compression.TestData": "1.0.4-prerelease",
- "System.IO.Packaging.TestData": "1.0.0-prerelease",
- "System.Security.Cryptography.X509Certificates.TestData": "1.0.2-prerelease",
- "System.Net.TestData": "1.0.0-prerelease",
- },
- "frameworks": {
- "netstandard2.0": {}
- },
- "runtimes": {
- "win7-x64": {}
- }
+ "dependencies": {
+ "xunit.console.netcore": "1.0.3-prerelease-00921-01",
+ "Microsoft.DotNet.BuildTools.TestSuite": "1.0.1-prerelease-01001-04",
+ "Microsoft.xunit.netcore.extensions": "1.0.1-prerelease-01001-04",
+ "Microsoft.DotNet.xunit.performance.analysis": "1.0.0-alpha-build0040",
+ "Microsoft.DotNet.xunit.performance.analysis.cli": "1.0.0-alpha-build0040",
+ "Microsoft.DotNet.xunit.performance.runner.cli": "1.0.0-alpha-build0040",
+ "Microsoft.DotNet.xunit.performance.runner.Windows": "1.0.0-alpha-build0040",
+ "Microsoft.NETCore.Runtime.CoreCLR": "1.2.0-beta-24916-02",
+ "Microsoft.NETCore.TestHost": "1.2.0-beta-24916-02",
+ "Newtonsoft.Json": "9.0.1",
+ "coveralls.io": "1.4",
+ "OpenCover": "4.6.519",
+ "ReportGenerator": "2.5.0",
+ "System.IO.Compression.TestData": "1.0.4-prerelease",
+ "System.IO.Packaging.TestData": "1.0.0-prerelease",
+ "System.Security.Cryptography.X509Certificates.TestData": "1.0.2-prerelease",
+ "System.Net.TestData": "1.0.0-prerelease"
+ },
+ "frameworks": {
+ "netstandard2.0": {}
+ },
+ "runtimes": {
+ "win7-x64": {}
+ }
}
diff --git a/netci.groovy b/netci.groovy
index 32dd488221..05b55f81a2 100644
--- a/netci.groovy
+++ b/netci.groovy
@@ -55,7 +55,7 @@ def buildArchConfiguration = ['Debug': 'x86',
def isLocal = (localType == 'local')
def newJobName = 'code_coverage_windows'
- def batchCommand = 'call build.cmd -coverage -outerloop -- /p:WithoutCategories=IgnoreForCI'
+ def batchCommand = 'call build.cmd && call build-tests.cmd -coverage -outerloop -- /p:WithoutCategories=IgnoreForCI'
if (isLocal) {
newJobName = "${newJobName}_local"
batchCommand = "${batchCommand}"
diff --git a/src/Common/src/Interop/Windows/kernel32/Interop.SetFileCompletionNotificationModes.cs b/src/Common/src/Interop/Windows/kernel32/Interop.SetFileCompletionNotificationModes.cs
new file mode 100644
index 0000000000..671800b7d2
--- /dev/null
+++ b/src/Common/src/Interop/Windows/kernel32/Interop.SetFileCompletionNotificationModes.cs
@@ -0,0 +1,23 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Runtime.InteropServices;
+
+internal static partial class Interop
+{
+ internal static partial class Kernel32
+ {
+ [Flags]
+ internal enum FileCompletionNotificationModes : byte
+ {
+ None = 0,
+ SkipCompletionPortOnSuccess = 1,
+ SkipSetEventOnHandle = 2
+ }
+
+ [DllImport(Libraries.Kernel32, SetLastError = true)]
+ internal static unsafe extern bool SetFileCompletionNotificationModes(SafeHandle handle, FileCompletionNotificationModes flags);
+ }
+}
diff --git a/src/Common/src/System/Net/CompletionPortHelper.Uap.cs b/src/Common/src/System/Net/CompletionPortHelper.Uap.cs
new file mode 100644
index 0000000000..aac501ccb3
--- /dev/null
+++ b/src/Common/src/System/Net/CompletionPortHelper.Uap.cs
@@ -0,0 +1,19 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Runtime.InteropServices;
+
+namespace System.Net.Sockets
+{
+ internal static class CompletionPortHelper
+ {
+ internal static bool SkipCompletionPortOnSuccess(SafeHandle handle)
+ {
+ // SetFileCompletionNotificationModes is not supported on UAP.
+ return false;
+ }
+
+ internal static readonly bool PlatformHasUdpIssue = false;
+ }
+}
diff --git a/src/Common/src/System/Net/CompletionPortHelper.Windows.cs b/src/Common/src/System/Net/CompletionPortHelper.Windows.cs
new file mode 100644
index 0000000000..1a4ea844ff
--- /dev/null
+++ b/src/Common/src/System/Net/CompletionPortHelper.Windows.cs
@@ -0,0 +1,31 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Runtime.InteropServices;
+
+namespace System.Net.Sockets
+{
+ internal static class CompletionPortHelper
+ {
+ internal static bool SkipCompletionPortOnSuccess(SafeHandle handle)
+ {
+ return Interop.Kernel32.SetFileCompletionNotificationModes(handle,
+ Interop.Kernel32.FileCompletionNotificationModes.SkipCompletionPortOnSuccess |
+ Interop.Kernel32.FileCompletionNotificationModes.SkipSetEventOnHandle);
+ }
+
+ // There's a bug with using SetFileCompletionNotificationModes with UDP on Windows 7 and before.
+ // This check tells us if the problem exists on the platform we're running on.
+ internal static readonly bool PlatformHasUdpIssue = CheckIfPlatformHasUdpIssue();
+
+ private static bool CheckIfPlatformHasUdpIssue()
+ {
+ Version osVersion = Environment.OSVersion.Version;
+
+ // 6.1 == Windows 7
+ return (osVersion.Major < 6 ||
+ (osVersion.Major == 6 && osVersion.Minor <= 1));
+ }
+ }
+}
diff --git a/src/Common/src/System/Net/SafeCloseSocket.Windows.cs b/src/Common/src/System/Net/SafeCloseSocket.Windows.cs
index f90789fc19..4f4615ecab 100644
--- a/src/Common/src/System/Net/SafeCloseSocket.Windows.cs
+++ b/src/Common/src/System/Net/SafeCloseSocket.Windows.cs
@@ -3,9 +3,7 @@
// See the LICENSE file in the project root for more information.
using Microsoft.Win32.SafeHandles;
-
using System.Diagnostics;
-using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
@@ -19,6 +17,7 @@ namespace System.Net.Sockets
#endif
{
private ThreadPoolBoundHandle _iocpBoundHandle;
+ private bool _skipCompletionPortOnSuccess;
private object _iocpBindingLock = new object();
public ThreadPoolBoundHandle IOCPBoundHandle
@@ -30,7 +29,7 @@ namespace System.Net.Sockets
}
// Binds the Socket Win32 Handle to the ThreadPool's CompletionPort.
- public ThreadPoolBoundHandle GetOrAllocateThreadPoolBoundHandle()
+ public ThreadPoolBoundHandle GetOrAllocateThreadPoolBoundHandle(bool trySkipCompletionPortOnSuccess)
{
if (_released)
{
@@ -49,12 +48,13 @@ namespace System.Net.Sockets
// Bind the socket native _handle to the ThreadPool.
if (NetEventSource.IsEnabled) NetEventSource.Info(this, "calling ThreadPool.BindHandle()");
+ ThreadPoolBoundHandle boundHandle;
try
{
// The handle (this) may have been already released:
// E.g.: The socket has been disposed in the main thread. A completion callback may
// attempt starting another operation.
- _iocpBoundHandle = ThreadPoolBoundHandle.BindHandle(this);
+ boundHandle = ThreadPoolBoundHandle.BindHandle(this);
}
catch (Exception exception)
{
@@ -62,6 +62,16 @@ namespace System.Net.Sockets
CloseAsIs();
throw;
}
+
+ // Try to disable completions for synchronous success, if requested
+ if (trySkipCompletionPortOnSuccess &&
+ CompletionPortHelper.SkipCompletionPortOnSuccess(boundHandle.Handle))
+ {
+ _skipCompletionPortOnSuccess = true;
+ }
+
+ // Don't set this until after we've configured the handle above (if we did)
+ _iocpBoundHandle = boundHandle;
}
}
}
@@ -69,6 +79,15 @@ namespace System.Net.Sockets
return _iocpBoundHandle;
}
+ public bool SkipCompletionPortOnSuccess
+ {
+ get
+ {
+ Debug.Assert(_iocpBoundHandle != null);
+ return _skipCompletionPortOnSuccess;
+ }
+ }
+
internal static unsafe SafeCloseSocket CreateWSASocket(byte* pinnedBuffer)
{
return CreateSocket(InnerSafeCloseSocket.CreateWSASocket(pinnedBuffer));
diff --git a/src/Common/tests/System/Collections/DelegateEqualityComparer.cs b/src/Common/tests/System/Collections/DelegateEqualityComparer.cs
new file mode 100644
index 0000000000..c22ed45883
--- /dev/null
+++ b/src/Common/tests/System/Collections/DelegateEqualityComparer.cs
@@ -0,0 +1,36 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+
+namespace System.Collections.Tests
+{
+ internal sealed class DelegateEqualityComparer<T> : IEqualityComparer<T>, IEqualityComparer
+ {
+ private readonly Func<T, T, bool> _equals;
+ private readonly Func<T, int> _getHashCode;
+ private readonly Func<object, object, bool> _objectEquals;
+ private readonly Func<object, int> _objectGetHashCode;
+
+ public DelegateEqualityComparer(
+ Func<T, T, bool> equals = null,
+ Func<T, int> getHashCode = null,
+ Func<object, object, bool> objectEquals = null,
+ Func<object, int> objectGetHashCode = null)
+ {
+ _equals = equals ?? ((x, y) => { throw new NotImplementedException(); });
+ _getHashCode = getHashCode ?? (obj => { throw new NotImplementedException(); });
+ _objectEquals = objectEquals ?? ((x, y) => { throw new NotImplementedException(); });
+ _objectGetHashCode = objectGetHashCode ?? (obj => { throw new NotImplementedException(); });
+ }
+
+ public bool Equals(T x, T y) => _equals(x, y);
+
+ public int GetHashCode(T obj) => _getHashCode(obj);
+
+ bool IEqualityComparer.Equals(object x, object y) => _objectEquals(x, y);
+
+ int IEqualityComparer.GetHashCode(object obj) => _objectGetHashCode(obj);
+ }
+}
diff --git a/src/System.CodeDom/ref/System.CodeDom.cs b/src/System.CodeDom/ref/System.CodeDom.cs
index 705b3e78a9..d20512ff00 100644
--- a/src/System.CodeDom/ref/System.CodeDom.cs
+++ b/src/System.CodeDom/ref/System.CodeDom.cs
@@ -876,7 +876,7 @@ namespace System.CodeDom.Compiler
System.CodeDom.Compiler.CompilerResults System.CodeDom.Compiler.ICodeCompiler.CompileAssemblyFromSource(System.CodeDom.Compiler.CompilerParameters options, string source) { throw null; }
System.CodeDom.Compiler.CompilerResults System.CodeDom.Compiler.ICodeCompiler.CompileAssemblyFromSourceBatch(System.CodeDom.Compiler.CompilerParameters options, string[] sources) { throw null; }
}
- public abstract partial class CodeDomProvider
+ public abstract partial class CodeDomProvider : System.ComponentModel.Component
{
protected CodeDomProvider() { }
public virtual string FileExtension { get { throw null; } }
diff --git a/src/System.CodeDom/ref/System.CodeDom.csproj b/src/System.CodeDom/ref/System.CodeDom.csproj
index fabaca9f5a..1e063610ba 100644
--- a/src/System.CodeDom/ref/System.CodeDom.csproj
+++ b/src/System.CodeDom/ref/System.CodeDom.csproj
@@ -14,6 +14,7 @@
<ProjectReference Include="..\..\System.Runtime.Extensions\ref\System.Runtime.Extensions.csproj" />
<ProjectReference Include="..\..\System.Security.Permissions\ref\System.Security.Permissions.csproj" />
<ProjectReference Include="..\..\System.Text.Encoding\ref\System.Text.Encoding.csproj" />
+ <ProjectReference Include="..\..\System.ComponentModel.Primitives\ref\System.ComponentModel.Primitives.csproj" />
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
</Project> \ No newline at end of file
diff --git a/src/System.CodeDom/src/System/CodeDom/Compiler/CodeDomProvider.cs b/src/System.CodeDom/src/System/CodeDom/Compiler/CodeDomProvider.cs
index b1d14061cc..bd266f132a 100644
--- a/src/System.CodeDom/src/System/CodeDom/Compiler/CodeDomProvider.cs
+++ b/src/System.CodeDom/src/System/CodeDom/Compiler/CodeDomProvider.cs
@@ -11,7 +11,7 @@ using System.Runtime.Serialization;
namespace System.CodeDom.Compiler
{
- public abstract class CodeDomProvider // TODO: Inherit Component
+ public abstract class CodeDomProvider : Component
{
private static readonly Dictionary<string, CompilerInfo> s_compilerLanguages = new Dictionary<string, CompilerInfo>(StringComparer.OrdinalIgnoreCase);
private static readonly Dictionary<string, CompilerInfo> s_compilerExtensions = new Dictionary<string, CompilerInfo>(StringComparer.OrdinalIgnoreCase);
diff --git a/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.cs b/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.cs
index f377e9a3ef..6e8ab32088 100644
--- a/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.cs
+++ b/src/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.cs
@@ -170,7 +170,7 @@ namespace System.Collections.Immutable
}
/// <summary>
- /// Gets the number of array in the collection.
+ /// Gets the number of elements in the array.
/// </summary>
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public int Length
diff --git a/src/System.Collections.Immutable/tests/ImmutableArrayTest.cs b/src/System.Collections.Immutable/tests/ImmutableArrayTest.cs
index bdea30f01c..4895474e34 100644
--- a/src/System.Collections.Immutable/tests/ImmutableArrayTest.cs
+++ b/src/System.Collections.Immutable/tests/ImmutableArrayTest.cs
@@ -3,8 +3,11 @@
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
+using System.Collections.Tests;
using System.Diagnostics;
using System.Linq;
+using System.Reflection;
+using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using Xunit;
@@ -20,239 +23,325 @@ namespace System.Collections.Immutable.Tests
private static readonly ImmutableArray<GenericParameterHelper> s_oneElementRefType = ImmutableArray.Create(new GenericParameterHelper(1));
private static readonly ImmutableArray<string> s_twoElementRefTypeWithNull = ImmutableArray.Create("1", null);
- [Fact]
- public void Clear()
+ public static IEnumerable<object[]> Int32EnumerableData()
{
- Assert.Equal(ImmutableArray<int>.Empty, ImmutableArray.Create<int>().Clear());
- Assert.Equal(ImmutableArray<int>.Empty, ImmutableArray.Create<int>(1).Clear());
- Assert.Equal(ImmutableArray<int>.Empty, ImmutableArray.Create<int>(1, 2, 3).Clear());
+ yield return new object[] { new int[0] };
+ yield return new object[] { new[] { 1 } };
+ yield return new object[] { Enumerable.Range(1, 3) };
+ yield return new object[] { Enumerable.Range(4, 4) };
+ yield return new object[] { new[] { 2, 3, 5 } };
}
- [Fact]
- public void CreateEmpty()
+ public static IEnumerable<object[]> StrongBoxedInt32EnumerableData()
{
- Assert.Equal(ImmutableArray<int>.Empty, ImmutableArray.Create<int>());
- Assert.Equal(ImmutableArray<int>.Empty, ImmutableArray.Create<int>(new int[0]));
+ // Once https://github.com/xunit/assert.xunit/pull/5 comes into corefx, all the StrongBox stuff can be removed.
+
+ return Int32EnumerableData()
+ .Select(array => array[0])
+ .Cast<IEnumerable<int>>()
+ .Select(enumerable => new object[]
+ {
+ new StrongBox<IEnumerable<int>>(enumerable)
+ });
}
- [Fact]
- public void CreateFromEnumerable()
+ public static IEnumerable<object[]> StrongBoxedSpecialInt32ImmutableArrayData()
{
- Assert.Throws<ArgumentNullException>("items", () => ImmutableArray.CreateRange((IEnumerable<int>)null));
+ // Once https://github.com/xunit/assert.xunit/pull/5 comes into corefx, all the StrongBox stuff can be removed.
- IEnumerable<int> source = new[] { 1, 2, 3 };
- var array = ImmutableArray.CreateRange(source);
- Assert.Equal(3, array.Length);
+ yield return new object[] { new StrongBox<IEnumerable<int>>(s_emptyDefault) };
+ yield return new object[] { new StrongBox<IEnumerable<int>>(s_empty) };
}
- [Fact]
- public void CreateFromEmptyEnumerableReturnsSingleton()
+ [Theory]
+ [MemberData(nameof(Int32EnumerableData))]
+ public void Clear(IEnumerable<int> source)
{
- IEnumerable<int> emptySource1 = new int[0];
- var immutable = ImmutableArray.CreateRange(emptySource1);
-
- // This equality check returns true if the underlying arrays are the same instance.
- Assert.Equal(s_empty, immutable);
+ Assert.True(s_empty == source.ToImmutableArray().Clear());
}
[Fact]
- public void CreateRangeFromImmutableArrayWithSelector()
+ public void CreateEnumerableElementType()
{
- var array = ImmutableArray.Create(4, 5, 6, 7);
-
- var copy1 = ImmutableArray.CreateRange(array, i => i + 0.5);
- Assert.Equal(new[] { 4.5, 5.5, 6.5, 7.5 }, copy1);
+ // Create should not have the same semantics as CreateRange, except for arrays.
+ // If you pass in an IEnumerable<T> to Create, you should get an
+ // ImmutableArray<IEnumerable<T>>. However, if you pass a T[] in, you should get
+ // a ImmutableArray<T>.
- var copy2 = ImmutableArray.CreateRange(array, i => i + 1);
- Assert.Equal(new[] { 5, 6, 7, 8 }, copy2);
+ var array = new int[0];
+ Assert.IsType<ImmutableArray<int>>(ImmutableArray.Create(array));
- Assert.Equal(new int[] { }, ImmutableArray.CreateRange(s_empty, i => i));
+ var immutable = ImmutableArray<int>.Empty;
+ Assert.IsType<ImmutableArray<ImmutableArray<int>>>(ImmutableArray.Create(immutable));
- Assert.Throws<ArgumentNullException>("selector", () => ImmutableArray.CreateRange(array, (Func<int, int>)null));
+ var enumerable = Enumerable.Empty<int>();
+ Assert.IsType<ImmutableArray<IEnumerable<int>>>(ImmutableArray.Create(enumerable));
}
[Fact]
- public void CreateRangeFromImmutableArrayWithSelectorAndArgument()
+ public void CreateEmpty()
{
- var array = ImmutableArray.Create(4, 5, 6, 7);
-
- var copy1 = ImmutableArray.CreateRange(array, (i, j) => i + j, 0.5);
- Assert.Equal(new[] { 4.5, 5.5, 6.5, 7.5 }, copy1);
-
- var copy2 = ImmutableArray.CreateRange(array, (i, j) => i + j, 1);
- Assert.Equal(new[] { 5, 6, 7, 8 }, copy2);
-
- var copy3 = ImmutableArray.CreateRange(array, (int i, object j) => i, null);
- Assert.Equal(new[] { 4, 5, 6, 7 }, copy3);
+ Assert.True(s_empty == ImmutableArray.Create<int>());
+ Assert.True(s_empty == ImmutableArray.Create(new int[0]));
+ }
- Assert.Equal(new int[] { }, ImmutableArray.CreateRange(s_empty, (i, j) => i + j, 0));
+ [Theory]
+ [MemberData(nameof(Int32EnumerableData))]
+ public void CreateRange(IEnumerable<int> source)
+ {
+ Assert.Equal(source, ImmutableArray.CreateRange(source));
+ }
- Assert.Throws<ArgumentNullException>("selector", () => ImmutableArray.CreateRange(array, (Func<int, int, int>)null, 0));
+ [Fact]
+ public void CreateRangeInvalid()
+ {
+ Assert.Throws<ArgumentNullException>("items", () => ImmutableArray.CreateRange((IEnumerable<int>)null));
}
[Fact]
- public void CreateRangeSliceFromImmutableArrayWithSelector()
+ public void CreateRangeEmptyReturnsSingleton()
{
- var array = ImmutableArray.Create(4, 5, 6, 7);
+ var empty = ImmutableArray.CreateRange(new int[0]);
+ // This equality check returns true if the underlying arrays are the same instance.
+ Assert.True(s_empty == empty);
+ }
- var copy1 = ImmutableArray.CreateRange(array, 0, 0, i => i + 0.5);
- Assert.Equal(new double[] { }, copy1);
+ [Theory]
+ [MemberData(nameof(CreateRangeWithSelectorData))]
+ public void CreateRangeWithSelector<TResult>(IEnumerable<int> source, Func<int, TResult> selector, TResult dummy)
+ {
+ // Remove the dummy parameters once https://github.com/xunit/xunit/pull/965 makes it into corefx.
- var copy2 = ImmutableArray.CreateRange(array, 0, 0, i => i);
- Assert.Equal(new int[] { }, copy2);
+ Assert.Equal(source.Select(selector), ImmutableArray.CreateRange(source.ToImmutableArray(), selector));
+ }
- var copy3 = ImmutableArray.CreateRange(array, 0, 1, i => i * 2);
- Assert.Equal(new int[] { 8 }, copy3);
+ public static IEnumerable<object[]> CreateRangeWithSelectorData()
+ {
+ yield return new object[] { new int[] { }, new Func<int, int>(i => i), 0 };
+ yield return new object[] { new[] { 4, 5, 6, 7 }, new Func<int, float>(i => i + 0.5f), 0f };
+ yield return new object[] { new[] { 4, 5, 6, 7 }, new Func<int, int>(i => i + 1), 0 };
+ }
- var copy4 = ImmutableArray.CreateRange(array, 0, 2, i => i + 1);
- Assert.Equal(new int[] { 5, 6 }, copy4);
+ [Theory]
+ [MemberData(nameof(Int32EnumerableData))]
+ public void CreateRangeWithSelectorInvalid(IEnumerable<int> source)
+ {
+ Assert.Throws<ArgumentNullException>("selector", () => ImmutableArray.CreateRange(source.ToImmutableArray(), (Func<int, int>)null));
+ // If both parameters are invalid, the selector should be validated first.
+ Assert.Throws<ArgumentNullException>("selector", () => ImmutableArray.CreateRange(s_emptyDefault, (Func<int, int>)null));
+ Assert.Throws<NullReferenceException>(() => ImmutableArray.CreateRange(s_emptyDefault, i => i));
+ }
- var copy5 = ImmutableArray.CreateRange(array, 0, 4, i => i);
- Assert.Equal(new int[] { 4, 5, 6, 7 }, copy5);
+ [Theory]
+ [MemberData(nameof(CreateRangeWithSelectorAndArgumentData))]
+ public void CreateRangeWithSelectorAndArgument<TArg, TResult>(IEnumerable<int> source, Func<int, TArg, TResult> selector, TArg arg, TArg dummy1, TResult dummy2)
+ {
+ // Remove the dummy parameters once https://github.com/xunit/xunit/pull/965 makes it into corefx.
- var copy6 = ImmutableArray.CreateRange(array, 3, 1, i => i);
- Assert.Equal(new int[] { 7 }, copy6);
+ var expected = source.Zip(Enumerable.Repeat(arg, source.Count()), selector);
+ Assert.Equal(expected, ImmutableArray.CreateRange(source.ToImmutableArray(), selector, arg));
+ }
- var copy7 = ImmutableArray.CreateRange(array, 3, 0, i => i);
- Assert.Equal(new int[] { }, copy7);
+ public static IEnumerable<object[]> CreateRangeWithSelectorAndArgumentData()
+ {
+ yield return new object[] { new int[] { }, new Func<int, int, int>((x, y) => x + y), 0, 0, 0 };
+ yield return new object[] { new[] { 4, 5, 6, 7 }, new Func<int, float, float>((x, y) => x + y), 0.5f, 0f, 0f };
+ yield return new object[] { new[] { 4, 5, 6, 7 }, new Func<int, int, int>((x, y) => x + y), 1, 0, 0 };
+ yield return new object[] { new[] { 4, 5, 6, 7 }, new Func<int, object, int>((x, y) => x), null, new object(), 0 };
+ }
- var copy8 = ImmutableArray.CreateRange(array, 4, 0, i => i);
- Assert.Equal(new int[] { }, copy8);
+ [Theory]
+ [MemberData(nameof(Int32EnumerableData))]
+ public void CreateRangeWithSelectorAndArgumentInvalid(IEnumerable<int> source)
+ {
+ Assert.Throws<ArgumentNullException>("selector", () => ImmutableArray.CreateRange(source.ToImmutableArray(), (Func<int, int, int>)null, 0));
+ // If both parameters are invalid, the selector should be validated first.
+ Assert.Throws<ArgumentNullException>("selector", () => ImmutableArray.CreateRange(s_emptyDefault, (Func<int, int, int>)null, 0));
+ Assert.Throws<NullReferenceException>(() => ImmutableArray.CreateRange(s_emptyDefault, (x, y) => 0, 0));
+ }
- Assert.Throws<ArgumentNullException>("selector", () => ImmutableArray.CreateRange(array, 0, 0, (Func<int, int>)null));
- Assert.Throws<ArgumentNullException>("selector", () => ImmutableArray.CreateRange(s_empty, 0, 0, (Func<int, int>)null));
+ [Theory]
+ [MemberData(nameof(CreateRangeSliceWithSelectorData))]
+ public void CreateRangeSliceWithSelector<TResult>(IEnumerable<int> source, int start, int length, Func<int, TResult> selector, TResult dummy)
+ {
+ // Remove the dummy parameters once https://github.com/xunit/xunit/pull/965 makes it into corefx.
- Assert.Throws<ArgumentOutOfRangeException>("start", () => ImmutableArray.CreateRange(array, -1, 1, (Func<int, int>)null));
- Assert.Throws<ArgumentOutOfRangeException>("start", () => ImmutableArray.CreateRange(array, -1, 1, i => i));
- Assert.Throws<ArgumentOutOfRangeException>("length", () => ImmutableArray.CreateRange(array, 0, 5, i => i));
- Assert.Throws<ArgumentOutOfRangeException>("length", () => ImmutableArray.CreateRange(array, 4, 1, i => i));
- Assert.Throws<ArgumentOutOfRangeException>("length", () => ImmutableArray.CreateRange(array, 3, 2, i => i));
- Assert.Throws<ArgumentOutOfRangeException>("length", () => ImmutableArray.CreateRange(array, 1, -1, i => i));
+ var expected = source.Skip(start).Take(length).Select(selector);
+ Assert.Equal(expected, ImmutableArray.CreateRange(source.ToImmutableArray(), start, length, selector));
}
- [Fact]
- public void CreateRangeSliceFromImmutableArrayWithSelectorAndArgument()
+ public static IEnumerable<object[]> CreateRangeSliceWithSelectorData()
{
- var array = ImmutableArray.Create(4, 5, 6, 7);
-
- var copy1 = ImmutableArray.CreateRange(array, 0, 0, (i, j) => i + j, 0.5);
- Assert.Equal(new double[] { }, copy1);
+ yield return new object[] { new[] { 4, 5, 6, 7 }, 0, 0, new Func<int, float>(i => i + 0.5f), 0f };
+ yield return new object[] { new[] { 4, 5, 6, 7 }, 0, 0, new Func<int, double>(i => i + 0.5d), 0d };
+ yield return new object[] { new[] { 4, 5, 6, 7 }, 0, 0, new Func<int, int>(i => i), 0 };
+ yield return new object[] { new[] { 4, 5, 6, 7 }, 0, 1, new Func<int, int>(i => i * 2), 0 };
+ yield return new object[] { new[] { 4, 5, 6, 7 }, 0, 2, new Func<int, int>(i => i + 1), 0 };
+ yield return new object[] { new[] { 4, 5, 6, 7 }, 0, 4, new Func<int, int>(i => i), 0 };
+ yield return new object[] { new[] { 4, 5, 6, 7 }, 3, 1, new Func<int, int>(i => i), 0 };
+ yield return new object[] { new[] { 4, 5, 6, 7 }, 3, 0, new Func<int, int>(i => i), 0 };
+ yield return new object[] { new[] { 4, 5, 6, 7 }, 4, 0, new Func<int, int>(i => i), 0 };
+ }
- var copy2 = ImmutableArray.CreateRange(array, 0, 0, (i, j) => i + j, 0);
- Assert.Equal(new int[] { }, copy2);
+ [Theory]
+ [MemberData(nameof(Int32EnumerableData))]
+ public void CreateRangeSliceWithSelectorInvalid(IEnumerable<int> source)
+ {
+ var array = source.ToImmutableArray();
- var copy3 = ImmutableArray.CreateRange(array, 0, 1, (i, j) => i * j, 2);
- Assert.Equal(new int[] { 8 }, copy3);
+ Assert.Throws<ArgumentNullException>("selector", () => ImmutableArray.CreateRange(array, 0, 0, (Func<int, int>)null));
- var copy4 = ImmutableArray.CreateRange(array, 0, 2, (i, j) => i + j, 1);
- Assert.Equal(new int[] { 5, 6 }, copy4);
+ Assert.Throws<ArgumentOutOfRangeException>("start", () => ImmutableArray.CreateRange(array, -1, 1, (Func<int, int>)null));
+ Assert.Throws<ArgumentOutOfRangeException>("start", () => ImmutableArray.CreateRange(array, -1, 1, i => i));
- var copy5 = ImmutableArray.CreateRange(array, 0, 4, (i, j) => i + j, 0);
- Assert.Equal(new int[] { 4, 5, 6, 7 }, copy5);
+ Assert.Throws<ArgumentOutOfRangeException>("length", () => ImmutableArray.CreateRange(array, 0, array.Length + 1, i => i));
+ Assert.Throws<ArgumentOutOfRangeException>("length", () => ImmutableArray.CreateRange(array, array.Length, 1, i => i));
+ Assert.Throws<ArgumentOutOfRangeException>("length", () => ImmutableArray.CreateRange(array, Math.Max(0, array.Length - 1), 2, i => i));
+ Assert.Throws<ArgumentOutOfRangeException>("length", () => ImmutableArray.CreateRange(array, 0, -1, i => i));
- var copy6 = ImmutableArray.CreateRange(array, 3, 1, (i, j) => i + j, 0);
- Assert.Equal(new int[] { 7 }, copy6);
+ Assert.Throws<NullReferenceException>(() => ImmutableArray.CreateRange(s_emptyDefault, 0, 0, i => i));
+ }
- var copy7 = ImmutableArray.CreateRange(array, 3, 0, (i, j) => i + j, 0);
- Assert.Equal(new int[] { }, copy7);
+ [Theory]
+ [MemberData(nameof(CreateRangeSliceWithSelectorAndArgumentData))]
+ public void CreateRangeSliceWithSelectorAndArgument<TArg, TResult>(IEnumerable<int> source, int start, int length, Func<int, TArg, TResult> selector, TArg arg, TArg dummy1, TResult dummy2)
+ {
+ // Remove the dummy parameters once https://github.com/xunit/xunit/pull/965 makes it into corefx.
- var copy8 = ImmutableArray.CreateRange(array, 4, 0, (i, j) => i + j, 0);
- Assert.Equal(new int[] { }, copy8);
+ var expected = source.Skip(start).Take(length).Zip(Enumerable.Repeat(arg, length), selector);
+ Assert.Equal(expected, ImmutableArray.CreateRange(source.ToImmutableArray(), start, length, selector, arg));
+ }
- var copy9 = ImmutableArray.CreateRange(array, 0, 1, (int i, object j) => i, null);
- Assert.Equal(new int[] { 4 }, copy9);
+ public static IEnumerable<object[]> CreateRangeSliceWithSelectorAndArgumentData()
+ {
+ yield return new object[] { new int[] { }, 0, 0, new Func<int, int, int>((x, y) => x + y), 0, 0, 0 };
+ yield return new object[] { new[] { 4, 5, 6, 7 }, 0, 0, new Func<int, float, float>((x, y) => x + y), 0.5f, 0f, 0f };
+ yield return new object[] { new[] { 4, 5, 6, 7 }, 0, 0, new Func<int, double, double>((x, y) => x + y), 0.5d, 0d, 0d };
+ yield return new object[] { new[] { 4, 5, 6, 7 }, 0, 0, new Func<int, int, int>((x, y) => x + y), 0, 0, 0 };
+ yield return new object[] { new[] { 4, 5, 6, 7 }, 0, 1, new Func<int, int, int>((x, y) => x * y), 2, 0, 0 };
+ yield return new object[] { new[] { 4, 5, 6, 7 }, 0, 2, new Func<int, int, int>((x, y) => x + y), 1, 0, 0 };
+ yield return new object[] { new[] { 4, 5, 6, 7 }, 0, 4, new Func<int, int, int>((x, y) => x + y), 0, 0, 0 };
+ yield return new object[] { new[] { 4, 5, 6, 7 }, 3, 1, new Func<int, int, int>((x, y) => x + y), 0, 0, 0 };
+ yield return new object[] { new[] { 4, 5, 6, 7 }, 3, 0, new Func<int, int, int>((x, y) => x + y), 0, 0, 0 };
+ yield return new object[] { new[] { 4, 5, 6, 7 }, 4, 0, new Func<int, int, int>((x, y) => x + y), 0, 0, 0 };
+ yield return new object[] { new[] { 4, 5, 6, 7 }, 0, 1, new Func<int, object, int>((x, y) => x), null, new object(), 0 };
+ }
- Assert.Equal(new int[] { }, ImmutableArray.CreateRange(s_empty, 0, 0, (i, j) => i + j, 0));
+ [Theory]
+ [MemberData(nameof(Int32EnumerableData))]
+ public void CreateRangeSliceWithSelectorAndArgumentInvalid(IEnumerable<int> source)
+ {
+ var array = source.ToImmutableArray();
Assert.Throws<ArgumentNullException>("selector", () => ImmutableArray.CreateRange(array, 0, 0, (Func<int, int, int>)null, 0));
- Assert.Throws<ArgumentNullException>("selector", () => ImmutableArray.CreateRange(s_empty, 0, 0, (Func<int, int, int>)null, 0));
Assert.Throws<ArgumentOutOfRangeException>("start", () => ImmutableArray.CreateRange(s_empty, -1, 1, (Func<int, int, int>)null, 0));
Assert.Throws<ArgumentOutOfRangeException>("start", () => ImmutableArray.CreateRange(array, -1, 1, (i, j) => i + j, 0));
- Assert.Throws<ArgumentOutOfRangeException>("length", () => ImmutableArray.CreateRange(array, 0, 5, (i, j) => i + j, 0));
- Assert.Throws<ArgumentOutOfRangeException>("length", () => ImmutableArray.CreateRange(array, 4, 1, (i, j) => i + j, 0));
- Assert.Throws<ArgumentOutOfRangeException>("length", () => ImmutableArray.CreateRange(array, 3, 2, (i, j) => i + j, 0));
- Assert.Throws<ArgumentOutOfRangeException>("length", () => ImmutableArray.CreateRange(array, 1, -1, (i, j) => i + j, 0));
+
+ Assert.Throws<ArgumentOutOfRangeException>("length", () => ImmutableArray.CreateRange(array, 0, array.Length + 1, (i, j) => i + j, 0));
+ Assert.Throws<ArgumentOutOfRangeException>("length", () => ImmutableArray.CreateRange(array, array.Length, 1, (i, j) => i + j, 0));
+ Assert.Throws<ArgumentOutOfRangeException>("length", () => ImmutableArray.CreateRange(array, Math.Max(0, array.Length - 1), 2, (i, j) => i + j, 0));
+ Assert.Throws<ArgumentOutOfRangeException>("length", () => ImmutableArray.CreateRange(array, 0, -1, (i, j) => i + j, 0));
+
+ Assert.Throws<NullReferenceException>(() => ImmutableArray.CreateRange(s_emptyDefault, 0, 0, (x, y) => 0, 0));
}
- [Fact]
- public void CreateFromSliceOfImmutableArray()
+ [Theory]
+ [MemberData(nameof(CreateFromSliceData))]
+ public void CreateFromSlice(IEnumerable<int> source, int start, int length)
+ {
+ Assert.Equal(source.Skip(start).Take(length), ImmutableArray.Create(source.ToImmutableArray(), start, length));
+ Assert.Equal(source.Skip(start).Take(length), ImmutableArray.Create(source.ToArray(), start, length));
+ }
+
+ public static IEnumerable<object[]> CreateFromSliceData()
{
- var array = ImmutableArray.Create(4, 5, 6, 7);
- Assert.Equal(new[] { 4, 5 }, ImmutableArray.Create(array, 0, 2));
- Assert.Equal(new[] { 5, 6 }, ImmutableArray.Create(array, 1, 2));
- Assert.Equal(new[] { 6, 7 }, ImmutableArray.Create(array, 2, 2));
- Assert.Equal(new[] { 7 }, ImmutableArray.Create(array, 3, 1));
- Assert.Equal(new int[0], ImmutableArray.Create(array, 4, 0));
+ yield return new object[] { new int[] { }, 0, 0 };
+ yield return new object[] { new[] { 4, 5, 6, 7 }, 0, 2 };
+ yield return new object[] { new[] { 4, 5, 6, 7 }, 1, 2 };
+ yield return new object[] { new[] { 4, 5, 6, 7 }, 2, 2 };
+ yield return new object[] { new[] { 4, 5, 6, 7 }, 3, 1 };
+ yield return new object[] { new[] { 4, 5, 6, 7 }, 4, 0 };
+ }
- Assert.Equal(new int[] { }, ImmutableArray.Create(s_empty, 0, 0));
+ [Theory]
+ [MemberData(nameof(Int32EnumerableData))]
+ public void CreateFromSliceOfImmutableArrayInvalid(IEnumerable<int> source)
+ {
+ var array = source.ToImmutableArray();
- Assert.Throws<ArgumentOutOfRangeException>("length", () => ImmutableArray.Create(s_empty, 0, 1));
Assert.Throws<ArgumentOutOfRangeException>("start", () => ImmutableArray.Create(array, -1, 0));
+ Assert.Throws<ArgumentOutOfRangeException>("start", () => ImmutableArray.Create(array, array.Length + 1, 0));
+
Assert.Throws<ArgumentOutOfRangeException>("length", () => ImmutableArray.Create(array, 0, -1));
Assert.Throws<ArgumentOutOfRangeException>("length", () => ImmutableArray.Create(array, 0, array.Length + 1));
- Assert.Throws<ArgumentOutOfRangeException>("length", () => ImmutableArray.Create(array, 1, array.Length));
- Assert.Throws<ArgumentOutOfRangeException>("start", () => ImmutableArray.Create(array, array.Length + 1, 0));
+ Assert.Throws<ArgumentOutOfRangeException>("length", () => ImmutableArray.Create(array, Math.Max(0, array.Length - 1), 2));
+
+ if (array.Length > 0)
+ {
+ Assert.Throws<ArgumentOutOfRangeException>("length", () => ImmutableArray.Create(array, 1, array.Length));
+ }
}
- [Fact]
- public void CreateFromSliceOfImmutableArrayOptimizations()
+ [Theory]
+ [MemberData(nameof(Int32EnumerableData))]
+ public void CreateFromSliceOfImmutableArrayOptimizations(IEnumerable<int> source)
{
- var array = ImmutableArray.Create(4, 5, 6, 7);
+ var array = source.ToImmutableArray();
var slice = ImmutableArray.Create(array, 0, array.Length);
- Assert.Equal(array, slice); // array instance actually shared between the two
+ Assert.True(array == slice); // Verify that the underlying arrays are reference-equal.
}
- [Fact]
- public void CreateFromSliceOfImmutableArrayEmptyReturnsSingleton()
+ [Theory]
+ [MemberData(nameof(Int32EnumerableData))]
+ public void CreateFromSliceOfImmutableArrayEmptyReturnsSingleton(IEnumerable<int> source)
{
- var array = ImmutableArray.Create(4, 5, 6, 7);
- var slice = ImmutableArray.Create(array, 1, 0);
- Assert.Equal(s_empty, slice);
+ var array = source.ToImmutableArray();
+ var slice = ImmutableArray.Create(array, Math.Min(1, array.Length), 0);
+ Assert.True(s_empty == slice);
}
- [Fact]
- public void CreateFromSliceOfArray()
+ [Theory]
+ [MemberData(nameof(Int32EnumerableData))]
+ public void CreateFromSliceOfArrayInvalid(IEnumerable<int> source)
{
- var array = new int[] { 4, 5, 6, 7 };
- Assert.Equal(new[] { 4, 5 }, ImmutableArray.Create(array, 0, 2));
- Assert.Equal(new[] { 5, 6 }, ImmutableArray.Create(array, 1, 2));
- Assert.Equal(new[] { 6, 7 }, ImmutableArray.Create(array, 2, 2));
- Assert.Equal(new[] { 7 }, ImmutableArray.Create(array, 3, 1));
- Assert.Equal(new int[0], ImmutableArray.Create(array, 4, 0));
+ var array = source.ToArray();
Assert.Throws<ArgumentOutOfRangeException>("start", () => ImmutableArray.Create(array, -1, 0));
+ Assert.Throws<ArgumentOutOfRangeException>("start", () => ImmutableArray.Create(array, array.Length + 1, 0));
+
Assert.Throws<ArgumentOutOfRangeException>("length", () => ImmutableArray.Create(array, 0, -1));
Assert.Throws<ArgumentOutOfRangeException>("length", () => ImmutableArray.Create(array, 0, array.Length + 1));
- Assert.Throws<ArgumentOutOfRangeException>("length", () => ImmutableArray.Create(array, 1, array.Length));
- Assert.Throws<ArgumentOutOfRangeException>("start", () => ImmutableArray.Create(array, array.Length + 1, 0));
+ Assert.Throws<ArgumentOutOfRangeException>("length", () => ImmutableArray.Create(array, Math.Max(0, array.Length - 1), 2));
+
+ if (array.Length > 0)
+ {
+ Assert.Throws<ArgumentOutOfRangeException>("length", () => ImmutableArray.Create(array, 1, array.Length));
+ }
}
- [Fact]
- public void CreateFromSliceOfArrayEmptyReturnsSingleton()
+ [Theory]
+ [MemberData(nameof(Int32EnumerableData))]
+ public void CreateFromSliceOfArrayEmptyReturnsSingleton(IEnumerable<int> source)
{
- var array = new int[] { 4, 5, 6, 7 };
- var slice = ImmutableArray.Create(array, 1, 0);
- Assert.Equal(s_empty, slice);
- slice = ImmutableArray.Create(array, array.Length, 0);
- Assert.Equal(s_empty, slice);
+ var array = source.ToArray();
+ var slice = ImmutableArray.Create(array, Math.Min(1, array.Length), 0);
+ Assert.True(s_empty == slice);
}
- [Fact]
- public void CreateFromArray()
+ [Theory]
+ [MemberData(nameof(Int32EnumerableData))]
+ public void CreateFromArray(IEnumerable<int> source)
{
- var source = new[] { 1, 2, 3 };
- var immutable = ImmutableArray.Create(source);
- Assert.Equal(source, immutable);
+ Assert.Equal(source, ImmutableArray.Create(source.ToArray()));
}
[Fact]
- public void CreateFromNullArray()
+ public void CreateFromArrayNull()
{
- int[] nullArray = null;
- ImmutableArray<int> immutable = ImmutableArray.Create(nullArray);
+ var immutable = ImmutableArray.Create(default(int[]));
Assert.False(immutable.IsDefault);
- Assert.Equal(0, immutable.Length);
+ Assert.True(immutable.IsEmpty);
}
[Fact]
@@ -261,13 +350,13 @@ namespace System.Collections.Immutable.Tests
ImmutableArray<string> derivedImmutable = ImmutableArray.Create("a", "b", "c");
ImmutableArray<object> baseImmutable = derivedImmutable.As<object>();
Assert.False(baseImmutable.IsDefault);
- // Must cast to object or the IEnumerable<object> overload of Equals would be used
+ // Must cast to object or the IEnumerable<object> overload of Assert.Equal would be used
Assert.Equal((object)derivedImmutable, baseImmutable, EqualityComparer<object>.Default);
// Make sure we can reverse that, as a means to verify the underlying array is the same instance.
ImmutableArray<string> derivedImmutable2 = baseImmutable.As<string>();
Assert.False(derivedImmutable2.IsDefault);
- Assert.Equal(derivedImmutable, derivedImmutable2);
+ Assert.True(derivedImmutable == derivedImmutable2);
// Try a cast that would fail.
Assert.True(baseImmutable.As<Encoder>().IsDefault);
@@ -287,13 +376,11 @@ namespace System.Collections.Immutable.Tests
Assert.True(derivedImmutable == derivedImmutable2);
}
- /// <summary>
- /// Verifies that using an ordinary Create factory method is smart enough to reuse
- /// an underlying array when possible.
- /// </summary>
[Fact]
public void CovarianceImplicit()
{
+ // Verify that CreateRange is smart enough to reuse the underlying array when possible.
+
ImmutableArray<string> derivedImmutable = ImmutableArray.Create("a", "b", "c");
ImmutableArray<object> baseImmutable = ImmutableArray.CreateRange<object>(derivedImmutable);
// Must cast to object or the IEnumerable<object> overload of Equals would be used
@@ -301,7 +388,7 @@ namespace System.Collections.Immutable.Tests
// Make sure we can reverse that, as a means to verify the underlying array is the same instance.
ImmutableArray<string> derivedImmutable2 = baseImmutable.As<string>();
- Assert.Equal(derivedImmutable, derivedImmutable2);
+ Assert.True(derivedImmutable == derivedImmutable2);
}
[Fact]
@@ -313,8 +400,8 @@ namespace System.Collections.Immutable.Tests
Assert.Equal((object)derivedImmutable, baseImmutable, EqualityComparer<object>.Default);
// Make sure we can reverse that, as a means to verify the underlying array is the same instance.
- Assert.Equal(derivedImmutable, baseImmutable.As<string>());
- Assert.Equal(derivedImmutable, baseImmutable.CastArray<string>());
+ Assert.True(derivedImmutable == baseImmutable.As<string>());
+ Assert.True(derivedImmutable == baseImmutable.CastArray<string>());
}
[Fact]
@@ -332,13 +419,13 @@ namespace System.Collections.Immutable.Tests
}
[Fact]
- public void CastUpRefToInterface()
+ public void CastUpReferenceToInterface()
{
var stringArray = ImmutableArray.Create("a", "b");
var enumArray = ImmutableArray<IEnumerable>.CastUp(stringArray);
Assert.Equal(2, enumArray.Length);
- Assert.Equal(stringArray, enumArray.CastArray<string>());
- Assert.Equal(stringArray, enumArray.As<string>());
+ Assert.True(stringArray == enumArray.CastArray<string>());
+ Assert.True(stringArray == enumArray.As<string>());
}
[Fact]
@@ -347,8 +434,8 @@ namespace System.Collections.Immutable.Tests
var genericEnumArray = ImmutableArray.Create<IEnumerable<int>>(new List<int>(), new List<int>());
var legacyEnumArray = ImmutableArray<IEnumerable>.CastUp(genericEnumArray);
Assert.Equal(2, legacyEnumArray.Length);
- Assert.Equal(genericEnumArray, legacyEnumArray.As<IEnumerable<int>>());
- Assert.Equal(genericEnumArray, legacyEnumArray.CastArray<IEnumerable<int>>());
+ Assert.True(genericEnumArray == legacyEnumArray.As<IEnumerable<int>>());
+ Assert.True(genericEnumArray == legacyEnumArray.CastArray<IEnumerable<int>>());
}
[Fact]
@@ -357,18 +444,18 @@ namespace System.Collections.Immutable.Tests
var arrayArray = ImmutableArray.Create(new int[] { 1, 2 }, new int[] { 3, 4 });
var sysArray = ImmutableArray<Array>.CastUp(arrayArray);
Assert.Equal(2, sysArray.Length);
- Assert.Equal(arrayArray, sysArray.As<int[]>());
- Assert.Equal(arrayArray, sysArray.CastArray<int[]>());
+ Assert.True(arrayArray == sysArray.As<int[]>());
+ Assert.True(arrayArray == sysArray.CastArray<int[]>());
}
[Fact]
public void CastUpArrayToObject()
{
var arrayArray = ImmutableArray.Create(new int[] { 1, 2 }, new int[] { 3, 4 });
- var objArray = ImmutableArray<object>.CastUp(arrayArray);
- Assert.Equal(2, objArray.Length);
- Assert.Equal(arrayArray, objArray.As<int[]>());
- Assert.Equal(arrayArray, objArray.CastArray<int[]>());
+ var objectArray = ImmutableArray<object>.CastUp(arrayArray);
+ Assert.Equal(2, objectArray.Length);
+ Assert.True(arrayArray == objectArray.As<int[]>());
+ Assert.True(arrayArray == objectArray.CastArray<int[]>());
}
[Fact]
@@ -377,19 +464,19 @@ namespace System.Collections.Immutable.Tests
var delArray = ImmutableArray.Create<Action>(() => { }, () => { });
var sysDelArray = ImmutableArray<Delegate>.CastUp(delArray);
Assert.Equal(2, sysDelArray.Length);
- Assert.Equal(delArray, sysDelArray.As<Action>());
- Assert.Equal(delArray, sysDelArray.CastArray<Action>());
+ Assert.True(delArray == sysDelArray.As<Action>());
+ Assert.True(delArray == sysDelArray.CastArray<Action>());
}
[Fact]
public void CastArrayUnrelatedInterface()
{
- var strArray = ImmutableArray.Create<string>("cat", "dog");
- var compArray = ImmutableArray<IComparable>.CastUp(strArray);
- var enumArray = compArray.CastArray<IEnumerable>();
+ var stringArray = ImmutableArray.Create("cat", "dog");
+ var comparableArray = ImmutableArray<IComparable>.CastUp(stringArray);
+ var enumArray = comparableArray.CastArray<IEnumerable>();
Assert.Equal(2, enumArray.Length);
- Assert.Equal(strArray, enumArray.As<string>());
- Assert.Equal(strArray, enumArray.CastArray<string>());
+ Assert.True(stringArray == enumArray.As<string>());
+ Assert.True(stringArray == enumArray.CastArray<string>());
}
[Fact]
@@ -400,48 +487,57 @@ namespace System.Collections.Immutable.Tests
}
[Fact]
- public void CastArrayBadRef()
+ public void CastArrayBadReference()
{
- var objArray = ImmutableArray.Create<object>("cat", "dog");
- Assert.Throws<InvalidCastException>(() => objArray.CastArray<string>());
+ var objectArray = ImmutableArray.Create<object>("cat", "dog");
+ Assert.Throws<InvalidCastException>(() => objectArray.CastArray<string>());
}
- [Fact]
- public void ToImmutableArray()
+ [Theory]
+ [MemberData(nameof(Int32EnumerableData))]
+ public void ToImmutableArray(IEnumerable<int> source)
{
- IEnumerable<int> source = new[] { 1, 2, 3 };
- ImmutableArray<int> immutable = source.ToImmutableArray();
- Assert.Equal(source, immutable);
+ var array = source.ToImmutableArray();
+ Assert.Equal(source, array);
+ Assert.True(array == array.ToImmutableArray()); // Compares referential equality.
+ }
- ImmutableArray<int> immutable2 = immutable.ToImmutableArray();
- Assert.Equal(immutable, immutable2); // this will compare array reference equality.
+ [Theory]
+ [MemberData(nameof(Int32EnumerableData))]
+ public void Count(IEnumerable<int> source)
+ {
+ var array = source.ToImmutableArray();
+
+ Assert.Equal(source.Count(), array.Length);
+ Assert.Equal(source.Count(), ((ICollection)array).Count);
+ Assert.Equal(source.Count(), ((ICollection<int>)array).Count);
+ Assert.Equal(source.Count(), ((IReadOnlyCollection<int>)array).Count);
}
[Fact]
- public void Count()
+ public void CountInvalid()
{
Assert.Throws<NullReferenceException>(() => s_emptyDefault.Length);
Assert.Throws<InvalidOperationException>(() => ((ICollection)s_emptyDefault).Count);
Assert.Throws<InvalidOperationException>(() => ((ICollection<int>)s_emptyDefault).Count);
Assert.Throws<InvalidOperationException>(() => ((IReadOnlyCollection<int>)s_emptyDefault).Count);
+ }
- Assert.Equal(0, s_empty.Length);
- Assert.Equal(0, ((IReadOnlyCollection<int>)s_empty).Count);
-
- Assert.Equal(1, s_oneElement.Length);
- Assert.Equal(1, ((IReadOnlyCollection<int>)s_oneElement).Count);
+ [Theory]
+ [MemberData(nameof(Int32EnumerableData))]
+ public void IsEmpty(IEnumerable<int> source)
+ {
+ Assert.Equal(!source.Any(), source.ToImmutableArray().IsEmpty);
}
[Fact]
- public void IsEmpty()
+ public void IsEmptyInvalid()
{
Assert.Throws<NullReferenceException>(() => s_emptyDefault.IsEmpty);
- Assert.True(s_empty.IsEmpty);
- Assert.False(s_oneElement.IsEmpty);
}
[Fact]
- public void IndexOfDefault()
+ public void IndexOfInvalid()
{
Assert.Throws<NullReferenceException>(() => s_emptyDefault.IndexOf(5));
Assert.Throws<NullReferenceException>(() => s_emptyDefault.IndexOf(5, 0));
@@ -449,7 +545,7 @@ namespace System.Collections.Immutable.Tests
}
[Fact]
- public void LastIndexOfDefault()
+ public void LastIndexOfInvalid()
{
Assert.Throws<NullReferenceException>(() => s_emptyDefault.LastIndexOf(5));
Assert.Throws<NullReferenceException>(() => s_emptyDefault.LastIndexOf(5, 0));
@@ -479,948 +575,1525 @@ namespace System.Collections.Immutable.Tests
(b, v, i, c, eq) => b.LastIndexOf(v, i, c, eq));
}
- [Fact]
- public void Contains()
+ [Theory]
+ [MemberData(nameof(Int32EnumerableData))]
+ public void ContainsInt32(IEnumerable<int> source)
{
- Assert.Throws<NullReferenceException>(() => s_emptyDefault.Contains(0));
- Assert.False(s_empty.Contains(0));
- Assert.True(s_oneElement.Contains(1));
- Assert.False(s_oneElement.Contains(2));
- Assert.True(s_manyElements.Contains(3));
- Assert.False(s_oneElementRefType.Contains(null));
- Assert.True(s_twoElementRefTypeWithNull.Contains(null));
+ var array = source.ToImmutableArray();
+
+ if (source.Any(i => i >= 0))
+ {
+ int contained = Enumerable.Range(0, int.MaxValue).First(i => source.Contains(i));
+ Assert.True(array.Contains(contained));
+ Assert.True(((ICollection<int>)array).Contains(contained));
+ }
+
+ int notContained = Enumerable.Range(0, int.MaxValue).First(i => !source.Contains(i));
+ Assert.False(array.Contains(notContained));
+ Assert.False(((ICollection<int>)array).Contains(notContained));
}
- [Fact]
- public void ContainsEqualityComparer()
+ [Theory]
+ [MemberData(nameof(ContainsNullData))]
+ public void ContainsNull<T>(IEnumerable<T> source, T dummy) where T : class
{
- var array = ImmutableArray.Create("a", "B");
- Assert.False(array.Contains("A", StringComparer.Ordinal));
- Assert.True(array.Contains("A", StringComparer.OrdinalIgnoreCase));
- Assert.False(array.Contains("b", StringComparer.Ordinal));
- Assert.True(array.Contains("b", StringComparer.OrdinalIgnoreCase));
+ // Remove the dummy parameters once https://github.com/xunit/xunit/pull/965 makes it into corefx.
+
+ bool expected = source.Contains(null, EqualityComparer<T>.Default);
+ Assert.Equal(expected, source.ToImmutableArray().Contains(null));
+ }
+
+ public static IEnumerable<object[]> ContainsNullData()
+ {
+ yield return new object[] { s_oneElementRefType, new GenericParameterHelper() };
+ yield return new object[] { s_twoElementRefTypeWithNull, string.Empty };
+ yield return new object[] { new[] { new object() }, new object() };
}
[Fact]
- public void Enumerator()
+ public void ContainsInvalid()
{
- Assert.Throws<NullReferenceException>(() => s_emptyDefault.GetEnumerator());
+ Assert.Throws<NullReferenceException>(() => s_emptyDefault.Contains(0));
+ }
- ImmutableArray<int>.Enumerator enumerator = default(ImmutableArray<int>.Enumerator);
- Assert.Throws<NullReferenceException>(() => enumerator.Current);
- Assert.Throws<NullReferenceException>(() => enumerator.MoveNext());
+ [Theory]
+ [MemberData(nameof(Int32EnumerableData))]
+ public void GetEnumerator(IEnumerable<int> source)
+ {
+ var array = source.ToImmutableArray();
+ var enumeratorStruct = array.GetEnumerator();
+
+ Assert.IsType<ImmutableArray<int>.Enumerator>(enumeratorStruct);
+ AssertNotAssignableFrom<IDisposable>(enumeratorStruct);
+ AssertNotAssignableFrom<IEnumerator>(enumeratorStruct);
+ AssertNotAssignableFrom<IEnumerator<int>>(enumeratorStruct);
+
+ var set = new HashSet<IEnumerator>();
- enumerator = s_empty.GetEnumerator();
- Assert.Throws<IndexOutOfRangeException>(() => enumerator.Current);
- Assert.False(enumerator.MoveNext());
+ set.Add(((IEnumerable<int>)array).GetEnumerator());
+ set.Add(((IEnumerable<int>)array).GetEnumerator());
- enumerator = s_manyElements.GetEnumerator();
- Assert.Throws<IndexOutOfRangeException>(() => enumerator.Current);
+ set.Add(((IEnumerable)array).GetEnumerator());
+ set.Add(((IEnumerable)array).GetEnumerator());
- Assert.True(enumerator.MoveNext());
- Assert.Equal(1, enumerator.Current);
- Assert.True(enumerator.MoveNext());
- Assert.Equal(2, enumerator.Current);
- Assert.True(enumerator.MoveNext());
- Assert.Equal(3, enumerator.Current);
+ int expected = array.IsEmpty ? 1 : 4; // Empty ImmutableArrays should cache their enumerators.
+ Assert.Equal(expected, set.Count);
+ Assert.DoesNotContain(null, set);
- Assert.False(enumerator.MoveNext());
- Assert.Throws<IndexOutOfRangeException>(() => enumerator.Current);
+ Assert.All(set, enumerator =>
+ {
+ Assert.NotEqual(enumeratorStruct.GetType(), enumerator.GetType());
+ Assert.Equal(set.First().GetType(), enumerator.GetType());
+ });
+ }
+
+ private static void AssertNotAssignableFrom<T>(object obj)
+ {
+ var typeInfo = obj.GetType().GetTypeInfo();
+ Assert.False(typeof(T).GetTypeInfo().IsAssignableFrom(typeInfo));
}
[Fact]
- public void ObjectEnumerator()
+ public void GetEnumeratorObjectEmptyReturnsSingleton()
{
+ var empty = (IEnumerable<int>)s_empty;
+ Assert.Same(empty.GetEnumerator(), empty.GetEnumerator());
+ }
+
+ [Fact]
+ public void GetEnumeratorInvalid()
+ {
+ Assert.Throws<NullReferenceException>(() => s_emptyDefault.GetEnumerator());
+ Assert.Throws<InvalidOperationException>(() => ((IEnumerable)s_emptyDefault).GetEnumerator());
Assert.Throws<InvalidOperationException>(() => ((IEnumerable<int>)s_emptyDefault).GetEnumerator());
+ }
+
+ [Theory]
+ [MemberData(nameof(Int32EnumerableData))]
+ public void EnumeratorTraversal(IEnumerable<int> source)
+ {
+ var array = source.ToImmutableArray();
- IEnumerator<int> enumerator = ((IEnumerable<int>)s_empty).GetEnumerator();
- Assert.Throws<InvalidOperationException>(() => enumerator.Current);
- Assert.False(enumerator.MoveNext());
+ var enumeratorStruct = array.GetEnumerator();
+ var enumeratorObject = ((IEnumerable<int>)array).GetEnumerator();
- enumerator = ((IEnumerable<int>)s_manyElements).GetEnumerator();
- Assert.Throws<InvalidOperationException>(() => enumerator.Current);
+ Assert.Throws<IndexOutOfRangeException>(() => enumeratorStruct.Current);
+ Assert.Throws<InvalidOperationException>(() => enumeratorObject.Current);
- for (int i = 0; i < 2; i++)
+ int count = source.Count();
+
+ for (int i = 0; i < count; i++)
{
- Assert.True(enumerator.MoveNext());
- Assert.Equal(1, enumerator.Current);
- Assert.True(enumerator.MoveNext());
- Assert.Equal(2, enumerator.Current);
- Assert.True(enumerator.MoveNext());
- Assert.Equal(3, enumerator.Current);
- if (i == 0)
- enumerator.Reset();
+ Assert.True(enumeratorStruct.MoveNext());
+ Assert.True(enumeratorObject.MoveNext());
+
+ int element = source.ElementAt(i);
+ Assert.Equal(element, enumeratorStruct.Current);
+ Assert.Equal(element, enumeratorObject.Current);
+ Assert.Equal(element, ((IEnumerator)enumeratorObject).Current);
}
- Assert.False(enumerator.MoveNext());
- Assert.Throws<InvalidOperationException>(() => enumerator.Current);
+ Assert.False(enumeratorStruct.MoveNext());
+ Assert.False(enumeratorObject.MoveNext());
+
+ Assert.Throws<IndexOutOfRangeException>(() => enumeratorStruct.Current);
+ Assert.Throws<InvalidOperationException>(() => enumeratorObject.Current);
}
- [Fact]
- public void EnumeratorWithNullValues()
+ [Theory]
+ [MemberData(nameof(Int32EnumerableData))]
+ public void EnumeratorObjectTraversalDisposeReset(IEnumerable<int> source)
{
- var enumerationResult = System.Linq.Enumerable.ToArray(s_twoElementRefTypeWithNull);
- Assert.Equal("1", enumerationResult[0]);
- Assert.Null(enumerationResult[1]);
+ var array = (IEnumerable<int>)source.ToImmutableArray();
+ var enumerator = array.GetEnumerator();
+
+ Assert.All(Enumerable.Range(0, source.Count()), bound =>
+ {
+ enumerator.Reset();
+ enumerator.Dispose(); // This should have no effect.
+
+ for (int i = 0; i < bound; i++)
+ {
+ int element = source.ElementAt(i);
+
+ enumerator.Dispose(); // This should have no effect.
+ Assert.True(enumerator.MoveNext());
+ Assert.Equal(element, enumerator.Current);
+ Assert.Equal(element, ((IEnumerator)enumerator).Current);
+ }
+ });
}
[Fact]
- public void EqualityCheckComparesInternalArrayByReference()
+ public void EnumeratorStructTraversalDefaultInvalid()
{
- var immutable1 = ImmutableArray.Create(1);
- var immutable2 = ImmutableArray.Create(1);
- Assert.NotEqual(immutable1, immutable2);
+ var enumerator = default(ImmutableArray<int>.Enumerator);
+ Assert.Throws<NullReferenceException>(() => enumerator.Current);
+ Assert.Throws<NullReferenceException>(() => enumerator.MoveNext());
+ }
- Assert.True(immutable1.Equals(immutable1));
- Assert.True(immutable1.Equals((object)immutable1));
+ [Theory]
+ [MemberData(nameof(EnumeratorTraversalNullData))]
+ public void EnumeratorTraversalNull<T>(IEnumerable<T> source, T dummy) where T : class
+ {
+ // Remove the dummy parameters once https://github.com/xunit/xunit/pull/965 makes it into corefx.
+
+ var array = ForceLazy(source.ToImmutableArray()).ToArray();
+ Assert.Equal(source, array);
+ Assert.Contains(null, array);
}
- [Fact]
- public void EqualsObjectNull()
+ public static IEnumerable<object[]> EnumeratorTraversalNullData()
{
- Assert.False(s_empty.Equals((object)null));
+ yield return new object[] { s_twoElementRefTypeWithNull, string.Empty };
+ yield return new object[] { new[] { default(object) }, new object() };
+ yield return new object[] { new[] { null, new object() }, new object() };
+ yield return new object[] { new[] { null, string.Empty }, string.Empty };
}
- [Fact]
- public void OperatorsAndEquality()
+ [Theory]
+ [MemberData(nameof(EqualsData))]
+ public void Equals(StrongBox<ImmutableArray<int>> firstBox, StrongBox<ImmutableArray<int>> secondBox, bool expected)
{
- Assert.True(s_empty.Equals(s_empty));
- var emptySame = s_empty;
- Assert.True(s_empty == emptySame);
- Assert.False(s_empty != emptySame);
+ ImmutableArray<int> first = firstBox.Value;
+ ImmutableArray<int> second = secondBox.Value;
+
+ Assert.Equal(expected, first == second);
+ Assert.NotEqual(expected, first != second);
+
+ Assert.Equal(expected, first.Equals(second));
+ Assert.Equal(expected, AsEquatable(first).Equals(second));
+ Assert.Equal(expected, first.Equals((object)second));
- // empty and default should not be seen as equal
- Assert.False(s_empty.Equals(s_emptyDefault));
- Assert.False(s_empty == s_emptyDefault);
- Assert.True(s_empty != s_emptyDefault);
- Assert.False(s_emptyDefault == s_empty);
- Assert.True(s_emptyDefault != s_empty);
+ Assert.Equal(expected, second == first);
+ Assert.NotEqual(expected, second != first);
- Assert.False(s_empty.Equals(s_oneElement));
- Assert.False(s_empty == s_oneElement);
- Assert.True(s_empty != s_oneElement);
- Assert.False(s_oneElement == s_empty);
- Assert.True(s_oneElement != s_empty);
+ Assert.Equal(expected, second.Equals(first));
+ Assert.Equal(expected, AsEquatable(second).Equals(first));
+ Assert.Equal(expected, second.Equals((object)first));
}
- [Fact]
- public void NullableOperators()
+ public static IEnumerable<object[]> EqualsData()
{
- ImmutableArray<int>? nullArray = null;
- ImmutableArray<int>? nonNullDefault = s_emptyDefault;
- ImmutableArray<int>? nonNullEmpty = s_empty;
+ // Once https://github.com/xunit/assert.xunit/pull/5 comes into corefx, all the StrongBox stuff can be removed.
+
+ var enumerables = Int32EnumerableData()
+ .Select(array => array[0])
+ .Cast<IEnumerable<int>>();
- Assert.True(nullArray == nonNullDefault);
- Assert.False(nullArray != nonNullDefault);
- Assert.True(nonNullDefault == nullArray);
- Assert.False(nonNullDefault != nullArray);
+ foreach (var enumerable in enumerables)
+ {
+ var array = enumerable.ToImmutableArray();
- Assert.False(nullArray == nonNullEmpty);
- Assert.True(nullArray != nonNullEmpty);
- Assert.False(nonNullEmpty == nullArray);
- Assert.True(nonNullEmpty != nullArray);
+ yield return new object[]
+ {
+ new StrongBox<ImmutableArray<int>>(array),
+ new StrongBox<ImmutableArray<int>>(array),
+ true
+ };
+
+ // Reference equality, not content equality, should be compared.
+ yield return new object[]
+ {
+ new StrongBox<ImmutableArray<int>>(array),
+ new StrongBox<ImmutableArray<int>>(enumerable.ToImmutableArray()),
+ !enumerable.Any() || enumerable is ImmutableArray<int>
+ };
+ }
+
+ // Empty and default ImmutableArrays should not be seen as equal.
+ yield return new object[]
+ {
+ new StrongBox<ImmutableArray<int>>(s_empty),
+ new StrongBox<ImmutableArray<int>>(s_emptyDefault),
+ false
+ };
+
+ yield return new object[]
+ {
+ new StrongBox<ImmutableArray<int>>(s_empty),
+ new StrongBox<ImmutableArray<int>>(s_oneElement),
+ false
+ };
}
- [Fact]
- public void GetHashCodeTest()
+ [Theory]
+ [MemberData(nameof(StrongBoxedInt32EnumerableData))]
+ [MemberData(nameof(StrongBoxedSpecialInt32ImmutableArrayData))]
+ public void EqualsSelf(StrongBox<IEnumerable<int>> box)
{
- Assert.Equal(0, s_emptyDefault.GetHashCode());
- Assert.NotEqual(0, s_empty.GetHashCode());
- Assert.NotEqual(0, s_oneElement.GetHashCode());
+ IEnumerable<int> source = box.Value;
+ var array = source.ToImmutableArray();
+
+#pragma warning disable CS1718 // Comparison made to same variable
+ Assert.True(array == array);
+ Assert.False(array != array);
+#pragma warning restore CS1718 // Comparison made to same variable
+
+ Assert.True(array.Equals(array));
+ Assert.True(AsEquatable(array).Equals(array));
+ Assert.True(array.Equals((object)array));
}
- [Fact]
- public void Add()
+ [Theory]
+ [MemberData(nameof(StrongBoxedInt32EnumerableData))]
+ [MemberData(nameof(StrongBoxedSpecialInt32ImmutableArrayData))]
+ public void EqualsNull(StrongBox<IEnumerable<int>> box)
{
- var source = new[] { 1, 2 };
- var array1 = ImmutableArray.Create(source);
- var array2 = array1.Add(3);
- Assert.Equal(source, array1);
- Assert.Equal(new[] { 1, 2, 3 }, array2);
- Assert.Equal(new[] { 1 }, s_empty.Add(1));
+ IEnumerable<int> source = box.Value;
+ Assert.False(source.ToImmutableArray().Equals(null));
}
- [Fact]
- public void AddRange()
+ [Theory]
+ [MemberData(nameof(StrongBoxedInt32EnumerableData))]
+ [MemberData(nameof(StrongBoxedSpecialInt32ImmutableArrayData))]
+ public void EqualsNullable(StrongBox<IEnumerable<int>> box)
{
- var nothingToEmpty = s_empty.AddRange(Enumerable.Empty<int>());
- Assert.False(nothingToEmpty.IsDefault);
- Assert.True(nothingToEmpty.IsEmpty);
+ // ImmutableArray<T> overrides the equality operators for ImmutableArray<T>?.
+ // If one nullable with HasValue = false is compared to a nullable with HasValue = true,
+ // but Value.IsDefault = true, the nullables will compare as equal.
- Assert.Equal(new[] { 1, 2 }, s_empty.AddRange(Enumerable.Range(1, 2)));
- Assert.Equal(new[] { 1, 2 }, s_empty.AddRange(new[] { 1, 2 }));
+ IEnumerable<int> source = box.Value;
+ var array = source.ToImmutableArray();
+ ImmutableArray<int>? nullable = array;
- Assert.Equal(new[] { 1, 2, 3, 4 }, s_manyElements.AddRange(new[] { 4 }));
- Assert.Equal(new[] { 1, 2, 3, 4, 5 }, s_manyElements.AddRange(new[] { 4, 5 }));
+ Assert.Equal(array.IsDefault, null == nullable);
+ Assert.NotEqual(array.IsDefault, null != nullable);
- Assert.Equal(new[] { 1, 2, 3, 4 }, s_manyElements.AddRange(ImmutableArray.Create(4)));
- Assert.Equal(new[] { 1, 2, 3, 4, 5 }, s_manyElements.AddRange(ImmutableArray.Create(4, 5)));
+ Assert.Equal(array.IsDefault, nullable == null);
+ Assert.NotEqual(array.IsDefault, nullable != null);
}
- [Fact]
- public void AddRangeDefaultEnumerable()
+ [Theory]
+ [MemberData(nameof(StrongBoxedInt32EnumerableData))]
+ [MemberData(nameof(StrongBoxedSpecialInt32ImmutableArrayData))]
+ public void GetHashCode(StrongBox<IEnumerable<int>> box)
{
- Assert.Throws<NullReferenceException>(() => s_emptyDefault.AddRange(Enumerable.Empty<int>()));
- Assert.Throws<NullReferenceException>(() => s_emptyDefault.AddRange(Enumerable.Range(1, 2)));
- Assert.Throws<NullReferenceException>(() => s_emptyDefault.AddRange(new[] { 1, 2 }));
+ IEnumerable<int> source = box.Value;
+ var array = source.ToImmutableArray();
+
+ // We must box once. Otherwise, the following assert would not have much purpose since
+ // RuntimeHelpers.GetHashCode returns different values for boxed objects that are not
+ // reference-equal.
+ object boxed = array;
+
+ // The default implementation of object.GetHashCode is a call to RuntimeHelpers.GetHashCode.
+ // This assert effectively ensures that ImmutableArray overrides GetHashCode.
+ Assert.NotEqual(RuntimeHelpers.GetHashCode(boxed), boxed.GetHashCode());
+
+ // Ensure that the hash is consistent.
+ Assert.Equal(array.GetHashCode(), array.GetHashCode());
+
+ if (array.IsDefault)
+ {
+ Assert.Equal(0, array.GetHashCode());
+ }
+ else if (array.IsEmpty)
+ {
+ // Empty array instances should be cached.
+ var same = ImmutableArray.Create(new int[0]);
+ Assert.Equal(array.GetHashCode(), same.GetHashCode());
+ }
+
+ // Use reflection to retrieve the underlying array, and ensure that the ImmutableArray's
+ // hash code is equivalent to the array's hash code.
+
+ int[] underlyingArray = GetUnderlyingArray(array);
+ Assert.Equal(underlyingArray?.GetHashCode() ?? 0, array.GetHashCode());
}
- [Fact]
- public void AddRangeDefaultStruct()
+ [Theory]
+ [MemberData(nameof(AddData))]
+ public void Add(IEnumerable<int> source, IEnumerable<int> items)
{
- Assert.Throws<NullReferenceException>(() => s_emptyDefault.AddRange(s_empty));
- Assert.Throws<NullReferenceException>(() => s_empty.AddRange(s_emptyDefault));
- Assert.Throws<NullReferenceException>(() => s_emptyDefault.AddRange(s_oneElement));
- Assert.Throws<NullReferenceException>(() => s_oneElement.AddRange(s_emptyDefault));
+ var array = source.ToImmutableArray();
- IEnumerable<int> emptyBoxed = s_empty;
- IEnumerable<int> emptyDefaultBoxed = s_emptyDefault;
- IEnumerable<int> oneElementBoxed = s_oneElement;
- Assert.Throws<NullReferenceException>(() => s_emptyDefault.AddRange(emptyBoxed));
- Assert.Throws<InvalidOperationException>(() => s_empty.AddRange(emptyDefaultBoxed));
- Assert.Throws<NullReferenceException>(() => s_emptyDefault.AddRange(oneElementBoxed));
- Assert.Throws<InvalidOperationException>(() => s_oneElement.AddRange(emptyDefaultBoxed));
+ var list = new List<Tuple<int[], ImmutableArray<int>>>();
+
+ int index = 0;
+ foreach (int item in items)
+ {
+ // Take a snapshot of the ImmutableArray before the Add.
+ list.Add(Tuple.Create(array.ToArray(), array));
+
+ // Add the next item.
+ array = array.Add(item);
+
+ var expected = source.Concat(items.Take(++index));
+ Assert.Equal(expected, array);
+
+ // Go back to previous ImmutableArrays and make sure their contents
+ // didn't change by comparing them against their snapshots.
+ foreach (var tuple in list)
+ {
+ Assert.Equal(tuple.Item1, tuple.Item2);
+ }
+ }
}
- [Fact]
- public void AddRangeNoOpIdentity()
+ [Theory]
+ [MemberData(nameof(AddData))]
+ public void AddRange(IEnumerable<int> source, IEnumerable<int> items)
{
- Assert.Equal(s_empty, s_empty.AddRange(s_empty));
- Assert.Equal(s_oneElement, s_empty.AddRange(s_oneElement)); // struct overload
- Assert.Equal(s_oneElement, s_empty.AddRange((IEnumerable<int>)s_oneElement)); // enumerable overload
- Assert.Equal(s_oneElement, s_oneElement.AddRange(s_empty));
+ Assert.All(ChangeType(items), it =>
+ {
+ var array = source.ToImmutableArray();
+
+ Assert.Equal(source.Concat(items), array.AddRange(it)); // Enumerable overload
+ Assert.Equal(source.Concat(items), array.AddRange(it.ToImmutableArray())); // Struct overload
+ Assert.Equal(source, array); // Make sure the original array wasn't affected.
+ });
}
- [Fact]
- public void Insert()
+ public static IEnumerable<object[]> AddData()
+ {
+ yield return new object[] { new int[] { }, new[] { 1 } };
+ yield return new object[] { new[] { 1, 2 }, new[] { 3 } };
+ yield return new object[] { s_empty, Enumerable.Empty<int>() };
+ yield return new object[] { s_empty, Enumerable.Range(1, 2) };
+ yield return new object[] { s_empty, new[] { 1, 2 } };
+ yield return new object[] { s_manyElements, new[] { 4 } };
+ yield return new object[] { s_manyElements, new[] { 4, 5 } };
+ yield return new object[] { s_manyElements, new[] { 4 } };
+ yield return new object[] { s_manyElements, new[] { 4, 5 } };
+ yield return new object[] { s_empty, s_empty };
+ yield return new object[] { s_empty, s_oneElement };
+ yield return new object[] { s_oneElement, s_empty };
+ }
+
+ [Theory]
+ [MemberData(nameof(Int32EnumerableData))]
+ public void AddRangeInvalid(IEnumerable<int> source)
{
- var array1 = ImmutableArray.Create<char>();
- Assert.Throws<ArgumentOutOfRangeException>("index", () => array1.Insert(-1, 'a'));
- Assert.Throws<ArgumentOutOfRangeException>("index", () => array1.Insert(1, 'a'));
+ // If the lhs or the rhs is a default ImmutableArray, AddRange should throw.
- var insertFirst = array1.Insert(0, 'c');
- Assert.Equal(new[] { 'c' }, insertFirst);
+ Assert.Throws<NullReferenceException>(() => s_emptyDefault.AddRange(source)); // Enumerable overload
+ Assert.Throws<NullReferenceException>(() => s_emptyDefault.AddRange(source.ToImmutableArray())); // Struct overload
+ Assert.Throws<NullReferenceException>(() => source.ToImmutableArray().AddRange(s_emptyDefault)); // Struct overload
+ Assert.Throws<InvalidOperationException>(() => source.ToImmutableArray().AddRange((IEnumerable<int>)s_emptyDefault)); // Enumerable overload
- var insertLeft = insertFirst.Insert(0, 'a');
- Assert.Equal(new[] { 'a', 'c' }, insertLeft);
+ Assert.Throws<NullReferenceException>(() => s_emptyDefault.AddRange(s_emptyDefault));
+ Assert.Throws<NullReferenceException>(() => s_emptyDefault.AddRange((IEnumerable<int>)s_emptyDefault));
+ }
- var insertRight = insertFirst.Insert(1, 'e');
- Assert.Equal(new[] { 'c', 'e' }, insertRight);
+ [Theory]
+ [MemberData(nameof(InsertData))]
+ public void Insert<T>(IEnumerable<T> source, int index, T item)
+ {
+ var expected = source.Take(index)
+ .Concat(new[] { item })
+ .Concat(source.Skip(index));
+ var array = source.ToImmutableArray();
- var insertBetween = insertLeft.Insert(1, 'b');
- Assert.Equal(new[] { 'a', 'b', 'c' }, insertBetween);
+ Assert.Equal(expected, array.Insert(index, item));
+ Assert.Equal(source, array); // Make sure the original array wasn't affected.
}
- [Fact]
- public void InsertDefault()
+ public static IEnumerable<object[]> InsertData()
{
- Assert.Throws<NullReferenceException>(() => s_emptyDefault.Insert(-1, 10));
- Assert.Throws<NullReferenceException>(() => s_emptyDefault.Insert(1, 10));
- Assert.Throws<NullReferenceException>(() => s_emptyDefault.Insert(0, 10));
+ yield return new object[] { new char[] { }, 0, 'c' };
+ yield return new object[] { new[] { 'c' }, 0, 'a' };
+ yield return new object[] { new[] { 'c' }, 1, 'e' };
+ yield return new object[] { new[] { 'a', 'c' }, 1, 'b' };
}
- [Fact]
- public void InsertRangeEmptyInvalid()
+ [Theory]
+ [MemberData(nameof(Int32EnumerableData))]
+ public void InsertInvalid(IEnumerable<int> source)
{
- Assert.Throws<NullReferenceException>(() => s_emptyDefault.Insert(-1, 10));
- Assert.Throws<NullReferenceException>(() => s_emptyDefault.Insert(1, 10));
- Assert.Throws<ArgumentOutOfRangeException>("index", () => s_empty.InsertRange(1, s_oneElement));
- Assert.Throws<ArgumentOutOfRangeException>("index", () => s_empty.InsertRange(-1, s_oneElement));
- Assert.Throws<ArgumentOutOfRangeException>("index", () => s_empty.InsertRange(1, (IEnumerable<int>)s_oneElement));
- Assert.Throws<ArgumentOutOfRangeException>("index", () => s_empty.InsertRange(-1, (IEnumerable<int>)s_oneElement));
+ var array = source.ToImmutableArray();
+
+ Assert.Throws<ArgumentOutOfRangeException>("index", () => array.Insert(-1, 0x61));
+ Assert.Throws<ArgumentOutOfRangeException>("index", () => array.Insert(array.Length + 1, 0x61));
}
- [Fact]
- public void InsertRangeDefault()
+ [Theory]
+ [InlineData(-1)]
+ [InlineData(1)]
+ [InlineData(0)]
+ public void InsertDefaultInvalid(int index)
{
- Assert.Throws<NullReferenceException>(() => s_emptyDefault.InsertRange(1, Enumerable.Empty<int>()));
- Assert.Throws<NullReferenceException>(() => s_emptyDefault.InsertRange(-1, Enumerable.Empty<int>()));
- Assert.Throws<NullReferenceException>(() => s_emptyDefault.InsertRange(0, Enumerable.Empty<int>()));
- Assert.Throws<NullReferenceException>(() => s_emptyDefault.InsertRange(0, new[] { 1 }));
- Assert.Throws<NullReferenceException>(() => s_emptyDefault.InsertRange(0, new[] { 2, 3, 4 }));
- Assert.Throws<NullReferenceException>(() => s_emptyDefault.InsertRange(0, Enumerable.Range(2, 3)));
+ Assert.Throws<NullReferenceException>(() => s_emptyDefault.Insert(index, 10));
}
- /// <summary>
- /// Validates that a fixed bug in the inappropriate adding of the
- /// Empty singleton enumerator to the reusable instances bag does not regress.
- /// </summary>
- [Fact]
- public void EmptyEnumeratorReuseRegressionTest()
+ [Theory]
+ [MemberData(nameof(Int32EnumerableData))]
+ public void InsertRangeInvalid(IEnumerable<int> source)
{
- IEnumerable<int> oneElementBoxed = s_oneElement;
- IEnumerable<int> emptyBoxed = s_empty;
- IEnumerable<int> emptyDefaultBoxed = s_emptyDefault;
+ var array = source.ToImmutableArray();
- Assert.Throws<NullReferenceException>(() => s_emptyDefault.RemoveRange(emptyBoxed));
- Assert.Throws<NullReferenceException>(() => s_emptyDefault.RemoveRange(emptyDefaultBoxed));
- Assert.Throws<InvalidOperationException>(() => s_empty.RemoveRange(emptyDefaultBoxed));
- Assert.Equal(oneElementBoxed, oneElementBoxed);
+ Assert.Throws<ArgumentOutOfRangeException>("index", () => array.InsertRange(array.Length + 1, s_oneElement));
+ Assert.Throws<ArgumentOutOfRangeException>("index", () => array.InsertRange(-1, s_oneElement));
+
+ Assert.Throws<ArgumentOutOfRangeException>("index", () => array.InsertRange(array.Length + 1, (IEnumerable<int>)s_oneElement));
+ Assert.Throws<ArgumentOutOfRangeException>("index", () => array.InsertRange(-1, (IEnumerable<int>)s_oneElement));
}
- [Fact]
- public void InsertRangeDefaultStruct()
+ [Theory]
+ [MemberData(nameof(Int32EnumerableData))]
+ public void InsertRangeDefaultInvalid(IEnumerable<int> items)
{
- Assert.Throws<NullReferenceException>(() => s_emptyDefault.InsertRange(0, s_empty));
- Assert.Throws<NullReferenceException>(() => s_empty.InsertRange(0, s_emptyDefault));
- Assert.Throws<NullReferenceException>(() => s_emptyDefault.InsertRange(0, s_oneElement));
- Assert.Throws<NullReferenceException>(() => s_oneElement.InsertRange(0, s_emptyDefault));
+ var array = items.ToImmutableArray();
- IEnumerable<int> emptyBoxed = s_empty;
- IEnumerable<int> emptyDefaultBoxed = s_emptyDefault;
- IEnumerable<int> oneElementBoxed = s_oneElement;
- Assert.Throws<NullReferenceException>(() => s_emptyDefault.InsertRange(0, emptyBoxed));
- Assert.Throws<InvalidOperationException>(() => s_empty.InsertRange(0, emptyDefaultBoxed));
- Assert.Throws<NullReferenceException>(() => s_emptyDefault.InsertRange(0, oneElementBoxed));
- Assert.Throws<InvalidOperationException>(() => s_oneElement.InsertRange(0, emptyDefaultBoxed));
+ Assert.Throws<NullReferenceException>(() => s_emptyDefault.InsertRange(1, items));
+ Assert.Throws<NullReferenceException>(() => s_emptyDefault.InsertRange(-1, items));
+ Assert.Throws<NullReferenceException>(() => s_emptyDefault.InsertRange(0, items));
+
+ Assert.Throws<NullReferenceException>(() => s_emptyDefault.InsertRange(1, array));
+ Assert.Throws<NullReferenceException>(() => s_emptyDefault.InsertRange(-1, array));
+ Assert.Throws<NullReferenceException>(() => s_emptyDefault.InsertRange(0, array));
+
+ Assert.Throws<NullReferenceException>(() => array.InsertRange(1, s_emptyDefault));
+ Assert.Throws<NullReferenceException>(() => array.InsertRange(-1, s_emptyDefault));
+ Assert.Throws<NullReferenceException>(() => array.InsertRange(0, s_emptyDefault));
+
+ if (array.Length > 0)
+ {
+ Assert.Throws<InvalidOperationException>(() => array.InsertRange(1, (IEnumerable<int>)s_emptyDefault));
+ }
+
+ Assert.Throws<InvalidOperationException>(() => array.InsertRange(0, (IEnumerable<int>)s_emptyDefault));
}
- public static IEnumerable<object[]> InsertRangeLeft()
+ [Theory]
+ [MemberData(nameof(InsertRangeData))]
+ public void InsertRange(IEnumerable<int> source, int index, IEnumerable<int> items)
{
- yield return new object[] { new[] { 7, 1, 2, 3 }, s_manyElements, 0, new[] { 7 } };
- yield return new object[] { new[] { 7, 8, 1, 2, 3 }, s_manyElements, 0, new[] { 7, 8 } };
+ var array = source.ToImmutableArray();
+
+ Assert.All(ChangeType(items), it =>
+ {
+ var expected = source.Take(index)
+ .Concat(items)
+ .Concat(source.Skip(index));
+
+ Assert.Equal(expected, array.InsertRange(index, it)); // Enumerable overload
+ Assert.Equal(expected, array.InsertRange(index, it.ToImmutableArray())); // Struct overload
+
+ if (index == array.Length)
+ {
+ // Insertion at the end is equivalent to adding.
+ Assert.Equal(expected, array.InsertRange(index, it)); // Enumerable overload
+ Assert.Equal(expected, array.InsertRange(index, it.ToImmutableArray())); // Struct overload
+ }
+ });
}
- public static IEnumerable<object[]> InsertRangeMiddle()
+ public static IEnumerable<object[]> InsertRangeData()
{
- yield return new object[] { new[] { 1, 7, 2, 3 }, s_manyElements, 1, new[] { 7 } };
- yield return new object[] { new[] { 1, 7, 8, 2, 3 }, s_manyElements, 1, new[] { 7, 8 } };
+ yield return new object[] { s_manyElements, 0, new[] { 7 } };
+ yield return new object[] { s_manyElements, 0, new[] { 7, 8 } };
+ yield return new object[] { s_manyElements, 1, new[] { 7 } };
+ yield return new object[] { s_manyElements, 1, new[] { 7, 8 } };
+ yield return new object[] { s_manyElements, 3, new[] { 7 } };
+ yield return new object[] { s_manyElements, 3, new[] { 7, 8 } };
+ yield return new object[] { s_empty, 0, new[] { 1 } };
+ yield return new object[] { s_empty, 0, new[] { 2, 3, 4 } };
+ yield return new object[] { s_manyElements, 0, new int[0] };
+ yield return new object[] { s_empty, 0, s_empty };
+ yield return new object[] { s_empty, 0, s_oneElement };
+ yield return new object[] { s_oneElement, 0, s_empty };
+ yield return new object[] { s_empty, 0, new uint[] { 1, 2, 3 } };
+ yield return new object[] { s_manyElements, 0, new uint[] { 4, 5, 6 } };
+ yield return new object[] { s_manyElements, 3, new uint[] { 4, 5, 6 } };
}
- public static IEnumerable<object[]> InsertRangeRight()
+ [Theory]
+ [MemberData(nameof(RemoveAtData))]
+ public void RemoveAt(IEnumerable<int> source, int index)
{
- yield return new object[] { new[] { 1, 2, 3, 7 }, s_manyElements, 3, new[] { 7 } };
- yield return new object[] { new[] { 1, 2, 3, 7, 8 }, s_manyElements, 3, new[] { 7, 8 } };
+ var array = source.ToImmutableArray();
+ var expected = source.Take(index).Concat(source.Skip(index + 1));
+ Assert.Equal(expected, array.RemoveAt(index));
}
- public static IEnumerable<object[]> InsertRangeEmpty()
+ public static IEnumerable<object[]> RemoveAtData()
{
- yield return new object[] { new int[0], s_empty, 0, Enumerable.Empty<int>() };
- yield return new object[] { s_empty, s_empty, 0, Enumerable.Empty<int>() };
- yield return new object[] { new[] { 1 }, s_empty, 0, new[] { 1 } };
- yield return new object[] { new[] { 2, 3, 4 }, s_empty, 0, new[] { 2, 3, 4 } };
- yield return new object[] { new[] { 2, 3, 4 }, s_empty, 0, Enumerable.Range(2, 3) };
- yield return new object[] { s_manyElements, s_manyElements, 0, Enumerable.Empty<int>() };
+ yield return new object[] { s_oneElement, 0 };
+ yield return new object[] { s_manyElements, 0 };
+ yield return new object[] { s_manyElements, 1 };
+ yield return new object[] { s_manyElements, 2 };
}
- public static IEnumerable<object[]> InsertRangeIdentity()
+ [Theory]
+ [MemberData(nameof(Int32EnumerableData))]
+ public void RemoveAtInvalid(IEnumerable<int> source)
{
- yield return new object[] { s_empty, s_empty, 0, s_empty };
- yield return new object[] { s_oneElement, s_empty, 0, s_oneElement };
- yield return new object[] { s_oneElement, s_oneElement, 0, s_empty };
+ var array = source.ToImmutableArray();
+
+ Assert.Throws<ArgumentOutOfRangeException>("index", () => array.RemoveAt(-1));
+ Assert.Throws<ArgumentOutOfRangeException>("length", () => array.RemoveAt(array.Length));
+ Assert.Throws<ArgumentOutOfRangeException>("index", () => array.RemoveAt(array.Length + 1));
}
- public static IEnumerable<object[]> InsertRangeDifferentUnderlyingType()
+ [Theory]
+ [InlineData(-1, Skip = "#14961")]
+ [InlineData(0)]
+ [InlineData(1)]
+ public void RemoveAtDefaultInvalid(int index)
{
- yield return new object[] { new int[] { 1, 2, 3 }, s_empty, 0, (int[])(object)new uint[] { 1, 2, 3 } };
- yield return new object[] { new int[] { 4, 5, 6, 1, 2, 3 }, s_manyElements, 0, (int[])(object)new uint[] { 4, 5, 6 } };
- yield return new object[] { new int[] { 1, 2, 3, 4, 5, 6 }, s_manyElements, 3, (int[])(object)new uint[] { 4, 5, 6 } };
+ Assert.Throws<NullReferenceException>(() => s_emptyDefault.RemoveAt(index));
}
[Theory]
- [MemberData(nameof(InsertRangeLeft))]
- [MemberData(nameof(InsertRangeMiddle))]
- [MemberData(nameof(InsertRangeRight))]
- [MemberData(nameof(InsertRangeEmpty))]
- [MemberData(nameof(InsertRangeIdentity))]
- public void InsertRange(IEnumerable<int> expected, ImmutableArray<int> array, int index, IEnumerable<int> items)
+ [MemberData(nameof(RemoveData))]
+ public void Remove<T>(IEnumerable<T> source, T item, IEqualityComparer<T> comparer)
{
- // All of these functions should take an enumerable and produce another w/ the same contents.
- var identityTransforms = new List<Func<IEnumerable<int>, IEnumerable<int>>>
- {
- e => e,
- e => e.ToArray(), // Array
- e => e.ToList(), // List
- e => new LinkedList<int>(e), // Non-array, non-List, non-ImmutableArray IList
- e => e.ToImmutableArray(), // ImmutableArray
- e => e.Select(i => i), // Lazy enumerable
- e => new Queue<int>(e) // IReadOnlyCollection / non-generic ICollection
- };
+ var array = source.ToImmutableArray();
+
+ var comparerOrDefault = comparer ?? EqualityComparer<T>.Default;
+ var expected = source
+ .TakeWhile(x => !comparerOrDefault.Equals(x, item))
+ .Concat(source.SkipWhile(x => !comparerOrDefault.Equals(x, item)).Skip(1));
- foreach (var equivalentItems in identityTransforms.Select(t => t(items)))
+ Assert.Equal(expected, array.Remove(item, comparer));
+ Assert.Equal(expected, ((IImmutableList<T>)array).Remove(item, comparer));
+
+ if (comparer == null || comparer == EqualityComparer<T>.Default)
{
- Assert.Equal(expected, array.InsertRange(index, equivalentItems));
- Assert.Equal(expected, array.InsertRange(index, equivalentItems.ToImmutableArray())); // Call the ImmutableArray overload.
+ Assert.Equal(expected, array.Remove(item));
+ Assert.Equal(expected, ((IImmutableList<T>)array).Remove(item));
+ }
+ }
- if (index == array.Length) // Insertion @ the end should be equivalent to adding.
+ public static IEnumerable<object[]> RemoveData()
+ {
+ return SharedEqualityComparers<int>().SelectMany(comparer =>
+ new[]
{
- Assert.Equal(expected, array.AddRange(equivalentItems));
- Assert.Equal(expected, array.AddRange(equivalentItems.ToImmutableArray()));
- }
- }
+ new object[] { s_manyElements, 1, comparer },
+ new object[] { s_manyElements, 2, comparer },
+ new object[] { s_manyElements, 3, comparer },
+ new object[] { s_manyElements, 4, comparer },
+ new object[] { new int[0], 4, comparer },
+ new object[] { new int[] { 1, 4 }, 4, comparer },
+ new object[] { s_oneElement, 1, comparer }
+ });
}
[Fact]
- public void RemoveAt()
+ public void RemoveDefaultInvalid()
{
- Assert.Throws<ArgumentOutOfRangeException>("length", () => s_empty.RemoveAt(0));
- Assert.Throws<NullReferenceException>(() => s_emptyDefault.RemoveAt(0));
- Assert.Throws<ArgumentOutOfRangeException>("length", () => s_oneElement.RemoveAt(1));
- Assert.Throws<ArgumentOutOfRangeException>("index", () => s_empty.RemoveAt(-1));
+ Assert.All(SharedEqualityComparers<int>(), comparer =>
+ {
+ Assert.Throws<NullReferenceException>(() => s_emptyDefault.Remove(5));
+ Assert.Throws<NullReferenceException>(() => s_emptyDefault.Remove(5, comparer));
- Assert.Equal(new int[0], s_oneElement.RemoveAt(0));
- Assert.Equal(new[] { 2, 3 }, s_manyElements.RemoveAt(0));
- Assert.Equal(new[] { 1, 3 }, s_manyElements.RemoveAt(1));
- Assert.Equal(new[] { 1, 2 }, s_manyElements.RemoveAt(2));
+ Assert.Throws<InvalidOperationException>(() => ((IImmutableList<int>)s_emptyDefault).Remove(5));
+ Assert.Throws<InvalidOperationException>(() => ((IImmutableList<int>)s_emptyDefault).Remove(5, comparer));
+ });
}
- [Fact]
- public void Remove_NullEqualityComparer()
+ [Theory]
+ [MemberData(nameof(RemoveRangeIndexLengthData))]
+ public void RemoveRangeIndexLength(IEnumerable<int> source, int index, int length)
{
- var modified = s_manyElements.Remove(2, null);
- Assert.Equal(new[] { 1, 3 }, modified);
+ var array = source.ToImmutableArray();
+ var expected = source.Take(index).Concat(source.Skip(index + length));
+ Assert.Equal(expected, array.RemoveRange(index, length));
+ }
- // Try again through the explicit interface implementation.
- IImmutableList<int> boxedCollection = s_manyElements;
- var modified2 = boxedCollection.Remove(2, null);
- Assert.Equal(new[] { 1, 3 }, modified2);
+ public static IEnumerable<object[]> RemoveRangeIndexLengthData()
+ {
+ yield return new object[] { s_empty, 0, 0 };
+ yield return new object[] { s_oneElement, 1, 0 };
+ yield return new object[] { s_oneElement, 0, 1 };
+ yield return new object[] { s_oneElement, 0, 0 };
+ yield return new object[] { new[] { 1, 2, 3, 4 }, 0, 2 };
+ yield return new object[] { new[] { 1, 2, 3, 4 }, 1, 2 };
+ yield return new object[] { new[] { 1, 2, 3, 4 }, 2, 2 };
}
- [Fact]
- public void Remove()
+ [Theory]
+ [MemberData(nameof(Int32EnumerableData))]
+ public void RemoveRangeIndexLengthInvalid(IEnumerable<int> source)
{
- Assert.Throws<NullReferenceException>(() => s_emptyDefault.Remove(5));
- Assert.False(s_empty.Remove(5).IsDefault);
+ var array = source.ToImmutableArray();
- Assert.True(s_oneElement.Remove(1).IsEmpty);
- Assert.Equal(new[] { 2, 3 }, s_manyElements.Remove(1));
- Assert.Equal(new[] { 1, 3 }, s_manyElements.Remove(2));
- Assert.Equal(new[] { 1, 2 }, s_manyElements.Remove(3));
+ Assert.Throws<ArgumentOutOfRangeException>("index", () => array.RemoveRange(-1, 1));
+ Assert.Throws<ArgumentOutOfRangeException>("index", () => array.RemoveRange(array.Length + 1, 1));
+ Assert.Throws<ArgumentOutOfRangeException>("length", () => array.RemoveRange(0, -1));
+ Assert.Throws<ArgumentOutOfRangeException>("length", () => array.RemoveRange(0, array.Length + 1));
}
- [Fact]
- public void RemoveRange()
- {
- Assert.Equal(s_empty, s_empty.RemoveRange(0, 0));
- Assert.Throws<NullReferenceException>(() => s_emptyDefault.RemoveRange(0, 0));
- Assert.Throws<ArgumentOutOfRangeException>("index", () => s_emptyDefault.RemoveRange(-1, 0));
- Assert.Throws<NullReferenceException>(() => s_emptyDefault.RemoveRange(0, -1));
- Assert.Throws<ArgumentOutOfRangeException>("index", () => s_oneElement.RemoveRange(2, 0));
- Assert.Equal(s_oneElement, s_oneElement.RemoveRange(1, 0));
- Assert.Throws<ArgumentOutOfRangeException>("index", () => s_empty.RemoveRange(-1, 0));
- Assert.Throws<ArgumentOutOfRangeException>("length", () => s_oneElement.RemoveRange(0, 2));
- Assert.Throws<ArgumentOutOfRangeException>("length", () => s_oneElement.RemoveRange(0, -1));
-
- var fourElements = ImmutableArray.Create(1, 2, 3, 4);
- Assert.Equal(new int[0], s_oneElement.RemoveRange(0, 1));
- Assert.Equal(s_oneElement.ToArray(), s_oneElement.RemoveRange(0, 0));
- Assert.Equal(new[] { 3, 4 }, fourElements.RemoveRange(0, 2));
- Assert.Equal(new[] { 1, 4 }, fourElements.RemoveRange(1, 2));
- Assert.Equal(new[] { 1, 2 }, fourElements.RemoveRange(2, 2));
+ [Theory]
+ [InlineData(-1, 0, Skip = "#14961")]
+ [InlineData(0, -1)]
+ [InlineData(0, 0)]
+ [InlineData(1, -1)]
+ public void RemoveRangeIndexLengthDefaultInvalid(int index, int length)
+ {
+ Assert.Throws<NullReferenceException>(() => s_emptyDefault.RemoveRange(index, length));
}
- [Fact]
- public void RemoveRangeDefaultStruct()
+ [Theory]
+ [MemberData(nameof(RemoveRangeEnumerableData))]
+ public void RemoveRangeEnumerable(IEnumerable<int> source, IEnumerable<int> items, IEqualityComparer<int> comparer)
{
- Assert.Throws<NullReferenceException>(() => s_emptyDefault.RemoveRange(s_empty));
- Assert.Throws<ArgumentNullException>("items", () => s_empty.RemoveRange(s_emptyDefault));
- Assert.Throws<NullReferenceException>(() => s_emptyDefault.RemoveRange(s_oneElement));
- Assert.Throws<ArgumentNullException>("items", () => s_oneElement.RemoveRange(s_emptyDefault));
+ var array = source.ToImmutableArray();
+ IEnumerable<int> expected = items.Aggregate(
+ seed: source.ToImmutableArray(),
+ func: (a, i) => a.Remove(i, comparer));
+
+ Assert.Equal(expected, array.RemoveRange(items, comparer)); // Enumerable overload
+ Assert.Equal(expected, array.RemoveRange(items.ToImmutableArray(), comparer)); // Struct overload
+ Assert.Equal(expected, ((IImmutableList<int>)array).RemoveRange(items, comparer));
+
+ if (comparer == null || comparer == EqualityComparer<int>.Default)
+ {
+ Assert.Equal(expected, array.RemoveRange(items)); // Enumerable overload
+ Assert.Equal(expected, array.RemoveRange(items.ToImmutableArray())); // Struct overload
+ Assert.Equal(expected, ((IImmutableList<int>)array).RemoveRange(items));
+ }
+ }
+ public static IEnumerable<object[]> RemoveRangeEnumerableData()
+ {
+ return SharedEqualityComparers<int>().SelectMany(comparer =>
+ new[]
+ {
+ new object[] { s_empty, s_empty, comparer },
+ new object[] { s_empty, s_oneElement, comparer },
+ new object[] { s_oneElement, s_empty, comparer },
+ new object[] { new[] { 1, 2, 3 }, new[] { 2, 3, 4 }, comparer },
+ new object[] { Enumerable.Range(1, 5), Enumerable.Range(6, 5), comparer },
+ new object[] { new[] { 1, 2, 3 }, new[] { 2 }, comparer },
+ new object[] { s_empty, new int[] { }, comparer },
+ new object[] { new[] { 1, 2, 3 }, new[] { 2 }, comparer },
+ new object[] { new[] { 1, 2, 3 }, new[] { 1, 3, 5 }, comparer },
+ new object[] { Enumerable.Range(1, 10), new[] { 2, 4, 5, 7, 10 }, comparer },
+ new object[] { Enumerable.Range(1, 10), new[] { 1, 2, 4, 5, 7, 10 }, comparer },
+ new object[] { new[] { 1, 2, 3 }, new[] { 5 }, comparer },
+ new object[] { new[] { 1, 2, 2, 3 }, new[] { 2 }, comparer },
+ new object[] { new[] { 1, 2, 2, 3 }, new[] { 2, 2 }, comparer },
+ new object[] { new[] { 1, 2, 2, 3 }, new[] { 2, 2, 2 }, comparer },
+ new object[] { new[] { 1, 2, 3 }, new[] { 42 }, comparer },
+ new object[] { new[] { 1, 2, 3 }, new[] { 42, 42 }, comparer },
+ new object[] { new[] { 1, 2, 3 }, new[] { 42, 42, 42 }, comparer },
+ });
+ }
+
+ [Theory]
+ [MemberData(nameof(Int32EnumerableData))]
+ public void RemoveRangeEnumerableInvalid(IEnumerable<int> source)
+ {
+ var array = source.ToImmutableArray();
+
+ Assert.All(SharedEqualityComparers<int>(), comparer =>
+ {
+ // Enumerable overloads, lhs is default
+ Assert.Throws<NullReferenceException>(() => s_emptyDefault.RemoveRange(source));
+ Assert.Throws<NullReferenceException>(() => s_emptyDefault.RemoveRange(source, comparer));
+ Assert.Throws<InvalidOperationException>(() => ((IImmutableList<int>)s_emptyDefault).RemoveRange(source));
+ Assert.Throws<InvalidOperationException>(() => ((IImmutableList<int>)s_emptyDefault).RemoveRange(source, comparer));
+
+ // Struct overloads, lhs is default
+ Assert.Throws<NullReferenceException>(() => s_emptyDefault.RemoveRange(array));
+ Assert.Throws<NullReferenceException>(() => s_emptyDefault.RemoveRange(array, comparer));
+
+ // Struct overloads, rhs is default
+ Assert.Throws<ArgumentNullException>("items", () => array.RemoveRange(s_emptyDefault));
+ Assert.Throws<ArgumentNullException>("items", () => array.RemoveRange(s_emptyDefault, comparer));
+
+ // Enumerable overloads, rhs is default
+ Assert.Throws<InvalidOperationException>(() => array.RemoveRange((IEnumerable<int>)s_emptyDefault));
+ Assert.Throws<InvalidOperationException>(() => array.RemoveRange((IEnumerable<int>)s_emptyDefault, comparer));
+ Assert.Throws<InvalidOperationException>(() => ((IImmutableList<int>)array).RemoveRange(s_emptyDefault));
+ Assert.Throws<InvalidOperationException>(() => ((IImmutableList<int>)array).RemoveRange(s_emptyDefault, comparer));
+
+ // Struct overloads, both sides are default
+ Assert.Throws<ArgumentNullException>("items", () => s_emptyDefault.RemoveRange(s_emptyDefault));
+ Assert.Throws<ArgumentNullException>("items", () => s_emptyDefault.RemoveRange(s_emptyDefault, comparer));
+
+ // Enumerable overloads, both sides are default
+ Assert.Throws<NullReferenceException>(() => s_emptyDefault.RemoveRange((IEnumerable<int>)s_emptyDefault));
+ Assert.Throws<NullReferenceException>(() => s_emptyDefault.RemoveRange((IEnumerable<int>)s_emptyDefault, comparer));
+ Assert.Throws<InvalidOperationException>(() => ((IImmutableList<int>)s_emptyDefault).RemoveRange(s_emptyDefault));
+ Assert.Throws<InvalidOperationException>(() => ((IImmutableList<int>)s_emptyDefault).RemoveRange(s_emptyDefault, comparer));
+
+ // Enumerable overloads, rhs is null
+ Assert.Throws<ArgumentNullException>("items", () => array.RemoveRange(items: null));
+ Assert.Throws<ArgumentNullException>("items", () => array.RemoveRange(items: null, equalityComparer: comparer));
+ Assert.Throws<ArgumentNullException>("items", () => ((IImmutableList<int>)array).RemoveRange(items: null));
+ Assert.Throws<ArgumentNullException>("items", () => ((IImmutableList<int>)array).RemoveRange(items: null, equalityComparer: comparer));
+
+ // Enumerable overloads, lhs is default and rhs is null
+ Assert.Throws<NullReferenceException>(() => s_emptyDefault.RemoveRange(items: null));
+ Assert.Throws<NullReferenceException>(() => s_emptyDefault.RemoveRange(items: null, equalityComparer: comparer));
+ Assert.Throws<InvalidOperationException>(() => ((IImmutableList<int>)s_emptyDefault).RemoveRange(items: null));
+ Assert.Throws<InvalidOperationException>(() => ((IImmutableList<int>)s_emptyDefault).RemoveRange(items: null, equalityComparer: comparer));
+ });
+ }
+
+ [Fact]
+ public void RemoveRangeEnumerableRegression()
+ {
+ // Validates that a fixed bug in the inappropriate adding of the Empty
+ // singleton enumerator to the reusable instances bag does not regress.
+
+ IEnumerable<int> oneElementBoxed = s_oneElement;
IEnumerable<int> emptyBoxed = s_empty;
IEnumerable<int> emptyDefaultBoxed = s_emptyDefault;
- IEnumerable<int> oneElementBoxed = s_oneElement;
+
Assert.Throws<NullReferenceException>(() => s_emptyDefault.RemoveRange(emptyBoxed));
+ Assert.Throws<NullReferenceException>(() => s_emptyDefault.RemoveRange(emptyDefaultBoxed));
Assert.Throws<InvalidOperationException>(() => s_empty.RemoveRange(emptyDefaultBoxed));
- Assert.Throws<NullReferenceException>(() => s_emptyDefault.RemoveRange(oneElementBoxed));
- Assert.Throws<InvalidOperationException>(() => s_oneElement.RemoveRange(emptyDefaultBoxed));
+
+ Assert.Equal(oneElementBoxed, oneElementBoxed);
}
- [Fact]
- public void RemoveRangeNoOpIdentity()
+ [Theory]
+ [MemberData(nameof(RemoveAllData))]
+ public void RemoveAll(IEnumerable<int> source, Predicate<int> match)
{
- Assert.Equal(s_empty, s_empty.RemoveRange(s_empty));
- Assert.Equal(s_empty, s_empty.RemoveRange(s_oneElement)); // struct overload
- Assert.Equal(s_empty, s_empty.RemoveRange((IEnumerable<int>)s_oneElement)); // enumerable overload
- Assert.Equal(s_oneElement, s_oneElement.RemoveRange(s_empty));
+ var array = source.ToImmutableArray();
+ var expected = source.Where(i => !match(i));
+ Assert.Equal(expected, array.RemoveAll(match));
}
- [Fact]
- public void RemoveAll()
+ public static IEnumerable<object[]> RemoveAllData()
{
- Assert.Throws<ArgumentNullException>("match", () => s_oneElement.RemoveAll(null));
-
- var array = ImmutableArray.CreateRange(Enumerable.Range(1, 10));
- var removedEvens = array.RemoveAll(n => n % 2 == 0);
- var removedOdds = array.RemoveAll(n => n % 2 == 1);
- var removedAll = array.RemoveAll(n => true);
- var removedNone = array.RemoveAll(n => false);
+ yield return new object[] { Enumerable.Range(1, 10), new Predicate<int>(i => i % 2 == 0) };
+ yield return new object[] { Enumerable.Range(1, 10), new Predicate<int>(i => i % 2 == 1) };
+ yield return new object[] { Enumerable.Range(1, 10), new Predicate<int>(i => true) };
+ yield return new object[] { Enumerable.Range(1, 10), new Predicate<int>(i => false) };
+ yield return new object[] { s_empty, new Predicate<int>(i => false) };
+ }
- Assert.Equal(new[] { 1, 3, 5, 7, 9 }, removedEvens);
- Assert.Equal(new[] { 2, 4, 6, 8, 10 }, removedOdds);
- Assert.True(removedAll.IsEmpty);
- Assert.Equal(Enumerable.Range(1, 10), removedNone);
+ [Theory]
+ [MemberData(nameof(Int32EnumerableData))]
+ public void RemoveAllInvalid(IEnumerable<int> source)
+ {
+ var array = source.ToImmutableArray();
- Assert.False(s_empty.RemoveAll(n => false).IsDefault);
- Assert.Throws<NullReferenceException>(() => s_emptyDefault.RemoveAll(n => false));
+ Assert.Throws<ArgumentNullException>("match", () => array.RemoveAll(match: null));
}
[Fact]
- public void RemoveRange_EnumerableEqualityComparer_AcceptsNullEQ()
+ public void RemoveAllDefaultInvalid()
{
- var array = ImmutableArray.Create(1, 2, 3);
- var removed2eq = array.RemoveRange(new[] { 2 }, null);
- Assert.Equal(2, removed2eq.Length);
- Assert.Equal(new[] { 1, 3 }, removed2eq);
+ Assert.Throws<NullReferenceException>(() => s_emptyDefault.RemoveAll(i => false));
}
- [Fact]
- public void RemoveRangeEnumerableTest()
+ [Theory]
+ [MemberData(nameof(ReplaceData))]
+ public void Replace<T>(IEnumerable<T> source, T oldValue, T newValue, IEqualityComparer<T> comparer)
{
- var list = ImmutableArray.Create(1, 2, 3);
- Assert.Throws<ArgumentNullException>("items", () => list.RemoveRange(null));
- Assert.Throws<NullReferenceException>(() => s_emptyDefault.RemoveRange(new int[0]).IsDefault);
- Assert.False(s_empty.RemoveRange(new int[0]).IsDefault);
+ var array = source.ToImmutableArray();
- ImmutableArray<int> removed2 = list.RemoveRange(new[] { 2 });
- Assert.Equal(2, removed2.Length);
- Assert.Equal(new[] { 1, 3 }, removed2);
+ var comparerOrDefault = comparer ?? EqualityComparer<T>.Default;
+ var expected = source
+ .TakeWhile(x => !comparerOrDefault.Equals(x, oldValue))
+ .Concat(new[] { newValue })
+ .Concat(source.SkipWhile(x => !comparerOrDefault.Equals(x, oldValue)).Skip(1));
- ImmutableArray<int> removed13 = list.RemoveRange(new[] { 1, 3, 5 });
- Assert.Equal(1, removed13.Length);
- Assert.Equal(new[] { 2 }, removed13);
+ // If the comparer is a faulty implementation that says nothing is equal,
+ // an exception will be thrown here. Check that the comparer says the source contains
+ // this value first.
- Assert.Equal(new[] { 1, 3, 6, 8, 9 }, ImmutableArray.CreateRange(Enumerable.Range(1, 10)).RemoveRange(new[] { 2, 4, 5, 7, 10 }));
- Assert.Equal(new[] { 3, 6, 8, 9 }, ImmutableArray.CreateRange(Enumerable.Range(1, 10)).RemoveRange(new[] { 1, 2, 4, 5, 7, 10 }));
+ if (source.Contains(oldValue, comparer))
+ {
+ Assert.Equal(expected, array.Replace(oldValue, newValue, comparer));
+ Assert.Equal(expected, ((IImmutableList<T>)array).Replace(oldValue, newValue, comparer));
+ }
- Assert.Equal(list, list.RemoveRange(new[] { 5 }));
- Assert.Equal(ImmutableArray.Create<int>(), ImmutableArray.Create<int>().RemoveRange(new[] { 1 }));
+ if (comparer == null || comparer == EqualityComparer<T>.Default)
+ {
+ Assert.Equal(expected, array.Replace(oldValue, newValue));
+ Assert.Equal(expected, ((IImmutableList<T>)array).Replace(oldValue, newValue));
+ }
+ }
- var listWithDuplicates = ImmutableArray.Create(1, 2, 2, 3);
- Assert.Equal(new[] { 1, 2, 3 }, listWithDuplicates.RemoveRange(new[] { 2 }));
- Assert.Equal(new[] { 1, 3 }, listWithDuplicates.RemoveRange(new[] { 2, 2 }));
- Assert.Equal(new[] { 1, 3 }, listWithDuplicates.RemoveRange(new[] { 2, 2, 2 }));
+ public static IEnumerable<object[]> ReplaceData()
+ {
+ return SharedEqualityComparers<int>().SelectMany(comparer =>
+ new[]
+ {
+ new object[] { s_oneElement, 1, 5, comparer },
+ new object[] { s_manyElements, 1, 6, comparer },
+ new object[] { s_manyElements, 2, 6, comparer },
+ new object[] { s_manyElements, 3, 6, comparer },
+ new object[] { new[] { 1, 3, 3, 4 }, 3, 2, comparer },
+ new object[] { s_manyElements, 2, 10, comparer }
+ });
}
- [Fact]
- public void RemoveRangeImmutableArrayTest()
+ [Theory]
+ [MemberData(nameof(Int32EnumerableData))]
+ public void ReplaceInvalid(IEnumerable<int> source)
{
- var list = ImmutableArray.Create(1, 2, 3);
+ var array = source.ToImmutableArray();
+ int notContained = Enumerable.Range(0, int.MaxValue).First(i => !source.Contains(i));
- ImmutableArray<int> removed2 = list.RemoveRange(ImmutableArray.Create(2));
- Assert.Equal(2, removed2.Length);
- Assert.Equal(new[] { 1, 3 }, removed2);
+ Assert.All(SharedEqualityComparers<int>(), comparer =>
+ {
+ Assert.Throws<ArgumentException>("oldValue", () => array.Replace(notContained, 123));
+ Assert.Throws<ArgumentException>("oldValue", () => ((IImmutableList<int>)array).Replace(notContained, 123));
- ImmutableArray<int> removed13 = list.RemoveRange(ImmutableArray.Create(1, 3, 5));
- Assert.Equal(1, removed13.Length);
- Assert.Equal(new[] { 2 }, removed13);
+ // If the comparer is a faulty implementation that says everything is equal,
+ // an exception won't be thrown here. Check that the comparer says the source does
+ // not contain this value first.
+ if (!source.Contains(notContained, comparer))
+ {
+ Assert.Throws<ArgumentException>("oldValue", () => array.Replace(notContained, 123, comparer));
+ Assert.Throws<ArgumentException>("oldValue", () => ((IImmutableList<int>)array).Replace(notContained, 123, comparer));
+ }
+ });
+ }
- Assert.Equal(new[] { 1, 3, 6, 8, 9 }, ImmutableArray.CreateRange(Enumerable.Range(1, 10)).RemoveRange(ImmutableArray.Create(2, 4, 5, 7, 10)));
- Assert.Equal(new[] { 3, 6, 8, 9 }, ImmutableArray.CreateRange(Enumerable.Range(1, 10)).RemoveRange(ImmutableArray.Create(1, 2, 4, 5, 7, 10)));
+ [Fact]
+ public void ReplaceDefaultInvalid()
+ {
+ Assert.All(SharedEqualityComparers<int>(), comparer =>
+ {
+ // Uncomment when #14961 is fixed.
+ // Assert.Throws<NullReferenceException>(() => s_emptyDefault.Replace(123, 123));
+ // Assert.Throws<NullReferenceException>(() => s_emptyDefault.Replace(123, 123, comparer));
- Assert.Equal(list, list.RemoveRange(ImmutableArray.Create(5)));
- Assert.Equal(ImmutableArray.Create<int>(), ImmutableArray.Create<int>().RemoveRange(ImmutableArray.Create(1)));
+ Assert.Throws<InvalidOperationException>(() => ((IImmutableList<int>)s_emptyDefault).Replace(123, 123));
+ Assert.Throws<InvalidOperationException>(() => ((IImmutableList<int>)s_emptyDefault).Replace(123, 123, comparer));
+ });
+ }
- var listWithDuplicates = ImmutableArray.Create(1, 2, 2, 3);
- Assert.Equal(new[] { 1, 2, 3 }, listWithDuplicates.RemoveRange(ImmutableArray.Create(2)));
- Assert.Equal(new[] { 1, 3 }, listWithDuplicates.RemoveRange(ImmutableArray.Create(2, 2)));
- Assert.Equal(new[] { 1, 3 }, listWithDuplicates.RemoveRange(ImmutableArray.Create(2, 2, 2)));
+ [Theory]
+ [MemberData(nameof(SetItemData))]
+ public void SetItem<T>(IEnumerable<T> source, int index, T item)
+ {
+ var array = source.ToImmutableArray();
+ var expected = source.ToArray();
+ expected[index] = item;
+ Assert.Equal(expected, array.SetItem(index, item));
+ }
- Assert.Equal(new[] { 2, 3 }, list.RemoveRange(ImmutableArray.Create(42), EverythingEqual<int>.Default));
- Assert.Equal(new[] { 3 }, list.RemoveRange(ImmutableArray.Create(42, 42), EverythingEqual<int>.Default));
- Assert.Equal(new int[0], list.RemoveRange(ImmutableArray.Create(42, 42, 42), EverythingEqual<int>.Default));
+ public static IEnumerable<object[]> SetItemData()
+ {
+ yield return new object[] { s_oneElement, 0, 12345 };
+ yield return new object[] { s_manyElements, 0, 12345 };
+ yield return new object[] { s_manyElements, 1, 12345 };
+ yield return new object[] { s_manyElements, 2, 12345 };
}
- [Fact]
- public void Replace()
+ [Theory]
+ [MemberData(nameof(Int32EnumerableData))]
+ public void SetItemInvalid(IEnumerable<int> source)
{
- Assert.Equal(new[] { 5 }, s_oneElement.Replace(1, 5));
+ var array = source.ToImmutableArray();
- Assert.Equal(new[] { 6, 2, 3 }, s_manyElements.Replace(1, 6));
- Assert.Equal(new[] { 1, 6, 3 }, s_manyElements.Replace(2, 6));
- Assert.Equal(new[] { 1, 2, 6 }, s_manyElements.Replace(3, 6));
+ Assert.Throws<ArgumentOutOfRangeException>("index", () => array.SetItem(index: -1, item: 0));
+ Assert.Throws<ArgumentOutOfRangeException>("index", () => array.SetItem(index: array.Length, item: 0));
+ Assert.Throws<ArgumentOutOfRangeException>("index", () => array.SetItem(index: array.Length + 1, item: 0));
+ }
- Assert.Equal(new[] { 1, 2, 3, 4 }, ImmutableArray.Create(1, 3, 3, 4).Replace(3, 2));
+ [Theory]
+ [InlineData(-1, Skip = "#14961")]
+ [InlineData(0)]
+ [InlineData(1)]
+ public void SetItemDefaultInvalid(int index)
+ {
+ Assert.Throws<NullReferenceException>(() => s_emptyDefault.SetItem(index, item: 0));
}
- [Fact]
- public void ReplaceWithEqualityComparerTest()
+ [Theory]
+ [MemberData(nameof(CopyToData))]
+ public void CopyTo(IEnumerable<int> source, int sourceIndex, IEnumerable<int> destination, int destinationIndex, int length)
{
- var updatedArray = s_manyElements.Replace(2, 10, null);
- Assert.Equal(new[] { 1, 10, 3 }, updatedArray);
+ var array = source.ToImmutableArray();
- // Finally, try one last time using the interface implementation.
- IImmutableList<int> iface = s_manyElements;
- var updatedIFace = iface.Replace(2, 10, null);
- Assert.Equal(new[] { 1, 10, 3 }, updatedIFace);
+ // Take a snapshot of the destination array before calling CopyTo.
+ // Afterwards, ensure that the range we copied to was overwritten, and check
+ // that other areas were unaffected.
+
+ CopyAndInvoke(destination, destinationArray =>
+ {
+ array.CopyTo(sourceIndex, destinationArray, destinationIndex, length);
+
+ Assert.Equal(destination.Take(destinationIndex), destinationArray.Take(destinationIndex));
+ Assert.Equal(source.Skip(sourceIndex).Take(length), destinationArray.Skip(destinationIndex).Take(length));
+ Assert.Equal(destination.Skip(destinationIndex + length), destinationArray.Skip(destinationIndex + length));
+ });
+
+ if (sourceIndex == 0 && length == array.Length)
+ {
+ CopyAndInvoke(destination, destinationArray =>
+ {
+ array.CopyTo(destinationArray, destinationIndex);
+
+ Assert.Equal(destination.Take(destinationIndex), destinationArray.Take(destinationIndex));
+ Assert.Equal(source, destinationArray.Skip(destinationIndex).Take(array.Length));
+ Assert.Equal(destination.Skip(destinationIndex + array.Length), destinationArray.Skip(destinationIndex + array.Length));
+ });
+
+ if (destinationIndex == 0)
+ {
+ CopyAndInvoke(destination, destinationArray =>
+ {
+ array.CopyTo(destinationArray);
+
+ Assert.Equal(source, destinationArray.Take(array.Length));
+ Assert.Equal(destination.Skip(array.Length), destinationArray.Skip(array.Length));
+ });
+ }
+ }
}
- [Fact]
- public void ReplaceMissingThrowsTest()
+ private static void CopyAndInvoke<T>(IEnumerable<T> source, Action<T[]> action) => action(source.ToArray());
+
+ public static IEnumerable<object[]> CopyToData()
{
- Assert.Throws<ArgumentException>("oldValue", () => s_empty.Replace(5, 3));
+ yield return new object[] { s_manyElements, 0, new int[3], 0, 3 };
+ yield return new object[] { new[] { 1, 2, 3 }, 0, new int[4], 1, 3 };
+ yield return new object[] { new[] { 1, 2, 3 }, 0, Enumerable.Range(1, 4), 1, 3 };
+ yield return new object[] { new[] { 1, 2, 3 }, 1, new int[4], 3, 1 };
+ yield return new object[] { new[] { 1, 2, 3 }, 1, Enumerable.Range(1, 4), 3, 1 };
}
- [Fact]
- public void SetItem()
+ [Theory]
+ [MemberData(nameof(Int32EnumerableData))]
+ public void CopyToInvalid(IEnumerable<int> source)
{
- Assert.Throws<ArgumentOutOfRangeException>("index", () => s_empty.SetItem(0, 10));
- Assert.Throws<NullReferenceException>(() => s_emptyDefault.SetItem(0, 10));
- Assert.Throws<ArgumentOutOfRangeException>("index", () => s_oneElement.SetItem(1, 10));
- Assert.Throws<ArgumentOutOfRangeException>("index", () => s_empty.SetItem(-1, 10));
+ var array = source.ToImmutableArray();
- Assert.Equal(new[] { 12345 }, s_oneElement.SetItem(0, 12345));
- Assert.Equal(new[] { 12345, 2, 3 }, s_manyElements.SetItem(0, 12345));
- Assert.Equal(new[] { 1, 12345, 3 }, s_manyElements.SetItem(1, 12345));
- Assert.Equal(new[] { 1, 2, 12345 }, s_manyElements.SetItem(2, 12345));
- }
+ // ImmutableArray<T>.CopyTo defers to Array.Copy for argument validation, so
+ // the parameter names here come from Array.Copy.
- [Fact]
- public void CopyToArray()
- {
+ Assert.Throws<ArgumentNullException>("dest", () => array.CopyTo(null));
+ Assert.Throws<ArgumentNullException>("dest", () => array.CopyTo(null, 0));
+ Assert.Throws<ArgumentNullException>("dest", () => array.CopyTo(0, null, 0, 0));
+ Assert.Throws<ArgumentNullException>("dest", () => array.CopyTo(-1, null, -1, -1)); // The destination should be validated first.
+
+ Assert.Throws<ArgumentOutOfRangeException>("length", () => array.CopyTo(-1, new int[0], -1, -1));
+ Assert.Throws<ArgumentOutOfRangeException>("srcIndex", () => array.CopyTo(-1, new int[0], -1, 0));
+ Assert.Throws<ArgumentOutOfRangeException>("dstIndex", () => array.CopyTo(0, new int[0], -1, 0));
+
+ Assert.Throws<ArgumentException>(() => array.CopyTo(array.Length, new int[1], 0, 1)); // Not enough room in the source.
+
+ if (array.Length > 0)
{
- var target = new int[s_manyElements.Length];
- s_manyElements.CopyTo(target);
- Assert.Equal(target, s_manyElements);
+ Assert.Throws<ArgumentException>(() => array.CopyTo(array.Length - 1, new int[1], 1, 1)); // Not enough room in the destination.
}
+ }
+ [Theory]
+ [InlineData(0, 0)]
+ [InlineData(1, 0)]
+ [InlineData(2, 2)]
+ [InlineData(3, 1)]
+ public void CopyToDefaultInvalid(int destinationLength, int destinationIndex)
+ {
+ var destination = new int[destinationLength];
+
+ if (destinationIndex == 0)
{
- var target = new int[0];
- Assert.Throws<NullReferenceException>(() => s_emptyDefault.CopyTo(target));
+ Assert.Throws<NullReferenceException>(() => s_emptyDefault.CopyTo(destination));
}
+
+ Assert.Throws<NullReferenceException>(() => s_emptyDefault.CopyTo(destination, destinationIndex));
+ Assert.Throws<NullReferenceException>(() => s_emptyDefault.CopyTo(0, destination, destinationIndex, 0));
}
- [Fact]
- public void CopyToArrayInt()
+ [Theory]
+ [MemberData(nameof(IsDefaultOrEmptyData))]
+ public void IsDefault(StrongBox<IEnumerable<int>> box, bool isDefault, bool isEmpty)
{
- var source = ImmutableArray.Create(1, 2, 3);
- var target = new int[4];
- source.CopyTo(target, 1);
- Assert.Equal(new[] { 0, 1, 2, 3 }, target);
+ IEnumerable<int> source = box.Value;
+ var array = source.ToImmutableArray();
- Assert.Throws<NullReferenceException>(() => s_emptyDefault.CopyTo(target, 0));
- Assert.Throws<ArgumentNullException>("dest", () => source.CopyTo(null, 0));
- Assert.Throws<ArgumentOutOfRangeException>("dstIndex", () => source.CopyTo(target, -1));
- Assert.Throws<ArgumentException>("", () => source.CopyTo(target, 2));
+ Assert.Equal(isDefault, array.IsDefault);
}
- [Fact]
- public void CopyToIntArrayIntInt()
+ [Theory]
+ [MemberData(nameof(IsDefaultOrEmptyData))]
+ public void IsDefaultOrEmpty(StrongBox<IEnumerable<int>> box, bool isDefault, bool isEmpty)
{
- var source = ImmutableArray.Create(1, 2, 3);
- var target = new int[4];
- source.CopyTo(1, target, 3, 1);
- Assert.Equal(new[] { 0, 0, 0, 2 }, target);
+ IEnumerable<int> source = box.Value;
+ var array = source.ToImmutableArray();
+
+ Assert.Equal(isDefault || isEmpty, array.IsDefaultOrEmpty);
}
- [Fact]
- public void Concat()
+ public static IEnumerable<object[]> IsDefaultOrEmptyData()
{
- var array1 = ImmutableArray.Create(1, 2, 3);
- var array2 = ImmutableArray.Create(4, 5, 6);
+ // Once https://github.com/xunit/assert.xunit/pull/5 comes into corefx, all the StrongBox stuff can be removed.
- var concat = array1.Concat(array2);
- Assert.Equal(new[] { 1, 2, 3, 4, 5, 6 }, concat);
+ yield return new object[] { new StrongBox<IEnumerable<int>>(s_emptyDefault), true, false };
+ yield return new object[] { new StrongBox<IEnumerable<int>>(s_empty), false, true };
+ yield return new object[] { new StrongBox<IEnumerable<int>>(s_oneElement), false, false };
}
- /// <summary>
- /// Verifies reuse of the original array when concatenated to an empty array.
- /// </summary>
- [Fact]
- public void ConcatEdgeCases()
+ [Theory]
+ [MemberData(nameof(Int32EnumerableData))]
+ public void GetIndexer(IEnumerable<int> source)
{
- // empty arrays
- Assert.Equal(s_manyElements, s_manyElements.Concat(s_empty));
- Assert.Equal(s_manyElements, s_empty.Concat(s_manyElements));
+ var array = source.ToImmutableArray();
+
+ for (int i = 0; i < array.Length; i++)
+ {
+ int expected = source.ElementAt(i);
- // default arrays
- s_manyElements.Concat(s_emptyDefault);
- Assert.Throws<InvalidOperationException>(() => s_manyElements.Concat(s_emptyDefault).Count());
- Assert.Throws<InvalidOperationException>(() => s_emptyDefault.Concat(s_manyElements).Count());
+ Assert.Equal(expected, array[i]);
+ Assert.Equal(expected, ((IList)array)[i]);
+ Assert.Equal(expected, ((IList<int>)array)[i]);
+ Assert.Equal(expected, ((IReadOnlyList<int>)array)[i]);
+ }
}
- [Fact]
- public void IsDefault()
+ [Theory]
+ [MemberData(nameof(Int32EnumerableData))]
+ public void GetIndexerInvalid(IEnumerable<int> source)
{
- Assert.True(s_emptyDefault.IsDefault);
- Assert.False(s_empty.IsDefault);
- Assert.False(s_oneElement.IsDefault);
+ var array = source.ToImmutableArray();
+
+ Assert.Throws<IndexOutOfRangeException>(() => array[-1]);
+ Assert.Throws<IndexOutOfRangeException>(() => ((IList)array)[-1]);
+ Assert.Throws<IndexOutOfRangeException>(() => ((IList<int>)array)[-1]);
+ Assert.Throws<IndexOutOfRangeException>(() => ((IReadOnlyList<int>)array)[-1]);
+
+ Assert.Throws<IndexOutOfRangeException>(() => array[array.Length]);
+ Assert.Throws<IndexOutOfRangeException>(() => ((IList)array)[array.Length]);
+ Assert.Throws<IndexOutOfRangeException>(() => ((IList<int>)array)[array.Length]);
+ Assert.Throws<IndexOutOfRangeException>(() => ((IReadOnlyList<int>)array)[array.Length]);
+
+ Assert.Throws<IndexOutOfRangeException>(() => array[array.Length + 1]);
+ Assert.Throws<IndexOutOfRangeException>(() => ((IList)array)[array.Length + 1]);
+ Assert.Throws<IndexOutOfRangeException>(() => ((IList<int>)array)[array.Length + 1]);
+ Assert.Throws<IndexOutOfRangeException>(() => ((IReadOnlyList<int>)array)[array.Length + 1]);
}
- [Fact]
- public void IsDefaultOrEmpty()
+ [Theory]
+ [InlineData(-1)]
+ [InlineData(0)]
+ [InlineData(1)]
+ public void GetIndexerDefaultInvalid(int index)
{
- Assert.True(s_empty.IsDefaultOrEmpty);
- Assert.True(s_emptyDefault.IsDefaultOrEmpty);
- Assert.False(s_oneElement.IsDefaultOrEmpty);
+ Assert.Throws<NullReferenceException>(() => s_emptyDefault[index]);
+ Assert.Throws<InvalidOperationException>(() => ((IList)s_emptyDefault)[index]);
+ Assert.Throws<InvalidOperationException>(() => ((IList<int>)s_emptyDefault)[index]);
+ Assert.Throws<InvalidOperationException>(() => ((IReadOnlyList<int>)s_emptyDefault)[index]);
}
- [Fact]
- public void IndexGetter()
+ [Theory]
+ [MemberData(nameof(SortData))]
+ public void Sort<T>(IEnumerable<T> source, int index, int count, IComparer<T> comparer, T dummy)
{
- Assert.Equal(1, s_oneElement[0]);
- Assert.Equal(1, ((IReadOnlyList<int>)s_oneElement)[0]);
+ // Remove the dummy parameters once https://github.com/xunit/xunit/pull/965 makes it into corefx.
+
+ var array = source.ToImmutableArray();
+
+ var expected = source.ToArray();
+ Array.Sort(expected, index, count, comparer);
- Assert.Throws<IndexOutOfRangeException>(() => s_oneElement[1]);
- Assert.Throws<IndexOutOfRangeException>(() => s_oneElement[-1]);
+ Assert.Equal(expected, array.Sort(index, count, comparer));
+ Assert.Equal(source, array); // Make sure the original array is unaffected.
- Assert.Throws<NullReferenceException>(() => s_emptyDefault[0]);
- Assert.Throws<InvalidOperationException>(() => ((IList)s_emptyDefault)[0]);
- Assert.Throws<InvalidOperationException>(() => ((IList<int>)s_emptyDefault)[0]);
- Assert.Throws<InvalidOperationException>(() => ((IReadOnlyList<int>)s_emptyDefault)[0]);
+ if (index == 0 && count == array.Length)
+ {
+ Assert.Equal(expected, array.Sort(comparer));
+ Assert.Equal(source, array); // Make sure the original array is unaffected.
+
+ if (comparer != null)
+ {
+ Assert.Equal(expected, array.Sort(comparer.Compare));
+ Assert.Equal(source, array); // Make sure the original array is unaffected.
+ }
+
+ if (comparer == null || comparer == Comparer<T>.Default)
+ {
+ Assert.Equal(expected, array.Sort());
+ Assert.Equal(source, array); // Make sure the original array is unaffected.
+ }
+ }
}
- [Fact]
- public void Sort()
+ public static IEnumerable<object[]> SortData()
{
- var array = ImmutableArray.Create(2, 4, 1, 3);
- Assert.Equal(new[] { 1, 2, 3, 4 }, array.Sort());
- Assert.Equal(new[] { 2, 4, 1, 3 }, array); // original array unaffected.
+ return SharedComparers<int>().SelectMany(comparer =>
+ new[]
+ {
+ new object[] { new[] { 2, 4, 1, 3 }, 0, 4, comparer, 0 },
+ new object[] { new[] { 1 }, 0, 1, comparer, 0 },
+ new object[] { new int[0], 0, 0, comparer, 0 },
+ new object[] { new[] { 2, 4, 1, 3 }, 1, 2, comparer, 0 },
+ new object[] { new[] { 2, 4, 1, 3 }, 4, 0, comparer, 0 },
+ new object[] { new[] { "c", "B", "a" }, 0, 3, StringComparer.OrdinalIgnoreCase, string.Empty },
+ new object[] { new[] { "c", "B", "a" }, 0, 3, StringComparer.Ordinal, string.Empty },
+ new object[] { new[] { 1, 2, 3, 4, 6, 5, 7, 8, 9, 10 }, 4, 2, comparer, 0 }
+ });
}
[Theory]
- [InlineData(new int[] { 2, 4, 1, 3 }, new int[] { 4, 3, 2, 1 })]
- [InlineData(new int[] { 1 }, new int[] { 1 })]
- [InlineData(new int[0], new int[0])]
- public void Sort_Comparison(int[] items, int[] expected)
+ [MemberData(nameof(Int32EnumerableData))]
+ public void SortComparisonInvalid(IEnumerable<int> source)
{
- var array = ImmutableArray.Create(items);
- Assert.Equal(expected, array.Sort((x, y) => y.CompareTo(x)));
- Assert.Equal(items, array); // original array unaffected.
- }
+ var array = source.ToImmutableArray();
+ Assert.Throws<ArgumentNullException>("comparison", () => array.Sort(comparison: null));
+ }
- [Fact]
- public void Sort_NullComparison_Throws()
+ [Theory]
+ [MemberData(nameof(Int32EnumerableData))]
+ public void SortComparerInvalid(IEnumerable<int> source)
{
- Assert.Throws<ArgumentNullException>("comparison", () => ImmutableArray.Create<int>().Sort((Comparison<int>)null));
+ var array = source.ToImmutableArray();
+
+ Assert.Throws<ArgumentOutOfRangeException>("index", () => array.Sort(-1, -1, Comparer<int>.Default));
+ Assert.Throws<ArgumentOutOfRangeException>("index", () => array.Sort(-1, 0, Comparer<int>.Default));
+
+ Assert.Throws<ArgumentOutOfRangeException>("count", () => array.Sort(0, -1, Comparer<int>.Default));
+ Assert.Throws<ArgumentOutOfRangeException>("count", () => array.Sort(array.Length + 1, 0, Comparer<int>.Default));
+ Assert.Throws<ArgumentOutOfRangeException>("count", () => array.Sort(0, array.Length + 1, Comparer<int>.Default));
}
- [Fact]
- public void SortNullComparer()
+ [Theory]
+ [InlineData(-1, -1)]
+ [InlineData(0, 0)]
+ [InlineData(1, 1)]
+ public void SortDefaultInvalid(int index, int count)
{
- var array = ImmutableArray.Create(2, 4, 1, 3);
- Assert.Equal(new[] { 1, 2, 3, 4 }, array.Sort((IComparer<int>)null));
- Assert.Equal(new[] { 2, 1, 4, 3 }, array.Sort(1, 2, null));
- Assert.Equal(new[] { 2, 4, 1, 3 }, array); // original array unaffected.
+ Assert.All(SharedComparers<int>(), comparer =>
+ {
+ Assert.Throws<NullReferenceException>(() => s_emptyDefault.Sort());
+ Assert.Throws<NullReferenceException>(() => s_emptyDefault.Sort(comparer));
+ Assert.Throws<NullReferenceException>(() => s_emptyDefault.Sort(comparer.Compare));
+ Assert.Throws<NullReferenceException>(() => s_emptyDefault.Sort(index, count, comparer));
+ });
}
- [Fact]
- public void SortRange()
+ [Theory]
+ [MemberData(nameof(SortAlreadySortedData))]
+ public void SortAlreadySorted(IEnumerable<int> source, int index, int count, IComparer<int> comparer)
{
- var array = ImmutableArray.Create(2, 4, 1, 3);
- Assert.Throws<ArgumentOutOfRangeException>("index", () => array.Sort(-1, 2, Comparer<int>.Default));
- Assert.Throws<ArgumentOutOfRangeException>("count", () => array.Sort(1, -1, Comparer<int>.Default));
- Assert.Throws<ArgumentOutOfRangeException>("count", () => array.Sort(1, 4, Comparer<int>.Default));
- Assert.Equal(new int[] { 2, 4, 1, 3 }, array.Sort(array.Length, 0, Comparer<int>.Default));
- Assert.Equal(new[] { 2, 1, 4, 3 }, array.Sort(1, 2, Comparer<int>.Default));
+ // If ImmutableArray<T>.Sort is called when the array is already sorted,
+ // it should just return the original array rather than allocating a new one.
+
+ var array = source.ToImmutableArray();
+
+ Assert.True(array == array.Sort(index, count, comparer));
+
+ if (index == 0 && count == array.Length)
+ {
+ Assert.True(array == array.Sort(comparer));
+
+ if (comparer != null)
+ {
+ Assert.True(array == array.Sort(comparer.Compare));
+ }
+
+ if (comparer == null || comparer == Comparer<int>.Default)
+ {
+ Assert.True(array == array.Sort());
+ }
+ }
}
- [Fact]
- public void SortComparer()
+ public static IEnumerable<object[]> SortAlreadySortedData()
{
- var array = ImmutableArray.Create("c", "B", "a");
- Assert.Equal(new[] { "a", "B", "c" }, array.Sort(StringComparer.OrdinalIgnoreCase));
- Assert.Equal(new[] { "B", "a", "c" }, array.Sort(StringComparer.Ordinal));
+ yield return new object[] { new[] { 1, 2, 3, 4 }, 0, 4, null };
+ yield return new object[] { new[] { 1, 2, 3, 4, 6, 5, 7, 8, 9, 10 }, 0, 5, null };
+ yield return new object[] { new[] { 1, 2, 3, 4, 6, 5, 7, 8, 9, 10 }, 5, 5, null };
+
+ yield return new object[] { new[] { 1, 2, 3, 4 }, 0, 4, Comparer<int>.Default };
+ yield return new object[] { new[] { 1, 2, 3, 4, 6, 5, 7, 8, 9, 10 }, 0, 5, Comparer<int>.Default };
+ yield return new object[] { new[] { 1, 2, 3, 4, 6, 5, 7, 8, 9, 10 }, 5, 5, Comparer<int>.Default };
+
+ yield return new object[] { new[] { 1, 5, 2 }, 0, 3, Comparer<int>.Create((x, y) => 0) };
+ yield return new object[] { new[] { 1, 5, 2 }, 1, 2, Comparer<int>.Create((x, y) => 0) };
+ yield return new object[] { new[] { 1, 5, 2 }, 1, 1, Comparer<int>.Create((x, y) => 0) };
+ yield return new object[] { new[] { 1, 5, 2 }, 0, 2, Comparer<int>.Create((x, y) => 0) };
+ yield return new object[] { new[] { 1, 5, 2, 4 }, 1, 2, Comparer<int>.Create((x, y) => 0) };
}
- [Fact]
- public void SortPreservesArrayWhenAlreadySorted()
+ [Theory]
+ [MemberData(nameof(Int32EnumerableData))]
+ public void ToBuilder(IEnumerable<int> source)
{
- var sortedArray = ImmutableArray.Create(1, 2, 3, 4);
- Assert.Equal(sortedArray, sortedArray.Sort());
+ var array = source.ToImmutableArray();
+ ImmutableArray<int>.Builder builder = array.ToBuilder();
+
+ Assert.Equal(array, builder);
+ Assert.Equal(array.Length, builder.Count);
- var mostlySorted = ImmutableArray.Create(1, 2, 3, 4, 6, 5, 7, 8, 9, 10);
- Assert.Equal(mostlySorted, mostlySorted.Sort(0, 5, Comparer<int>.Default));
- Assert.Equal(mostlySorted, mostlySorted.Sort(5, 5, Comparer<int>.Default));
- Assert.Equal(Enumerable.Range(1, 10), mostlySorted.Sort(4, 2, Comparer<int>.Default));
+ // Make sure that mutating the builder doesn't change the ImmutableArray.
+ if (array.Length > 0)
+ {
+ builder[0] += 1;
+ Assert.Equal(source.First(), array[0]);
+ Assert.Equal(source.First() + 1, builder[0]);
+ builder[0] -= 1;
+ }
+
+ builder.Add(int.MinValue);
+ Assert.Equal(array.Length + 1, builder.Count);
+ Assert.Equal(array.Add(int.MinValue), builder);
}
- [Fact]
- public void ToBuilder()
+ [Theory]
+ [MemberData(nameof(IStructuralEquatableEqualsData))]
+ [MemberData(nameof(IStructuralEquatableEqualsNullComparerData))]
+ public void IStructuralEquatableEquals(StrongBox<IEnumerable<int>> firstBox, StrongBox<object> secondBox, IEqualityComparer comparer, bool expected)
{
- Assert.Equal(0, s_empty.ToBuilder().Count);
- Assert.Throws<NullReferenceException>(() => s_emptyDefault.ToBuilder().Count);
+ // Once https://github.com/xunit/assert.xunit/pull/5 comes into corefx, all the StrongBox stuff can be removed.
- var builder = s_oneElement.ToBuilder();
- Assert.Equal(s_oneElement.ToArray(), builder);
+ ImmutableArray<int> first = firstBox.Value.ToImmutableArray();
+ object second = secondBox.Value;
- builder = s_manyElements.ToBuilder();
- Assert.Equal(s_manyElements.ToArray(), builder);
+ Assert.Equal(expected, ((IStructuralEquatable)first).Equals(second, comparer));
- // Make sure that changing the builder doesn't change the original immutable array.
- int expected = s_manyElements[0];
- builder[0] = expected + 1;
- Assert.Equal(expected, s_manyElements[0]);
- Assert.Equal(expected + 1, builder[0]);
+ if (!first.IsDefault)
+ {
+ int[] firstArray = first.ToArray();
+ Assert.Equal(!IsImmutableArray(second) && expected, ((IStructuralEquatable)firstArray).Equals(second, comparer));
+
+ var secondEquatable = second as IStructuralEquatable;
+ if (secondEquatable != null)
+ {
+ Assert.Equal(expected, secondEquatable.Equals(firstArray, comparer));
+ }
+ }
}
- [Fact]
- public void StructuralEquatableEqualsDefault()
+ public static IEnumerable<object[]> IStructuralEquatableEqualsData()
{
- IStructuralEquatable eq = s_emptyDefault;
+ // The comparers here must consider two arrays structurally equal if the default comparer does.
+ var optimisticComparers = new IEqualityComparer[]
+ {
+ EqualityComparer<int>.Default,
+ new DelegateEqualityComparer<object>(objectEquals: (x, y) => true)
+ };
- Assert.True(eq.Equals(s_emptyDefault, EqualityComparer<int>.Default));
- Assert.False(eq.Equals(s_empty, EqualityComparer<int>.Default));
- Assert.False(eq.Equals(s_oneElement, EqualityComparer<int>.Default));
- }
+ // The comparers here must not consider two arrays structurally equal if the default comparer doesn't.
+ var pessimisticComparers = new IEqualityComparer[]
+ {
+ EqualityComparer<int>.Default,
+ new DelegateEqualityComparer<object>(objectEquals: (x, y) => false)
+ };
- [Fact]
- public void StructuralEquatableEquals()
- {
- IStructuralEquatable array = new int[3] { 1, 2, 3 };
- IStructuralEquatable immArray = ImmutableArray.Create(1, 2, 3);
-
- var otherArray = new object[] { 1, 2, 3 };
- var otherImmArray = ImmutableArray.Create(otherArray);
- var unequalArray = new int[] { 1, 2, 4 };
- var unequalImmArray = ImmutableArray.Create(unequalArray);
- var unrelatedArray = new string[3];
- var unrelatedImmArray = ImmutableArray.Create(unrelatedArray);
- var otherList = new List<int> { 1, 2, 3 };
- Assert.Equal(array.Equals(otherArray, EqualityComparer<int>.Default), immArray.Equals(otherImmArray, EqualityComparer<int>.Default));
- Assert.Equal(array.Equals(otherList, EqualityComparer<int>.Default), immArray.Equals(otherList, EqualityComparer<int>.Default));
- Assert.Equal(array.Equals(unrelatedArray, EverythingEqual<object>.Default), immArray.Equals(unrelatedImmArray, EverythingEqual<object>.Default));
- Assert.Equal(array.Equals(new object(), EqualityComparer<int>.Default), immArray.Equals(new object(), EqualityComparer<int>.Default));
- Assert.Equal(array.Equals(null, EqualityComparer<int>.Default), immArray.Equals(null, EqualityComparer<int>.Default));
- Assert.Equal(array.Equals(unequalArray, EqualityComparer<int>.Default), immArray.Equals(unequalImmArray, EqualityComparer<int>.Default));
+ foreach (IEqualityComparer comparer in optimisticComparers)
+ {
+ yield return new object[] { new StrongBox<IEnumerable<int>>(s_empty), new StrongBox<object>(s_empty), comparer, true };
+ yield return new object[] { new StrongBox<IEnumerable<int>>(s_emptyDefault), new StrongBox<object>(s_emptyDefault), comparer, true };
+ yield return new object[] { new StrongBox<IEnumerable<int>>(new[] { 1, 2, 3 }), new StrongBox<object>(new[] { 1, 2, 3 }), comparer, true };
+ yield return new object[] { new StrongBox<IEnumerable<int>>(new[] { 1, 2, 3 }), new StrongBox<object>(ImmutableArray.Create(1, 2, 3)), comparer, true };
+ yield return new object[] { new StrongBox<IEnumerable<int>>(new[] { 1, 2, 3 }), new StrongBox<object>(new object[] { 1, 2, 3 }), comparer, true };
+ yield return new object[] { new StrongBox<IEnumerable<int>>(new[] { 1, 2, 3 }), new StrongBox<object>(ImmutableArray.Create<object>(1, 2, 3)), comparer, true };
+ }
+
+ foreach (IEqualityComparer comparer in pessimisticComparers)
+ {
+ yield return new object[] { new StrongBox<IEnumerable<int>>(s_emptyDefault), new StrongBox<object>(s_empty), comparer, false };
+ yield return new object[] { new StrongBox<IEnumerable<int>>(s_emptyDefault), new StrongBox<object>(s_oneElement), comparer, false };
+ yield return new object[] { new StrongBox<IEnumerable<int>>(new[] { 1, 2, 3 }), new StrongBox<object>(new List<int> { 1, 2, 3 }), comparer, false };
+ yield return new object[] { new StrongBox<IEnumerable<int>>(new[] { 1, 2, 3 }), new StrongBox<object>(new object()), comparer, false };
+ yield return new object[] { new StrongBox<IEnumerable<int>>(new[] { 1, 2, 3 }), new StrongBox<object>(null), comparer, false };
+ yield return new object[] { new StrongBox<IEnumerable<int>>(new[] { 1, 2, 3 }), new StrongBox<object>(new[] { 1, 2, 4 }), comparer, false };
+ yield return new object[] { new StrongBox<IEnumerable<int>>(new[] { 1, 2, 3 }), new StrongBox<object>(ImmutableArray.Create(1, 2, 4)), comparer, false };
+ yield return new object[] { new StrongBox<IEnumerable<int>>(new[] { 1, 2, 3 }), new StrongBox<object>(new string[3]), comparer, false };
+ yield return new object[] { new StrongBox<IEnumerable<int>>(new[] { 1, 2, 3 }), new StrongBox<object>(ImmutableArray.Create(new string[3])), comparer, false };
+ }
}
- [Fact]
- public void StructuralEquatableEqualsArrayInterop()
+ public static IEnumerable<object[]> IStructuralEquatableEqualsNullComparerData()
{
- IStructuralEquatable array = new int[3] { 1, 2, 3 };
- IStructuralEquatable immArray = ImmutableArray.Create(1, 2, 3);
- var unequalArray = new int[] { 1, 2, 4 };
+ // Unlike other methods on ImmutableArray, null comparers are invalid inputs for IStructuralEquatable.Equals.
+ // However, it will not throw for a null comparer if the array is default and `other` is an ImmutableArray, or
+ // if Array's IStructuralEquatable.Equals implementation (which it calls under the cover) short-circuits before
+ // trying to use the comparer.
+
+ yield return new object[] { new StrongBox<IEnumerable<int>>(s_emptyDefault), new StrongBox<object>(s_emptyDefault), null, true };
+ yield return new object[] { new StrongBox<IEnumerable<int>>(s_emptyDefault), new StrongBox<object>(ImmutableArray.Create(1, 2, 3)), null, false };
- Assert.True(immArray.Equals(array, EqualityComparer<int>.Default));
- Assert.False(immArray.Equals(unequalArray, EqualityComparer<int>.Default));
+ yield return new object[] { new StrongBox<IEnumerable<int>>(new int[0]), new StrongBox<object>(null), null, false }; // Array short-circuits because `other` is null
+ yield return new object[] { new StrongBox<IEnumerable<int>>(s_empty), new StrongBox<object>(s_empty), null, true }; // Array short-circuits because the arrays are reference-equal
+ yield return new object[] { new StrongBox<IEnumerable<int>>(new int[0]), new StrongBox<object>(new List<int>()), null, false }; // Array short-circuits because `other` is not an array
+ yield return new object[] { new StrongBox<IEnumerable<int>>(new int[0]), new StrongBox<object>(new int[1]), null, false }; // Array short-circuits because `other.Length` isn't equal
+ yield return new object[] { new StrongBox<IEnumerable<int>>(new int[0]), new StrongBox<object>(new int[0]), null, true }; // For zero-element arrays, Array doesn't have to use the comparer
}
[Fact]
- public void IStructuralEquatable_Equals_NullComparerNonNullUnderlyingArray_ThrowsNullReferenceException()
+ public void IStructuralEquatableEqualsNullComparerInvalid()
{
- // This was not fixed for compatability reasons. See #13410
- IStructuralEquatable equatable = ImmutableArray.Create(1, 2, 3);
-
- Assert.True(equatable.Equals(equatable, null));
- Assert.Throws<NullReferenceException>(() => equatable.Equals(ImmutableArray.Create(1, 2, 3), null));
- Assert.False(equatable.Equals(new ImmutableArray<int>(), null));
- Assert.False(equatable.Equals(null, null));
+ // This was not fixed for compatability reasons. See https://github.com/dotnet/corefx/issues/13410
+ Assert.Throws<NullReferenceException>(() => ((IStructuralEquatable)ImmutableArray.Create(1, 2, 3)).Equals(ImmutableArray.Create(1, 2, 3), comparer: null));
+ Assert.Throws<NullReferenceException>(() => ((IStructuralEquatable)s_emptyDefault).Equals(other: null, comparer: null));
}
- [Fact]
- public void IStructuralEquatable_Equals_NullComparerNullUnderlyingArray_Works()
+ [Theory]
+ [MemberData(nameof(IStructuralEquatableGetHashCodeData))]
+ public void IStructuralEquatableGetHashCode(IEnumerable<int> source, IEqualityComparer comparer)
{
- IStructuralEquatable equatable = new ImmutableArray<int>();
- Assert.True(equatable.Equals(equatable, null));
- Assert.False(equatable.Equals(ImmutableArray.Create(1, 2, 3), null));
- Assert.True(equatable.Equals(new ImmutableArray<int>(), null));
- Assert.Throws<NullReferenceException>(() => equatable.Equals(null, null));
+ var array = source.ToImmutableArray();
+ int expected = ((IStructuralEquatable)source.ToArray()).GetHashCode(comparer);
+ Assert.Equal(expected, ((IStructuralEquatable)array).GetHashCode(comparer));
}
- [Fact]
- public void StructuralEquatableGetHashCodeDefault()
+ public static IEnumerable<object[]> IStructuralEquatableGetHashCodeData()
{
- IStructuralEquatable defaultImmArray = s_emptyDefault;
- Assert.Equal(0, defaultImmArray.GetHashCode(EqualityComparer<int>.Default));
+ var enumerables = Int32EnumerableData()
+ .Select(array => array[0])
+ .Cast<IEnumerable<int>>();
+
+ return SharedComparers<int>()
+ .OfType<IEqualityComparer>()
+ .Except(new IEqualityComparer[] { null })
+ .SelectMany(comparer => enumerables.Select(enumerable => new object[] { enumerable, comparer }));
}
[Fact]
- public void StructuralEquatableGetHashCode()
+ public void IStructuralEquatableGetHashCodeDefault()
{
- IStructuralEquatable emptyArray = new int[0];
- IStructuralEquatable emptyImmArray = s_empty;
- IStructuralEquatable array = new int[3] { 1, 2, 3 };
- IStructuralEquatable immArray = ImmutableArray.Create(1, 2, 3);
+ Assert.All(SharedComparers<int>().OfType<IEqualityComparer>(), comparer =>
+ {
+ // A default ImmutableArray should always hash to the same value, regardless of comparer.
+ // This includes null, which is included in the set of shared comparers.
+ Assert.Equal(0, ((IStructuralEquatable)s_emptyDefault).GetHashCode(comparer));
+ });
+ }
- Assert.Equal(emptyArray.GetHashCode(EqualityComparer<int>.Default), emptyImmArray.GetHashCode(EqualityComparer<int>.Default));
- Assert.Equal(array.GetHashCode(EqualityComparer<int>.Default), immArray.GetHashCode(EqualityComparer<int>.Default));
- Assert.Equal(array.GetHashCode(EverythingEqual<int>.Default), immArray.GetHashCode(EverythingEqual<int>.Default));
+ [Theory]
+ [MemberData(nameof(Int32EnumerableData))]
+ public void IStructuralEquatableGetHashCodeNullComparerNonNullUnderlyingArrayInvalid(IEnumerable<int> source)
+ {
+ var array = source.ToImmutableArray();
+ Assert.Throws<ArgumentNullException>("comparer", () => ((IStructuralEquatable)array).GetHashCode(comparer: null));
}
[Fact]
- public void IStructuralEquatable_GetHashCode_NullComparerNonNullUnderlyingArray_ThrowsArgumentNullException()
+ public void IStructuralComparableCompareToDefaultAndDefault()
{
- IStructuralEquatable equatable = ImmutableArray.Create(1, 2, 3);
- Assert.Throws<ArgumentNullException>(() => equatable.GetHashCode(null));
+ Assert.All(SharedComparers<int>().OfType<IComparer>(), comparer =>
+ {
+ // Default ImmutableArrays are always considered the same as other default ImmutableArrays, no matter
+ // what the comparer is. (Even if the comparer is null.)
+ Assert.Equal(0, ((IStructuralComparable)s_emptyDefault).CompareTo(s_emptyDefault, comparer));
+ });
}
- [Fact]
- public void IStructuralEquatable_GetHashCode_NullComparerNullUnderlyingArray_Works()
+ [Theory]
+ [MemberData(nameof(Int32EnumerableData))]
+ public void IStructuralComparableCompareToDefaultAndNonDefaultInvalid(IEnumerable<int> source)
{
- IStructuralEquatable equatable = new ImmutableArray<int>();
- Assert.Equal(0, equatable.GetHashCode(null));
+ object other = source.ToImmutableArray();
+ var comparers = SharedComparers<int>().OfType<IComparer>().Except(new IComparer[] { null });
+
+ Assert.All(comparers, comparer =>
+ {
+ // CompareTo should throw if the arrays are of different lengths. The default ImmutableArray is considered to have
+ // a different length from every other array, including empty ones.
+ Assert.Throws<ArgumentException>("other", () => ((IStructuralComparable)s_emptyDefault).CompareTo(other, comparer));
+ Assert.Throws<ArgumentException>("other", () => ((IStructuralComparable)other).CompareTo(s_emptyDefault, comparer));
+ });
}
- [Fact]
- public void StructuralComparableDefault()
+ [Theory]
+ [MemberData(nameof(IStructuralComparableCompareToNullComparerArgumentInvalidData))]
+ public void IStructuralComparableCompareToNullComparerArgumentInvalid(IEnumerable<int> source, object other)
{
- IStructuralComparable def = s_emptyDefault;
- IStructuralComparable mt = s_empty;
+ // Because of https://github.com/xunit/assert.xunit/pull/5 we cannot pass in a default ImmutableArray directly,
+ // so a null source is used as a sentinel value to represent that here. Once that change makes it into corefx,
+ // just pass in a default array from the MemberData.
- // default to default is fine, and should be seen as equal.
- Assert.Equal(0, def.CompareTo(s_emptyDefault, Comparer<int>.Default));
+ var array = source?.ToImmutableArray() ?? s_emptyDefault;
+ Assert.Throws<ArgumentException>("other", () => ((IStructuralComparable)array).CompareTo(other, comparer: null));
- // default to empty and vice versa should throw, on the basis that
- // arrays compared that are of different lengths throw. Empty vs. default aren't really compatible.
- Assert.Throws<ArgumentException>("other", () => def.CompareTo(s_empty, Comparer<int>.Default));
- Assert.Throws<ArgumentException>("other", () => mt.CompareTo(s_emptyDefault, Comparer<int>.Default));
+ if (other is Array || IsImmutableArray(other))
+ {
+ Assert.Throws<ArgumentException>("other", () => ((IStructuralComparable)other).CompareTo(array, comparer: null));
+ }
}
- [Fact]
- public void StructuralComparable()
+ public static IEnumerable<object[]> IStructuralComparableCompareToNullComparerArgumentInvalidData()
{
- IStructuralComparable array = new int[3] { 1, 2, 3 };
- IStructuralComparable equalArray = new int[3] { 1, 2, 3 };
- IStructuralComparable immArray = ImmutableArray.Create((int[])array);
- IStructuralComparable equalImmArray = ImmutableArray.Create((int[])equalArray);
+ yield return new object[] { null, null };
+ yield return new object[] { null, ImmutableArray.Create(1, 2, 3) };
+ yield return new object[] { new[] { 1, 2, 3 }, null };
+ }
- IStructuralComparable longerArray = new int[] { 1, 2, 3, 4 };
- IStructuralComparable longerImmArray = ImmutableArray.Create((int[])longerArray);
+ [Theory]
+ [MemberData(nameof(IStructuralComparableCompareToNullComparerNullReferenceInvalidData))]
+ public void IStructuralComparableCompareToNullComparerNullReferenceInvalid(IEnumerable<int> source, object other)
+ {
+ var array = source.ToImmutableArray();
+ Assert.Throws<NullReferenceException>(() => ((IStructuralComparable)array).CompareTo(other, comparer: null));
- Assert.Equal(array.CompareTo(equalArray, Comparer<int>.Default), immArray.CompareTo(equalImmArray, Comparer<int>.Default));
+ if (other == null)
+ {
+ Assert.Throws<NullReferenceException>(() => ((IStructuralComparable)array).CompareTo(s_emptyDefault, comparer: null));
+ }
+ }
- Assert.Throws<ArgumentException>("other", () => array.CompareTo(longerArray, Comparer<int>.Default));
- Assert.Throws<ArgumentException>("other", () => immArray.CompareTo(longerImmArray, Comparer<int>.Default));
+ public static IEnumerable<object[]> IStructuralComparableCompareToNullComparerNullReferenceInvalidData()
+ {
+ // This was not fixed for compatability reasons. See https://github.com/dotnet/corefx/issues/13410
+ yield return new object[] { new[] { 1, 2, 3 }, new[] { 1, 2, 3 } };
+ yield return new object[] { new[] { 1, 2, 3 }, ImmutableArray.Create(1, 2, 3) };
+ // Cache this into a local so the comparands are reference-equal.
+ var oneTwoThree = ImmutableArray.Create(1, 2, 3);
+ yield return new object[] { oneTwoThree, oneTwoThree };
+ }
- var list = new List<int> { 1, 2, 3 };
- Assert.Throws<ArgumentException>("other", () => array.CompareTo(list, Comparer<int>.Default));
- Assert.Throws<ArgumentException>("other", () => immArray.CompareTo(list, Comparer<int>.Default));
+ [Theory]
+ [MemberData(nameof(IStructuralComparableCompareToData))]
+ public void IStructuralComparableCompareTo(IEnumerable<int> source, object other, IComparer comparer, int expected)
+ {
+ var array = source?.ToImmutableArray() ?? s_emptyDefault;
+ Assert.Equal(expected, ((IStructuralComparable)array).CompareTo(other ?? s_emptyDefault, comparer));
+
+ if (other is Array)
+ {
+ Assert.Equal(expected, ((IStructuralComparable)source.ToArray()).CompareTo(other ?? s_emptyDefault, comparer));
+ }
}
- [Fact]
- public void StructuralComparableArrayInterop()
+ public static IEnumerable<object[]> IStructuralComparableCompareToData()
{
- IStructuralComparable array = new int[3] { 1, 2, 3 };
- IStructuralComparable equalArray = new int[3] { 1, 2, 3 };
- IStructuralComparable immArray = ImmutableArray.Create((int[])array);
- IStructuralComparable equalImmArray = ImmutableArray.Create((int[])equalArray);
+ yield return new object[] { new[] { 1, 2, 3 }, new[] { 1, 2, 3 }, Comparer<int>.Default, 0 };
+ yield return new object[] { new[] { 1, 2, 3 }, new[] { 1, 2, 3 }, Comparer<object>.Default, 0 };
+
+ yield return new object[] { new[] { 1, 2, 3 }, ImmutableArray.Create(1, 2, 3), Comparer<int>.Default, 0 };
+ yield return new object[] { new[] { 1, 2, 3 }, ImmutableArray.Create(1, 2, 3), Comparer<object>.Default, 0 };
- Assert.Equal(array.CompareTo(equalArray, Comparer<int>.Default), immArray.CompareTo(equalArray, Comparer<int>.Default));
+ // The comparands are the same instance, so Array can short-circuit.
+ yield return new object[] { s_empty, s_empty, Comparer<int>.Default, 0 };
+ yield return new object[] { s_empty, s_empty, Comparer<object>.Default, 0 };
+
+ // Normally, a null comparer is an invalid input. However, if both comparands are default ImmutableArrays
+ // then CompareTo will short-circuit before it validates the comparer.
+ yield return new object[] { null, null, null, 0 };
}
- [Fact]
- public void IStructuralComparable_NullComparerNonNullUnderlyingArray_ThrowsNullReferenceException()
+ [Theory]
+ [MemberData(nameof(IStructuralComparableCompareToInvalidData))]
+ public void IStructuralComparableCompareToInvalid(IEnumerable<int> source, object other, IComparer comparer)
{
- // This was not fixed for compatability reasons. See #13410
- IStructuralComparable comparable = ImmutableArray.Create(1, 2, 3);
- Assert.Throws<NullReferenceException>(() => comparable.CompareTo(comparable, null));
- Assert.Throws<NullReferenceException>(() => comparable.CompareTo(ImmutableArray.Create(1, 2, 3), null));
- Assert.Throws<ArgumentException>("other", () => comparable.CompareTo(new ImmutableArray<int>(), null));
+ var array = source.ToImmutableArray();
+
+ Assert.Throws<ArgumentException>("other", () => ((IStructuralComparable)array).CompareTo(other, comparer));
+ Assert.Throws<ArgumentException>("other", () => ((IStructuralComparable)source.ToArray()).CompareTo(other, comparer));
+
+ if (other is Array || IsImmutableArray(other))
+ {
+ Assert.Throws<ArgumentException>("other", () => ((IStructuralComparable)other).CompareTo(array, comparer));
+ Assert.Throws<ArgumentException>("other", () => ((IStructuralComparable)other).CompareTo(source.ToArray(), comparer));
+ }
}
- [Fact]
- public void IStructuralComparable_NullComparerNullUnderlyingArray_Works()
+ public static IEnumerable<object[]> IStructuralComparableCompareToInvalidData()
{
- IStructuralComparable comparable = new ImmutableArray<int>();
- Assert.Equal(0, comparable.CompareTo(comparable, null));
- Assert.Throws<ArgumentException>("other", () => comparable.CompareTo(null, null));
- Assert.Throws<ArgumentException>("other", () => comparable.CompareTo(ImmutableArray.Create(1, 2, 3), null));
- Assert.Equal(0, comparable.CompareTo(new ImmutableArray<int>(), null));
+ return SharedComparers<int>()
+ .OfType<IComparer>()
+ .Except(new IComparer[] { null })
+ .SelectMany(comparer => new[]
+ {
+ new object[] { new[] { 1, 2, 3 }, new[] { 1, 2, 3, 4 }, comparer },
+ new object[] { new[] { 1, 2, 3 }, ImmutableArray.Create(1, 2, 3, 4), comparer },
+ new object[] { new[] { 1, 2, 3 }, new List<int> { 1, 2, 3 }, comparer }
+ });
}
[Theory]
- [InlineData(new int[0], 5)]
- [InlineData(new int[] { 3 }, 5)]
- [InlineData(new int[] { 5 }, 5)]
- [InlineData(new int[] { 1, 2, 3 }, 1)]
- [InlineData(new int[] { 1, 2, 3 }, 2)]
- [InlineData(new int[] { 1, 2, 3 }, 3)]
- [InlineData(new int[] { 1, 2, 3, 4 }, 4)]
- public void BinarySearch(int[] array, int value)
+ [MemberData(nameof(BinarySearchData))]
+ public void BinarySearch(IEnumerable<int> source, int value)
{
- Assert.Throws<ArgumentNullException>("array", () => ImmutableArray.BinarySearch(default(ImmutableArray<int>), value));
+ var array = source.ToArray();
Assert.Equal(
Array.BinarySearch(array, value),
@@ -1446,17 +2119,35 @@ namespace System.Collections.Immutable.Tests
ImmutableArray.BinarySearch(ImmutableArray.Create(array), 0, array.Length, value, Comparer<int>.Default));
}
+ public static IEnumerable<object[]> BinarySearchData()
+ {
+ yield return new object[] { new int[0], 5 };
+ yield return new object[] { new[] { 3 }, 5 };
+ yield return new object[] { new[] { 5 }, 5 };
+ yield return new object[] { new[] { 1, 2, 3 }, 1 };
+ yield return new object[] { new[] { 1, 2, 3 }, 2 };
+ yield return new object[] { new[] { 1, 2, 3 }, 3 };
+ yield return new object[] { new[] { 1, 2, 3, 4 }, 4 };
+ }
+
+ [Theory]
+ [MemberData(nameof(BinarySearchData))]
+ public void BinarySearchDefaultInvalid(IEnumerable<int> source, int value)
+ {
+ Assert.Throws<ArgumentNullException>("array", () => ImmutableArray.BinarySearch(s_emptyDefault, value));
+ }
+
[Fact]
public void OfType()
{
- Assert.Equal(0, s_emptyDefault.OfType<int>().Count());
- Assert.Equal(0, s_empty.OfType<int>().Count());
- Assert.Equal(1, s_oneElement.OfType<int>().Count());
- Assert.Equal(1, s_twoElementRefTypeWithNull.OfType<string>().Count());
+ Assert.Equal(new int[0], s_emptyDefault.OfType<int>());
+ Assert.Equal(new int[0], s_empty.OfType<int>());
+ Assert.Equal(s_oneElement, s_oneElement.OfType<int>());
+ Assert.Equal(new[] { "1" }, s_twoElementRefTypeWithNull.OfType<string>());
}
[Fact]
- public void Add_ThreadSafety()
+ public void AddThreadSafety()
{
// Note the point of this thread-safety test is *not* to test the thread-safety of the test itself.
// This test has a known issue where the two threads will stomp on each others updates, but that's not the point.
@@ -1477,11 +2168,11 @@ namespace System.Collections.Immutable.Tests
Task.WaitAll(Task.Run(mutator), Task.Run(mutator));
}
- [Fact]
- public void DebuggerAttributesValid()
+ [Theory]
+ [MemberData(nameof(Int32EnumerableData))]
+ public void DebuggerAttributesValid(IEnumerable<int> source)
{
- DebuggerAttributes.ValidateDebuggerDisplayReferences(ImmutableArray.Create<string>()); // verify empty
- DebuggerAttributes.ValidateDebuggerDisplayReferences(ImmutableArray.Create(1, 2, 3)); // verify non-empty
+ DebuggerAttributes.ValidateDebuggerDisplayReferences(source.ToImmutableArray());
}
protected override IEnumerable<T> GetEnumerableOf<T>(params T[] contents)
@@ -1490,6 +2181,111 @@ namespace System.Collections.Immutable.Tests
}
/// <summary>
+ /// Returns an object typed as an <see cref="IEquatable{T}"/>.
+ /// </summary>
+ /// <typeparam name="T">The type of the object.</typeparam>
+ /// <param name="obj">The object.</param>
+ private static IEquatable<T> AsEquatable<T>(T obj) where T : IEquatable<T> => obj;
+
+ /// <summary>
+ /// Given an enumerable, produces a list of enumerables that have the same contents,
+ /// but have different underlying types.
+ /// </summary>
+ /// <typeparam name="T">The element type.</typeparam>
+ /// <param name="source">The source enumerable.</param>
+ private static IEnumerable<IEnumerable<T>> ChangeType<T>(IEnumerable<T> source)
+ {
+ yield return source;
+ // Implements IList<T>, but isn't a type we're likely to explicitly optimize for.
+ yield return new LinkedList<T>(source);
+ yield return source.Select(x => x); // A lazy enumerable.
+
+ // Constructing these types will be problematic if the source is a T[], but
+ // its underlying type is not typeof(T[]).
+ // The reason is since they are contiguous in memory, they call Array.Copy
+ // if the source is an array as an optimization, which throws if the types
+ // of the arrays do not exactly match up.
+ // More info here: https://github.com/dotnet/corefx/issues/2241
+
+ if (!(source is T[]) || source.GetType() == typeof(T[]))
+ {
+ yield return source.ToArray();
+ yield return source.ToList();
+ yield return source.ToImmutableArray();
+ // Is not an ICollection<T>, but does implement ICollection and IReadOnlyCollection<T>.
+ yield return new Queue<T>(source);
+ }
+ }
+
+ /// <summary>
+ /// Wraps an enumerable in an iterator, so that it does not implement interfaces such as <see cref="IList{T}"/>.
+ /// </summary>
+ /// <typeparam name="T">The element type.</typeparam>
+ /// <param name="source">The source enumerable.</param>
+ private static IEnumerable<T> ForceLazy<T>(IEnumerable<T> source)
+ {
+ foreach (T element in source)
+ {
+ yield return element;
+ }
+ }
+
+ /// <summary>
+ /// Gets the underlying array of an <see cref="ImmutableArray{T}"/>. For testing purposes only.
+ /// </summary>
+ /// <typeparam name="T">The element type.</typeparam>
+ /// <param name="array">The immutable array.</param>
+ /// <returns>The underlying array.</returns>
+ private static T[] GetUnderlyingArray<T>(ImmutableArray<T> array)
+ {
+ FieldInfo arrayField = typeof(ImmutableArray<T>)
+ .GetField("array", BindingFlags.Instance | BindingFlags.NonPublic);
+ return (T[])arrayField.GetValue(array);
+ }
+
+ /// <summary>
+ /// Returns whether the object is an instance of an <see cref="ImmutableArray{T}"/>.
+ /// </summary>
+ /// <param name="obj">The object.</param>
+ private static bool IsImmutableArray(object obj)
+ {
+ if (obj == null)
+ {
+ return false;
+ }
+
+ var typeInfo = obj.GetType().GetTypeInfo();
+ return typeInfo.IsGenericType && typeInfo.GetGenericTypeDefinition() == typeof(ImmutableArray<>);
+ }
+
+ /// <summary>
+ /// Returns a list of comparers that are shared between tests.
+ /// </summary>
+ /// <typeparam name="T">The comparand type.</typeparam>
+ private static IEnumerable<IComparer<T>> SharedComparers<T>()
+ where T : IComparable<T>
+ {
+ // Null comparers should be accepted and translated to the default comparer.
+ yield return null;
+ yield return Comparer<T>.Default;
+ yield return Comparer<T>.Create((x, y) => y.CompareTo(x));
+ yield return Comparer<T>.Create((x, y) => 0);
+ }
+
+ /// <summary>
+ /// Returns a list of equality comparers that are shared between tests.
+ /// </summary>
+ /// <typeparam name="T">The comparand type.</typeparam>
+ private static IEnumerable<IEqualityComparer<T>> SharedEqualityComparers<T>()
+ {
+ // Null comparers should be accepted and translated to the default comparer.
+ yield return null;
+ yield return EqualityComparer<T>.Default;
+ yield return new DelegateEqualityComparer<T>(equals: (x, y) => true, objectGetHashCode: obj => 0);
+ yield return new DelegateEqualityComparer<T>(equals: (x, y) => false, objectGetHashCode: obj => 0);
+ }
+
+ /// <summary>
/// A structure that takes exactly 3 bytes of memory.
/// </summary>
private struct ThreeByteStruct : IEquatable<ThreeByteStruct>
diff --git a/src/System.Collections.Immutable/tests/System.Collections.Immutable.Tests.csproj b/src/System.Collections.Immutable/tests/System.Collections.Immutable.Tests.csproj
index be35a331ba..92268822d9 100644
--- a/src/System.Collections.Immutable/tests/System.Collections.Immutable.Tests.csproj
+++ b/src/System.Collections.Immutable/tests/System.Collections.Immutable.Tests.csproj
@@ -49,6 +49,9 @@
<Compile Include="$(CommonTestPath)\System\Collections\CollectionAsserts.cs">
<Link>Common\System\Collections\CollectionAsserts.cs</Link>
</Compile>
+ <Compile Include="$(CommonTestPath)\System\Collections\DelegateEqualityComparer.cs">
+ <Link>Common\System\Collections\DelegateEqualityComparer.cs</Link>
+ </Compile>
<Compile Include="$(CommonTestPath)\System\Collections\ICollection.NonGeneric.Tests.cs">
<Link>Common\System\Collections\ICollection.NonGeneric.Tests.cs</Link>
</Compile>
diff --git a/src/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceEventSource.cs b/src/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceEventSource.cs
index 9a54037808..b440b286a2 100644
--- a/src/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceEventSource.cs
+++ b/src/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/DiagnosticSourceEventSource.cs
@@ -176,7 +176,9 @@ namespace System.Diagnostics
"httpContext.Request.Path;" +
"httpContext.Request.QueryString" +
"\n" +
- "Microsoft.AspNetCore/Microsoft.AspNetCore.Hosting.EndRequest@Activity1Stop:-";
+ "Microsoft.AspNetCore/Microsoft.AspNetCore.Hosting.EndRequest@Activity1Stop:-" +
+ "httpContext.TraceIdentifier;" +
+ "httpContext.Response.StatusCode";
// Setting EntityFrameworkCoreCommands is like having this in the FilterAndPayloadSpecs string
// It turns on basic SQL commands.
diff --git a/src/System.Diagnostics.DiagnosticSource/tests/DiagnosticSourceEventSourceBridgeTests.cs b/src/System.Diagnostics.DiagnosticSource/tests/DiagnosticSourceEventSourceBridgeTests.cs
index 71b000a4a9..d0e0c39f2b 100644
--- a/src/System.Diagnostics.DiagnosticSource/tests/DiagnosticSourceEventSourceBridgeTests.cs
+++ b/src/System.Diagnostics.DiagnosticSource/tests/DiagnosticSourceEventSourceBridgeTests.cs
@@ -594,11 +594,25 @@ namespace System.Diagnostics.Tests
eventSourceListener.ResetEventCountAndLastEvent();
// Stop the ASP.NET reqeust.
- aspNetCoreSource.Write("Microsoft.AspNetCore.Hosting.EndRequest", null);
+ aspNetCoreSource.Write("Microsoft.AspNetCore.Hosting.EndRequest",
+ new
+ {
+ httpContext = new
+ {
+ Response = new
+ {
+ StatusCode = "200"
+ },
+ TraceIdentifier = "MyTraceId"
+ }
+ });
Assert.Equal(1, eventSourceListener.EventCount); // Exactly one more event has been emitted.
Assert.Equal("Activity1Stop", eventSourceListener.LastEvent.EventSourceEventName);
Assert.Equal("Microsoft.AspNetCore", eventSourceListener.LastEvent.SourceName);
Assert.Equal("Microsoft.AspNetCore.Hosting.EndRequest", eventSourceListener.LastEvent.EventName);
+ Assert.True(2 <= eventSourceListener.LastEvent.Arguments.Count);
+ Assert.Equal("MyTraceId", eventSourceListener.LastEvent.Arguments["TraceIdentifier"]);
+ Assert.Equal("200", eventSourceListener.LastEvent.Arguments["StatusCode"]);
eventSourceListener.ResetEventCountAndLastEvent();
}
}
diff --git a/src/System.Net.NetworkInformation/src/System.Net.NetworkInformation.csproj b/src/System.Net.NetworkInformation/src/System.Net.NetworkInformation.csproj
index 582975a226..39ab948767 100644
--- a/src/System.Net.NetworkInformation/src/System.Net.NetworkInformation.csproj
+++ b/src/System.Net.NetworkInformation/src/System.Net.NetworkInformation.csproj
@@ -138,6 +138,9 @@
<Compile Include="$(CommonPath)\System\Net\ContextAwareResult.Windows.cs">
<Link>Common\System\Net\ContextAwareResult.Windows.cs</Link>
</Compile>
+ <Compile Include="$(CommonPath)\System\Net\CompletionPortHelper.Windows.cs">
+ <Link>Common\System\Net\CompletionPortHelper.Windows.cs</Link>
+ </Compile>
<Compile Include="$(CommonPath)\System\Net\LazyAsyncResult.cs">
<Link>Common\System\Net\LazyAsyncResult.cs</Link>
</Compile>
@@ -197,6 +200,9 @@
<Compile Include="$(CommonPath)\Interop\Windows\kernel32\Interop.LocalFree.cs">
<Link>Interop\Windows\kernel32\Interop.LocalFree.cs</Link>
</Compile>
+ <Compile Include="$(CommonPath)\Interop\Windows\kernel32\Interop.SetFileCompletionNotificationModes.cs">
+ <Link>Interop\Windows\kernel32\Interop.SetFileCompletionNotificationModes.cs</Link>
+ </Compile>
<Compile Include="$(CommonPath)\Interop\Windows\Winsock\Interop.accept.cs">
<Link>Interop\Windows\Winsock\Interop.accept.cs</Link>
</Compile>
diff --git a/src/System.Net.Sockets/src/System.Net.Sockets.csproj b/src/System.Net.Sockets/src/System.Net.Sockets.csproj
index 202817827e..64853466bc 100644
--- a/src/System.Net.Sockets/src/System.Net.Sockets.csproj
+++ b/src/System.Net.Sockets/src/System.Net.Sockets.csproj
@@ -282,15 +282,24 @@
<Compile Include="$(CommonPath)\Interop\Windows\kernel32\Interop.LocalFree.cs">
<Link>Common\Interop\Windows\kernel32\Interop.LocalFree.cs</Link>
</Compile>
+ <Compile Include="$(CommonPath)\Interop\Windows\kernel32\Interop.SetFileCompletionNotificationModes.cs">
+ <Link>Interop\Windows\kernel32\Interop.SetFileCompletionNotificationModes.cs</Link>
+ </Compile>
<Compile Include="$(CommonPath)\System\Net\ContextAwareResult.Windows.cs">
<Link>Common\System\Net\ContextAwareResult.Windows.cs</Link>
</Compile>
+ <Compile Include="$(CommonPath)\System\Net\CompletionPortHelper.Windows.cs">
+ <Link>Common\System\Net\CompletionPortHelper.Windows.cs</Link>
+ </Compile>
</ItemGroup>
<!-- Windows : Win32 + WinRT -->
<ItemGroup Condition="'$(TargetsWindows)' == 'true' AND '$(EnableWinRT)' == 'true'">
<Compile Include="$(CommonPath)\System\Net\ContextAwareResult.Uap.cs">
<Link>Common\System\Net\ContextAwareResult.Uap.cs</Link>
</Compile>
+ <Compile Include="$(CommonPath)\System\Net\CompletionPortHelper.Uap.cs">
+ <Link>Common\System\Net\CompletionPortHelper.Uap.cs</Link>
+ </Compile>
</ItemGroup>
<ItemGroup Condition=" '$(TargetsUnix)' == 'true' ">
<Compile Include="System\Net\Sockets\AcceptOverlappedAsyncResult.Unix.cs" />
diff --git a/src/System.Net.Sockets/src/System/Net/Sockets/BaseOverlappedAsyncResult.Unix.cs b/src/System.Net.Sockets/src/System/Net/Sockets/BaseOverlappedAsyncResult.Unix.cs
index 7fc1d182b3..d37ede5443 100644
--- a/src/System.Net.Sockets/src/System/Net/Sockets/BaseOverlappedAsyncResult.Unix.cs
+++ b/src/System.Net.Sockets/src/System/Net/Sockets/BaseOverlappedAsyncResult.Unix.cs
@@ -23,16 +23,10 @@ namespace System.Net.Sockets
if (NetEventSource.IsEnabled) NetEventSource.Info(this, socket);
}
- public void CompletionCallback(int numBytes, SocketError errorCode)
+ protected void CompletionCallback(int numBytes, SocketError errorCode)
{
ErrorCode = (int)errorCode;
InvokeCallback(PostCompletion(numBytes));
}
-
- private void ReleaseUnmanagedStructures()
- {
- // NOTE: this method needs to exist to conform to the contract expected by the
- // platform-independent code in BaseOverlappedAsyncResult.CheckAsyncCallOverlappedResult.
- }
}
}
diff --git a/src/System.Net.Sockets/src/System/Net/Sockets/BaseOverlappedAsyncResult.Windows.cs b/src/System.Net.Sockets/src/System/Net/Sockets/BaseOverlappedAsyncResult.Windows.cs
index 9718acccd5..8178bee824 100644
--- a/src/System.Net.Sockets/src/System/Net/Sockets/BaseOverlappedAsyncResult.Windows.cs
+++ b/src/System.Net.Sockets/src/System/Net/Sockets/BaseOverlappedAsyncResult.Windows.cs
@@ -30,14 +30,6 @@ namespace System.Net.Sockets
if (NetEventSource.IsEnabled) NetEventSource.Info(this, socket);
}
- internal SafeNativeOverlapped NativeOverlapped
- {
- get
- {
- return _nativeOverlapped;
- }
- }
-
// SetUnmanagedStructures
//
// This needs to be called for overlapped IO to function properly.
@@ -59,14 +51,15 @@ namespace System.Net.Sockets
throw new ObjectDisposedException(s.GetType().FullName);
}
- ThreadPoolBoundHandle boundHandle = s.SafeHandle.GetOrAllocateThreadPoolBoundHandle();
+ ThreadPoolBoundHandle boundHandle = s.GetOrAllocateThreadPoolBoundHandle();
unsafe
{
NativeOverlapped* overlapped = boundHandle.AllocateNativeOverlapped(s_ioCallback, this, objectsToPin);
_nativeOverlapped = new SafeNativeOverlapped(s.SafeHandle, overlapped);
- if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"{boundHandle}::AllocateNativeOverlapped. return={_nativeOverlapped}");
}
+
+ if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"{boundHandle}::AllocateNativeOverlapped. return={_nativeOverlapped}");
}
private static unsafe void CompletionPortCallback(uint errorCode, uint numBytes, NativeOverlapped* nativeOverlapped)
@@ -78,8 +71,6 @@ namespace System.Net.Sockets
#endif
BaseOverlappedAsyncResult asyncResult = (BaseOverlappedAsyncResult)ThreadPoolBoundHandle.GetNativeOverlappedState(nativeOverlapped);
- object returnObject = null;
-
if (asyncResult.InternalPeekCompleted)
{
NetEventSource.Fail(null, $"asyncResult.IsCompleted: {asyncResult}");
@@ -115,7 +106,7 @@ namespace System.Net.Sockets
SocketFlags ignore;
bool success = Interop.Winsock.WSAGetOverlappedResult(
socket.SafeHandle,
- asyncResult.NativeOverlapped,
+ asyncResult._nativeOverlapped,
out numBytes,
false,
out ignore);
@@ -135,15 +126,23 @@ namespace System.Net.Sockets
}
}
}
- asyncResult.ErrorCode = (int)socketError;
- returnObject = asyncResult.PostCompletion((int)numBytes);
- asyncResult.ReleaseUnmanagedStructures();
- asyncResult.InvokeCallback(returnObject);
+
+ // Set results and invoke callback
+ asyncResult.CompletionCallback((int)numBytes, socketError);
#if DEBUG
}
#endif
}
+ // Called either synchronously from SocketPal async routines or asynchronously via CompletionPortCallback above.
+ private void CompletionCallback(int numBytes, SocketError socketError)
+ {
+ ReleaseUnmanagedStructures();
+
+ ErrorCode = (int)socketError;
+ InvokeCallback(PostCompletion(numBytes));
+ }
+
// The following property returns the Win32 unsafe pointer to
// whichever Overlapped structure we're using for IO.
internal SafeHandle OverlappedHandle
@@ -157,7 +156,46 @@ namespace System.Net.Sockets
}
}
- private void ReleaseUnmanagedStructures()
+ // Check the result of the overlapped operation.
+ // Handle synchronous success by completing the asyncResult here.
+ // Handle synchronous failure by cleaning up and returning a SocketError.
+ internal SocketError ProcessOverlappedResult(bool success, int bytesTransferred)
+ {
+ if (success)
+ {
+ // Synchronous success.
+ Socket socket = (Socket)AsyncObject;
+ if (socket.SafeHandle.SkipCompletionPortOnSuccess)
+ {
+ // The socket handle is configured to skip completion on success,
+ // so we can complete this asyncResult right now.
+ CompletionCallback(bytesTransferred, SocketError.Success);
+ return SocketError.Success;
+ }
+
+ // Socket handle is going to post a completion to the completion port (may have done so already).
+ // Return pending and we will continue in the completion port callback.
+ return SocketError.IOPending;
+ }
+
+ // Get the socket error (which may be IOPending)
+ SocketError errorCode = SocketPal.GetLastSocketError();
+
+ if (errorCode == SocketError.IOPending)
+ {
+ // Operation is pending.
+ // We will continue when the completion arrives (may have already at this point).
+ return SocketError.IOPending;
+ }
+
+ // Synchronous failure.
+ // Release overlapped and pinned structures.
+ ReleaseUnmanagedStructures();
+
+ return errorCode;
+ }
+
+ internal void ReleaseUnmanagedStructures()
{
if (Interlocked.Decrement(ref _cleanupCount) == 0)
{
diff --git a/src/System.Net.Sockets/src/System/Net/Sockets/BaseOverlappedAsyncResult.cs b/src/System.Net.Sockets/src/System/Net/Sockets/BaseOverlappedAsyncResult.cs
index 1248e7ce73..1a6a243329 100644
--- a/src/System.Net.Sockets/src/System/Net/Sockets/BaseOverlappedAsyncResult.cs
+++ b/src/System.Net.Sockets/src/System/Net/Sockets/BaseOverlappedAsyncResult.cs
@@ -36,30 +36,5 @@ namespace System.Net.Sockets
base.InternalWaitForCompletion();
return _numBytes;
}
-
- // This method is called after an asynchronous call is made for the user.
- // It checks and acts accordingly if the IO:
- // 1) completed synchronously.
- // 2) was pended.
- // 3) failed.
- internal unsafe SocketError CheckAsyncCallOverlappedResult(SocketError errorCode)
- {
- if (NetEventSource.IsEnabled) NetEventSource.Info(this, errorCode);
-
- if (errorCode == SocketError.Success || errorCode == SocketError.IOPending)
- {
- // Ignore cases in which a completion packet will be queued:
- // we'll deal with this IO in the callback.
- return SocketError.Success;
- }
-
- // In the remaining cases a completion packet will NOT be queued:
- // we have to call the callback explicitly signaling an error.
- ErrorCode = (int)errorCode;
- Result = -1;
-
- ReleaseUnmanagedStructures(); // Additional release for the completion that won't happen.
- return errorCode;
- }
}
}
diff --git a/src/System.Net.Sockets/src/System/Net/Sockets/Socket.Windows.cs b/src/System.Net.Sockets/src/System/Net/Sockets/Socket.Windows.cs
index 015c0b8b3a..bde3c459d1 100644
--- a/src/System.Net.Sockets/src/System/Net/Sockets/Socket.Windows.cs
+++ b/src/System.Net.Sockets/src/System/Net/Sockets/Socket.Windows.cs
@@ -6,6 +6,7 @@ using Microsoft.Win32.SafeHandles;
using System.Collections;
using System.IO;
using System.Runtime.InteropServices;
+using System.Threading;
namespace System.Net.Sockets
{
@@ -246,12 +247,9 @@ namespace System.Net.Sockets
SocketError errorCode = SocketPal.SendFileAsync(_handle, fileStream, preBuffer, postBuffer, flags, asyncResult);
// Check for synchronous exception
- if (errorCode != SocketError.Success)
+ if (!CheckErrorAndUpdateStatus(errorCode))
{
- SocketException socketException = new SocketException((int)errorCode);
- UpdateStatusAfterSocketError(socketException);
- if (NetEventSource.IsEnabled) NetEventSource.Error(this, socketException);
- throw socketException;
+ throw new SocketException((int)errorCode);
}
asyncResult.FinishPostingAsyncOp(ref Caches.SendClosureCache);
@@ -292,5 +290,14 @@ namespace System.Net.Sockets
}
}
+
+ internal ThreadPoolBoundHandle GetOrAllocateThreadPoolBoundHandle()
+ {
+ // There is a known bug that exists through Windows 7 with UDP and
+ // SetFileCompletionNotificationModes.
+ // So, don't try to enable skipping the completion port on success in this case.
+ bool trySkipCompletionPortOnSuccess = !(CompletionPortHelper.PlatformHasUdpIssue && _protocolType == ProtocolType.Udp);
+ return _handle.GetOrAllocateThreadPoolBoundHandle(trySkipCompletionPortOnSuccess);
+ }
}
}
diff --git a/src/System.Net.Sockets/src/System/Net/Sockets/Socket.cs b/src/System.Net.Sockets/src/System/Net/Sockets/Socket.cs
index f3fc7915b6..697943480d 100644
--- a/src/System.Net.Sockets/src/System/Net/Sockets/Socket.cs
+++ b/src/System.Net.Sockets/src/System/Net/Sockets/Socket.cs
@@ -2415,17 +2415,10 @@ namespace System.Net.Sockets
if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"UnsafeNclNativeMethods.OSSOCK.DisConnectEx returns:{errorCode}");
- // if the asynchronous native call fails synchronously
- // we'll throw a SocketException
- errorCode = asyncResult.CheckAsyncCallOverlappedResult(errorCode);
-
- if (errorCode != SocketError.Success)
+ // If the call failed, update our status and throw
+ if (!CheckErrorAndUpdateStatus(errorCode))
{
- // update our internal state after this socket error and throw
- SocketException socketException = new SocketException((int)errorCode);
- UpdateStatusAfterSocketError(socketException);
- if (NetEventSource.IsEnabled) NetEventSource.Error(this, socketException);
- throw socketException;
+ throw new SocketException((int)errorCode);
}
if (NetEventSource.IsEnabled) NetEventSource.Exit(this, asyncResult);
@@ -2702,32 +2695,16 @@ namespace System.Net.Sockets
{
if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"SRC:{LocalEndPoint} DST:{RemoteEndPoint} size:{size}");
- // Guarantee to call CheckAsyncCallOverlappedResult if we call SetUnamangedStructures with a cache in order to
- // avoid a Socket leak in case of error.
- SocketError errorCode = SocketError.SocketError;
- try
- {
- // Get the Send going.
- if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"asyncResult:{asyncResult} size:{size}");
+ // Get the Send going.
+ if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"asyncResult:{asyncResult} size:{size}");
- errorCode = SocketPal.SendAsync(_handle, buffer, offset, size, socketFlags, asyncResult);
+ SocketError errorCode = SocketPal.SendAsync(_handle, buffer, offset, size, socketFlags, asyncResult);
- if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"Interop.Winsock.WSASend returns:{errorCode} size:{size} returning AsyncResult:{asyncResult}");
- }
- finally
- {
- errorCode = asyncResult.CheckAsyncCallOverlappedResult(errorCode);
- }
+ if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"Interop.Winsock.WSASend returns:{errorCode} size:{size} returning AsyncResult:{asyncResult}");
+
+ // If the call failed, update our status
+ CheckErrorAndUpdateStatus(errorCode);
- // Throw an appropriate SocketException if the native call fails synchronously.
- if (errorCode != SocketError.Success)
- {
- UpdateStatusAfterSocketError(errorCode);
- if (NetEventSource.IsEnabled)
- {
- if (NetEventSource.IsEnabled) NetEventSource.Error(this, new SocketException((int)errorCode));
- }
- }
return errorCode;
}
@@ -2784,29 +2761,15 @@ namespace System.Net.Sockets
private SocketError DoBeginSend(IList<ArraySegment<byte>> buffers, SocketFlags socketFlags, OverlappedAsyncResult asyncResult)
{
if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"SRC:{LocalEndPoint} DST:{RemoteEndPoint} buffers:{buffers}");
+ if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"asyncResult:{asyncResult}");
- // Guarantee to call CheckAsyncCallOverlappedResult if we call SetUnamangedStructures with a cache in order to
- // avoid a Socket leak in case of error.
- SocketError errorCode = SocketError.SocketError;
- try
- {
- if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"asyncResult:{asyncResult}");
+ SocketError errorCode = SocketPal.SendAsync(_handle, buffers, socketFlags, asyncResult);
- errorCode = SocketPal.SendAsync(_handle, buffers, socketFlags, asyncResult);
+ if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"Interop.Winsock.WSASend returns:{errorCode} returning AsyncResult:{asyncResult}");
- if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"Interop.Winsock.WSASend returns:{errorCode} returning AsyncResult:{asyncResult}");
- }
- finally
- {
- errorCode = asyncResult.CheckAsyncCallOverlappedResult(errorCode);
- }
+ // If the call failed, update our status
+ CheckErrorAndUpdateStatus(errorCode);
- // Throw an appropriate SocketException if the native call fails synchronously.
- if (errorCode != SocketError.Success)
- {
- UpdateStatusAfterSocketError(errorCode);
- if (NetEventSource.IsEnabled) NetEventSource.Error(this, new SocketException((int)errorCode));
- }
return errorCode;
}
@@ -3027,20 +2990,14 @@ namespace System.Net.Sockets
_rightEndPoint = oldEndPoint;
throw;
}
- finally
- {
- errorCode = asyncResult.CheckAsyncCallOverlappedResult(errorCode);
- }
// Throw an appropriate SocketException if the native call fails synchronously.
- if (errorCode != SocketError.Success)
+ if (!CheckErrorAndUpdateStatus(errorCode))
{
// Update the internal state of this socket according to the error before throwing.
_rightEndPoint = oldEndPoint;
- SocketException socketException = new SocketException((int)errorCode);
- UpdateStatusAfterSocketError(socketException);
- if (NetEventSource.IsEnabled) NetEventSource.Error(this, socketException);
- throw socketException;
+
+ throw new SocketException((int)errorCode);
}
if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"size:{size} returning AsyncResult:{asyncResult}");
@@ -3216,28 +3173,13 @@ namespace System.Net.Sockets
#if DEBUG
IntPtr lastHandle = _handle.DangerousGetHandle();
#endif
- // Guarantee to call CheckAsyncCallOverlappedResult if we call SetUnamangedStructures with a cache in order to
- // avoid a Socket leak in case of error.
- SocketError errorCode = SocketError.SocketError;
- try
- {
- errorCode = SocketPal.ReceiveAsync(_handle, buffer, offset, size, socketFlags, asyncResult);
+ SocketError errorCode = SocketPal.ReceiveAsync(_handle, buffer, offset, size, socketFlags, asyncResult);
- if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"Interop.Winsock.WSARecv returns:{errorCode} returning AsyncResult:{asyncResult}");
- }
- finally
- {
- errorCode = asyncResult.CheckAsyncCallOverlappedResult(errorCode);
- }
+ if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"Interop.Winsock.WSARecv returns:{errorCode} returning AsyncResult:{asyncResult}");
// Throw an appropriate SocketException if the native call fails synchronously.
- if (errorCode != SocketError.Success)
+ if (!CheckErrorAndUpdateStatus(errorCode))
{
- // Update the internal state of this socket according to the error before throwing.
- UpdateStatusAfterSocketError(errorCode);
- var socketException = new SocketException((int)errorCode);
- if (NetEventSource.IsEnabled) NetEventSource.Error(this, socketException);
- asyncResult.InvokeCallback(new SocketException((int)errorCode));
}
#if DEBUG
else
@@ -3309,28 +3251,12 @@ namespace System.Net.Sockets
#if DEBUG
IntPtr lastHandle = _handle.DangerousGetHandle();
#endif
- // Guarantee to call CheckAsyncCallOverlappedResult if we call SetUnamangedStructures with a cache in order to
- // avoid a Socket leak in case of error.
- SocketError errorCode = SocketError.SocketError;
- try
- {
- errorCode = SocketPal.ReceiveAsync(_handle, buffers, socketFlags, asyncResult);
- if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"Interop.Winsock.WSARecv returns:{errorCode} returning AsyncResult:{asyncResult}");
- }
- finally
- {
- errorCode = asyncResult.CheckAsyncCallOverlappedResult(errorCode);
- }
+ SocketError errorCode = SocketPal.ReceiveAsync(_handle, buffers, socketFlags, asyncResult);
- // Throw an appropriate SocketException if the native call fails synchronously.
- if (errorCode != SocketError.Success)
+ if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"Interop.Winsock.WSARecv returns:{errorCode} returning AsyncResult:{asyncResult}");
+
+ if (!CheckErrorAndUpdateStatus(errorCode))
{
- // Update the internal state of this socket according to the error before throwing.
- UpdateStatusAfterSocketError(errorCode);
- if (NetEventSource.IsEnabled)
- {
- if (NetEventSource.IsEnabled) NetEventSource.Error(this, new SocketException((int)errorCode));
- }
}
#if DEBUG
else
@@ -3530,20 +3456,14 @@ namespace System.Net.Sockets
_rightEndPoint = oldEndPoint;
throw;
}
- finally
- {
- errorCode = asyncResult.CheckAsyncCallOverlappedResult(errorCode);
- }
// Throw an appropriate SocketException if the native call fails synchronously.
- if (errorCode != SocketError.Success)
+ if (!CheckErrorAndUpdateStatus(errorCode))
{
// Update the internal state of this socket according to the error before throwing.
_rightEndPoint = oldEndPoint;
- SocketException socketException = new SocketException((int)errorCode);
- UpdateStatusAfterSocketError(socketException);
- if (NetEventSource.IsEnabled) NetEventSource.Error(this, socketException);
- throw socketException;
+
+ throw new SocketException((int)errorCode);
}
// Capture the context, maybe call the callback, and return.
@@ -3768,20 +3688,14 @@ namespace System.Net.Sockets
_rightEndPoint = oldEndPoint;
throw;
}
- finally
- {
- errorCode = asyncResult.CheckAsyncCallOverlappedResult(errorCode);
- }
// Throw an appropriate SocketException if the native call fails synchronously.
- if (errorCode != SocketError.Success)
+ if (!CheckErrorAndUpdateStatus(errorCode))
{
// Update the internal state of this socket according to the error before throwing.
_rightEndPoint = oldEndPoint;
- SocketException socketException = new SocketException((int)errorCode);
- UpdateStatusAfterSocketError(socketException);
- if (NetEventSource.IsEnabled) NetEventSource.Error(this, socketException);
- throw socketException;
+
+ throw new SocketException((int)errorCode);
}
if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"size:{size} return AsyncResult:{asyncResult}");
@@ -3964,17 +3878,12 @@ namespace System.Net.Sockets
int socketAddressSize = _rightEndPoint.Serialize().Size;
SocketError errorCode = SocketPal.AcceptAsync(this, _handle, acceptHandle, receiveSize, socketAddressSize, asyncResult);
- errorCode = asyncResult.CheckAsyncCallOverlappedResult(errorCode);
-
if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"Interop.Winsock.AcceptEx returns:{errorCode} {asyncResult}");
// Throw an appropriate SocketException if the native call fails synchronously.
- if (errorCode != SocketError.Success)
+ if (!CheckErrorAndUpdateStatus(errorCode))
{
- SocketException socketException = new SocketException((int)errorCode);
- UpdateStatusAfterSocketError(socketException);
- if (NetEventSource.IsEnabled) NetEventSource.Error(this, socketException);
- throw socketException;
+ throw new SocketException((int)errorCode);
}
}
@@ -4109,7 +4018,6 @@ namespace System.Net.Sockets
public bool AcceptAsync(SocketAsyncEventArgs e)
{
if (NetEventSource.IsEnabled) NetEventSource.Enter(this, e);
- bool retval;
if (CleanedUp)
{
@@ -4142,13 +4050,12 @@ namespace System.Net.Sockets
e.StartOperationAccept();
// Local variables for sync completion.
- int bytesTransferred;
SocketError socketError = SocketError.Success;
// Make the native call.
try
{
- socketError = e.DoOperationAccept(this, _handle, acceptHandle, out bytesTransferred);
+ socketError = e.DoOperationAccept(this, _handle, acceptHandle);
}
catch
{
@@ -4157,25 +4064,15 @@ namespace System.Net.Sockets
throw;
}
- // Handle completion when completion port is not posted.
- if (socketError != SocketError.Success && socketError != SocketError.IOPending)
- {
- e.FinishOperationSyncFailure(socketError, bytesTransferred, SocketFlags.None);
- retval = false;
- }
- else
- {
- retval = true;
- }
-
- if (NetEventSource.IsEnabled) NetEventSource.Exit(this, retval);
- return retval;
+ bool pending = (socketError == SocketError.IOPending);
+ if (NetEventSource.IsEnabled) NetEventSource.Exit(this, pending);
+ return pending;
}
public bool ConnectAsync(SocketAsyncEventArgs e)
{
if (NetEventSource.IsEnabled) NetEventSource.Enter(this, e);
- bool retval;
+ bool pending;
if (CleanedUp)
{
@@ -4219,7 +4116,7 @@ namespace System.Net.Sockets
e.StartOperationCommon(this);
e.StartOperationWrapperConnect(multipleConnectAsync);
- retval = multipleConnectAsync.StartConnectAsync(e, dnsEP);
+ pending = multipleConnectAsync.StartConnectAsync(e, dnsEP);
}
else
{
@@ -4256,11 +4153,10 @@ namespace System.Net.Sockets
e.StartOperationConnect();
// Make the native call.
- int bytesTransferred;
SocketError socketError = SocketError.Success;
try
{
- socketError = e.DoOperationConnect(this, _handle, out bytesTransferred);
+ socketError = e.DoOperationConnect(this, _handle);
}
catch
{
@@ -4271,26 +4167,17 @@ namespace System.Net.Sockets
throw;
}
- // Handle failure where completion port is not posted.
- if (socketError != SocketError.Success && socketError != SocketError.IOPending)
- {
- e.FinishOperationSyncFailure(socketError, bytesTransferred, SocketFlags.None);
- retval = false;
- }
- else
- {
- retval = true;
- }
+ pending = (socketError == SocketError.IOPending);
}
- if (NetEventSource.IsEnabled) NetEventSource.Exit(this, retval);
- return retval;
+ if (NetEventSource.IsEnabled) NetEventSource.Exit(this, pending);
+ return pending;
}
public static bool ConnectAsync(SocketType socketType, ProtocolType protocolType, SocketAsyncEventArgs e)
{
if (NetEventSource.IsEnabled) NetEventSource.Enter(null);
- bool retval;
+ bool pending;
if (e == null)
{
@@ -4336,16 +4223,16 @@ namespace System.Net.Sockets
e.StartOperationCommon(attemptSocket);
e.StartOperationWrapperConnect(multipleConnectAsync);
- retval = multipleConnectAsync.StartConnectAsync(e, dnsEP);
+ pending = multipleConnectAsync.StartConnectAsync(e, dnsEP);
}
else
{
Socket attemptSocket = new Socket(endPointSnapshot.AddressFamily, socketType, protocolType);
- retval = attemptSocket.ConnectAsync(e);
+ pending = attemptSocket.ConnectAsync(e);
}
- if (NetEventSource.IsEnabled) NetEventSource.Exit(null, retval);
- return retval;
+ if (NetEventSource.IsEnabled) NetEventSource.Exit(null, pending);
+ return pending;
}
public static void CancelConnectAsync(SocketAsyncEventArgs e)
@@ -4360,7 +4247,6 @@ namespace System.Net.Sockets
public bool DisconnectAsync(SocketAsyncEventArgs e)
{
if (NetEventSource.IsEnabled) NetEventSource.Enter(this);
- bool retval;
// Throw if socket disposed
if (CleanedUp)
@@ -4384,17 +4270,7 @@ namespace System.Net.Sockets
throw;
}
- // Handle completion when completion port is not posted.
- if (socketError != SocketError.Success && socketError != SocketError.IOPending)
- {
- e.FinishOperationSyncFailure(socketError, 0, SocketFlags.None);
- retval = false;
- }
- else
- {
- retval = true;
- }
-
+ bool retval = (socketError == SocketError.IOPending);
if (NetEventSource.IsEnabled) NetEventSource.Exit(this, retval);
return retval;
}
@@ -4402,7 +4278,6 @@ namespace System.Net.Sockets
public bool ReceiveAsync(SocketAsyncEventArgs e)
{
if (NetEventSource.IsEnabled) NetEventSource.Enter(this, e);
- bool retval;
if (CleanedUp)
{
@@ -4420,13 +4295,12 @@ namespace System.Net.Sockets
// Local vars for sync completion of native call.
SocketFlags flags;
- int bytesTransferred;
SocketError socketError;
// Wrap native methods with try/catch so event args object can be cleaned up.
try
{
- socketError = e.DoOperationReceive(_handle, out flags, out bytesTransferred);
+ socketError = e.DoOperationReceive(_handle, out flags);
}
catch
{
@@ -4435,25 +4309,14 @@ namespace System.Net.Sockets
throw;
}
- // Handle completion when completion port is not posted.
- if (socketError != SocketError.Success && socketError != SocketError.IOPending)
- {
- e.FinishOperationSyncFailure(socketError, bytesTransferred, flags);
- retval = false;
- }
- else
- {
- retval = true;
- }
-
- if (NetEventSource.IsEnabled) NetEventSource.Exit(this, retval);
- return retval;
+ bool pending = (socketError == SocketError.IOPending);
+ if (NetEventSource.IsEnabled) NetEventSource.Exit(this, pending);
+ return pending;
}
public bool ReceiveFromAsync(SocketAsyncEventArgs e)
{
if (NetEventSource.IsEnabled) NetEventSource.Enter(this, e);
- bool retval;
if (CleanedUp)
{
@@ -4491,12 +4354,11 @@ namespace System.Net.Sockets
// Make the native call.
SocketFlags flags;
- int bytesTransferred;
SocketError socketError;
try
{
- socketError = e.DoOperationReceiveFrom(_handle, out flags, out bytesTransferred);
+ socketError = e.DoOperationReceiveFrom(_handle, out flags);
}
catch
{
@@ -4505,25 +4367,14 @@ namespace System.Net.Sockets
throw;
}
- // Handle completion when completion port is not posted.
- if (socketError != SocketError.Success && socketError != SocketError.IOPending)
- {
- e.FinishOperationSyncFailure(socketError, bytesTransferred, flags);
- retval = false;
- }
- else
- {
- retval = true;
- }
-
- if (NetEventSource.IsEnabled) NetEventSource.Exit(this, retval);
- return retval;
+ bool pending = (socketError == SocketError.IOPending);
+ if (NetEventSource.IsEnabled) NetEventSource.Exit(this, pending);
+ return pending;
}
public bool ReceiveMessageFromAsync(SocketAsyncEventArgs e)
{
if (NetEventSource.IsEnabled) NetEventSource.Enter(this, e);
- bool retval;
if (CleanedUp)
{
@@ -4562,12 +4413,11 @@ namespace System.Net.Sockets
e.StartOperationReceiveMessageFrom();
// Make the native call.
- int bytesTransferred;
SocketError socketError;
try
{
- socketError = e.DoOperationReceiveMessageFrom(this, _handle, out bytesTransferred);
+ socketError = e.DoOperationReceiveMessageFrom(this, _handle);
}
catch
{
@@ -4576,25 +4426,14 @@ namespace System.Net.Sockets
throw;
}
- // Handle completion when completion port is not posted.
- if (socketError != SocketError.Success && socketError != SocketError.IOPending)
- {
- e.FinishOperationSyncFailure(socketError, bytesTransferred, SocketFlags.None);
- retval = false;
- }
- else
- {
- retval = true;
- }
-
- if (NetEventSource.IsEnabled) NetEventSource.Exit(this, retval);
- return retval;
+ bool pending = (socketError == SocketError.IOPending);
+ if (NetEventSource.IsEnabled) NetEventSource.Exit(this, pending);
+ return pending;
}
public bool SendAsync(SocketAsyncEventArgs e)
{
if (NetEventSource.IsEnabled) NetEventSource.Enter(this, e);
- bool retval;
if (CleanedUp)
{
@@ -4611,13 +4450,12 @@ namespace System.Net.Sockets
e.StartOperationSend();
// Local vars for sync completion of native call.
- int bytesTransferred;
SocketError socketError;
// Wrap native methods with try/catch so event args object can be cleaned up.
try
{
- socketError = e.DoOperationSend(_handle, out bytesTransferred);
+ socketError = e.DoOperationSend(_handle);
}
catch
{
@@ -4626,25 +4464,14 @@ namespace System.Net.Sockets
throw;
}
- // Handle completion when completion port is not posted.
- if (socketError != SocketError.Success && socketError != SocketError.IOPending)
- {
- e.FinishOperationSyncFailure(socketError, bytesTransferred, SocketFlags.None);
- retval = false;
- }
- else
- {
- retval = true;
- }
-
- if (NetEventSource.IsEnabled) NetEventSource.Exit(this, retval);
- return retval;
+ bool pending = (socketError == SocketError.IOPending);
+ if (NetEventSource.IsEnabled) NetEventSource.Exit(this, pending);
+ return pending;
}
public bool SendPacketsAsync(SocketAsyncEventArgs e)
{
if (NetEventSource.IsEnabled) NetEventSource.Enter(this, e);
- bool retval;
// Throw if socket disposed
if (CleanedUp)
@@ -4686,33 +4513,22 @@ namespace System.Net.Sockets
e.Complete();
throw;
}
-
- // Handle completion when completion port is not posted.
- if (socketError != SocketError.Success && socketError != SocketError.IOPending)
- {
- e.FinishOperationSyncFailure(socketError, 0, SocketFlags.None);
- retval = false;
- }
- else
- {
- retval = true;
- }
}
else
{
// No buffers or files to send.
- e.FinishOperationSuccess(SocketError.Success, 0, SocketFlags.None);
- retval = false;
+ e.FinishOperationSyncSuccess(0, SocketFlags.None);
+ socketError = SocketError.Success;
}
- if (NetEventSource.IsEnabled) NetEventSource.Exit(this, retval);
- return retval;
+ bool pending = (socketError == SocketError.IOPending);
+ if (NetEventSource.IsEnabled) NetEventSource.Exit(this, pending);
+ return pending;
}
public bool SendToAsync(SocketAsyncEventArgs e)
{
if (NetEventSource.IsEnabled) NetEventSource.Enter(this, e);
- bool retval;
if (CleanedUp)
{
@@ -4737,13 +4553,12 @@ namespace System.Net.Sockets
e.StartOperationSendTo();
// Make the native call.
- int bytesTransferred;
SocketError socketError;
// Wrap native methods with try/catch so event args object can be cleaned up.
try
{
- socketError = e.DoOperationSendTo(_handle, out bytesTransferred);
+ socketError = e.DoOperationSendTo(_handle);
}
catch
{
@@ -4752,17 +4567,7 @@ namespace System.Net.Sockets
throw;
}
- // Handle completion when completion port is not posted.
- if (socketError != SocketError.Success && socketError != SocketError.IOPending)
- {
- e.FinishOperationSyncFailure(socketError, bytesTransferred, SocketFlags.None);
- retval = false;
- }
- else
- {
- retval = true;
- }
-
+ bool retval = (socketError == SocketError.IOPending);
if (NetEventSource.IsEnabled) NetEventSource.Exit(this, retval);
return retval;
}
@@ -5431,32 +5236,25 @@ namespace System.Net.Sockets
}
catch
{
- // If ConnectEx throws we need to unpin the socketAddress buffer.
// _rightEndPoint will always equal oldEndPoint.
- asyncResult.InternalCleanup();
_rightEndPoint = oldEndPoint;
throw;
}
+ if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"Interop.Winsock.connect returns:{errorCode}");
if (errorCode == SocketError.Success)
{
+ // Synchronous success. Indicate that we're connected.
SetToConnected();
}
- if (NetEventSource.IsEnabled) NetEventSource.Info(this, $"Interop.Winsock.connect returns:{errorCode}");
-
- errorCode = asyncResult.CheckAsyncCallOverlappedResult(errorCode);
-
- // Throw an appropriate SocketException if the native call fails synchronously.
- if (errorCode != SocketError.Success)
+ if (!CheckErrorAndUpdateStatus(errorCode))
{
// Update the internal state of this socket according to the error before throwing.
_rightEndPoint = oldEndPoint;
- SocketException socketException = new SocketException((int)errorCode);
- UpdateStatusAfterSocketError(socketException);
- if (NetEventSource.IsEnabled) NetEventSource.Error(this, socketException);
- throw socketException;
+
+ throw new SocketException((int)errorCode);
}
// We didn't throw, so indicate that we're returning this result to the user. This may call the callback.
@@ -5810,6 +5608,17 @@ namespace System.Net.Sockets
}
}
+ private bool CheckErrorAndUpdateStatus(SocketError errorCode)
+ {
+ if (errorCode == SocketError.Success || errorCode == SocketError.IOPending)
+ {
+ return true;
+ }
+
+ UpdateStatusAfterSocketError(errorCode);
+ return false;
+ }
+
// ValidateBlockingMode - called before synchronous calls to validate
// the fact that we are in blocking mode (not in non-blocking mode) so the
// call will actually be synchronous.
diff --git a/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs b/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs
index 36a95649dc..066e5f3fae 100644
--- a/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs
+++ b/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncContext.Unix.cs
@@ -11,27 +11,25 @@ using System.Threading;
namespace System.Net.Sockets
{
- //
- // NOTE: the publicly-exposed asynchronous methods should match the behavior of
- // Winsock overlapped sockets as closely as possible. Especially important are
- // completion semantics, as the consuming code relies on the Winsock behavior.
- //
- // Winsock queues a completion callback for an overlapped operation in two cases:
- // 1. the operation successfully completes synchronously, or
- // 2. the operation completes asynchronously, successfully or otherwise.
- // In other words, a completion callback is queued iff an operation does not
- // fail synchronously. The asynchronous methods below (e.g. ReceiveAsync) may
- // fail synchronously for either of the following reasons:
- // 1. an underlying system call fails synchronously, or
- // 2. an underlying system call returns EAGAIN, but the socket is closed before
- // the method is able to enqueue its corresponding operation.
- // In the first case, the async method should return the SocketError that
- // corresponds to the native error code; in the second, the method should return
- // SocketError.OperationAborted (which matches what Winsock would return in this
- // case). The publicly-exposed synchronous methods may also encounter the second
- // case. In this situation these methods should return SocketError.Interrupted
- // (which again matches Winsock).
- //
+ // Note on asynchronous behavior here:
+
+ // The asynchronous socket operations here generally do the following:
+ // (1) If the operation queue is empty, try to perform the operation immediately, non-blocking.
+ // If this completes (i.e. does not return EWOULDBLOCK), then we return the results immediately
+ // for both success (SocketError.Success) or failure.
+ // No callback will happen; callers are expected to handle these synchronous completions themselves.
+ // (2) If EWOULDBLOCK is returned, or the queue is not empty, then we enqueue an operation to the
+ // appropriate queue and return SocketError.IOPending.
+ // Enqueuing itself may fail because the socket is closed before the operation can be enqueued;
+ // in this case, we return SocketError.OperationAborted (which matches what Winsock would return in this case).
+ // (3) When the queue completes the operation, it will post a work item to the threadpool
+ // to call the callback with results (either success or failure).
+
+ // Synchronous operations generally do the same, except that instead of returning IOPending,
+ // they block on an event handle until the operation is processed by the queue.
+ // Also, synchronous methods return SocketError.Interrupted when enqueuing fails
+ // (which again matches Winsock behavior).
+
internal sealed class SocketAsyncContext
{
private abstract class AsyncOperation
@@ -662,7 +660,7 @@ namespace System.Net.Sockets
}
}
- public SocketError AcceptAsync(byte[] socketAddress, int socketAddressLen, Action<IntPtr, byte[], int, SocketError> callback)
+ public SocketError AcceptAsync(byte[] socketAddress, ref int socketAddressLen, out IntPtr acceptedFd, Action<IntPtr, byte[], int, SocketError> callback)
{
Debug.Assert(socketAddress != null, "Expected non-null socketAddress");
Debug.Assert(socketAddressLen > 0, $"Unexpected socketAddressLen: {socketAddressLen}");
@@ -670,20 +668,11 @@ namespace System.Net.Sockets
SetNonBlocking();
- IntPtr acceptedFd;
SocketError errorCode;
if (SocketPal.TryCompleteAccept(_socket, socketAddress, ref socketAddressLen, out acceptedFd, out errorCode))
{
Debug.Assert(errorCode == SocketError.Success || acceptedFd == (IntPtr)(-1), $"Unexpected values: errorCode={errorCode}, acceptedFd={acceptedFd}");
- if (errorCode == SocketError.Success)
- {
- ThreadPool.QueueUserWorkItem(args =>
- {
- var tup = (Tuple<Action<IntPtr, byte[], int, SocketError>, IntPtr, byte[], int>)args;
- tup.Item1(tup.Item2, tup.Item3, tup.Item4, SocketError.Success);
- }, Tuple.Create(callback, acceptedFd, socketAddress, socketAddressLen));
- }
return errorCode;
}
@@ -782,11 +771,6 @@ namespace System.Net.Sockets
{
RegisterConnectResult(errorCode);
- if (errorCode == SocketError.Success)
- {
- ThreadPool.QueueUserWorkItem(arg => ((Action<SocketError>)arg)(SocketError.Success), callback);
- }
-
return errorCode;
}
@@ -827,9 +811,10 @@ namespace System.Net.Sockets
return ReceiveFrom(buffer, offset, count, ref flags, null, ref socketAddressLen, timeout, out bytesReceived);
}
- public SocketError ReceiveAsync(byte[] buffer, int offset, int count, SocketFlags flags, Action<int, byte[], int, SocketFlags, SocketError> callback)
+ public SocketError ReceiveAsync(byte[] buffer, int offset, int count, SocketFlags flags, out int bytesReceived, out SocketFlags receivedFlags, Action<int, byte[], int, SocketFlags, SocketError> callback)
{
- return ReceiveFromAsync(buffer, offset, count, flags, null, 0, callback);
+ int socketAddressLen = 0;
+ return ReceiveFromAsync(buffer, offset, count, flags, null, ref socketAddressLen, out bytesReceived, out receivedFlags, callback);
}
public SocketError ReceiveFrom(byte[] buffer, int offset, int count, ref SocketFlags flags, byte[] socketAddress, ref int socketAddressLen, int timeout, out int bytesReceived)
@@ -897,30 +882,24 @@ namespace System.Net.Sockets
}
}
- public SocketError ReceiveFromAsync(byte[] buffer, int offset, int count, SocketFlags flags, byte[] socketAddress, int socketAddressLen, Action<int, byte[], int, SocketFlags, SocketError> callback)
+ public SocketError ReceiveFromAsync(byte[] buffer, int offset, int count, SocketFlags flags, byte[] socketAddress, ref int socketAddressLen, out int bytesReceived, out SocketFlags receivedFlags, Action<int, byte[], int, SocketFlags, SocketError> callback)
{
SetNonBlocking();
lock (_receiveLock)
{
- int bytesReceived;
- SocketFlags receivedFlags;
SocketError errorCode;
if (_receiveQueue.IsEmpty &&
SocketPal.TryCompleteReceiveFrom(_socket, buffer, offset, count, flags, socketAddress, ref socketAddressLen, out bytesReceived, out receivedFlags, out errorCode))
{
- if (errorCode == SocketError.Success)
- {
- ThreadPool.QueueUserWorkItem(args =>
- {
- var tup = (Tuple<Action<int, byte[], int, SocketFlags, SocketError>, int, byte[], int, SocketFlags>)args;
- tup.Item1(tup.Item2, tup.Item3, tup.Item4, tup.Item5, SocketError.Success);
- }, Tuple.Create(callback, bytesReceived, socketAddress, socketAddressLen, receivedFlags));
- }
+ // Synchronous success or failure
return errorCode;
}
+ bytesReceived = 0;
+ receivedFlags = SocketFlags.None;
+
var operation = new ReceiveOperation
{
Callback = callback,
@@ -955,9 +934,10 @@ namespace System.Net.Sockets
return ReceiveFrom(buffers, ref flags, null, 0, timeout, out bytesReceived);
}
- public SocketError ReceiveAsync(IList<ArraySegment<byte>> buffers, SocketFlags flags, Action<int, byte[], int, SocketFlags, SocketError> callback)
+ public SocketError ReceiveAsync(IList<ArraySegment<byte>> buffers, SocketFlags flags, out int bytesReceived, out SocketFlags receivedFlags, Action<int, byte[], int, SocketFlags, SocketError> callback)
{
- return ReceiveFromAsync(buffers, flags, null, 0, callback);
+ int socketAddressLen = 0;
+ return ReceiveFromAsync(buffers, flags, null, ref socketAddressLen, out bytesReceived, out receivedFlags, callback);
}
public SocketError ReceiveFrom(IList<ArraySegment<byte>> buffers, ref SocketFlags flags, byte[] socketAddress, int socketAddressLen, int timeout, out int bytesReceived)
@@ -1024,7 +1004,7 @@ namespace System.Net.Sockets
}
}
- public SocketError ReceiveFromAsync(IList<ArraySegment<byte>> buffers, SocketFlags flags, byte[] socketAddress, int socketAddressLen, Action<int, byte[], int, SocketFlags, SocketError> callback)
+ public SocketError ReceiveFromAsync(IList<ArraySegment<byte>> buffers, SocketFlags flags, byte[] socketAddress, ref int socketAddressLen, out int bytesReceived, out SocketFlags receivedFlags, Action<int, byte[], int, SocketFlags, SocketError> callback)
{
SetNonBlocking();
@@ -1032,23 +1012,17 @@ namespace System.Net.Sockets
lock (_receiveLock)
{
- int bytesReceived;
- SocketFlags receivedFlags;
SocketError errorCode;
if (_receiveQueue.IsEmpty &&
SocketPal.TryCompleteReceiveFrom(_socket, buffers, flags, socketAddress, ref socketAddressLen, out bytesReceived, out receivedFlags, out errorCode))
{
- if (errorCode == SocketError.Success)
- {
- ThreadPool.QueueUserWorkItem(args =>
- {
- var tup = (Tuple<Action<int, byte[], int, SocketFlags, SocketError>, int, byte[], int, SocketFlags>)args;
- tup.Item1(tup.Item2, tup.Item3, tup.Item4, tup.Item5, SocketError.Success);
- }, Tuple.Create(callback, bytesReceived, socketAddress, socketAddressLen, receivedFlags));
- }
+ // Synchronous success or failure
return errorCode;
}
+ bytesReceived = 0;
+ receivedFlags = SocketFlags.None;
+
operation = new ReceiveOperation
{
Callback = callback,
@@ -1147,31 +1121,25 @@ namespace System.Net.Sockets
}
}
- public SocketError ReceiveMessageFromAsync(byte[] buffer, int offset, int count, SocketFlags flags, byte[] socketAddress, int socketAddressLen, bool isIPv4, bool isIPv6, Action<int, byte[], int, SocketFlags, IPPacketInformation, SocketError> callback)
+ public SocketError ReceiveMessageFromAsync(byte[] buffer, int offset, int count, SocketFlags flags, byte[] socketAddress, ref int socketAddressLen, bool isIPv4, bool isIPv6, out int bytesReceived, out SocketFlags receivedFlags, out IPPacketInformation ipPacketInformation, Action<int, byte[], int, SocketFlags, IPPacketInformation, SocketError> callback)
{
SetNonBlocking();
lock (_receiveLock)
{
- int bytesReceived;
- SocketFlags receivedFlags;
- IPPacketInformation ipPacketInformation;
+ ipPacketInformation = default(IPPacketInformation);
SocketError errorCode;
if (_receiveQueue.IsEmpty &&
SocketPal.TryCompleteReceiveMessageFrom(_socket, buffer, offset, count, flags, socketAddress, ref socketAddressLen, isIPv4, isIPv6, out bytesReceived, out receivedFlags, out ipPacketInformation, out errorCode))
{
- if (errorCode == SocketError.Success)
- {
- ThreadPool.QueueUserWorkItem(args =>
- {
- var tup = (Tuple<Action<int, byte[], int, SocketFlags, IPPacketInformation, SocketError>, int, byte[], int, SocketFlags, IPPacketInformation>)args;
- tup.Item1(tup.Item2, tup.Item3, tup.Item4, tup.Item5, tup.Item6, SocketError.Success);
- }, Tuple.Create(callback, bytesReceived, socketAddress, socketAddressLen, receivedFlags, ipPacketInformation));
- }
+ // Synchronous success or failure
return errorCode;
}
+ bytesReceived = 0;
+ receivedFlags = SocketFlags.None;
+
var operation = new ReceiveMessageFromOperation
{
Callback = callback,
@@ -1208,9 +1176,10 @@ namespace System.Net.Sockets
return SendTo(buffer, offset, count, flags, null, 0, timeout, out bytesSent);
}
- public SocketError SendAsync(byte[] buffer, int offset, int count, SocketFlags flags, Action<int, byte[], int, SocketFlags, SocketError> callback)
+ public SocketError SendAsync(byte[] buffer, int offset, int count, SocketFlags flags, out int bytesSent, Action<int, byte[], int, SocketFlags, SocketError> callback)
{
- return SendToAsync(buffer, offset, count, flags, null, 0, callback);
+ int socketAddressLen = 0;
+ return SendToAsync(buffer, offset, count, flags, null, ref socketAddressLen, out bytesSent, callback);
}
public SocketError SendTo(byte[] buffer, int offset, int count, SocketFlags flags, byte[] socketAddress, int socketAddressLen, int timeout, out int bytesSent)
@@ -1274,26 +1243,19 @@ namespace System.Net.Sockets
}
}
- public SocketError SendToAsync(byte[] buffer, int offset, int count, SocketFlags flags, byte[] socketAddress, int socketAddressLen, Action<int, byte[], int, SocketFlags, SocketError> callback)
+ public SocketError SendToAsync(byte[] buffer, int offset, int count, SocketFlags flags, byte[] socketAddress, ref int socketAddressLen, out int bytesSent, Action<int, byte[], int, SocketFlags, SocketError> callback)
{
SetNonBlocking();
lock (_sendAcceptConnectLock)
{
- int bytesSent = 0;
+ bytesSent = 0;
SocketError errorCode;
if (_sendQueue.IsEmpty &&
SocketPal.TryCompleteSendTo(_socket, buffer, ref offset, ref count, flags, socketAddress, socketAddressLen, ref bytesSent, out errorCode))
{
- if (errorCode == SocketError.Success)
- {
- ThreadPool.QueueUserWorkItem(args =>
- {
- var tup = (Tuple<Action<int, byte[], int, SocketFlags, SocketError>, int, byte[], int>)args;
- tup.Item1(tup.Item2, tup.Item3, tup.Item4, 0, SocketError.Success);
- }, Tuple.Create(callback, bytesSent, socketAddress, socketAddressLen));
- }
+ // Synchronous success or failure
return errorCode;
}
@@ -1332,9 +1294,10 @@ namespace System.Net.Sockets
return SendTo(buffers, flags, null, 0, timeout, out bytesSent);
}
- public SocketError SendAsync(IList<ArraySegment<byte>> buffers, SocketFlags flags, Action<int, byte[], int, SocketFlags, SocketError> callback)
+ public SocketError SendAsync(IList<ArraySegment<byte>> buffers, SocketFlags flags, out int bytesSent, Action<int, byte[], int, SocketFlags, SocketError> callback)
{
- return SendToAsync(buffers, flags, null, 0, callback);
+ int socketAddressLen = 0;
+ return SendToAsync(buffers, flags, null, ref socketAddressLen, out bytesSent, callback);
}
public SocketError SendTo(IList<ArraySegment<byte>> buffers, SocketFlags flags, byte[] socketAddress, int socketAddressLen, int timeout, out int bytesSent)
@@ -1400,28 +1363,23 @@ namespace System.Net.Sockets
}
}
- public SocketError SendToAsync(IList<ArraySegment<byte>> buffers, SocketFlags flags, byte[] socketAddress, int socketAddressLen, Action<int, byte[], int, SocketFlags, SocketError> callback)
+
+
+ public SocketError SendToAsync(IList<ArraySegment<byte>> buffers, SocketFlags flags, byte[] socketAddress, ref int socketAddressLen, out int bytesSent, Action<int, byte[], int, SocketFlags, SocketError> callback)
{
SetNonBlocking();
lock (_sendAcceptConnectLock)
{
+ bytesSent = 0;
int bufferIndex = 0;
int offset = 0;
- int bytesSent = 0;
SocketError errorCode;
if (_sendQueue.IsEmpty &&
SocketPal.TryCompleteSendTo(_socket, buffers, ref bufferIndex, ref offset, flags, socketAddress, socketAddressLen, ref bytesSent, out errorCode))
{
- if (errorCode == SocketError.Success)
- {
- ThreadPool.QueueUserWorkItem(args =>
- {
- var tup = (Tuple<Action<int, byte[], int, SocketFlags, SocketError>, int, byte[], int>)args;
- tup.Item1(tup.Item2, tup.Item3, tup.Item4, SocketFlags.None, SocketError.Success);
- }, Tuple.Create(callback, bytesSent, socketAddress, socketAddressLen));
- }
+ // Synchronous success or failure
return errorCode;
}
@@ -1512,26 +1470,19 @@ namespace System.Net.Sockets
}
}
- public SocketError SendFileAsync(SafeFileHandle fileHandle, long offset, long count, Action<long, SocketError> callback)
+ public SocketError SendFileAsync(SafeFileHandle fileHandle, long offset, long count, out long bytesSent, Action<long, SocketError> callback)
{
SetNonBlocking();
lock (_sendAcceptConnectLock)
{
- long bytesSent = 0;
+ bytesSent = 0;
SocketError errorCode;
if (_sendQueue.IsEmpty &&
SocketPal.TryCompleteSendFile(_socket, fileHandle, ref offset, ref count, ref bytesSent, out errorCode))
{
- if (errorCode == SocketError.Success)
- {
- ThreadPool.QueueUserWorkItem(args =>
- {
- var c = (Action<long, SocketError>)args;
- c(bytesSent, SocketError.Success);
- }, callback);
- }
+ // Synchronous success or failure
return errorCode;
}
diff --git a/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Unix.cs b/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Unix.cs
index e93e2bd5d0..f71663b54b 100644
--- a/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Unix.cs
+++ b/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Unix.cs
@@ -52,14 +52,19 @@ namespace System.Net.Sockets
private void AcceptCompletionCallback(IntPtr acceptedFileDescriptor, byte[] socketAddress, int socketAddressSize, SocketError socketError)
{
+ CompleteAcceptOperation(acceptedFileDescriptor, socketAddress, socketAddressSize, socketError);
+
+ CompletionCallback(0, SocketFlags.None, socketError);
+ }
+
+ private void CompleteAcceptOperation(IntPtr acceptedFileDescriptor, byte[] socketAddress, int socketAddressSize, SocketError socketError)
+ {
_acceptedFileDescriptor = acceptedFileDescriptor;
Debug.Assert(socketAddress == null || socketAddress == _acceptBuffer, $"Unexpected socketAddress: {socketAddress}");
_acceptAddressBufferCount = socketAddressSize;
-
- CompletionCallback(0, socketError);
}
- internal unsafe SocketError DoOperationAccept(Socket socket, SafeCloseSocket handle, SafeCloseSocket acceptHandle, out int bytesTransferred)
+ internal unsafe SocketError DoOperationAccept(Socket socket, SafeCloseSocket handle, SafeCloseSocket acceptHandle)
{
if (_buffer != null)
{
@@ -68,9 +73,17 @@ namespace System.Net.Sockets
Debug.Assert(acceptHandle == null, $"Unexpected acceptHandle: {acceptHandle}");
- bytesTransferred = 0;
+ IntPtr acceptedFd;
+ int socketAddressLen = _acceptAddressBufferCount / 2;
+ SocketError socketError = handle.AsyncContext.AcceptAsync(_acceptBuffer, ref socketAddressLen, out acceptedFd, AcceptCompletionCallback);
- return handle.AsyncContext.AcceptAsync(_acceptBuffer, _acceptAddressBufferCount / 2, AcceptCompletionCallback);
+ if (socketError != SocketError.IOPending)
+ {
+ CompleteAcceptOperation(acceptedFd, _acceptBuffer, socketAddressLen, socketError);
+ FinishOperationSync(socketError, 0, SocketFlags.None);
+ }
+
+ return socketError;
}
private void InnerStartOperationConnect()
@@ -80,14 +93,17 @@ namespace System.Net.Sockets
private void ConnectCompletionCallback(SocketError socketError)
{
- CompletionCallback(0, socketError);
+ CompletionCallback(0, SocketFlags.None, socketError);
}
- internal unsafe SocketError DoOperationConnect(Socket socket, SafeCloseSocket handle, out int bytesTransferred)
+ internal unsafe SocketError DoOperationConnect(Socket socket, SafeCloseSocket handle)
{
- bytesTransferred = 0;
-
- return handle.AsyncContext.ConnectAsync(_socketAddress.Buffer, _socketAddress.Size, ConnectCompletionCallback);
+ SocketError socketError = handle.AsyncContext.ConnectAsync(_socketAddress.Buffer, _socketAddress.Size, ConnectCompletionCallback);
+ if (socketError != SocketError.IOPending)
+ {
+ FinishOperationSync(socketError, 0, SocketFlags.None);
+ }
+ return socketError;
}
internal SocketError DoOperationDisconnect(Socket socket, SafeCloseSocket handle)
@@ -105,11 +121,16 @@ namespace System.Net.Sockets
private void TransferCompletionCallbackCore(int bytesTransferred, byte[] socketAddress, int socketAddressSize, SocketFlags receivedFlags, SocketError socketError)
{
+ CompleteTransferOperation(bytesTransferred, socketAddress, socketAddressSize, receivedFlags, socketError);
+
+ CompletionCallback(bytesTransferred, receivedFlags, socketError);
+ }
+
+ private void CompleteTransferOperation(int bytesTransferred, byte[] socketAddress, int socketAddressSize, SocketFlags receivedFlags, SocketError socketError)
+ {
Debug.Assert(socketAddress == null || socketAddress == _socketAddress.Buffer, $"Unexpected socketAddress: {socketAddress}");
_socketAddressSize = socketAddressSize;
_receivedFlags = receivedFlags;
-
- CompletionCallback(bytesTransferred, socketError);
}
private void InnerStartOperationReceive()
@@ -118,20 +139,25 @@ namespace System.Net.Sockets
_socketAddressSize = 0;
}
- internal unsafe SocketError DoOperationReceive(SafeCloseSocket handle, out SocketFlags flags, out int bytesTransferred)
+ internal unsafe SocketError DoOperationReceive(SafeCloseSocket handle, out SocketFlags flags)
{
+ int bytesReceived;
SocketError errorCode;
if (_buffer != null)
{
- errorCode = handle.AsyncContext.ReceiveAsync(_buffer, _offset, _count, _socketFlags, TransferCompletionCallback);
+ errorCode = handle.AsyncContext.ReceiveAsync(_buffer, _offset, _count, _socketFlags, out bytesReceived, out flags, TransferCompletionCallback);
}
else
{
- errorCode = handle.AsyncContext.ReceiveAsync(_bufferList, _socketFlags, TransferCompletionCallback);
+ errorCode = handle.AsyncContext.ReceiveAsync(_bufferList, _socketFlags, out bytesReceived, out flags, TransferCompletionCallback);
+ }
+
+ if (errorCode != SocketError.IOPending)
+ {
+ CompleteTransferOperation(bytesReceived, null, 0, flags, errorCode);
+ FinishOperationSync(errorCode, bytesReceived, flags);
}
- flags = _socketFlags;
- bytesTransferred = 0;
return errorCode;
}
@@ -141,20 +167,26 @@ namespace System.Net.Sockets
_socketAddressSize = 0;
}
- internal unsafe SocketError DoOperationReceiveFrom(SafeCloseSocket handle, out SocketFlags flags, out int bytesTransferred)
+ internal unsafe SocketError DoOperationReceiveFrom(SafeCloseSocket handle, out SocketFlags flags)
{
SocketError errorCode;
+ int bytesReceived = 0;
+ int socketAddressLen = _socketAddress.Size;
if (_buffer != null)
{
- errorCode = handle.AsyncContext.ReceiveFromAsync(_buffer, _offset, _count, _socketFlags, _socketAddress.Buffer, _socketAddress.Size, TransferCompletionCallback);
+ errorCode = handle.AsyncContext.ReceiveFromAsync(_buffer, _offset, _count, _socketFlags, _socketAddress.Buffer, ref socketAddressLen, out bytesReceived, out flags, TransferCompletionCallback);
}
else
{
- errorCode = handle.AsyncContext.ReceiveFromAsync(_bufferList, _socketFlags, _socketAddress.Buffer, _socketAddress.Size, TransferCompletionCallback);
+ errorCode = handle.AsyncContext.ReceiveFromAsync(_bufferList, _socketFlags, _socketAddress.Buffer, ref socketAddressLen, out bytesReceived, out flags, TransferCompletionCallback);
+ }
+
+ if (errorCode != SocketError.IOPending)
+ {
+ CompleteTransferOperation(bytesReceived, _socketAddress.Buffer, socketAddressLen, flags, errorCode);
+ FinishOperationSync(errorCode, bytesReceived, flags);
}
- flags = _socketFlags;
- bytesTransferred = 0;
return errorCode;
}
@@ -167,23 +199,37 @@ namespace System.Net.Sockets
private void ReceiveMessageFromCompletionCallback(int bytesTransferred, byte[] socketAddress, int socketAddressSize, SocketFlags receivedFlags, IPPacketInformation ipPacketInformation, SocketError errorCode)
{
+ CompleteReceiveMessageFromOperation(bytesTransferred, socketAddress, socketAddressSize, receivedFlags, ipPacketInformation, errorCode);
+
+ CompletionCallback(bytesTransferred, receivedFlags, errorCode);
+ }
+
+ private void CompleteReceiveMessageFromOperation(int bytesTransferred, byte[] socketAddress, int socketAddressSize, SocketFlags receivedFlags, IPPacketInformation ipPacketInformation, SocketError errorCode)
+ {
Debug.Assert(_socketAddress != null, "Expected non-null _socketAddress");
Debug.Assert(socketAddress == null || _socketAddress.Buffer == socketAddress, $"Unexpected socketAddress: {socketAddress}");
_socketAddressSize = socketAddressSize;
_receivedFlags = receivedFlags;
_receiveMessageFromPacketInfo = ipPacketInformation;
-
- CompletionCallback(bytesTransferred, errorCode);
}
- internal unsafe SocketError DoOperationReceiveMessageFrom(Socket socket, SafeCloseSocket handle, out int bytesTransferred)
+ internal unsafe SocketError DoOperationReceiveMessageFrom(Socket socket, SafeCloseSocket handle)
{
bool isIPv4, isIPv6;
Socket.GetIPProtocolInformation(socket.AddressFamily, _socketAddress, out isIPv4, out isIPv6);
- bytesTransferred = 0;
- return handle.AsyncContext.ReceiveMessageFromAsync(_buffer, _offset, _count, _socketFlags, _socketAddress.Buffer, _socketAddress.Size, isIPv4, isIPv6, ReceiveMessageFromCompletionCallback);
+ int socketAddressSize = _socketAddress.Size;
+ int bytesReceived;
+ SocketFlags receivedFlags;
+ IPPacketInformation ipPacketInformation;
+ SocketError socketError = handle.AsyncContext.ReceiveMessageFromAsync(_buffer, _offset, _count, _socketFlags, _socketAddress.Buffer, ref socketAddressSize, isIPv4, isIPv6, out bytesReceived, out receivedFlags, out ipPacketInformation, ReceiveMessageFromCompletionCallback);
+ if (socketError != SocketError.IOPending)
+ {
+ CompleteReceiveMessageFromOperation(bytesReceived, _socketAddress.Buffer, socketAddressSize, receivedFlags, ipPacketInformation, socketError);
+ FinishOperationSync(socketError, bytesReceived, receivedFlags);
+ }
+ return socketError;
}
private void InnerStartOperationSend()
@@ -192,19 +238,25 @@ namespace System.Net.Sockets
_socketAddressSize = 0;
}
- internal unsafe SocketError DoOperationSend(SafeCloseSocket handle, out int bytesTransferred)
+ internal unsafe SocketError DoOperationSend(SafeCloseSocket handle)
{
+ int bytesSent;
SocketError errorCode;
if (_buffer != null)
{
- errorCode = handle.AsyncContext.SendAsync(_buffer, _offset, _count, _socketFlags, TransferCompletionCallback);
+ errorCode = handle.AsyncContext.SendAsync(_buffer, _offset, _count, _socketFlags, out bytesSent, TransferCompletionCallback);
}
else
{
- errorCode = handle.AsyncContext.SendAsync(_bufferList, _socketFlags, TransferCompletionCallback);
+ errorCode = handle.AsyncContext.SendAsync(_bufferList, _socketFlags, out bytesSent, TransferCompletionCallback);
+ }
+
+ if (errorCode != SocketError.IOPending)
+ {
+ CompleteTransferOperation(bytesSent, null, 0, SocketFlags.None, errorCode);
+ FinishOperationSync(errorCode, bytesSent, SocketFlags.None);
}
- bytesTransferred = 0;
return errorCode;
}
@@ -224,19 +276,26 @@ namespace System.Net.Sockets
_socketAddressSize = 0;
}
- internal SocketError DoOperationSendTo(SafeCloseSocket handle, out int bytesTransferred)
+ internal SocketError DoOperationSendTo(SafeCloseSocket handle)
{
+ int bytesSent;
+ int socketAddressLen = _socketAddress.Size;
SocketError errorCode;
if (_buffer != null)
{
- errorCode = handle.AsyncContext.SendToAsync(_buffer, _offset, _count, _socketFlags, _socketAddress.Buffer, _socketAddress.Size, TransferCompletionCallback);
+ errorCode = handle.AsyncContext.SendToAsync(_buffer, _offset, _count, _socketFlags, _socketAddress.Buffer, ref socketAddressLen, out bytesSent, TransferCompletionCallback);
}
else
{
- errorCode = handle.AsyncContext.SendToAsync(_bufferList, _socketFlags, _socketAddress.Buffer, _socketAddress.Size, TransferCompletionCallback);
+ errorCode = handle.AsyncContext.SendToAsync(_bufferList, _socketFlags, _socketAddress.Buffer, ref socketAddressLen, out bytesSent, TransferCompletionCallback);
+ }
+
+ if (errorCode != SocketError.IOPending)
+ {
+ CompleteTransferOperation(bytesSent, _socketAddress.Buffer, socketAddressLen, SocketFlags.None, errorCode);
+ FinishOperationSync(errorCode, bytesSent, SocketFlags.None);
}
- bytesTransferred = 0;
return errorCode;
}
@@ -289,11 +348,11 @@ namespace System.Net.Sockets
throw new PlatformNotSupportedException();
}
- private void CompletionCallback(int bytesTransferred, SocketError socketError)
+ private void CompletionCallback(int bytesTransferred, SocketFlags flags, SocketError socketError)
{
if (socketError == SocketError.Success)
{
- FinishOperationSuccess(socketError, bytesTransferred, _receivedFlags);
+ FinishOperationAsyncSuccess(bytesTransferred, flags);
}
else
{
@@ -302,7 +361,7 @@ namespace System.Net.Sockets
socketError = SocketError.OperationAborted;
}
- FinishOperationAsyncFailure(socketError, bytesTransferred, _receivedFlags);
+ FinishOperationAsyncFailure(socketError, bytesTransferred, flags);
}
}
}
diff --git a/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs b/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs
index 53d6c029c7..934942018b 100644
--- a/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs
+++ b/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.Windows.cs
@@ -127,7 +127,7 @@ namespace System.Net.Sockets
Debug.Assert(_currentSocket.SafeHandle != null, "_currentSocket.SafeHandle is null");
Debug.Assert(!_currentSocket.SafeHandle.IsInvalid, "_currentSocket.SafeHandle is invalid");
- ThreadPoolBoundHandle boundHandle = _currentSocket.SafeHandle.GetOrAllocateThreadPoolBoundHandle();
+ ThreadPoolBoundHandle boundHandle = _currentSocket.GetOrAllocateThreadPoolBoundHandle();
NativeOverlapped* overlapped = null;
if (_preAllocatedOverlapped != null)
@@ -157,15 +157,40 @@ namespace System.Net.Sockets
}
}
- private void CompleteIOCPOperation()
+ private SocketError ProcessIOCPResult(bool success, int bytesTransferred)
{
- // TODO #4900: Optimization to remove callbacks if the operations are completed synchronously:
- // Use SetFileCompletionNotificationModes(FILE_SKIP_COMPLETION_PORT_ON_SUCCESS).
+ if (success)
+ {
+ // Synchronous success.
+ if (_currentSocket.SafeHandle.SkipCompletionPortOnSuccess)
+ {
+ // The socket handle is configured to skip completion on success,
+ // so we can set the results right now.
+ FinishOperationSyncSuccess(bytesTransferred, SocketFlags.None);
+ return SocketError.Success;
+ }
+
+ // Socket handle is going to post a completion to the completion port (may have done so already).
+ // Return pending and we will continue in the completion port callback.
+ return SocketError.IOPending;
+ }
- // If SetFileCompletionNotificationModes(FILE_SKIP_COMPLETION_PORT_ON_SUCCESS) is not set on this handle
- // it is guaranteed that the IOCP operation will be completed in the callback even if Socket.Success was
- // returned by the Win32 API.
+ // Get the socket error (which may be IOPending)
+ SocketError errorCode = SocketPal.GetLastSocketError();
+
+ if (errorCode == SocketError.IOPending)
+ {
+ return errorCode;
+ }
+
+ FinishOperationSyncFailure(errorCode, bytesTransferred, SocketFlags.None);
+
+ // Note, the overlapped will be release in CompleteIOCPOperation below, for either success or failure
+ return errorCode;
+ }
+ private void CompleteIOCPOperation()
+ {
// Required to allow another IOCP operation for the same handle. We release the native overlapped
// in the safe handle, but keep the safe handle object around so as to be able to reuse it
// for other operations.
@@ -180,13 +205,12 @@ namespace System.Net.Sockets
}
}
- internal unsafe SocketError DoOperationAccept(Socket socket, SafeCloseSocket handle, SafeCloseSocket acceptHandle, out int bytesTransferred)
+ internal unsafe SocketError DoOperationAccept(Socket socket, SafeCloseSocket handle, SafeCloseSocket acceptHandle)
{
PrepareIOCPOperation();
- SocketError socketError = SocketError.Success;
-
- if (!socket.AcceptEx(
+ int bytesTransferred;
+ bool success = socket.AcceptEx(
handle,
acceptHandle,
(_ptrSingleBuffer != IntPtr.Zero) ? _ptrSingleBuffer : _ptrAcceptBuffer,
@@ -194,12 +218,9 @@ namespace System.Net.Sockets
_acceptAddressBufferCount / 2,
_acceptAddressBufferCount / 2,
out bytesTransferred,
- _ptrNativeOverlapped))
- {
- socketError = SocketPal.GetLastSocketError();
- }
+ _ptrNativeOverlapped);
- return socketError;
+ return ProcessIOCPResult(success, bytesTransferred);
}
private void InnerStartOperationConnect()
@@ -213,25 +234,21 @@ namespace System.Net.Sockets
CheckPinNoBuffer();
}
- internal unsafe SocketError DoOperationConnect(Socket socket, SafeCloseSocket handle, out int bytesTransferred)
+ internal unsafe SocketError DoOperationConnect(Socket socket, SafeCloseSocket handle)
{
PrepareIOCPOperation();
- SocketError socketError = SocketError.Success;
-
- if (!socket.ConnectEx(
+ int bytesTransferred;
+ bool success = socket.ConnectEx(
handle,
_ptrSocketAddressBuffer,
_socketAddress.Size,
_ptrSingleBuffer,
Count,
out bytesTransferred,
- _ptrNativeOverlapped))
- {
- socketError = SocketPal.GetLastSocketError();
- }
+ _ptrNativeOverlapped);
- return socketError;
+ return ProcessIOCPResult(success, bytesTransferred);
}
private void InnerStartOperationDisconnect()
@@ -243,18 +260,13 @@ namespace System.Net.Sockets
{
PrepareIOCPOperation();
- SocketError socketError = SocketError.Success;
-
- if (!socket.DisconnectEx(
+ bool success = socket.DisconnectEx(
handle,
_ptrNativeOverlapped,
(int)(DisconnectReuseSocket ? TransmitFileOptions.ReuseSocket : 0),
- 0))
- {
- socketError = SocketPal.GetLastSocketError();
- }
+ 0);
- return socketError;
+ return ProcessIOCPResult(success, 0);
}
private void InnerStartOperationReceive()
@@ -274,12 +286,13 @@ namespace System.Net.Sockets
// An array of WSABuffer descriptors is allocated.
}
- internal unsafe SocketError DoOperationReceive(SafeCloseSocket handle, out SocketFlags flags, out int bytesTransferred)
+ internal unsafe SocketError DoOperationReceive(SafeCloseSocket handle, out SocketFlags flags)
{
PrepareIOCPOperation();
flags = _socketFlags;
+ int bytesTransferred;
SocketError socketError;
if (_buffer != null)
{
@@ -306,12 +319,7 @@ namespace System.Net.Sockets
IntPtr.Zero);
}
- if (socketError == SocketError.SocketError)
- {
- socketError = SocketPal.GetLastSocketError();
- }
-
- return socketError;
+ return ProcessIOCPResult(socketError == SocketError.Success, bytesTransferred);
}
private void InnerStartOperationReceiveFrom()
@@ -334,12 +342,13 @@ namespace System.Net.Sockets
PinSocketAddressBuffer();
}
- internal unsafe SocketError DoOperationReceiveFrom(SafeCloseSocket handle, out SocketFlags flags, out int bytesTransferred)
+ internal unsafe SocketError DoOperationReceiveFrom(SafeCloseSocket handle, out SocketFlags flags)
{
PrepareIOCPOperation();
flags = _socketFlags;
+ int bytesTransferred;
SocketError socketError;
if (_buffer != null)
{
@@ -368,12 +377,7 @@ namespace System.Net.Sockets
IntPtr.Zero);
}
- if (socketError == SocketError.SocketError)
- {
- socketError = SocketPal.GetLastSocketError();
- }
-
- return socketError;
+ return ProcessIOCPResult(socketError == SocketError.Success, bytesTransferred);
}
private void InnerStartOperationReceiveMessageFrom()
@@ -465,10 +469,11 @@ namespace System.Net.Sockets
}
}
- internal unsafe SocketError DoOperationReceiveMessageFrom(Socket socket, SafeCloseSocket handle, out int bytesTransferred)
+ internal unsafe SocketError DoOperationReceiveMessageFrom(Socket socket, SafeCloseSocket handle)
{
PrepareIOCPOperation();
+ int bytesTransferred;
SocketError socketError = socket.WSARecvMsg(
handle,
_ptrWSAMessageBuffer,
@@ -476,12 +481,7 @@ namespace System.Net.Sockets
_ptrNativeOverlapped,
IntPtr.Zero);
- if (socketError == SocketError.SocketError)
- {
- socketError = SocketPal.GetLastSocketError();
- }
-
- return socketError;
+ return ProcessIOCPResult(socketError == SocketError.Success, bytesTransferred);
}
private void InnerStartOperationSend()
@@ -501,11 +501,12 @@ namespace System.Net.Sockets
// An array of WSABuffer descriptors is allocated.
}
- internal unsafe SocketError DoOperationSend(SafeCloseSocket handle, out int bytesTransferred)
+ internal unsafe SocketError DoOperationSend(SafeCloseSocket handle)
{
PrepareIOCPOperation();
SocketError socketError;
+ int bytesTransferred;
if (_buffer != null)
{
// Single buffer case.
@@ -531,12 +532,7 @@ namespace System.Net.Sockets
IntPtr.Zero);
}
- if (socketError == SocketError.SocketError)
- {
- socketError = SocketPal.GetLastSocketError();
- }
-
- return socketError;
+ return ProcessIOCPResult(socketError == SocketError.Success, bytesTransferred);
}
private void InnerStartOperationSendPackets()
@@ -640,7 +636,7 @@ namespace System.Net.Sockets
_ptrNativeOverlapped,
_sendPacketsFlags);
- return result ? SocketError.Success : SocketPal.GetLastSocketError();
+ return ProcessIOCPResult(result, 0);
}
private void InnerStartOperationSendTo()
@@ -663,10 +659,11 @@ namespace System.Net.Sockets
PinSocketAddressBuffer();
}
- internal SocketError DoOperationSendTo(SafeCloseSocket handle, out int bytesTransferred)
+ internal SocketError DoOperationSendTo(SafeCloseSocket handle)
{
PrepareIOCPOperation();
+ int bytesTransferred;
SocketError socketError;
if (_buffer != null)
{
@@ -696,12 +693,7 @@ namespace System.Net.Sockets
IntPtr.Zero);
}
- if (socketError == SocketError.SocketError)
- {
- socketError = SocketPal.GetLastSocketError();
- }
-
- return socketError;
+ return ProcessIOCPResult(socketError == SocketError.Success, bytesTransferred);
}
// Ensures Overlapped object exists for operations that need no data buffer.
@@ -1218,7 +1210,7 @@ namespace System.Net.Sockets
if (socketError == SocketError.Success)
{
- FinishOperationSuccess(socketError, (int)numBytes, socketFlags);
+ FinishOperationAsyncSuccess((int)numBytes, SocketFlags.None);
}
else
{
@@ -1249,6 +1241,7 @@ namespace System.Net.Sockets
}
}
}
+
FinishOperationAsyncFailure(socketError, (int)numBytes, socketFlags);
}
diff --git a/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.cs b/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.cs
index 4b01cf738b..fcc13d76f5 100644
--- a/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.cs
+++ b/src/System.Net.Sockets/src/System/Net/Sockets/SocketAsyncEventArgs.cs
@@ -585,6 +585,20 @@ namespace System.Net.Sockets
}
}
+ internal void FinishOperationSync(SocketError socketError, int bytesTransferred, SocketFlags flags)
+ {
+ Debug.Assert(socketError != SocketError.IOPending);
+
+ if (socketError == SocketError.Success)
+ {
+ FinishOperationSyncSuccess(bytesTransferred, flags);
+ }
+ else
+ {
+ FinishOperationSyncFailure(socketError, bytesTransferred, flags);
+ }
+ }
+
internal void FinishOperationSyncFailure(SocketError socketError, int bytesTransferred, SocketFlags flags)
{
SetResults(socketError, bytesTransferred, flags);
@@ -671,10 +685,11 @@ namespace System.Net.Sockets
}
}
- internal void FinishOperationSuccess(SocketError socketError, int bytesTransferred, SocketFlags flags)
+ internal void FinishOperationSyncSuccess(int bytesTransferred, SocketFlags flags)
{
- SetResults(socketError, bytesTransferred, flags);
+ SetResults(SocketError.Success, bytesTransferred, flags);
+ SocketError socketError = SocketError.Success;
switch (_completedOperation)
{
case SocketAsyncOperation.Accept:
@@ -871,8 +886,15 @@ namespace System.Net.Sockets
_currentSocket.UpdateStatusAfterSocketError(socketError);
}
- // Complete the operation and raise completion event.
+ // Complete the operation.
Complete();
+ }
+
+ internal void FinishOperationAsyncSuccess(int bytesTransferred, SocketFlags flags)
+ {
+ FinishOperationSyncSuccess(bytesTransferred, flags);
+
+ // Raise completion event.
if (_context == null)
{
OnCompleted(this);
diff --git a/src/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Unix.cs b/src/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Unix.cs
index dc53587964..67aa36c33d 100644
--- a/src/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Unix.cs
+++ b/src/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Unix.cs
@@ -1341,46 +1341,98 @@ namespace System.Net.Sockets
public static SocketError ConnectAsync(Socket socket, SafeCloseSocket handle, byte[] socketAddress, int socketAddressLen, ConnectOverlappedAsyncResult asyncResult)
{
- return handle.AsyncContext.ConnectAsync(socketAddress, socketAddressLen, asyncResult.CompletionCallback);
+ SocketError socketError = handle.AsyncContext.ConnectAsync(socketAddress, socketAddressLen, asyncResult.CompletionCallback);
+ if (socketError == SocketError.Success)
+ {
+ asyncResult.CompletionCallback(SocketError.Success);
+ }
+ return socketError;
}
public static SocketError SendAsync(SafeCloseSocket handle, byte[] buffer, int offset, int count, SocketFlags socketFlags, OverlappedAsyncResult asyncResult)
{
- return handle.AsyncContext.SendAsync(buffer, offset, count, socketFlags, asyncResult.CompletionCallback);
+ int bytesSent;
+ SocketError socketError = handle.AsyncContext.SendAsync(buffer, offset, count, socketFlags, out bytesSent, asyncResult.CompletionCallback);
+ if (socketError == SocketError.Success)
+ {
+ asyncResult.CompletionCallback(bytesSent, null, 0, SocketFlags.None, SocketError.Success);
+ }
+ return socketError;
}
public static SocketError SendAsync(SafeCloseSocket handle, IList<ArraySegment<byte>> buffers, SocketFlags socketFlags, OverlappedAsyncResult asyncResult)
{
- return handle.AsyncContext.SendAsync(buffers, socketFlags, asyncResult.CompletionCallback);
+ int bytesSent;
+ SocketError socketError = handle.AsyncContext.SendAsync(buffers, socketFlags, out bytesSent, asyncResult.CompletionCallback);
+ if (socketError == SocketError.Success)
+ {
+ asyncResult.CompletionCallback(bytesSent, null, 0, SocketFlags.None, SocketError.Success);
+ }
+ return socketError;
}
public static SocketError SendFileAsync(SafeCloseSocket handle, FileStream fileStream, Action<long, SocketError> callback)
{
- return handle.AsyncContext.SendFileAsync(fileStream.SafeFileHandle, 0, (int)fileStream.Length, callback);
+ long bytesSent;
+ SocketError socketError = handle.AsyncContext.SendFileAsync(fileStream.SafeFileHandle, 0, (int)fileStream.Length, out bytesSent, callback);
+ if (socketError == SocketError.Success)
+ {
+ callback(bytesSent, SocketError.Success);
+ }
+ return socketError;
}
public static SocketError SendToAsync(SafeCloseSocket handle, byte[] buffer, int offset, int count, SocketFlags socketFlags, Internals.SocketAddress socketAddress, OverlappedAsyncResult asyncResult)
{
asyncResult.SocketAddress = socketAddress;
- return handle.AsyncContext.SendToAsync(buffer, offset, count, socketFlags, socketAddress.Buffer, socketAddress.Size, asyncResult.CompletionCallback);
+ int bytesSent;
+ int socketAddressLen = socketAddress.Size;
+ SocketError socketError = handle.AsyncContext.SendToAsync(buffer, offset, count, socketFlags, socketAddress.Buffer, ref socketAddressLen, out bytesSent, asyncResult.CompletionCallback);
+ if (socketError == SocketError.Success)
+ {
+ asyncResult.CompletionCallback(bytesSent, socketAddress.Buffer, socketAddressLen, SocketFlags.None, SocketError.Success);
+ }
+ return socketError;
}
public static SocketError ReceiveAsync(SafeCloseSocket handle, byte[] buffer, int offset, int count, SocketFlags socketFlags, OverlappedAsyncResult asyncResult)
{
- return handle.AsyncContext.ReceiveAsync(buffer, offset, count, socketFlags, asyncResult.CompletionCallback);
+ int bytesReceived;
+ SocketFlags receivedFlags;
+ SocketError socketError = handle.AsyncContext.ReceiveAsync(buffer, offset, count, socketFlags, out bytesReceived, out receivedFlags, asyncResult.CompletionCallback);
+ if (socketError == SocketError.Success)
+ {
+ asyncResult.CompletionCallback(bytesReceived, null, 0, receivedFlags, SocketError.Success);
+ }
+ return socketError;
}
public static SocketError ReceiveAsync(SafeCloseSocket handle, IList<ArraySegment<byte>> buffers, SocketFlags socketFlags, OverlappedAsyncResult asyncResult)
{
- return handle.AsyncContext.ReceiveAsync(buffers, socketFlags, asyncResult.CompletionCallback);
+ int bytesReceived;
+ SocketFlags receivedFlags;
+ SocketError socketError = handle.AsyncContext.ReceiveAsync(buffers, socketFlags, out bytesReceived, out receivedFlags, asyncResult.CompletionCallback);
+ if (socketError == SocketError.Success)
+ {
+ asyncResult.CompletionCallback(bytesReceived, null, 0, receivedFlags, SocketError.Success);
+ }
+ return socketError;
}
public static SocketError ReceiveFromAsync(SafeCloseSocket handle, byte[] buffer, int offset, int count, SocketFlags socketFlags, Internals.SocketAddress socketAddress, OverlappedAsyncResult asyncResult)
{
asyncResult.SocketAddress = socketAddress;
- return handle.AsyncContext.ReceiveFromAsync(buffer, offset, count, socketFlags, socketAddress.Buffer, socketAddress.InternalSize, asyncResult.CompletionCallback);
+ int socketAddressSize = socketAddress.InternalSize;
+ int bytesReceived;
+ SocketFlags receivedFlags;
+ SocketError socketError = handle.AsyncContext.ReceiveFromAsync(buffer, offset, count, socketFlags, socketAddress.Buffer, ref socketAddressSize, out bytesReceived, out receivedFlags, asyncResult.CompletionCallback);
+ if (socketError == SocketError.Success)
+ {
+ asyncResult.CompletionCallback(bytesReceived, socketAddress.Buffer, socketAddressSize, receivedFlags, SocketError.Success);
+ }
+ return socketError;
}
public static SocketError ReceiveMessageFromAsync(Socket socket, SafeCloseSocket handle, byte[] buffer, int offset, int count, SocketFlags socketFlags, Internals.SocketAddress socketAddress, ReceiveMessageOverlappedAsyncResult asyncResult)
@@ -1390,7 +1442,16 @@ namespace System.Net.Sockets
bool isIPv4, isIPv6;
Socket.GetIPProtocolInformation(((Socket)asyncResult.AsyncObject).AddressFamily, socketAddress, out isIPv4, out isIPv6);
- return handle.AsyncContext.ReceiveMessageFromAsync(buffer, offset, count, socketFlags, socketAddress.Buffer, socketAddress.InternalSize, isIPv4, isIPv6, asyncResult.CompletionCallback);
+ int socketAddressSize = socketAddress.InternalSize;
+ int bytesReceived;
+ SocketFlags receivedFlags;
+ IPPacketInformation ipPacketInformation;
+ SocketError socketError = handle.AsyncContext.ReceiveMessageFromAsync(buffer, offset, count, socketFlags, socketAddress.Buffer, ref socketAddressSize, isIPv4, isIPv6, out bytesReceived, out receivedFlags, out ipPacketInformation, asyncResult.CompletionCallback);
+ if (socketError == SocketError.Success)
+ {
+ asyncResult.CompletionCallback(bytesReceived, socketAddress.Buffer, socketAddressSize, receivedFlags, ipPacketInformation, SocketError.Success);
+ }
+ return socketError;
}
public static SocketError AcceptAsync(Socket socket, SafeCloseSocket handle, SafeCloseSocket acceptHandle, int receiveSize, int socketAddressSize, AcceptOverlappedAsyncResult asyncResult)
@@ -1400,7 +1461,14 @@ namespace System.Net.Sockets
byte[] socketAddressBuffer = new byte[socketAddressSize];
- return handle.AsyncContext.AcceptAsync(socketAddressBuffer, socketAddressSize, asyncResult.CompletionCallback);
+ IntPtr acceptedFd;
+ SocketError socketError = handle.AsyncContext.AcceptAsync(socketAddressBuffer, ref socketAddressSize, out acceptedFd, asyncResult.CompletionCallback);
+ if (socketError == SocketError.Success)
+ {
+ asyncResult.CompletionCallback(acceptedFd, socketAddressBuffer, socketAddressSize, SocketError.Success);
+ }
+
+ return socketError;
}
internal static SocketError DisconnectAsync(Socket socket, SafeCloseSocket handle, bool reuseSocket, DisconnectOverlappedAsyncResult asyncResult)
diff --git a/src/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs b/src/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs
index 009a541edf..2b216eb6bd 100644
--- a/src/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs
+++ b/src/System.Net.Sockets/src/System/Net/Sockets/SocketPal.Windows.cs
@@ -214,7 +214,8 @@ namespace System.Net.Sockets
fixed (byte* prePinnedBuffer = preBuffer)
fixed (byte* postPinnedBuffer = postBuffer)
{
- return TransmitFileHelper(handle, fileHandle, SafeNativeOverlapped.Zero, preBuffer, postBuffer, flags);
+ bool success = TransmitFileHelper(handle, fileHandle, SafeNativeOverlapped.Zero, preBuffer, postBuffer, flags);
+ return (success ? SocketError.Success : SocketPal.GetLastSocketError());
}
}
@@ -802,76 +803,82 @@ namespace System.Net.Sockets
{
// This will pin the socketAddress buffer.
asyncResult.SetUnmanagedStructures(socketAddress);
+ try
+ {
+ int ignoreBytesSent;
+ bool success = socket.ConnectEx(
+ handle,
+ Marshal.UnsafeAddrOfPinnedArrayElement(socketAddress, 0),
+ socketAddressLen,
+ IntPtr.Zero,
+ 0,
+ out ignoreBytesSent,
+ asyncResult.OverlappedHandle);
- int ignoreBytesSent;
- if (!socket.ConnectEx(
- handle,
- Marshal.UnsafeAddrOfPinnedArrayElement(socketAddress, 0),
- socketAddressLen,
- IntPtr.Zero,
- 0,
- out ignoreBytesSent,
- asyncResult.OverlappedHandle))
+ return asyncResult.ProcessOverlappedResult(success, 0);
+ }
+ catch
{
- return GetLastSocketError();
+ asyncResult.ReleaseUnmanagedStructures();
+ throw;
}
-
- return SocketError.Success;
}
public static unsafe SocketError SendAsync(SafeCloseSocket handle, byte[] buffer, int offset, int count, SocketFlags socketFlags, OverlappedAsyncResult asyncResult)
{
- // Set up asyncResult for overlapped WSASend.
- // This call will use completion ports.
+ // Set up unmanaged structures for overlapped WSASend.
asyncResult.SetUnmanagedStructures(buffer, offset, count, null, false /*don't pin null remoteEP*/);
+ try
+ {
+ // This can throw ObjectDisposedException.
+ int bytesTransferred;
+ SocketError errorCode = Interop.Winsock.WSASend(
+ handle,
+ ref asyncResult._singleBuffer,
+ 1, // There is only ever 1 buffer being sent.
+ out bytesTransferred,
+ socketFlags,
+ asyncResult.OverlappedHandle,
+ IntPtr.Zero);
- // This can throw ObjectDisposedException.
- int bytesTransferred;
- SocketError errorCode = Interop.Winsock.WSASend(
- handle,
- ref asyncResult._singleBuffer,
- 1, // There is only ever 1 buffer being sent.
- out bytesTransferred,
- socketFlags,
- asyncResult.OverlappedHandle,
- IntPtr.Zero);
-
- if (errorCode != SocketError.Success)
+ return asyncResult.ProcessOverlappedResult(errorCode == SocketError.Success, bytesTransferred);
+ }
+ catch
{
- errorCode = GetLastSocketError();
+ asyncResult.ReleaseUnmanagedStructures();
+ throw;
}
-
- return errorCode;
}
public static unsafe SocketError SendAsync(SafeCloseSocket handle, IList<ArraySegment<byte>> buffers, SocketFlags socketFlags, OverlappedAsyncResult asyncResult)
{
// Set up asyncResult for overlapped WSASend.
- // This call will use completion ports.
asyncResult.SetUnmanagedStructures(buffers);
+ try
+ {
+ // This can throw ObjectDisposedException.
+ int bytesTransferred;
+ SocketError errorCode = Interop.Winsock.WSASend(
+ handle,
+ asyncResult._wsaBuffers,
+ asyncResult._wsaBuffers.Length,
+ out bytesTransferred,
+ socketFlags,
+ asyncResult.OverlappedHandle,
+ IntPtr.Zero);
- // This can throw ObjectDisposedException.
- int bytesTransferred;
- SocketError errorCode = Interop.Winsock.WSASend(
- handle,
- asyncResult._wsaBuffers,
- asyncResult._wsaBuffers.Length,
- out bytesTransferred,
- socketFlags,
- asyncResult.OverlappedHandle,
- IntPtr.Zero);
-
- if (errorCode != SocketError.Success)
+ return asyncResult.ProcessOverlappedResult(errorCode == SocketError.Success, bytesTransferred);
+ }
+ catch
{
- errorCode = GetLastSocketError();
+ asyncResult.ReleaseUnmanagedStructures();
+ throw;
}
-
- return errorCode;
}
// This assumes preBuffer/postBuffer are pinned already
- private static unsafe SocketError TransmitFileHelper(
+ private static unsafe bool TransmitFileHelper(
SafeHandle socket,
SafeHandle fileHandle,
SafeHandle overlapped,
@@ -899,141 +906,157 @@ namespace System.Net.Sockets
bool success = Interop.Mswsock.TransmitFile(socket, fileHandle, 0, 0, overlapped,
needTransmitFileBuffers ? &transmitFileBuffers : null, flags);
- return success ? SocketError.Success : GetLastSocketError();
+ return success;
}
public static unsafe SocketError SendFileAsync(SafeCloseSocket handle, FileStream fileStream, byte[] preBuffer, byte[] postBuffer, TransmitFileOptions flags, TransmitFileAsyncResult asyncResult)
{
asyncResult.SetUnmanagedStructures(fileStream, preBuffer, postBuffer, (flags & (TransmitFileOptions.Disconnect | TransmitFileOptions.ReuseSocket)) != 0);
-
- SocketError errorCode = TransmitFileHelper(handle, fileStream?.SafeFileHandle, asyncResult.OverlappedHandle, preBuffer, postBuffer, flags);
-
- // This will release resources if necessary
- errorCode = asyncResult.CheckAsyncCallOverlappedResult(errorCode);
-
- return errorCode;
+ try
+ {
+ bool success = TransmitFileHelper(
+ handle,
+ fileStream?.SafeFileHandle,
+ asyncResult.OverlappedHandle,
+ preBuffer,
+ postBuffer,
+ flags);
+
+ return asyncResult.ProcessOverlappedResult(success, 0);
+ }
+ catch
+ {
+ asyncResult.ReleaseUnmanagedStructures();
+ throw;
+ }
}
public static unsafe SocketError SendToAsync(SafeCloseSocket handle, byte[] buffer, int offset, int count, SocketFlags socketFlags, Internals.SocketAddress socketAddress, OverlappedAsyncResult asyncResult)
{
// Set up asyncResult for overlapped WSASendTo.
- // This call will use completion ports.
asyncResult.SetUnmanagedStructures(buffer, offset, count, socketAddress, false /* don't pin RemoteEP*/);
+ try
+ {
+ int bytesTransferred;
+ SocketError errorCode = Interop.Winsock.WSASendTo(
+ handle,
+ ref asyncResult._singleBuffer,
+ 1, // There is only ever 1 buffer being sent.
+ out bytesTransferred,
+ socketFlags,
+ asyncResult.GetSocketAddressPtr(),
+ asyncResult.SocketAddress.Size,
+ asyncResult.OverlappedHandle,
+ IntPtr.Zero);
- int bytesTransferred;
- SocketError errorCode = Interop.Winsock.WSASendTo(
- handle,
- ref asyncResult._singleBuffer,
- 1, // There is only ever 1 buffer being sent.
- out bytesTransferred,
- socketFlags,
- asyncResult.GetSocketAddressPtr(),
- asyncResult.SocketAddress.Size,
- asyncResult.OverlappedHandle,
- IntPtr.Zero);
-
- if (errorCode != SocketError.Success)
+ return asyncResult.ProcessOverlappedResult(errorCode == SocketError.Success, bytesTransferred);
+ }
+ catch
{
- errorCode = GetLastSocketError();
+ asyncResult.ReleaseUnmanagedStructures();
+ throw;
}
-
- return errorCode;
}
public static unsafe SocketError ReceiveAsync(SafeCloseSocket handle, byte[] buffer, int offset, int count, SocketFlags socketFlags, OverlappedAsyncResult asyncResult)
{
// Set up asyncResult for overlapped WSARecv.
- // This call will use completion ports.
asyncResult.SetUnmanagedStructures(buffer, offset, count, null, false /* don't pin null RemoteEP*/);
+ try
+ {
+ // This can throw ObjectDisposedException.
+ int bytesTransferred;
+ SocketError errorCode = Interop.Winsock.WSARecv(
+ handle,
+ ref asyncResult._singleBuffer,
+ 1,
+ out bytesTransferred,
+ ref socketFlags,
+ asyncResult.OverlappedHandle,
+ IntPtr.Zero);
- // This can throw ObjectDisposedException.
- int bytesTransferred;
- SocketError errorCode = Interop.Winsock.WSARecv(
- handle,
- ref asyncResult._singleBuffer,
- 1,
- out bytesTransferred,
- ref socketFlags,
- asyncResult.OverlappedHandle,
- IntPtr.Zero);
-
- if (errorCode != SocketError.Success)
+ return asyncResult.ProcessOverlappedResult(errorCode == SocketError.Success, bytesTransferred);
+ }
+ catch
{
- errorCode = GetLastSocketError();
+ asyncResult.ReleaseUnmanagedStructures();
+ throw;
}
-
- return errorCode;
}
public static unsafe SocketError ReceiveAsync(SafeCloseSocket handle, IList<ArraySegment<byte>> buffers, SocketFlags socketFlags, OverlappedAsyncResult asyncResult)
{
// Set up asyncResult for overlapped WSASend.
- // This call will use completion ports.
asyncResult.SetUnmanagedStructures(buffers);
+ try
+ {
+ // This can throw ObjectDisposedException.
+ int bytesTransferred;
+ SocketError errorCode = Interop.Winsock.WSARecv(
+ handle,
+ asyncResult._wsaBuffers,
+ asyncResult._wsaBuffers.Length,
+ out bytesTransferred,
+ ref socketFlags,
+ asyncResult.OverlappedHandle,
+ IntPtr.Zero);
- // This can throw ObjectDisposedException.
- int bytesTransferred;
- SocketError errorCode = Interop.Winsock.WSARecv(
- handle,
- asyncResult._wsaBuffers,
- asyncResult._wsaBuffers.Length,
- out bytesTransferred,
- ref socketFlags,
- asyncResult.OverlappedHandle,
- IntPtr.Zero);
-
- if (errorCode != SocketError.Success)
+ return asyncResult.ProcessOverlappedResult(errorCode == SocketError.Success, bytesTransferred);
+ }
+ catch
{
- errorCode = GetLastSocketError();
+ asyncResult.ReleaseUnmanagedStructures();
+ throw;
}
-
- return errorCode;
}
public static unsafe SocketError ReceiveFromAsync(SafeCloseSocket handle, byte[] buffer, int offset, int count, SocketFlags socketFlags, Internals.SocketAddress socketAddress, OverlappedAsyncResult asyncResult)
{
// Set up asyncResult for overlapped WSARecvFrom.
- // This call will use completion ports on WinNT and Overlapped IO on Win9x.
asyncResult.SetUnmanagedStructures(buffer, offset, count, socketAddress, true);
+ try
+ {
+ int bytesTransferred;
+ SocketError errorCode = Interop.Winsock.WSARecvFrom(
+ handle,
+ ref asyncResult._singleBuffer,
+ 1,
+ out bytesTransferred,
+ ref socketFlags,
+ asyncResult.GetSocketAddressPtr(),
+ asyncResult.GetSocketAddressSizePtr(),
+ asyncResult.OverlappedHandle,
+ IntPtr.Zero);
- int bytesTransferred;
- SocketError errorCode = Interop.Winsock.WSARecvFrom(
- handle,
- ref asyncResult._singleBuffer,
- 1,
- out bytesTransferred,
- ref socketFlags,
- asyncResult.GetSocketAddressPtr(),
- asyncResult.GetSocketAddressSizePtr(),
- asyncResult.OverlappedHandle,
- IntPtr.Zero);
-
- if (errorCode != SocketError.Success)
+ return asyncResult.ProcessOverlappedResult(errorCode == SocketError.Success, bytesTransferred);
+ }
+ catch
{
- errorCode = GetLastSocketError();
+ asyncResult.ReleaseUnmanagedStructures();
+ throw;
}
-
- return errorCode;
}
public static unsafe SocketError ReceiveMessageFromAsync(Socket socket, SafeCloseSocket handle, byte[] buffer, int offset, int count, SocketFlags socketFlags, Internals.SocketAddress socketAddress, ReceiveMessageOverlappedAsyncResult asyncResult)
{
asyncResult.SetUnmanagedStructures(buffer, offset, count, socketAddress, socketFlags);
+ try
+ {
+ int bytesTransfered;
+ SocketError errorCode = (SocketError)socket.WSARecvMsg(
+ handle,
+ Marshal.UnsafeAddrOfPinnedArrayElement(asyncResult._messageBuffer, 0),
+ out bytesTransfered,
+ asyncResult.OverlappedHandle,
+ IntPtr.Zero);
- int bytesTransfered;
- SocketError errorCode = (SocketError)socket.WSARecvMsg(
- handle,
- Marshal.UnsafeAddrOfPinnedArrayElement(asyncResult._messageBuffer, 0),
- out bytesTransfered,
- asyncResult.OverlappedHandle,
- IntPtr.Zero);
-
- if (errorCode != SocketError.Success)
+ return asyncResult.ProcessOverlappedResult(errorCode == SocketError.Success, bytesTransfered);
+ }
+ catch
{
- errorCode = GetLastSocketError();
+ asyncResult.ReleaseUnmanagedStructures();
+ throw;
}
-
- return errorCode;
}
public static unsafe SocketError AcceptAsync(Socket socket, SafeCloseSocket handle, SafeCloseSocket acceptHandle, int receiveSize, int socketAddressSize, AcceptOverlappedAsyncResult asyncResult)
@@ -1046,24 +1069,27 @@ namespace System.Net.Sockets
// Set up asyncResult for overlapped AcceptEx.
// This call will use completion ports on WinNT.
asyncResult.SetUnmanagedStructures(buffer, addressBufferSize);
+ try
+ {
+ // This can throw ObjectDisposedException.
+ int bytesTransferred;
+ bool success = socket.AcceptEx(
+ handle,
+ acceptHandle,
+ Marshal.UnsafeAddrOfPinnedArrayElement(asyncResult.Buffer, 0),
+ receiveSize,
+ addressBufferSize,
+ addressBufferSize,
+ out bytesTransferred,
+ asyncResult.OverlappedHandle);
- // This can throw ObjectDisposedException.
- int bytesTransferred;
- SocketError errorCode = SocketError.Success;
- if (!socket.AcceptEx(
- handle,
- acceptHandle,
- Marshal.UnsafeAddrOfPinnedArrayElement(asyncResult.Buffer, 0),
- receiveSize,
- addressBufferSize,
- addressBufferSize,
- out bytesTransferred,
- asyncResult.OverlappedHandle))
+ return asyncResult.ProcessOverlappedResult(success, 0);
+ }
+ catch
{
- errorCode = GetLastSocketError();
+ asyncResult.ReleaseUnmanagedStructures();
+ throw;
}
-
- return errorCode;
}
public static void CheckDualModeReceiveSupport(Socket socket)
@@ -1074,15 +1100,22 @@ namespace System.Net.Sockets
internal static SocketError DisconnectAsync(Socket socket, SafeCloseSocket handle, bool reuseSocket, DisconnectOverlappedAsyncResult asyncResult)
{
asyncResult.SetUnmanagedStructures(null);
-
- // This can throw ObjectDisposedException
- SocketError errorCode = SocketError.Success;
- if (!socket.DisconnectEx(handle, asyncResult.OverlappedHandle, (int)(reuseSocket ? TransmitFileOptions.ReuseSocket : 0), 0))
+ try
{
- errorCode = GetLastSocketError();
+ // This can throw ObjectDisposedException
+ bool success = socket.DisconnectEx(
+ handle,
+ asyncResult.OverlappedHandle,
+ (int)(reuseSocket ? TransmitFileOptions.ReuseSocket : 0),
+ 0);
+
+ return asyncResult.ProcessOverlappedResult(success, 0);
+ }
+ catch
+ {
+ asyncResult.ReleaseUnmanagedStructures();
+ throw;
}
-
- return errorCode;
}
internal static SocketError Disconnect(Socket socket, SafeCloseSocket handle, bool reuseSocket)
diff --git a/src/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFromAsync.cs b/src/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFromAsync.cs
index 1542628ba1..c3a03890f7 100644
--- a/src/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFromAsync.cs
+++ b/src/System.Net.Sockets/tests/FunctionalTests/ReceiveMessageFromAsync.cs
@@ -44,7 +44,11 @@ namespace System.Net.Sockets.Tests
args.Completed += OnCompleted;
args.UserToken = completed;
- Assert.True(receiver.ReceiveMessageFromAsync(args));
+ bool pending = receiver.ReceiveMessageFromAsync(args);
+ if (!pending)
+ {
+ OnCompleted(null, args);
+ }
Assert.True(completed.WaitOne(TestSettings.PassingTestTimeout), "Timeout while waiting for connection");
@@ -84,7 +88,11 @@ namespace System.Net.Sockets.Tests
args.Completed += OnCompleted;
args.UserToken = completed;
- Assert.True(receiver.ReceiveMessageFromAsync(args));
+ bool pending = receiver.ReceiveMessageFromAsync(args);
+ if (!pending)
+ {
+ OnCompleted(null, args);
+ }
Assert.True(completed.WaitOne(TestSettings.PassingTestTimeout), "Timeout while waiting for connection");
diff --git a/src/System.Net.Sockets/tests/FunctionalTests/Shutdown.cs b/src/System.Net.Sockets/tests/FunctionalTests/Shutdown.cs
index c83c827fd4..1e46e00d05 100644
--- a/src/System.Net.Sockets/tests/FunctionalTests/Shutdown.cs
+++ b/src/System.Net.Sockets/tests/FunctionalTests/Shutdown.cs
@@ -30,7 +30,11 @@ namespace System.Net.Sockets.Tests
args.SetBuffer(new byte[1], 0, 1);
args.UserToken = client;
- Assert.True(client.ReceiveAsync(args));
+ bool pending = client.ReceiveAsync(args);
+ if (!pending)
+ {
+ OnOperationCompleted(null, args);
+ }
break;
}
@@ -43,7 +47,11 @@ namespace System.Net.Sockets.Tests
break;
}
- Assert.True(client.SendAsync(args));
+ bool pending = client.SendAsync(args);
+ if (!pending)
+ {
+ OnOperationCompleted(null, args);
+ }
break;
}
@@ -52,7 +60,12 @@ namespace System.Net.Sockets.Tests
var client = (Socket)args.UserToken;
Assert.True(args.BytesTransferred == args.Buffer.Length);
- Assert.True(client.ReceiveAsync(args));
+
+ bool pending = client.ReceiveAsync(args);
+ if (!pending)
+ {
+ OnOperationCompleted(null, args);
+ }
break;
}
}
diff --git a/src/System.Net.Sockets/tests/FunctionalTests/UnixDomainSocketTest.cs b/src/System.Net.Sockets/tests/FunctionalTests/UnixDomainSocketTest.cs
index 82fb41fe28..fe363e15d8 100644
--- a/src/System.Net.Sockets/tests/FunctionalTests/UnixDomainSocketTest.cs
+++ b/src/System.Net.Sockets/tests/FunctionalTests/UnixDomainSocketTest.cs
@@ -70,9 +70,11 @@ namespace System.Net.Sockets.Tests
using (Socket sock = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified))
{
- Assert.True(sock.ConnectAsync(args));
-
- await complete.Task;
+ bool willRaiseEvent = sock.ConnectAsync(args);
+ if (willRaiseEvent)
+ {
+ await complete.Task;
+ }
Assert.Equal(SocketError.Success, args.SocketError);
Assert.Null(args.ConnectByNameError);
diff --git a/src/System.Threading.Tasks.Extensions/System.Threading.Tasks.Extensions.sln b/src/System.Threading.Tasks.Extensions/System.Threading.Tasks.Extensions.sln
index c760bb4df5..155c2f5afc 100644
--- a/src/System.Threading.Tasks.Extensions/System.Threading.Tasks.Extensions.sln
+++ b/src/System.Threading.Tasks.Extensions/System.Threading.Tasks.Extensions.sln
@@ -1,26 +1,26 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
-VisualStudioVersion = 14.0.23103.0
+VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Threading.Tasks.Extensions", "src\System.Threading.Tasks.Extensions.csproj", "{F24D3391-2928-4E83-AADE-B34423498750}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Threading.Tasks.Extensions.Tests", "tests\System.Threading.Tasks.Extensions.Tests.csproj", "{82B54697-0251-47A1-8546-FC507D0F3B08}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Threading.Tasks.Extensions", "src\System.Threading.Tasks.Extensions.csproj", "{F24D3391-2928-4E83-AADE-B34423498750}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {F24D3391-2928-4E83-AADE-B34423498750}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {F24D3391-2928-4E83-AADE-B34423498750}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {F24D3391-2928-4E83-AADE-B34423498750}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {F24D3391-2928-4E83-AADE-B34423498750}.Release|Any CPU.Build.0 = Release|Any CPU
- {82B54697-0251-47A1-8546-FC507D0F3B08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {82B54697-0251-47A1-8546-FC507D0F3B08}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {82B54697-0251-47A1-8546-FC507D0F3B08}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {82B54697-0251-47A1-8546-FC507D0F3B08}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F24D3391-2928-4E83-AADE-B34423498750}.Debug|Any CPU.ActiveCfg = netstandard1.0-Debug|Any CPU
+ {F24D3391-2928-4E83-AADE-B34423498750}.Debug|Any CPU.Build.0 = netstandard1.0-Debug|Any CPU
+ {F24D3391-2928-4E83-AADE-B34423498750}.Release|Any CPU.ActiveCfg = netstandard1.0-Release|Any CPU
+ {F24D3391-2928-4E83-AADE-B34423498750}.Release|Any CPU.Build.0 = netstandard1.0-Release|Any CPU
+ {82B54697-0251-47A1-8546-FC507D0F3B08}.Debug|Any CPU.ActiveCfg = netstandard1.3-Debug|Any CPU
+ {82B54697-0251-47A1-8546-FC507D0F3B08}.Debug|Any CPU.Build.0 = netstandard1.3-Debug|Any CPU
+ {82B54697-0251-47A1-8546-FC507D0F3B08}.Release|Any CPU.ActiveCfg = netstandard1.3-Release|Any CPU
+ {82B54697-0251-47A1-8546-FC507D0F3B08}.Release|Any CPU.Build.0 = netstandard1.3-Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/src/System.Threading.Tasks.Extensions/src/System.Threading.Tasks.Extensions.csproj b/src/System.Threading.Tasks.Extensions/src/System.Threading.Tasks.Extensions.csproj
index 8dfc843d63..62e25594b6 100644
--- a/src/System.Threading.Tasks.Extensions/src/System.Threading.Tasks.Extensions.csproj
+++ b/src/System.Threading.Tasks.Extensions/src/System.Threading.Tasks.Extensions.csproj
@@ -9,8 +9,8 @@
<PackageTargetFramework>netstandard1.0;portable-net45+win8+wp8+wpa81</PackageTargetFramework>
</PropertyGroup>
<!-- Default configurations to help VS understand the configurations -->
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard1.0-Debug|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard1.0-Release|AnyCPU'" />
<ItemGroup>
<Compile Include="System\Runtime\CompilerServices\AsyncMethodBuilderAttribute.cs" />
<Compile Include="System\Runtime\CompilerServices\AsyncValueTaskMethodBuilder.cs" />
diff --git a/src/System.Threading.Tasks.Extensions/tests/System.Threading.Tasks.Extensions.Tests.csproj b/src/System.Threading.Tasks.Extensions/tests/System.Threading.Tasks.Extensions.Tests.csproj
index c245b4910e..b863b484f7 100644
--- a/src/System.Threading.Tasks.Extensions/tests/System.Threading.Tasks.Extensions.Tests.csproj
+++ b/src/System.Threading.Tasks.Extensions/tests/System.Threading.Tasks.Extensions.Tests.csproj
@@ -4,8 +4,8 @@
<PropertyGroup>
<ProjectGuid>{82B54697-0251-47A1-8546-FC507D0F3B08}</ProjectGuid>
</PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'" />
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard1.3-Debug|AnyCPU'" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard1.3-Release|AnyCPU'" />
<ItemGroup>
<Compile Include="AsyncMethodBuilderAttributeTests.cs" />
<Compile Include="AsyncValueTaskMethodBuilderTests.cs" />
diff --git a/src/System.Threading.Thread/tests/ThreadTests.netstandard.cs b/src/System.Threading.Thread/tests/ThreadTests.netstandard.cs
index c005a7c4eb..687350fc4a 100644
--- a/src/System.Threading.Thread/tests/ThreadTests.netstandard.cs
+++ b/src/System.Threading.Thread/tests/ThreadTests.netstandard.cs
@@ -620,7 +620,7 @@ namespace System.Threading.Threads.Tests
public static void InterruptTest()
{
// Interrupting a thread that is not blocked does not do anything, but once the thread starts blocking, it gets
- // interrupted
+ // interrupted and does not auto-reset the signaled event
var threadReady = new AutoResetEvent(false);
var continueThread = new AutoResetEvent(false);
bool continueThreadBool = false;
@@ -636,10 +636,12 @@ namespace System.Threading.Threads.Tests
t.IsBackground = true;
t.Start();
threadReady.CheckedWait();
+ continueThread.Set();
t.Interrupt();
Assert.False(threadReady.WaitOne(ExpectedTimeoutMilliseconds));
Volatile.Write(ref continueThreadBool, true);
waitForThread();
+ Assert.True(continueThread.WaitOne(0));
// Interrupting a dead thread does nothing
t.Interrupt();
@@ -664,6 +666,32 @@ namespace System.Threading.Threads.Tests
}
[Fact]
+ [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework)]
+ public static void InterruptInFinallyBlockTest_SkipOnDesktopFramework()
+ {
+ // A wait in a finally block can be interrupted. The desktop framework applies the same rules as thread abort, and
+ // does not allow thread interrupt in a finally block. There is nothing special about thread interrupt that requires
+ // not allowing it in finally blocks, so this behavior has changed in .NET Core.
+ var continueThread = new AutoResetEvent(false);
+ Action waitForThread;
+ Thread t =
+ ThreadTestHelpers.CreateGuardedThread(out waitForThread, () =>
+ {
+ try
+ {
+ }
+ finally
+ {
+ Assert.Throws<ThreadInterruptedException>(() => continueThread.CheckedWait());
+ }
+ });
+ t.IsBackground = true;
+ t.Start();
+ t.Interrupt();
+ waitForThread();
+ }
+
+ [Fact]
public static void JoinTest()
{
var threadReady = new ManualResetEvent(false);