From ef4ea449b2e0290c110eda20a1de6b4395013b91 Mon Sep 17 00:00:00 2001 From: Arlo Godfrey Date: Mon, 13 Jun 2022 16:33:57 -0500 Subject: [PATCH 01/15] Created UIControlTestMethod generator + unit tests --- ...enerators.UIControlTestMethod.Tests.csproj | 21 + .../UIControlTestMethodTests.cs | 363 ++++++++++++++++++ ...ourceGenerators.UIControlTestMethod.csproj | 14 + .../Diagnostics/DiagnosticDescriptors.cs | 59 +++ .../Extensions/ISymbolExtensions.cs | 122 ++++++ .../Extensions/ITypeSymbolExtensions.cs | 133 +++++++ .../UIControlTestMethodAttribute.cs | 22 ++ .../UIControlTestMethodlGenerator.cs | 116 ++++++ tests/Labs.Tests.props | 4 + 9 files changed, 854 insertions(+) create mode 100644 common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Tests/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Tests.csproj create mode 100644 common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Tests/UIControlTestMethodTests.cs create mode 100644 common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.csproj create mode 100644 common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/Diagnostics/DiagnosticDescriptors.cs create mode 100644 common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/Extensions/ISymbolExtensions.cs create mode 100644 common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/Extensions/ITypeSymbolExtensions.cs create mode 100644 common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/UIControlTestMethodAttribute.cs create mode 100644 common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/UIControlTestMethodlGenerator.cs diff --git a/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Tests/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Tests.csproj b/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Tests/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Tests.csproj new file mode 100644 index 000000000..e8fff1603 --- /dev/null +++ b/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Tests/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Tests.csproj @@ -0,0 +1,21 @@ + + + + net6.0 + enable + + false + + + + + + + + + + + + + + diff --git a/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Tests/UIControlTestMethodTests.cs b/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Tests/UIControlTestMethodTests.cs new file mode 100644 index 000000000..4483c565a --- /dev/null +++ b/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Tests/UIControlTestMethodTests.cs @@ -0,0 +1,363 @@ +using CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod; +using CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Diagnostics; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; + +namespace CommunityToolkit.Labs.Core.SourceGenerators.Tests +{ + [TestClass] + public partial class UIControlTestMethodTests + { + private const string AppDispatcherQueueDefinition = @" +namespace MyApp +{ + public class DispatcherQueue + { + public System.Threading.Tasks.Task EnqueueAsync(System.Action function) + { + return System.Threading.Tasks.Task.Run(function); + } + } + + public class App + { + public static DispatcherQueue DispatcherQueue { get; } = new DispatcherQueue(); + } +} +"; + + [TestMethod] + public void TypeDoesNotInheritFrameworkElement() + { + string source = @" + using System.ComponentModel; + using CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod; + + namespace MyApp + { + public partial class Test + { + public Windows.UI.Xaml.FrameworkElement? TestPage { get; private set; } + public System.Threading.Tasks.Task SetTestContentAsync(Windows.UI.Xaml.FrameworkElement content) => System.Threading.Tasks.Task.CompletedTask; + + [UIControlTestMethod(typeof(Test))] + public void TestMethod() + { + } + } + + public class MyControl : Windows.UI.Xaml.FrameworkElement + { + } + + namespace Windows.UI.Xaml + { + public class FrameworkElement { } + } + }"; + + VerifyGeneratedDiagnostics(source + AppDispatcherQueueDefinition, DiagnosticDescriptors.TypeDoesNotInheritFrameworkElement.Id); + } + + [TestMethod] + public void TypeDoesInheritFrameworkElement_Wux() + { + string source = @" + using System.ComponentModel; + using CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod; + + namespace MyApp + { + public partial class Test + { + public Windows.UI.Xaml.FrameworkElement? TestPage { get; private set; } + public System.Threading.Tasks.Task SetTestContentAsync(Windows.UI.Xaml.FrameworkElement content) => System.Threading.Tasks.Task.CompletedTask; + + [UIControlTestMethod(typeof(MyControl))] + public void TestMethod() + { + } + } + + public class MyControl : Windows.UI.Xaml.FrameworkElement + { + } + } + + namespace Windows.UI.Xaml + { + public class FrameworkElement { } + }"; + + VerifyGeneratedDiagnostics(source + AppDispatcherQueueDefinition); + } + + [TestMethod] + public void TypeDoesInheritFrameworkElement_Mux() + { + string source = @" + using System.ComponentModel; + using CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod; + + namespace MyApp + { + public partial class Test + { + public Microsoft.UI.Xaml.FrameworkElement? TestPage { get; private set; } + public System.Threading.Tasks.Task SetTestContentAsync(Microsoft.UI.Xaml.FrameworkElement content) => System.Threading.Tasks.Task.CompletedTask; + + [UIControlTestMethod(typeof(MyControl))] + public void TestMethod() + { + } + } + + public class MyControl : Microsoft.UI.Xaml.FrameworkElement + { + } + } + + namespace Microsoft.UI.Xaml + { + public class FrameworkElement { } + }"; + + VerifyGeneratedDiagnostics(source + AppDispatcherQueueDefinition); + } + + [TestMethod] + public void TestControlHasNoConstructorWithParameters() + { + string source = @" + using System.ComponentModel; + using CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod; + + namespace MyApp + { + public partial class Test + { + public Microsoft.UI.Xaml.FrameworkElement? TestPage { get; private set; } + public System.Threading.Tasks.Task SetTestContentAsync(Microsoft.UI.Xaml.FrameworkElement content) => System.Threading.Tasks.Task.CompletedTask; + + [UIControlTestMethod(typeof(MyControl))] + public void TestMethod() + { + } + } + + public class MyControl : Microsoft.UI.Xaml.FrameworkElement + { + } + } + + namespace Microsoft.UI.Xaml + { + public class FrameworkElement { } + }"; + + VerifyGeneratedDiagnostics(source + AppDispatcherQueueDefinition); + } + + [TestMethod] + public void TestControlHasConstructorWithParameters() + { + string source = @" + using System.ComponentModel; + using CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod; + + namespace MyApp + { + public partial class Test + { + public Microsoft.UI.Xaml.FrameworkElement? TestPage { get; private set; } + public System.Threading.Tasks.Task SetTestContentAsync(Microsoft.UI.Xaml.FrameworkElement content) => System.Threading.Tasks.Task.CompletedTask; + + [UIControlTestMethod(typeof(MyControl))] + public void TestMethod() + { + } + } + + public class MyControl : Microsoft.UI.Xaml.FrameworkElement + { + public MyControl(string id) + { + } + } + } + + namespace Microsoft.UI.Xaml + { + public class FrameworkElement { } + }"; + + VerifyGeneratedDiagnostics(source + AppDispatcherQueueDefinition, DiagnosticDescriptors.TestControlHasConstructorWithParameters.Id); + } + + [TestMethod] + public void TestMethodHasParameterlessConstructor() + { + string source = @" + using System.ComponentModel; + using CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod; + + namespace MyApp + { + public partial class Test + { + public Microsoft.UI.Xaml.FrameworkElement? TestPage { get; private set; } + public System.Threading.Tasks.Task SetTestContentAsync(Microsoft.UI.Xaml.FrameworkElement content) => System.Threading.Tasks.Task.CompletedTask; + + [UIControlTestMethod(typeof(MyControl))] + public void TestMethod() + { + } + } + + public class MyControl : Microsoft.UI.Xaml.FrameworkElement + { + } + } + + namespace Microsoft.UI.Xaml + { + public class FrameworkElement { } + }"; + + VerifyGeneratedDiagnostics(source + AppDispatcherQueueDefinition); + } + + [TestMethod] + public void TestMethodDoesNotHaveParameterlessConstructor() + { + string source = @" + using System.ComponentModel; + using CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod; + + namespace MyApp + { + public partial class Test + { + public Microsoft.UI.Xaml.FrameworkElement? TestPage { get; private set; } + public System.Threading.Tasks.Task SetTestContentAsync(Microsoft.UI.Xaml.FrameworkElement content) => System.Threading.Tasks.Task.CompletedTask; + + [UIControlTestMethod(typeof(MyControl))] + public void TestMethod(string id) + { + } + } + + public class MyControl : Microsoft.UI.Xaml.FrameworkElement + { + public MyControl() + { + } + } + } + + namespace Microsoft.UI.Xaml + { + public class FrameworkElement { } + }"; + + VerifyGeneratedDiagnostics(source + AppDispatcherQueueDefinition, DiagnosticDescriptors.TestMethodIsNotParameterless.Id); + } + + [TestMethod] + public void AsyncMethod_NoErrors() + { + string source = @" + using System.ComponentModel; + using CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod; + + namespace MyApp + { + public partial class Test + { + public Microsoft.UI.Xaml.FrameworkElement? TestPage { get; private set; } + public System.Threading.Tasks.Task SetTestContentAsync(Microsoft.UI.Xaml.FrameworkElement content) => System.Threading.Tasks.Task.CompletedTask; + + [UIControlTestMethod(typeof(MyControl))] + public async System.Threading.Tasks.Task TestMethod() + { + } + } + + public class MyControl : Microsoft.UI.Xaml.FrameworkElement + { + } + } + + namespace Microsoft.UI.Xaml + { + public class FrameworkElement { } + }"; + + VerifyGeneratedDiagnostics(source + AppDispatcherQueueDefinition); + } + + /// + /// Verifies the output of a source generator. + /// + /// The generator type to use. + /// The input source to process. + /// The input documentation info to process. + /// The diagnostic ids to expect for the input source code. + private static void VerifyGeneratedDiagnostics(string source, params string[] diagnosticsIds) + where TGenerator : class, IIncrementalGenerator, new() + { + VerifyGeneratedDiagnostics(CSharpSyntaxTree.ParseText(source), diagnosticsIds); + } + + /// + /// Verifies the output of a source generator. + /// + /// The generator type to use. + /// The input source tree to process. + /// The input documentation info to process. + /// The diagnostic ids to expect for the input source code. + private static void VerifyGeneratedDiagnostics(SyntaxTree syntaxTree, params string[] diagnosticsIds) + where TGenerator : class, IIncrementalGenerator, new() + { + var attributeType = typeof(UIControlTestMethodAttribute); + + var references = + from assembly in AppDomain.CurrentDomain.GetAssemblies() + where !assembly.IsDynamic + let reference = MetadataReference.CreateFromFile(assembly.Location) + select reference; + + var compilation = CSharpCompilation.Create( + "original.Sample", + new[] { syntaxTree }, + references, + new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); + + var compilationDiagnostics = compilation.GetDiagnostics(); + + Assert.IsTrue(compilationDiagnostics.All(x => x.Severity != DiagnosticSeverity.Error), $"Expected no compilation errors. Got: \n[{string.Join("\n", compilationDiagnostics.Where(x => x.Severity == DiagnosticSeverity.Error).Select(x => $"{x.Id}: {x.GetMessage()}"))}]"); + + IIncrementalGenerator generator = new TGenerator(); + + GeneratorDriver driver = + CSharpGeneratorDriver + .Create(generator) + .WithUpdatedParseOptions((CSharpParseOptions)syntaxTree.Options); + + _ = driver.RunGeneratorsAndUpdateCompilation(compilation, out Compilation outputCompilation, out ImmutableArray diagnostics); + + HashSet resultingIds = diagnostics.Select(diagnostic => diagnostic.Id).ToHashSet(); + var generatedCompilationDiaghostics = outputCompilation.GetDiagnostics(); + + Assert.IsTrue(resultingIds.SetEquals(diagnosticsIds), $"Expected one of [{string.Join(", ", diagnosticsIds)}] diagnostic Ids. Got [{string.Join(", ", resultingIds)}]"); + Assert.IsTrue(generatedCompilationDiaghostics.All(x => x.Severity != DiagnosticSeverity.Error), $"Expected no generated compilation errors. Got: \n[{string.Join("\n", generatedCompilationDiaghostics.Where(x => x.Severity == DiagnosticSeverity.Error).Select(x => $"{x.Id}: {x.GetMessage()}"))}]"); + + GC.KeepAlive(attributeType); + } + } +} diff --git a/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.csproj b/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.csproj new file mode 100644 index 000000000..9b3c8d237 --- /dev/null +++ b/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.csproj @@ -0,0 +1,14 @@ + + + + netstandard2.0 + enable + nullable + 10.0 + + + + + + + diff --git a/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/Diagnostics/DiagnosticDescriptors.cs b/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/Diagnostics/DiagnosticDescriptors.cs new file mode 100644 index 000000000..8d2fe6dc6 --- /dev/null +++ b/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/Diagnostics/DiagnosticDescriptors.cs @@ -0,0 +1,59 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis; + +namespace CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Diagnostics +{ + /// + /// A container for all instances for errors reported by analyzers in this project. + /// + public static class DiagnosticDescriptors + { + /// + /// Gets a indicating the provided isn't a valid FrameworkElement. + /// + /// Format: "Cannot generate test with type {0} as it does not inherit from FrameworkElement". + /// + /// + public static readonly DiagnosticDescriptor TypeDoesNotInheritFrameworkElement = new( + id: "UICTRLTM0001", + title: $"Invalid UI test control type", + messageFormat: $"Cannot generate test with type {{0}} as it does not inherit from FrameworkElement", + category: typeof(UIControlTestMethodGenerator).FullName, + defaultSeverity: DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: $"Cannot generate test method."); + + /// + /// Gets a indicating the provided is a type has non-parameterless constructor. + /// + /// Format: "Cannot generate test with type {0} as it has a constructor with parameters.". + /// + /// + public static readonly DiagnosticDescriptor TestControlHasConstructorWithParameters = new( + id: "UICTRLTM0002", + title: $"Provided test control must not have a constructor with parameters.", + messageFormat: $"Cannot generate test with type {{0}} as it has a constructor with parameters.", + category: typeof(UIControlTestMethodGenerator).FullName, + defaultSeverity: DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: $"Cannot generate test method."); + + /// + /// Gets a indicating the attached test method is not parameterless. + /// + /// Format: "Cannot generate test as the attached method is not parameterless.". + /// + /// + public static readonly DiagnosticDescriptor TestMethodIsNotParameterless = new( + id: "UICTRLTM0003", + title: $"Attached test method must not take any parameters.", + messageFormat: $"Cannot generate test as the attached method is not parameterless.", + category: typeof(UIControlTestMethodGenerator).FullName, + defaultSeverity: DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: $"Cannot generate test method."); + } +} diff --git a/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/Extensions/ISymbolExtensions.cs b/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/Extensions/ISymbolExtensions.cs new file mode 100644 index 000000000..5dc0570c7 --- /dev/null +++ b/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/Extensions/ISymbolExtensions.cs @@ -0,0 +1,122 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; + +namespace CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Extensions; + +/// +/// Extension methods for the type. +/// +/// +/// Borrowed from ISymbolExtensions in the dotnet Toolkit's CommunityToolkit.Mvvm.SourceGenerators project. +/// +internal static class ISymbolExtensions +{ + /// + /// Gets the fully qualified name for a given symbol. + /// + /// The input instance. + /// The fully qualified name for . + public static string GetFullyQualifiedName(this ISymbol symbol) + { + return symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); + } + + /// + /// Gets the fully qualified name for a given symbol, including nullability annotations + /// + /// The input instance. + /// The fully qualified name for . + public static string GetFullyQualifiedNameWithNullabilityAnnotations(this ISymbol symbol) + { + return symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat.AddMiscellaneousOptions(SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier)); + } + + /// + /// Checks whether or not a given type symbol has a specified full name. + /// + /// The input instance to check. + /// The full name to check. + /// Whether has a full name equals to . + public static bool HasFullyQualifiedName(this ISymbol symbol, string name) + { + return symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat) == name; + } + + /// + /// Checks whether or not a given symbol has an attribute with the specified full name. + /// + /// The input instance to check. + /// The attribute name to look for. + /// Whether or not has an attribute with the specified name. + public static bool HasAttributeWithFullyQualifiedName(this ISymbol symbol, string name) + { + ImmutableArray attributes = symbol.GetAttributes(); + + foreach (AttributeData attribute in attributes) + { + if (attribute.AttributeClass?.HasFullyQualifiedName(name) == true) + { + return true; + } + } + + return false; + } + + /// + /// Calculates the effective accessibility for a given symbol. + /// + /// The instance to check. + /// The effective accessibility for . + public static Accessibility GetEffectiveAccessibility(this ISymbol symbol) + { + // Start by assuming it's visible + Accessibility visibility = Accessibility.Public; + + // Handle special cases + switch (symbol.Kind) + { + case SymbolKind.Alias: return Accessibility.Private; + case SymbolKind.Parameter: return GetEffectiveAccessibility(symbol.ContainingSymbol); + case SymbolKind.TypeParameter: return Accessibility.Private; + } + + // Traverse the symbol hierarchy to determine the effective accessibility + while (symbol is not null && symbol.Kind != SymbolKind.Namespace) + { + switch (symbol.DeclaredAccessibility) + { + case Accessibility.NotApplicable: + case Accessibility.Private: + return Accessibility.Private; + case Accessibility.Internal: + case Accessibility.ProtectedAndInternal: + visibility = Accessibility.Internal; + break; + } + + symbol = symbol.ContainingSymbol; + } + + return visibility; + } + + /// + /// Checks whether or not a given symbol can be accessed from a specified assembly. + /// + /// The input instance to check. + /// The assembly to check the accessibility of for. + /// Whether can access . + public static bool CanBeAccessedFrom(this ISymbol symbol, IAssemblySymbol assembly) + { + Accessibility accessibility = symbol.GetEffectiveAccessibility(); + + return + accessibility == Accessibility.Public || + accessibility == Accessibility.Internal && symbol.ContainingAssembly.GivesAccessTo(assembly); + } +} diff --git a/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/Extensions/ITypeSymbolExtensions.cs b/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/Extensions/ITypeSymbolExtensions.cs new file mode 100644 index 000000000..657dcccb5 --- /dev/null +++ b/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/Extensions/ITypeSymbolExtensions.cs @@ -0,0 +1,133 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Linq; +using Microsoft.CodeAnalysis; + +namespace CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Extensions; + +/// +/// Extension methods for the type. +/// +/// Borrowed from ITypeSymbolExtensions in the dotnet Toolkit's CommunityToolkit.Mvvm.SourceGenerators project. +/// +internal static class ITypeSymbolExtensions +{ + /// + /// Checks whether or not a given has or inherits from a specified type. + /// + /// The target instance to check. + /// The full name of the type to check for inheritance. + /// Whether or not is or inherits from . + public static bool HasOrInheritsFromFullyQualifiedName(this ITypeSymbol typeSymbol, string name) + { + for (var currentType = typeSymbol; currentType is not null; currentType = currentType.BaseType) + { + if (currentType.HasFullyQualifiedName(name)) + { + return true; + } + } + + return false; + } + + /// + /// Checks whether or not a given inherits from a specified type. + /// + /// The target instance to check. + /// The full name of the type to check for inheritance. + /// Whether or not inherits from . + public static bool InheritsFromFullyQualifiedName(this ITypeSymbol typeSymbol, string name) + { + var baseType = typeSymbol.BaseType; + + while (baseType != null) + { + if (baseType.HasFullyQualifiedName(name)) + { + return true; + } + + baseType = baseType.BaseType; + } + + return false; + } + + /// + /// Checks whether or not a given implements an interface with a specified name. + /// + /// The target instance to check. + /// The full name of the type to check for interface implementation. + /// Whether or not has an interface with the specified name. + public static bool HasInterfaceWithFullyQualifiedName(this ITypeSymbol typeSymbol, string name) + { + foreach (var interfaceType in typeSymbol.AllInterfaces) + { + if (interfaceType.HasFullyQualifiedName(name)) + { + return true; + } + } + + return false; + } + + /// + /// Checks whether or not a given has or inherits a specified attribute. + /// + /// The target instance to check. + /// The predicate used to match available attributes. + /// Whether or not has an attribute matching . + public static bool HasOrInheritsAttribute(this ITypeSymbol typeSymbol, Func predicate) + { + for (var currentType = typeSymbol; currentType is not null; currentType = currentType.BaseType) + { + if (currentType.GetAttributes().Any(predicate)) + { + return true; + } + } + + return false; + } + + /// + /// Checks whether or not a given has or inherits a specified attribute. + /// + /// The target instance to check. + /// The name of the attribute to look for. + /// Whether or not has an attribute with the specified type name. + public static bool HasOrInheritsAttributeWithFullyQualifiedName(this ITypeSymbol typeSymbol, string name) + { + for (var currentType = typeSymbol; currentType is not null; currentType = currentType.BaseType) + { + if (currentType.HasAttributeWithFullyQualifiedName(name)) + { + return true; + } + } + + return false; + } + + /// + /// Checks whether or not a given inherits a specified attribute. + /// If the type has no base type, this method will automatically handle that and return . + /// + /// The target instance to check. + /// The name of the attribute to look for. + /// Whether or not has an attribute with the specified type name. + public static bool InheritsAttributeWithFullyQualifiedName(this ITypeSymbol typeSymbol, string name) + { + if (typeSymbol.BaseType is INamedTypeSymbol baseTypeSymbol) + { + return baseTypeSymbol.HasOrInheritsAttributeWithFullyQualifiedName(name); + } + + return false; + } +} diff --git a/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/UIControlTestMethodAttribute.cs b/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/UIControlTestMethodAttribute.cs new file mode 100644 index 000000000..ce78fabeb --- /dev/null +++ b/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/UIControlTestMethodAttribute.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod; + +/// +/// Generates a test method that provides an instance of the given FrameworkElement type +/// and auto-dispatches method contents to the UI thread. +/// +[AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)] +public sealed class UIControlTestMethodAttribute : Attribute +{ + public UIControlTestMethodAttribute(Type type) + { + Type = type; + } + + public Type Type { get; } +} diff --git a/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/UIControlTestMethodlGenerator.cs b/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/UIControlTestMethodlGenerator.cs new file mode 100644 index 000000000..14c9d8a97 --- /dev/null +++ b/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/UIControlTestMethodlGenerator.cs @@ -0,0 +1,116 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Diagnostics; +using CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Extensions; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System.Linq; + +namespace CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod; + +/// +/// Generates code that provides access to XAML elements with x:Name from code-behind by wrapping an instance of a control, without the need to use x:FieldProvider="public" directly in markup. +/// +[Generator] +public class UIControlTestMethodGenerator : IIncrementalGenerator +{ + public void Initialize(IncrementalGeneratorInitializationContext context) + { + // Get all method declarations with at least one attribute + var methodSymbols = context.SyntaxProvider + .CreateSyntaxProvider( + static (node, _) => node is MethodDeclarationSyntax { Parent: ClassDeclarationSyntax, AttributeLists.Count: > 0 }, + static (context, token) => context.SemanticModel.GetDeclaredSymbol((MethodDeclarationSyntax)context.Node, cancellationToken: token)) + .Where(x => x is not null) + .Select((x, _) => x!); + + // Filter the methods using [UIControlTestMethod] + var methodAndPageTypeSymbols = methodSymbols + .Select(static (item, _) => + ( + Symbol: item, + Attribute: item.GetAttributes().FirstOrDefault(a => a.AttributeClass?.HasFullyQualifiedName("global::CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.UIControlTestMethodAttribute") ?? false) + )) + + .Where(static item => item.Attribute is not null && item.Symbol is IMethodSymbol) + .Select(static (x, _) => (MethodSymbol: (IMethodSymbol)x.Symbol, Attribute: x.Attribute!)) + + .Where(static x => !x.Attribute.ConstructorArguments.IsEmpty) + .Select(static (x, _) => (x.MethodSymbol, ControlTypeSymbol: GetControlTypeSymbolFromAttribute(x.Attribute))) + + .Where(static x => x.ControlTypeSymbol is not null) + .Select(static (x, _) => (x.MethodSymbol, ControlTypeSymbol: x.ControlTypeSymbol!)); + + // Generate source + context.RegisterSourceOutput(methodAndPageTypeSymbols, (x, y) => GenerateTestMethod(x, y.MethodSymbol, y.ControlTypeSymbol)); + } + + private static void GenerateTestMethod(SourceProductionContext context, IMethodSymbol methodSymbol, INamedTypeSymbol controlTypeSymbol) + { + if (!ControlTypeInheritsFrameworkElement(controlTypeSymbol)) + { + context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.TypeDoesNotInheritFrameworkElement, methodSymbol.Locations.FirstOrDefault(), controlTypeSymbol.Name)); + return; + } + + if (controlTypeSymbol.Constructors.Any(x => !x.Parameters.IsEmpty)) + { + context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.TestControlHasConstructorWithParameters, methodSymbol.Locations.FirstOrDefault(), controlTypeSymbol.Name)); + return; + } + + if (!methodSymbol.Parameters.IsEmpty) + { + context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.TestMethodIsNotParameterless, methodSymbol.Locations.FirstOrDefault(), controlTypeSymbol.Name)); + return; + } + + var isAsync = methodSymbol.ReturnType.HasFullyQualifiedName("global::System.Threading.Tasks.Task") || + methodSymbol.ReturnType.InheritsFromFullyQualifiedName("global::System.Threading.Tasks.Task"); + + var source = $@"using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace {methodSymbol.ContainingType.ContainingNamespace} +{{ + partial class {methodSymbol.ContainingType.Name} + {{ + [TestMethod] + public Task {methodSymbol.Name}_Test() + {{ + return App.DispatcherQueue.EnqueueAsync(async () => {{ + TestPage = new {controlTypeSymbol.GetFullyQualifiedName()}(); + + // Set content + Task result = SetTestContentAsync(TestPage); + await result; + + Assert.IsTrue(result.IsCompletedSuccessfully, $""Failed to load page {controlTypeSymbol.GetFullyQualifiedName()} for test { methodSymbol.Name } with Exception: {{ result.Exception?.Message}} ""); + + // Call original + {(isAsync ? "await " : string.Empty)}{methodSymbol.Name}(); + + TestPage = null; + }}); + }} + }} +}} +"; + + context.AddSource($"{methodSymbol}.g", source); + } + + private static bool ControlTypeInheritsFrameworkElement(INamedTypeSymbol controlType) + { + return controlType.HasOrInheritsFromFullyQualifiedName("global::Windows.UI.Xaml.FrameworkElement") || + controlType.HasOrInheritsFromFullyQualifiedName("global::Microsoft.UI.Xaml.FrameworkElement"); + } + + private static INamedTypeSymbol? GetControlTypeSymbolFromAttribute(AttributeData attribute) + { + return attribute.ConstructorArguments.FirstOrDefault(x => x.Kind == TypedConstantKind.Type).Value as INamedTypeSymbol; + } +} + diff --git a/tests/Labs.Tests.props b/tests/Labs.Tests.props index e85d63e0d..b7026a690 100644 --- a/tests/Labs.Tests.props +++ b/tests/Labs.Tests.props @@ -4,8 +4,12 @@ 2.2.8 + 2.2.8 + + From 48e1d818ff57a6254a452f83bb9311f68eb6050f Mon Sep 17 00:00:00 2001 From: Arlo Godfrey Date: Mon, 13 Jun 2022 16:45:28 -0500 Subject: [PATCH 02/15] Added new source generators to template --- common/Toolkit.Labs.All.sln.template | 118 +++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) diff --git a/common/Toolkit.Labs.All.sln.template b/common/Toolkit.Labs.All.sln.template index 4a7cd5ad9..ae61ea3dd 100644 --- a/common/Toolkit.Labs.All.sln.template +++ b/common/Toolkit.Labs.All.sln.template @@ -54,6 +54,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Labs.UnitT EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Experiments", "Experiments", "{DD69BA61-C86D-4138-AE6F-76E2C8445C9A}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod", "common\CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod\CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.csproj", "{CA16C45E-DFB1-4641-A28D-EC52B6FB370A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Tests", "common\CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Tests\CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Tests.csproj", "{6961F49B-705D-47E0-81C3-D8CF7741C3E4}" +EndProject [TemplatedProjectDefinitions] Global GlobalSection(SharedMSBuildProjectFiles) = preSolution @@ -996,6 +1000,118 @@ Global {53892F07-FE54-4E36-81D8-105427D097E5}.Release|x86.ActiveCfg = Release|x86 {53892F07-FE54-4E36-81D8-105427D097E5}.Release|x86.Build.0 = Release|x86 {53892F07-FE54-4E36-81D8-105427D097E5}.Release|x86.Deploy.0 = Release|x86 + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Ad-Hoc|ARM64.ActiveCfg = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Ad-Hoc|ARM64.Build.0 = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.AppStore|ARM.Build.0 = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.AppStore|ARM64.ActiveCfg = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.AppStore|ARM64.Build.0 = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.AppStore|iPhone.Build.0 = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.AppStore|x64.ActiveCfg = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.AppStore|x64.Build.0 = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.AppStore|x86.ActiveCfg = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.AppStore|x86.Build.0 = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Debug|ARM.ActiveCfg = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Debug|ARM.Build.0 = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Debug|ARM64.Build.0 = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Debug|iPhone.Build.0 = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Debug|x64.ActiveCfg = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Debug|x64.Build.0 = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Debug|x86.ActiveCfg = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Debug|x86.Build.0 = Debug|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Release|Any CPU.Build.0 = Release|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Release|ARM.ActiveCfg = Release|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Release|ARM.Build.0 = Release|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Release|ARM64.ActiveCfg = Release|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Release|ARM64.Build.0 = Release|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Release|iPhone.ActiveCfg = Release|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Release|iPhone.Build.0 = Release|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Release|x64.ActiveCfg = Release|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Release|x64.Build.0 = Release|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Release|x86.ActiveCfg = Release|Any CPU + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A}.Release|x86.Build.0 = Release|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Ad-Hoc|Any CPU.ActiveCfg = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Ad-Hoc|Any CPU.Build.0 = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Ad-Hoc|ARM.ActiveCfg = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Ad-Hoc|ARM.Build.0 = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Ad-Hoc|ARM64.ActiveCfg = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Ad-Hoc|ARM64.Build.0 = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Ad-Hoc|iPhone.ActiveCfg = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Ad-Hoc|iPhone.Build.0 = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Ad-Hoc|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Ad-Hoc|iPhoneSimulator.Build.0 = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Ad-Hoc|x64.ActiveCfg = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Ad-Hoc|x64.Build.0 = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Ad-Hoc|x86.ActiveCfg = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Ad-Hoc|x86.Build.0 = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.AppStore|Any CPU.ActiveCfg = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.AppStore|Any CPU.Build.0 = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.AppStore|ARM.ActiveCfg = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.AppStore|ARM.Build.0 = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.AppStore|ARM64.ActiveCfg = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.AppStore|ARM64.Build.0 = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.AppStore|iPhone.ActiveCfg = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.AppStore|iPhone.Build.0 = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.AppStore|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.AppStore|iPhoneSimulator.Build.0 = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.AppStore|x64.ActiveCfg = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.AppStore|x64.Build.0 = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.AppStore|x86.ActiveCfg = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.AppStore|x86.Build.0 = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Debug|ARM.ActiveCfg = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Debug|ARM.Build.0 = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Debug|ARM64.Build.0 = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Debug|iPhone.ActiveCfg = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Debug|iPhone.Build.0 = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Debug|iPhoneSimulator.ActiveCfg = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Debug|iPhoneSimulator.Build.0 = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Debug|x64.ActiveCfg = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Debug|x64.Build.0 = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Debug|x86.ActiveCfg = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Debug|x86.Build.0 = Debug|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Release|Any CPU.Build.0 = Release|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Release|ARM.ActiveCfg = Release|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Release|ARM.Build.0 = Release|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Release|ARM64.ActiveCfg = Release|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Release|ARM64.Build.0 = Release|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Release|iPhone.ActiveCfg = Release|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Release|iPhone.Build.0 = Release|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Release|iPhoneSimulator.ActiveCfg = Release|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Release|iPhoneSimulator.Build.0 = Release|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Release|x64.ActiveCfg = Release|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Release|x64.Build.0 = Release|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Release|x86.ActiveCfg = Release|Any CPU + {6961F49B-705D-47E0-81C3-D8CF7741C3E4}.Release|x86.Build.0 = Release|Any CPU [TemplatedProjectConfigurations] EndGlobalSection GlobalSection(SolutionProperties) = preSolution @@ -1019,6 +1135,8 @@ Global {FD78002E-C4E6-4BF8-9EC3-C06250DFEF34} = {FF878CF0-59B1-4B8C-A7DB-1E2A7B47575A} {53892F07-FE54-4E36-81D8-105427D097E5} = {FF878CF0-59B1-4B8C-A7DB-1E2A7B47575A} {DD69BA61-C86D-4138-AE6F-76E2C8445C9A} = {FF878CF0-59B1-4B8C-A7DB-1E2A7B47575A} + {CA16C45E-DFB1-4641-A28D-EC52B6FB370A} = {09003B35-7A35-4BD1-9A26-5CFD02AB88DD} + {6961F49B-705D-47E0-81C3-D8CF7741C3E4} = {09003B35-7A35-4BD1-9A26-5CFD02AB88DD} [TemplatedProjectFolderConfig] EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution From 6f69e44b0c11f7250b3320304e03db7169e4ca6c Mon Sep 17 00:00:00 2001 From: Arlo Godfrey Date: Mon, 13 Jun 2022 18:57:42 -0500 Subject: [PATCH 03/15] Migrated existing tests to new source generator attribute --- .../UIControlTestMethodTests.cs | 34 ++--- .../UIControlTestMethodlGenerator.cs | 9 +- .../App.xaml.cs | 16 +-- ...ityToolkit.Labs.UnitTests.Shared.projitems | 1 - .../TestPageAttribute.cs | 26 ---- .../VisualUITestBase.cs | 122 +++++++----------- .../ExampleSizerBaseTestClass.cs | 46 +++---- tests/Labs.Tests.props | 2 +- 8 files changed, 86 insertions(+), 170 deletions(-) delete mode 100644 common/CommunityToolkit.Labs.UnitTests.Shared/TestPageAttribute.cs diff --git a/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Tests/UIControlTestMethodTests.cs b/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Tests/UIControlTestMethodTests.cs index 4483c565a..dd9a56246 100644 --- a/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Tests/UIControlTestMethodTests.cs +++ b/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Tests/UIControlTestMethodTests.cs @@ -13,20 +13,14 @@ namespace CommunityToolkit.Labs.Core.SourceGenerators.Tests [TestClass] public partial class UIControlTestMethodTests { - private const string AppDispatcherQueueDefinition = @" + private const string DispatcherQueueDefinition = @" namespace MyApp { - public class DispatcherQueue + public partial class Test { - public System.Threading.Tasks.Task EnqueueAsync(System.Action function) - { - return System.Threading.Tasks.Task.Run(function); - } - } + public System.Threading.Tasks.Task EnqueueAsync(System.Func> function) => System.Threading.Tasks.Task.Run(function); - public class App - { - public static DispatcherQueue DispatcherQueue { get; } = new DispatcherQueue(); + public System.Threading.Tasks.Task EnqueueAsync(System.Action function) => System.Threading.Tasks.Task.Run(function); } } "; @@ -61,7 +55,7 @@ public class FrameworkElement { } } }"; - VerifyGeneratedDiagnostics(source + AppDispatcherQueueDefinition, DiagnosticDescriptors.TypeDoesNotInheritFrameworkElement.Id); + VerifyGeneratedDiagnostics(source + DispatcherQueueDefinition, DiagnosticDescriptors.TypeDoesNotInheritFrameworkElement.Id); } [TestMethod] @@ -94,7 +88,7 @@ namespace Windows.UI.Xaml public class FrameworkElement { } }"; - VerifyGeneratedDiagnostics(source + AppDispatcherQueueDefinition); + VerifyGeneratedDiagnostics(source + DispatcherQueueDefinition); } [TestMethod] @@ -127,7 +121,7 @@ namespace Microsoft.UI.Xaml public class FrameworkElement { } }"; - VerifyGeneratedDiagnostics(source + AppDispatcherQueueDefinition); + VerifyGeneratedDiagnostics(source + DispatcherQueueDefinition); } [TestMethod] @@ -160,7 +154,7 @@ namespace Microsoft.UI.Xaml public class FrameworkElement { } }"; - VerifyGeneratedDiagnostics(source + AppDispatcherQueueDefinition); + VerifyGeneratedDiagnostics(source + DispatcherQueueDefinition); } [TestMethod] @@ -196,7 +190,7 @@ namespace Microsoft.UI.Xaml public class FrameworkElement { } }"; - VerifyGeneratedDiagnostics(source + AppDispatcherQueueDefinition, DiagnosticDescriptors.TestControlHasConstructorWithParameters.Id); + VerifyGeneratedDiagnostics(source + DispatcherQueueDefinition, DiagnosticDescriptors.TestControlHasConstructorWithParameters.Id); } [TestMethod] @@ -229,7 +223,7 @@ namespace Microsoft.UI.Xaml public class FrameworkElement { } }"; - VerifyGeneratedDiagnostics(source + AppDispatcherQueueDefinition); + VerifyGeneratedDiagnostics(source + DispatcherQueueDefinition); } [TestMethod] @@ -265,7 +259,7 @@ namespace Microsoft.UI.Xaml public class FrameworkElement { } }"; - VerifyGeneratedDiagnostics(source + AppDispatcherQueueDefinition, DiagnosticDescriptors.TestMethodIsNotParameterless.Id); + VerifyGeneratedDiagnostics(source + DispatcherQueueDefinition, DiagnosticDescriptors.TestMethodIsNotParameterless.Id); } [TestMethod] @@ -298,7 +292,7 @@ namespace Microsoft.UI.Xaml public class FrameworkElement { } }"; - VerifyGeneratedDiagnostics(source + AppDispatcherQueueDefinition); + VerifyGeneratedDiagnostics(source + DispatcherQueueDefinition); } /// @@ -340,7 +334,7 @@ from assembly in AppDomain.CurrentDomain.GetAssemblies() var compilationDiagnostics = compilation.GetDiagnostics(); - Assert.IsTrue(compilationDiagnostics.All(x => x.Severity != DiagnosticSeverity.Error), $"Expected no compilation errors. Got: \n[{string.Join("\n", compilationDiagnostics.Where(x => x.Severity == DiagnosticSeverity.Error).Select(x => $"{x.Id}: {x.GetMessage()}"))}]"); + Assert.IsTrue(compilationDiagnostics.All(x => x.Severity != DiagnosticSeverity.Error), $"Expected no compilation errors before source generation. Got: \n{string.Join("\n", compilationDiagnostics.Where(x => x.Severity == DiagnosticSeverity.Error).Select(x => $"[{x.Id}: {x.GetMessage()}]"))}"); IIncrementalGenerator generator = new TGenerator(); @@ -355,7 +349,7 @@ from assembly in AppDomain.CurrentDomain.GetAssemblies() var generatedCompilationDiaghostics = outputCompilation.GetDiagnostics(); Assert.IsTrue(resultingIds.SetEquals(diagnosticsIds), $"Expected one of [{string.Join(", ", diagnosticsIds)}] diagnostic Ids. Got [{string.Join(", ", resultingIds)}]"); - Assert.IsTrue(generatedCompilationDiaghostics.All(x => x.Severity != DiagnosticSeverity.Error), $"Expected no generated compilation errors. Got: \n[{string.Join("\n", generatedCompilationDiaghostics.Where(x => x.Severity == DiagnosticSeverity.Error).Select(x => $"{x.Id}: {x.GetMessage()}"))}]"); + Assert.IsTrue(generatedCompilationDiaghostics.All(x => x.Severity != DiagnosticSeverity.Error), $"Expected no generated compilation errors. Got: \n{string.Join("\n", generatedCompilationDiaghostics.Where(x => x.Severity == DiagnosticSeverity.Error).Select(x => $"[{x.Id}: {x.GetMessage()}]"))}"); GC.KeepAlive(attributeType); } diff --git a/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/UIControlTestMethodlGenerator.cs b/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/UIControlTestMethodlGenerator.cs index 14c9d8a97..b5e50034f 100644 --- a/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/UIControlTestMethodlGenerator.cs +++ b/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/UIControlTestMethodlGenerator.cs @@ -80,14 +80,11 @@ partial class {methodSymbol.ContainingType.Name} [TestMethod] public Task {methodSymbol.Name}_Test() {{ - return App.DispatcherQueue.EnqueueAsync(async () => {{ + return EnqueueAsync(async () => {{ TestPage = new {controlTypeSymbol.GetFullyQualifiedName()}(); // Set content - Task result = SetTestContentAsync(TestPage); - await result; - - Assert.IsTrue(result.IsCompletedSuccessfully, $""Failed to load page {controlTypeSymbol.GetFullyQualifiedName()} for test { methodSymbol.Name } with Exception: {{ result.Exception?.Message}} ""); + await SetTestContentAsync(TestPage); // Call original {(isAsync ? "await " : string.Empty)}{methodSymbol.Name}(); @@ -99,7 +96,7 @@ partial class {methodSymbol.ContainingType.Name} }} "; - context.AddSource($"{methodSymbol}.g", source); + context.AddSource($"{methodSymbol.Name}.g", source); } private static bool ControlTypeInheritsFrameworkElement(INamedTypeSymbol controlType) diff --git a/common/CommunityToolkit.Labs.UnitTests.Shared/App.xaml.cs b/common/CommunityToolkit.Labs.UnitTests.Shared/App.xaml.cs index 491855b9e..7ead236cf 100644 --- a/common/CommunityToolkit.Labs.UnitTests.Shared/App.xaml.cs +++ b/common/CommunityToolkit.Labs.UnitTests.Shared/App.xaml.cs @@ -47,20 +47,8 @@ public sealed partial class App : Application // Holder for test content to abstract Window.Current.Content public static FrameworkElement? ContentRoot { - get - { - var rootFrame = currentWindow.Content as Frame; - return rootFrame?.Content as FrameworkElement; - } - - set - { - var rootFrame = currentWindow.Content as Frame; - if (rootFrame != null) - { - rootFrame.Content = value; - } - } + get => currentWindow.Content as FrameworkElement; + set => currentWindow.Content = value; } // Abstract CoreApplication.MainView.DispatcherQueue diff --git a/common/CommunityToolkit.Labs.UnitTests.Shared/CommunityToolkit.Labs.UnitTests.Shared.projitems b/common/CommunityToolkit.Labs.UnitTests.Shared/CommunityToolkit.Labs.UnitTests.Shared.projitems index f14f94ed2..8a363fae1 100644 --- a/common/CommunityToolkit.Labs.UnitTests.Shared/CommunityToolkit.Labs.UnitTests.Shared.projitems +++ b/common/CommunityToolkit.Labs.UnitTests.Shared/CommunityToolkit.Labs.UnitTests.Shared.projitems @@ -19,7 +19,6 @@ App.xaml - \ No newline at end of file diff --git a/common/CommunityToolkit.Labs.UnitTests.Shared/TestPageAttribute.cs b/common/CommunityToolkit.Labs.UnitTests.Shared/TestPageAttribute.cs deleted file mode 100644 index 3b77b8b3c..000000000 --- a/common/CommunityToolkit.Labs.UnitTests.Shared/TestPageAttribute.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; - -namespace CommunityToolkit.Labs.UnitTests; - -/// -/// Attribute to add to a implementation in order to load a XAML based page to use within that test. Class with containing method needs to inherit from for functionality to work. Requires to be set to function. -/// -[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] -public sealed class TestPageAttribute : Attribute -{ - public TestPageAttribute(Type pageType) - { - if (pageType == null) - { - throw new ArgumentException($"'{nameof(pageType)}' cannot be null", nameof(pageType)); - } - - PageType = pageType; - } - - public Type PageType { get; private set; } -} diff --git a/common/CommunityToolkit.Labs.UnitTests.Shared/VisualUITestBase.cs b/common/CommunityToolkit.Labs.UnitTests.Shared/VisualUITestBase.cs index 189a725e1..a6f7b5d83 100644 --- a/common/CommunityToolkit.Labs.UnitTests.Shared/VisualUITestBase.cs +++ b/common/CommunityToolkit.Labs.UnitTests.Shared/VisualUITestBase.cs @@ -2,10 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Threading.Tasks; -using System.Reflection; +using Microsoft.VisualStudio.TestTools.UnitTesting; #if !WINAPPSDK using Microsoft.Toolkit.Uwp; @@ -26,32 +25,51 @@ namespace CommunityToolkit.Labs.UnitTests; /// public class VisualUITestBase { - public TestContext? TestContext { get; set; } + public FrameworkElement? TestPage { get; protected set; } + + protected Task EnqueueAsync(Func> function) + { + var taskCompletionSource = new TaskCompletionSource(); + + var addedToQueue = App.DispatcherQueue.TryEnqueue(async () => + { + var res = await function(); + taskCompletionSource?.TrySetResult(res); + }); - public FrameworkElement? TestPage { get; private set; } + Assert.IsTrue(addedToQueue); - [TestInitialize] - public async Task TestInitialize() + return taskCompletionSource.Task; + } + + protected Task EnqueueAsync(Func function) { - if (TestContext != null) + var taskCompletionSource = new TaskCompletionSource(); + + var addedToQueue = App.DispatcherQueue.TryEnqueue(async () => { - await App.DispatcherQueue.EnqueueAsync(async () => - { - TestPage = GetPageForTest(TestContext); + await function(); + taskCompletionSource?.TrySetResult(null); + }); + + Assert.IsTrue(addedToQueue); + + return taskCompletionSource.Task; + } - if (TestPage != null) - { - Task result = SetTestContentAsync(TestPage); + protected Task EnqueueAsync(Action function) + { + var taskCompletionSource = new TaskCompletionSource(); - await result; + var addedToQueue = App.DispatcherQueue.TryEnqueue(() => + { + function(); + taskCompletionSource?.TrySetResult(null); + }); - if (!result.IsCompletedSuccessfully) - { - throw new Exception($"Failed to load page for {TestContext.TestName} with Exception: {result.Exception?.Message}", result.Exception); - } - } - }); - } + Assert.IsTrue(addedToQueue); + + return taskCompletionSource.Task; } /// @@ -60,9 +78,9 @@ await App.DispatcherQueue.EnqueueAsync(async () => /// /// Content to set in test app. /// When UI is loaded. - protected Task SetTestContentAsync(FrameworkElement content) + protected async Task SetTestContentAsync(FrameworkElement content) { - return App.DispatcherQueue.EnqueueAsync(() => + await App.DispatcherQueue.EnqueueAsync(async () => { var taskCompletionSource = new TaskCompletionSource(); @@ -79,7 +97,6 @@ async void Callback(object sender, RoutedEventArgs args) // Going to wait for our original content to unload content.Loaded += Callback; - // Trigger that now try { App.ContentRoot = content; @@ -89,8 +106,10 @@ async void Callback(object sender, RoutedEventArgs args) taskCompletionSource.SetException(e); } - return taskCompletionSource.Task; + await taskCompletionSource.Task; }); + + Assert.IsTrue(content.IsLoaded); } [TestCleanup] @@ -98,7 +117,7 @@ public async Task Cleanup() { var taskCompletionSource = new TaskCompletionSource(); - await App.DispatcherQueue.EnqueueAsync(() => + await EnqueueAsync(() => { // If we didn't set our content we don't have to do anything but complete here. if (App.ContentRoot is null) @@ -111,60 +130,9 @@ await App.DispatcherQueue.EnqueueAsync(() => App.ContentRoot.Unloaded += (_, _) => taskCompletionSource.SetResult(true); TestPage = null; - - // Trigger that now App.ContentRoot = null; }); await taskCompletionSource.Task; } - - private static FrameworkElement? GetPageForTest(TestContext testContext) - { - var testName = testContext.TestName; - var theClassName = testContext.FullyQualifiedTestClassName; - - var testClassString = $"test class \"{theClassName}\""; - if (Type.GetType(theClassName) is not Type type) - { - throw new Exception($"Could not find {testClassString}."); - } - - Log.Comment($"Found {testClassString}."); - - var testMethodString = $"test method \"{testName}\" in {testClassString}"; - if (type.GetMethod(testName) is not MethodInfo method) - { - throw new Exception($"Could not find {testMethodString}."); - } - - Log.Comment($"Found {testMethodString}."); - - var testpageAttributeString = $"\"{typeof(TestPageAttribute)}\" on {testMethodString}"; - if (method.GetCustomAttribute(typeof(TestPageAttribute), true) is not TestPageAttribute attribute) - { - // If we don't have an attribute, we'll return null here to indicate such. - // Otherwise, we'll be throwing an exception on failure anyway below. - return null; - } - - if (attribute.PageType is null) - { - throw new Exception($"{testpageAttributeString} requires `PageType` to be set."); - } - - var obj = Activator.CreateInstance(attribute.PageType); - - if (obj is null) - { - throw new Exception($"Could not instantiate page of type {attribute.PageType.FullName} for {testpageAttributeString}."); - } - - if (obj is FrameworkElement element) - { - return element; - } - - throw new Exception($"{attribute.PageType.FullName} is required to inherit from `FrameworkElement` for the {testpageAttributeString}."); - } } diff --git a/labs/SizerBase/tests/SizerBase.Tests/ExampleSizerBaseTestClass.cs b/labs/SizerBase/tests/SizerBase.Tests/ExampleSizerBaseTestClass.cs index ec90d946d..fe1d5be92 100644 --- a/labs/SizerBase/tests/SizerBase.Tests/ExampleSizerBaseTestClass.cs +++ b/labs/SizerBase/tests/SizerBase.Tests/ExampleSizerBaseTestClass.cs @@ -11,6 +11,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer; using System.Threading.Tasks; +using CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod; #if !WINAPPSDK using Microsoft.Toolkit.Uwp; @@ -31,8 +32,9 @@ namespace SizerBase.Tests; [TestClass] -public class ExampleSizerBaseTestClass : VisualUITestBase +public partial class ExampleSizerBaseTestClass : VisualUITestBase { + [TestMethod] public void Just_an_example_test() { @@ -42,7 +44,7 @@ public void Just_an_example_test() [TestMethod] public async Task ShouldConfigureGridSplitterAutomationPeer() { - await App.DispatcherQueue.EnqueueAsync(() => + await EnqueueAsync(() => { const string automationName = "MyCustomAutomationName"; const string name = "Sizer"; @@ -60,37 +62,31 @@ await App.DispatcherQueue.EnqueueAsync(() => }); } - [TestMethod] - [TestPage(typeof(PropertySizerTestInitialBinding))] - public async Task PropertySizer_TestInitialBinding() + [UIControlTestMethod(typeof(PropertySizerTestInitialBinding))] + public void PropertySizer_TestInitialBinding() { - await App.DispatcherQueue.EnqueueAsync(() => { - // TestPage shouldn't be null here, but we'll do the safer ?. to be sure. - var propertySizer = TestPage?.FindDescendant(); + // TestPage shouldn't be null here, but we'll do the safer ?. to be sure. + var propertySizer = TestPage?.FindDescendant(); - Assert.IsNotNull(propertySizer, "Could not find PropertySizer control."); + Assert.IsNotNull(propertySizer, "Could not find PropertySizer control."); - // Set in XAML Page LINK: PropertySizerTestInitialBinding.xaml#L14 - Assert.AreEqual(300, propertySizer.Binding, "Property Sizer not at expected initial value."); - }); + // Set in XAML Page LINK: PropertySizerTestInitialBinding.xaml#L14 + Assert.AreEqual(300, propertySizer.Binding, "Property Sizer not at expected initial value."); } - [TestMethod] - [TestPage(typeof(PropertySizerTestInitialBinding))] - public async Task PropertySizer_TestChangeBinding() + [UIControlTestMethod(typeof(PropertySizerTestInitialBinding))] + public void PropertySizer_TestChangeBinding() { - await App.DispatcherQueue.EnqueueAsync(() => { - // TestPage shouldn't be null here, but we'll do the safer ?. to be sure. - var propertySizer = TestPage?.FindDescendant(); - var navigationView = TestPage?.FindDescendant(); + // TestPage shouldn't be null here, but we'll do the safer ?. to be sure. + var propertySizer = TestPage?.FindDescendant(); + var navigationView = TestPage?.FindDescendant(); - Assert.IsNotNull(propertySizer, "Could not find PropertySizer control."); - Assert.IsNotNull(navigationView, "Could not find NavigationView control."); + Assert.IsNotNull(propertySizer, "Could not find PropertySizer control."); + Assert.IsNotNull(navigationView, "Could not find NavigationView control."); - navigationView.OpenPaneLength = 200; + navigationView.OpenPaneLength = 200; - // Set in XAML Page LINK: PropertySizerTestInitialBinding.xaml#L14 - Assert.AreEqual(200, propertySizer.Binding, "Property Sizer not at expected changed value."); - }); + // Set in XAML Page LINK: PropertySizerTestInitialBinding.xaml#L14 + Assert.AreEqual(200, propertySizer.Binding, "Property Sizer not at expected changed value."); } } diff --git a/tests/Labs.Tests.props b/tests/Labs.Tests.props index b7026a690..1a56d5f16 100644 --- a/tests/Labs.Tests.props +++ b/tests/Labs.Tests.props @@ -9,7 +9,7 @@ 2.2.8 - From 65824ce7b81b46fa1801622b1fd6b70907ffd52d Mon Sep 17 00:00:00 2001 From: Arlo Godfrey Date: Tue, 14 Jun 2022 16:46:55 -0500 Subject: [PATCH 04/15] Refactored to pull control instance directly from method params. Improved control lifetime. --- ...eGenerators.LabsUITestMethod.Tests.csproj} | 2 +- .../LabsUITestMethodTests.cs} | 172 ++++++------------ ....SourceGenerators.LabsUITestMethod.csproj} | 0 .../Diagnostics/DiagnosticDescriptors.cs | 29 +++ .../Extensions/ISymbolExtensions.cs | 2 +- .../Extensions/ITypeSymbolExtensions.cs | 2 +- .../LabsUITestMethodAttribute.cs | 16 ++ .../LabsUITestMethodGenerator.cs} | 57 +++--- .../Diagnostics/DiagnosticDescriptors.cs | 59 ------ .../UIControlTestMethodAttribute.cs | 22 --- .../VisualUITestBase.cs | 71 +++----- common/Toolkit.Labs.All.sln.template | 4 +- .../ExampleSizerBaseTestClass.cs | 17 +- tests/Labs.Tests.props | 2 +- 14 files changed, 165 insertions(+), 290 deletions(-) rename common/{CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Tests/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Tests.csproj => CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.Tests/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.Tests.csproj} (85%) rename common/{CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Tests/UIControlTestMethodTests.cs => CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.Tests/LabsUITestMethodTests.cs} (54%) rename common/{CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.csproj => CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.csproj} (100%) create mode 100644 common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod/Diagnostics/DiagnosticDescriptors.cs rename common/{CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod => CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod}/Extensions/ISymbolExtensions.cs (98%) rename common/{CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod => CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod}/Extensions/ITypeSymbolExtensions.cs (98%) create mode 100644 common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod/LabsUITestMethodAttribute.cs rename common/{CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/UIControlTestMethodlGenerator.cs => CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod/LabsUITestMethodGenerator.cs} (60%) delete mode 100644 common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/Diagnostics/DiagnosticDescriptors.cs delete mode 100644 common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/UIControlTestMethodAttribute.cs diff --git a/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Tests/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Tests.csproj b/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.Tests/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.Tests.csproj similarity index 85% rename from common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Tests/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Tests.csproj rename to common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.Tests/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.Tests.csproj index e8fff1603..ff9b195ac 100644 --- a/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Tests/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Tests.csproj +++ b/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.Tests/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.Tests.csproj @@ -15,7 +15,7 @@ - + diff --git a/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Tests/UIControlTestMethodTests.cs b/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.Tests/LabsUITestMethodTests.cs similarity index 54% rename from common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Tests/UIControlTestMethodTests.cs rename to common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.Tests/LabsUITestMethodTests.cs index dd9a56246..6a5491933 100644 --- a/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Tests/UIControlTestMethodTests.cs +++ b/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.Tests/LabsUITestMethodTests.cs @@ -1,5 +1,5 @@ -using CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod; -using CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Diagnostics; +using CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod; +using CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.Diagnostics; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -11,7 +11,7 @@ namespace CommunityToolkit.Labs.Core.SourceGenerators.Tests { [TestClass] - public partial class UIControlTestMethodTests + public partial class LabsUITestMethodTests { private const string DispatcherQueueDefinition = @" namespace MyApp @@ -26,87 +26,57 @@ public partial class Test "; [TestMethod] - public void TypeDoesNotInheritFrameworkElement() + public void TestControlHasConstructorWithParameters() { string source = @" using System.ComponentModel; - using CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod; + using CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod; namespace MyApp { public partial class Test { - public Windows.UI.Xaml.FrameworkElement? TestPage { get; private set; } - public System.Threading.Tasks.Task SetTestContentAsync(Windows.UI.Xaml.FrameworkElement content) => System.Threading.Tasks.Task.CompletedTask; + public System.Threading.Tasks.Task LoadTestContentAsync(Microsoft.UI.Xaml.FrameworkElement content) => System.Threading.Tasks.Task.CompletedTask; + public System.Threading.Tasks.Task UnloadTestContentAsync(Microsoft.UI.Xaml.FrameworkElement content) => System.Threading.Tasks.Task.CompletedTask; - [UIControlTestMethod(typeof(Test))] - public void TestMethod() + [LabsUITestMethod] + public void TestMethod(MyControl control) { } } - public class MyControl : Windows.UI.Xaml.FrameworkElement - { - } - - namespace Windows.UI.Xaml - { - public class FrameworkElement { } - } - }"; - - VerifyGeneratedDiagnostics(source + DispatcherQueueDefinition, DiagnosticDescriptors.TypeDoesNotInheritFrameworkElement.Id); - } - - [TestMethod] - public void TypeDoesInheritFrameworkElement_Wux() - { - string source = @" - using System.ComponentModel; - using CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod; - - namespace MyApp - { - public partial class Test + public class MyControl : Microsoft.UI.Xaml.FrameworkElement { - public Windows.UI.Xaml.FrameworkElement? TestPage { get; private set; } - public System.Threading.Tasks.Task SetTestContentAsync(Windows.UI.Xaml.FrameworkElement content) => System.Threading.Tasks.Task.CompletedTask; - - [UIControlTestMethod(typeof(MyControl))] - public void TestMethod() + public MyControl(string id) { } } - - public class MyControl : Windows.UI.Xaml.FrameworkElement - { - } } - namespace Windows.UI.Xaml + namespace Microsoft.UI.Xaml { public class FrameworkElement { } }"; - VerifyGeneratedDiagnostics(source + DispatcherQueueDefinition); + VerifyGeneratedDiagnostics(source + DispatcherQueueDefinition, DiagnosticDescriptors.TestControlHasConstructorWithParameters.Id); } [TestMethod] - public void TypeDoesInheritFrameworkElement_Mux() + public void Async_Mux_NoErrors() { string source = @" using System.ComponentModel; - using CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod; + using CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod; namespace MyApp { public partial class Test { - public Microsoft.UI.Xaml.FrameworkElement? TestPage { get; private set; } - public System.Threading.Tasks.Task SetTestContentAsync(Microsoft.UI.Xaml.FrameworkElement content) => System.Threading.Tasks.Task.CompletedTask; + public System.Threading.Tasks.Task LoadTestContentAsync(Microsoft.UI.Xaml.FrameworkElement content) => System.Threading.Tasks.Task.CompletedTask; + public System.Threading.Tasks.Task UnloadTestContentAsync(Microsoft.UI.Xaml.FrameworkElement content) => System.Threading.Tasks.Task.CompletedTask; - [UIControlTestMethod(typeof(MyControl))] - public void TestMethod() + [LabsUITestMethod] + public async System.Threading.Tasks.Task TestMethod(MyControl control) { } } @@ -121,94 +91,79 @@ namespace Microsoft.UI.Xaml public class FrameworkElement { } }"; - VerifyGeneratedDiagnostics(source + DispatcherQueueDefinition); + VerifyGeneratedDiagnostics(source + DispatcherQueueDefinition); } [TestMethod] - public void TestControlHasNoConstructorWithParameters() + public void Async_Wux_NoErrors() { string source = @" using System.ComponentModel; - using CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod; + using CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod; namespace MyApp { public partial class Test { - public Microsoft.UI.Xaml.FrameworkElement? TestPage { get; private set; } - public System.Threading.Tasks.Task SetTestContentAsync(Microsoft.UI.Xaml.FrameworkElement content) => System.Threading.Tasks.Task.CompletedTask; + public System.Threading.Tasks.Task LoadTestContentAsync(Windows.UI.Xaml.FrameworkElement content) => System.Threading.Tasks.Task.CompletedTask; + public System.Threading.Tasks.Task UnloadTestContentAsync(Windows.UI.Xaml.FrameworkElement content) => System.Threading.Tasks.Task.CompletedTask; - [UIControlTestMethod(typeof(MyControl))] - public void TestMethod() + [LabsUITestMethod] + public async System.Threading.Tasks.Task TestMethod(MyControl control) { } } - public class MyControl : Microsoft.UI.Xaml.FrameworkElement + public class MyControl : Windows.UI.Xaml.FrameworkElement { } } - namespace Microsoft.UI.Xaml + namespace Windows.UI.Xaml { public class FrameworkElement { } }"; - VerifyGeneratedDiagnostics(source + DispatcherQueueDefinition); + VerifyGeneratedDiagnostics(source + DispatcherQueueDefinition); } [TestMethod] - public void TestControlHasConstructorWithParameters() + public void Async_NoMethodParams_NoErrors() { string source = @" using System.ComponentModel; - using CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod; + using CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod; namespace MyApp { public partial class Test { - public Microsoft.UI.Xaml.FrameworkElement? TestPage { get; private set; } - public System.Threading.Tasks.Task SetTestContentAsync(Microsoft.UI.Xaml.FrameworkElement content) => System.Threading.Tasks.Task.CompletedTask; - - [UIControlTestMethod(typeof(MyControl))] - public void TestMethod() - { - } - } - - public class MyControl : Microsoft.UI.Xaml.FrameworkElement - { - public MyControl(string id) + [LabsUITestMethod] + public async System.Threading.Tasks.Task TestMethod() { } } - } - - namespace Microsoft.UI.Xaml - { - public class FrameworkElement { } }"; - VerifyGeneratedDiagnostics(source + DispatcherQueueDefinition, DiagnosticDescriptors.TestControlHasConstructorWithParameters.Id); + VerifyGeneratedDiagnostics(source + DispatcherQueueDefinition); } [TestMethod] - public void TestMethodHasParameterlessConstructor() + public void Synchronous_Mux_NoErrors() { string source = @" using System.ComponentModel; - using CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod; + using CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod; namespace MyApp { public partial class Test { - public Microsoft.UI.Xaml.FrameworkElement? TestPage { get; private set; } - public System.Threading.Tasks.Task SetTestContentAsync(Microsoft.UI.Xaml.FrameworkElement content) => System.Threading.Tasks.Task.CompletedTask; + public System.Threading.Tasks.Task LoadTestContentAsync(Microsoft.UI.Xaml.FrameworkElement content) => System.Threading.Tasks.Task.CompletedTask; + public System.Threading.Tasks.Task UnloadTestContentAsync(Microsoft.UI.Xaml.FrameworkElement content) => System.Threading.Tasks.Task.CompletedTask; - [UIControlTestMethod(typeof(MyControl))] - public void TestMethod() + [LabsUITestMethod] + public void TestMethod(MyControl control) { } } @@ -223,76 +178,61 @@ namespace Microsoft.UI.Xaml public class FrameworkElement { } }"; - VerifyGeneratedDiagnostics(source + DispatcherQueueDefinition); + VerifyGeneratedDiagnostics(source + DispatcherQueueDefinition); } [TestMethod] - public void TestMethodDoesNotHaveParameterlessConstructor() + public void Synchronous_Wux_NoErrors() { string source = @" using System.ComponentModel; - using CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod; + using CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod; namespace MyApp { public partial class Test { - public Microsoft.UI.Xaml.FrameworkElement? TestPage { get; private set; } - public System.Threading.Tasks.Task SetTestContentAsync(Microsoft.UI.Xaml.FrameworkElement content) => System.Threading.Tasks.Task.CompletedTask; + public System.Threading.Tasks.Task LoadTestContentAsync(Windows.UI.Xaml.FrameworkElement content) => System.Threading.Tasks.Task.CompletedTask; + public System.Threading.Tasks.Task UnloadTestContentAsync(Windows.UI.Xaml.FrameworkElement content) => System.Threading.Tasks.Task.CompletedTask; - [UIControlTestMethod(typeof(MyControl))] - public void TestMethod(string id) + [LabsUITestMethod] + public void TestMethod(MyControl control) { } } - public class MyControl : Microsoft.UI.Xaml.FrameworkElement + public class MyControl : Windows.UI.Xaml.FrameworkElement { - public MyControl() - { - } } } - namespace Microsoft.UI.Xaml + namespace Windows.UI.Xaml { public class FrameworkElement { } }"; - VerifyGeneratedDiagnostics(source + DispatcherQueueDefinition, DiagnosticDescriptors.TestMethodIsNotParameterless.Id); + VerifyGeneratedDiagnostics(source + DispatcherQueueDefinition); } [TestMethod] - public void AsyncMethod_NoErrors() + public void Synchronous_NoMethodParams_NoErrors() { string source = @" using System.ComponentModel; - using CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod; + using CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod; namespace MyApp { public partial class Test { - public Microsoft.UI.Xaml.FrameworkElement? TestPage { get; private set; } - public System.Threading.Tasks.Task SetTestContentAsync(Microsoft.UI.Xaml.FrameworkElement content) => System.Threading.Tasks.Task.CompletedTask; - - [UIControlTestMethod(typeof(MyControl))] - public async System.Threading.Tasks.Task TestMethod() + [LabsUITestMethod] + public void TestMethod() { } } - - public class MyControl : Microsoft.UI.Xaml.FrameworkElement - { - } - } - - namespace Microsoft.UI.Xaml - { - public class FrameworkElement { } }"; - VerifyGeneratedDiagnostics(source + DispatcherQueueDefinition); + VerifyGeneratedDiagnostics(source + DispatcherQueueDefinition); } /// @@ -318,7 +258,7 @@ private static void VerifyGeneratedDiagnostics(string source, params private static void VerifyGeneratedDiagnostics(SyntaxTree syntaxTree, params string[] diagnosticsIds) where TGenerator : class, IIncrementalGenerator, new() { - var attributeType = typeof(UIControlTestMethodAttribute); + var attributeType = typeof(LabsUITestMethodAttribute); var references = from assembly in AppDomain.CurrentDomain.GetAssemblies() diff --git a/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.csproj b/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.csproj similarity index 100% rename from common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.csproj rename to common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.csproj diff --git a/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod/Diagnostics/DiagnosticDescriptors.cs b/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod/Diagnostics/DiagnosticDescriptors.cs new file mode 100644 index 000000000..d9d1803f3 --- /dev/null +++ b/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod/Diagnostics/DiagnosticDescriptors.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.CodeAnalysis; + +namespace CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.Diagnostics +{ + /// + /// A container for all instances for errors reported by analyzers in this project. + /// + public static class DiagnosticDescriptors + { + /// + /// Gets a indicating that a test method decorated with asks for a control instance with a non-parameterless constructor. + /// + /// Format: "Cannot generate test with type {0} as it has a constructor with parameters.". + /// + /// + public static readonly DiagnosticDescriptor TestControlHasConstructorWithParameters = new( + id: "UICTRLTM0001", + title: $"Provided control must not have a constructor with parameters.", + messageFormat: $"Cannot generate test with control {{0}} as it has a constructor with parameters.", + category: typeof(LabsUITestMethodGenerator).FullName, + defaultSeverity: DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: $"Cannot generate test method with provided control."); + } +} diff --git a/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/Extensions/ISymbolExtensions.cs b/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod/Extensions/ISymbolExtensions.cs similarity index 98% rename from common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/Extensions/ISymbolExtensions.cs rename to common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod/Extensions/ISymbolExtensions.cs index 5dc0570c7..a95702b75 100644 --- a/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/Extensions/ISymbolExtensions.cs +++ b/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod/Extensions/ISymbolExtensions.cs @@ -5,7 +5,7 @@ using System.Collections.Immutable; using Microsoft.CodeAnalysis; -namespace CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Extensions; +namespace CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.Extensions; /// /// Extension methods for the type. diff --git a/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/Extensions/ITypeSymbolExtensions.cs b/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod/Extensions/ITypeSymbolExtensions.cs similarity index 98% rename from common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/Extensions/ITypeSymbolExtensions.cs rename to common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod/Extensions/ITypeSymbolExtensions.cs index 657dcccb5..7d4f813c4 100644 --- a/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/Extensions/ITypeSymbolExtensions.cs +++ b/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod/Extensions/ITypeSymbolExtensions.cs @@ -6,7 +6,7 @@ using System.Linq; using Microsoft.CodeAnalysis; -namespace CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Extensions; +namespace CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.Extensions; /// /// Extension methods for the type. diff --git a/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod/LabsUITestMethodAttribute.cs b/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod/LabsUITestMethodAttribute.cs new file mode 100644 index 000000000..b5f05b575 --- /dev/null +++ b/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod/LabsUITestMethodAttribute.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod; + +/// +/// Generates a test method that auto-dispatches method contents to the UI thread, +/// and provides an instance of a control as a parameter if present in the method signature. +/// +[AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)] +public sealed class LabsUITestMethodAttribute : Attribute +{ +} diff --git a/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/UIControlTestMethodlGenerator.cs b/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod/LabsUITestMethodGenerator.cs similarity index 60% rename from common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/UIControlTestMethodlGenerator.cs rename to common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod/LabsUITestMethodGenerator.cs index b5e50034f..1218a503b 100644 --- a/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/UIControlTestMethodlGenerator.cs +++ b/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod/LabsUITestMethodGenerator.cs @@ -2,19 +2,19 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Diagnostics; -using CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Extensions; +using CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.Diagnostics; +using CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.Extensions; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using System.Linq; -namespace CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod; +namespace CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod; /// /// Generates code that provides access to XAML elements with x:Name from code-behind by wrapping an instance of a control, without the need to use x:FieldProvider="public" directly in markup. /// [Generator] -public class UIControlTestMethodGenerator : IIncrementalGenerator +public class LabsUITestMethodGenerator : IIncrementalGenerator { public void Initialize(IncrementalGeneratorInitializationContext context) { @@ -26,20 +26,19 @@ public void Initialize(IncrementalGeneratorInitializationContext context) .Where(x => x is not null) .Select((x, _) => x!); - // Filter the methods using [UIControlTestMethod] + // Filter the methods using [LabsUITestMethod] var methodAndPageTypeSymbols = methodSymbols .Select(static (item, _) => ( Symbol: item, - Attribute: item.GetAttributes().FirstOrDefault(a => a.AttributeClass?.HasFullyQualifiedName("global::CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.UIControlTestMethodAttribute") ?? false) + Attribute: item.GetAttributes().FirstOrDefault(a => a.AttributeClass?.HasFullyQualifiedName("global::CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.LabsUITestMethodAttribute") ?? false) )) .Where(static item => item.Attribute is not null && item.Symbol is IMethodSymbol) - .Select(static (x, _) => (MethodSymbol: (IMethodSymbol)x.Symbol, Attribute: x.Attribute!)) - - .Where(static x => !x.Attribute.ConstructorArguments.IsEmpty) - .Select(static (x, _) => (x.MethodSymbol, ControlTypeSymbol: GetControlTypeSymbolFromAttribute(x.Attribute))) + .Select(static (x, _) => (IMethodSymbol)x.Symbol) + .Select(static (x, _) => (MethodSymbol: x, ControlTypeSymbol: GetControlTypeSymbolFromMethodParameters(x))) + .Where(static x => x.ControlTypeSymbol is not null) .Select(static (x, _) => (x.MethodSymbol, ControlTypeSymbol: x.ControlTypeSymbol!)); @@ -47,26 +46,14 @@ public void Initialize(IncrementalGeneratorInitializationContext context) context.RegisterSourceOutput(methodAndPageTypeSymbols, (x, y) => GenerateTestMethod(x, y.MethodSymbol, y.ControlTypeSymbol)); } - private static void GenerateTestMethod(SourceProductionContext context, IMethodSymbol methodSymbol, INamedTypeSymbol controlTypeSymbol) + private static void GenerateTestMethod(SourceProductionContext context, IMethodSymbol methodSymbol, INamedTypeSymbol? controlTypeSymbol) { - if (!ControlTypeInheritsFrameworkElement(controlTypeSymbol)) - { - context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.TypeDoesNotInheritFrameworkElement, methodSymbol.Locations.FirstOrDefault(), controlTypeSymbol.Name)); - return; - } - - if (controlTypeSymbol.Constructors.Any(x => !x.Parameters.IsEmpty)) + if (controlTypeSymbol is not null && controlTypeSymbol.Constructors.Any(x => !x.Parameters.IsEmpty)) { context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.TestControlHasConstructorWithParameters, methodSymbol.Locations.FirstOrDefault(), controlTypeSymbol.Name)); return; } - if (!methodSymbol.Parameters.IsEmpty) - { - context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.TestMethodIsNotParameterless, methodSymbol.Locations.FirstOrDefault(), controlTypeSymbol.Name)); - return; - } - var isAsync = methodSymbol.ReturnType.HasFullyQualifiedName("global::System.Threading.Tasks.Task") || methodSymbol.ReturnType.InheritsFromFullyQualifiedName("global::System.Threading.Tasks.Task"); @@ -81,15 +68,19 @@ partial class {methodSymbol.ContainingType.Name} public Task {methodSymbol.Name}_Test() {{ return EnqueueAsync(async () => {{ - TestPage = new {controlTypeSymbol.GetFullyQualifiedName()}(); + {(controlTypeSymbol is not null ? @$" + // Create content + var testControl = new {controlTypeSymbol.GetFullyQualifiedName()}(); - // Set content - await SetTestContentAsync(TestPage); + // Load content + await LoadTestContentAsync(testControl);" : string.Empty)} - // Call original - {(isAsync ? "await " : string.Empty)}{methodSymbol.Name}(); + // Run test + {(isAsync ? "await " : string.Empty)}{methodSymbol.Name}({(controlTypeSymbol is not null ? "testControl" : string.Empty)}); - TestPage = null; + {(controlTypeSymbol is not null ? + @"// Unload content + await UnloadTestContentAsync(testControl);" : string.Empty)} }}); }} }} @@ -99,15 +90,15 @@ partial class {methodSymbol.ContainingType.Name} context.AddSource($"{methodSymbol.Name}.g", source); } - private static bool ControlTypeInheritsFrameworkElement(INamedTypeSymbol controlType) + private static bool ControlTypeInheritsFrameworkElement(ITypeSymbol controlType) { return controlType.HasOrInheritsFromFullyQualifiedName("global::Windows.UI.Xaml.FrameworkElement") || controlType.HasOrInheritsFromFullyQualifiedName("global::Microsoft.UI.Xaml.FrameworkElement"); } - private static INamedTypeSymbol? GetControlTypeSymbolFromAttribute(AttributeData attribute) + private static INamedTypeSymbol? GetControlTypeSymbolFromMethodParameters(IMethodSymbol methodSymbol) { - return attribute.ConstructorArguments.FirstOrDefault(x => x.Kind == TypedConstantKind.Type).Value as INamedTypeSymbol; + return methodSymbol.Parameters.FirstOrDefault(x => ControlTypeInheritsFrameworkElement(x.Type))?.Type as INamedTypeSymbol; } } diff --git a/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/Diagnostics/DiagnosticDescriptors.cs b/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/Diagnostics/DiagnosticDescriptors.cs deleted file mode 100644 index 8d2fe6dc6..000000000 --- a/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/Diagnostics/DiagnosticDescriptors.cs +++ /dev/null @@ -1,59 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.CodeAnalysis; - -namespace CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Diagnostics -{ - /// - /// A container for all instances for errors reported by analyzers in this project. - /// - public static class DiagnosticDescriptors - { - /// - /// Gets a indicating the provided isn't a valid FrameworkElement. - /// - /// Format: "Cannot generate test with type {0} as it does not inherit from FrameworkElement". - /// - /// - public static readonly DiagnosticDescriptor TypeDoesNotInheritFrameworkElement = new( - id: "UICTRLTM0001", - title: $"Invalid UI test control type", - messageFormat: $"Cannot generate test with type {{0}} as it does not inherit from FrameworkElement", - category: typeof(UIControlTestMethodGenerator).FullName, - defaultSeverity: DiagnosticSeverity.Error, - isEnabledByDefault: true, - description: $"Cannot generate test method."); - - /// - /// Gets a indicating the provided is a type has non-parameterless constructor. - /// - /// Format: "Cannot generate test with type {0} as it has a constructor with parameters.". - /// - /// - public static readonly DiagnosticDescriptor TestControlHasConstructorWithParameters = new( - id: "UICTRLTM0002", - title: $"Provided test control must not have a constructor with parameters.", - messageFormat: $"Cannot generate test with type {{0}} as it has a constructor with parameters.", - category: typeof(UIControlTestMethodGenerator).FullName, - defaultSeverity: DiagnosticSeverity.Error, - isEnabledByDefault: true, - description: $"Cannot generate test method."); - - /// - /// Gets a indicating the attached test method is not parameterless. - /// - /// Format: "Cannot generate test as the attached method is not parameterless.". - /// - /// - public static readonly DiagnosticDescriptor TestMethodIsNotParameterless = new( - id: "UICTRLTM0003", - title: $"Attached test method must not take any parameters.", - messageFormat: $"Cannot generate test as the attached method is not parameterless.", - category: typeof(UIControlTestMethodGenerator).FullName, - defaultSeverity: DiagnosticSeverity.Error, - isEnabledByDefault: true, - description: $"Cannot generate test method."); - } -} diff --git a/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/UIControlTestMethodAttribute.cs b/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/UIControlTestMethodAttribute.cs deleted file mode 100644 index ce78fabeb..000000000 --- a/common/CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod/UIControlTestMethodAttribute.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; - -namespace CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod; - -/// -/// Generates a test method that provides an instance of the given FrameworkElement type -/// and auto-dispatches method contents to the UI thread. -/// -[AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)] -public sealed class UIControlTestMethodAttribute : Attribute -{ - public UIControlTestMethodAttribute(Type type) - { - Type = type; - } - - public Type Type { get; } -} diff --git a/common/CommunityToolkit.Labs.UnitTests.Shared/VisualUITestBase.cs b/common/CommunityToolkit.Labs.UnitTests.Shared/VisualUITestBase.cs index a6f7b5d83..d707c832f 100644 --- a/common/CommunityToolkit.Labs.UnitTests.Shared/VisualUITestBase.cs +++ b/common/CommunityToolkit.Labs.UnitTests.Shared/VisualUITestBase.cs @@ -25,8 +25,6 @@ namespace CommunityToolkit.Labs.UnitTests; /// public class VisualUITestBase { - public FrameworkElement? TestPage { get; protected set; } - protected Task EnqueueAsync(Func> function) { var taskCompletionSource = new TaskCompletionSource(); @@ -78,61 +76,42 @@ protected Task EnqueueAsync(Action function) /// /// Content to set in test app. /// When UI is loaded. - protected async Task SetTestContentAsync(FrameworkElement content) + protected async Task LoadTestContentAsync(FrameworkElement content) { - await App.DispatcherQueue.EnqueueAsync(async () => - { - var taskCompletionSource = new TaskCompletionSource(); - - async void Callback(object sender, RoutedEventArgs args) - { - content.Loaded -= Callback; - - // Wait for first Render pass - await CompositionTargetHelper.ExecuteAfterCompositionRenderingAsync(() => { }); + var taskCompletionSource = new TaskCompletionSource(); - taskCompletionSource.SetResult(true); - } + content.Loaded += OnLoaded; + App.ContentRoot = content; - // Going to wait for our original content to unload - content.Loaded += Callback; + await taskCompletionSource.Task; + Assert.IsTrue(content.IsLoaded); - try - { - App.ContentRoot = content; - } - catch (Exception e) - { - taskCompletionSource.SetException(e); - } + async void OnLoaded(object sender, RoutedEventArgs args) + { + content.Loaded -= OnLoaded; - await taskCompletionSource.Task; - }); + // Wait for first Render pass + await CompositionTargetHelper.ExecuteAfterCompositionRenderingAsync(() => { }); - Assert.IsTrue(content.IsLoaded); + taskCompletionSource.SetResult(null); + } } - [TestCleanup] - public async Task Cleanup() + public async Task UnloadTestContentAsync(FrameworkElement element) { - var taskCompletionSource = new TaskCompletionSource(); + var taskCompletionSource = new TaskCompletionSource(); - await EnqueueAsync(() => - { - // If we didn't set our content we don't have to do anything but complete here. - if (App.ContentRoot is null) - { - taskCompletionSource.SetResult(true); - return; - } - - // Going to wait for our original content to unload - App.ContentRoot.Unloaded += (_, _) => taskCompletionSource.SetResult(true); - - TestPage = null; - App.ContentRoot = null; - }); + element.Unloaded += OnUnloaded; + + App.ContentRoot = null; await taskCompletionSource.Task; + Assert.IsFalse(element.IsLoaded); + + void OnUnloaded(object sender, RoutedEventArgs args) + { + element.Unloaded -= OnUnloaded; + taskCompletionSource.SetResult(null); + } } } diff --git a/common/Toolkit.Labs.All.sln.template b/common/Toolkit.Labs.All.sln.template index ae61ea3dd..b53cc1570 100644 --- a/common/Toolkit.Labs.All.sln.template +++ b/common/Toolkit.Labs.All.sln.template @@ -54,9 +54,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Labs.UnitT EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Experiments", "Experiments", "{DD69BA61-C86D-4138-AE6F-76E2C8445C9A}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod", "common\CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod\CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.csproj", "{CA16C45E-DFB1-4641-A28D-EC52B6FB370A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod", "common\CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod\CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.csproj", "{CA16C45E-DFB1-4641-A28D-EC52B6FB370A}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Tests", "common\CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Tests\CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod.Tests.csproj", "{6961F49B-705D-47E0-81C3-D8CF7741C3E4}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.Tests", "common\CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.Tests\CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.Tests.csproj", "{6961F49B-705D-47E0-81C3-D8CF7741C3E4}" EndProject [TemplatedProjectDefinitions] Global diff --git a/labs/SizerBase/tests/SizerBase.Tests/ExampleSizerBaseTestClass.cs b/labs/SizerBase/tests/SizerBase.Tests/ExampleSizerBaseTestClass.cs index fe1d5be92..f45c39a42 100644 --- a/labs/SizerBase/tests/SizerBase.Tests/ExampleSizerBaseTestClass.cs +++ b/labs/SizerBase/tests/SizerBase.Tests/ExampleSizerBaseTestClass.cs @@ -11,7 +11,8 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer; using System.Threading.Tasks; -using CommunityToolkit.Labs.Core.SourceGenerators.UIControlTestMethod; +using CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod; +using Microsoft.UI.Xaml; #if !WINAPPSDK using Microsoft.Toolkit.Uwp; @@ -62,11 +63,11 @@ await EnqueueAsync(() => }); } - [UIControlTestMethod(typeof(PropertySizerTestInitialBinding))] - public void PropertySizer_TestInitialBinding() + [LabsUITestMethod] + public void PropertySizer_TestInitialBinding(PropertySizerTestInitialBinding testControl) { // TestPage shouldn't be null here, but we'll do the safer ?. to be sure. - var propertySizer = TestPage?.FindDescendant(); + var propertySizer = testControl.FindDescendant(); Assert.IsNotNull(propertySizer, "Could not find PropertySizer control."); @@ -74,12 +75,12 @@ public void PropertySizer_TestInitialBinding() Assert.AreEqual(300, propertySizer.Binding, "Property Sizer not at expected initial value."); } - [UIControlTestMethod(typeof(PropertySizerTestInitialBinding))] - public void PropertySizer_TestChangeBinding() + [LabsUITestMethod] + public void PropertySizer_TestChangeBinding(PropertySizerTestInitialBinding testControl) { // TestPage shouldn't be null here, but we'll do the safer ?. to be sure. - var propertySizer = TestPage?.FindDescendant(); - var navigationView = TestPage?.FindDescendant(); + var propertySizer = testControl.FindDescendant(); + var navigationView = testControl.FindDescendant(); Assert.IsNotNull(propertySizer, "Could not find PropertySizer control."); Assert.IsNotNull(navigationView, "Could not find NavigationView control."); diff --git a/tests/Labs.Tests.props b/tests/Labs.Tests.props index 1a56d5f16..d12874175 100644 --- a/tests/Labs.Tests.props +++ b/tests/Labs.Tests.props @@ -9,7 +9,7 @@ 2.2.8 - From e8222b5678e78f259099e9144a1909edca2507b8 Mon Sep 17 00:00:00 2001 From: Arlo Godfrey Date: Tue, 14 Jun 2022 16:52:16 -0500 Subject: [PATCH 05/15] Cleanup outdated comments --- .../tests/SizerBase.Tests/ExampleSizerBaseTestClass.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/labs/SizerBase/tests/SizerBase.Tests/ExampleSizerBaseTestClass.cs b/labs/SizerBase/tests/SizerBase.Tests/ExampleSizerBaseTestClass.cs index f45c39a42..cbfc3b3f1 100644 --- a/labs/SizerBase/tests/SizerBase.Tests/ExampleSizerBaseTestClass.cs +++ b/labs/SizerBase/tests/SizerBase.Tests/ExampleSizerBaseTestClass.cs @@ -66,7 +66,6 @@ await EnqueueAsync(() => [LabsUITestMethod] public void PropertySizer_TestInitialBinding(PropertySizerTestInitialBinding testControl) { - // TestPage shouldn't be null here, but we'll do the safer ?. to be sure. var propertySizer = testControl.FindDescendant(); Assert.IsNotNull(propertySizer, "Could not find PropertySizer control."); @@ -78,7 +77,6 @@ public void PropertySizer_TestInitialBinding(PropertySizerTestInitialBinding tes [LabsUITestMethod] public void PropertySizer_TestChangeBinding(PropertySizerTestInitialBinding testControl) { - // TestPage shouldn't be null here, but we'll do the safer ?. to be sure. var propertySizer = testControl.FindDescendant(); var navigationView = testControl.FindDescendant(); From 2eaaa3183e1512b23a1a31e937503b57dea7d54e Mon Sep 17 00:00:00 2001 From: Arlo Godfrey Date: Wed, 15 Jun 2022 17:50:47 -0500 Subject: [PATCH 06/15] Added mandatory content cleanup for UI tests --- .../VisualUITestBase.cs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/common/CommunityToolkit.Labs.UnitTests.Shared/VisualUITestBase.cs b/common/CommunityToolkit.Labs.UnitTests.Shared/VisualUITestBase.cs index d707c832f..41136c461 100644 --- a/common/CommunityToolkit.Labs.UnitTests.Shared/VisualUITestBase.cs +++ b/common/CommunityToolkit.Labs.UnitTests.Shared/VisualUITestBase.cs @@ -114,4 +114,24 @@ void OnUnloaded(object sender, RoutedEventArgs args) taskCompletionSource.SetResult(null); } } + + [TestInitialize] + public virtual async Task TestSetup() + { + // Make sure every test starts with a clean slate, even if it doesn't use LoadTestContentAsync. + if (App.ContentRoot is FrameworkElement element) + await UnloadTestContentAsync(element); + + Assert.IsNull(App.ContentRoot); + } + + [TestCleanup] + public virtual async Task TestCleanup() + { + // Make sure every test ends with a clean slate, even if it doesn't use LoadTestContentAsync. + if (App.ContentRoot is FrameworkElement element) + await UnloadTestContentAsync(element); + + Assert.IsNull(App.ContentRoot); + } } From b8cafae581fda5228f6725a0039cd3b1426ea8c3 Mon Sep 17 00:00:00 2001 From: Arlo Godfrey Date: Wed, 15 Jun 2022 18:47:23 -0500 Subject: [PATCH 07/15] Fixed diagnostic ID to match project name initials --- .../Diagnostics/DiagnosticDescriptors.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod/Diagnostics/DiagnosticDescriptors.cs b/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod/Diagnostics/DiagnosticDescriptors.cs index d9d1803f3..43bb4490e 100644 --- a/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod/Diagnostics/DiagnosticDescriptors.cs +++ b/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod/Diagnostics/DiagnosticDescriptors.cs @@ -18,7 +18,7 @@ public static class DiagnosticDescriptors /// /// public static readonly DiagnosticDescriptor TestControlHasConstructorWithParameters = new( - id: "UICTRLTM0001", + id: "LUITM0001", title: $"Provided control must not have a constructor with parameters.", messageFormat: $"Cannot generate test with control {{0}} as it has a constructor with parameters.", category: typeof(LabsUITestMethodGenerator).FullName, From 1e386dca6d2d310bb6748fb05c1007d6aeab1972 Mon Sep 17 00:00:00 2001 From: Arlo Godfrey Date: Wed, 15 Jun 2022 18:47:34 -0500 Subject: [PATCH 08/15] Fixed source generator to only check public constructors for LUITM0001 --- .../LabsUITestMethodGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod/LabsUITestMethodGenerator.cs b/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod/LabsUITestMethodGenerator.cs index 1218a503b..e60a249fc 100644 --- a/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod/LabsUITestMethodGenerator.cs +++ b/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod/LabsUITestMethodGenerator.cs @@ -48,7 +48,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) private static void GenerateTestMethod(SourceProductionContext context, IMethodSymbol methodSymbol, INamedTypeSymbol? controlTypeSymbol) { - if (controlTypeSymbol is not null && controlTypeSymbol.Constructors.Any(x => !x.Parameters.IsEmpty)) + if (controlTypeSymbol is not null && controlTypeSymbol.Constructors.Any(x => x.DeclaredAccessibility == Accessibility.Public && !x.Parameters.IsEmpty)) { context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptors.TestControlHasConstructorWithParameters, methodSymbol.Locations.FirstOrDefault(), controlTypeSymbol.Name)); return; From d73a60ae678d9e6e56e3df88a74f3e1e11a78122 Mon Sep 17 00:00:00 2001 From: Arlo Godfrey Date: Wed, 15 Jun 2022 18:48:20 -0500 Subject: [PATCH 09/15] Fixed test setup/cleanup invoking on the wrong thread --- .../VisualUITestBase.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/common/CommunityToolkit.Labs.UnitTests.Shared/VisualUITestBase.cs b/common/CommunityToolkit.Labs.UnitTests.Shared/VisualUITestBase.cs index 41136c461..11d5ae41e 100644 --- a/common/CommunityToolkit.Labs.UnitTests.Shared/VisualUITestBase.cs +++ b/common/CommunityToolkit.Labs.UnitTests.Shared/VisualUITestBase.cs @@ -116,22 +116,22 @@ void OnUnloaded(object sender, RoutedEventArgs args) } [TestInitialize] - public virtual async Task TestSetup() + public virtual Task TestSetup() => EnqueueAsync(async () => { // Make sure every test starts with a clean slate, even if it doesn't use LoadTestContentAsync. if (App.ContentRoot is FrameworkElement element) await UnloadTestContentAsync(element); Assert.IsNull(App.ContentRoot); - } + }); [TestCleanup] - public virtual async Task TestCleanup() + public virtual Task TestCleanup() => EnqueueAsync(async () => { // Make sure every test ends with a clean slate, even if it doesn't use LoadTestContentAsync. if (App.ContentRoot is FrameworkElement element) await UnloadTestContentAsync(element); Assert.IsNull(App.ContentRoot); - } + }); } From 0d69c75699f64d46674b387e05919c9441d6c235 Mon Sep 17 00:00:00 2001 From: Arlo Godfrey Date: Wed, 15 Jun 2022 20:08:09 -0500 Subject: [PATCH 10/15] Added new source generator to individual project solutions --- labs/CanvasLayout/CanvasLayout.sln | 23 ++++++ labs/SizerBase/SizerBase.sln | 115 +++++++++++++++++------------ template/lab/ProjectTemplate.sln | 109 ++++++++++++++++----------- 3 files changed, 158 insertions(+), 89 deletions(-) diff --git a/labs/CanvasLayout/CanvasLayout.sln b/labs/CanvasLayout/CanvasLayout.sln index 8b9a9c8b9..73ecd111d 100644 --- a/labs/CanvasLayout/CanvasLayout.sln +++ b/labs/CanvasLayout/CanvasLayout.sln @@ -29,6 +29,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CanvasLayout.UnitTests.Uwp" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Labs Dependencies", "Labs Dependencies", "{EC5804D7-C94B-4CF8-8344-132F2A58F88E}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod", "..\..\common\CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod\CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.csproj", "{563D0F3A-84BF-4286-8652-84230D0468D9}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution tests\CanvasLayout.Tests\CanvasLayout.Tests.projitems*{2c7a065b-9349-4071-af95-e88f320a09a9}*SharedItemsImports = 13 @@ -269,6 +271,26 @@ Global {F17EE40D-CEE8-402F-AA43-45A7F87ACBF3}.Release|x86.ActiveCfg = Release|x86 {F17EE40D-CEE8-402F-AA43-45A7F87ACBF3}.Release|x86.Build.0 = Release|x86 {F17EE40D-CEE8-402F-AA43-45A7F87ACBF3}.Release|x86.Deploy.0 = Release|x86 + {563D0F3A-84BF-4286-8652-84230D0468D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {563D0F3A-84BF-4286-8652-84230D0468D9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {563D0F3A-84BF-4286-8652-84230D0468D9}.Debug|ARM.ActiveCfg = Debug|Any CPU + {563D0F3A-84BF-4286-8652-84230D0468D9}.Debug|ARM.Build.0 = Debug|Any CPU + {563D0F3A-84BF-4286-8652-84230D0468D9}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {563D0F3A-84BF-4286-8652-84230D0468D9}.Debug|ARM64.Build.0 = Debug|Any CPU + {563D0F3A-84BF-4286-8652-84230D0468D9}.Debug|x64.ActiveCfg = Debug|Any CPU + {563D0F3A-84BF-4286-8652-84230D0468D9}.Debug|x64.Build.0 = Debug|Any CPU + {563D0F3A-84BF-4286-8652-84230D0468D9}.Debug|x86.ActiveCfg = Debug|Any CPU + {563D0F3A-84BF-4286-8652-84230D0468D9}.Debug|x86.Build.0 = Debug|Any CPU + {563D0F3A-84BF-4286-8652-84230D0468D9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {563D0F3A-84BF-4286-8652-84230D0468D9}.Release|Any CPU.Build.0 = Release|Any CPU + {563D0F3A-84BF-4286-8652-84230D0468D9}.Release|ARM.ActiveCfg = Release|Any CPU + {563D0F3A-84BF-4286-8652-84230D0468D9}.Release|ARM.Build.0 = Release|Any CPU + {563D0F3A-84BF-4286-8652-84230D0468D9}.Release|ARM64.ActiveCfg = Release|Any CPU + {563D0F3A-84BF-4286-8652-84230D0468D9}.Release|ARM64.Build.0 = Release|Any CPU + {563D0F3A-84BF-4286-8652-84230D0468D9}.Release|x64.ActiveCfg = Release|Any CPU + {563D0F3A-84BF-4286-8652-84230D0468D9}.Release|x64.Build.0 = Release|Any CPU + {563D0F3A-84BF-4286-8652-84230D0468D9}.Release|x86.ActiveCfg = Release|Any CPU + {563D0F3A-84BF-4286-8652-84230D0468D9}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -281,6 +303,7 @@ Global {B3090946-9399-43C6-AF7C-2E21F5C93935} = {EC5804D7-C94B-4CF8-8344-132F2A58F88E} {DD3E4652-114A-4614-8904-1DBCE62589CC} = {69AFC3EC-DC11-4031-8624-0E9F942D19C3} {F17EE40D-CEE8-402F-AA43-45A7F87ACBF3} = {69AFC3EC-DC11-4031-8624-0E9F942D19C3} + {563D0F3A-84BF-4286-8652-84230D0468D9} = {EC5804D7-C94B-4CF8-8344-132F2A58F88E} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {6D11723C-7575-40DF-9BC9-300A09554B5D} diff --git a/labs/SizerBase/SizerBase.sln b/labs/SizerBase/SizerBase.sln index 3fa65bbe9..7f36fa8a8 100644 --- a/labs/SizerBase/SizerBase.sln +++ b/labs/SizerBase/SizerBase.sln @@ -29,12 +29,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SizerBase.UnitTests.Uwp", " EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Labs Dependencies", "Labs Dependencies", "{F3172F4D-9A5B-4B0E-8461-EAC2BC6230A5}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod", "..\..\common\CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod\CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.csproj", "{7B345661-551E-433A-B26A-0168788D1B3B}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution - tests\SizerBase.Tests\SizerBase.Tests.projitems*{CC1804EF-1663-464A-AC8C-C38836CB70AF}*SharedItemsImports = 13 - ..\..\common\CommunityToolkit.Labs.Shared\CommunityToolkit.Labs.Shared.projitems*{1F1E15B8-C732-411C-9273-F48702E8E4E4}*SharedItemsImports = 4 - tests\SizerBase.Tests\SizerBase.Tests.projitems*{03580331-0918-4A52-9A9E-C50061A226CF}*SharedItemsImports = 4 - tests\SizerBase.Tests\SizerBase.Tests.projitems*{B914E653-2225-442E-9250-D8EF639777C3}*SharedItemsImports = 5 + tests\SizerBase.Tests\SizerBase.Tests.projitems*{03580331-0918-4a52-9a9e-c50061a226cf}*SharedItemsImports = 4 + ..\..\common\CommunityToolkit.Labs.Shared\CommunityToolkit.Labs.Shared.projitems*{1f1e15b8-c732-411c-9273-f48702e8e4e4}*SharedItemsImports = 4 + tests\SizerBase.Tests\SizerBase.Tests.projitems*{b914e653-2225-442e-9250-d8ef639777c3}*SharedItemsImports = 5 + tests\SizerBase.Tests\SizerBase.Tests.projitems*{cc1804ef-1663-464a-ac8c-c38836cb70af}*SharedItemsImports = 13 EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -169,6 +171,46 @@ Global {70DF1194-D158-473E-B350-F630231FB328}.Release|x86.ActiveCfg = Release|x86 {70DF1194-D158-473E-B350-F630231FB328}.Release|x86.Build.0 = Release|x86 {70DF1194-D158-473E-B350-F630231FB328}.Release|x86.Deploy.0 = Release|x86 + {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Debug|ARM.ActiveCfg = Debug|Any CPU + {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Debug|ARM.Build.0 = Debug|Any CPU + {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Debug|ARM64.Build.0 = Debug|Any CPU + {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Debug|x64.ActiveCfg = Debug|Any CPU + {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Debug|x64.Build.0 = Debug|Any CPU + {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Debug|x86.ActiveCfg = Debug|Any CPU + {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Debug|x86.Build.0 = Debug|Any CPU + {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Release|Any CPU.Build.0 = Release|Any CPU + {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Release|ARM.ActiveCfg = Release|Any CPU + {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Release|ARM.Build.0 = Release|Any CPU + {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Release|ARM64.ActiveCfg = Release|Any CPU + {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Release|ARM64.Build.0 = Release|Any CPU + {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Release|x64.ActiveCfg = Release|Any CPU + {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Release|x64.Build.0 = Release|Any CPU + {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Release|x86.ActiveCfg = Release|Any CPU + {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Release|x86.Build.0 = Release|Any CPU + {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Debug|ARM.ActiveCfg = Debug|Any CPU + {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Debug|ARM.Build.0 = Debug|Any CPU + {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Debug|ARM64.Build.0 = Debug|Any CPU + {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Debug|x64.ActiveCfg = Debug|Any CPU + {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Debug|x64.Build.0 = Debug|Any CPU + {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Debug|x86.ActiveCfg = Debug|Any CPU + {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Debug|x86.Build.0 = Debug|Any CPU + {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Release|Any CPU.Build.0 = Release|Any CPU + {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Release|ARM.ActiveCfg = Release|Any CPU + {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Release|ARM.Build.0 = Release|Any CPU + {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Release|ARM64.ActiveCfg = Release|Any CPU + {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Release|ARM64.Build.0 = Release|Any CPU + {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Release|x64.ActiveCfg = Release|Any CPU + {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Release|x64.Build.0 = Release|Any CPU + {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Release|x86.ActiveCfg = Release|Any CPU + {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Release|x86.Build.0 = Release|Any CPU {B914E653-2225-442E-9250-D8EF639777C3}.Debug|Any CPU.ActiveCfg = Debug|x64 {B914E653-2225-442E-9250-D8EF639777C3}.Debug|Any CPU.Build.0 = Debug|x64 {B914E653-2225-442E-9250-D8EF639777C3}.Debug|Any CPU.Deploy.0 = Debug|x64 @@ -229,46 +271,26 @@ Global {03580331-0918-4A52-9A9E-C50061A226CF}.Release|x86.ActiveCfg = Release|x86 {03580331-0918-4A52-9A9E-C50061A226CF}.Release|x86.Build.0 = Release|x86 {03580331-0918-4A52-9A9E-C50061A226CF}.Release|x86.Deploy.0 = Release|x86 - {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Debug|ARM.ActiveCfg = Debug|Any CPU - {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Debug|ARM.Build.0 = Debug|Any CPU - {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Debug|ARM64.ActiveCfg = Debug|Any CPU - {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Debug|ARM64.Build.0 = Debug|Any CPU - {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Debug|x64.ActiveCfg = Debug|Any CPU - {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Debug|x64.Build.0 = Debug|Any CPU - {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Debug|x86.ActiveCfg = Debug|Any CPU - {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Debug|x86.Build.0 = Debug|Any CPU - {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Release|Any CPU.Build.0 = Release|Any CPU - {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Release|ARM.ActiveCfg = Release|Any CPU - {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Release|ARM.Build.0 = Release|Any CPU - {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Release|ARM64.ActiveCfg = Release|Any CPU - {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Release|ARM64.Build.0 = Release|Any CPU - {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Release|x64.ActiveCfg = Release|Any CPU - {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Release|x64.Build.0 = Release|Any CPU - {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Release|x86.ActiveCfg = Release|Any CPU - {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Release|x86.Build.0 = Release|Any CPU - {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Debug|ARM.ActiveCfg = Debug|Any CPU - {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Debug|ARM.Build.0 = Debug|Any CPU - {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Debug|ARM64.ActiveCfg = Debug|Any CPU - {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Debug|ARM64.Build.0 = Debug|Any CPU - {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Debug|x64.ActiveCfg = Debug|Any CPU - {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Debug|x64.Build.0 = Debug|Any CPU - {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Debug|x86.ActiveCfg = Debug|Any CPU - {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Debug|x86.Build.0 = Debug|Any CPU - {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Release|Any CPU.Build.0 = Release|Any CPU - {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Release|ARM.ActiveCfg = Release|Any CPU - {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Release|ARM.Build.0 = Release|Any CPU - {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Release|ARM64.ActiveCfg = Release|Any CPU - {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Release|ARM64.Build.0 = Release|Any CPU - {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Release|x64.ActiveCfg = Release|Any CPU - {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Release|x64.Build.0 = Release|Any CPU - {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Release|x86.ActiveCfg = Release|Any CPU - {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Release|x86.Build.0 = Release|Any CPU + {7B345661-551E-433A-B26A-0168788D1B3B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7B345661-551E-433A-B26A-0168788D1B3B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7B345661-551E-433A-B26A-0168788D1B3B}.Debug|ARM.ActiveCfg = Debug|Any CPU + {7B345661-551E-433A-B26A-0168788D1B3B}.Debug|ARM.Build.0 = Debug|Any CPU + {7B345661-551E-433A-B26A-0168788D1B3B}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {7B345661-551E-433A-B26A-0168788D1B3B}.Debug|ARM64.Build.0 = Debug|Any CPU + {7B345661-551E-433A-B26A-0168788D1B3B}.Debug|x64.ActiveCfg = Debug|Any CPU + {7B345661-551E-433A-B26A-0168788D1B3B}.Debug|x64.Build.0 = Debug|Any CPU + {7B345661-551E-433A-B26A-0168788D1B3B}.Debug|x86.ActiveCfg = Debug|Any CPU + {7B345661-551E-433A-B26A-0168788D1B3B}.Debug|x86.Build.0 = Debug|Any CPU + {7B345661-551E-433A-B26A-0168788D1B3B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7B345661-551E-433A-B26A-0168788D1B3B}.Release|Any CPU.Build.0 = Release|Any CPU + {7B345661-551E-433A-B26A-0168788D1B3B}.Release|ARM.ActiveCfg = Release|Any CPU + {7B345661-551E-433A-B26A-0168788D1B3B}.Release|ARM.Build.0 = Release|Any CPU + {7B345661-551E-433A-B26A-0168788D1B3B}.Release|ARM64.ActiveCfg = Release|Any CPU + {7B345661-551E-433A-B26A-0168788D1B3B}.Release|ARM64.Build.0 = Release|Any CPU + {7B345661-551E-433A-B26A-0168788D1B3B}.Release|x64.ActiveCfg = Release|Any CPU + {7B345661-551E-433A-B26A-0168788D1B3B}.Release|x64.Build.0 = Release|Any CPU + {7B345661-551E-433A-B26A-0168788D1B3B}.Release|x86.ActiveCfg = Release|Any CPU + {7B345661-551E-433A-B26A-0168788D1B3B}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -277,10 +299,11 @@ Global {1F1E15B8-C732-411C-9273-F48702E8E4E4} = {EF78C64C-AEA9-4A35-AF14-40C392925F47} {50B1FC51-ECC0-480D-80C2-4957622BA2F6} = {EF78C64C-AEA9-4A35-AF14-40C392925F47} {70DF1194-D158-473E-B350-F630231FB328} = {EF78C64C-AEA9-4A35-AF14-40C392925F47} - {B914E653-2225-442E-9250-D8EF639777C3} = {40D1D98D-ED3A-473F-990F-3473948F5E3D} - {03580331-0918-4A52-9A9E-C50061A226CF} = {40D1D98D-ED3A-473F-990F-3473948F5E3D} {66E6DA8A-FEFC-4221-A476-4314A4D692F6} = {F3172F4D-9A5B-4B0E-8461-EAC2BC6230A5} {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91} = {F3172F4D-9A5B-4B0E-8461-EAC2BC6230A5} + {B914E653-2225-442E-9250-D8EF639777C3} = {40D1D98D-ED3A-473F-990F-3473948F5E3D} + {03580331-0918-4A52-9A9E-C50061A226CF} = {40D1D98D-ED3A-473F-990F-3473948F5E3D} + {7B345661-551E-433A-B26A-0168788D1B3B} = {F3172F4D-9A5B-4B0E-8461-EAC2BC6230A5} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {ACDAC286-33A3-40F4-884D-16C359D857AE} diff --git a/template/lab/ProjectTemplate.sln b/template/lab/ProjectTemplate.sln index e0fd54ef4..38d65f553 100644 --- a/template/lab/ProjectTemplate.sln +++ b/template/lab/ProjectTemplate.sln @@ -29,11 +29,13 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProjectTemplate.UnitTests.U EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Labs Dependencies", "Labs Dependencies", "{D4DCFA4B-63B3-4D93-8C91-FBEE3B92E5A0}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod", "..\..\common\CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod\CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.csproj", "{79F79471-9947-45F5-81FE-4EBE2B8D0B1D}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution tests\ProjectTemplate.Tests\ProjectTemplate.Tests.projitems*{19ac7a6a-b001-45aa-acbe-0536f688db74}*SharedItemsImports = 13 - ..\..\common\CommunityToolkit.Labs.Shared\CommunityToolkit.Labs.Shared.projitems*{e25bf6d0-24d6-459c-a180-1e9405d59f87}*SharedItemsImports = 4 tests\ProjectTemplate.Tests\ProjectTemplate.Tests.projitems*{7134bd2e-0a74-4345-868a-e425fc452a89}*SharedItemsImports = 4 + ..\..\common\CommunityToolkit.Labs.Shared\CommunityToolkit.Labs.Shared.projitems*{e25bf6d0-24d6-459c-a180-1e9405d59f87}*SharedItemsImports = 4 tests\ProjectTemplate.Tests\ProjectTemplate.Tests.projitems*{fe390707-a244-460a-bc1c-5559038a1975}*SharedItemsImports = 5 EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -169,6 +171,46 @@ Global {ED318A01-D129-4F06-A9EB-8D911000E243}.Release|x86.ActiveCfg = Release|x86 {ED318A01-D129-4F06-A9EB-8D911000E243}.Release|x86.Build.0 = Release|x86 {ED318A01-D129-4F06-A9EB-8D911000E243}.Release|x86.Deploy.0 = Release|x86 + {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Debug|ARM.ActiveCfg = Debug|Any CPU + {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Debug|ARM.Build.0 = Debug|Any CPU + {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Debug|ARM64.Build.0 = Debug|Any CPU + {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Debug|x64.ActiveCfg = Debug|Any CPU + {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Debug|x64.Build.0 = Debug|Any CPU + {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Debug|x86.ActiveCfg = Debug|Any CPU + {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Debug|x86.Build.0 = Debug|Any CPU + {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Release|Any CPU.Build.0 = Release|Any CPU + {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Release|ARM.ActiveCfg = Release|Any CPU + {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Release|ARM.Build.0 = Release|Any CPU + {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Release|ARM64.ActiveCfg = Release|Any CPU + {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Release|ARM64.Build.0 = Release|Any CPU + {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Release|x64.ActiveCfg = Release|Any CPU + {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Release|x64.Build.0 = Release|Any CPU + {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Release|x86.ActiveCfg = Release|Any CPU + {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Release|x86.Build.0 = Release|Any CPU + {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Debug|ARM.ActiveCfg = Debug|Any CPU + {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Debug|ARM.Build.0 = Debug|Any CPU + {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Debug|ARM64.Build.0 = Debug|Any CPU + {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Debug|x64.ActiveCfg = Debug|Any CPU + {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Debug|x64.Build.0 = Debug|Any CPU + {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Debug|x86.ActiveCfg = Debug|Any CPU + {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Debug|x86.Build.0 = Debug|Any CPU + {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Release|Any CPU.Build.0 = Release|Any CPU + {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Release|ARM.ActiveCfg = Release|Any CPU + {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Release|ARM.Build.0 = Release|Any CPU + {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Release|ARM64.ActiveCfg = Release|Any CPU + {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Release|ARM64.Build.0 = Release|Any CPU + {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Release|x64.ActiveCfg = Release|Any CPU + {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Release|x64.Build.0 = Release|Any CPU + {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Release|x86.ActiveCfg = Release|Any CPU + {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Release|x86.Build.0 = Release|Any CPU {FE390707-A244-460A-BC1C-5559038A1975}.Debug|Any CPU.ActiveCfg = Debug|x64 {FE390707-A244-460A-BC1C-5559038A1975}.Debug|Any CPU.Build.0 = Debug|x64 {FE390707-A244-460A-BC1C-5559038A1975}.Debug|Any CPU.Deploy.0 = Debug|x64 @@ -229,46 +271,26 @@ Global {7134BD2E-0A74-4345-868A-E425FC452A89}.Release|x86.ActiveCfg = Release|x86 {7134BD2E-0A74-4345-868A-E425FC452A89}.Release|x86.Build.0 = Release|x86 {7134BD2E-0A74-4345-868A-E425FC452A89}.Release|x86.Deploy.0 = Release|x86 - {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Debug|ARM.ActiveCfg = Debug|Any CPU - {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Debug|ARM.Build.0 = Debug|Any CPU - {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Debug|ARM64.ActiveCfg = Debug|Any CPU - {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Debug|ARM64.Build.0 = Debug|Any CPU - {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Debug|x64.ActiveCfg = Debug|Any CPU - {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Debug|x64.Build.0 = Debug|Any CPU - {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Debug|x86.ActiveCfg = Debug|Any CPU - {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Debug|x86.Build.0 = Debug|Any CPU - {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Release|Any CPU.Build.0 = Release|Any CPU - {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Release|ARM.ActiveCfg = Release|Any CPU - {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Release|ARM.Build.0 = Release|Any CPU - {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Release|ARM64.ActiveCfg = Release|Any CPU - {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Release|ARM64.Build.0 = Release|Any CPU - {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Release|x64.ActiveCfg = Release|Any CPU - {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Release|x64.Build.0 = Release|Any CPU - {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Release|x86.ActiveCfg = Release|Any CPU - {66E6DA8A-FEFC-4221-A476-4314A4D692F6}.Release|x86.Build.0 = Release|Any CPU - {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Debug|ARM.ActiveCfg = Debug|Any CPU - {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Debug|ARM.Build.0 = Debug|Any CPU - {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Debug|ARM64.ActiveCfg = Debug|Any CPU - {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Debug|ARM64.Build.0 = Debug|Any CPU - {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Debug|x64.ActiveCfg = Debug|Any CPU - {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Debug|x64.Build.0 = Debug|Any CPU - {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Debug|x86.ActiveCfg = Debug|Any CPU - {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Debug|x86.Build.0 = Debug|Any CPU - {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Release|Any CPU.Build.0 = Release|Any CPU - {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Release|ARM.ActiveCfg = Release|Any CPU - {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Release|ARM.Build.0 = Release|Any CPU - {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Release|ARM64.ActiveCfg = Release|Any CPU - {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Release|ARM64.Build.0 = Release|Any CPU - {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Release|x64.ActiveCfg = Release|Any CPU - {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Release|x64.Build.0 = Release|Any CPU - {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Release|x86.ActiveCfg = Release|Any CPU - {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91}.Release|x86.Build.0 = Release|Any CPU + {79F79471-9947-45F5-81FE-4EBE2B8D0B1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {79F79471-9947-45F5-81FE-4EBE2B8D0B1D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {79F79471-9947-45F5-81FE-4EBE2B8D0B1D}.Debug|ARM.ActiveCfg = Debug|Any CPU + {79F79471-9947-45F5-81FE-4EBE2B8D0B1D}.Debug|ARM.Build.0 = Debug|Any CPU + {79F79471-9947-45F5-81FE-4EBE2B8D0B1D}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {79F79471-9947-45F5-81FE-4EBE2B8D0B1D}.Debug|ARM64.Build.0 = Debug|Any CPU + {79F79471-9947-45F5-81FE-4EBE2B8D0B1D}.Debug|x64.ActiveCfg = Debug|Any CPU + {79F79471-9947-45F5-81FE-4EBE2B8D0B1D}.Debug|x64.Build.0 = Debug|Any CPU + {79F79471-9947-45F5-81FE-4EBE2B8D0B1D}.Debug|x86.ActiveCfg = Debug|Any CPU + {79F79471-9947-45F5-81FE-4EBE2B8D0B1D}.Debug|x86.Build.0 = Debug|Any CPU + {79F79471-9947-45F5-81FE-4EBE2B8D0B1D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {79F79471-9947-45F5-81FE-4EBE2B8D0B1D}.Release|Any CPU.Build.0 = Release|Any CPU + {79F79471-9947-45F5-81FE-4EBE2B8D0B1D}.Release|ARM.ActiveCfg = Release|Any CPU + {79F79471-9947-45F5-81FE-4EBE2B8D0B1D}.Release|ARM.Build.0 = Release|Any CPU + {79F79471-9947-45F5-81FE-4EBE2B8D0B1D}.Release|ARM64.ActiveCfg = Release|Any CPU + {79F79471-9947-45F5-81FE-4EBE2B8D0B1D}.Release|ARM64.Build.0 = Release|Any CPU + {79F79471-9947-45F5-81FE-4EBE2B8D0B1D}.Release|x64.ActiveCfg = Release|Any CPU + {79F79471-9947-45F5-81FE-4EBE2B8D0B1D}.Release|x64.Build.0 = Release|Any CPU + {79F79471-9947-45F5-81FE-4EBE2B8D0B1D}.Release|x86.ActiveCfg = Release|Any CPU + {79F79471-9947-45F5-81FE-4EBE2B8D0B1D}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -277,10 +299,11 @@ Global {E25BF6D0-24D6-459C-A180-1E9405D59F87} = {ACD20BEE-45EF-4CAB-BA06-40F38A0C98EA} {507C24FC-DD1C-4B91-BB6F-4A4CFB30A252} = {ACD20BEE-45EF-4CAB-BA06-40F38A0C98EA} {ED318A01-D129-4F06-A9EB-8D911000E243} = {ACD20BEE-45EF-4CAB-BA06-40F38A0C98EA} - {FE390707-A244-460A-BC1C-5559038A1975} = {7F03DDE8-FC53-451D-B0A4-E852EC44C0E9} - {7134BD2E-0A74-4345-868A-E425FC452A89} = {7F03DDE8-FC53-451D-B0A4-E852EC44C0E9} {66E6DA8A-FEFC-4221-A476-4314A4D692F6} = {D4DCFA4B-63B3-4D93-8C91-FBEE3B92E5A0} {7A1E5FBF-B51A-4B56-8EA3-6AB56A7E9D91} = {D4DCFA4B-63B3-4D93-8C91-FBEE3B92E5A0} + {FE390707-A244-460A-BC1C-5559038A1975} = {7F03DDE8-FC53-451D-B0A4-E852EC44C0E9} + {7134BD2E-0A74-4345-868A-E425FC452A89} = {7F03DDE8-FC53-451D-B0A4-E852EC44C0E9} + {79F79471-9947-45F5-81FE-4EBE2B8D0B1D} = {D4DCFA4B-63B3-4D93-8C91-FBEE3B92E5A0} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A17E5FCF-1282-4567-A5BC-811F2F45DD7E} From 7973bfd6079be9b2a0a03b59358d697f2a0c7ffb Mon Sep 17 00:00:00 2001 From: Arlo Godfrey Date: Wed, 22 Jun 2022 11:36:22 -0500 Subject: [PATCH 11/15] Reverted to toolkit extension for dispatcher EnqueueAsync --- .../VisualUITestBase.cs | 48 +++---------------- 1 file changed, 6 insertions(+), 42 deletions(-) diff --git a/common/CommunityToolkit.Labs.UnitTests.Shared/VisualUITestBase.cs b/common/CommunityToolkit.Labs.UnitTests.Shared/VisualUITestBase.cs index 11d5ae41e..1127cb507 100644 --- a/common/CommunityToolkit.Labs.UnitTests.Shared/VisualUITestBase.cs +++ b/common/CommunityToolkit.Labs.UnitTests.Shared/VisualUITestBase.cs @@ -25,50 +25,14 @@ namespace CommunityToolkit.Labs.UnitTests; /// public class VisualUITestBase { - protected Task EnqueueAsync(Func> function) - { - var taskCompletionSource = new TaskCompletionSource(); - - var addedToQueue = App.DispatcherQueue.TryEnqueue(async () => - { - var res = await function(); - taskCompletionSource?.TrySetResult(res); - }); - - Assert.IsTrue(addedToQueue); - - return taskCompletionSource.Task; - } - - protected Task EnqueueAsync(Func function) - { - var taskCompletionSource = new TaskCompletionSource(); - - var addedToQueue = App.DispatcherQueue.TryEnqueue(async () => - { - await function(); - taskCompletionSource?.TrySetResult(null); - }); - - Assert.IsTrue(addedToQueue); - - return taskCompletionSource.Task; - } - - protected Task EnqueueAsync(Action function) - { - var taskCompletionSource = new TaskCompletionSource(); + // Used by source generators to dispatch to the UI thread + // Methods must be declared or interfaced for compatibility with the source generator's unit tests. - var addedToQueue = App.DispatcherQueue.TryEnqueue(() => - { - function(); - taskCompletionSource?.TrySetResult(null); - }); + protected Task EnqueueAsync(Func> function) => App.DispatcherQueue.EnqueueAsync(function); - Assert.IsTrue(addedToQueue); + protected Task EnqueueAsync(Func function) => App.DispatcherQueue.EnqueueAsync(function); - return taskCompletionSource.Task; - } + protected Task EnqueueAsync(Action function) => App.DispatcherQueue.EnqueueAsync(function); /// /// Sets the content of the test app to a simple to load into the visual tree. @@ -77,7 +41,7 @@ protected Task EnqueueAsync(Action function) /// Content to set in test app. /// When UI is loaded. protected async Task LoadTestContentAsync(FrameworkElement content) - { + { var taskCompletionSource = new TaskCompletionSource(); content.Loaded += OnLoaded; From 2cd1c956a7eb3518c034a272c510deb6242752a7 Mon Sep 17 00:00:00 2001 From: Arlo Godfrey Date: Wed, 22 Jun 2022 11:36:32 -0500 Subject: [PATCH 12/15] Remove whitespace --- .../SizerBase/tests/SizerBase.Tests/ExampleSizerBaseTestClass.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/labs/SizerBase/tests/SizerBase.Tests/ExampleSizerBaseTestClass.cs b/labs/SizerBase/tests/SizerBase.Tests/ExampleSizerBaseTestClass.cs index cbfc3b3f1..463444006 100644 --- a/labs/SizerBase/tests/SizerBase.Tests/ExampleSizerBaseTestClass.cs +++ b/labs/SizerBase/tests/SizerBase.Tests/ExampleSizerBaseTestClass.cs @@ -35,7 +35,6 @@ namespace SizerBase.Tests; [TestClass] public partial class ExampleSizerBaseTestClass : VisualUITestBase { - [TestMethod] public void Just_an_example_test() { From cc33331af17dd45086c607c1276ad53f5c687360 Mon Sep 17 00:00:00 2001 From: Arlo Godfrey Date: Wed, 22 Jun 2022 14:38:03 -0500 Subject: [PATCH 13/15] Fixed non file-scoped namespaces --- .../LabsUITestMethodTests.cs | 211 +++++++++--------- .../Diagnostics/DiagnosticDescriptors.cs | 37 ++- 2 files changed, 123 insertions(+), 125 deletions(-) diff --git a/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.Tests/LabsUITestMethodTests.cs b/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.Tests/LabsUITestMethodTests.cs index 6a5491933..84dbceefe 100644 --- a/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.Tests/LabsUITestMethodTests.cs +++ b/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.Tests/LabsUITestMethodTests.cs @@ -8,12 +8,12 @@ using System.Collections.Immutable; using System.Linq; -namespace CommunityToolkit.Labs.Core.SourceGenerators.Tests +namespace CommunityToolkit.Labs.Core.SourceGenerators.Tests; + +[TestClass] +public partial class LabsUITestMethodTests { - [TestClass] - public partial class LabsUITestMethodTests - { - private const string DispatcherQueueDefinition = @" + private const string DispatcherQueueDefinition = @" namespace MyApp { public partial class Test @@ -25,10 +25,10 @@ public partial class Test } "; - [TestMethod] - public void TestControlHasConstructorWithParameters() - { - string source = @" + [TestMethod] + public void TestControlHasConstructorWithParameters() + { + string source = @" using System.ComponentModel; using CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod; @@ -58,13 +58,13 @@ namespace Microsoft.UI.Xaml public class FrameworkElement { } }"; - VerifyGeneratedDiagnostics(source + DispatcherQueueDefinition, DiagnosticDescriptors.TestControlHasConstructorWithParameters.Id); - } + VerifyGeneratedDiagnostics(source + DispatcherQueueDefinition, DiagnosticDescriptors.TestControlHasConstructorWithParameters.Id); + } - [TestMethod] - public void Async_Mux_NoErrors() - { - string source = @" + [TestMethod] + public void Async_Mux_NoErrors() + { + string source = @" using System.ComponentModel; using CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod; @@ -91,13 +91,13 @@ namespace Microsoft.UI.Xaml public class FrameworkElement { } }"; - VerifyGeneratedDiagnostics(source + DispatcherQueueDefinition); - } + VerifyGeneratedDiagnostics(source + DispatcherQueueDefinition); + } - [TestMethod] - public void Async_Wux_NoErrors() - { - string source = @" + [TestMethod] + public void Async_Wux_NoErrors() + { + string source = @" using System.ComponentModel; using CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod; @@ -124,13 +124,13 @@ namespace Windows.UI.Xaml public class FrameworkElement { } }"; - VerifyGeneratedDiagnostics(source + DispatcherQueueDefinition); - } + VerifyGeneratedDiagnostics(source + DispatcherQueueDefinition); + } - [TestMethod] - public void Async_NoMethodParams_NoErrors() - { - string source = @" + [TestMethod] + public void Async_NoMethodParams_NoErrors() + { + string source = @" using System.ComponentModel; using CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod; @@ -145,13 +145,13 @@ public async System.Threading.Tasks.Task TestMethod() } }"; - VerifyGeneratedDiagnostics(source + DispatcherQueueDefinition); - } + VerifyGeneratedDiagnostics(source + DispatcherQueueDefinition); + } - [TestMethod] - public void Synchronous_Mux_NoErrors() - { - string source = @" + [TestMethod] + public void Synchronous_Mux_NoErrors() + { + string source = @" using System.ComponentModel; using CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod; @@ -178,13 +178,13 @@ namespace Microsoft.UI.Xaml public class FrameworkElement { } }"; - VerifyGeneratedDiagnostics(source + DispatcherQueueDefinition); - } + VerifyGeneratedDiagnostics(source + DispatcherQueueDefinition); + } - [TestMethod] - public void Synchronous_Wux_NoErrors() - { - string source = @" + [TestMethod] + public void Synchronous_Wux_NoErrors() + { + string source = @" using System.ComponentModel; using CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod; @@ -211,13 +211,13 @@ namespace Windows.UI.Xaml public class FrameworkElement { } }"; - VerifyGeneratedDiagnostics(source + DispatcherQueueDefinition); - } + VerifyGeneratedDiagnostics(source + DispatcherQueueDefinition); + } - [TestMethod] - public void Synchronous_NoMethodParams_NoErrors() - { - string source = @" + [TestMethod] + public void Synchronous_NoMethodParams_NoErrors() + { + string source = @" using System.ComponentModel; using CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod; @@ -232,66 +232,65 @@ public void TestMethod() } }"; - VerifyGeneratedDiagnostics(source + DispatcherQueueDefinition); - } - - /// - /// Verifies the output of a source generator. - /// - /// The generator type to use. - /// The input source to process. - /// The input documentation info to process. - /// The diagnostic ids to expect for the input source code. - private static void VerifyGeneratedDiagnostics(string source, params string[] diagnosticsIds) - where TGenerator : class, IIncrementalGenerator, new() - { - VerifyGeneratedDiagnostics(CSharpSyntaxTree.ParseText(source), diagnosticsIds); - } - - /// - /// Verifies the output of a source generator. - /// - /// The generator type to use. - /// The input source tree to process. - /// The input documentation info to process. - /// The diagnostic ids to expect for the input source code. - private static void VerifyGeneratedDiagnostics(SyntaxTree syntaxTree, params string[] diagnosticsIds) - where TGenerator : class, IIncrementalGenerator, new() - { - var attributeType = typeof(LabsUITestMethodAttribute); - - var references = - from assembly in AppDomain.CurrentDomain.GetAssemblies() - where !assembly.IsDynamic - let reference = MetadataReference.CreateFromFile(assembly.Location) - select reference; - - var compilation = CSharpCompilation.Create( - "original.Sample", - new[] { syntaxTree }, - references, - new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); - - var compilationDiagnostics = compilation.GetDiagnostics(); - - Assert.IsTrue(compilationDiagnostics.All(x => x.Severity != DiagnosticSeverity.Error), $"Expected no compilation errors before source generation. Got: \n{string.Join("\n", compilationDiagnostics.Where(x => x.Severity == DiagnosticSeverity.Error).Select(x => $"[{x.Id}: {x.GetMessage()}]"))}"); - - IIncrementalGenerator generator = new TGenerator(); - - GeneratorDriver driver = - CSharpGeneratorDriver - .Create(generator) - .WithUpdatedParseOptions((CSharpParseOptions)syntaxTree.Options); - - _ = driver.RunGeneratorsAndUpdateCompilation(compilation, out Compilation outputCompilation, out ImmutableArray diagnostics); - - HashSet resultingIds = diagnostics.Select(diagnostic => diagnostic.Id).ToHashSet(); - var generatedCompilationDiaghostics = outputCompilation.GetDiagnostics(); - - Assert.IsTrue(resultingIds.SetEquals(diagnosticsIds), $"Expected one of [{string.Join(", ", diagnosticsIds)}] diagnostic Ids. Got [{string.Join(", ", resultingIds)}]"); - Assert.IsTrue(generatedCompilationDiaghostics.All(x => x.Severity != DiagnosticSeverity.Error), $"Expected no generated compilation errors. Got: \n{string.Join("\n", generatedCompilationDiaghostics.Where(x => x.Severity == DiagnosticSeverity.Error).Select(x => $"[{x.Id}: {x.GetMessage()}]"))}"); - - GC.KeepAlive(attributeType); - } + VerifyGeneratedDiagnostics(source + DispatcherQueueDefinition); + } + + /// + /// Verifies the output of a source generator. + /// + /// The generator type to use. + /// The input source to process. + /// The input documentation info to process. + /// The diagnostic ids to expect for the input source code. + private static void VerifyGeneratedDiagnostics(string source, params string[] diagnosticsIds) + where TGenerator : class, IIncrementalGenerator, new() + { + VerifyGeneratedDiagnostics(CSharpSyntaxTree.ParseText(source), diagnosticsIds); + } + + /// + /// Verifies the output of a source generator. + /// + /// The generator type to use. + /// The input source tree to process. + /// The input documentation info to process. + /// The diagnostic ids to expect for the input source code. + private static void VerifyGeneratedDiagnostics(SyntaxTree syntaxTree, params string[] diagnosticsIds) + where TGenerator : class, IIncrementalGenerator, new() + { + var attributeType = typeof(LabsUITestMethodAttribute); + + var references = + from assembly in AppDomain.CurrentDomain.GetAssemblies() + where !assembly.IsDynamic + let reference = MetadataReference.CreateFromFile(assembly.Location) + select reference; + + var compilation = CSharpCompilation.Create( + "original.Sample", + new[] { syntaxTree }, + references, + new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); + + var compilationDiagnostics = compilation.GetDiagnostics(); + + Assert.IsTrue(compilationDiagnostics.All(x => x.Severity != DiagnosticSeverity.Error), $"Expected no compilation errors before source generation. Got: \n{string.Join("\n", compilationDiagnostics.Where(x => x.Severity == DiagnosticSeverity.Error).Select(x => $"[{x.Id}: {x.GetMessage()}]"))}"); + + IIncrementalGenerator generator = new TGenerator(); + + GeneratorDriver driver = + CSharpGeneratorDriver + .Create(generator) + .WithUpdatedParseOptions((CSharpParseOptions)syntaxTree.Options); + + _ = driver.RunGeneratorsAndUpdateCompilation(compilation, out Compilation outputCompilation, out ImmutableArray diagnostics); + + HashSet resultingIds = diagnostics.Select(diagnostic => diagnostic.Id).ToHashSet(); + var generatedCompilationDiaghostics = outputCompilation.GetDiagnostics(); + + Assert.IsTrue(resultingIds.SetEquals(diagnosticsIds), $"Expected one of [{string.Join(", ", diagnosticsIds)}] diagnostic Ids. Got [{string.Join(", ", resultingIds)}]"); + Assert.IsTrue(generatedCompilationDiaghostics.All(x => x.Severity != DiagnosticSeverity.Error), $"Expected no generated compilation errors. Got: \n{string.Join("\n", generatedCompilationDiaghostics.Where(x => x.Severity == DiagnosticSeverity.Error).Select(x => $"[{x.Id}: {x.GetMessage()}]"))}"); + + GC.KeepAlive(attributeType); } } diff --git a/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod/Diagnostics/DiagnosticDescriptors.cs b/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod/Diagnostics/DiagnosticDescriptors.cs index 43bb4490e..c9a304039 100644 --- a/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod/Diagnostics/DiagnosticDescriptors.cs +++ b/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod/Diagnostics/DiagnosticDescriptors.cs @@ -4,26 +4,25 @@ using Microsoft.CodeAnalysis; -namespace CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.Diagnostics +namespace CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.Diagnostics; + +/// +/// A container for all instances for errors reported by analyzers in this project. +/// +public static class DiagnosticDescriptors { /// - /// A container for all instances for errors reported by analyzers in this project. + /// Gets a indicating that a test method decorated with asks for a control instance with a non-parameterless constructor. + /// + /// Format: "Cannot generate test with type {0} as it has a constructor with parameters.". + /// /// - public static class DiagnosticDescriptors - { - /// - /// Gets a indicating that a test method decorated with asks for a control instance with a non-parameterless constructor. - /// - /// Format: "Cannot generate test with type {0} as it has a constructor with parameters.". - /// - /// - public static readonly DiagnosticDescriptor TestControlHasConstructorWithParameters = new( - id: "LUITM0001", - title: $"Provided control must not have a constructor with parameters.", - messageFormat: $"Cannot generate test with control {{0}} as it has a constructor with parameters.", - category: typeof(LabsUITestMethodGenerator).FullName, - defaultSeverity: DiagnosticSeverity.Error, - isEnabledByDefault: true, - description: $"Cannot generate test method with provided control."); - } + public static readonly DiagnosticDescriptor TestControlHasConstructorWithParameters = new( + id: "LUITM0001", + title: $"Provided control must not have a constructor with parameters.", + messageFormat: $"Cannot generate test with control {{0}} as it has a constructor with parameters.", + category: typeof(LabsUITestMethodGenerator).FullName, + defaultSeverity: DiagnosticSeverity.Error, + isEnabledByDefault: true, + description: $"Cannot generate test method with provided control."); } From 1fc72502b0f64c6afb5de90f4408ddde8eb04262 Mon Sep 17 00:00:00 2001 From: Arlo Godfrey Date: Wed, 22 Jun 2022 15:30:41 -0500 Subject: [PATCH 14/15] Added missing file header --- .../LabsUITestMethodTests.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.Tests/LabsUITestMethodTests.cs b/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.Tests/LabsUITestMethodTests.cs index 84dbceefe..5a6240d31 100644 --- a/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.Tests/LabsUITestMethodTests.cs +++ b/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.Tests/LabsUITestMethodTests.cs @@ -1,3 +1,7 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + using CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod; using CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.Diagnostics; using Microsoft.CodeAnalysis; From 9b6d5243b174a15f2471cb57616ba024cfdbe2a1 Mon Sep 17 00:00:00 2001 From: Arlo Godfrey Date: Wed, 22 Jun 2022 15:37:23 -0500 Subject: [PATCH 15/15] Removed unused using directives --- .../LabsUITestMethodTests.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.Tests/LabsUITestMethodTests.cs b/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.Tests/LabsUITestMethodTests.cs index 5a6240d31..3656bb557 100644 --- a/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.Tests/LabsUITestMethodTests.cs +++ b/common/CommunityToolkit.Labs.Core.SourceGenerators.LabsUITestMethod.Tests/LabsUITestMethodTests.cs @@ -7,10 +7,6 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Linq; namespace CommunityToolkit.Labs.Core.SourceGenerators.Tests;