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
1 change: 1 addition & 0 deletions CentralPackageManagementMigrator.slnx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<Solution>
<Project Path="src/CentralPackageManagementMigrator/CentralPackageManagementMigrator.csproj" />
<Project Path="tests/CentralPackageManagementMigrator.IntegrationTests/CentralPackageManagementMigrator.IntegrationTests.csproj" />
<Project Path="tests/CentralPackageManagementMigrator.Tests/CentralPackageManagementMigrator.Tests.csproj" />
</Solution>
5 changes: 5 additions & 0 deletions global.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"test": {
"runner": "Microsoft.Testing.Platform"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,13 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.10" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="10.0.10" />
<PackageReference Include="System.CommandLine" Version="2.0.11" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.10"/>
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="10.0.10"/>
<PackageReference Include="System.CommandLine" Version="2.0.11"/>
</ItemGroup>

<ItemGroup>
<InternalsVisibleTo Include="$(MSBuildProjectName).IntegrationTests"/>
<InternalsVisibleTo Include="$(MSBuildProjectName).Tests"/>
</ItemGroup>

Expand Down
2 changes: 1 addition & 1 deletion src/CentralPackageManagementMigrator/LoggingUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public static void SetupLogging(LogLevel logLevel)
/// existing logs that haven't been output yet. Failure to call this
/// method may result in lost messages.
/// </summary>
public static void FlushLogging() => Factory?.Dispose();
public static void FlushLogging() => Factory.Dispose();

public static ILogger<T> CreateLogger<T>() => Factory.CreateLogger<T>();
}
12 changes: 11 additions & 1 deletion src/CentralPackageManagementMigrator/MigratorCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,17 @@ public MigratorCommand() : base(CommandDescription)
SetAction(parseResult =>
{
var logLevel = parseResult.GetRequiredValue(_logLevelOption);
return Migrate(logLevel);

try
{
return Migrate(logLevel);
}
catch
{
// Uncaught exception occurred. Indicate error.
}

return 1;
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<OutputType>Exe</OutputType>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<Using Include="Xunit"/>
</ItemGroup>

<ItemGroup>
<Content Include="TestData\**">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\CentralPackageManagementMigrator\CentralPackageManagementMigrator.csproj"/>
</ItemGroup>

<ItemGroup>
<PackageReference Include="xunit.v3" Version="4.0.0"/>
</ItemGroup>

</Project>
169 changes: 169 additions & 0 deletions tests/CentralPackageManagementMigrator.IntegrationTests/Helper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
using System.Reflection;

namespace CentralPackageManagementMigrator.IntegrationTests;

internal sealed class Helper : IDisposable
{
/// <summary>
/// Contains absolute paths to various directories used for the test.
/// </summary>
/// <param name="Base">
/// The temporary directory created for the requested test. Will serve as
/// the working directory for the migrator command to run in.
/// </param>
/// <param name="Actual">
/// Path to the <c>Actual</c> directory that contains setup files for the
/// test.
/// </param>
/// <param name="Expected">
/// Path to the <c>Expected</c> directory that contains expected files for
/// the test.
/// </param>
private readonly record struct WorkDirectory(string Base, string Actual, string Expected);

private WorkDirectory WorkDirectoryInfo { get; init; }

private bool _disposed;

private Helper()
{
}

~Helper()
{
Dispose(false);
}

public static Helper Create(string test)
{
var helper = new Helper
{
WorkDirectoryInfo = FindWorkDirectory(test)
};

// Copy actual files to the work directory.
CopyDirectory(helper.WorkDirectoryInfo.Actual, helper.WorkDirectoryInfo.Base);

return helper;
}

public async Task<int> RunMigrator()
{
Directory.SetCurrentDirectory(WorkDirectoryInfo.Base);

var command = new MigratorCommand();
return await command.Parse([]).InvokeAsync(null, TestContext.Current.CancellationToken);
}

public async Task AssertDirectoryPackagesFile()
{
var directoryPackagesPath = Path.Combine(WorkDirectoryInfo.Base, "Directory.Packages.props");

Assert.True(Path.Exists(directoryPackagesPath), $"'{directoryPackagesPath}' not found");

var actual = await File.ReadAllTextAsync(directoryPackagesPath, TestContext.Current.CancellationToken);
var expected = await File.ReadAllTextAsync(Path.Combine(WorkDirectoryInfo.Expected, "Directory.Packages.xml"),
TestContext.Current.CancellationToken);

Assert.Equal(actual, expected);
}

public async Task AssertProjectFile(string filename)
{
var actualFilename = Path.Combine(WorkDirectoryInfo.Base, filename + ".csproj");
Assert.True(Path.Exists(actualFilename), $"'{actualFilename}' not found");

var actual = await File.ReadAllTextAsync(actualFilename, TestContext.Current.CancellationToken);

var expectedFilename = Path.Combine(WorkDirectoryInfo.Expected, filename + ".xml");
Assert.True(Path.Exists(expectedFilename), $"'{expectedFilename}' not found");

var expected = await File.ReadAllTextAsync(expectedFilename, TestContext.Current.CancellationToken);

Assert.Equal(actual, expected);
}

private static WorkDirectory FindWorkDirectory(string test)
{
var assemblyDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
Assert.NotNull(assemblyDirectory);

// Ensure test data is set up correctly.

var testDataDirectory = Path.Combine(assemblyDirectory, "TestData", test);
Assert.True(Directory.Exists(testDataDirectory), $"'{testDataDirectory}' directory cannot be found");

var actualDirectory = Path.Combine(testDataDirectory, "Actual");
Assert.True(Directory.Exists(actualDirectory), "'Actual' subdirectory cannot be found");

var expectedDirectory = Path.Combine(testDataDirectory, "Expected");
Assert.True(Directory.Exists(expectedDirectory), "'Expected' subdirectory cannot be found");

var tempDirectory = Directory.CreateTempSubdirectory().FullName;

return new WorkDirectory(tempDirectory, actualDirectory, expectedDirectory);
}

/// <seealso href="https://learn.microsoft.com/en-us/dotnet/standard/io/how-to-copy-directories"/>
private static void CopyDirectory(string source, string destination)
{
var dir = new DirectoryInfo(source);
Assert.True(dir.Exists, $"Source directory '{source}' not found");

var dirs = dir.GetDirectories();

Directory.CreateDirectory(destination);

foreach (var file in dir.GetFiles())
{
string filename;

if (file.Name.StartsWith("project", StringComparison.InvariantCultureIgnoreCase))
{
filename = Path.GetFileNameWithoutExtension(file.Name) + ".csproj";
}
else if (file.Name.StartsWith("directory.packages", StringComparison.InvariantCultureIgnoreCase))
{
filename = Path.GetFileNameWithoutExtension(file.Name) + ".props";
}
else
{
filename = file.Name;
}

var target = Path.Combine(destination, filename);
file.CopyTo(target);
}

foreach (var subDirectory in dirs)
{
var newDestination = Path.Combine(destination, subDirectory.Name);
CopyDirectory(subDirectory.FullName, newDestination);
}
}

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

private void Dispose(bool disposing)
{
if (_disposed || !disposing)
{
return;
}

try
{
Directory.Delete(WorkDirectoryInfo.Base, true);
}
catch
{
// Let OS prune it eventually
}

_disposed = true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
namespace CentralPackageManagementMigrator.IntegrationTests;

public class IntegrationTests
{
[Fact]
public async Task Test001_BasicExample()
{
using var helper = Helper.Create("Test001");

var exitCode = await helper.RunMigrator();

Assert.Equal(0, exitCode);
await helper.AssertDirectoryPackagesFile();
await helper.AssertProjectFile("Project");
}

[Fact]
public async Task Test002_PackageVersionAsChildElement()
{
using var helper = Helper.Create("Test002");

var exitCode = await helper.RunMigrator();

Assert.Equal(0, exitCode);
await helper.AssertDirectoryPackagesFile();
await helper.AssertProjectFile("Project");
}

[Fact]
public async Task Test003_PackageNamesCaseInsensitive()
{
using var helper = Helper.Create("Test003");

var exitCode = await helper.RunMigrator();

Assert.Equal(0, exitCode);
await helper.AssertDirectoryPackagesFile();
await helper.AssertProjectFile("ProjectA");
await helper.AssertProjectFile("ProjectB");
}

[Fact]
public async Task Test004_DirectoryPackagesPropsAlreadyExists()
{
using var helper = Helper.Create("Test004");

var exitCode = await helper.RunMigrator();

Assert.Equal(1, exitCode);
await helper.AssertProjectFile("Project");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Integration Tests

Each scenario is stored under the [`TestData`](./TestData) directory as its own subdirectory.

Within it, there are two additional subdirectories: `Actual` and `Expected`. Everything in the `Actual` subdirectory is copied to a temporary directory for the tool to work from -- all files and subdirectories are recursively copied. After the tool is invoked, the test case can make assertions on the resulting files against those in the `Expected` directory for equality.

All the test files have had their extensions changed since having .CSPROJ files and .PROPS files could affect the solution. When these files are copied in preparation for the tool, their extensions are renamed to their intended use. `ProjectA.xml` becomes `ProjectA.csproj`, for example.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Contoso.Utility.UsefulStuff" Version="17.9.0" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Contoso.Utility.UsefulStuff" Version="17.9.0" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Contoso.Utility.UsefulStuff" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Contoso.Utility.UsefulStuff">
<Version>17.9.0</Version>
</PackageReference>
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Contoso.Utility.UsefulStuff" Version="17.9.0" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Contoso.Utility.UsefulStuff" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<PackageReference Include="NUnit" Version="4.3.2" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<PackageReference Include="nunit" Version="4.3.2" />
</ItemGroup>
</Project>
Loading