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
44 changes: 44 additions & 0 deletions src/BuildPrediction/FakesPredictor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.IO;
using Microsoft.Build.Execution;

namespace Microsoft.Build.Prediction.Predictors
{
/// <summary>
/// Predicts inputs and outputs related to Fakes.
/// </summary>
public sealed class FakesPredictor : IProjectPredictor
{
internal const string FakesImportedPropertyName = "FakesImported";

internal const string FakesUseV2GenerationPropertyName = "FakesUseV2Generation";

internal const string FakesOutputPathPropertyName = "FakesOutputPath";

internal const string FakesItemName = "Fakes";

/// <inheritdoc/>
public void PredictInputsAndOutputs(ProjectInstance projectInstance, ProjectPredictionReporter predictionReporter)
{
if (!projectInstance.GetPropertyValue(FakesImportedPropertyName).Equals("true", StringComparison.OrdinalIgnoreCase))
{
return;
}

string fakesOutputPath = projectInstance.GetPropertyValue(FakesOutputPathPropertyName);
foreach (ProjectItemInstance item in projectInstance.GetItems(FakesItemName))
{
predictionReporter.ReportInputFile(item.EvaluatedInclude);

if (!string.IsNullOrWhiteSpace(fakesOutputPath))
{
string fakesAssembly = Path.Combine(fakesOutputPath, $"{Path.GetFileNameWithoutExtension(item.EvaluatedInclude)}.Fakes.dll");
predictionReporter.ReportOutputFile(fakesAssembly);
}
}
}
}
}
27 changes: 0 additions & 27 deletions src/BuildPrediction/Predictors/FakesOutputPathPredictor.cs

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,29 @@ private static void PredictInputsAndOutputs(
}
}
}

// FakesV2 projects add Fakes assemblies as content which are transitively copied to referencing projects. See CopyFakesAssembliesToOutputDir target.
if (dependency.ProjectInstance.GetPropertyValue(FakesPredictor.FakesImportedPropertyName).Equals("true", StringComparison.OrdinalIgnoreCase)
&& dependency.ProjectInstance.GetPropertyValue(FakesPredictor.FakesUseV2GenerationPropertyName).Equals("true", StringComparison.OrdinalIgnoreCase))
{
string fakesOutputPath = dependency.ProjectInstance.GetPropertyValue(FakesPredictor.FakesOutputPathPropertyName);
if (!string.IsNullOrWhiteSpace(fakesOutputPath))
{
// Make it absolute since it may be relative to the dependency project
fakesOutputPath = Path.Combine(dependency.ProjectInstance.Directory, fakesOutputPath);

foreach (ProjectItemInstance item in dependency.ProjectInstance.GetItems(FakesPredictor.FakesItemName))
{
string fakesAssemblyFileName = $"{Path.GetFileNameWithoutExtension(item.EvaluatedInclude)}.Fakes.dll";
predictionReporter.ReportInputFile(Path.Combine(fakesOutputPath, fakesAssemblyFileName));

if (!string.IsNullOrEmpty(outDir))
{
predictionReporter.ReportOutputFile(Path.Combine(outDir, fakesAssemblyFileName));
}
}
}
}
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/BuildPrediction/ProjectPredictors.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public static class ProjectPredictors
/// <item><see cref="GenerateBuildDependencyFilePredictor"/></item>
/// <item><see cref="GeneratePublishDependencyFilePredictor"/></item>
/// <item><see cref="GenerateRuntimeConfigurationFilesPredictor"/></item>
/// <item><see cref="FakesOutputPathPredictor"/></item>
/// <item><see cref="FakesPredictor"/></item>
/// </list>
/// </remarks>
/// <returns>A collection of <see cref="IProjectPredictor"/>.</returns>
Expand Down Expand Up @@ -115,7 +115,7 @@ public static class ProjectPredictors
new GenerateBuildDependencyFilePredictor(),
new GeneratePublishDependencyFilePredictor(),
new GenerateRuntimeConfigurationFilesPredictor(),
new FakesOutputPathPredictor(),
new FakesPredictor(),
//// NOTE! When adding a new predictor here, be sure to update the doc comment above.
};

Expand Down

This file was deleted.

103 changes: 103 additions & 0 deletions src/BuildPredictionTests/Predictors/FakesPredictorTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.IO;
using Microsoft.Build.Construction;
using Microsoft.Build.Execution;
using Microsoft.Build.Prediction.Predictors;
using Xunit;

namespace Microsoft.Build.Prediction.Tests.Predictors
{
public class FakesPredictorTests
{
[Fact]
public void FindItems()
{
const string FakesOutputPath = @"bin\FakesAssemblies";
ProjectInstance projectInstance = CreateTestProjectInstance(FakesOutputPath, ["A.fakes", "B.fakes", "C.fakes"]);

var expectedInputFiles = new[]
{
new PredictedItem("A.fakes", nameof(FakesPredictor)),
new PredictedItem("B.fakes", nameof(FakesPredictor)),
new PredictedItem("C.fakes", nameof(FakesPredictor)),
};
var expectedOutputFiles = new[]
{
new PredictedItem(Path.Combine(FakesOutputPath, "A.Fakes.dll"), nameof(FakesPredictor)),
new PredictedItem(Path.Combine(FakesOutputPath, "B.Fakes.dll"), nameof(FakesPredictor)),
new PredictedItem(Path.Combine(FakesOutputPath, "C.Fakes.dll"), nameof(FakesPredictor)),
};
new FakesPredictor()
.GetProjectPredictions(projectInstance)
.AssertPredictions(
projectInstance,
expectedInputFiles,
null,
expectedOutputFiles,
null);
}

[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData(" ")]
public void NoOutputsReportedIfInvalidFakesOutputPath(string fakesOutputPath)
{
ProjectInstance projectInstance = CreateTestProjectInstance(fakesOutputPath, ["A.fakes", "B.fakes", "C.fakes"]);
var expectedInputFiles = new[]
{
new PredictedItem("A.fakes", nameof(FakesPredictor)),
new PredictedItem("B.fakes", nameof(FakesPredictor)),
new PredictedItem("C.fakes", nameof(FakesPredictor)),
};

new FakesPredictor()
.GetProjectPredictions(projectInstance)
.AssertPredictions(
projectInstance,
expectedInputFiles,
null,
null,
null);
}

[Fact]
public void NoPredictionsReportedNoFakesItems()
{
ProjectInstance projectInstance = CreateTestProjectInstance(@"bin\FakesAssemblies", []);
var expectedInputFiles = new[]
{
new PredictedItem("A.fakes", nameof(FakesPredictor)),
new PredictedItem("B.fakes", nameof(FakesPredictor)),
new PredictedItem("C.fakes", nameof(FakesPredictor)),
};

new FakesPredictor()
.GetProjectPredictions(projectInstance)
.AssertNoPredictions();
}

private static ProjectInstance CreateTestProjectInstance(
string fakesOutputPath,
ReadOnlySpan<string> fakesItems)
{
ProjectRootElement projectRootElement = ProjectRootElement.Create();
projectRootElement.AddProperty(FakesPredictor.FakesImportedPropertyName, "true");

if (fakesOutputPath != null)
{
projectRootElement.AddProperty(FakesPredictor.FakesOutputPathPropertyName, fakesOutputPath);
}

foreach (string fakesItem in fakesItems)
{
projectRootElement.AddItem(FakesPredictor.FakesItemName, fakesItem);
}

return TestHelpers.CreateProjectInstanceFromRootElement(projectRootElement);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,51 @@ void AddPropertyToAllProjects(string propertyName, string propertyValue)
}
}

[Fact]
public void DependencyWithFakesAssemblies()
{
string projectFile = Path.Combine(_rootDir, @"src\project.csproj");
ProjectRootElement projectRootElement = ProjectRootElement.Create(projectFile);
projectRootElement.AddProperty(GetCopyToOutputDirectoryItemsGraphPredictor.OutDirPropertyName, @"bin\");

string dependencyProjectFile = Path.Combine(_rootDir, @"dep\dep.csproj");
ProjectRootElement dependencyProjectRootElement = ProjectRootElement.Create(dependencyProjectFile);
dependencyProjectRootElement.AddProperty(FakesPredictor.FakesImportedPropertyName, "true");
dependencyProjectRootElement.AddProperty(FakesPredictor.FakesUseV2GenerationPropertyName, "true");
dependencyProjectRootElement.AddProperty(FakesPredictor.FakesOutputPathPropertyName, @"bin\FakesAssemblies");
dependencyProjectRootElement.AddItem(FakesPredictor.FakesItemName, "A.fakes");
dependencyProjectRootElement.AddItem(FakesPredictor.FakesItemName, "B.fakes");
dependencyProjectRootElement.AddItem(FakesPredictor.FakesItemName, "C.fakes");

projectRootElement.AddItem("ProjectReference", @"..\dep\dep.csproj");

projectRootElement.Save();
dependencyProjectRootElement.Save();

var expectedInputFiles = new[]
{
new PredictedItem(@"dep\bin\FakesAssemblies\A.Fakes.dll", nameof(GetCopyToOutputDirectoryItemsGraphPredictor)),
new PredictedItem(@"dep\bin\FakesAssemblies\B.Fakes.dll", nameof(GetCopyToOutputDirectoryItemsGraphPredictor)),
new PredictedItem(@"dep\bin\FakesAssemblies\C.Fakes.dll", nameof(GetCopyToOutputDirectoryItemsGraphPredictor)),
};

var expectedOutputFiles = new[]
{
new PredictedItem(@"src\bin\A.Fakes.dll", nameof(GetCopyToOutputDirectoryItemsGraphPredictor)),
new PredictedItem(@"src\bin\B.Fakes.dll", nameof(GetCopyToOutputDirectoryItemsGraphPredictor)),
new PredictedItem(@"src\bin\C.Fakes.dll", nameof(GetCopyToOutputDirectoryItemsGraphPredictor)),
};

new GetCopyToOutputDirectoryItemsGraphPredictor()
.GetProjectPredictions(projectFile)
.AssertPredictions(
_rootDir,
expectedInputFiles,
null,
expectedOutputFiles,
null);
}

private ProjectRootElement CreateDependencyProject(string projectName, bool shouldCopy)
{
string projectDir = Path.Combine(_rootDir, projectName);
Expand Down
Loading