Test "dotnet restore" for all ASP.NET templates with 3 NuGet configs

This commit is contained in:
Mike Harder 2018-04-26 16:15:05 -07:00
parent 5a0b8fd8d9
commit 9dc18f277d
14 changed files with 360 additions and 0 deletions

25
AspNetCoreSdkTests.sln Normal file
View File

@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27428.2037
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCoreSdkTests", "AspNetCoreSdkTests\AspNetCoreSdkTests.csproj", "{982F3170-BE98-4B2F-B36F-4C036BCDB2E6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{982F3170-BE98-4B2F-B36F-4C036BCDB2E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{982F3170-BE98-4B2F-B36F-4C036BCDB2E6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{982F3170-BE98-4B2F-B36F-4C036BCDB2E6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{982F3170-BE98-4B2F-B36F-4C036BCDB2E6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3B64DDE7-BAD8-4DB3-A294-669059DA3334}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.0" />
<PackageReference Include="NUnit" Version="3.10.1" />
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
</ItemGroup>
<ItemGroup>
<None Update="NuGetConfig\NuGet.DotNetCore.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="NuGetConfig\NuGet.NuGetOrg.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="NuGetConfig\NuGet.Empty.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

View File

@ -0,0 +1,9 @@
namespace AspNetCoreSdkTests
{
public enum NuGetConfig
{
Empty,
DotNetCore,
NuGetOrg
}
}

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="DotNetCore" value="https://dotnet.myget.org/F/dotnet-core/api/v3/index.json" />
</packageSources>
</configuration>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
</packageSources>
</configuration>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="NuGet" value="https://api.nuget.org/v3/index.json" />
</packageSources>
</configuration>

View File

@ -0,0 +1,14 @@
namespace AspNetCoreSdkTests
{
public enum Template
{
Web,
Mvc,
Razor,
Angular,
React,
ReactRedux,
RazorClassLib,
WebApi
}
}

View File

@ -0,0 +1,20 @@
using AspNetCoreSdkTests.Util;
using NUnit.Framework;
namespace AspNetCoreSdkTests
{
[TestFixture]
public class TemplateTests
{
[Test]
[TestCaseSource(typeof(TestData), nameof(TestData.AllTemplates))]
public void Restore(Template template, NuGetConfig nuGetConfig)
{
using (var context = new DotNetContext())
{
context.New(template, restore: false);
context.Restore(nuGetConfig);
}
}
}
}

View File

@ -0,0 +1,24 @@
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
namespace AspNetCoreSdkTests
{
public static class TestData
{
public static IEnumerable<TestCaseData> AllTemplates { get; } =
from t in Enum.GetValues(typeof(Template)).Cast<Template>()
from c in Enum.GetValues(typeof(NuGetConfig)).Cast<NuGetConfig>()
let data = new TestCaseData(t, c)
select (
c == NuGetConfig.NuGetOrg ?
data.Ignore("RC1 not yet published to nuget.org") :
data);
public static IEnumerable<TestCaseData> ApplicationTemplates { get; } =
from d in AllTemplates
where ((Template)d.Arguments[0] != Template.RazorClassLib)
select d;
}
}

View File

@ -0,0 +1,4 @@
using NUnit.Framework;
// Run all test cases in parallel
[assembly: Parallelizable(ParallelScope.Children)]

View File

@ -0,0 +1,15 @@
namespace AspNetCoreSdkTests.Util
{
public class DotNetContext : TempDir
{
public string New(Template template, bool restore)
{
return DotNet.New(template.ToString(), Path, restore);
}
public string Restore(NuGetConfig config)
{
return DotNet.Restore(Path, config);
}
}
}

View File

@ -0,0 +1,113 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Text;
namespace AspNetCoreSdkTests.Util
{
internal static class DotNet
{
private static IEnumerable<KeyValuePair<string, string>> GetEnvironment(string workingDirectory)
{
// Set NUGET_PACKAGES to an empty folder to ensure all packages are loaded from either NuGetFallbackFolder or configured sources,
// and *not* loaded from the default per-user global-packages folder.
yield return new KeyValuePair<string, string>("NUGET_PACKAGES", Path.Combine(workingDirectory, ".nuget", "packages"));
}
public static string New(string template, string workingDirectory, bool restore)
{
var arguments = $"new {template} --name {template} --output ." + (restore ? "" : " --no-restore");
return RunDotNet(arguments, workingDirectory, GetEnvironment(workingDirectory));
}
public static string Restore(string workingDirectory, NuGetConfig config)
{
var configPath = Path.GetFullPath(Path.Combine("NuGetConfig", $"NuGet.{config}.config"));
return RunDotNet($"restore --no-cache --configfile {configPath}", workingDirectory, GetEnvironment(workingDirectory));
}
private static string RunDotNet(string arguments, string workingDirectory,
IEnumerable<KeyValuePair<string, string>> environment = null, bool throwOnError = true)
{
var p = StartDotNet(arguments, workingDirectory, environment);
return WaitForExit(p.Process, p.OutputBuilder, p.ErrorBuilder, throwOnError: throwOnError);
}
private static (Process Process, StringBuilder OutputBuilder, StringBuilder ErrorBuilder) StartDotNet(
string arguments, string workingDirectory, IEnumerable<KeyValuePair<string, string>> environment = null)
{
return StartProcess("dotnet", arguments, workingDirectory, environment);
}
private static (Process Process, StringBuilder OutputBuilder, StringBuilder ErrorBuilder) StartProcess(
string filename, string arguments, string workingDirectory, IEnumerable<KeyValuePair<string, string>> environment = null)
{
var process = new Process()
{
StartInfo =
{
FileName = filename,
Arguments = arguments,
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
WorkingDirectory = workingDirectory,
},
};
if (environment != null)
{
foreach (var kvp in environment)
{
process.StartInfo.Environment.Add(kvp);
}
}
var outputBuilder = new StringBuilder();
process.OutputDataReceived += (_, e) =>
{
outputBuilder.AppendLine(e.Data);
};
var errorBuilder = new StringBuilder();
process.ErrorDataReceived += (_, e) =>
{
errorBuilder.AppendLine(e.Data);
};
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
return (process, outputBuilder, errorBuilder);
}
public static string WaitForExit(Process process, StringBuilder outputBuilder, StringBuilder errorBuilder,
bool throwOnError = true)
{
// Workaround issue where WaitForExit() blocks until child processes are killed, which is problematic
// for the dotnet.exe NodeReuse child processes. I'm not sure why this is problematic for dotnet.exe child processes
// but not for MSBuild.exe child processes. The workaround is to specify a large timeout.
// https://stackoverflow.com/a/37983587/102052
process.WaitForExit(int.MaxValue);
if (throwOnError && process.ExitCode != 0)
{
var sb = new StringBuilder();
sb.AppendLine($"Command {process.StartInfo.FileName} {process.StartInfo.Arguments} returned exit code {process.ExitCode}");
sb.AppendLine();
sb.AppendLine(outputBuilder.ToString());
throw new InvalidOperationException(sb.ToString());
}
return outputBuilder.ToString();
}
}
}

View File

@ -0,0 +1,69 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
namespace AspNetCoreSdkTests.Util
{
internal static class IOUtil
{
public static IEnumerable<string> GetFiles(string path)
{
return Directory.GetFiles(path, "*", SearchOption.AllDirectories)
.Select(p => Path.GetRelativePath(path, p));
}
public static IEnumerable<string> GetDirectories(string path)
{
return Directory.GetDirectories(path, "*", SearchOption.AllDirectories)
.Select(p => Path.GetRelativePath(path, p));
}
public static string GetTempDir()
{
var temp = Path.GetTempFileName();
File.Delete(temp);
Directory.CreateDirectory(temp);
return temp;
}
public static void DeleteDir(string path)
{
// If delete fails (e.g. due to a file in use), retry once every second up to 20 times.
for (var i = 0; i < 20; i++)
{
try
{
var dir = new DirectoryInfo(path) { Attributes = FileAttributes.Normal };
foreach (var info in dir.GetFileSystemInfos("*", SearchOption.AllDirectories))
{
info.Attributes = FileAttributes.Normal;
}
dir.Delete(recursive: true);
break;
}
catch (DirectoryNotFoundException)
{
break;
}
catch (FileNotFoundException)
{
break;
}
catch (Exception)
{
if (i < 19)
{
Thread.Sleep(TimeSpan.FromSeconds(1));
}
else
{
throw;
}
}
}
}
}
}

View File

@ -0,0 +1,19 @@
using System;
namespace AspNetCoreSdkTests.Util
{
public class TempDir : IDisposable
{
public string Path { get; }
public TempDir()
{
Path = IOUtil.GetTempDir();
}
public void Dispose()
{
IOUtil.DeleteDir(Path);
}
}
}