Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add support for generating app manifest files for tests #28575

Merged
merged 12 commits into from
Dec 17, 2020
Merged
1 change: 1 addition & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@
<DeterministicSourcePaths Condition="'$(IsSampleProject)' == 'true' OR '$(IsTestAssetProject)' == 'true'">false</DeterministicSourcePaths>
<!-- Projects which reference Microsoft.AspNetCore.Mvc.Testing should import this targets file to ensure dependency .deps.json files are copied into test output. -->
<MvcTestingTargets>$(MSBuildThisFileDirectory)src\Mvc\Mvc.Testing\src\Microsoft.AspNetCore.Mvc.Testing.targets</MvcTestingTargets>
<_MvcTestingTasksAssembly>$(ArtifactsBinDir)\Microsoft.AspNetCore.Mvc.Testing.Tasks\$(Configuration)\netstandard2.0\Microsoft.AspNetCore.Mvc.Testing.Tasks.dll</_MvcTestingTasksAssembly>
<!-- IIS native projects can only be built on Windows for x86 and x64. -->
<BuildIisNativeProjects Condition=" '$(TargetOsName)' == 'win' AND ('$(TargetArchitecture)' == 'x86' OR '$(TargetArchitecture)' == 'x64') ">true</BuildIisNativeProjects>
<!-- This property is shared by several projects to layout the AspNetCore.App targeting pack for installers -->
Expand Down
1 change: 1 addition & 0 deletions eng/ProjectReferences.props
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.RazorPages" ProjectPath="$(RepoRoot)src\Mvc\Mvc.RazorPages\src\Microsoft.AspNetCore.Mvc.RazorPages.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.Razor" ProjectPath="$(RepoRoot)src\Mvc\Mvc.Razor\src\Microsoft.AspNetCore.Mvc.Razor.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.TagHelpers" ProjectPath="$(RepoRoot)src\Mvc\Mvc.TagHelpers\src\Microsoft.AspNetCore.Mvc.TagHelpers.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.Testing.Tasks" ProjectPath="$(RepoRoot)src\Mvc\Mvc.Testing.Tasks\src\Microsoft.AspNetCore.Mvc.Testing.Tasks.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.Testing" ProjectPath="$(RepoRoot)src\Mvc\Mvc.Testing\src\Microsoft.AspNetCore.Mvc.Testing.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc.ViewFeatures" ProjectPath="$(RepoRoot)src\Mvc\Mvc.ViewFeatures\src\Microsoft.AspNetCore.Mvc.ViewFeatures.csproj" />
<ProjectReferenceProvider Include="Microsoft.AspNetCore.Mvc" ProjectPath="$(RepoRoot)src\Mvc\Mvc\src\Microsoft.AspNetCore.Mvc.csproj" />
Expand Down
58 changes: 58 additions & 0 deletions src/Mvc/Mvc.Testing.Tasks/src/GenerateMvcTestManifestTask.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// 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.IO;
using System.Reflection;
using System.Runtime.Serialization.Json;
using System.Text;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;

namespace Microsoft.AspNetCore.Mvc.Testing.Tasks
{
/// <summary>
/// Generate a JSON file mapping assemblies to content root paths.
/// </summary>
public class GenerateMvcTestManifestTask : Task
pranavkm marked this conversation as resolved.
Show resolved Hide resolved
{
/// <summary>
/// The path to output the manifest file to.
/// </summary>
[Required]
public string ManifestPath { get; set; }

/// <summary>
/// A list of content root paths and assembly names to generate the
/// manifest from.
/// </summary>
[Required]
public ITaskItem[] Projects { get; set; }

/// <summary>
/// <inheritdoc />
/// </summary>
captainsafia marked this conversation as resolved.
Show resolved Hide resolved
public override bool Execute()
{
using var fileStream = File.Create(ManifestPath);
var output = new Dictionary<string, string>();

foreach (var project in Projects)
{
var contentRoot = project.GetMetadata("ContentRoot");
var assemblyName = project.GetMetadata("Identity");
output[assemblyName] = contentRoot;
}

var serializer = new DataContractJsonSerializer(typeof(Dictionary<string, string>), new DataContractJsonSerializerSettings
{
UseSimpleDictionaryFormat = true
});
using var writer = JsonReaderWriterFactory.CreateJsonWriter(fileStream, Encoding.UTF8, ownsStream: false, indent: true);
serializer.WriteObject(writer, output);

return !Log.HasLoggedErrors;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Build tasks for functional tests.</Description>
<TargetFramework>netstandard2.0</TargetFramework>

<GenerateDocumentationFile>true</GenerateDocumentationFile>
captainsafia marked this conversation as resolved.
Show resolved Hide resolved
<AddPublicApiAnalyzers>false</AddPublicApiAnalyzers>
captainsafia marked this conversation as resolved.
Show resolved Hide resolved
</PropertyGroup>

<ItemGroup>
<Reference Include="Microsoft.Build.Framework" />
<Reference Include="Microsoft.Build.Utilities.Core" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@
<Reference Include="Microsoft.Extensions.HostFactoryResolver.Sources" />
</ItemGroup>

<ItemGroup>
<Reference
Include="Microsoft.AspNetCore.Mvc.Testing.Tasks"
Targets="Publish"
captainsafia marked this conversation as resolved.
Show resolved Hide resolved
ReferenceOutputAssembly="false"
SkipGetTargetFrameworkProperties="true"
UndefineProperties="TargetFramework;TargetFrameworks;RuntimeIdentifier;PublishDir" />
</ItemGroup>

<ItemGroup>
<Content Include="Microsoft.AspNetCore.Mvc.Testing.targets" Pack="true" PackagePath="build/$(TargetFramework)/" />
</ItemGroup>
Expand Down
36 changes: 18 additions & 18 deletions src/Mvc/Mvc.Testing/src/Microsoft.AspNetCore.Mvc.Testing.targets
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

<PropertyGroup>
<_MvcTestingTasksAssembly Condition="$(_MvcTestingTasksAssembly) == ''">$(MSBuildThisFileDirectory)../tasks/Microsoft.AspNetCore.Mvc.Testing.dll</_MvcTestingTasksAssembly>
captainsafia marked this conversation as resolved.
Show resolved Hide resolved
</PropertyGroup>
<UsingTask TaskName="GenerateMvcTestManifestTask" AssemblyFile="$(_MvcTestingTasksAssembly)"/>

<!--
Work around https://github.com/NuGet/Home/issues/4412. MVC uses DependencyContext.Load() which looks next to a .dll
for a .deps.json. Information isn't available elsewhere. Need the .deps.json file for all web site applications.
Expand All @@ -23,30 +28,25 @@
</ItemGroup>
</Target>

<Target Name="_AddContentRootForProjectReferences" BeforeTargets="GetAssemblyAttributes" DependsOnTargets="_ResolveMvcTestProjectReferences">
<Target Name="_AddContentRootForProjectReferences" BeforeTargets="PrepareResources" DependsOnTargets="_ResolveMvcTestProjectReferences">
captainsafia marked this conversation as resolved.
Show resolved Hide resolved
<ItemGroup>
<WebApplicationFactoryContentRootAttribute
Condition="'%(_ContentRootProjectReferences.Identity)' != ''"
Include="%(_ContentRootProjectReferences.Identity)"
AssemblyName="%(_ContentRootProjectReferences.FusionName)"
ContentRootPath="$([System.IO.Path]::GetDirectoryName(%(_ContentRootProjectReferences.MSBuildSourceProjectFile)))"
ContentRootTest="$([System.IO.Path]::GetFileName(%(_ContentRootProjectReferences.MSBuildSourceProjectFile)))"
Priority="0" />
<_ManifestProjects Include="%(_ContentRootProjectReferences.FusionName)">
<ContentRoot>$([System.IO.Path]::GetDirectoryName(%(_ContentRootProjectReferences.MSBuildSourceProjectFile)))</ContentRoot>
</_ManifestProjects>
</ItemGroup>

<GenerateMvcTestManifestTask ManifestPath="$(IntermediateOutputPath)MvcTestingAppManifest.json" Projects="@(_ManifestProjects)"/>

<ItemGroup>
<AssemblyAttribute
Condition=" '%(WebApplicationFactoryContentRootAttribute.Identity)' != '' "
Include="Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryContentRootAttribute">
<_Parameter1>%(WebApplicationFactoryContentRootAttribute.AssemblyName)</_Parameter1>
<_Parameter2>%(WebApplicationFactoryContentRootAttribute.ContentRootPath)</_Parameter2>
<_Parameter3>%(WebApplicationFactoryContentRootAttribute.ContentRootTest)</_Parameter3>
<_Parameter4>%(WebApplicationFactoryContentRootAttribute.Priority)</_Parameter4>
</AssemblyAttribute>
<ContentWithTargetPath Include="$(IntermediateOutputPath)MvcTestingAppManifest.json"
TargetPath="MvcTestingAppManifest.json"
CopyToOutputDirectory="PreserveNewest"
CopyToPublishDirectory="Never"/>
<FileWrites Include="$(IntermediateOutputPath)MvcTestingAppManifest.json" />
</ItemGroup>
</Target>

<Target Name="CopyAditionalFiles" AfterTargets="Build;_ResolveMvcTestProjectReferences" Condition="'$(TargetFramework)'!=''">
<Target Name="_MvcCopyDependencyFiles" AfterTargets="Build;_ResolveMvcTestProjectReferences" Condition="'$(TargetFramework)'!=''">
<ItemGroup>
<DepsFilePaths
Condition="'%(_ContentRootProjectReferences.Identity)' != ''"
Expand All @@ -56,4 +56,4 @@
<Copy SourceFiles="%(DepsFilePaths.FullPath)" DestinationFolder="$(OutDir)" Condition="Exists('%(DepsFilePaths.FullPath)')" />
</Target>

</Project>
</Project>
35 changes: 26 additions & 9 deletions src/Mvc/Mvc.Testing/src/WebApplicationFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Text.Json;
using System.Runtime.Serialization.Json;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.TestHost;
Expand Down Expand Up @@ -93,7 +95,7 @@ public virtual IServiceProvider Services

/// <summary>
/// Gets the <see cref="IReadOnlyList{WebApplicationFactory}"/> of factories created from this factory
/// by further customizing the <see cref="IWebHostBuilder"/> when calling
/// by further customizing the <see cref="IWebHostBuilder"/> when calling
/// <see cref="WebApplicationFactory{TEntryPoint}.WithWebHostBuilder(Action{IWebHostBuilder})"/>.
/// </summary>
public IReadOnlyList<WebApplicationFactory<TEntryPoint>> Factories => _derivedFactories.AsReadOnly();
Expand Down Expand Up @@ -171,6 +173,28 @@ private void SetContentRoot(IWebHostBuilder builder)
return;
}

var fromFile = File.Exists("MvcTestingAppManifest.json");
var contentRoot = fromFile ? GetContentRootFromFile("MvcTestingAppManifest.json") : GetContentRootFromAssembly();

if (contentRoot != null)
{
builder.UseContentRoot(contentRoot);
}
else
{
builder.UseSolutionRelativeContentRoot(typeof(TEntryPoint).Assembly.GetName().Name);
}
}

private string GetContentRootFromFile(string file)
{
var data = JsonSerializer.Deserialize<IDictionary<string, string>>(File.ReadAllText(file));
captainsafia marked this conversation as resolved.
Show resolved Hide resolved
var key = typeof(TEntryPoint).Assembly.GetName().FullName;
return data[key];
captainsafia marked this conversation as resolved.
Show resolved Hide resolved
}

private string GetContentRootFromAssembly()
{
var metadataAttributes = GetContentRootMetadataAttributes(
typeof(TEntryPoint).Assembly.FullName,
typeof(TEntryPoint).Assembly.GetName().Name);
Expand All @@ -194,14 +218,7 @@ private void SetContentRoot(IWebHostBuilder builder)
}
}

if (contentRoot != null)
{
builder.UseContentRoot(contentRoot);
}
else
{
builder.UseSolutionRelativeContentRoot(typeof(TEntryPoint).Assembly.GetName().Name);
}
return contentRoot;
}

private static bool SetContentRootFromSetting(IWebHostBuilder builder)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ public void TestingInfrastructure_WebHost_WithWebHostBuilderRespectsCustomizatio
Assert.Equal(new[] { "ConfigureWebHost", "Customization", "FurtherCustomization" }, factory.ConfigureWebHostCalled.ToArray());
Assert.True(factory.CreateServerCalled);
Assert.True(factory.CreateWebHostBuilderCalled);
Assert.True(factory.GetTestAssembliesCalled);
// GetTestAssemblies is not called when reading content roots from MvcAppManifest
Assert.False(factory.GetTestAssembliesCalled);
Assert.True(factory.CreateHostBuilderCalled);
Assert.False(factory.CreateHostCalled);
}
Expand All @@ -46,7 +47,7 @@ public void TestingInfrastructure_GenericHost_WithWithHostBuilderRespectsCustomi

// Assert
Assert.Equal(new[] { "ConfigureWebHost", "Customization", "FurtherCustomization" }, factory.ConfigureWebHostCalled.ToArray());
Assert.True(factory.GetTestAssembliesCalled);
Assert.False(factory.GetTestAssembliesCalled);
Assert.True(factory.CreateHostBuilderCalled);
Assert.True(factory.CreateHostCalled);
Assert.False(factory.CreateServerCalled);
Expand Down