Skip to content
Open
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
29 changes: 29 additions & 0 deletions convert_teardown.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
param($filePath)

$content = Get-Content $filePath -Raw

# Check if file needs processing
if ($content -notmatch "public void TearDown\(\)") {
return $false
}

# Pattern 1: Find the class declaration and check if it already has IDisposable
$classPattern = 'public class Tests\s*(?::\s*IDisposable)?'
if ($content -match $classPattern) {
$classMatch = $Matches[0]
# Add IDisposable if not present
if ($classMatch -notmatch "IDisposable") {
$content = $content -replace 'public class Tests\s*\r?\n?\s*\{', "public class Tests : IDisposable`n`t{"
}
}

# Pattern 2: Convert TearDown() to Dispose()
$content = $content -replace 'public void TearDown\(\)', 'public void Dispose()'

# Pattern 3: Remove the TODO comment for TearDown
$content = $content -replace '\s*// TODO: Convert to IDisposable\.Dispose\(\).*?\r?\n', "`n"

# Save the file
Set-Content -Path $filePath -Value $content -NoNewline

return $true
91 changes: 91 additions & 0 deletions migrate-to-xunit.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Script to migrate NUnit tests to XUnit in the Xaml.UnitTests project

$projectPath = "C:\repos\dotnet\maui\src\Controls\tests\Xaml.UnitTests"

# Get all C# files excluding Generated, obj, bin directories
$csFiles = Get-ChildItem -Path $projectPath -Filter "*.cs" -Recurse | Where-Object {
$_.Directory.Name -ne 'obj' -and
$_.Directory.Name -ne 'bin' -and
$_.Directory.Name -ne 'Generated' -and
$_.FullName -notlike '*\obj\*' -and
$_.FullName -notlike '*\bin\*' -and
$_.FullName -notlike '*\Generated\*'
}

Write-Host "Processing $($csFiles.Count) files..."
$modifiedCount = 0

foreach ($file in $csFiles) {
$content = Get-Content $file.FullName -Raw
$originalContent = $content

# Skip if no NUnit references
if ($content -notmatch 'NUnit\.Framework') {
continue
}

# Replace using statements
$content = $content -replace 'using NUnit\.Framework;', 'using Xunit;'
$content = $content -replace 'using NUnit\.Framework\.Constraints;', 'using Xunit;'

# Replace attributes
$content = $content -replace '\[TestFixture\]', ''
$content = $content -replace '\[Test\]', '[Fact]'
$content = $content -replace '\[SetUp\]', '[MemberData(nameof(InitializeTest))] // TODO: Convert to IDisposable or constructor'
$content = $content -replace '\[TearDown\]', '// TODO: Convert to IDisposable.Dispose()'
$content = $content -replace '\[OneTimeSetUp\]', '// TODO: Convert to IClassFixture or static constructor'
$content = $content -replace '\[OneTimeTearDown\]', '// TODO: Convert to IClassFixture'
$content = $content -replace '\[SetUpFixture\]', '// TODO: Convert to AssemblyFixture or ICollectionFixture'

# Replace TestCase with InlineData
$content = $content -replace '\[TestCase\((.*?)\)\]', '[InlineData($1)]'

# Replace TestCaseSource with MemberData
$content = $content -replace '\[TestCaseSource\(nameof\((.*?)\)\)\]', '[MemberData(nameof($1))]'
$content = $content -replace '\[TestCaseSource\("(.*?)"\)\]', '[MemberData(nameof($1))]'

# Replace Category with Trait
$content = $content -replace '\[Category\("(.*?)"\)\]', '[Trait("Category", "$1")]'

# Replace Ignore with Skip
$content = $content -replace '\[Ignore\("(.*?)"\)\]', '[Fact(Skip = "$1")]'

# Replace common assertions
$content = $content -replace 'Assert\.AreEqual\(', 'Assert.Equal('
$content = $content -replace 'Assert\.AreNotEqual\(', 'Assert.NotEqual('
$content = $content -replace 'Assert\.AreSame\(', 'Assert.Same('
$content = $content -replace 'Assert\.AreNotSame\(', 'Assert.NotSame('
$content = $content -replace 'Assert\.IsTrue\(', 'Assert.True('
$content = $content -replace 'Assert\.IsFalse\(', 'Assert.False('
$content = $content -replace 'Assert\.IsNull\(', 'Assert.Null('
$content = $content -replace 'Assert\.IsNotNull\(', 'Assert.NotNull('
$content = $content -replace 'Assert\.IsEmpty\(', 'Assert.Empty('
$content = $content -replace 'Assert\.IsNotEmpty\(', 'Assert.NotEmpty('
$content = $content -replace 'Assert\.That\((.*?), Is\.TypeOf<(.*?)>\(\)\)', 'Assert.IsType<$2>($1)'
$content = $content -replace 'Assert\.That\((.*?), Is\.Not\.Null\)', 'Assert.NotNull($1)'
$content = $content -replace 'Assert\.That\((.*?), Is\.Null\)', 'Assert.Null($1)'
$content = $content -replace 'Assert\.That\((.*?), Is\.True\)', 'Assert.True($1)'
$content = $content -replace 'Assert\.That\((.*?), Is\.False\)', 'Assert.False($1)'
$content = $content -replace 'Assert\.That\((.*?), Is\.EqualTo\((.*?)\)\)', 'Assert.Equal($2, $1)'
$content = $content -replace 'Assert\.That\((.*?), Is\.Not\.EqualTo\((.*?)\)\)', 'Assert.NotEqual($2, $1)'

# Replace Assert.Throws patterns
$content = $content -replace 'Assert\.Throws<(.*?)>\(\(\) =>', 'Assert.Throws<$1>(() =>'
$content = $content -replace 'Assert\.Throws\(new (.*?)\(\), \(\) =>', '// TODO: XUnit does not support custom constraint objects like $1. Use Assert.Throws<ExceptionType>(() =>'
$content = $content -replace 'Assert\.DoesNotThrow\(\(\) =>', '// TODO: XUnit has no DoesNotThrow. Remove this or use try/catch if needed: // (() =>'

# Replace Contains assertions
$content = $content -replace 'Assert\.Contains\((.*?), (.*?)\)', 'Assert.Contains($1, $2)'

# Clean up multiple blank lines
$content = $content -replace '(\r?\n){3,}', "`r`n`r`n"

if ($content -ne $originalContent) {
Set-Content -Path $file.FullName -Value $content -NoNewline
$modifiedCount++
Write-Host "Modified: $($file.Name)"
}
}

Write-Host "`nMigration complete! Modified $modifiedCount files."
Write-Host "Please review TODOs and custom constraint replacements manually."
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,8 @@
Microsoft.Maui.Controls.Xaml.Internals.XamlDataTypeProvider
~Microsoft.Maui.Controls.Xaml.Internals.XamlDataTypeProvider.XamlDataTypeProvider(string dataType) -> void
~Microsoft.Maui.Controls.Xaml.Internals.XamlServiceProvider.XamlServiceProvider(object rootObject) -> void

Microsoft.Maui.Controls.Xaml.XamlInflator
Microsoft.Maui.Controls.Xaml.XamlInflator.Runtime = 1 -> Microsoft.Maui.Controls.Xaml.XamlInflator
Microsoft.Maui.Controls.Xaml.XamlInflator.SourceGen = 4 -> Microsoft.Maui.Controls.Xaml.XamlInflator
Microsoft.Maui.Controls.Xaml.XamlInflator.XamlC = 2 -> Microsoft.Maui.Controls.Xaml.XamlInflator
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,8 @@
Microsoft.Maui.Controls.Xaml.Internals.XamlDataTypeProvider
~Microsoft.Maui.Controls.Xaml.Internals.XamlDataTypeProvider.XamlDataTypeProvider(string dataType) -> void
~Microsoft.Maui.Controls.Xaml.Internals.XamlServiceProvider.XamlServiceProvider(object rootObject) -> void

Microsoft.Maui.Controls.Xaml.XamlInflator
Microsoft.Maui.Controls.Xaml.XamlInflator.Runtime = 1 -> Microsoft.Maui.Controls.Xaml.XamlInflator
Microsoft.Maui.Controls.Xaml.XamlInflator.SourceGen = 4 -> Microsoft.Maui.Controls.Xaml.XamlInflator
Microsoft.Maui.Controls.Xaml.XamlInflator.XamlC = 2 -> Microsoft.Maui.Controls.Xaml.XamlInflator
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,8 @@
Microsoft.Maui.Controls.Xaml.Internals.XamlDataTypeProvider
~Microsoft.Maui.Controls.Xaml.Internals.XamlDataTypeProvider.XamlDataTypeProvider(string dataType) -> void
~Microsoft.Maui.Controls.Xaml.Internals.XamlServiceProvider.XamlServiceProvider(object rootObject) -> void

Microsoft.Maui.Controls.Xaml.XamlInflator
Microsoft.Maui.Controls.Xaml.XamlInflator.Runtime = 1 -> Microsoft.Maui.Controls.Xaml.XamlInflator
Microsoft.Maui.Controls.Xaml.XamlInflator.SourceGen = 4 -> Microsoft.Maui.Controls.Xaml.XamlInflator
Microsoft.Maui.Controls.Xaml.XamlInflator.XamlC = 2 -> Microsoft.Maui.Controls.Xaml.XamlInflator
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,8 @@
Microsoft.Maui.Controls.Xaml.Internals.XamlDataTypeProvider
~Microsoft.Maui.Controls.Xaml.Internals.XamlDataTypeProvider.XamlDataTypeProvider(string dataType) -> void
~Microsoft.Maui.Controls.Xaml.Internals.XamlServiceProvider.XamlServiceProvider(object rootObject) -> void

Microsoft.Maui.Controls.Xaml.XamlInflator
Microsoft.Maui.Controls.Xaml.XamlInflator.Runtime = 1 -> Microsoft.Maui.Controls.Xaml.XamlInflator
Microsoft.Maui.Controls.Xaml.XamlInflator.SourceGen = 4 -> Microsoft.Maui.Controls.Xaml.XamlInflator
Microsoft.Maui.Controls.Xaml.XamlInflator.XamlC = 2 -> Microsoft.Maui.Controls.Xaml.XamlInflator
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ Microsoft.Maui.Controls.Xaml.XamlCompilationOptions.Compile = 2 -> Microsoft.Mau
Microsoft.Maui.Controls.Xaml.XamlCompilationOptions.Skip = 1 -> Microsoft.Maui.Controls.Xaml.XamlCompilationOptions
Microsoft.Maui.Controls.Xaml.XamlFilePathAttribute
Microsoft.Maui.Controls.Xaml.Internals.XamlDataTypeProvider
Microsoft.Maui.Controls.Xaml.XamlInflator
Microsoft.Maui.Controls.Xaml.XamlInflator.Runtime = 1 -> Microsoft.Maui.Controls.Xaml.XamlInflator
Microsoft.Maui.Controls.Xaml.XamlInflator.SourceGen = 4 -> Microsoft.Maui.Controls.Xaml.XamlInflator
Microsoft.Maui.Controls.Xaml.XamlInflator.XamlC = 2 -> Microsoft.Maui.Controls.Xaml.XamlInflator
~Microsoft.Maui.Controls.Xaml.AppThemeBindingExtension.Dark.get -> object
~Microsoft.Maui.Controls.Xaml.AppThemeBindingExtension.Dark.set -> void
~Microsoft.Maui.Controls.Xaml.AppThemeBindingExtension.Default.get -> object
Expand Down
5 changes: 5 additions & 0 deletions src/Controls/src/Xaml/PublicAPI/net/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,8 @@
Microsoft.Maui.Controls.Xaml.Internals.XamlDataTypeProvider
~Microsoft.Maui.Controls.Xaml.Internals.XamlDataTypeProvider.XamlDataTypeProvider(string dataType) -> void
~Microsoft.Maui.Controls.Xaml.Internals.XamlServiceProvider.XamlServiceProvider(object rootObject) -> void

Microsoft.Maui.Controls.Xaml.XamlInflator
Microsoft.Maui.Controls.Xaml.XamlInflator.Runtime = 1 -> Microsoft.Maui.Controls.Xaml.XamlInflator
Microsoft.Maui.Controls.Xaml.XamlInflator.SourceGen = 4 -> Microsoft.Maui.Controls.Xaml.XamlInflator
Microsoft.Maui.Controls.Xaml.XamlInflator.XamlC = 2 -> Microsoft.Maui.Controls.Xaml.XamlInflator
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,8 @@
Microsoft.Maui.Controls.Xaml.Internals.XamlDataTypeProvider
~Microsoft.Maui.Controls.Xaml.Internals.XamlDataTypeProvider.XamlDataTypeProvider(string dataType) -> void
~Microsoft.Maui.Controls.Xaml.Internals.XamlServiceProvider.XamlServiceProvider(object rootObject) -> void

Microsoft.Maui.Controls.Xaml.XamlInflator
Microsoft.Maui.Controls.Xaml.XamlInflator.Runtime = 1 -> Microsoft.Maui.Controls.Xaml.XamlInflator
Microsoft.Maui.Controls.Xaml.XamlInflator.SourceGen = 4 -> Microsoft.Maui.Controls.Xaml.XamlInflator
Microsoft.Maui.Controls.Xaml.XamlInflator.XamlC = 2 -> Microsoft.Maui.Controls.Xaml.XamlInflator
2 changes: 1 addition & 1 deletion src/Controls/src/Xaml/XamlInflator.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
namespace Microsoft.Maui.Controls.Xaml;

//used for unit testing switching, and internal use of the sourcegen
enum XamlInflator
public enum XamlInflator
{
Runtime = 1 << 0,
XamlC = 1 << 1,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
using System.Collections.Generic;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Controls.Core.UnitTests;
using NUnit.Framework;
using Xunit;

namespace Microsoft.Maui.Controls.Xaml.UnitTests
{
Expand All @@ -29,14 +29,15 @@ public AcceptEmptyServiceProvider()

public IServiceProvider ServiceProvider { get; set; }

[TestFixture]
class Tests

public class Tests
{
[Test]
public void ServiceProviderIsNullOnAttributedExtensions([Values] XamlInflator inflator)
[Theory]
[Values]
public void ServiceProviderIsNullOnAttributedExtensions(XamlInflator inflator)
{
var p = new AcceptEmptyServiceProvider(inflator);
Assert.IsNull(p.ServiceProvider);
Assert.Null(p.ServiceProvider);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
using System.Collections.Generic;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Controls.Core.UnitTests;
using NUnit.Framework;
using Xunit;

namespace Microsoft.Maui.Controls.Xaml.UnitTests
{
Expand Down
67 changes: 41 additions & 26 deletions src/Controls/tests/Xaml.UnitTests/AssemblyInfoTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
using System.Diagnostics;
using System.IO;
using System.Reflection;
using NUnit.Framework;
using Xunit;
using IOPath = System.IO.Path;

namespace Microsoft.Maui.Controls.MSBuild.UnitTests
{
[TestFixture]

public class AssemblyInfoTests
{
static readonly string[] references = new[]
public static readonly string[] references = new[]
{
"Microsoft.Maui",
"Microsoft.Maui.Controls",
Expand All @@ -25,23 +25,36 @@ public class AssemblyInfoTests

const string s_versionPropsFile = "eng/Versions.props";

[Test, TestCaseSource(nameof(references))]
public static TheoryData<string> ReferencesTheoryData
{
get
{
var data = new TheoryData<string>();
foreach (var reference in references)
data.Add(reference);
return data;
}
}

[Theory]
[MemberData(nameof(ReferencesTheoryData))]
public void AssemblyTitle(string assemblyName)
{
Assembly testAssembly = System.Reflection.Assembly.Load(assemblyName);
Assert.AreEqual(assemblyName, testAssembly.GetName().Name);
Assert.Equal(assemblyName, testAssembly.GetName().Name);
}

[Test, TestCaseSource(nameof(references))]
[Theory]
[MemberData(nameof(ReferencesTheoryData))]
public void AssemblyVersion(string assemblyName)
{
Assembly testAssembly = System.Reflection.Assembly.Load(assemblyName);
Version actual = testAssembly.GetName().Version;
// Currently we keep the assembly verison at 10.0.0.0
Assert.AreEqual(10, actual.Major, actual.ToString());
Assert.AreEqual(0, actual.Minor, actual.ToString());
Assert.AreEqual(0, actual.Build, actual.ToString());
Assert.AreEqual(0, actual.Revision, actual.ToString());
Assert.Equal(10, actual.Major);
Assert.Equal(0, actual.Minor);
Assert.Equal(0, actual.Build);
Assert.Equal(0, actual.Revision);
}

// [Test, TestCaseSource(nameof(references))]
Expand All @@ -56,28 +69,31 @@ public void AssemblyVersion(string assemblyName)
// var majorString = xml.SelectSingleNode("//MajorVersion").InnerText;
// var minorString = xml.SelectSingleNode("//MinorVersion").InnerText;
// Version expected = Version.Parse($"{majorString}.{minorString}.0.0");
// Assert.AreEqual(expected.Major, actual.FileMajorPart, $"FileMajorPart is wrong. {actual}");
// Assert.AreEqual(expected.Minor, actual.FileMinorPart, $"FileMinorPart is wrong. {actual}");
// Assert.Equal(expected.Major, actual.FileMajorPart, $"FileMajorPart is wrong. {actual}");
// Assert.Equal(expected.Minor, actual.FileMinorPart, $"FileMinorPart is wrong. {actual}");
// // Fails locally
// //Assert.AreEqual(expected.Build, actual.FileBuildPart, $"FileBuildPart is wrong. {actual.ToString()}");
// //Assert.Equal(expected.Build, actual.FileBuildPart, $"FileBuildPart is wrong. {actual.ToString()}");
// //We need to enable this
// // Assert.AreEqual(ThisAssembly.Git.Commits, version.FilePrivatePart);
// Assert.AreEqual(s_productName, actual.ProductName);
// Assert.AreEqual(s_company, actual.CompanyName);
// // Assert.Equal(ThisAssembly.Git.Commits, version.FilePrivatePart);
// Assert.Equal(s_productName, actual.ProductName);
// Assert.Equal(s_company, actual.CompanyName);
// }

[Test, TestCaseSource(nameof(references))]
[Theory]
[MemberData(nameof(ReferencesTheoryData))]
public void ProductAndCompany(string assemblyName)
{
Assembly testAssembly = System.Reflection.Assembly.Load(assemblyName);
FileVersionInfo actual = FileVersionInfo.GetVersionInfo(testAssembly.Location);
Assert.AreEqual(s_productName, actual.ProductName);
Assert.AreEqual(s_company, actual.CompanyName);
Assert.Equal(s_productName, actual.ProductName);
Assert.Equal(s_company, actual.CompanyName);
}

internal static string GetFilePathFromRoot(string file)
{
var fileFromRoot = IOPath.Combine(TestContext.CurrentContext.TestDirectory, "..", "..", "..", "..", "..", file);
var assemblyLocation = typeof(AssemblyInfoTests).Assembly.Location;
var testDirectory = IOPath.GetDirectoryName(assemblyLocation);
var fileFromRoot = IOPath.Combine(testDirectory, "..", "..", "..", "..", "..", file);
if (!File.Exists(fileFromRoot))
{
//NOTE: VSTS may be running tests in a staging directory, so we can use an environment variable to find the source
Expand All @@ -88,14 +104,13 @@ internal static string GetFilePathFromRoot(string file)
fileFromRoot = IOPath.Combine(sourcesDirectory, file);
if (!File.Exists(fileFromRoot))
{
Assert.Fail($"Unable to find {file} at path: {fileFromRoot}");
return null;
throw new InvalidOperationException($"Unable to find {file} at path: {fileFromRoot}");
}
}
else
{
Assert.Fail($"Unable to find {file} at path: {fileFromRoot}");
return null;
// TODO: XUnit doesn't have Assert.Fail, use Assert.True(false, ...) or throw
throw new InvalidOperationException($"Unable to find {file} at path: {fileFromRoot}");
}
}
return fileFromRoot;
Expand All @@ -106,8 +121,8 @@ internal static string GetFileFromRoot(string file)
var fileFromRootpath = GetFilePathFromRoot(file);
if (string.IsNullOrEmpty(fileFromRootpath))
{
Assert.Fail($"Unable to find {file} at path: {fileFromRootpath}");
return null;
// TODO: XUnit doesn't have Assert.Fail, use Assert.True(false, ...) or throw
throw new InvalidOperationException($"Unable to find {file} at path: {fileFromRootpath}");
}
return File.ReadAllText(fileFromRootpath);
}
Expand Down
Loading
Loading