Merge branch 'master' into johluo/remove-extensions-deps
This commit is contained in:
commit
6ad0d5849c
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -189,7 +189,6 @@
|
|||
|
||||
<ArchiveExtension>.tar.gz</ArchiveExtension>
|
||||
<ArchiveExtension Condition="'$(TargetOsName)' == 'win'">.zip</ArchiveExtension>
|
||||
<ChecksumExtension>.sha512</ChecksumExtension>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="eng\Workarounds.props" />
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)" />
|
||||
|
|
|
|||
|
|
@ -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" />
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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)" />
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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%
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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. -->
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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)')" />
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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" />
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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" />
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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")]
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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 =>
|
||||
//{
|
||||
|
|
|
|||
|
|
@ -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")]
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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")]
|
||||
|
|
|
|||
|
|
@ -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")]
|
||||
|
|
|
|||
|
|
@ -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")]
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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:");
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
<AssemblyName>InProcessWebSite</AssemblyName>
|
||||
<TestAssetOutputName>InProcessNewShimWebSite</TestAssetOutputName>
|
||||
<DefineConstants>FORWARDCOMPAT</DefineConstants>
|
||||
<CompileUsingReferenceAssemblies>false</CompileUsingReferenceAssemblies>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="'$(OS)' == 'Windows_NT'">
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)));
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>$(DefaultNetCoreTargetFramework)</TargetFrameworks>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Reference in New Issue