Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion sdk.sln
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27004.2005
VisualStudioVersion = 15.0.27101.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{50A89C27-BA35-44B2-AC57-E54551791C64}"
ProjectSection(SolutionItems) = preProject
Expand Down Expand Up @@ -110,6 +110,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.NET.Build.Tests",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.NET.Pack.Tests", "src\Tests\Microsoft.NET.Pack.Tests\Microsoft.NET.Pack.Tests.csproj", "{8746DC05-3035-4F24-9F2C-BAAAB5B50FD3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.NET.Perf.Tests", "src\Tests\Microsoft.NET.Perf.Tests\Microsoft.NET.Perf.Tests.csproj", "{AFB72217-54A0-4E76-870A-42718B8E0AE5}"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want the perf tests to run as part of the unit test switch, or do we want them to run separately (which, I believe, is what most of the other repos are doing)?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We want the perf tests to run with a single iteration during normal tests. That will ensure that they don't get functionally broken. When running in the perf lab, they should run with more iterations, but this will only happen automatically after a PR is merged (though it will be possible to request a perf run for an unmerged PR).

EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.NET.Publish.Tests", "src\Tests\Microsoft.NET.Publish.Tests\Microsoft.NET.Publish.Tests.csproj", "{5B3E6EC9-AD8D-4F68-A9F8-C60CF11F4753}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.NET.Restore.Tests", "src\Tests\Microsoft.NET.Restore.Tests\Microsoft.NET.Restore.Tests.csproj", "{112668D7-322D-4F83-A6CE-B814C25AD3BF}"
Expand Down Expand Up @@ -163,6 +165,10 @@ Global
{CAF71BDC-7B7D-4A43-AB8C-E440A1E4F108}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CAF71BDC-7B7D-4A43-AB8C-E440A1E4F108}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CAF71BDC-7B7D-4A43-AB8C-E440A1E4F108}.Release|Any CPU.Build.0 = Release|Any CPU
{AFB72217-54A0-4E76-870A-42718B8E0AE5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AFB72217-54A0-4E76-870A-42718B8E0AE5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AFB72217-54A0-4E76-870A-42718B8E0AE5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AFB72217-54A0-4E76-870A-42718B8E0AE5}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -185,6 +191,7 @@ Global
{112668D7-322D-4F83-A6CE-B814C25AD3BF} = {580D1AE7-AA8F-4912-8B76-105594E00B3B}
{CAF71BDC-7B7D-4A43-AB8C-E440A1E4F108} = {580D1AE7-AA8F-4912-8B76-105594E00B3B}
{7C6A88D0-DBCC-4933-A92D-A0AC133DD5FC} = {5293658E-96D2-421F-A789-D0B6BA129570}
{AFB72217-54A0-4E76-870A-42718B8E0AE5} = {580D1AE7-AA8F-4912-8B76-105594E00B3B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {FB8F26CE-4DE6-433F-B32A-79183020BBD6}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
<Target Name="PrepareAdditionalFilesToLayout" BeforeTargets="AssignTargetPaths">
<PropertyGroup>
<_NugetBuildTasksPackPath>$(NuGetPackageRoot)nuget.build.tasks.pack\$(NuGetBuildTasksPackVersion)</_NugetBuildTasksPackPath>
<_Stage0SdksFolder>$(DOTNET_INSTALL_DIR)\sdk\$(DotNetCliVersion)\Sdks</_Stage0SdksFolder>
</PropertyGroup>
<ItemGroup>
<LayoutFile Include="@(None)" Condition="'%(None.PackagePath)' != ''">
Expand All @@ -88,6 +89,16 @@
<LayoutFile Include="@(PackFile)">
<TargetPath>..\NuGet.Build.Tasks.Pack\%(PackFile.RecursiveDir)%(PackFile.Filename)%(PackFile.Extension)</TargetPath>
</LayoutFile>

<!-- Include some of the Sdks from the Stage 0 CLI for performance tests-->

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these the SDK files that the CLI requires that are not produced by dotnet/sdk?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are SDK files that the Performance tests require that are not produced by dotnet/sdk

<Stage0SdkFile Include="$(_Stage0SdksFolder)\FSharp.NET.Sdk\**" SdkName="FSharp.NET.Sdk" />
<Stage0SdkFile Include="$(_Stage0SdksFolder)\Microsoft.NET.Sdk.Publish\**" SdkName="Microsoft.NET.Sdk.Publish" />
<Stage0SdkFile Include="$(_Stage0SdksFolder)\Microsoft.NET.Sdk.Web\**" SdkName="Microsoft.NET.Sdk.Web" />
<Stage0SdkFile Include="$(_Stage0SdksFolder)\Microsoft.NET.Sdk.Web.ProjectSystem\**" SdkName="Microsoft.NET.Sdk.Web.ProjectSystem"/>
<LayoutFile Include="@(Stage0SdkFile)">
<TargetPath>..\%(Stage0SdkFile.SdkName)\%(Stage0SdkFile.RecursiveDir)%(Stage0SdkFile.Filename)%(Stage0SdkFile.Extension)</TargetPath>
</LayoutFile>

</ItemGroup>
</Target>

Expand Down
14 changes: 13 additions & 1 deletion src/Tests/Common/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
using System.Text;
using Microsoft.NET.TestFramework;

class Program
partial class Program
{
public static int Main(string[] args)
{
Expand All @@ -20,13 +20,25 @@ public static int Main(string[] args)
newArgs.Insert(0, typeof(Program).Assembly.Location);
}

if (!showHelp)
{
BeforeTestRun(newArgs);
}

int returnCode = Xunit.ConsoleClient.Program.Main(newArgs.ToArray());

if (showHelp)
{
TestCommandLine.ShowHelp();
}
else
{
AfterTestRun();
}

return returnCode;
}

static partial void BeforeTestRun(List<string> args);
static partial void AfterTestRun();
}
239 changes: 239 additions & 0 deletions src/Tests/Microsoft.NET.Perf.Tests/BuildPerf.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using FluentAssertions;
using Microsoft.NET.TestFramework;
using Microsoft.NET.TestFramework.Assertions;
using Microsoft.NET.TestFramework.Commands;
using Microsoft.NET.TestFramework.ProjectConstruction;
using Microsoft.Xunit.Performance.Api;
using Xunit;
using Xunit.Abstractions;

namespace Microsoft.NET.Perf.Tests
{
public class BuildPerf : SdkTest
{
public BuildPerf(ITestOutputHelper log) : base(log)
{
}

// These tests are currently disabled for full framework MSBuild because the CI machines don't
// have an MSBuild that supports the /restore command-line argument
[CoreMSBuildOnlyTheory]
[InlineData(ProjectPerfOperation.CleanBuild)]
[InlineData(ProjectPerfOperation.BuildWithNoChanges)]
public void BuildNetCore2App(ProjectPerfOperation operation)
{
var testProject = new TestProject()
{
Name = "NetCoreApp",
TargetFrameworks = "netcoreapp2.0",
IsSdkProject = true,
IsExe = true
};

var testAsset = _testAssetsManager.CreateTestProject(testProject, identifier: operation.ToString());

TestProject(testAsset.Path, ".NET Core 2 Console App", operation);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This name, TestProject, does it mean go run a test on the project or is it a more of a name to create TestProject, which happens to do things like build.

The name seems strange here. Does not read like an action. At least, I didn't read it as such at first.

}

[CoreMSBuildOnlyTheory]
[InlineData(ProjectPerfOperation.CleanBuild)]
[InlineData(ProjectPerfOperation.BuildWithNoChanges)]
public void BuildNetStandard2Library(ProjectPerfOperation operation)
{
var testProject = new TestProject()
{
Name = "NetCoreApp",
TargetFrameworks = "netstandard2.0",
IsSdkProject = true
};

var testAsset = _testAssetsManager.CreateTestProject(testProject, identifier: operation.ToString());

TestProject(testAsset.Path, ".NET Standard 2.0 Library", operation);
}

[CoreMSBuildOnlyTheory]
[InlineData(ProjectPerfOperation.CleanBuild)]
[InlineData(ProjectPerfOperation.BuildWithNoChanges)]
public void BuildMVCApp(ProjectPerfOperation operation)
{
var testDir = _testAssetsManager.CreateTestDirectory(identifier: operation.ToString());
var newCommand = new DotnetCommand(Log);
newCommand.WorkingDirectory = testDir.Path;

newCommand.Execute("new", "mvc", "--no-restore").Should().Pass();

TestProject(testDir.Path, "ASP.NET Core MVC app", operation);
}

[CoreMSBuildOnlyTheory(Skip ="The code for these scenarios needs to be acquired during the test run (instead of relying on hard-coded local path)")]
[InlineData("SmallP2POldCsproj", ProjectPerfOperation.CleanBuild)]
[InlineData("SmallP2POldCsproj", ProjectPerfOperation.BuildWithNoChanges)]
[InlineData("SmallP2PNewCsproj", ProjectPerfOperation.CleanBuild)]
[InlineData("SmallP2PNewCsproj", ProjectPerfOperation.BuildWithNoChanges)]
[InlineData("LargeP2POldCsproj", ProjectPerfOperation.CleanBuild)]
[InlineData("LargeP2POldCsproj", ProjectPerfOperation.BuildWithNoChanges)]
public void BuildProjectFromPerfSuite(string name, ProjectPerfOperation operation)
{
string sourceProject = Path.Combine(@"C:\MSBPerf\3", name);
var testDir = _testAssetsManager.CreateTestDirectory("Perf_" + name, identifier: operation.ToString());
FolderSnapshot.MirrorFiles(sourceProject, testDir.Path);

// The generated projects target .NET Core 2.1, retarget them to .NET Core 2.0
foreach (var projFile in Directory.GetFiles(testDir.Path, "*.csproj", SearchOption.AllDirectories))
{
var project = XDocument.Load(projFile);
var ns = project.Root.Name.Namespace;

// Find both TargetFramework and TargetFrameworks elements
var targetFrameworkElements = project.Root.Elements(ns + "PropertyGroup").Elements("TargetFramework");
targetFrameworkElements = targetFrameworkElements.Concat(project.Root.Elements(ns + "PropertyGroup").Elements("TargetFrameworks"));

foreach (var tfElement in targetFrameworkElements)
{
tfElement.Value = tfElement.Value.Replace("netcoreapp2.1", "netcoreapp2.0");
}

project.Save(projFile);
}

TestProject(testDir.Path, name, operation);
}

[CoreMSBuildOnlyTheory(Skip ="This test needs to clone the Roslyn repo and checkout a given commit instead of relying on a local copy of the repo")]
[InlineData(ProjectPerfOperation.CleanBuild)]
[InlineData(ProjectPerfOperation.BuildWithNoChanges)]
public void BuildRoslynCompilers(ProjectPerfOperation operation)
{

string sourceProject = @"C:\git\roslyn";
var testDir = _testAssetsManager.CreateTestDirectory("Perf_Roslyn", identifier: operation.ToString());
Console.WriteLine($"Mirroring {sourceProject} to {testDir.Path}...");
FolderSnapshot.MirrorFiles(sourceProject, testDir.Path);
Console.WriteLine("Done");

// Override global.json from repo
File.Delete(Path.Combine(testDir.Path, "global.json"));

// Run Roslyn's restore script
var restoreCmd = new SdkCommandSpec()
{
FileName = Path.Combine(testDir.Path, "Restore.cmd"),
WorkingDirectory = testDir.Path
};
TestContext.Current.AddTestEnvironmentVariables(restoreCmd);
restoreCmd.ToCommand().Execute().Should().Pass();

TestProject(Path.Combine(testDir.Path, "Compilers.sln"), "Roslyn", operation);
}

public enum ProjectPerfOperation
{
CleanBuild,
BuildWithNoChanges,
NoOpRestore
}

private void TestProject(string projectFolderOrFile, string testName, ProjectPerfOperation perfOperation)
{
string testProjectPath;
string testProjectDirectory;
bool projectFileSpecified;

if (File.Exists(projectFolderOrFile))
{
projectFileSpecified = true;
testProjectPath = projectFolderOrFile;
testProjectDirectory = Path.GetDirectoryName(projectFolderOrFile);
}
else
{
projectFileSpecified = false;
testProjectPath = Directory.GetFiles(projectFolderOrFile, "*.sln", SearchOption.AllDirectories).SingleOrDefault();
if (testProjectPath == null)
{
testProjectPath = Directory.GetFiles(projectFolderOrFile, "*.csproj", SearchOption.AllDirectories).SingleOrDefault();
if (testProjectPath == null)
{
throw new ArgumentException("Could not find project file to test in folder: " + projectFolderOrFile);
}
}
testProjectDirectory = Path.GetDirectoryName(testProjectPath);
}

TestCommand commandToTest;
var perfTest = new PerfTest();
perfTest.ScenarioName = testName;

if (perfOperation == ProjectPerfOperation.NoOpRestore)
{
TestCommand restoreCommand;

if (TestContext.Current.ToolsetUnderTest.ShouldUseFullFrameworkMSBuild)
{
restoreCommand = new RestoreCommand(Log, testProjectPath);
}
else
{
restoreCommand = new RestoreCommand(Log, testProjectPath);
restoreCommand = new DotnetCommand(Log, "restore");
if (projectFileSpecified)
{
restoreCommand.Arguments.Add(testProjectPath);
}
}
restoreCommand.WorkingDirectory = testProjectDirectory;

restoreCommand.Execute().Should().Pass();

commandToTest = restoreCommand;
perfTest.TestName = "Restore (No-op)";
}
else
{
if (TestContext.Current.ToolsetUnderTest.ShouldUseFullFrameworkMSBuild)
{
commandToTest = new BuildCommand(Log, projectFileSpecified ? testProjectPath : testProjectDirectory);
commandToTest.Arguments.Add("/restore");
}
else
{
commandToTest = new DotnetCommand(Log, "build");
if (projectFileSpecified)
{
commandToTest.Arguments.Add(testProjectPath);
}
}
commandToTest.WorkingDirectory = testProjectDirectory;

if (perfOperation == ProjectPerfOperation.CleanBuild)
{
perfTest.TestName = "Build";
}
else if (perfOperation == ProjectPerfOperation.BuildWithNoChanges)
{
// Build once before taking folder snaspshot
commandToTest.Execute().Should().Pass();

perfTest.TestName = "Build (no changes)";
}
else
{
throw new ArgumentException("Unexpected perf operation: " + perfOperation);
}
}

perfTest.ProcessToMeasure = commandToTest.GetProcessStartInfo();
perfTest.TestFolder = testProjectDirectory;

perfTest.Run();
}
}
}
63 changes: 63 additions & 0 deletions src/Tests/Microsoft.NET.Perf.Tests/FolderSnapshot.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace Microsoft.NET.Perf.Tests
{
class FolderSnapshot : IDisposable
{
string OriginalPath { get; set; }
string BackupPath { get; set; }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: there is an extra empty line here.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed...

public static FolderSnapshot Create(string path)
{
FolderSnapshot folderSnapshot = new FolderSnapshot();
folderSnapshot.OriginalPath = path;
folderSnapshot.BackupPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
MirrorFiles(folderSnapshot.OriginalPath, folderSnapshot.BackupPath);
return folderSnapshot;
}

public void Restore()
{
MirrorFiles(BackupPath, OriginalPath);
}

public static void MirrorFiles(string source, string dest)
{
if (Directory.Exists(dest))
{
Directory.Delete(dest, true);
}
CopyDirectory(source, dest);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: empty line after closing brackets. Check this in the rest of the files.

We should add this to our editorconfig.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed...

}

static void CopyDirectory(string source, string dest)
{
Directory.CreateDirectory(dest);

DirectoryInfo sourceInfo = new DirectoryInfo(source);
foreach (var fileInfo in sourceInfo.GetFiles())
{
string destFile = Path.Combine(dest, fileInfo.Name);
fileInfo.CopyTo(destFile);
}
foreach (var subdirInfo in sourceInfo.GetDirectories())
{
// Don't mirror .git folder
if (subdirInfo.Name == ".git")
{
continue;
}
string destDir = Path.Combine(dest, subdirInfo.Name);
CopyDirectory(subdirInfo.FullName, destDir);
}
}

public void Dispose()
{
Directory.Delete(BackupPath, true);
}
}
}
Loading