Merge branch 'master' into johluo/remove-extensions-deps

This commit is contained in:
John Luo 2020-04-06 11:17:56 -07:00 committed by GitHub
commit 6ad0d5849c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
130 changed files with 4508 additions and 820 deletions

View File

@ -71,6 +71,7 @@ variables:
# The following extra properties are not set when testing. Use with final build.[cmd,sh] of asset-producing jobs.
- name: _PublishArgs
value: /p:Publish=true
/p:GenerateChecksums=true
/p:DotNetPublishBlobFeedKey=$(dotnetfeed-storage-access-key-1)
/p:DotNetPublishBlobFeedUrl=https://dotnetfeed.blob.core.windows.net/dotnet-core/index.json
/p:DotNetPublishToBlobFeed=$(_DotNetPublishToBlobFeed)
@ -92,27 +93,28 @@ stages:
displayName: Build
jobs:
# Code check
- template: jobs/default-build.yml
parameters:
jobName: Code_check
jobDisplayName: Code check
agentOs: Windows
steps:
- ${{ if ne(variables['System.TeamProject'], 'public') }}:
- task: PowerShell@2
displayName: Setup Private Feeds Credentials
inputs:
filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.ps1
arguments: -ConfigFile $(Build.SourcesDirectory)/NuGet.config -Password $Env:Token
env:
Token: $(dn-bot-dnceng-artifact-feeds-rw)
- powershell: ./eng/scripts/CodeCheck.ps1 -ci $(_InternalRuntimeDownloadArgs)
displayName: Run eng/scripts/CodeCheck.ps1
artifacts:
- name: Code_Check_Logs
path: artifacts/log/
publishOnError: true
includeForks: true
- ${{ if or(eq(variables['System.TeamProject'], 'public'), in(variables['Build.Reason'], 'PullRequest')) }}:
- template: jobs/default-build.yml
parameters:
jobName: Code_check
jobDisplayName: Code check
agentOs: Windows
steps:
- ${{ if ne(variables['System.TeamProject'], 'public') }}:
- task: PowerShell@2
displayName: Setup Private Feeds Credentials
inputs:
filePath: $(Build.SourcesDirectory)/eng/common/SetupNugetSources.ps1
arguments: -ConfigFile $(Build.SourcesDirectory)/NuGet.config -Password $Env:Token
env:
Token: $(dn-bot-dnceng-artifact-feeds-rw)
- powershell: ./eng/scripts/CodeCheck.ps1 -ci $(_InternalRuntimeDownloadArgs)
displayName: Run eng/scripts/CodeCheck.ps1
artifacts:
- name: Code_Check_Logs
path: artifacts/log/
publishOnError: true
includeForks: true
# Build Windows (x64/x86)
- template: jobs/default-build.yml
@ -594,7 +596,7 @@ stages:
parameters:
condition: ne(variables['SkipTests'], 'true')
jobName: MacOS_Test
jobDisplayName: "Test: macOS 10.13"
jobDisplayName: "Test: macOS 10.14"
agentOs: macOS
isTestingJob: true
buildArgs: --all --test "/p:RunTemplateTests=false /p:SkipHelixReadyTests=true" $(_InternalRuntimeDownloadArgs)

6
CODE-OF-CONDUCT.md Normal file
View File

@ -0,0 +1,6 @@
# Code of Conduct
This project has adopted the code of conduct defined by the Contributor Covenant
to clarify expected behavior in our community.
For more information, see the [.NET Foundation Code of Conduct](https://dotnetfoundation.org/code-of-conduct).

View File

@ -57,4 +57,4 @@ Your pull request will now go through extensive checks by the subject matter exp
## Code of conduct
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information, see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
See [CODE-OF-CONDUCT.md](./CODE-OF-CONDUCT.md)

View File

@ -189,7 +189,6 @@
<ArchiveExtension>.tar.gz</ArchiveExtension>
<ArchiveExtension Condition="'$(TargetOsName)' == 'win'">.zip</ArchiveExtension>
<ChecksumExtension>.sha512</ChecksumExtension>
</PropertyGroup>
<Import Project="eng\Workarounds.props" />

View File

@ -9,7 +9,7 @@ Follow the [Getting Started](https://docs.microsoft.com/aspnet/core/getting-star
Also check out the [.NET Homepage](https://www.microsoft.com/net) for released versions of .NET, getting started guides, and learning resources.
See the [Issue Management Policies](https://github.com/dotnet/aspnetcore/blob/anurse/issue-policies/docs/IssueManagementPolicies.md) document for more information on how we handle incoming issues.
See the [Issue Management Policies](https://github.com/dotnet/aspnetcore/blob/master/docs/IssueManagementPolicies.md) document for more information on how we handle incoming issues.
## How to Engage, Contribute, and Give Feedback
@ -37,4 +37,4 @@ These are some other repos for related projects:
## Code of conduct
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
See [CODE-OF-CONDUCT](./CODE-OF-CONDUCT.md)

33
eng/AfterSigning.targets Normal file
View File

@ -0,0 +1,33 @@
<Project>
<Import Project="Sdk.props" Sdk="Microsoft.DotNet.Arcade.Sdk" Condition="'$(GenerateChecksums)' == 'true'" />
<PropertyGroup Condition="'$(GenerateChecksums)' == 'true'">
<!-- The one use of ArtifactsDir in Publish.proj adds an additional slash, confusing itself. -->
<ArtifactsDir Condition=" HasTrailingSlash('$(ArtifactsDir)') ">$(ArtifactsDir.Substring(0, $([MSBuild]::Subtract($(ArtifactsDir.Length), 1))))</ArtifactsDir>
<!-- $(InstallersOutputPath) is not defined. Root Directory.Build.props is not imported. -->
<InstallersOutputPath>$(ArtifactsDir)\installers\</InstallersOutputPath>
</PropertyGroup>
<Target Name="PopulateGenerateChecksumItems"
Condition="'$(GenerateChecksums)' == 'true'"
AfterTargets="Build"
BeforeTargets="GenerateChecksums" >
<ItemGroup>
<InstallerFiles Include="$(InstallersOutputPath)**\*.msi" />
<InstallerFiles Include="$(InstallersOutputPath)**\*.exe" />
<InstallerFiles Include="$(InstallersOutputPath)**\*.zip" />
<InstallerFiles Include="$(InstallersOutputPath)**\*.tar.gz" />
<InstallerFiles Include="$(InstallersOutputPath)**\*.wixlib" />
<InstallerFiles Include="$(InstallersOutputPath)**\*.rpm" />
<GenerateChecksumItems Include="%(InstallerFiles.Identity)" >
<DestinationPath>%(FullPath).sha512</DestinationPath>
</GenerateChecksumItems>
</ItemGroup>
</Target>
<Import Project="Sdk.targets" Sdk="Microsoft.DotNet.Arcade.Sdk" Condition="'$(GenerateChecksums)' == 'true'" />
</Project>

View File

@ -152,6 +152,7 @@
$(RepoRoot)src\SiteExtensions\LoggingAggregate\test\**\*.csproj;
$(RepoRoot)src\Shared\**\*.*proj;
$(RepoRoot)src\Tools\**\*.*proj;
$(RepoRoot)src\Logging.AzureAppServices\**\src\*.csproj;
$(RepoRoot)src\Middleware\**\*.csproj;
$(RepoRoot)src\Razor\**\*.*proj;
$(RepoRoot)src\Mvc\**\*.*proj;
@ -191,6 +192,7 @@
$(RepoRoot)src\Security\**\src\*.csproj;
$(RepoRoot)src\SiteExtensions\**\src\*.csproj;
$(RepoRoot)src\Tools\**\src\*.csproj;
$(RepoRoot)src\Logging.AzureAppServices\**\src\*.csproj;
$(RepoRoot)src\Middleware\**\src\*.csproj;
$(RepoRoot)src\Razor\**\src\*.csproj;
$(RepoRoot)src\Mvc\**\src\*.csproj;

View File

@ -29,7 +29,6 @@ and are generated based on the last package release.
<LatestPackageReference Include="Microsoft.CodeAnalysis.Razor" Version="$(MicrosoftCodeAnalysisRazorPackageVersion)" />
<LatestPackageReference Include="Microsoft.CSharp" Version="$(MicrosoftCSharpPackageVersion)" />
<LatestPackageReference Include="Microsoft.DotNet.GenAPI" Version="$(MicrosoftDotNetGenApiPackageVersion)" />
<LatestPackageReference Include="Microsoft.DotNet.PlatformAbstractions" Version="$(MicrosoftDotNetPlatformAbstractionsPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="$(MicrosoftExtensionsCachingAbstractionsPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.Caching.Memory" Version="$(MicrosoftExtensionsCachingMemoryPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="$(MicrosoftExtensionsConfigurationAbstractionsPackageVersion)" />
@ -119,13 +118,11 @@ and are generated based on the last package release.
<LatestPackageReference Include="Microsoft.EntityFrameworkCore" Version="$(MicrosoftEntityFrameworkCorePackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.Caching.SqlServer" Version="$(MicrosoftExtensionsCachingSqlServerPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="$(MicrosoftExtensionsCachingStackExchangeRedisPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.Logging.AzureAppServices" Version="$(MicrosoftExtensionsLoggingAzureAppServicesPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.Logging.Testing" Version="$(MicrosoftExtensionsLoggingTestingPackageVersion)" />
<LatestPackageReference Include="Microsoft.IdentityModel.Clients.ActiveDirectory" Version="$(MicrosoftIdentityModelClientsActiveDirectoryPackageVersion)" />
<LatestPackageReference Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="$(MicrosoftIdentityModelProtocolsOpenIdConnectPackageVersion)" />
<LatestPackageReference Include="Microsoft.IdentityModel.Protocols.WsFederation" Version="$(MicrosoftIdentityModelProtocolsWsFederationPackageVersion)" />
<LatestPackageReference Include="Microsoft.Internal.AspNetCore.H2Spec.All" Version="$(MicrosoftInternalAspNetCoreH2SpecAllPackageVersion)" />
<LatestPackageReference Include="Microsoft.Internal.Extensions.Refs" Version="$(MicrosoftInternalExtensionsRefsPackageVersion)" />
<LatestPackageReference Include="Microsoft.Extensions.Internal.Transport" Version="$(MicrosoftExtensionsInternalTransportPackageVersion)" />
<LatestPackageReference Include="Microsoft.NETCore.Windows.ApiSets" Version="$(MicrosoftNETCoreWindowsApiSetsPackageVersion)" />
<LatestPackageReference Include="Microsoft.Owin.Security.Cookies" Version="$(MicrosoftOwinSecurityCookiesPackageVersion)" />
<LatestPackageReference Include="Microsoft.Owin.Testing" Version="$(MicrosoftOwinTestingPackageVersion)" />

View File

@ -32,6 +32,7 @@
<ProjectReferenceProvider Include="Microsoft.Extensions.ApiDescription.Server" ProjectPath="$(RepoRoot)src\Tools\Extensions.ApiDescription.Server\src\Microsoft.Extensions.ApiDescription.Server.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.DeveloperCertificates.XPlat" ProjectPath="$(RepoRoot)src\Tools\FirstRunCertGenerator\src\Microsoft.AspNetCore.DeveloperCertificates.XPlat.csproj" />
<ProjectReferenceProvider Include="GetDocument.Insider" ProjectPath="$(RepoRoot)src\Tools\GetDocumentInsider\src\GetDocumentInsider.csproj" />
<ProjectReferenceProvider Include="Microsoft.Extensions.Logging.AzureAppServices" ProjectPath="$(RepoRoot)src\Logging.AzureAppServices\src\Microsoft.Extensions.Logging.AzureAppServices.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.ConcurrencyLimiter" ProjectPath="$(RepoRoot)src\Middleware\ConcurrencyLimiter\src\Microsoft.AspNetCore.ConcurrencyLimiter.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" ProjectPath="$(RepoRoot)src\Middleware\Diagnostics.EntityFrameworkCore\src\Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.HeaderPropagation" ProjectPath="$(RepoRoot)src\Middleware\HeaderPropagation\src\Microsoft.AspNetCore.HeaderPropagation.csproj" />

View File

@ -1,7 +1,7 @@
<Project>
<PropertyGroup Condition=" HasTrailingSlash('$(ArtifactsDir)') ">
<PropertyGroup>
<!-- The one use of ArtifactsDir in Publish.proj adds an additional slash, confusing itself. -->
<ArtifactsDir>$(ArtifactsDir.Substring(0, $([MSBuild]::Subtract($(ArtifactsDir.Length), 1))))</ArtifactsDir>
<ArtifactsDir Condition=" HasTrailingSlash('$(ArtifactsDir)') ">$(ArtifactsDir.Substring(0, $([MSBuild]::Subtract($(ArtifactsDir.Length), 1))))</ArtifactsDir>
<PublishDependsOnTargets>$(PublishDependsOnTargets);_PublishInstallersAndChecksums</PublishDependsOnTargets>
@ -50,12 +50,10 @@
<!-- Do not push .nupkg files from Linux and macOS builds. They'll be packed up separately and signed on Windows. -->
<ItemsToPushToBlobFeed Remove="@(ItemsToPushToBlobFeed)" Condition="'$(OS)' != 'Windows_NT'" />
<!-- Skip publishing checksums for now - the checksums for the .zip files don't match (https://github.com/dotnet/aspnetcore/issues/18792)
<ItemsToPushToBlobFeed Include="@(_ChecksumsToPublish)">
<PublishFlatContainer>true</PublishFlatContainer>
<RelativeBlobPath>$(_UploadPathRoot)/Runtime/$(_PackageVersion)/%(Filename)%(Extension)</RelativeBlobPath>
</ItemsToPushToBlobFeed>
-->
<ItemsToPushToBlobFeed Include="@(_InstallersToPublish)">
<IsShipping>true</IsShipping>

View File

@ -18,9 +18,8 @@
<SystemWindowsExtensionsPackageVersion>$(SystemWindowsExtensionsPackageVersion.Split('.')[0]).$(SystemWindowsExtensionsPackageVersion.Split('.')[1]).0</SystemWindowsExtensionsPackageVersion>
</PropertyGroup>
<ItemGroup>
<!-- Dependencies from dotnet/extensions -->
<!-- Dependencies from dotnet/runtime -->
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Caching.Abstractions" Version="$(MicrosoftExtensionsCachingAbstractionsPackageVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Caching.Memory" Version="$(MicrosoftExtensionsCachingMemoryPackageVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="$(MicrosoftExtensionsConfigurationAbstractionsPackageVersion)" />
@ -54,8 +53,6 @@
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Options.DataAnnotations" Version="$(MicrosoftExtensionsOptionsDataAnnotationsPackageVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Options" Version="$(MicrosoftExtensionsOptionsPackageVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Primitives" Version="$(MicrosoftExtensionsPrimitivesPackageVersion)" />
<!-- Dependencies from dotnet/corefx -->
<ExternalAspNetCoreAppReference Include="System.IO.Pipelines" Version="$(SystemIOPipelinesPackageVersion)" />
<ExternalAspNetCoreAppReference Include="System.Security.Cryptography.Xml" Version="$(SystemSecurityCryptographyXmlPackageVersion)" />

View File

@ -29,185 +29,169 @@
<Uri>https://github.com/dotnet/aspnetcore-tooling</Uri>
<Sha>4ec71cb57e45db101bbd4ffcf64dafa1711de0af</Sha>
</Dependency>
<Dependency Name="dotnet-ef" Version="5.0.0-preview.4.20201.1">
<Dependency Name="dotnet-ef" Version="5.0.0-preview.4.20203.1">
<Uri>https://github.com/dotnet/efcore</Uri>
<Sha>0f28f7168a1a6b1f34ccc4546eb6d5d667fee011</Sha>
<Sha>b0636ed8050797d0a9c16da8b98c2eea7d7e1f16</Sha>
</Dependency>
<Dependency Name="Microsoft.EntityFrameworkCore.InMemory" Version="5.0.0-preview.4.20201.1">
<Dependency Name="Microsoft.EntityFrameworkCore.InMemory" Version="5.0.0-preview.4.20203.1">
<Uri>https://github.com/dotnet/efcore</Uri>
<Sha>0f28f7168a1a6b1f34ccc4546eb6d5d667fee011</Sha>
<Sha>b0636ed8050797d0a9c16da8b98c2eea7d7e1f16</Sha>
</Dependency>
<Dependency Name="Microsoft.EntityFrameworkCore.Relational" Version="5.0.0-preview.4.20201.1">
<Dependency Name="Microsoft.EntityFrameworkCore.Relational" Version="5.0.0-preview.4.20203.1">
<Uri>https://github.com/dotnet/efcore</Uri>
<Sha>0f28f7168a1a6b1f34ccc4546eb6d5d667fee011</Sha>
<Sha>b0636ed8050797d0a9c16da8b98c2eea7d7e1f16</Sha>
</Dependency>
<Dependency Name="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.0-preview.4.20201.1">
<Dependency Name="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.0-preview.4.20203.1">
<Uri>https://github.com/dotnet/efcore</Uri>
<Sha>0f28f7168a1a6b1f34ccc4546eb6d5d667fee011</Sha>
<Sha>b0636ed8050797d0a9c16da8b98c2eea7d7e1f16</Sha>
</Dependency>
<Dependency Name="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.0-preview.4.20201.1">
<Dependency Name="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.0-preview.4.20203.1">
<Uri>https://github.com/dotnet/efcore</Uri>
<Sha>0f28f7168a1a6b1f34ccc4546eb6d5d667fee011</Sha>
<Sha>b0636ed8050797d0a9c16da8b98c2eea7d7e1f16</Sha>
</Dependency>
<Dependency Name="Microsoft.EntityFrameworkCore.Tools" Version="5.0.0-preview.4.20201.1">
<Dependency Name="Microsoft.EntityFrameworkCore.Tools" Version="5.0.0-preview.4.20203.1">
<Uri>https://github.com/dotnet/efcore</Uri>
<Sha>0f28f7168a1a6b1f34ccc4546eb6d5d667fee011</Sha>
<Sha>b0636ed8050797d0a9c16da8b98c2eea7d7e1f16</Sha>
</Dependency>
<Dependency Name="Microsoft.EntityFrameworkCore" Version="5.0.0-preview.4.20201.1">
<Dependency Name="Microsoft.EntityFrameworkCore" Version="5.0.0-preview.4.20203.1">
<Uri>https://github.com/dotnet/efcore</Uri>
<Sha>0f28f7168a1a6b1f34ccc4546eb6d5d667fee011</Sha>
<Sha>b0636ed8050797d0a9c16da8b98c2eea7d7e1f16</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Caching.Abstractions" Version="5.0.0-preview.4.20201.2" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>03c40031d618f923aa88da125cb078aabde9ebb1</Sha>
<Dependency Name="Microsoft.Extensions.Caching.Abstractions" Version="5.0.0-preview.4-runtime.20201.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>e1fa5d7648d46f067e265211fc2c695d409fe788</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Caching.Memory" Version="5.0.0-preview.4.20201.2" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>03c40031d618f923aa88da125cb078aabde9ebb1</Sha>
<Dependency Name="Microsoft.Extensions.Caching.Memory" Version="5.0.0-preview.4-runtime.20201.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>e1fa5d7648d46f067e265211fc2c695d409fe788</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Caching.SqlServer" Version="5.0.0-preview.4.20201.2" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>03c40031d618f923aa88da125cb078aabde9ebb1</Sha>
<Dependency Name="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0-preview.4-runtime.20201.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>e1fa5d7648d46f067e265211fc2c695d409fe788</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Caching.StackExchangeRedis" Version="5.0.0-preview.4.20201.2" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>03c40031d618f923aa88da125cb078aabde9ebb1</Sha>
<Dependency Name="Microsoft.Extensions.Configuration.Binder" Version="5.0.0-preview.4-runtime.20201.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>e1fa5d7648d46f067e265211fc2c695d409fe788</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0-preview.4.20201.2" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>03c40031d618f923aa88da125cb078aabde9ebb1</Sha>
<Dependency Name="Microsoft.Extensions.Configuration.CommandLine" Version="5.0.0-preview.4-runtime.20201.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>e1fa5d7648d46f067e265211fc2c695d409fe788</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.Binder" Version="5.0.0-preview.4.20201.2" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>03c40031d618f923aa88da125cb078aabde9ebb1</Sha>
<Dependency Name="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="5.0.0-preview.4-runtime.20201.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>e1fa5d7648d46f067e265211fc2c695d409fe788</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.CommandLine" Version="5.0.0-preview.4.20201.2" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>03c40031d618f923aa88da125cb078aabde9ebb1</Sha>
<Dependency Name="Microsoft.Extensions.Configuration.FileExtensions" Version="5.0.0-preview.4-runtime.20201.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>e1fa5d7648d46f067e265211fc2c695d409fe788</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="5.0.0-preview.4.20201.2" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>03c40031d618f923aa88da125cb078aabde9ebb1</Sha>
<Dependency Name="Microsoft.Extensions.Configuration.Ini" Version="5.0.0-preview.4-runtime.20201.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>e1fa5d7648d46f067e265211fc2c695d409fe788</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.FileExtensions" Version="5.0.0-preview.4.20201.2" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>03c40031d618f923aa88da125cb078aabde9ebb1</Sha>
<Dependency Name="Microsoft.Extensions.Configuration.Json" Version="5.0.0-preview.4-runtime.20201.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>e1fa5d7648d46f067e265211fc2c695d409fe788</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.Ini" Version="5.0.0-preview.4.20201.2" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>03c40031d618f923aa88da125cb078aabde9ebb1</Sha>
<Dependency Name="Microsoft.Extensions.Configuration.UserSecrets" Version="5.0.0-preview.4-runtime.20201.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>e1fa5d7648d46f067e265211fc2c695d409fe788</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.Json" Version="5.0.0-preview.4.20201.2" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>03c40031d618f923aa88da125cb078aabde9ebb1</Sha>
<Dependency Name="Microsoft.Extensions.Configuration.Xml" Version="5.0.0-preview.4-runtime.20201.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>e1fa5d7648d46f067e265211fc2c695d409fe788</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.UserSecrets" Version="5.0.0-preview.4.20201.2" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>03c40031d618f923aa88da125cb078aabde9ebb1</Sha>
<Dependency Name="Microsoft.Extensions.Configuration" Version="5.0.0-preview.4-runtime.20201.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>e1fa5d7648d46f067e265211fc2c695d409fe788</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Configuration.Xml" Version="5.0.0-preview.4.20201.2" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>03c40031d618f923aa88da125cb078aabde9ebb1</Sha>
<Dependency Name="Microsoft.Extensions.DependencyInjection.Abstractions" Version="5.0.0-preview.4-runtime.20201.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>e1fa5d7648d46f067e265211fc2c695d409fe788</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Configuration" Version="5.0.0-preview.4.20201.2" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>03c40031d618f923aa88da125cb078aabde9ebb1</Sha>
<Dependency Name="Microsoft.Extensions.DependencyInjection" Version="5.0.0-preview.4-runtime.20201.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>e1fa5d7648d46f067e265211fc2c695d409fe788</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.DependencyInjection.Abstractions" Version="5.0.0-preview.4.20201.2" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>03c40031d618f923aa88da125cb078aabde9ebb1</Sha>
<Dependency Name="Microsoft.Extensions.FileProviders.Abstractions" Version="5.0.0-preview.4-runtime.20201.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>e1fa5d7648d46f067e265211fc2c695d409fe788</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.DependencyInjection" Version="5.0.0-preview.4.20201.2" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>03c40031d618f923aa88da125cb078aabde9ebb1</Sha>
<Dependency Name="Microsoft.Extensions.FileProviders.Composite" Version="5.0.0-preview.4-runtime.20201.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>e1fa5d7648d46f067e265211fc2c695d409fe788</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.FileProviders.Abstractions" Version="5.0.0-preview.4.20201.2" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>03c40031d618f923aa88da125cb078aabde9ebb1</Sha>
<Dependency Name="Microsoft.Extensions.FileProviders.Physical" Version="5.0.0-preview.4-runtime.20201.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>e1fa5d7648d46f067e265211fc2c695d409fe788</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.FileProviders.Composite" Version="5.0.0-preview.4.20201.2" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>03c40031d618f923aa88da125cb078aabde9ebb1</Sha>
<Dependency Name="Microsoft.Extensions.FileSystemGlobbing" Version="5.0.0-preview.4-runtime.20201.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>e1fa5d7648d46f067e265211fc2c695d409fe788</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.FileProviders.Physical" Version="5.0.0-preview.4.20201.2" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>03c40031d618f923aa88da125cb078aabde9ebb1</Sha>
<Dependency Name="Microsoft.Extensions.Hosting.Abstractions" Version="5.0.0-preview.4-runtime.20201.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>e1fa5d7648d46f067e265211fc2c695d409fe788</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.FileSystemGlobbing" Version="5.0.0-preview.4.20201.2" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>03c40031d618f923aa88da125cb078aabde9ebb1</Sha>
<Dependency Name="Microsoft.Extensions.Hosting" Version="5.0.0-preview.4-runtime.20201.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>e1fa5d7648d46f067e265211fc2c695d409fe788</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Hosting.Abstractions" Version="5.0.0-preview.4.20201.2" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>03c40031d618f923aa88da125cb078aabde9ebb1</Sha>
<Dependency Name="Microsoft.Extensions.Http" Version="5.0.0-preview.4-runtime.20201.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>e1fa5d7648d46f067e265211fc2c695d409fe788</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Hosting" Version="5.0.0-preview.4.20201.2" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>03c40031d618f923aa88da125cb078aabde9ebb1</Sha>
<Dependency Name="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0-preview.4-runtime.20201.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>e1fa5d7648d46f067e265211fc2c695d409fe788</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Http" Version="5.0.0-preview.4.20201.2" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>03c40031d618f923aa88da125cb078aabde9ebb1</Sha>
<Dependency Name="Microsoft.Extensions.Logging.Configuration" Version="5.0.0-preview.4-runtime.20201.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>e1fa5d7648d46f067e265211fc2c695d409fe788</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0-preview.4.20201.2" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>03c40031d618f923aa88da125cb078aabde9ebb1</Sha>
<Dependency Name="Microsoft.Extensions.Logging.Console" Version="5.0.0-preview.4-runtime.20201.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>e1fa5d7648d46f067e265211fc2c695d409fe788</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Logging.AzureAppServices" Version="5.0.0-preview.4.20201.2" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>03c40031d618f923aa88da125cb078aabde9ebb1</Sha>
<Dependency Name="Microsoft.Extensions.Logging.Debug" Version="5.0.0-preview.4-runtime.20201.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>e1fa5d7648d46f067e265211fc2c695d409fe788</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Logging.Configuration" Version="5.0.0-preview.4.20201.2" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>03c40031d618f923aa88da125cb078aabde9ebb1</Sha>
<Dependency Name="Microsoft.Extensions.Logging.EventSource" Version="5.0.0-preview.4-runtime.20201.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>e1fa5d7648d46f067e265211fc2c695d409fe788</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Logging.Console" Version="5.0.0-preview.4.20201.2" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>03c40031d618f923aa88da125cb078aabde9ebb1</Sha>
<Dependency Name="Microsoft.Extensions.Logging.EventLog" Version="5.0.0-preview.4-runtime.20201.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>e1fa5d7648d46f067e265211fc2c695d409fe788</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Logging.Debug" Version="5.0.0-preview.4.20201.2" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>03c40031d618f923aa88da125cb078aabde9ebb1</Sha>
<Dependency Name="Microsoft.Extensions.Logging.TraceSource" Version="5.0.0-preview.4-runtime.20201.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>e1fa5d7648d46f067e265211fc2c695d409fe788</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Logging.EventSource" Version="5.0.0-preview.4.20201.2" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>03c40031d618f923aa88da125cb078aabde9ebb1</Sha>
<Dependency Name="Microsoft.Extensions.Logging" Version="5.0.0-preview.4-runtime.20201.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>e1fa5d7648d46f067e265211fc2c695d409fe788</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Logging.EventLog" Version="5.0.0-preview.4.20201.2" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>03c40031d618f923aa88da125cb078aabde9ebb1</Sha>
<Dependency Name="Microsoft.Extensions.Options.ConfigurationExtensions" Version="5.0.0-preview.4-runtime.20201.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>e1fa5d7648d46f067e265211fc2c695d409fe788</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Logging.TraceSource" Version="5.0.0-preview.4.20201.2" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>03c40031d618f923aa88da125cb078aabde9ebb1</Sha>
<Dependency Name="Microsoft.Extensions.Options.DataAnnotations" Version="5.0.0-preview.4-runtime.20201.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>e1fa5d7648d46f067e265211fc2c695d409fe788</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Logging.Testing" Version="5.0.0-preview.4.20201.2" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>03c40031d618f923aa88da125cb078aabde9ebb1</Sha>
<Dependency Name="Microsoft.Extensions.Options" Version="5.0.0-preview.4-runtime.20201.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>e1fa5d7648d46f067e265211fc2c695d409fe788</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Logging" Version="5.0.0-preview.4.20201.2" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>03c40031d618f923aa88da125cb078aabde9ebb1</Sha>
<Dependency Name="Microsoft.Extensions.Primitives" Version="5.0.0-preview.4-runtime.20201.1" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>e1fa5d7648d46f067e265211fc2c695d409fe788</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Options.ConfigurationExtensions" Version="5.0.0-preview.4.20201.2" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>03c40031d618f923aa88da125cb078aabde9ebb1</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Options.DataAnnotations" Version="5.0.0-preview.4.20201.2" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>03c40031d618f923aa88da125cb078aabde9ebb1</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Options" Version="5.0.0-preview.4.20201.2" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>03c40031d618f923aa88da125cb078aabde9ebb1</Sha>
</Dependency>
<Dependency Name="Microsoft.Extensions.Primitives" Version="5.0.0-preview.4.20201.2" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>03c40031d618f923aa88da125cb078aabde9ebb1</Sha>
</Dependency>
<Dependency Name="Microsoft.Internal.Extensions.Refs" Version="5.0.0-preview.4.20201.2" CoherentParentDependency="Microsoft.AspNetCore.Razor.Language">
<Uri>https://github.com/dotnet/extensions</Uri>
<Sha>03c40031d618f923aa88da125cb078aabde9ebb1</Sha>
<Dependency Name="Microsoft.Extensions.Internal.Transport" Version="5.0.0-preview.4-runtime.20201.1" CoherentParentDependency="Microsoft.NETCore.App.Runtime.win-x64">
<Uri>https://github.com/dotnet/runtime</Uri>
<Sha>e1fa5d7648d46f067e265211fc2c695d409fe788</Sha>
</Dependency>
<Dependency Name="Microsoft.Win32.Registry" Version="5.0.0-preview.4.20201.1" CoherentParentDependency="Microsoft.NETCore.App.Runtime.win-x64">
<Uri>https://github.com/dotnet/runtime</Uri>

View File

@ -65,14 +65,47 @@
<MicrosoftDotNetGenAPIPackageVersion>5.0.0-beta.20180.5</MicrosoftDotNetGenAPIPackageVersion>
<!-- Packages from dotnet/roslyn -->
<MicrosoftNetCompilersToolsetPackageVersion>3.6.0-3.20201.6</MicrosoftNetCompilersToolsetPackageVersion>
<!-- Packages from dotnet/core-setup -->
<!-- Packages from dotnet/runtime -->
<MicrosoftExtensionsDependencyModelPackageVersion>5.0.0-preview.4-runtime.20201.1</MicrosoftExtensionsDependencyModelPackageVersion>
<MicrosoftNETCoreAppInternalPackageVersion>5.0.0-preview.4.20201.1</MicrosoftNETCoreAppInternalPackageVersion>
<MicrosoftNETCoreAppRefPackageVersion>5.0.0-preview.4.20201.1</MicrosoftNETCoreAppRefPackageVersion>
<MicrosoftNETCoreAppRuntimewinx64PackageVersion>5.0.0-preview.4.20201.1</MicrosoftNETCoreAppRuntimewinx64PackageVersion>
<!-- Packages from dotnet/corefx -->
<MicrosoftWin32RegistryPackageVersion>5.0.0-preview.4.20201.1</MicrosoftWin32RegistryPackageVersion>
<MicrosoftWin32SystemEventsPackageVersion>5.0.0-preview.4.20201.1</MicrosoftWin32SystemEventsPackageVersion>
<MicrosoftExtensionsCachingAbstractionsPackageVersion>5.0.0-preview.4-runtime.20201.1</MicrosoftExtensionsCachingAbstractionsPackageVersion>
<MicrosoftExtensionsCachingMemoryPackageVersion>5.0.0-preview.4-runtime.20201.1</MicrosoftExtensionsCachingMemoryPackageVersion>
<MicrosoftExtensionsConfigurationAbstractionsPackageVersion>5.0.0-preview.4-runtime.20201.1</MicrosoftExtensionsConfigurationAbstractionsPackageVersion>
<MicrosoftExtensionsConfigurationBinderPackageVersion>5.0.0-preview.4-runtime.20201.1</MicrosoftExtensionsConfigurationBinderPackageVersion>
<MicrosoftExtensionsConfigurationCommandLinePackageVersion>5.0.0-preview.4-runtime.20201.1</MicrosoftExtensionsConfigurationCommandLinePackageVersion>
<MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion>5.0.0-preview.4-runtime.20201.1</MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion>
<MicrosoftExtensionsConfigurationFileExtensionsPackageVersion>5.0.0-preview.4-runtime.20201.1</MicrosoftExtensionsConfigurationFileExtensionsPackageVersion>
<MicrosoftExtensionsConfigurationIniPackageVersion>5.0.0-preview.4-runtime.20201.1</MicrosoftExtensionsConfigurationIniPackageVersion>
<MicrosoftExtensionsConfigurationJsonPackageVersion>5.0.0-preview.4-runtime.20201.1</MicrosoftExtensionsConfigurationJsonPackageVersion>
<MicrosoftExtensionsConfigurationPackageVersion>5.0.0-preview.4-runtime.20201.1</MicrosoftExtensionsConfigurationPackageVersion>
<MicrosoftExtensionsConfigurationUserSecretsPackageVersion>5.0.0-preview.4-runtime.20201.1</MicrosoftExtensionsConfigurationUserSecretsPackageVersion>
<MicrosoftExtensionsConfigurationXmlPackageVersion>5.0.0-preview.4-runtime.20201.1</MicrosoftExtensionsConfigurationXmlPackageVersion>
<MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>5.0.0-preview.4-runtime.20201.1</MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>
<MicrosoftExtensionsDependencyInjectionPackageVersion>5.0.0-preview.4-runtime.20201.1</MicrosoftExtensionsDependencyInjectionPackageVersion>
<MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>5.0.0-preview.4-runtime.20201.1</MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>
<MicrosoftExtensionsFileProvidersCompositePackageVersion>5.0.0-preview.4-runtime.20201.1</MicrosoftExtensionsFileProvidersCompositePackageVersion>
<MicrosoftExtensionsFileProvidersPhysicalPackageVersion>5.0.0-preview.4-runtime.20201.1</MicrosoftExtensionsFileProvidersPhysicalPackageVersion>
<MicrosoftExtensionsFileSystemGlobbingPackageVersion>5.0.0-preview.4-runtime.20201.1</MicrosoftExtensionsFileSystemGlobbingPackageVersion>
<MicrosoftExtensionsHostingAbstractionsPackageVersion>5.0.0-preview.4-runtime.20201.1</MicrosoftExtensionsHostingAbstractionsPackageVersion>
<MicrosoftExtensionsHostingPackageVersion>5.0.0-preview.4-runtime.20201.1</MicrosoftExtensionsHostingPackageVersion>
<MicrosoftExtensionsHttpPackageVersion>5.0.0-preview.4-runtime.20201.1</MicrosoftExtensionsHttpPackageVersion>
<MicrosoftExtensionsLoggingAbstractionsPackageVersion>5.0.0-preview.4-runtime.20201.1</MicrosoftExtensionsLoggingAbstractionsPackageVersion>
<MicrosoftExtensionsLoggingConfigurationPackageVersion>5.0.0-preview.4-runtime.20201.1</MicrosoftExtensionsLoggingConfigurationPackageVersion>
<MicrosoftExtensionsLoggingConsolePackageVersion>5.0.0-preview.4-runtime.20201.1</MicrosoftExtensionsLoggingConsolePackageVersion>
<MicrosoftExtensionsLoggingDebugPackageVersion>5.0.0-preview.4-runtime.20201.1</MicrosoftExtensionsLoggingDebugPackageVersion>
<MicrosoftExtensionsLoggingEventSourcePackageVersion>5.0.0-preview.4-runtime.20201.1</MicrosoftExtensionsLoggingEventSourcePackageVersion>
<MicrosoftExtensionsLoggingEventLogPackageVersion>5.0.0-preview.4-runtime.20201.1</MicrosoftExtensionsLoggingEventLogPackageVersion>
<MicrosoftExtensionsLoggingPackageVersion>5.0.0-preview.4-runtime.20201.1</MicrosoftExtensionsLoggingPackageVersion>
<MicrosoftExtensionsLoggingTraceSourcePackageVersion>5.0.0-preview.4-runtime.20201.1</MicrosoftExtensionsLoggingTraceSourcePackageVersion>
<MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>5.0.0-preview.4-runtime.20201.1</MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>
<MicrosoftExtensionsOptionsDataAnnotationsPackageVersion>5.0.0-preview.4-runtime.20201.1</MicrosoftExtensionsOptionsDataAnnotationsPackageVersion>
<MicrosoftExtensionsOptionsPackageVersion>5.0.0-preview.4-runtime.20201.1</MicrosoftExtensionsOptionsPackageVersion>
<MicrosoftExtensionsPrimitivesPackageVersion>5.0.0-preview.4-runtime.20201.1</MicrosoftExtensionsPrimitivesPackageVersion>
<MicrosoftExtensionsInternalTransportPackageVersion>5.0.0-preview.4-runtime.20201.1</MicrosoftExtensionsInternalTransportPackageVersion>
<SystemComponentModelAnnotationsPackageVersion>5.0.0-preview.4.20201.1</SystemComponentModelAnnotationsPackageVersion>
<SystemDiagnosticsEventLogPackageVersion>5.0.0-preview.4.20201.1</SystemDiagnosticsEventLogPackageVersion>
<SystemDrawingCommonPackageVersion>5.0.0-preview.4.20201.1</SystemDrawingCommonPackageVersion>
@ -95,53 +128,14 @@
<MicrosoftNETCorePlatformsPackageVersion>5.0.0-preview.4.20201.1</MicrosoftNETCorePlatformsPackageVersion>
<!-- Packages from dotnet/blazor -->
<MicrosoftAspNetCoreBlazorMonoPackageVersion>3.2.0-preview1.20067.1</MicrosoftAspNetCoreBlazorMonoPackageVersion>
<!-- Packages from dotnet/extensions -->
<MicrosoftExtensionsCachingAbstractionsPackageVersion>5.0.0-preview.4.20201.2</MicrosoftExtensionsCachingAbstractionsPackageVersion>
<MicrosoftExtensionsCachingMemoryPackageVersion>5.0.0-preview.4.20201.2</MicrosoftExtensionsCachingMemoryPackageVersion>
<MicrosoftExtensionsCachingSqlServerPackageVersion>5.0.0-preview.4.20201.2</MicrosoftExtensionsCachingSqlServerPackageVersion>
<MicrosoftExtensionsCachingStackExchangeRedisPackageVersion>5.0.0-preview.4.20201.2</MicrosoftExtensionsCachingStackExchangeRedisPackageVersion>
<MicrosoftExtensionsConfigurationAbstractionsPackageVersion>5.0.0-preview.4.20201.2</MicrosoftExtensionsConfigurationAbstractionsPackageVersion>
<MicrosoftExtensionsConfigurationBinderPackageVersion>5.0.0-preview.4.20201.2</MicrosoftExtensionsConfigurationBinderPackageVersion>
<MicrosoftExtensionsConfigurationCommandLinePackageVersion>5.0.0-preview.4.20201.2</MicrosoftExtensionsConfigurationCommandLinePackageVersion>
<MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion>5.0.0-preview.4.20201.2</MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion>
<MicrosoftExtensionsConfigurationFileExtensionsPackageVersion>5.0.0-preview.4.20201.2</MicrosoftExtensionsConfigurationFileExtensionsPackageVersion>
<MicrosoftExtensionsConfigurationIniPackageVersion>5.0.0-preview.4.20201.2</MicrosoftExtensionsConfigurationIniPackageVersion>
<MicrosoftExtensionsConfigurationJsonPackageVersion>5.0.0-preview.4.20201.2</MicrosoftExtensionsConfigurationJsonPackageVersion>
<MicrosoftExtensionsConfigurationPackageVersion>5.0.0-preview.4.20201.2</MicrosoftExtensionsConfigurationPackageVersion>
<MicrosoftExtensionsConfigurationUserSecretsPackageVersion>5.0.0-preview.4.20201.2</MicrosoftExtensionsConfigurationUserSecretsPackageVersion>
<MicrosoftExtensionsConfigurationXmlPackageVersion>5.0.0-preview.4.20201.2</MicrosoftExtensionsConfigurationXmlPackageVersion>
<MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>5.0.0-preview.4.20201.2</MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion>
<MicrosoftExtensionsDependencyInjectionPackageVersion>5.0.0-preview.4.20201.2</MicrosoftExtensionsDependencyInjectionPackageVersion>
<MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>5.0.0-preview.4.20201.2</MicrosoftExtensionsFileProvidersAbstractionsPackageVersion>
<MicrosoftExtensionsFileProvidersCompositePackageVersion>5.0.0-preview.4.20201.2</MicrosoftExtensionsFileProvidersCompositePackageVersion>
<MicrosoftExtensionsFileProvidersPhysicalPackageVersion>5.0.0-preview.4.20201.2</MicrosoftExtensionsFileProvidersPhysicalPackageVersion>
<MicrosoftExtensionsFileSystemGlobbingPackageVersion>5.0.0-preview.4.20201.2</MicrosoftExtensionsFileSystemGlobbingPackageVersion>
<MicrosoftExtensionsHostingAbstractionsPackageVersion>5.0.0-preview.4.20201.2</MicrosoftExtensionsHostingAbstractionsPackageVersion>
<MicrosoftExtensionsHostingPackageVersion>5.0.0-preview.4.20201.2</MicrosoftExtensionsHostingPackageVersion>
<MicrosoftExtensionsHttpPackageVersion>5.0.0-preview.4.20201.2</MicrosoftExtensionsHttpPackageVersion>
<MicrosoftExtensionsLoggingAbstractionsPackageVersion>5.0.0-preview.4.20201.2</MicrosoftExtensionsLoggingAbstractionsPackageVersion>
<MicrosoftExtensionsLoggingAzureAppServicesPackageVersion>5.0.0-preview.4.20201.2</MicrosoftExtensionsLoggingAzureAppServicesPackageVersion>
<MicrosoftExtensionsLoggingConfigurationPackageVersion>5.0.0-preview.4.20201.2</MicrosoftExtensionsLoggingConfigurationPackageVersion>
<MicrosoftExtensionsLoggingConsolePackageVersion>5.0.0-preview.4.20201.2</MicrosoftExtensionsLoggingConsolePackageVersion>
<MicrosoftExtensionsLoggingDebugPackageVersion>5.0.0-preview.4.20201.2</MicrosoftExtensionsLoggingDebugPackageVersion>
<MicrosoftExtensionsLoggingEventSourcePackageVersion>5.0.0-preview.4.20201.2</MicrosoftExtensionsLoggingEventSourcePackageVersion>
<MicrosoftExtensionsLoggingEventLogPackageVersion>5.0.0-preview.4.20201.2</MicrosoftExtensionsLoggingEventLogPackageVersion>
<MicrosoftExtensionsLoggingPackageVersion>5.0.0-preview.4.20201.2</MicrosoftExtensionsLoggingPackageVersion>
<MicrosoftExtensionsLoggingTestingPackageVersion>5.0.0-preview.4.20201.2</MicrosoftExtensionsLoggingTestingPackageVersion>
<MicrosoftExtensionsLoggingTraceSourcePackageVersion>5.0.0-preview.4.20201.2</MicrosoftExtensionsLoggingTraceSourcePackageVersion>
<MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>5.0.0-preview.4.20201.2</MicrosoftExtensionsOptionsConfigurationExtensionsPackageVersion>
<MicrosoftExtensionsOptionsDataAnnotationsPackageVersion>5.0.0-preview.4.20201.2</MicrosoftExtensionsOptionsDataAnnotationsPackageVersion>
<MicrosoftExtensionsOptionsPackageVersion>5.0.0-preview.4.20201.2</MicrosoftExtensionsOptionsPackageVersion>
<MicrosoftExtensionsPrimitivesPackageVersion>5.0.0-preview.4.20201.2</MicrosoftExtensionsPrimitivesPackageVersion>
<MicrosoftInternalExtensionsRefsPackageVersion>5.0.0-preview.4.20201.2</MicrosoftInternalExtensionsRefsPackageVersion>
<!-- Packages from dotnet/efcore -->
<dotnetefPackageVersion>5.0.0-preview.4.20201.1</dotnetefPackageVersion>
<MicrosoftEntityFrameworkCoreInMemoryPackageVersion>5.0.0-preview.4.20201.1</MicrosoftEntityFrameworkCoreInMemoryPackageVersion>
<MicrosoftEntityFrameworkCoreRelationalPackageVersion>5.0.0-preview.4.20201.1</MicrosoftEntityFrameworkCoreRelationalPackageVersion>
<MicrosoftEntityFrameworkCoreSqlitePackageVersion>5.0.0-preview.4.20201.1</MicrosoftEntityFrameworkCoreSqlitePackageVersion>
<MicrosoftEntityFrameworkCoreSqlServerPackageVersion>5.0.0-preview.4.20201.1</MicrosoftEntityFrameworkCoreSqlServerPackageVersion>
<MicrosoftEntityFrameworkCoreToolsPackageVersion>5.0.0-preview.4.20201.1</MicrosoftEntityFrameworkCoreToolsPackageVersion>
<MicrosoftEntityFrameworkCorePackageVersion>5.0.0-preview.4.20201.1</MicrosoftEntityFrameworkCorePackageVersion>
<dotnetefPackageVersion>5.0.0-preview.4.20203.1</dotnetefPackageVersion>
<MicrosoftEntityFrameworkCoreInMemoryPackageVersion>5.0.0-preview.4.20203.1</MicrosoftEntityFrameworkCoreInMemoryPackageVersion>
<MicrosoftEntityFrameworkCoreRelationalPackageVersion>5.0.0-preview.4.20203.1</MicrosoftEntityFrameworkCoreRelationalPackageVersion>
<MicrosoftEntityFrameworkCoreSqlitePackageVersion>5.0.0-preview.4.20203.1</MicrosoftEntityFrameworkCoreSqlitePackageVersion>
<MicrosoftEntityFrameworkCoreSqlServerPackageVersion>5.0.0-preview.4.20203.1</MicrosoftEntityFrameworkCoreSqlServerPackageVersion>
<MicrosoftEntityFrameworkCoreToolsPackageVersion>5.0.0-preview.4.20203.1</MicrosoftEntityFrameworkCoreToolsPackageVersion>
<MicrosoftEntityFrameworkCorePackageVersion>5.0.0-preview.4.20203.1</MicrosoftEntityFrameworkCorePackageVersion>
<!-- Packages from dotnet/aspnetcore-tooling -->
<MicrosoftAspNetCoreMvcRazorExtensionsPackageVersion>5.0.0-preview.4.20201.4</MicrosoftAspNetCoreMvcRazorExtensionsPackageVersion>
<MicrosoftAspNetCoreRazorLanguagePackageVersion>5.0.0-preview.4.20201.4</MicrosoftAspNetCoreRazorLanguagePackageVersion>

View File

@ -14,222 +14,41 @@ namespace RunTests
{
static async Task Main(string[] args)
{
var command = new RootCommand()
try
{
new Option(
aliases: new string[] { "--target", "-t" },
description: "The test dll to run")
{ Argument = new Argument<string>(), Required = true },
var runner = new TestRunner(RunTestsOptions.Parse(args));
new Option(
aliases: new string[] { "--sdk" },
description: "The version of the sdk being used")
{ Argument = new Argument<string>(), Required = true },
new Option(
aliases: new string[] { "--runtime" },
description: "The version of the runtime being used")
{ Argument = new Argument<string>(), Required = true },
new Option(
aliases: new string[] { "--queue" },
description: "The name of the Helix queue being run on")
{ Argument = new Argument<string>(), Required = true },
new Option(
aliases: new string[] { "--arch" },
description: "The architecture being run on")
{ Argument = new Argument<string>(), Required = true },
new Option(
aliases: new string[] { "--quarantined" },
description: "Whether quarantined tests should run or not")
{ Argument = new Argument<bool>(), Required = true },
new Option(
aliases: new string[] { "--ef" },
description: "The version of the EF tool to use")
{ Argument = new Argument<string>(), Required = true },
};
var parseResult = command.Parse(args);
var target = parseResult.ValueForOption<string>("--target");
var sdkVersion = parseResult.ValueForOption<string>("--sdk");
var runtimeVersion = parseResult.ValueForOption<string>("--runtime");
var helixQueue = parseResult.ValueForOption<string>("--queue");
var architecture = parseResult.ValueForOption<string>("--arch");
var quarantined = parseResult.ValueForOption<bool>("--quarantined");
var efVersion = parseResult.ValueForOption<string>("--ef");
var HELIX_WORKITEM_ROOT = Environment.GetEnvironmentVariable("HELIX_WORKITEM_ROOT");
var path = Environment.GetEnvironmentVariable("PATH");
var dotnetRoot = Environment.GetEnvironmentVariable("DOTNET_ROOT");
// Rename default.NuGet.config to NuGet.config if there is not a custom one from the project
// We use a local NuGet.config file to avoid polluting global machine state and avoid relying on global machine state
if (!File.Exists("NuGet.config"))
{
File.Copy("default.NuGet.config", "NuGet.config");
}
var environmentVariables = new Dictionary<string, string>();
environmentVariables.Add("PATH", path);
environmentVariables.Add("DOTNET_ROOT", dotnetRoot);
environmentVariables.Add("helix", helixQueue);
Console.WriteLine($"Current Directory: {HELIX_WORKITEM_ROOT}");
var helixDir = HELIX_WORKITEM_ROOT;
Console.WriteLine($"Setting HELIX_DIR: {helixDir}");
environmentVariables.Add("HELIX_DIR", helixDir);
environmentVariables.Add("NUGET_FALLBACK_PACKAGES", helixDir);
var nugetRestore = Path.Combine(helixDir, "nugetRestore");
Console.WriteLine($"Creating nuget restore directory: {nugetRestore}");
environmentVariables.Add("NUGET_RESTORE", nugetRestore);
var dotnetEFFullPath = Path.Combine(nugetRestore, $"dotnet-ef/{efVersion}/tools/netcoreapp3.1/any/dotnet-ef.exe");
Console.WriteLine($"Set DotNetEfFullPath: {dotnetEFFullPath}");
environmentVariables.Add("DotNetEfFullPath", dotnetEFFullPath);
Console.WriteLine("Checking for Microsoft.AspNetCore.App/");
if (Directory.Exists("Microsoft.AspNetCore.App"))
{
Console.WriteLine($"Found Microsoft.AspNetCore.App/, copying to {dotnetRoot}/shared/Microsoft.AspNetCore.App/{runtimeVersion}");
foreach (var file in Directory.EnumerateFiles("Microsoft.AspNetCore.App", "*.*", SearchOption.AllDirectories))
var keepGoing = runner.SetupEnvironment();
if (keepGoing)
{
File.Copy(file, $"{dotnetRoot}/shared/Microsoft.AspNetCore.App/{runtimeVersion}", overwrite: true);
keepGoing = await runner.InstallAspNetAppIfNeededAsync();
}
Console.WriteLine($"Adding current directory to nuget sources: {HELIX_WORKITEM_ROOT}");
runner.DisplayContents();
await ProcessUtil.RunAsync($"{dotnetRoot}/dotnet",
$"nuget add source {HELIX_WORKITEM_ROOT} --configfile NuGet.config",
environmentVariables: environmentVariables);
if (keepGoing)
{
if (!await runner.CheckTestDiscoveryAsync())
{
Console.WriteLine("RunTest stopping due to test discovery failure.");
Environment.Exit(1);
return;
}
await ProcessUtil.RunAsync($"{dotnetRoot}/dotnet",
"nuget add source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet5/nuget/v3/index.json --configfile NuGet.config",
environmentVariables: environmentVariables);
var exitCode = await runner.RunTestsAsync();
runner.UploadResults();
Console.WriteLine($"Completed Helix job with exit code '{exitCode}'");
Environment.Exit(exitCode);
}
// Write nuget sources to console, useful for debugging purposes
await ProcessUtil.RunAsync($"{dotnetRoot}/dotnet",
"nuget list source",
environmentVariables: environmentVariables,
outputDataReceived: Console.WriteLine,
errorDataReceived: Console.WriteLine);
await ProcessUtil.RunAsync($"{dotnetRoot}/dotnet",
$"tool install dotnet-ef --global --version {efVersion}",
environmentVariables: environmentVariables);
// ';' is the path separator on Windows, and ':' on Unix
path += RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ";" : ":";
path += $"{Environment.GetEnvironmentVariable("DOTNET_CLI_HOME")}/.dotnet/tools";
environmentVariables["PATH"] = path;
}
Directory.CreateDirectory(nugetRestore);
// Rename default.runner.json to xunit.runner.json if there is not a custom one from the project
if (!File.Exists("xunit.runner.json"))
{
File.Copy("default.runner.json", "xunit.runner.json");
}
Console.WriteLine();
Console.WriteLine("Displaying directory contents:");
foreach (var file in Directory.EnumerateFiles("./"))
{
Console.WriteLine(Path.GetFileName(file));
}
foreach (var file in Directory.EnumerateDirectories("./"))
{
Console.WriteLine(Path.GetFileName(file));
}
Console.WriteLine();
// Run test discovery so we know if there are tests to run
var discoveryResult = await ProcessUtil.RunAsync($"{dotnetRoot}/dotnet",
$"vstest {target} -lt",
environmentVariables: environmentVariables);
if (discoveryResult.StandardOutput.Contains("Exception thrown"))
{
Console.WriteLine("Exception thrown during test discovery.");
Console.WriteLine(discoveryResult.StandardOutput);
Console.WriteLine("Tests were not run due to previous failures. Exit code=1");
Environment.Exit(1);
return;
}
var exitCode = 0;
var commonTestArgs = $"vstest {target} --logger:xunit --logger:\"console;verbosity=normal\" --blame";
if (quarantined)
catch (Exception e)
{
Console.WriteLine("Running quarantined tests.");
// Filter syntax: https://github.com/Microsoft/vstest-docs/blob/master/docs/filter.md
var result = await ProcessUtil.RunAsync($"{dotnetRoot}/dotnet",
commonTestArgs + " --TestCaseFilter:\"Quarantined=true\"",
environmentVariables: environmentVariables,
outputDataReceived: Console.WriteLine,
errorDataReceived: Console.WriteLine,
throwOnError: false);
if (result.ExitCode != 0)
{
Console.WriteLine($"Failure in quarantined tests. Exit code: {result.ExitCode}.");
}
Console.WriteLine($"RunTests uncaught exception: {e.ToString()}");
Environment.Exit(1);
}
else
{
Console.WriteLine("Running non-quarantined tests.");
// Filter syntax: https://github.com/Microsoft/vstest-docs/blob/master/docs/filter.md
var result = await ProcessUtil.RunAsync($"{dotnetRoot}/dotnet",
commonTestArgs + " --TestCaseFilter:\"Quarantined!=true\"",
environmentVariables: environmentVariables,
outputDataReceived: Console.WriteLine,
errorDataReceived: Console.Error.WriteLine,
throwOnError: false);
if (result.ExitCode != 0)
{
Console.WriteLine($"Failure in non-quarantined tests. Exit code: {result.ExitCode}.");
exitCode = result.ExitCode;
}
}
// 'testResults.xml' is the file Helix looks for when processing test results
Console.WriteLine();
if (File.Exists("TestResults/TestResults.xml"))
{
Console.WriteLine("Copying TestResults/TestResults.xml to ./testResults.xml");
File.Copy("TestResults/TestResults.xml", "testResults.xml");
}
else
{
Console.WriteLine("No test results found.");
}
var HELIX_WORKITEM_UPLOAD_ROOT = Environment.GetEnvironmentVariable("HELIX_WORKITEM_UPLOAD_ROOT");
Console.WriteLine($"Copying artifacts/log/ to {HELIX_WORKITEM_UPLOAD_ROOT}/");
if (Directory.Exists("artifacts/log"))
{
foreach (var file in Directory.EnumerateFiles("artifacts/log", "*.log", SearchOption.AllDirectories))
{
// Combine the directory name + log name for the copied log file name to avoid overwriting duplicate test names in different test projects
var logName = $"{Path.GetFileName(Path.GetDirectoryName(file))}_{Path.GetFileName(file)}";
Console.WriteLine($"Copying: {file} to {Path.Combine(HELIX_WORKITEM_UPLOAD_ROOT, logName)}");
// Need to copy to HELIX_WORKITEM_UPLOAD_ROOT and HELIX_WORKITEM_UPLOAD_ROOT/../ in order for Azure Devops attachments to link properly and for Helix to store the logs
File.Copy(file, Path.Combine(HELIX_WORKITEM_UPLOAD_ROOT, logName));
File.Copy(file, Path.Combine(HELIX_WORKITEM_UPLOAD_ROOT, "..", logName));
}
}
else
{
Console.WriteLine("No logs found in artifacts/log");
}
Console.WriteLine("Completed Helix job.");
Environment.Exit(exitCode);
}
}
}

View File

@ -0,0 +1,81 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.CommandLine;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
namespace RunTests
{
public class RunTestsOptions
{
public static RunTestsOptions Parse(string[] args)
{
var command = new RootCommand()
{
new Option(
aliases: new string[] { "--target", "-t" },
description: "The test dll to run")
{ Argument = new Argument<string>(), Required = true },
new Option(
aliases: new string[] { "--sdk" },
description: "The version of the sdk being used")
{ Argument = new Argument<string>(), Required = true },
new Option(
aliases: new string[] { "--runtime" },
description: "The version of the runtime being used")
{ Argument = new Argument<string>(), Required = true },
new Option(
aliases: new string[] { "--queue" },
description: "The name of the Helix queue being run on")
{ Argument = new Argument<string>(), Required = true },
new Option(
aliases: new string[] { "--arch" },
description: "The architecture being run on")
{ Argument = new Argument<string>(), Required = true },
new Option(
aliases: new string[] { "--quarantined" },
description: "Whether quarantined tests should run or not")
{ Argument = new Argument<bool>(), Required = true },
new Option(
aliases: new string[] { "--ef" },
description: "The version of the EF tool to use")
{ Argument = new Argument<string>(), Required = true },
};
var parseResult = command.Parse(args);
var options = new RunTestsOptions();
options.Target = parseResult.ValueForOption<string>("--target");
options.SdkVersion = parseResult.ValueForOption<string>("--sdk");
options.RuntimeVersion = parseResult.ValueForOption<string>("--runtime");
options.HelixQueue = parseResult.ValueForOption<string>("--queue");
options.Architecture = parseResult.ValueForOption<string>("--arch");
options.Quarantined = parseResult.ValueForOption<bool>("--quarantined");
options.EfVersion = parseResult.ValueForOption<string>("--ef");
options.HELIX_WORKITEM_ROOT = Environment.GetEnvironmentVariable("HELIX_WORKITEM_ROOT");
options.Path = Environment.GetEnvironmentVariable("PATH");
options.DotnetRoot = Environment.GetEnvironmentVariable("DOTNET_ROOT");
return options;
}
public string Target { get; set;}
public string SdkVersion { get; set;}
public string RuntimeVersion { get; set;}
public string HelixQueue { get; set;}
public string Architecture { get; set;}
public bool Quarantined { get; set;}
public string EfVersion { get; set;}
public string HELIX_WORKITEM_ROOT { get; set;}
public string DotnetRoot { get; set; }
public string Path { get; set; }
}
}

View File

@ -0,0 +1,251 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.CommandLine;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
namespace RunTests
{
public class TestRunner
{
public TestRunner(RunTestsOptions options)
{
Options = options;
EnvironmentVariables = new Dictionary<string, string>();
}
public RunTestsOptions Options { get; set; }
public Dictionary<string, string> EnvironmentVariables { get; set; }
public bool SetupEnvironment()
{
try
{
// Rename default.NuGet.config to NuGet.config if there is not a custom one from the project
// We use a local NuGet.config file to avoid polluting global machine state and avoid relying on global machine state
if (!File.Exists("NuGet.config"))
{
File.Copy("default.NuGet.config", "NuGet.config");
}
EnvironmentVariables.Add("PATH", Options.Path);
EnvironmentVariables.Add("DOTNET_ROOT", Options.DotnetRoot);
EnvironmentVariables.Add("helix", Options.HelixQueue);
Console.WriteLine($"Current Directory: {Options.HELIX_WORKITEM_ROOT}");
var helixDir = Options.HELIX_WORKITEM_ROOT;
Console.WriteLine($"Setting HELIX_DIR: {helixDir}");
EnvironmentVariables.Add("HELIX_DIR", helixDir);
EnvironmentVariables.Add("NUGET_FALLBACK_PACKAGES", helixDir);
var nugetRestore = Path.Combine(helixDir, "nugetRestore");
EnvironmentVariables.Add("NUGET_RESTORE", nugetRestore);
var dotnetEFFullPath = Path.Combine(nugetRestore, $"dotnet-ef/{Options.EfVersion}/tools/netcoreapp3.1/any/dotnet-ef.exe");
Console.WriteLine($"Set DotNetEfFullPath: {dotnetEFFullPath}");
EnvironmentVariables.Add("DotNetEfFullPath", dotnetEFFullPath);
Console.WriteLine($"Creating nuget restore directory: {nugetRestore}");
Directory.CreateDirectory(nugetRestore);
// Rename default.runner.json to xunit.runner.json if there is not a custom one from the project
if (!File.Exists("xunit.runner.json"))
{
File.Copy("default.runner.json", "xunit.runner.json");
}
return true;
}
catch (Exception e)
{
Console.WriteLine($"Exception in SetupEnvironment: {e.ToString()}");
return false;
}
}
public void DisplayContents()
{
try
{
Console.WriteLine();
Console.WriteLine("Displaying directory contents:");
foreach (var file in Directory.EnumerateFiles("./"))
{
Console.WriteLine(Path.GetFileName(file));
}
foreach (var file in Directory.EnumerateDirectories("./"))
{
Console.WriteLine(Path.GetFileName(file));
}
Console.WriteLine();
}
catch (Exception e)
{
Console.WriteLine($"Exception in DisplayInitialState: {e.ToString()}");
}
}
public async Task<bool> InstallAspNetAppIfNeededAsync()
{
try
{
Console.WriteLine("Checking for Microsoft.AspNetCore.App/");
if (Directory.Exists("Microsoft.AspNetCore.App"))
{
var appRuntimePath = $"{Options.DotnetRoot}/shared/Microsoft.AspNetCore.App/{Options.RuntimeVersion}";
Console.WriteLine($"Found Microsoft.AspNetCore.App/, copying to {appRuntimePath}");
foreach (var file in Directory.EnumerateFiles("Microsoft.AspNetCore.App", "*.*", SearchOption.AllDirectories))
{
File.Copy(file, Path.Combine(appRuntimePath, file), overwrite: true);
}
Console.WriteLine($"Adding current directory to nuget sources: {Options.HELIX_WORKITEM_ROOT}");
await ProcessUtil.RunAsync($"{Options.DotnetRoot}/dotnet",
$"nuget add source {Options.HELIX_WORKITEM_ROOT} --configfile NuGet.config",
environmentVariables: EnvironmentVariables);
await ProcessUtil.RunAsync($"{Options.DotnetRoot}/dotnet",
"nuget add source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet5/nuget/v3/index.json --configfile NuGet.config",
environmentVariables: EnvironmentVariables);
// Write nuget sources to console, useful for debugging purposes
await ProcessUtil.RunAsync($"{Options.DotnetRoot}/dotnet",
"nuget list source",
environmentVariables: EnvironmentVariables,
outputDataReceived: Console.WriteLine,
errorDataReceived: Console.WriteLine);
await ProcessUtil.RunAsync($"{Options.DotnetRoot}/dotnet",
$"tool install dotnet-ef --global --version {Options.EfVersion}",
environmentVariables: EnvironmentVariables);
// ';' is the path separator on Windows, and ':' on Unix
Options.Path += RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ";" : ":";
Options.Path += $"{Environment.GetEnvironmentVariable("DOTNET_CLI_HOME")}/.dotnet/tools";
EnvironmentVariables["PATH"] = Options.Path;
}
else
{
Console.WriteLine($"No app runtime found, skipping...");
}
return true;
}
catch (Exception e)
{
Console.WriteLine($"Exception in InstallAspNetAppIfNeeded: {e.ToString()}");
return false;
}
}
public async Task<bool> CheckTestDiscoveryAsync()
{
try
{
// Run test discovery so we know if there are tests to run
var discoveryResult = await ProcessUtil.RunAsync($"{Options.DotnetRoot}/dotnet",
$"vstest {Options.Target} -lt",
environmentVariables: EnvironmentVariables);
if (discoveryResult.StandardOutput.Contains("Exception thrown"))
{
Console.WriteLine("Exception thrown during test discovery.");
Console.WriteLine(discoveryResult.StandardOutput);
return false;
}
return true;
}
catch (Exception e)
{
Console.WriteLine($"Exception in CheckTestDiscovery: {e.ToString()}");
return false;
}
}
public async Task<int> RunTestsAsync()
{
var exitCode = 0;
try
{
var commonTestArgs = $"vstest {Options.Target} --logger:xunit --logger:\"console;verbosity=normal\" --blame";
if (Options.Quarantined)
{
Console.WriteLine("Running quarantined tests.");
// Filter syntax: https://github.com/Microsoft/vstest-docs/blob/master/docs/filter.md
var result = await ProcessUtil.RunAsync($"{Options.DotnetRoot}/dotnet",
commonTestArgs + " --TestCaseFilter:\"Quarantined=true\"",
environmentVariables: EnvironmentVariables,
outputDataReceived: Console.WriteLine,
errorDataReceived: Console.WriteLine,
throwOnError: false);
if (result.ExitCode != 0)
{
Console.WriteLine($"Failure in quarantined tests. Exit code: {result.ExitCode}.");
}
}
else
{
Console.WriteLine("Running non-quarantined tests.");
// Filter syntax: https://github.com/Microsoft/vstest-docs/blob/master/docs/filter.md
var result = await ProcessUtil.RunAsync($"{Options.DotnetRoot}/dotnet",
commonTestArgs + " --TestCaseFilter:\"Quarantined!=true\"",
environmentVariables: EnvironmentVariables,
outputDataReceived: Console.WriteLine,
errorDataReceived: Console.Error.WriteLine,
throwOnError: false);
if (result.ExitCode != 0)
{
Console.WriteLine($"Failure in non-quarantined tests. Exit code: {result.ExitCode}.");
exitCode = result.ExitCode;
}
}
}
catch (Exception e)
{
Console.WriteLine($"Exception in RunTests: {e.ToString()}");
exitCode = 1;
}
return exitCode;
}
public void UploadResults()
{
// 'testResults.xml' is the file Helix looks for when processing test results
Console.WriteLine("Trying to upload results...");
if (File.Exists("TestResults/TestResults.xml"))
{
Console.WriteLine("Copying TestResults/TestResults.xml to ./testResults.xml");
File.Copy("TestResults/TestResults.xml", "testResults.xml");
}
else
{
Console.WriteLine("No test results found.");
}
var HELIX_WORKITEM_UPLOAD_ROOT = Environment.GetEnvironmentVariable("HELIX_WORKITEM_UPLOAD_ROOT");
Console.WriteLine($"Copying artifacts/log/ to {HELIX_WORKITEM_UPLOAD_ROOT}/");
if (Directory.Exists("artifacts/log"))
{
foreach (var file in Directory.EnumerateFiles("artifacts/log", "*.log", SearchOption.AllDirectories))
{
// Combine the directory name + log name for the copied log file name to avoid overwriting duplicate test names in different test projects
var logName = $"{Path.GetFileName(Path.GetDirectoryName(file))}_{Path.GetFileName(file)}";
Console.WriteLine($"Copying: {file} to {Path.Combine(HELIX_WORKITEM_UPLOAD_ROOT, logName)}");
// Need to copy to HELIX_WORKITEM_UPLOAD_ROOT and HELIX_WORKITEM_UPLOAD_ROOT/../ in order for Azure Devops attachments to link properly and for Helix to store the logs
File.Copy(file, Path.Combine(HELIX_WORKITEM_UPLOAD_ROOT, logName));
File.Copy(file, Path.Combine(HELIX_WORKITEM_UPLOAD_ROOT, "..", logName));
}
}
else
{
Console.WriteLine("No logs found in artifacts/log");
}
}
}
}

View File

@ -21,10 +21,12 @@ echo "Installing Runtime"
powershell.exe -NoProfile -ExecutionPolicy unrestricted -Command "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; &([scriptblock]::Create((Invoke-WebRequest -useb 'https://dot.net/v1/dotnet-install.ps1'))) -Architecture %$arch% -Runtime dotnet -Version %$runtimeVersion% -InstallDir %DOTNET_ROOT%"
set exit_code=0
echo "Restore for RunTests..."
dotnet restore RunTests\RunTests.csproj --source https://api.nuget.org/v3/index.json --ignore-failed-sources
echo "Running tests..."
dotnet run --project RunTests\RunTests.csproj -- --target %1 --sdk %2 --runtime %3 --queue %4 --arch %5 --quarantined %6 --ef %7
if errorlevel 1 (
set exit_code=1
)
echo "Finished running tests: exit_code=%exit_code%"
exit /b %exit_code%

View File

@ -86,7 +86,10 @@ fi
sync
exit_code=0
echo "Restore for RunTests..."
$DOTNET_ROOT/dotnet restore RunTests/RunTests.csproj --source https://api.nuget.org/v3/index.json --ignore-failed-sources
echo "Running tests..."
$DOTNET_ROOT/dotnet run --project RunTests/RunTests.csproj -- --target $1 --sdk $2 --runtime $3 --queue $4 --arch $5 --quarantined $6 --ef $7
exit $?
exit_code = $?
echo "Finished tests...exit_code=$exit_code"
exit $exit_code

View File

@ -38,6 +38,15 @@ function _killSeleniumTrackedProcesses() {
}
}
function _listProcesses($processName) {
$processes = Get-WmiObject win32_process -Filter "name like '%$processName'" -ErrorAction SilentlyContinue;
if ($processes) {
Write-Host "These processes will be killed..."
$processes | select commandline | Out-String -Width 800
}
}
_listProcesses dotnet
_kill dotnet.exe
_kill testhost.exe
_kill iisexpress.exe

View File

@ -1,4 +1,12 @@
#!/usr/bin/env bash
# list processes that would be killed so they appear in the log
p=$(pgrep dotnet)
if [ $? -eq 0 ]
then
echo "These processes will be killed..."
ps -p $p
fi
pkill dotnet || true
exit 0

View File

@ -216,16 +216,16 @@
Condition=" '$(CompileUsingReferenceAssemblies)' != false AND '$(TargetFramework)' == '$(DefaultNetCoreTargetFramework)' ">true</_CompileTfmUsingReferenceAssemblies>
</PropertyGroup>
<!--
If we have a ref/ assembly from Extensions for a package, use that when compiling. The build-only reference to
Microsoft.Internal.Extensions.Refs ensures package is installed and $(MicrosoftInternalExtensionsRefsPath) is set.
If we have a ref/ assembly from dotnet/runtime for an Extension package, use that when compiling but do not reference its assemblies.
-->
<ItemGroup
Condition=" $(_CompileTfmUsingReferenceAssemblies) OR ('$(IsTargetingPackBuilding)' != 'false' AND '$(MSBuildProjectName)' == 'Microsoft.AspNetCore.App.Ref') ">
<PackageReference Include="Microsoft.Internal.Extensions.Refs"
Version="$(MicrosoftInternalExtensionsRefsPackageVersion)"
<PackageReference Include="Microsoft.Extensions.Internal.Transport"
Version="$(MicrosoftExtensionsInternalTransportPackageVersion)"
IsImplicitlyDefined="true"
IncludeAssets="Build"
PrivateAssets="All" />
IncludeAssets="Compile"
PrivateAssets="All"
GeneratePathProperty="true" />
</ItemGroup>
<!-- These targets are used to generate the map of assembly name to project files. See also the /t:GenerateProjectList target in build/repo.targets. -->

View File

@ -2810,8 +2810,7 @@ namespace Microsoft.AspNetCore.Components.Test
Assert.Equal(10, component.OnAfterRenderCallCount);
}
[ConditionalFact]
[SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/7487")]
[Fact]
public async Task CanTriggerEventHandlerDisposedInEarlierPendingBatchAsync()
{
// This represents the scenario where the same event handler is being triggered

View File

@ -14,8 +14,8 @@
<Reference Include="Microsoft.AspNetCore.Mvc" />
<Reference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" />
<Reference Include="Microsoft.AspNetCore.Mvc.ViewFeatures" />
<Reference Include="Microsoft.AspNetCore.Testing" />
<Reference Include="Microsoft.Extensions.Hosting" />
<Reference Include="Microsoft.Extensions.Logging.Testing" />
</ItemGroup>
<ItemGroup>

View File

@ -53,6 +53,10 @@ This package is an internal implementation of the .NET Core SDK and is not meant
<!-- Platform manifest and package override metatdata -->
<ReferencePackSharedFxVersion>$(AspNetCoreMajorVersion).$(AspNetCoreMinorVersion).0</ReferencePackSharedFxVersion>
<ReferencePackSharedFxVersion Condition="'$(VersionSuffix)' != ''">$(ReferencePackSharedFxVersion)-$(VersionSuffix)</ReferencePackSharedFxVersion>
<!-- Runtime extensions transport paths -->
<RuntimeExtensionsReferenceDirectory>$(PkgMicrosoft_Extensions_Internal_Transport)\ref\$(TargetFramework)\</RuntimeExtensionsReferenceDirectory>
</PropertyGroup>
<ItemGroup>
@ -109,14 +113,14 @@ This package is an internal implementation of the .NET Core SDK and is not meant
BeforeTargets="_GetPackageFiles"
DependsOnTargets="ResolveReferences;FindReferenceAssembliesForReferences">
<ItemGroup>
<_AvailableExtensionsRefAssemblies Include="$(MicrosoftInternalExtensionsRefsPath)\*.dll" />
<_AvailableExtensionsRefAssemblies Include="$(RuntimeExtensionsReferenceDirectory)*.dll" />
</ItemGroup>
<JoinItems Left="@(ReferencePathWithRefAssemblies)" Right="@(_AvailableExtensionsRefAssemblies)" LeftKey="Filename" RightKey="Filename" ItemSpecToUse="Left">
<Output TaskParameter="JoinResult" ItemName="_ReferencedExtensionsRefAssemblies" />
</JoinItems>
<!-- _DuplicatedExtensionsRefAssemblies represents ref assemblies that are present in Microsoft.Internal.Extensions.Refs package and also are built in this repo. -->
<!-- _DuplicatedExtensionsRefAssemblies represents ref assemblies that are present in Microsoft.Extensions.Internal.Transport package and also are built in this repo. -->
<!-- This should be temporary while we migrate sources since there should be no duplication when sources are deleted from extensions. -->
<JoinItems Left="@(_ReferencedExtensionsRefAssemblies)" Right="@(_ResolvedProjectReferencePaths)" LeftKey="Filename" RightKey="Filename" ItemSpecToUse="Left">
<Output TaskParameter="JoinResult" ItemName="_DuplicatedExtensionsRefAssemblies" />
@ -131,7 +135,7 @@ This package is an internal implementation of the .NET Core SDK and is not meant
</JoinItems>
<ItemGroup>
<!-- Do not exclude the ref assemblies that we built in this repo if it's also included in Microsoft.Internal.Extensions.Refs -->
<!-- Do not exclude the ref assemblies that we built in this repo if it's also included in Microsoft.Extensions.Internal.Transport -->
<_ReferencedExtensionsRefAssembliesToExclude Include="@(_ReferencedExtensionsRefAssemblies)" Exclude="@(_DuplicatedExtensionsRefAssemblies)" />
<!-- Exclude transitive external dependencies that are not directly referenced by projects in AspNetCore or Extensions -->
@ -147,10 +151,10 @@ This package is an internal implementation of the .NET Core SDK and is not meant
@(ReferencePathWithRefAssemblies->WithMetadataValue('ReferenceGrouping', 'Microsoft.NETCore.App'));" />
<AspNetCoreReferenceAssemblyPath
Include="@(_SelectedExtensionsRefAssemblies->'$(MicrosoftInternalExtensionsRefsPath)%(FileName)%(Extension)')" />
Include="@(_SelectedExtensionsRefAssemblies->'$(RuntimeExtensionsReferenceDirectory)%(FileName)%(Extension)')" />
<AspNetCoreReferenceDocXml Include="@(_ResolvedProjectReferencePaths->WithMetadataValue('IsReferenceAssembly', 'false')->'%(RootDir)%(Directory)%(FileName).xml')" />
<AspNetCoreReferenceDocXml Include="@(_SelectedExtensionsRefAssemblies->'$(MicrosoftInternalExtensionsRefsPath)%(FileName).xml')" />
<AspNetCoreReferenceDocXml Include="@(_SelectedExtensionsRefAssemblies->'$(RuntimeExtensionsReferenceDirectory)%(FileName).xml')" />
<RefPackContent Include="@(AspNetCoreReferenceAssemblyPath)" PackagePath="$(RefAssemblyPackagePath)" />
<RefPackContent Include="@(AspNetCoreReferenceDocXml)" PackagePath="$(RefAssemblyPackagePath)" />
@ -165,7 +169,7 @@ This package is an internal implementation of the .NET Core SDK and is not meant
Outputs="$(TargetDir)$(PackageConflictManifestFileName)">
<ItemGroup>
<!-- Use package version for non-Extensions references -->
<_AspNetCoreAppPackageOverrides Include="@(AspNetCoreReferenceAssemblyPath->'%(NuGetPackageId)|%(NuGetPackageVersion)')" Condition="!Exists('$(MicrosoftInternalExtensionsRefsPath)%(AspNetCoreReferenceAssemblyPath.NuGetPackageId).dll') AND '%(AspNetCoreReferenceAssemblyPath.NuGetPackageId)' != 'Microsoft.NETCore.App' AND '%(AspNetCoreReferenceAssemblyPath.NuGetPackageId)' != 'Microsoft.Internal.Extensions.Refs' AND '%(AspNetCoreReferenceAssemblyPath.NuGetSourceType)' == 'Package' " />
<_AspNetCoreAppPackageOverrides Include="@(AspNetCoreReferenceAssemblyPath->'%(NuGetPackageId)|%(NuGetPackageVersion)')" Condition="!Exists('$(RuntimeExtensionsReferenceDirectory)%(AspNetCoreReferenceAssemblyPath.NuGetPackageId).dll') AND '%(AspNetCoreReferenceAssemblyPath.NuGetPackageId)' != 'Microsoft.NETCore.App' AND '%(AspNetCoreReferenceAssemblyPath.NuGetPackageId)' != 'Microsoft.Extensions.Internal.Transport' AND '%(AspNetCoreReferenceAssemblyPath.NuGetSourceType)' == 'Package' " />
<!-- Pin version for extensions references -->
<_AspNetCoreAppPackageOverrides Include="@(_SelectedExtensionsRefAssemblies->'%(FileName)|$(MicrosoftInternalExtensionsRefsPackageOverrideVersion)')" />

View File

@ -156,12 +156,6 @@ This package is an internal implementation of the .NET Core SDK and is not meant
<RedistArchiveOutputPath>$(InstallersOutputPath)$(RedistArchiveOutputFileName)</RedistArchiveOutputPath>
</PropertyGroup>
<ItemGroup Condition="'$(DotNetBuildFromSource)' != 'true'">
<GenerateChecksumItems Include="$(RedistArchiveOutputPath)">
<DestinationPath>$(RedistArchiveOutputPath)$(ChecksumExtension)</DestinationPath>
</GenerateChecksumItems>
</ItemGroup>
<!-- Target chain -->
<PropertyGroup>
<ResolveReferencesDependsOn>

View File

@ -36,6 +36,10 @@
<_Parameter1>TargetingPackLayoutRoot</_Parameter1>
<_Parameter2>$(TargetingPackLayoutRoot)</_Parameter2>
</AssemblyAttribute>
<AssemblyAttribute Include="Microsoft.AspNetCore.TestData">
<_Parameter1>IsTargetingPackBuilding</_Parameter1>
<_Parameter2>$(IsTargetingPackBuilding)</_Parameter2>
</AssemblyAttribute>
<AssemblyAttribute Include="Microsoft.AspNetCore.TestData">
<_Parameter1>VerifyAncmBinary</_Parameter1>
<_Parameter2>$(VerifyAncmBinary)</_Parameter2>

View File

@ -20,17 +20,24 @@ namespace Microsoft.AspNetCore
private readonly string _expectedRid;
private readonly string _targetingPackRoot;
private readonly ITestOutputHelper _output;
private readonly bool _isTargetingPackBuilding;
public TargetingPackTests(ITestOutputHelper output)
{
_output = output;
_expectedRid = TestData.GetSharedFxRuntimeIdentifier();
_targetingPackRoot = Path.Combine(TestData.GetTestDataValue("TargetingPackLayoutRoot"), "packs", "Microsoft.AspNetCore.App.Ref", TestData.GetTestDataValue("TargetingPackVersion"));
_isTargetingPackBuilding = bool.Parse(TestData.GetTestDataValue("IsTargetingPackBuilding"));
}
[Fact]
public void AssembliesAreReferenceAssemblies()
{
if (!_isTargetingPackBuilding)
{
return;
}
IEnumerable<string> dlls = Directory.GetFiles(_targetingPackRoot, "*.dll", SearchOption.AllDirectories);
Assert.NotEmpty(dlls);
@ -58,6 +65,11 @@ namespace Microsoft.AspNetCore
[Fact]
public void PlatformManifestListsAllFiles()
{
if (!_isTargetingPackBuilding)
{
return;
}
var platformManifestPath = Path.Combine(_targetingPackRoot, "data", "PlatformManifest.txt");
var expectedAssemblies = TestData.GetSharedFxDependencies()
.Split(';', StringSplitOptions.RemoveEmptyEntries)

View File

@ -9,6 +9,7 @@
<Reference Include="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" />
<Reference Include="Microsoft.Extensions.Hosting.Abstractions" />
<Reference Include="Microsoft.Extensions.Options" />
<Reference Include="Microsoft.Extensions.Logging.Abstractions" />
<InternalsVisibleTo Include="Microsoft.Extensions.Diagnostics.HealthChecks.Tests" Key="" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == '$(DefaultNetCoreTargetFramework)'">
@ -16,6 +17,7 @@
<Reference Include="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" />
<Reference Include="Microsoft.Extensions.Hosting.Abstractions" />
<Reference Include="Microsoft.Extensions.Options" />
<Reference Include="Microsoft.Extensions.Logging.Abstractions" />
<InternalsVisibleTo Include="Microsoft.Extensions.Diagnostics.HealthChecks.Tests" Key="" />
</ItemGroup>
</Project>

View File

@ -28,6 +28,7 @@ Microsoft.Extensions.Diagnostics.HealthChecks.IHealthChecksBuilder
<Reference Include="Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions" />
<Reference Include="Microsoft.Extensions.Hosting.Abstractions" />
<Reference Include="Microsoft.Extensions.Options" />
<Reference Include="Microsoft.Extensions.Logging.Abstractions" />
</ItemGroup>
</Project>

View File

@ -23,7 +23,6 @@
<Reference Include="Microsoft.Extensions.FileProviders.Embedded" />
<Reference Include="Microsoft.Extensions.Logging" />
<Reference Include="Microsoft.Extensions.Logging.Console" />
<Reference Include="Microsoft.Extensions.Logging.Testing" />
<Reference Include="Microsoft.NETCore.Windows.ApiSets" />
<Reference Include="Serilog.Extensions.Logging" />
<Reference Include="Serilog.Sinks.File" />

View File

@ -32,6 +32,7 @@ namespace Microsoft.AspNetCore.Hosting.FunctionalTests
await ExecuteShutdownTest(nameof(ShutdownTestRun), "Run");
}
[QuarantinedTest]
[ConditionalFact]
[OSSkipCondition(OperatingSystems.Windows)]
[OSSkipCondition(OperatingSystems.MacOSX)]
@ -133,7 +134,7 @@ namespace Microsoft.AspNetCore.Hosting.FunctionalTests
private static void WaitForExitOrKill(Process process)
{
process.WaitForExit(1000);
process.WaitForExit(5 * 1000);
if (!process.HasExited)
{
process.Kill();

View File

@ -23,7 +23,6 @@ namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
X509KeyStorageFlags.DefaultKeySet);
[ConditionalFact]
[SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/6720")]
[FrameworkSkipCondition(RuntimeFrameworks.CLR)]
public void Configure_AddsDevelopmentKeyFromConfiguration()
{
@ -64,7 +63,7 @@ namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
}
[ConditionalFact]
[SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/6720")]
[SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/6720", Queues = "OSX.1014.Amd64;OSX.1014.Amd64.Open")]
public void Configure_LoadsPfxCertificateCredentialFromConfiguration()
{
// Arrange
@ -94,7 +93,7 @@ namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer
}
[ConditionalFact]
[SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/6720")]
[SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/6720", Queues = "OSX.1014.Amd64;OSX.1014.Amd64.Open")]
public void Configure_LoadsCertificateStoreCertificateCredentialFromConfiguration()
{
try

View File

@ -59,7 +59,7 @@ namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer.Configuration
}
[ConditionalFact]
[SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/6720")]
[SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/6720", Queues = "OSX.1014.Amd64;OSX.1014.Amd64.Open")]
public static void LoadFromStoreCert_SkipsCertificatesNotYetValid()
{
try
@ -82,7 +82,7 @@ namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer.Configuration
}
[ConditionalFact]
[SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/6720")]
[SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/6720", Queues = "OSX.1014.Amd64;OSX.1014.Amd64.Open")]
public static void LoadFromStoreCert_PrefersCertificatesCloserToExpirationDate()
{
try
@ -105,7 +105,7 @@ namespace Microsoft.AspNetCore.ApiAuthorization.IdentityServer.Configuration
}
[ConditionalFact]
[SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/6720")]
[SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/6720", Queues = "OSX.1014.Amd64;OSX.1014.Amd64.Open")]
public static void LoadFromStoreCert_SkipsExpiredCertificates()
{
try

View File

@ -11,6 +11,7 @@
<Reference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" />
<Reference Include="Microsoft.Extensions.Logging" />
<Reference Include="Microsoft.Extensions.Options" />
<Reference Include="System.ComponentModel.Annotations" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == '$(DefaultNetCoreTargetFramework)'">
<Compile Include="Microsoft.Extensions.Identity.Core.netcoreapp.cs" />

View File

@ -13,7 +13,8 @@
<Reference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" />
<Reference Include="Microsoft.Extensions.Logging" />
<Reference Include="Microsoft.Extensions.Options" />
<SuppressBaselineReference Include="System.ComponentModel.Annotations" />
<Reference Include="System.ComponentModel.Annotations" Condition="'$(TargetFramework)' == 'netstandard2.0'"/>
</ItemGroup>
</Project>

View File

@ -2,17 +2,3 @@ Microsoft IIS Common
--------------------------------
The repository contains common resources shared by IIS Out-Of-Band (OOB) products.
### Contributing
This project welcomes contributions and suggestions. Most contributions require you to agree to a
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
the rights to use your contribution. For details, visit https://cla.microsoft.com.
When you submit a pull request, a CLA-bot will automatically determine whether you need to provide
a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions
provided by the bot. You will only need to do this once across all repos using our CLA.
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.

View File

@ -2,17 +2,3 @@ Microsoft IIS Setup
--------------------------------
The repository contains setup resources shared by IIS Out-Of-Band (OOB) products.
### Contributing
This project welcomes contributions and suggestions. Most contributions require you to agree to a
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
the rights to use your contribution. For details, visit https://cla.microsoft.com.
When you submit a pull request, a CLA-bot will automatically determine whether you need to provide
a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions
provided by the bot. You will only need to do this once across all repos using our CLA.
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.

View File

@ -64,7 +64,7 @@
<Target Name="CopyToArtifactsDirectory"
Condition=" '$(IsShipping)' == 'true' AND '$(SkipCopyToArtifactsDirectory)' != 'true' "
AfterTargets="Build" BeforeTargets="GenerateChecksums">
BeforeTargets="Build">
<Copy SourceFiles="$(TargetPath)" DestinationFiles="$(InstallersOutputPath)$(PackageFileName)" />
<ItemGroup>
<_cabs Include="$(TargetDir)**/*.cab" />
@ -72,10 +72,4 @@
<Copy SourceFiles="@(_cabs)" DestinationFolder="$(InstallersOutputPath)" />
</Target>
<ItemGroup Condition=" '$(IsShipping)' == 'true' AND '$(SkipCopyToArtifactsDirectory)' != 'true' ">
<GenerateChecksumItems Include="$(InstallersOutputPath)$(PackageFileName)">
<DestinationPath>$(InstallersOutputPath)$(PackageFileName)$(ChecksumExtension)</DestinationPath>
</GenerateChecksumItems>
</ItemGroup>
</Project>

View File

@ -0,0 +1,8 @@
<Project>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory)..\, Directory.Build.props))\Directory.Build.props" />
<PropertyGroup>
<!-- These projects depend on a 3rd party source -->
<ExcludeFromSourceBuild>true</ExcludeFromSourceBuild>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,98 @@
// 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 Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging.AzureAppServices;
using Microsoft.Extensions.Logging.Configuration;
using Microsoft.Extensions.Options;
using static Microsoft.Extensions.DependencyInjection.ServiceDescriptor;
namespace Microsoft.Extensions.Logging
{
/// <summary>
/// Extension methods for adding Azure diagnostics logger.
/// </summary>
public static class AzureAppServicesLoggerFactoryExtensions
{
/// <summary>
/// Adds an Azure Web Apps diagnostics logger.
/// </summary>
/// <param name="builder">The extension method argument</param>
public static ILoggingBuilder AddAzureWebAppDiagnostics(this ILoggingBuilder builder)
{
var context = WebAppContext.Default;
// Only add the provider if we're in Azure WebApp. That cannot change once the apps started
return AddAzureWebAppDiagnostics(builder, context);
}
internal static ILoggingBuilder AddAzureWebAppDiagnostics(this ILoggingBuilder builder, IWebAppContext context)
{
if (!context.IsRunningInAzureWebApp)
{
return builder;
}
builder.AddConfiguration();
var config = SiteConfigurationProvider.GetAzureLoggingConfiguration(context);
var services = builder.Services;
var addedFileLogger = TryAddEnumerable(services, Singleton<ILoggerProvider, FileLoggerProvider>());
var addedBlobLogger = TryAddEnumerable(services, Singleton<ILoggerProvider, BlobLoggerProvider>());
if (addedFileLogger || addedBlobLogger)
{
services.AddSingleton(context);
services.AddSingleton<IOptionsChangeTokenSource<LoggerFilterOptions>>(
new ConfigurationChangeTokenSource<LoggerFilterOptions>(config));
}
if (addedFileLogger)
{
services.AddSingleton<IConfigureOptions<LoggerFilterOptions>>(CreateFileFilterConfigureOptions(config));
services.AddSingleton<IConfigureOptions<AzureFileLoggerOptions>>(new FileLoggerConfigureOptions(config, context));
services.AddSingleton<IOptionsChangeTokenSource<AzureFileLoggerOptions>>(
new ConfigurationChangeTokenSource<AzureFileLoggerOptions>(config));
LoggerProviderOptions.RegisterProviderOptions<AzureFileLoggerOptions, FileLoggerProvider>(builder.Services);
}
if (addedBlobLogger)
{
services.AddSingleton<IConfigureOptions<LoggerFilterOptions>>(CreateBlobFilterConfigureOptions(config));
services.AddSingleton<IConfigureOptions<AzureBlobLoggerOptions>>(new BlobLoggerConfigureOptions(config, context));
services.AddSingleton<IOptionsChangeTokenSource<AzureBlobLoggerOptions>>(
new ConfigurationChangeTokenSource<AzureBlobLoggerOptions>(config));
LoggerProviderOptions.RegisterProviderOptions<AzureBlobLoggerOptions, BlobLoggerProvider>(builder.Services);
}
return builder;
}
private static bool TryAddEnumerable(IServiceCollection collection, ServiceDescriptor descriptor)
{
var beforeCount = collection.Count;
collection.TryAddEnumerable(descriptor);
return beforeCount != collection.Count;
}
private static ConfigurationBasedLevelSwitcher CreateBlobFilterConfigureOptions(IConfiguration config)
{
return new ConfigurationBasedLevelSwitcher(
configuration: config,
provider: typeof(BlobLoggerProvider),
levelKey: "AzureBlobTraceLevel");
}
private static ConfigurationBasedLevelSwitcher CreateFileFilterConfigureOptions(IConfiguration config)
{
return new ConfigurationBasedLevelSwitcher(
configuration: config,
provider: typeof(FileLoggerProvider),
levelKey: "AzureDriveTraceLevel");
}
}
}

View File

@ -0,0 +1,39 @@
// 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;
namespace Microsoft.Extensions.Logging.AzureAppServices
{
/// <summary>
/// Options for Azure diagnostics blob logging.
/// </summary>
public class AzureBlobLoggerOptions: BatchingLoggerOptions
{
private string _blobName = "applicationLog.txt";
/// <summary>
/// Gets or sets the last section of log blob name.
/// Defaults to <c>"applicationLog.txt"</c>.
/// </summary>
public string BlobName
{
get { return _blobName; }
set
{
if (string.IsNullOrEmpty(value))
{
throw new ArgumentException(nameof(value), $"{nameof(BlobName)} must be non-empty string.");
}
_blobName = value;
}
}
internal string ContainerUrl { get; set; }
internal string ApplicationName { get; set; }
internal string ApplicationInstanceId { get; set; }
}
}

View File

@ -0,0 +1,73 @@
// 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;
namespace Microsoft.Extensions.Logging.AzureAppServices
{
/// <summary>
/// Options for Azure diagnostics file logging.
/// </summary>
public class AzureFileLoggerOptions: BatchingLoggerOptions
{
private int? _fileSizeLimit = 10 * 1024 * 1024;
private int? _retainedFileCountLimit = 2;
private string _fileName = "diagnostics-";
/// <summary>
/// Gets or sets a strictly positive value representing the maximum log size in bytes or null for no limit.
/// Once the log is full, no more messages will be appended.
/// Defaults to <c>10MB</c>.
/// </summary>
public int? FileSizeLimit
{
get { return _fileSizeLimit; }
set
{
if (value <= 0)
{
throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(FileSizeLimit)} must be positive.");
}
_fileSizeLimit = value;
}
}
/// <summary>
/// Gets or sets a strictly positive value representing the maximum retained file count or null for no limit.
/// Defaults to <c>2</c>.
/// </summary>
public int? RetainedFileCountLimit
{
get { return _retainedFileCountLimit; }
set
{
if (value <= 0)
{
throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(RetainedFileCountLimit)} must be positive.");
}
_retainedFileCountLimit = value;
}
}
/// <summary>
/// Gets or sets a string representing the prefix of the file name used to store the logging information.
/// The current date, in the format YYYYMMDD will be added after the given value.
/// Defaults to <c>diagnostics-</c>.
/// </summary>
public string FileName
{
get { return _fileName; }
set
{
if (string.IsNullOrEmpty(value))
{
throw new ArgumentException(nameof(value));
}
_fileName = value;
}
}
internal string LogDirectory { get; set; }
}
}

View File

@ -0,0 +1,37 @@
// 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 Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
namespace Microsoft.Extensions.Logging.AzureAppServices
{
internal class BatchLoggerConfigureOptions : IConfigureOptions<BatchingLoggerOptions>
{
private readonly IConfiguration _configuration;
private readonly string _isEnabledKey;
public BatchLoggerConfigureOptions(IConfiguration configuration, string isEnabledKey)
{
_configuration = configuration;
_isEnabledKey = isEnabledKey;
}
public void Configure(BatchingLoggerOptions options)
{
options.IsEnabled = TextToBoolean(_configuration.GetSection(_isEnabledKey)?.Value);
}
private static bool TextToBoolean(string text)
{
if (string.IsNullOrEmpty(text) ||
!bool.TryParse(text, out var result))
{
result = false;
}
return result;
}
}
}

View File

@ -0,0 +1,75 @@
// 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.Text;
namespace Microsoft.Extensions.Logging.AzureAppServices
{
internal class BatchingLogger : ILogger
{
private readonly BatchingLoggerProvider _provider;
private readonly string _category;
public BatchingLogger(BatchingLoggerProvider loggerProvider, string categoryName)
{
_provider = loggerProvider;
_category = categoryName;
}
public IDisposable BeginScope<TState>(TState state)
{
return null;
}
public bool IsEnabled(LogLevel logLevel)
{
return _provider.IsEnabled;
}
public void Log<TState>(DateTimeOffset timestamp, LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
if (!IsEnabled(logLevel))
{
return;
}
var builder = new StringBuilder();
builder.Append(timestamp.ToString("yyyy-MM-dd HH:mm:ss.fff zzz"));
builder.Append(" [");
builder.Append(logLevel.ToString());
builder.Append("] ");
builder.Append(_category);
var scopeProvider = _provider.ScopeProvider;
if (scopeProvider != null)
{
scopeProvider.ForEachScope((scope, stringBuilder) =>
{
stringBuilder.Append(" => ").Append(scope);
}, builder);
builder.AppendLine(":");
}
else
{
builder.Append(": ");
}
builder.AppendLine(formatter(state, exception));
if (exception != null)
{
builder.AppendLine(exception.ToString());
}
_provider.AddMessage(timestamp, builder.ToString());
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
{
Log(DateTimeOffset.Now, logLevel, eventId, state, exception, formatter);
}
}
}

View File

@ -0,0 +1,80 @@
// 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;
namespace Microsoft.Extensions.Logging.AzureAppServices
{
/// <summary>
/// Options for a logger which batches up log messages.
/// </summary>
public class BatchingLoggerOptions
{
private int? _batchSize;
private int? _backgroundQueueSize = 1000;
private TimeSpan _flushPeriod = TimeSpan.FromSeconds(1);
/// <summary>
/// Gets or sets the period after which logs will be flushed to the store.
/// </summary>
public TimeSpan FlushPeriod
{
get { return _flushPeriod; }
set
{
if (value <= TimeSpan.Zero)
{
throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(FlushPeriod)} must be positive.");
}
_flushPeriod = value;
}
}
/// <summary>
/// Gets or sets the maximum size of the background log message queue or null for no limit.
/// After maximum queue size is reached log event sink would start blocking.
/// Defaults to <c>1000</c>.
/// </summary>
public int? BackgroundQueueSize
{
get { return _backgroundQueueSize; }
set
{
if (value < 0)
{
throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(BackgroundQueueSize)} must be non-negative.");
}
_backgroundQueueSize = value;
}
}
/// <summary>
/// Gets or sets a maximum number of events to include in a single batch or null for no limit.
/// </summary>
/// Defaults to <c>null</c>.
public int? BatchSize
{
get { return _batchSize; }
set
{
if (value <= 0)
{
throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(BatchSize)} must be positive.");
}
_batchSize = value;
}
}
/// <summary>
/// Gets or sets value indicating if logger accepts and queues writes.
/// </summary>
public bool IsEnabled { get; set; }
/// <summary>
/// Gets or sets a value indicating whether scopes should be included in the message.
/// Defaults to <c>false</c>.
/// </summary>
public bool IncludeScopes { get; set; } = false;
}
}

View File

@ -0,0 +1,208 @@
// 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.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
namespace Microsoft.Extensions.Logging.AzureAppServices
{
/// <summary>
/// A provider of <see cref="BatchingLogger"/> instances.
/// </summary>
public abstract class BatchingLoggerProvider : ILoggerProvider, ISupportExternalScope
{
private readonly List<LogMessage> _currentBatch = new List<LogMessage>();
private readonly TimeSpan _interval;
private readonly int? _queueSize;
private readonly int? _batchSize;
private readonly IDisposable _optionsChangeToken;
private int _messagesDropped;
private BlockingCollection<LogMessage> _messageQueue;
private Task _outputTask;
private CancellationTokenSource _cancellationTokenSource;
private bool _includeScopes;
private IExternalScopeProvider _scopeProvider;
internal IExternalScopeProvider ScopeProvider => _includeScopes ? _scopeProvider : null;
internal BatchingLoggerProvider(IOptionsMonitor<BatchingLoggerOptions> options)
{
// NOTE: Only IsEnabled is monitored
var loggerOptions = options.CurrentValue;
if (loggerOptions.BatchSize <= 0)
{
throw new ArgumentOutOfRangeException(nameof(loggerOptions.BatchSize), $"{nameof(loggerOptions.BatchSize)} must be a positive number.");
}
if (loggerOptions.FlushPeriod <= TimeSpan.Zero)
{
throw new ArgumentOutOfRangeException(nameof(loggerOptions.FlushPeriod), $"{nameof(loggerOptions.FlushPeriod)} must be longer than zero.");
}
_interval = loggerOptions.FlushPeriod;
_batchSize = loggerOptions.BatchSize;
_queueSize = loggerOptions.BackgroundQueueSize;
_optionsChangeToken = options.OnChange(UpdateOptions);
UpdateOptions(options.CurrentValue);
}
/// <summary>
/// Checks if the queue is enabled.
/// </summary>
public bool IsEnabled { get; private set; }
private void UpdateOptions(BatchingLoggerOptions options)
{
var oldIsEnabled = IsEnabled;
IsEnabled = options.IsEnabled;
_includeScopes = options.IncludeScopes;
if (oldIsEnabled != IsEnabled)
{
if (IsEnabled)
{
Start();
}
else
{
Stop();
}
}
}
internal abstract Task WriteMessagesAsync(IEnumerable<LogMessage> messages, CancellationToken token);
private async Task ProcessLogQueue()
{
while (!_cancellationTokenSource.IsCancellationRequested)
{
var limit = _batchSize ?? int.MaxValue;
while (limit > 0 && _messageQueue.TryTake(out var message))
{
_currentBatch.Add(message);
limit--;
}
var messagesDropped = Interlocked.Exchange(ref _messagesDropped, 0);
if (messagesDropped != 0)
{
_currentBatch.Add(new LogMessage(DateTimeOffset.Now, $"{messagesDropped} message(s) dropped because of queue size limit. Increase the queue size or decrease logging verbosity to avoid this.{Environment.NewLine}"));
}
if (_currentBatch.Count > 0)
{
try
{
await WriteMessagesAsync(_currentBatch, _cancellationTokenSource.Token);
}
catch
{
// ignored
}
_currentBatch.Clear();
}
else
{
await IntervalAsync(_interval, _cancellationTokenSource.Token);
}
}
}
/// <summary>
/// Wait for the given <see cref="TimeSpan"/>.
/// </summary>
/// <param name="interval">The amount of time to wait.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the delay.</param>
/// <returns>A <see cref="Task"/> which completes when the <paramref name="interval"/> has passed or the <paramref name="cancellationToken"/> has been canceled.</returns>
protected virtual Task IntervalAsync(TimeSpan interval, CancellationToken cancellationToken)
{
return Task.Delay(interval, cancellationToken);
}
internal void AddMessage(DateTimeOffset timestamp, string message)
{
if (!_messageQueue.IsAddingCompleted)
{
try
{
if (!_messageQueue.TryAdd(new LogMessage(timestamp, message), millisecondsTimeout: 0, cancellationToken: _cancellationTokenSource.Token))
{
Interlocked.Increment(ref _messagesDropped);
}
}
catch
{
//cancellation token canceled or CompleteAdding called
}
}
}
private void Start()
{
_messageQueue = _queueSize == null ?
new BlockingCollection<LogMessage>(new ConcurrentQueue<LogMessage>()) :
new BlockingCollection<LogMessage>(new ConcurrentQueue<LogMessage>(), _queueSize.Value);
_cancellationTokenSource = new CancellationTokenSource();
_outputTask = Task.Run(ProcessLogQueue);
}
private void Stop()
{
_cancellationTokenSource.Cancel();
_messageQueue.CompleteAdding();
try
{
_outputTask.Wait(_interval);
}
catch (TaskCanceledException)
{
}
catch (AggregateException ex) when (ex.InnerExceptions.Count == 1 && ex.InnerExceptions[0] is TaskCanceledException)
{
}
}
/// <inheritdoc/>
public void Dispose()
{
_optionsChangeToken?.Dispose();
if (IsEnabled)
{
Stop();
}
}
/// <summary>
/// Creates a <see cref="BatchingLogger"/> with the given <paramref name="categoryName"/>.
/// </summary>
/// <param name="categoryName">The name of the category to create this logger with.</param>
/// <returns>The <see cref="BatchingLogger"/> that was created.</returns>
public ILogger CreateLogger(string categoryName)
{
return new BatchingLogger(this, categoryName);
}
/// <summary>
/// Sets the scope on this provider.
/// </summary>
/// <param name="scopeProvider">Provides the scope.</param>
void ISupportExternalScope.SetScopeProvider(IExternalScopeProvider scopeProvider)
{
_scopeProvider = scopeProvider;
}
}
}

View File

@ -0,0 +1,97 @@
// 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.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.Extensions.Logging.AzureAppServices
{
/// <inheritdoc />
internal class BlobAppendReferenceWrapper : ICloudAppendBlob
{
private readonly Uri _fullUri;
private readonly HttpClient _client;
private readonly Uri _appendUri;
public BlobAppendReferenceWrapper(string containerUrl, string name, HttpClient client)
{
var uriBuilder = new UriBuilder(containerUrl);
uriBuilder.Path += "/" + name;
_fullUri = uriBuilder.Uri;
AppendBlockQuery(uriBuilder);
_appendUri = uriBuilder.Uri;
_client = client;
}
/// <inheritdoc />
public async Task AppendAsync(ArraySegment<byte> data, CancellationToken cancellationToken)
{
Task<HttpResponseMessage> AppendDataAsync()
{
var message = new HttpRequestMessage(HttpMethod.Put, _appendUri)
{
Content = new ByteArrayContent(data.Array, data.Offset, data.Count)
};
AddCommonHeaders(message);
return _client.SendAsync(message, cancellationToken);
}
var response = await AppendDataAsync();
if (response.StatusCode == HttpStatusCode.NotFound)
{
// If no blob exists try creating it
var message = new HttpRequestMessage(HttpMethod.Put, _fullUri)
{
// Set Content-Length to 0 to create "Append Blob"
Content = new ByteArrayContent(Array.Empty<byte>()),
Headers =
{
{ "If-None-Match", "*" }
}
};
AddCommonHeaders(message);
response = await _client.SendAsync(message, cancellationToken);
// If result is 2** or 412 try to append again
if (response.IsSuccessStatusCode ||
response.StatusCode == HttpStatusCode.PreconditionFailed)
{
// Retry sending data after blob creation
response = await AppendDataAsync();
}
}
response.EnsureSuccessStatusCode();
}
private static void AddCommonHeaders(HttpRequestMessage message)
{
message.Headers.Add("x-ms-blob-type", "AppendBlob");
message.Headers.Add("x-ms-version", "2016-05-31");
message.Headers.Date = DateTimeOffset.UtcNow;
}
private static void AppendBlockQuery(UriBuilder uriBuilder)
{
// See https://msdn.microsoft.com/en-us/library/system.uribuilder.query.aspx for:
// Note: Do not append a string directly to Query property.
// If the length of Query is greater than 1, retrieve the property value
// as a string, remove the leading question mark, append the new query string,
// and set the property with the combined string.
var queryToAppend = "comp=appendblock";
if (uriBuilder.Query != null && uriBuilder.Query.Length > 1)
uriBuilder.Query = uriBuilder.Query.Substring(1) + "&" + queryToAppend;
else
uriBuilder.Query = queryToAppend;
}
}
}

View File

@ -0,0 +1,30 @@
// 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 Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
namespace Microsoft.Extensions.Logging.AzureAppServices
{
internal class BlobLoggerConfigureOptions : BatchLoggerConfigureOptions, IConfigureOptions<AzureBlobLoggerOptions>
{
private readonly IConfiguration _configuration;
private readonly IWebAppContext _context;
public BlobLoggerConfigureOptions(IConfiguration configuration, IWebAppContext context)
: base(configuration, "AzureBlobEnabled")
{
_configuration = configuration;
_context = context;
}
public void Configure(AzureBlobLoggerOptions options)
{
base.Configure(options);
options.ContainerUrl = _configuration.GetSection("APPSETTING_DIAGNOSTICS_AZUREBLOBCONTAINERSASURL")?.Value;
options.ApplicationName = _context.SiteName;
options.ApplicationInstanceId = _context.SiteInstanceId;
}
}
}

View File

@ -0,0 +1,92 @@
// 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.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
namespace Microsoft.Extensions.Logging.AzureAppServices
{
/// <summary>
/// The <see cref="ILoggerProvider"/> implementation that stores messages by appending them to Azure Blob in batches.
/// </summary>
[ProviderAlias("AzureAppServicesBlob")]
public class BlobLoggerProvider : BatchingLoggerProvider
{
private readonly string _appName;
private readonly string _fileName;
private readonly Func<string, ICloudAppendBlob> _blobReferenceFactory;
private readonly HttpClient _httpClient;
/// <summary>
/// Creates a new instance of <see cref="BlobLoggerProvider"/>
/// </summary>
/// <param name="options">The options to use when creating a provider.</param>
public BlobLoggerProvider(IOptionsMonitor<AzureBlobLoggerOptions> options)
: this(options, null)
{
_blobReferenceFactory = name => new BlobAppendReferenceWrapper(
options.CurrentValue.ContainerUrl,
name,
_httpClient);
}
/// <summary>
/// Creates a new instance of <see cref="BlobLoggerProvider"/>
/// </summary>
/// <param name="blobReferenceFactory">The container to store logs to.</param>
/// <param name="options">Options to be used in creating a logger.</param>
internal BlobLoggerProvider(
IOptionsMonitor<AzureBlobLoggerOptions> options,
Func<string, ICloudAppendBlob> blobReferenceFactory) :
base(options)
{
var value = options.CurrentValue;
_appName = value.ApplicationName;
_fileName = value.ApplicationInstanceId + "_" + value.BlobName;
_blobReferenceFactory = blobReferenceFactory;
_httpClient = new HttpClient();
}
internal override async Task WriteMessagesAsync(IEnumerable<LogMessage> messages, CancellationToken cancellationToken)
{
var eventGroups = messages.GroupBy(GetBlobKey);
foreach (var eventGroup in eventGroups)
{
var key = eventGroup.Key;
var blobName = $"{_appName}/{key.Year}/{key.Month:00}/{key.Day:00}/{key.Hour:00}/{_fileName}";
var blob = _blobReferenceFactory(blobName);
using (var stream = new MemoryStream())
using (var writer = new StreamWriter(stream))
{
foreach (var logEvent in eventGroup)
{
writer.Write(logEvent.Message);
}
await writer.FlushAsync();
var tryGetBuffer = stream.TryGetBuffer(out var buffer);
Debug.Assert(tryGetBuffer);
await blob.AppendAsync(buffer, cancellationToken);
}
}
}
private (int Year, int Month, int Day, int Hour) GetBlobKey(LogMessage e)
{
return (e.Timestamp.Year,
e.Timestamp.Month,
e.Timestamp.Day,
e.Timestamp.Hour);
}
}
}

View File

@ -0,0 +1,51 @@
// 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 Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
namespace Microsoft.Extensions.Logging.AzureAppServices
{
internal class ConfigurationBasedLevelSwitcher: IConfigureOptions<LoggerFilterOptions>
{
private readonly IConfiguration _configuration;
private readonly Type _provider;
private readonly string _levelKey;
public ConfigurationBasedLevelSwitcher(IConfiguration configuration, Type provider, string levelKey)
{
_configuration = configuration;
_provider = provider;
_levelKey = levelKey;
}
public void Configure(LoggerFilterOptions options)
{
options.Rules.Add(new LoggerFilterRule(_provider.FullName, null, GetLogLevel(), null));
}
private LogLevel GetLogLevel()
{
return TextToLogLevel(_configuration.GetSection(_levelKey)?.Value);
}
private static LogLevel TextToLogLevel(string text)
{
switch (text?.ToUpperInvariant())
{
case "ERROR":
return LogLevel.Error;
case "WARNING":
return LogLevel.Warning;
case "INFORMATION":
return LogLevel.Information;
case "VERBOSE":
return LogLevel.Trace;
default:
return LogLevel.None;
}
}
}
}

View File

@ -0,0 +1,27 @@
// 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.IO;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
namespace Microsoft.Extensions.Logging.AzureAppServices
{
internal class FileLoggerConfigureOptions : BatchLoggerConfigureOptions, IConfigureOptions<AzureFileLoggerOptions>
{
private readonly IWebAppContext _context;
public FileLoggerConfigureOptions(IConfiguration configuration, IWebAppContext context)
: base(configuration, "AzureDriveEnabled")
{
_context = context;
}
public void Configure(AzureFileLoggerOptions options)
{
base.Configure(options);
options.LogDirectory = Path.Combine(_context.HomeFolder, "LogFiles", "Application");
}
}
}

View File

@ -0,0 +1,89 @@
// 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;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;
namespace Microsoft.Extensions.Logging.AzureAppServices
{
/// <summary>
/// A <see cref="BatchingLoggerProvider"/> which writes out to a file.
/// </summary>
[ProviderAlias("AzureAppServicesFile")]
public class FileLoggerProvider : BatchingLoggerProvider
{
private readonly string _path;
private readonly string _fileName;
private readonly int? _maxFileSize;
private readonly int? _maxRetainedFiles;
/// <summary>
/// Creates a new instance of <see cref="FileLoggerProvider"/>.
/// </summary>
/// <param name="options">The options to use when creating a provider.</param>
public FileLoggerProvider(IOptionsMonitor<AzureFileLoggerOptions> options) : base(options)
{
var loggerOptions = options.CurrentValue;
_path = loggerOptions.LogDirectory;
_fileName = loggerOptions.FileName;
_maxFileSize = loggerOptions.FileSizeLimit;
_maxRetainedFiles = loggerOptions.RetainedFileCountLimit;
}
internal override async Task WriteMessagesAsync(IEnumerable<LogMessage> messages, CancellationToken cancellationToken)
{
Directory.CreateDirectory(_path);
foreach (var group in messages.GroupBy(GetGrouping))
{
var fullName = GetFullName(group.Key);
var fileInfo = new FileInfo(fullName);
if (_maxFileSize > 0 && fileInfo.Exists && fileInfo.Length > _maxFileSize)
{
return;
}
using (var streamWriter = File.AppendText(fullName))
{
foreach (var item in group)
{
await streamWriter.WriteAsync(item.Message);
}
}
}
RollFiles();
}
private string GetFullName((int Year, int Month, int Day) group)
{
return Path.Combine(_path, $"{_fileName}{group.Year:0000}{group.Month:00}{group.Day:00}.txt");
}
private (int Year, int Month, int Day) GetGrouping(LogMessage message)
{
return (message.Timestamp.Year, message.Timestamp.Month, message.Timestamp.Day);
}
private void RollFiles()
{
if (_maxRetainedFiles > 0)
{
var files = new DirectoryInfo(_path)
.GetFiles(_fileName + "*")
.OrderByDescending(f => f.Name)
.Skip(_maxRetainedFiles.Value);
foreach (var item in files)
{
item.Delete();
}
}
}
}
}

View File

@ -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.IO;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.Extensions.Logging.AzureAppServices
{
/// <summary>
/// Represents an append blob, a type of blob where blocks of data are always committed to the end of the blob.
/// </summary>
internal interface ICloudAppendBlob
{
/// <summary>
/// Initiates an asynchronous operation to open a stream for writing to the blob.
/// </summary>
/// <returns>A <see cref="T:System.Threading.Tasks.Task`1" /> object of type <see cref="Stream" /> that represents the asynchronous operation.</returns>
Task AppendAsync(ArraySegment<byte> data, CancellationToken cancellationToken);
}
}

View File

@ -0,0 +1,32 @@
// 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.
namespace Microsoft.Extensions.Logging.AzureAppServices
{
/// <summary>
/// Represents an Azure WebApp context
/// </summary>
internal interface IWebAppContext
{
/// <summary>
/// Gets the path to the home folder if running in Azure WebApp
/// </summary>
string HomeFolder { get; }
/// <summary>
/// Gets the name of site if running in Azure WebApp
/// </summary>
string SiteName { get; }
/// <summary>
/// Gets the id of site if running in Azure WebApp
/// </summary>
string SiteInstanceId { get; }
/// <summary>
/// Gets a value indicating whether or new we're in an Azure WebApp
/// </summary>
bool IsRunningInAzureWebApp { get; }
}
}

View File

@ -0,0 +1,20 @@
// 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;
namespace Microsoft.Extensions.Logging.AzureAppServices
{
internal readonly struct LogMessage
{
public LogMessage(DateTimeOffset timestamp, string message)
{
Timestamp = timestamp;
Message = message;
}
public DateTimeOffset Timestamp { get; }
public string Message { get; }
}
}

View File

@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Logger implementation to support Azure App Services 'Diagnostics logs' and 'Log stream' features.</Description>
<TargetFramework>netstandard2.0</TargetFramework>
<NoWarn>$(NoWarn);CS1591</NoWarn>
<IsPackable>true</IsPackable>
<IsShipping>true</IsShipping>
</PropertyGroup>
<ItemGroup>
<InternalsVisibleTo Include="Microsoft.Extensions.Logging.AzureAppServices.Tests" />
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" />
<Reference Include="Microsoft.Extensions.Configuration.Json" />
<Reference Include="Microsoft.Extensions.Logging.Abstractions" />
<Reference Include="Microsoft.Extensions.Logging.Configuration" />
<Reference Include="Microsoft.Extensions.Logging" />
<Reference Include="System.ValueTuple" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,8 @@
// 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.CompilerServices;
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]

View File

@ -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.IO;
using Microsoft.Extensions.Configuration;
namespace Microsoft.Extensions.Logging.AzureAppServices
{
internal class SiteConfigurationProvider
{
public static IConfiguration GetAzureLoggingConfiguration(IWebAppContext context)
{
var settingsFolder = Path.Combine(context.HomeFolder, "site", "diagnostics");
var settingsFile = Path.Combine(settingsFolder, "settings.json");
return new ConfigurationBuilder()
.AddEnvironmentVariables()
.AddJsonFile(settingsFile, optional: true, reloadOnChange: true)
.Build();
}
}
}

View File

@ -0,0 +1,34 @@
// 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;
namespace Microsoft.Extensions.Logging.AzureAppServices
{
/// <summary>
/// Represents the default implementation of <see cref="IWebAppContext"/>.
/// </summary>
internal class WebAppContext : IWebAppContext
{
/// <summary>
/// Gets the default instance of the WebApp context.
/// </summary>
public static WebAppContext Default { get; } = new WebAppContext();
private WebAppContext() { }
/// <inheritdoc />
public string HomeFolder { get; } = Environment.GetEnvironmentVariable("HOME");
/// <inheritdoc />
public string SiteName { get; } = Environment.GetEnvironmentVariable("WEBSITE_SITE_NAME");
/// <inheritdoc />
public string SiteInstanceId { get; } = Environment.GetEnvironmentVariable("WEBSITE_INSTANCE_ID");
/// <inheritdoc />
public bool IsRunningInAzureWebApp => !string.IsNullOrEmpty(HomeFolder) &&
!string.IsNullOrEmpty(SiteName);
}
}

View File

@ -0,0 +1,186 @@
// 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.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
namespace Microsoft.Extensions.Logging.AzureAppServices.Test
{
public class AzureAppendBlobTests
{
public string _containerUrl = "https://host/container?query=1";
public string _blobName = "blob/path";
[Fact]
public async Task SendsDataAsStream()
{
var testMessageHandler = new TestMessageHandler(async message =>
{
Assert.Equal(HttpMethod.Put, message.Method);
Assert.Equal("https://host/container/blob/path?query=1&comp=appendblock", message.RequestUri.ToString());
Assert.Equal(new byte[] { 0, 2, 3 }, await message.Content.ReadAsByteArrayAsync());
AssertDefaultHeaders(message);
return new HttpResponseMessage(HttpStatusCode.OK);
});
var blob = new BlobAppendReferenceWrapper(_containerUrl, _blobName, new HttpClient(testMessageHandler));
await blob.AppendAsync(new ArraySegment<byte>(new byte[] { 0, 2, 3 }), CancellationToken.None);
}
private static void AssertDefaultHeaders(HttpRequestMessage message)
{
Assert.Equal(new[] {"AppendBlob"}, message.Headers.GetValues("x-ms-blob-type"));
Assert.Equal(new[] {"2016-05-31"}, message.Headers.GetValues("x-ms-version"));
Assert.NotNull(message.Headers.Date);
}
[Theory]
[InlineData(HttpStatusCode.Created)]
[InlineData(HttpStatusCode.PreconditionFailed)]
public async Task CreatesBlobIfNotExist(HttpStatusCode createStatusCode)
{
var stage = 0;
var testMessageHandler = new TestMessageHandler(async message =>
{
// First PUT request
if (stage == 0)
{
Assert.Equal(HttpMethod.Put, message.Method);
Assert.Equal("https://host/container/blob/path?query=1&comp=appendblock", message.RequestUri.ToString());
Assert.Equal(new byte[] { 0, 2, 3 }, await message.Content.ReadAsByteArrayAsync());
Assert.Equal(3, message.Content.Headers.ContentLength);
AssertDefaultHeaders(message);
stage++;
return new HttpResponseMessage(HttpStatusCode.NotFound);
}
// Create request
if (stage == 1)
{
Assert.Equal(HttpMethod.Put, message.Method);
Assert.Equal("https://host/container/blob/path?query=1", message.RequestUri.ToString());
Assert.Equal(0, message.Content.Headers.ContentLength);
Assert.Equal(new[] { "*" }, message.Headers.GetValues("If-None-Match"));
AssertDefaultHeaders(message);
stage++;
return new HttpResponseMessage(createStatusCode);
}
// First PUT request
if (stage == 2)
{
Assert.Equal(HttpMethod.Put, message.Method);
Assert.Equal("https://host/container/blob/path?query=1&comp=appendblock", message.RequestUri.ToString());
Assert.Equal(new byte[] { 0, 2, 3 }, await message.Content.ReadAsByteArrayAsync());
Assert.Equal(3, message.Content.Headers.ContentLength);
AssertDefaultHeaders(message);
stage++;
return new HttpResponseMessage(HttpStatusCode.Created);
}
throw new NotImplementedException();
});
var blob = new BlobAppendReferenceWrapper(_containerUrl, _blobName, new HttpClient(testMessageHandler));
await blob.AppendAsync(new ArraySegment<byte>(new byte[] { 0, 2, 3 }), CancellationToken.None);
Assert.Equal(3, stage);
}
[Fact]
public async Task ThrowsForUnknownStatus()
{
var stage = 0;
var testMessageHandler = new TestMessageHandler(async message =>
{
// First PUT request
if (stage == 0)
{
Assert.Equal(HttpMethod.Put, message.Method);
Assert.Equal("https://host/container/blob/path?query=1&comp=appendblock", message.RequestUri.ToString());
Assert.Equal(new byte[] { 0, 2, 3 }, await message.Content.ReadAsByteArrayAsync());
Assert.Equal(3, message.Content.Headers.ContentLength);
AssertDefaultHeaders(message);
stage++;
return new HttpResponseMessage(HttpStatusCode.InternalServerError);
}
throw new NotImplementedException();
});
var blob = new BlobAppendReferenceWrapper(_containerUrl, _blobName, new HttpClient(testMessageHandler));
await Assert.ThrowsAsync<HttpRequestException>(() => blob.AppendAsync(new ArraySegment<byte>(new byte[] { 0, 2, 3 }), CancellationToken.None));
Assert.Equal(1, stage);
}
[Fact]
public async Task ThrowsForUnknownStatusDuringCreation()
{
var stage = 0;
var testMessageHandler = new TestMessageHandler(async message =>
{
// First PUT request
if (stage == 0)
{
Assert.Equal(HttpMethod.Put, message.Method);
Assert.Equal("https://host/container/blob/path?query=1&comp=appendblock", message.RequestUri.ToString());
Assert.Equal(new byte[] { 0, 2, 3 }, await message.Content.ReadAsByteArrayAsync());
Assert.Equal(3, message.Content.Headers.ContentLength);
AssertDefaultHeaders(message);
stage++;
return new HttpResponseMessage(HttpStatusCode.NotFound);
}
// Create request
if (stage == 1)
{
Assert.Equal(HttpMethod.Put, message.Method);
Assert.Equal("https://host/container/blob/path?query=1", message.RequestUri.ToString());
Assert.Equal(0, message.Content.Headers.ContentLength);
Assert.Equal(new[] { "*" }, message.Headers.GetValues("If-None-Match"));
AssertDefaultHeaders(message);
stage++;
return new HttpResponseMessage(HttpStatusCode.InternalServerError);
}
throw new NotImplementedException();
});
var blob = new BlobAppendReferenceWrapper(_containerUrl, _blobName, new HttpClient(testMessageHandler));
await Assert.ThrowsAsync<HttpRequestException>(() => blob.AppendAsync(new ArraySegment<byte>(new byte[] { 0, 2, 3 }), CancellationToken.None));
Assert.Equal(2, stage);
}
private class TestMessageHandler : HttpMessageHandler
{
private readonly Func<HttpRequestMessage, Task<HttpResponseMessage>> _callback;
public TestMessageHandler(Func<HttpRequestMessage, Task<HttpResponseMessage>> callback)
{
_callback = callback;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return await _callback(request);
}
}
}
}

View File

@ -0,0 +1,98 @@
// 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.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Moq;
using Xunit;
namespace Microsoft.Extensions.Logging.AzureAppServices.Test
{
public class AzureBlobSinkTests
{
DateTimeOffset _timestampOne = new DateTimeOffset(2016, 05, 04, 03, 02, 01, TimeSpan.Zero);
[Fact]
public async Task WritesMessagesInBatches()
{
var blob = new Mock<ICloudAppendBlob>();
var buffers = new List<byte[]>();
blob.Setup(b => b.AppendAsync(It.IsAny<ArraySegment<byte>>(), It.IsAny<CancellationToken>()))
.Callback((ArraySegment<byte> s, CancellationToken ct) => buffers.Add(ToArray(s)))
.Returns(Task.CompletedTask);
var sink = new TestBlobSink(name => blob.Object);
var logger = (BatchingLogger)sink.CreateLogger("Cat");
await sink.IntervalControl.Pause;
for (int i = 0; i < 5; i++)
{
logger.Log(_timestampOne, LogLevel.Information, 0, "Text " + i, null, (state, ex) => state);
}
sink.IntervalControl.Resume();
await sink.IntervalControl.Pause;
Assert.Single(buffers);
Assert.Equal(
"2016-05-04 03:02:01.000 +00:00 [Information] Cat: Text 0" + Environment.NewLine +
"2016-05-04 03:02:01.000 +00:00 [Information] Cat: Text 1" + Environment.NewLine +
"2016-05-04 03:02:01.000 +00:00 [Information] Cat: Text 2" + Environment.NewLine +
"2016-05-04 03:02:01.000 +00:00 [Information] Cat: Text 3" + Environment.NewLine +
"2016-05-04 03:02:01.000 +00:00 [Information] Cat: Text 4" + Environment.NewLine,
Encoding.UTF8.GetString(buffers[0]));
}
[Fact]
public async Task GroupsByHour()
{
var blob = new Mock<ICloudAppendBlob>();
var buffers = new List<byte[]>();
var names = new List<string>();
blob.Setup(b => b.AppendAsync(It.IsAny<ArraySegment<byte>>(), It.IsAny<CancellationToken>()))
.Callback((ArraySegment<byte> s, CancellationToken ct) => buffers.Add(ToArray(s)))
.Returns(Task.CompletedTask);
var sink = new TestBlobSink(name =>
{
names.Add(name);
return blob.Object;
});
var logger = (BatchingLogger)sink.CreateLogger("Cat");
await sink.IntervalControl.Pause;
var startDate = _timestampOne;
for (int i = 0; i < 3; i++)
{
logger.Log(startDate, LogLevel.Information, 0, "Text " + i, null, (state, ex) => state);
startDate = startDate.AddHours(1);
}
sink.IntervalControl.Resume();
await sink.IntervalControl.Pause;
Assert.Equal(3, buffers.Count);
Assert.Equal("appname/2016/05/04/03/42_filename", names[0]);
Assert.Equal("appname/2016/05/04/04/42_filename", names[1]);
Assert.Equal("appname/2016/05/04/05/42_filename", names[2]);
}
private byte[] ToArray(ArraySegment<byte> inputStream)
{
return inputStream.Array
.Skip(inputStream.Offset)
.Take(inputStream.Count)
.ToArray();
}
}
}

View File

@ -0,0 +1,70 @@
// 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.IO;
using Moq;
using Xunit;
namespace Microsoft.Extensions.Logging.AzureAppServices.Test
{
public class AzureDiagnosticsConfigurationProviderTests
{
[Fact]
public void NoConfigFile()
{
var tempFolder = Path.Combine(Path.GetTempPath(), "AzureWebAppLoggerThisFolderShouldNotExist");
var contextMock = new Mock<IWebAppContext>();
contextMock.SetupGet(c => c.HomeFolder)
.Returns(tempFolder);
var config = SiteConfigurationProvider.GetAzureLoggingConfiguration(contextMock.Object);
Assert.NotNull(config);
}
[Fact]
public void ReadsSettingsFileAndEnvironment()
{
var tempFolder = Path.Combine(Path.GetTempPath(), "WebAppLoggerConfigurationDisabledInSettingsFile");
try
{
var settingsFolder = Path.Combine(tempFolder, "site", "diagnostics");
var settingsFile = Path.Combine(settingsFolder, "settings.json");
if (!Directory.Exists(settingsFolder))
{
Directory.CreateDirectory(settingsFolder);
}
Environment.SetEnvironmentVariable("RANDOM_ENVIRONMENT_VARIABLE", "USEFUL_VALUE");
File.WriteAllText(settingsFile, @"{ ""key"":""test value"" }");
var contextMock = new Mock<IWebAppContext>();
contextMock.SetupGet(c => c.HomeFolder)
.Returns(tempFolder);
var config = SiteConfigurationProvider.GetAzureLoggingConfiguration(contextMock.Object);
Assert.Equal("test value", config["key"]);
Assert.Equal("USEFUL_VALUE", config["RANDOM_ENVIRONMENT_VARIABLE"]);
}
finally
{
if (Directory.Exists(tempFolder))
{
try
{
Directory.Delete(tempFolder, recursive: true);
}
catch
{
// Don't break the test if temp folder deletion fails.
}
}
}
}
}
}

View File

@ -0,0 +1,136 @@
// 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.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Xunit;
namespace Microsoft.Extensions.Logging.AzureAppServices.Test
{
public class BatchingLoggerProviderTests
{
private DateTimeOffset _timestampOne = new DateTimeOffset(2016, 05, 04, 03, 02, 01, TimeSpan.Zero);
private string _nl = Environment.NewLine;
private Regex _timeStampRegex = new Regex(@"^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d{3} .\d{2}:\d{2} ");
[Fact]
public async Task LogsInIntervals()
{
var provider = new TestBatchingLoggingProvider();
var logger = (BatchingLogger)provider.CreateLogger("Cat");
await provider.IntervalControl.Pause;
logger.Log(_timestampOne, LogLevel.Information, 0, "Info message", null, (state, ex) => state);
logger.Log(_timestampOne.AddHours(1), LogLevel.Error, 0, "Error message", null, (state, ex) => state);
provider.IntervalControl.Resume();
await provider.IntervalControl.Pause;
Assert.Equal("2016-05-04 03:02:01.000 +00:00 [Information] Cat: Info message" + _nl, provider.Batches[0][0].Message);
Assert.Equal("2016-05-04 04:02:01.000 +00:00 [Error] Cat: Error message" + _nl, provider.Batches[0][1].Message);
}
[Fact]
public async Task IncludesScopes()
{
var provider = new TestBatchingLoggingProvider(includeScopes: true);
var factory = new LoggerFactory(new [] { provider });
var logger = factory.CreateLogger("Cat");
await provider.IntervalControl.Pause;
using (logger.BeginScope("Scope"))
{
using (logger.BeginScope("Scope2"))
{
logger.Log(LogLevel.Information, 0, "Info message", null, (state, ex) => state);
}
}
provider.IntervalControl.Resume();
await provider.IntervalControl.Pause;
Assert.Matches(_timeStampRegex, provider.Batches[0][0].Message);
Assert.EndsWith(
" [Information] Cat => Scope => Scope2:" + _nl +
"Info message" + _nl,
provider.Batches[0][0].Message);
}
[Fact]
public async Task RespectsBatchSize()
{
var provider = new TestBatchingLoggingProvider(maxBatchSize: 1);
var logger = (BatchingLogger)provider.CreateLogger("Cat");
await provider.IntervalControl.Pause;
logger.Log(_timestampOne, LogLevel.Information, 0, "Info message", null, (state, ex) => state);
logger.Log(_timestampOne.AddHours(1), LogLevel.Error, 0, "Error message", null, (state, ex) => state);
provider.IntervalControl.Resume();
await provider.IntervalControl.Pause;
Assert.Equal(2, provider.Batches.Count);
Assert.Single(provider.Batches[0]);
Assert.Equal("2016-05-04 03:02:01.000 +00:00 [Information] Cat: Info message" + _nl, provider.Batches[0][0].Message);
Assert.Single(provider.Batches[1]);
Assert.Equal("2016-05-04 04:02:01.000 +00:00 [Error] Cat: Error message" + _nl, provider.Batches[1][0].Message);
}
[Fact]
public async Task DropsMessagesWhenReachingMaxQueue()
{
var provider = new TestBatchingLoggingProvider(maxQueueSize: 1);
var logger = (BatchingLogger)provider.CreateLogger("Cat");
await provider.IntervalControl.Pause;
logger.Log(_timestampOne, LogLevel.Information, 0, "Info message", null, (state, ex) => state);
logger.Log(_timestampOne.AddHours(1), LogLevel.Error, 0, "Error message", null, (state, ex) => state);
provider.IntervalControl.Resume();
await provider.IntervalControl.Pause;
Assert.Equal(2, provider.Batches[0].Length);
Assert.Equal("2016-05-04 03:02:01.000 +00:00 [Information] Cat: Info message" + _nl, provider.Batches[0][0].Message);
Assert.Equal("1 message(s) dropped because of queue size limit. Increase the queue size or decrease logging verbosity to avoid this." + _nl, provider.Batches[0][1].Message);
}
private class TestBatchingLoggingProvider: BatchingLoggerProvider
{
public List<LogMessage[]> Batches { get; } = new List<LogMessage[]>();
public ManualIntervalControl IntervalControl { get; } = new ManualIntervalControl();
public TestBatchingLoggingProvider(TimeSpan? interval = null, int? maxBatchSize = null, int? maxQueueSize = null, bool includeScopes = false)
: base(new OptionsWrapperMonitor<BatchingLoggerOptions>(new BatchingLoggerOptions
{
FlushPeriod = interval ?? TimeSpan.FromSeconds(1),
BatchSize = maxBatchSize,
BackgroundQueueSize = maxQueueSize,
IsEnabled = true,
IncludeScopes = includeScopes
}))
{
}
internal override Task WriteMessagesAsync(IEnumerable<LogMessage> messages, CancellationToken token)
{
Batches.Add(messages.ToArray());
return Task.CompletedTask;
}
protected override Task IntervalAsync(TimeSpan interval, CancellationToken cancellationToken)
{
return IntervalControl.IntervalAsync();
}
}
}
}

View File

@ -0,0 +1,71 @@
// 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.Collections.Generic;
using System.IO;
using Microsoft.Extensions.Configuration;
using Moq;
using Xunit;
namespace Microsoft.Extensions.Logging.AzureAppServices.Test
{
public class ConfigureOptionsTests
{
[Theory]
[InlineData(true)]
[InlineData(false)]
[InlineData(null)]
public void InitializesIsEnabled(bool? enabled)
{
var configuration = new ConfigurationBuilder().AddInMemoryCollection(new[]
{
new KeyValuePair<string, string>("IsEnabledKey", Convert.ToString(enabled))
}).Build();
var options = new BatchingLoggerOptions();
new BatchLoggerConfigureOptions(configuration, "IsEnabledKey").Configure(options);
Assert.Equal(enabled ?? false, options.IsEnabled);
}
[Fact]
public void InitializesLogDirectory()
{
var configuration = new ConfigurationBuilder().AddInMemoryCollection(new[]
{
new KeyValuePair<string, string>("APPSETTING_DIAGNOSTICS_AZUREBLOBCONTAINERSASURL", "http://container/url")
}).Build();
var contextMock = new Mock<IWebAppContext>();
contextMock.SetupGet(c => c.HomeFolder).Returns("Home");
var options = new AzureFileLoggerOptions();
new FileLoggerConfigureOptions(configuration, contextMock.Object).Configure(options);
Assert.Equal(Path.Combine("Home", "LogFiles", "Application"), options.LogDirectory);
}
[Fact]
public void InitializesBlobUriSiteInstanceAndName()
{
var configuration = new ConfigurationBuilder().AddInMemoryCollection(new []
{
new KeyValuePair<string, string>("APPSETTING_DIAGNOSTICS_AZUREBLOBCONTAINERSASURL", "http://container/url")
}).Build();
var contextMock = new Mock<IWebAppContext>();
contextMock.SetupGet(c => c.HomeFolder).Returns("Home");
contextMock.SetupGet(c => c.SiteInstanceId).Returns("InstanceId");
contextMock.SetupGet(c => c.SiteName).Returns("Name");
var options = new AzureBlobLoggerOptions();
new BlobLoggerConfigureOptions(configuration, contextMock.Object).Configure(options);
Assert.Equal("http://container/url", options.ContainerUrl);
Assert.Equal("InstanceId", options.ApplicationInstanceId);
Assert.Equal("Name", options.ApplicationName);
}
}
}

View File

@ -0,0 +1,122 @@
// 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.IO;
using System.Linq;
using System.Threading.Tasks;
using Xunit;
namespace Microsoft.Extensions.Logging.AzureAppServices.Test
{
public class FileLoggerTests: IDisposable
{
DateTimeOffset _timestampOne = new DateTimeOffset(2016, 05, 04, 03, 02, 01, TimeSpan.Zero);
public FileLoggerTests()
{
TempPath = Path.GetTempFileName() + "_";
}
public string TempPath { get; }
public void Dispose()
{
try
{
if (Directory.Exists(TempPath))
{
Directory.Delete(TempPath, true);
}
}
catch
{
// ignored
}
}
[Fact]
public async Task WritesToTextFile()
{
var provider = new TestFileLoggerProvider(TempPath);
var logger = (BatchingLogger)provider.CreateLogger("Cat");
await provider.IntervalControl.Pause;
logger.Log(_timestampOne, LogLevel.Information, 0, "Info message", null, (state, ex) => state);
logger.Log(_timestampOne.AddHours(1), LogLevel.Error, 0, "Error message", null, (state, ex) => state);
provider.IntervalControl.Resume();
await provider.IntervalControl.Pause;
Assert.Equal(
"2016-05-04 03:02:01.000 +00:00 [Information] Cat: Info message" + Environment.NewLine +
"2016-05-04 04:02:01.000 +00:00 [Error] Cat: Error message" + Environment.NewLine,
File.ReadAllText(Path.Combine(TempPath, "LogFile.20160504.txt")));
}
[Fact]
public async Task RollsTextFile()
{
var provider = new TestFileLoggerProvider(TempPath);
var logger = (BatchingLogger)provider.CreateLogger("Cat");
await provider.IntervalControl.Pause;
logger.Log(_timestampOne, LogLevel.Information, 0, "Info message", null, (state, ex) => state);
logger.Log(_timestampOne.AddDays(1), LogLevel.Error, 0, "Error message", null, (state, ex) => state);
provider.IntervalControl.Resume();
await provider.IntervalControl.Pause;
Assert.Equal(
"2016-05-04 03:02:01.000 +00:00 [Information] Cat: Info message" + Environment.NewLine,
File.ReadAllText(Path.Combine(TempPath, "LogFile.20160504.txt")));
Assert.Equal(
"2016-05-05 03:02:01.000 +00:00 [Error] Cat: Error message" + Environment.NewLine,
File.ReadAllText(Path.Combine(TempPath, "LogFile.20160505.txt")));
}
[Fact]
public async Task RespectsMaxFileCount()
{
Directory.CreateDirectory(TempPath);
File.WriteAllText(Path.Combine(TempPath, "randomFile.txt"), "Text");
var provider = new TestFileLoggerProvider(TempPath, maxRetainedFiles: 5);
var logger = (BatchingLogger)provider.CreateLogger("Cat");
await provider.IntervalControl.Pause;
var timestamp = _timestampOne;
for (int i = 0; i < 10; i++)
{
logger.Log(timestamp, LogLevel.Information, 0, "Info message", null, (state, ex) => state);
logger.Log(timestamp.AddHours(1), LogLevel.Error, 0, "Error message", null, (state, ex) => state);
timestamp = timestamp.AddDays(1);
}
provider.IntervalControl.Resume();
await provider.IntervalControl.Pause;
var actualFiles = new DirectoryInfo(TempPath)
.GetFiles()
.Select(f => f.Name)
.OrderBy(f => f)
.ToArray();
Assert.Equal(6, actualFiles.Length);
Assert.Equal(new[] {
"LogFile.20160509.txt",
"LogFile.20160510.txt",
"LogFile.20160511.txt",
"LogFile.20160512.txt",
"LogFile.20160513.txt",
"randomFile.txt"
}, actualFiles);
}
}
}

View File

@ -0,0 +1,79 @@
// 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.Linq;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Moq;
using Xunit;
namespace Microsoft.Extensions.Logging.AzureAppServices.Test
{
public class LoggerBuilderExtensionsTests
{
private IWebAppContext _appContext;
public LoggerBuilderExtensionsTests()
{
var contextMock = new Mock<IWebAppContext>();
contextMock.SetupGet(c => c.IsRunningInAzureWebApp).Returns(true);
contextMock.SetupGet(c => c.HomeFolder).Returns(".");
_appContext = contextMock.Object;
}
[Fact]
public void BuilderExtensionAddsSingleSetOfServicesWhenCalledTwice()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddLogging(builder => builder.AddAzureWebAppDiagnostics(_appContext));
var count = serviceCollection.Count;
Assert.NotEqual(0, count);
serviceCollection.AddLogging(builder => builder.AddAzureWebAppDiagnostics(_appContext));
Assert.Equal(count, serviceCollection.Count);
}
[Fact]
public void BuilderExtensionAddsConfigurationChangeTokenSource()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddLogging(builder => builder.AddConfiguration(new ConfigurationBuilder().Build()));
// Tracking for main configuration
Assert.Equal(1, serviceCollection.Count(d => d.ServiceType == typeof(IOptionsChangeTokenSource<LoggerFilterOptions>)));
serviceCollection.AddLogging(builder => builder.AddAzureWebAppDiagnostics(_appContext));
// Make sure we add another config change token for azure diagnostic configuration
Assert.Equal(2, serviceCollection.Count(d => d.ServiceType == typeof(IOptionsChangeTokenSource<LoggerFilterOptions>)));
}
[Fact]
public void BuilderExtensionAddsIConfigureOptions()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddLogging(builder => builder.AddConfiguration(new ConfigurationBuilder().Build()));
// Tracking for main configuration
Assert.Equal(2, serviceCollection.Count(d => d.ServiceType == typeof(IConfigureOptions<LoggerFilterOptions>)));
serviceCollection.AddLogging(builder => builder.AddAzureWebAppDiagnostics(_appContext));
Assert.Equal(4, serviceCollection.Count(d => d.ServiceType == typeof(IConfigureOptions<LoggerFilterOptions>)));
}
[Fact]
public void LoggerProviderIsResolvable()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddLogging(builder => builder.AddAzureWebAppDiagnostics(_appContext));
var serviceProvider = serviceCollection.BuildServiceProvider();
var loggerFactory = serviceProvider.GetService<ILoggerProvider>();
}
}
}

View File

@ -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.Threading.Tasks;
namespace Microsoft.Extensions.Logging.AzureAppServices.Test
{
internal class ManualIntervalControl
{
private TaskCompletionSource<object> _pauseCompletionSource = new TaskCompletionSource<object>();
private TaskCompletionSource<object> _resumeCompletionSource;
public Task Pause => _pauseCompletionSource.Task;
public void Resume()
{
_pauseCompletionSource = new TaskCompletionSource<object>();
_resumeCompletionSource.SetResult(null);
}
public async Task IntervalAsync()
{
_resumeCompletionSource = new TaskCompletionSource<object>();
_pauseCompletionSource.SetResult(null);
await _resumeCompletionSource.Task;
}
}
}

View File

@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(DefaultNetCoreTargetFramework);net472</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.Extensions.Configuration.Abstractions" />
<Reference Include="Microsoft.Extensions.Configuration.Binder" />
<Reference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" />
<Reference Include="Microsoft.Extensions.Configuration.FileExtensions" />
<Reference Include="Microsoft.Extensions.Configuration.Json" />
<Reference Include="Microsoft.Extensions.Configuration" />
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
<Reference Include="Microsoft.Extensions.DependencyInjection" />
<Reference Include="Microsoft.Extensions.FileProviders.Abstractions" />
<Reference Include="Microsoft.Extensions.FileProviders.Physical" />
<Reference Include="Microsoft.Extensions.FileSystemGlobbing" />
<Reference Include="Microsoft.Extensions.Logging.Abstractions" />
<Reference Include="Microsoft.Extensions.Logging.AzureAppServices" />
<Reference Include="Microsoft.Extensions.Logging.Configuration" />
<Reference Include="Microsoft.Extensions.Logging" />
<Reference Include="Microsoft.Extensions.Options" />
<Reference Include="Microsoft.Extensions.Options.ConfigurationExtensions" />
<Reference Include="Microsoft.Extensions.Primitives" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,26 @@
// 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 Microsoft.Extensions.Options;
namespace Microsoft.Extensions.Logging.AzureAppServices.Test
{
internal class OptionsWrapperMonitor<T> : IOptionsMonitor<T>
{
public OptionsWrapperMonitor(T currentValue)
{
CurrentValue = currentValue;
}
public IDisposable OnChange(Action<T, string> listener)
{
return null;
}
public T Get(string name) => CurrentValue;
public T CurrentValue { get; }
}
}

View File

@ -0,0 +1,32 @@
// 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.Threading;
using System.Threading.Tasks;
namespace Microsoft.Extensions.Logging.AzureAppServices.Test
{
internal class TestBlobSink : BlobLoggerProvider
{
internal ManualIntervalControl IntervalControl { get; } = new ManualIntervalControl();
public TestBlobSink(Func<string, ICloudAppendBlob> blobReferenceFactory) : base(
new OptionsWrapperMonitor<AzureBlobLoggerOptions>(new AzureBlobLoggerOptions()
{
ApplicationInstanceId = "42",
ApplicationName = "appname",
BlobName = "filename",
IsEnabled = true
}),
blobReferenceFactory)
{
}
protected override Task IntervalAsync(TimeSpan interval, CancellationToken cancellationToken)
{
return IntervalControl.IntervalAsync();
}
}
}

View File

@ -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;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.Extensions.Logging.AzureAppServices.Test
{
internal class TestFileLoggerProvider : FileLoggerProvider
{
internal ManualIntervalControl IntervalControl { get; } = new ManualIntervalControl();
public TestFileLoggerProvider(
string path,
string fileName = "LogFile.",
int maxFileSize = 32_000,
int maxRetainedFiles = 100)
: base(new OptionsWrapperMonitor<AzureFileLoggerOptions>(new AzureFileLoggerOptions()
{
LogDirectory = path,
FileName = fileName,
FileSizeLimit = maxFileSize,
RetainedFileCountLimit = maxRetainedFiles,
IsEnabled = true
}))
{
}
protected override Task IntervalAsync(TimeSpan interval, CancellationToken cancellationToken)
{
return IntervalControl.IntervalAsync();
}
}
}

View File

@ -0,0 +1,40 @@
// 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;
using Microsoft.Extensions.Configuration;
using Xunit;
namespace Microsoft.Extensions.Logging.AzureAppServices.Test
{
public class WebConfigurationLevelSwitchTests
{
[Theory]
[InlineData("Error", LogLevel.Error)]
[InlineData("Warning", LogLevel.Warning)]
[InlineData("Information", LogLevel.Information)]
[InlineData("Verbose", LogLevel.Trace)]
[InlineData("ABCD", LogLevel.None)]
public void AddsRuleWithCorrectLevel(string levelValue, LogLevel expectedLevel)
{
var configuration = new ConfigurationBuilder().AddInMemoryCollection(
new[]
{
new KeyValuePair<string, string>("levelKey", levelValue),
})
.Build();
var levelSwitcher = new ConfigurationBasedLevelSwitcher(configuration, typeof(TestFileLoggerProvider), "levelKey");
var filterConfiguration = new LoggerFilterOptions();
levelSwitcher.Configure(filterConfiguration);
Assert.Equal(1, filterConfiguration.Rules.Count);
var rule = filterConfiguration.Rules[0];
Assert.Equal(typeof(TestFileLoggerProvider).FullName, rule.ProviderName);
Assert.Equal(expectedLevel, rule.LogLevel);
}
}
}

View File

@ -9,8 +9,6 @@
<Reference Include="Microsoft.AspNetCore.Server.IISIntegration" />
<Reference Include="Microsoft.AspNetCore.Server.Kestrel" />
<Reference Include="Microsoft.Extensions.Caching.Memory" />
<Reference Include="Microsoft.Extensions.Caching.StackExchangeRedis" />
<Reference Include="Microsoft.Extensions.Caching.SqlServer" />
<Reference Include="Microsoft.Extensions.Configuration.UserSecrets" />
<Reference Include="Microsoft.Extensions.Logging.Console" />
</ItemGroup>

View File

@ -21,7 +21,8 @@ namespace SessionSample
// Uncomment the following line to use the in-memory implementation of IDistributedCache
services.AddDistributedMemoryCache();
// Uncomment the following line to use the Microsoft SQL Server implementation of IDistributedCache.
// Uncomment the following line to use the Microsoft SQL Server implementation of IDistributedCache
// and add a PackageReference to Microsoft.Extensions.Caching.SqlServer in the .csrpoj.
// Note that this would require setting up the session state database.
//services.AddDistributedSqlServerCache(o =>
//{
@ -30,7 +31,8 @@ namespace SessionSample
// o.TableName = "Sessions";
//});
// Uncomment the following line to use the Redis implementation of IDistributedCache.
// Uncomment the following line to use the Redis implementation of IDistributedCache
// and add a PackageReference to Microsoft.Extensions.Caching.StackExchangeRedis in the .csrpoj.
// This will override any previously registered IDistributedCache service.
//services.AddStackExchangeRedisCache(o =>
//{

View File

@ -81,7 +81,7 @@ namespace Templates.Test
}
}
[ConditionalTheory]
[ConditionalTheory(Skip = "See: https://github.com/dotnet/aspnetcore/issues/20520")]
[InlineData(true)]
[InlineData(false)]
[SkipOnHelix("ef restore no worky")]

View File

@ -25,7 +25,7 @@ namespace Templates.Test
public ProjectFactoryFixture ProjectFactory { get; }
public ITestOutputHelper Output { get; }
[ConditionalFact(Skip = "This test run for over an hour")]
[ConditionalFact]
[SkipOnHelix("Not supported queues", Queues = "Windows.7.Amd64;Windows.7.Amd64.Open;OSX.1014.Amd64;OSX.1014.Amd64.Open")]
[QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/19716")]
public async Task GrpcTemplate()

View File

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
@ -30,6 +31,9 @@ namespace Templates.Test.Helpers
private readonly HttpClient _httpClient;
private readonly ITestOutputHelper _output;
private string _certificatePath;
private string _certificatePassword = Guid.NewGuid().ToString();
internal readonly Uri ListeningUri;
internal ProcessEx Process { get; }
@ -48,12 +52,14 @@ namespace Templates.Test.Helpers
AllowAutoRedirect = true,
UseCookies = true,
CookieContainer = new CookieContainer(),
ServerCertificateCustomValidationCallback = (m, c, ch, p) => true,
ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator,
})
{
Timeout = TimeSpan.FromMinutes(2)
};
_certificatePath = Path.Combine(workingDirectory, $"{Guid.NewGuid()}.pfx");
EnsureDevelopmentCertificates();
output.WriteLine("Running ASP.NET application...");
@ -62,7 +68,13 @@ namespace Templates.Test.Helpers
logger?.LogInformation($"AspNetProcess - process: {DotNetMuxer.MuxerPathOrDefault()} arguments: {arguments}");
Process = ProcessEx.Run(output, workingDirectory, DotNetMuxer.MuxerPathOrDefault(), arguments, envVars: environmentVariables);
var finalEnvironmentVariables = new Dictionary<string, string>(environmentVariables)
{
["ASPNETCORE_KESTREL__CERTIFICATES__DEFAULT__PATH"] = _certificatePath,
["ASPNETCORE_KESTREL__CERTIFICATES__DEFAULT__PASSWORD"] = _certificatePassword
};
Process = ProcessEx.Run(output, workingDirectory, DotNetMuxer.MuxerPathOrDefault(), arguments, envVars: finalEnvironmentVariables);
logger?.LogInformation("AspNetProcess - process started");
@ -74,10 +86,12 @@ namespace Templates.Test.Helpers
}
}
internal static void EnsureDevelopmentCertificates()
internal void EnsureDevelopmentCertificates()
{
var now = DateTimeOffset.Now;
new CertificateManager().EnsureAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1));
var manager = new CertificateManager();
var certificate = manager.CreateAspNetCoreHttpsDevelopmentCertificate(now, now.AddYears(1), "CN=localhost");
manager.ExportCertificate(certificate, path: _certificatePath, includePrivateKey: true, _certificatePassword);
}
public void VisitInBrowser(IWebDriver driver)

View File

@ -118,7 +118,7 @@ namespace Templates.Test
"Identity/lib/jquery-validation-unobtrusive/LICENSE.txt",
};
[ConditionalTheory(Skip = "This test run for over an hour")]
[ConditionalTheory]
[MemberData(nameof(MSBuildIdentityUIPackageOptions))]
[SkipOnHelix("cert failure", Queues = "OSX.1014.Amd64;OSX.1014.Amd64.Open")]
[QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/19716")]

View File

@ -104,7 +104,7 @@ namespace Templates.Test
}
}
[ConditionalTheory(Skip = "This test run for over an hour")]
[ConditionalTheory]
[InlineData(true)]
[InlineData(false)]
[SkipOnHelix("cert failure", Queues = "OSX.1014.Amd64;OSX.1014.Amd64.Open")]

View File

@ -94,7 +94,7 @@ namespace Templates.Test
}
}
[ConditionalTheory(Skip = "This test run for over an hour")]
[ConditionalTheory]
[InlineData(false)]
[InlineData(true)]
[SkipOnHelix("cert failure", Queues = "OSX.1014.Amd64;OSX.1014.Amd64.Open")]

View File

@ -23,7 +23,7 @@ namespace Templates.Test.SpaTemplateTest
=> SpaTemplateImplAsync("reactnoauth", "react", useLocalDb: false, usesAuth: false);
[QuarantinedTest]
[ConditionalFact(Skip="This test run for over an hour")]
[ConditionalFact]
[SkipOnHelix("selenium")]
public Task ReactTemplate_IndividualAuth_NetCore()
=> SpaTemplateImplAsync("reactindividual", "react", useLocalDb: false, usesAuth: true);

View File

@ -21,7 +21,7 @@ namespace Templates.Test
public ProjectFactoryFixture ProjectFactory { get; }
public ITestOutputHelper Output { get; }
[Fact(Skip = "This test run for over an hour")]
[Fact]
[QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/19716")]
public async Task WorkerTemplateAsync()
{

View File

@ -89,7 +89,7 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests
}
catch (Exception ex)
{
Logger.LogError($"Certificate is invalid. Issuer name: {cert.Issuer}");
Logger.LogError($"Certificate is invalid. Issuer name: {cert?.Issuer}");
using (var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine))
{
Logger.LogError($"List of current certificates in root store:");

View File

@ -7,8 +7,8 @@
<ItemGroup>
<ProjectReference Include="$(RepoRoot)src\Servers\IIS\IntegrationTesting.IIS\src\Microsoft.AspNetCore.Server.IntegrationTesting.IIS.csproj" />
<Reference Include="Microsoft.AspNetCore.Hosting" />
<Reference Include="Microsoft.AspNetCore.Testing" />
<Reference Include="Microsoft.Extensions.Logging" />
<Reference Include="Microsoft.Extensions.Logging.Testing" />
<Reference Include="System.Diagnostics.EventLog" />
</ItemGroup>

View File

@ -7,6 +7,7 @@
<AssemblyName>InProcessWebSite</AssemblyName>
<TestAssetOutputName>InProcessNewShimWebSite</TestAssetOutputName>
<DefineConstants>FORWARDCOMPAT</DefineConstants>
<CompileUsingReferenceAssemblies>false</CompileUsingReferenceAssemblies>
</PropertyGroup>
<ItemGroup Condition="'$(OS)' == 'Windows_NT'">

View File

@ -547,6 +547,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{
try
{
// We run the request processing loop in a seperate async method so per connection
// exception handling doesn't complicate the generated asm for the loop.
await ProcessRequests(application);
}
catch (BadHttpRequestException ex)
@ -624,91 +626,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
InitializeBodyControl(messageBody);
var context = application.CreateContext(this);
try
{
KestrelEventSource.Log.RequestStart(this);
// Run the application code for this request
await application.ProcessRequestAsync(context);
// Trigger OnStarting if it hasn't been called yet and the app hasn't
// already failed. If an OnStarting callback throws we can go through
// our normal error handling in ProduceEnd.
// https://github.com/aspnet/KestrelHttpServer/issues/43
if (!HasResponseStarted && _applicationException == null && _onStarting?.Count > 0)
{
await FireOnStarting();
}
if (!_connectionAborted && !VerifyResponseContentLength(out var lengthException))
{
ReportApplicationError(lengthException);
}
}
catch (BadHttpRequestException ex)
{
// Capture BadHttpRequestException for further processing
// This has to be caught here so StatusCode is set properly before disposing the HttpContext
// (DisposeContext logs StatusCode).
SetBadRequestState(ex);
ReportApplicationError(ex);
}
catch (Exception ex)
{
ReportApplicationError(ex);
}
KestrelEventSource.Log.RequestStop(this);
// At this point all user code that needs use to the request or response streams has completed.
// Using these streams in the OnCompleted callback is not allowed.
try
{
await _bodyControl.StopAsync();
}
catch (Exception ex)
{
// BodyControl.StopAsync() can throw if the PipeWriter was completed prior to the application writing
// enough bytes to satisfy the specified Content-Length. This risks double-logging the exception,
// but this scenario generally indicates an app bug, so I don't want to risk not logging it.
ReportApplicationError(ex);
}
// 4XX responses are written by TryProduceInvalidRequestResponse during connection tear down.
if (_requestRejectedException == null)
{
if (!_connectionAborted)
{
// Call ProduceEnd() before consuming the rest of the request body to prevent
// delaying clients waiting for the chunk terminator:
//
// https://github.com/dotnet/corefx/issues/17330#issuecomment-288248663
//
// This also prevents the 100 Continue response from being sent if the app
// never tried to read the body.
// https://github.com/aspnet/KestrelHttpServer/issues/2102
//
// ProduceEnd() must be called before _application.DisposeContext(), to ensure
// HttpContext.Response.StatusCode is correctly set when
// IHttpContextFactory.Dispose(HttpContext) is called.
await ProduceEnd();
}
else if (!HasResponseStarted)
{
// If the request was aborted and no response was sent, there's no
// meaningful status code to log.
StatusCode = 0;
}
}
if (_onCompleted?.Count > 0)
{
await FireOnCompleted();
}
application.DisposeContext(context, _applicationException);
// We run user controlled request processing in a seperate async method
// so any changes made to ExecutionContext are undone when it returns and
// each request starts with a fresh ExecutionContext state.
await ProcessRequest(application);
// Even for non-keep-alive requests, try to consume the entire body to avoid RSTs.
if (!_connectionAborted && _requestRejectedException == null && !messageBody.IsEmpty)
@ -723,6 +644,95 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
}
}
private async ValueTask ProcessRequest<TContext>(IHttpApplication<TContext> application)
{
var context = application.CreateContext(this);
try
{
KestrelEventSource.Log.RequestStart(this);
// Run the application code for this request
await application.ProcessRequestAsync(context);
// Trigger OnStarting if it hasn't been called yet and the app hasn't
// already failed. If an OnStarting callback throws we can go through
// our normal error handling in ProduceEnd.
// https://github.com/aspnet/KestrelHttpServer/issues/43
if (!HasResponseStarted && _applicationException == null && _onStarting?.Count > 0)
{
await FireOnStarting();
}
if (!_connectionAborted && !VerifyResponseContentLength(out var lengthException))
{
ReportApplicationError(lengthException);
}
}
catch (BadHttpRequestException ex)
{
// Capture BadHttpRequestException for further processing
// This has to be caught here so StatusCode is set properly before disposing the HttpContext
// (DisposeContext logs StatusCode).
SetBadRequestState(ex);
ReportApplicationError(ex);
}
catch (Exception ex)
{
ReportApplicationError(ex);
}
KestrelEventSource.Log.RequestStop(this);
// At this point all user code that needs use to the request or response streams has completed.
// Using these streams in the OnCompleted callback is not allowed.
try
{
await _bodyControl.StopAsync();
}
catch (Exception ex)
{
// BodyControl.StopAsync() can throw if the PipeWriter was completed prior to the application writing
// enough bytes to satisfy the specified Content-Length. This risks double-logging the exception,
// but this scenario generally indicates an app bug, so I don't want to risk not logging it.
ReportApplicationError(ex);
}
// 4XX responses are written by TryProduceInvalidRequestResponse during connection tear down.
if (_requestRejectedException == null)
{
if (!_connectionAborted)
{
// Call ProduceEnd() before consuming the rest of the request body to prevent
// delaying clients waiting for the chunk terminator:
//
// https://github.com/dotnet/corefx/issues/17330#issuecomment-288248663
//
// This also prevents the 100 Continue response from being sent if the app
// never tried to read the body.
// https://github.com/aspnet/KestrelHttpServer/issues/2102
//
// ProduceEnd() must be called before _application.DisposeContext(), to ensure
// HttpContext.Response.StatusCode is correctly set when
// IHttpContextFactory.Dispose(HttpContext) is called.
await ProduceEnd();
}
else if (!HasResponseStarted)
{
// If the request was aborted and no response was sent, there's no
// meaningful status code to log.
StatusCode = 0;
}
}
if (_onCompleted?.Count > 0)
{
await FireOnCompleted();
}
application.DisposeContext(context, _applicationException);
}
public void OnStarting(Func<object, Task> callback, object state)
{
if (HasResponseStarted)
@ -749,108 +759,55 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
protected Task FireOnStarting()
{
var onStarting = _onStarting;
if (onStarting == null || onStarting.Count == 0)
if (onStarting?.Count > 0)
{
return Task.CompletedTask;
}
else
{
return FireOnStartingMayAwait(onStarting);
}
}
private Task FireOnStartingMayAwait(Stack<KeyValuePair<Func<object, Task>, object>> onStarting)
{
try
{
while (onStarting.TryPop(out var entry))
{
var task = entry.Key.Invoke(entry.Value);
if (!task.IsCompletedSuccessfully)
{
return FireOnStartingAwaited(task, onStarting);
}
}
}
catch (Exception ex)
{
ReportApplicationError(ex);
return ProcessEvents(this, onStarting);
}
return Task.CompletedTask;
}
private async Task FireOnStartingAwaited(Task currentTask, Stack<KeyValuePair<Func<object, Task>, object>> onStarting)
{
try
static async Task ProcessEvents(HttpProtocol protocol, Stack<KeyValuePair<Func<object, Task>, object>> events)
{
await currentTask;
while (onStarting.TryPop(out var entry))
// Try/Catch is outside the loop as any error that occurs is before the request starts.
// So we want to report it as an ApplicationError to fail the request and not process more events.
try
{
await entry.Key.Invoke(entry.Value);
while (events.TryPop(out var entry))
{
await entry.Key.Invoke(entry.Value);
}
}
catch (Exception ex)
{
protocol.ReportApplicationError(ex);
}
}
catch (Exception ex)
{
ReportApplicationError(ex);
}
}
protected Task FireOnCompleted()
{
var onCompleted = _onCompleted;
if (onCompleted == null || onCompleted.Count == 0)
if (onCompleted?.Count > 0)
{
return Task.CompletedTask;
}
return FireOnCompletedMayAwait(onCompleted);
}
private Task FireOnCompletedMayAwait(Stack<KeyValuePair<Func<object, Task>, object>> onCompleted)
{
while (onCompleted.TryPop(out var entry))
{
try
{
var task = entry.Key.Invoke(entry.Value);
if (!task.IsCompletedSuccessfully)
{
return FireOnCompletedAwaited(task, onCompleted);
}
}
catch (Exception ex)
{
ReportApplicationError(ex);
}
return ProcessEvents(this, onCompleted);
}
return Task.CompletedTask;
}
private async Task FireOnCompletedAwaited(Task currentTask, Stack<KeyValuePair<Func<object, Task>, object>> onCompleted)
{
try
static async Task ProcessEvents(HttpProtocol protocol, Stack<KeyValuePair<Func<object, Task>, object>> events)
{
await currentTask;
}
catch (Exception ex)
{
Log.ApplicationError(ConnectionId, TraceIdentifier, ex);
}
while (onCompleted.TryPop(out var entry))
{
try
// Try/Catch is inside the loop as any error that occurs is after the request has finished.
// So we will just log it and keep processing the events, as the completion has already happened.
while (events.TryPop(out var entry))
{
await entry.Key.Invoke(entry.Value);
}
catch (Exception ex)
{
Log.ApplicationError(ConnectionId, TraceIdentifier, ex);
try
{
await entry.Key.Invoke(entry.Value);
}
catch (Exception ex)
{
protocol.Log.ApplicationError(protocol.ConnectionId, protocol.TraceIdentifier, ex);
}
}
}
}

View File

@ -41,10 +41,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
private bool _suffixSent;
private bool _streamEnded;
private bool _writerComplete;
private bool _disposed;
// Internal for testing
internal ValueTask _dataWriteProcessingTask;
internal bool _disposed;
/// <summary>The core logic for the IValueTaskSource implementation.</summary>
private ManualResetValueTaskSourceCore<FlushResult> _responseCompleteTaskSource = new ManualResetValueTaskSourceCore<FlushResult> { RunContinuationsAsynchronously = true }; // mutable struct, do not make this readonly

View File

@ -37,6 +37,21 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
return true;
}
public bool TryPeek(out Http2Stream result)
{
int size = _size - 1;
Http2StreamAsValueType[] array = _array;
if ((uint)size >= (uint)array.Length)
{
result = default;
return false;
}
result = array[size];
return true;
}
// Pushes an item to the top of the stack.
public void Push(Http2Stream item)
{

View File

@ -20,7 +20,5 @@
<ItemGroup Condition=" '$(IsTestProject)' == 'true' ">
<None Include="$(MSBuildThisFileDirectory)xunit.runner.json" Link="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" />
<Reference Include="Microsoft.Extensions.Logging.Testing" />
</ItemGroup>
</Project>

View File

@ -26,6 +26,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
}
}
[Benchmark(OperationsPerInvoke = RequestParsingData.InnerLoopCount)]
public void JsonTechEmpower()
{
for (var i = 0; i < RequestParsingData.InnerLoopCount; i++)
{
InsertData(RequestParsingData.JsonTechEmpowerRequest);
ParseData();
}
}
[Benchmark(OperationsPerInvoke = RequestParsingData.InnerLoopCount)]
public void LiveAspNet()
{

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Linq;
@ -19,6 +19,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
"Connection: keep-alive\r\n" +
"\r\n";
private const string _jsonTechEmpowerRequest =
"GET /json HTTP/1.1\r\n" +
"Host: localhost\r\n" +
"Accept: Accept:application/json,text/html;q=0.9,application/xhtml+xml;q=0.9,application/xml;q=0.8,*/*;q=0.7\r\n" +
"Connection: keep-alive\r\n" +
"\r\n";
// edge-casey - client's don't normally send this
private const string _plaintextAbsoluteUriRequest =
"GET http://localhost/plaintext HTTP/1.1\r\n" +
@ -59,6 +66,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
public static readonly byte[] PlaintextTechEmpowerPipelinedRequests = Encoding.ASCII.GetBytes(string.Concat(Enumerable.Repeat(_plaintextTechEmpowerRequest, Pipelining)));
public static readonly byte[] PlaintextTechEmpowerRequest = Encoding.ASCII.GetBytes(_plaintextTechEmpowerRequest);
public static readonly byte[] JsonTechEmpowerRequest = Encoding.ASCII.GetBytes(_jsonTechEmpowerRequest);
public static readonly byte[] PlaintextAbsoluteUriRequest = Encoding.ASCII.GetBytes(_plaintextAbsoluteUriRequest);
public static readonly byte[] LiveaspnetPipelinedRequests = Encoding.ASCII.GetBytes(string.Concat(Enumerable.Repeat(_liveaspnetRequest, Pipelining)));

View File

@ -414,6 +414,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
appDelegateTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
await StartStreamAsync(1, _browserRequestHeaders, endStream: true);
// Get the in progress stream
var stream = _connection._streams[1];
appDelegateTcs.TrySetResult(null);
await ExpectAsync(Http2FrameType.HEADERS,
@ -421,15 +424,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 1);
// Ping will trigger the stream to be returned to the pool so we can assert it
await SendPingAsync(Http2PingFrameFlags.NONE);
await ExpectAsync(Http2FrameType.PING,
withLength: 8,
withFlags: (byte)Http2PingFrameFlags.ACK,
withStreamId: 0);
// Stream has been returned to the pool
Assert.Equal(1, _connection.StreamPool.Count);
await PingUntilStreamPooled(expectedCount: 1).DefaultTimeout();
Assert.True(_connection.StreamPool.TryPeek(out var pooledStream));
Assert.Equal(stream, pooledStream);
appDelegateTcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);
await StartStreamAsync(3, _browserRequestHeaders, endStream: true);
@ -444,17 +442,25 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
withStreamId: 3);
// Ping will trigger the stream to be returned to the pool so we can assert it
await SendPingAsync(Http2PingFrameFlags.NONE);
await ExpectAsync(Http2FrameType.PING,
withLength: 8,
withFlags: (byte)Http2PingFrameFlags.ACK,
withStreamId: 0);
// Stream was reused and returned to the pool
Assert.Equal(1, _connection.StreamPool.Count);
await PingUntilStreamPooled(expectedCount: 1).DefaultTimeout();
Assert.True(_connection.StreamPool.TryPeek(out pooledStream));
Assert.Equal(stream, pooledStream);
await StopConnectionAsync(expectedLastStreamId: 3, ignoreNonGoAwayFrames: false);
async Task PingUntilStreamPooled(int expectedCount)
{
do
{
// Ping will trigger the stream to be returned to the pool so we can assert it
await SendPingAsync(Http2PingFrameFlags.NONE);
await ExpectAsync(Http2FrameType.PING,
withLength: 8,
withFlags: (byte)Http2PingFrameFlags.ACK,
withStreamId: 0);
} while (_connection.StreamPool.Count != expectedCount);
}
}
[Fact]
@ -486,12 +492,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
withStreamId: 1);
await WaitForStreamErrorAsync(1, Http2ErrorCode.INTERNAL_ERROR, null);
// Ping will trigger the stream to be returned to the pool so we can assert it
await SendPingAsync(Http2PingFrameFlags.NONE);
await ExpectAsync(Http2FrameType.PING,
withLength: 8,
withFlags: (byte)Http2PingFrameFlags.ACK,
withStreamId: 0);
await PingUntilStreamDisposed(stream).DefaultTimeout();
// Stream is not returned to the pool
Assert.Equal(0, _connection.StreamPool.Count);
@ -500,6 +501,21 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
await output._dataWriteProcessingTask.DefaultTimeout();
await StopConnectionAsync(expectedLastStreamId: 1, ignoreNonGoAwayFrames: false);
async Task PingUntilStreamDisposed(Http2Stream stream)
{
var output = (Http2OutputProducer)stream.Output;
do
{
// Ping will trigger the stream to be returned to the pool so we can assert it
await SendPingAsync(Http2PingFrameFlags.NONE);
await ExpectAsync(Http2FrameType.PING,
withLength: 8,
withFlags: (byte)Http2PingFrameFlags.ACK,
withStreamId: 0);
} while (!output._disposed);
}
}
[Fact]

View File

@ -285,6 +285,264 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
}
}
[Fact]
public async Task ExecutionContextMutationsOfValueTypeDoNotLeakAcrossRequestsOnSameConnection()
{
var local = new AsyncLocal<int>();
// It's important this method isn't async as that will revert the ExecutionContext
Task ExecuteApplication(HttpContext context)
{
var value = local.Value;
Assert.Equal(0, value);
context.Response.OnStarting(() =>
{
local.Value++;
return Task.CompletedTask;
});
context.Response.OnCompleted(() =>
{
local.Value++;
return Task.CompletedTask;
});
local.Value++;
context.Response.ContentLength = 1;
return context.Response.WriteAsync($"{value}");
}
var testContext = new TestServiceContext(LoggerFactory);
await using var server = new TestServer(ExecuteApplication, testContext);
await TestAsyncLocalValues(testContext, server);
}
[Fact]
public async Task ExecutionContextMutationsOfReferenceTypeDoNotLeakAcrossRequestsOnSameConnection()
{
var local = new AsyncLocal<IntAsClass>();
// It's important this method isn't async as that will revert the ExecutionContext
Task ExecuteApplication(HttpContext context)
{
Assert.Null(local.Value);
local.Value = new IntAsClass();
var value = local.Value.Value;
Assert.Equal(0, value);
context.Response.OnStarting(() =>
{
local.Value.Value++;
return Task.CompletedTask;
});
context.Response.OnCompleted(() =>
{
local.Value.Value++;
return Task.CompletedTask;
});
local.Value.Value++;
context.Response.ContentLength = 1;
return context.Response.WriteAsync($"{value}");
}
var testContext = new TestServiceContext(LoggerFactory);
await using var server = new TestServer(ExecuteApplication, testContext);
await TestAsyncLocalValues(testContext, server);
}
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
[Fact]
public async Task ExecutionContextMutationsDoNotLeakAcrossAwaits()
{
var local = new AsyncLocal<int>();
// It's important this method isn't async as that will revert the ExecutionContext
Task ExecuteApplication(HttpContext context)
{
var value = local.Value;
Assert.Equal(0, value);
context.Response.OnStarting(async () =>
{
local.Value++;
Assert.Equal(1, local.Value);
});
context.Response.OnCompleted(async () =>
{
local.Value++;
Assert.Equal(1, local.Value);
});
context.Response.ContentLength = 1;
return context.Response.WriteAsync($"{value}");
}
var testContext = new TestServiceContext(LoggerFactory);
await using var server = new TestServer(ExecuteApplication, testContext);
await TestAsyncLocalValues(testContext, server);
}
[Fact]
public async Task ExecutionContextMutationsOfValueTypeFlowIntoButNotOutOfAsyncEvents()
{
var local = new AsyncLocal<int>();
async Task ExecuteApplication(HttpContext context)
{
var value = local.Value;
Assert.Equal(0, value);
context.Response.OnStarting(async () =>
{
local.Value++;
Assert.Equal(2, local.Value);
});
context.Response.OnCompleted(async () =>
{
local.Value++;
Assert.Equal(2, local.Value);
});
local.Value++;
Assert.Equal(1, local.Value);
context.Response.ContentLength = 1;
await context.Response.WriteAsync($"{value}");
local.Value++;
Assert.Equal(2, local.Value);
}
var testContext = new TestServiceContext(LoggerFactory);
await using var server = new TestServer(ExecuteApplication, testContext);
await TestAsyncLocalValues(testContext, server);
}
[Fact]
public async Task ExecutionContextMutationsOfReferenceTypeFlowThroughAsyncEvents()
{
var local = new AsyncLocal<IntAsClass>();
async Task ExecuteApplication(HttpContext context)
{
Assert.Null(local.Value);
local.Value = new IntAsClass();
var value = local.Value.Value;
Assert.Equal(0, value); // Start
context.Response.OnStarting(async () =>
{
local.Value.Value++;
Assert.Equal(2, local.Value.Value); // Second
});
context.Response.OnCompleted(async () =>
{
local.Value.Value++;
Assert.Equal(4, local.Value.Value); // Fourth
});
local.Value.Value++;
Assert.Equal(1, local.Value.Value); // First
context.Response.ContentLength = 1;
await context.Response.WriteAsync($"{value}");
local.Value.Value++;
Assert.Equal(3, local.Value.Value); // Third
}
var testContext = new TestServiceContext(LoggerFactory);
await using var server = new TestServer(ExecuteApplication, testContext);
await TestAsyncLocalValues(testContext, server);
}
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
[Fact]
public async Task ExecutionContextMutationsOfValueTypeFlowIntoButNotOutOfNonAsyncEvents()
{
var local = new AsyncLocal<int>();
async Task ExecuteApplication(HttpContext context)
{
var value = local.Value;
Assert.Equal(0, value);
context.Response.OnStarting(() =>
{
local.Value++;
Assert.Equal(2, local.Value);
return Task.CompletedTask;
});
context.Response.OnCompleted(() =>
{
local.Value++;
Assert.Equal(2, local.Value);
return Task.CompletedTask;
});
local.Value++;
Assert.Equal(1, local.Value);
context.Response.ContentLength = 1;
await context.Response.WriteAsync($"{value}");
local.Value++;
Assert.Equal(2, local.Value);
}
var testContext = new TestServiceContext(LoggerFactory);
await using var server = new TestServer(ExecuteApplication, testContext);
await TestAsyncLocalValues(testContext, server);
}
private static async Task TestAsyncLocalValues(TestServiceContext testContext, TestServer server)
{
using var connection = server.CreateConnection();
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"");
await connection.Receive(
"HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Content-Length: 1",
"",
"0");
await connection.Send(
"GET / HTTP/1.1",
"Host:",
"",
"");
await connection.Receive(
"HTTP/1.1 200 OK",
$"Date: {testContext.DateHeaderValue}",
"Content-Length: 1",
"",
"0");
}
[Fact]
public async Task AppCanSetTraceIdentifier()
{
@ -1803,5 +2061,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
}
public static TheoryData<string, string> HostHeaderData => HttpParsingData.HostHeaderData;
private class IntAsClass
{
public int Value;
}
}
}

View File

@ -82,10 +82,10 @@ namespace Microsoft.Extensions.Hosting.Tests
[Fact]
public void CreateHostBuilderPattern_CanFindHostBuilder()
{
var factory = HostFactoryResolver.ResolveHostBuilderFactory<IHostBuilder>(typeof(CreateHostBuilderPatternTestSite.Program).Assembly);
var factory = HostFactoryResolver.ResolveHostBuilderFactory<MockHostTypes.IHostBuilder>(typeof(CreateHostBuilderPatternTestSite.Program).Assembly);
Assert.NotNull(factory);
Assert.IsAssignableFrom<IHostBuilder>(factory(Array.Empty<string>()));
Assert.IsAssignableFrom<MockHostTypes.IHostBuilder>(factory(Array.Empty<string>()));
}
[Fact]
@ -100,7 +100,7 @@ namespace Microsoft.Extensions.Hosting.Tests
[Fact]
public void CreateHostBuilderPattern__Invalid_CantFindHostBuilder()
{
var factory = HostFactoryResolver.ResolveHostBuilderFactory<IHostBuilder>(typeof(CreateHostBuilderInvalidSignature.Program).Assembly);
var factory = HostFactoryResolver.ResolveHostBuilderFactory<MockHostTypes.IHostBuilder>(typeof(CreateHostBuilderInvalidSignature.Program).Assembly);
Assert.Null(factory);
}

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(DefaultNetCoreTargetFramework)</TargetFrameworks>

View File

@ -21,7 +21,6 @@
<Content Include="$(MSBuildThisFileDirectory)xunit.runner.json" Link="xunit.runner.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Reference Include="Microsoft.Extensions.Logging.Testing" />
</ItemGroup>
</Project>

View File

@ -235,7 +235,7 @@ namespace Microsoft.AspNetCore.SignalR.Client
private async Task StartAsyncInner(CancellationToken cancellationToken = default)
{
await _state.WaitConnectionLockAsync();
await _state.WaitConnectionLockAsync(token: cancellationToken);
try
{
if (!_state.TryChangeState(HubConnectionState.Disconnected, HubConnectionState.Connecting))
@ -465,7 +465,7 @@ namespace Microsoft.AspNetCore.SignalR.Client
// Potentially wait for StartAsync to finish, and block a new StartAsync from
// starting until we've finished stopping.
await _state.WaitConnectionLockAsync();
await _state.WaitConnectionLockAsync(token: default);
// Ensure that ReconnectingState.ReconnectTask is not accessed outside of the lock.
var reconnectTask = _state.ReconnectTask;
@ -478,7 +478,7 @@ namespace Microsoft.AspNetCore.SignalR.Client
// The StopCts should prevent the HubConnection from restarting until it is reset.
_state.ReleaseConnectionLock();
await reconnectTask;
await _state.WaitConnectionLockAsync();
await _state.WaitConnectionLockAsync(token: default);
}
ConnectionState connectionState;
@ -574,7 +574,7 @@ namespace Microsoft.AspNetCore.SignalR.Client
async Task OnStreamCanceled(InvocationRequest irq)
{
// We need to take the connection lock in order to ensure we a) have a connection and b) are the only one accessing the write end of the pipe.
await _state.WaitConnectionLockAsync();
await _state.WaitConnectionLockAsync(token: default);
try
{
if (_state.CurrentConnectionStateUnsynchronized != null)
@ -601,7 +601,7 @@ namespace Microsoft.AspNetCore.SignalR.Client
var readers = default(Dictionary<string, object>);
CheckDisposed();
var connectionState = await _state.WaitForActiveConnectionAsync(nameof(StreamAsChannelCoreAsync));
var connectionState = await _state.WaitForActiveConnectionAsync(nameof(StreamAsChannelCoreAsync), token: cancellationToken);
ChannelReader<object> channel;
try
@ -704,7 +704,7 @@ namespace Microsoft.AspNetCore.SignalR.Client
{
while (!tokenSource.Token.IsCancellationRequested && reader.TryRead(out var item))
{
await SendWithLock(connectionState, new StreamItemMessage(streamId, item));
await SendWithLock(connectionState, new StreamItemMessage(streamId, item), tokenSource.Token);
Log.SendingStreamItem(_logger, streamId);
}
}
@ -722,7 +722,7 @@ namespace Microsoft.AspNetCore.SignalR.Client
await foreach (var streamValue in streamValues)
{
await SendWithLock(connectionState, new StreamItemMessage(streamId, streamValue));
await SendWithLock(connectionState, new StreamItemMessage(streamId, streamValue), tokenSource.Token);
Log.SendingStreamItem(_logger, streamId);
}
}
@ -750,7 +750,9 @@ namespace Microsoft.AspNetCore.SignalR.Client
Log.CompletingStream(_logger, streamId);
await SendWithLock(connectionState, CompletionMessage.WithError(streamId, responseError), cts.Token);
// Don't use cancellation token here
// this is triggered by a cancellation token to tell the server that the client is done streaming
await SendWithLock(connectionState, CompletionMessage.WithError(streamId, responseError), cancellationToken: default);
}
private async Task<object> InvokeCoreAsyncCore(string methodName, Type returnType, object[] args, CancellationToken cancellationToken)
@ -758,7 +760,7 @@ namespace Microsoft.AspNetCore.SignalR.Client
var readers = default(Dictionary<string, object>);
CheckDisposed();
var connectionState = await _state.WaitForActiveConnectionAsync(nameof(InvokeCoreAsync));
var connectionState = await _state.WaitForActiveConnectionAsync(nameof(InvokeCoreAsync), token: cancellationToken);
Task<object> invocationTask;
try
@ -853,7 +855,7 @@ namespace Microsoft.AspNetCore.SignalR.Client
var readers = default(Dictionary<string, object>);
CheckDisposed();
var connectionState = await _state.WaitForActiveConnectionAsync(nameof(SendCoreAsync));
var connectionState = await _state.WaitForActiveConnectionAsync(nameof(SendCoreAsync), token: cancellationToken);
try
{
CheckDisposed();
@ -872,10 +874,10 @@ namespace Microsoft.AspNetCore.SignalR.Client
}
}
private async Task SendWithLock(ConnectionState expectedConnectionState, HubMessage message, CancellationToken cancellationToken = default, [CallerMemberName] string callerName = "")
private async Task SendWithLock(ConnectionState expectedConnectionState, HubMessage message, CancellationToken cancellationToken, [CallerMemberName] string callerName = "")
{
CheckDisposed();
var connectionState = await _state.WaitForActiveConnectionAsync(callerName);
var connectionState = await _state.WaitForActiveConnectionAsync(callerName, token: cancellationToken);
try
{
CheckDisposed();
@ -1245,7 +1247,7 @@ namespace Microsoft.AspNetCore.SignalR.Client
private async Task HandleConnectionClose(ConnectionState connectionState)
{
// Clear the connectionState field
await _state.WaitConnectionLockAsync();
await _state.WaitConnectionLockAsync(token: default);
try
{
SafeAssert(ReferenceEquals(_state.CurrentConnectionStateUnsynchronized, connectionState),
@ -1363,7 +1365,7 @@ namespace Microsoft.AspNetCore.SignalR.Client
{
Log.ReconnectingStoppedDuringRetryDelay(_logger);
await _state.WaitConnectionLockAsync();
await _state.WaitConnectionLockAsync(token: default);
try
{
_state.ChangeState(HubConnectionState.Reconnecting, HubConnectionState.Disconnected);
@ -1378,7 +1380,7 @@ namespace Microsoft.AspNetCore.SignalR.Client
return;
}
await _state.WaitConnectionLockAsync();
await _state.WaitConnectionLockAsync(token: default);
try
{
SafeAssert(ReferenceEquals(_state.CurrentConnectionStateUnsynchronized, null),
@ -1417,7 +1419,7 @@ namespace Microsoft.AspNetCore.SignalR.Client
nextRetryDelay = GetNextRetryDelay(previousReconnectAttempts++, DateTime.UtcNow - reconnectStartTime, retryReason);
}
await _state.WaitConnectionLockAsync();
await _state.WaitConnectionLockAsync(token: default);
try
{
SafeAssert(ReferenceEquals(_state.CurrentConnectionStateUnsynchronized, null),
@ -1956,10 +1958,10 @@ namespace Microsoft.AspNetCore.SignalR.Client
SafeAssert(CurrentConnectionStateUnsynchronized != null, "We don't have a connection!", memberName, fileName, lineNumber);
}
public Task WaitConnectionLockAsync([CallerMemberName] string memberName = null, [CallerFilePath] string filePath = null, [CallerLineNumber] int lineNumber = 0)
public Task WaitConnectionLockAsync(CancellationToken token, [CallerMemberName] string memberName = null, [CallerFilePath] string filePath = null, [CallerLineNumber] int lineNumber = 0)
{
Log.WaitingOnConnectionLock(_logger, memberName, filePath, lineNumber);
return _connectionLock.WaitAsync();
return _connectionLock.WaitAsync(token);
}
public bool TryAcquireConnectionLock()
@ -1968,9 +1970,9 @@ namespace Microsoft.AspNetCore.SignalR.Client
}
// Don't call this method in a try/finally that releases the lock since we're also potentially releasing the connection lock here.
public async Task<ConnectionState> WaitForActiveConnectionAsync(string methodName, [CallerMemberName] string memberName = null, [CallerFilePath] string filePath = null, [CallerLineNumber] int lineNumber = 0)
public async Task<ConnectionState> WaitForActiveConnectionAsync(string methodName, CancellationToken token, [CallerMemberName] string memberName = null, [CallerFilePath] string filePath = null, [CallerLineNumber] int lineNumber = 0)
{
await WaitConnectionLockAsync(methodName);
await WaitConnectionLockAsync(token, methodName);
if (CurrentConnectionStateUnsynchronized == null || CurrentConnectionStateUnsynchronized.Stopping)
{

View File

@ -1335,6 +1335,102 @@ namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests
}
}
[Theory]
[QuarantinedTest]
[MemberData(nameof(HubProtocolsList))]
public async Task ServerLogsErrorIfClientInvokeCannotBeSerialized(string protocolName)
{
// Just to help sanity check that the right exception is thrown
var exceptionSubstring = protocolName switch
{
"json" => "A possible object cycle was detected.",
"newtonsoft-json" => "A possible object cycle was detected.",
"messagepack" => "Failed to serialize Microsoft.AspNetCore.SignalR.Client.FunctionalTests.TestHub+Unserializable value.",
var x => throw new Exception($"The test does not have an exception string for the protocol '{x}'!"),
};
var protocol = HubProtocols[protocolName];
using (var server = await StartServer<Startup>(write => write.EventId.Name == "FailedWritingMessage"))
{
var connection = CreateHubConnection(server.Url, "/default", HttpTransportType.WebSockets, protocol, LoggerFactory);
var closedTcs = new TaskCompletionSource<Exception>(TaskCreationOptions.RunContinuationsAsynchronously);
connection.Closed += (ex) => { closedTcs.TrySetResult(ex); return Task.CompletedTask; };
try
{
await connection.StartAsync().OrTimeout();
var result = connection.InvokeAsync<string>(nameof(TestHub.CallWithUnserializableObject));
// The connection should close.
Assert.Null(await closedTcs.Task.OrTimeout());
await Assert.ThrowsAsync<TaskCanceledException>(() => result).OrTimeout();
}
catch (Exception ex)
{
LoggerFactory.CreateLogger<HubConnectionTests>().LogError(ex, "{ExceptionType} from test", ex.GetType().FullName);
throw;
}
finally
{
await connection.DisposeAsync().OrTimeout();
}
var errorLog = server.GetLogs().SingleOrDefault(r => r.Write.EventId.Name == "FailedWritingMessage");
Assert.NotNull(errorLog);
Assert.Contains(exceptionSubstring, errorLog.Write.Exception.Message);
Assert.Equal(LogLevel.Error, errorLog.Write.LogLevel);
}
}
[Theory]
[QuarantinedTest]
[MemberData(nameof(HubProtocolsList))]
public async Task ServerLogsErrorIfReturnValueCannotBeSerialized(string protocolName)
{
// Just to help sanity check that the right exception is thrown
var exceptionSubstring = protocolName switch
{
"json" => "A possible object cycle was detected.",
"newtonsoft-json" => "A possible object cycle was detected.",
"messagepack" => "Failed to serialize Microsoft.AspNetCore.SignalR.Client.FunctionalTests.TestHub+Unserializable value.",
var x => throw new Exception($"The test does not have an exception string for the protocol '{x}'!"),
};
var protocol = HubProtocols[protocolName];
using (var server = await StartServer<Startup>(write => write.EventId.Name == "FailedWritingMessage"))
{
var connection = CreateHubConnection(server.Url, "/default", HttpTransportType.LongPolling, protocol, LoggerFactory);
var closedTcs = new TaskCompletionSource<Exception>(TaskCreationOptions.RunContinuationsAsynchronously);
connection.Closed += (ex) => { closedTcs.TrySetResult(ex); return Task.CompletedTask; };
try
{
await connection.StartAsync().OrTimeout();
var result = connection.InvokeAsync<string>(nameof(TestHub.GetUnserializableObject)).OrTimeout();
// The connection should close.
Assert.Null(await closedTcs.Task.OrTimeout());
await Assert.ThrowsAsync<TaskCanceledException>(() => result).OrTimeout();
}
catch (Exception ex)
{
LoggerFactory.CreateLogger<HubConnectionTests>().LogError(ex, "{ExceptionType} from test", ex.GetType().FullName);
throw;
}
finally
{
await connection.DisposeAsync().OrTimeout();
}
var errorLog = server.GetLogs().SingleOrDefault(r => r.Write.EventId.Name == "FailedWritingMessage");
Assert.NotNull(errorLog);
Assert.Contains(exceptionSubstring, errorLog.Write.Exception.Message);
Assert.Equal(LogLevel.Error, errorLog.Write.LogLevel);
}
}
[Fact]
public async Task RandomGenericIsNotTreatedAsStream()
{

View File

@ -95,6 +95,33 @@ namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests
{
return Context.Features.Get<IHttpTransportFeature>().TransportType.ToString();
}
public async Task CallWithUnserializableObject()
{
await Clients.All.SendAsync("Foo", Unserializable.Create());
}
public Unserializable GetUnserializableObject()
{
return Unserializable.Create();
}
public class Unserializable
{
public Unserializable Child { get; private set; }
private Unserializable()
{
}
internal static Unserializable Create()
{
// Loops throw off every serializer ;).
var o = new Unserializable();
o.Child = o;
return o;
}
}
}
public class DynamicTestHub : DynamicHub

View File

@ -443,7 +443,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
})),
async (connection) =>
{
// We aggregate failures that happen when we start the transport. The operation cancelled exception will
// We aggregate failures that happen when we start the transport. The operation canceled exception will
// be an inner exception.
var ex = await Assert.ThrowsAsync<AggregateException>(async () => await connection.StartAsync(cts.Token)).OrTimeout();
Assert.Equal(3, ex.InnerExceptions.Count);
@ -454,6 +454,29 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests
}
}
[Fact]
public async Task CanceledCancellationTokenPassedToStartThrows()
{
using (StartVerifiableLog())
{
bool transportStartCalled = false;
var httpHandler = new TestHttpMessageHandler();
await WithConnectionAsync(
CreateConnection(httpHandler,
transport: new TestTransport(onTransportStart: () => {
transportStartCalled = true;
return Task.CompletedTask;
})),
async (connection) =>
{
await Assert.ThrowsAsync<TaskCanceledException>(async () => await connection.StartAsync(new CancellationToken(canceled: true))).OrTimeout();
});
Assert.False(transportStartCalled);
}
}
[Fact]
public async Task SSECanBeCanceled()
{

Some files were not shown because too many files have changed in this diff Show More