From 7d444a4331e777a0ea75478bfbba51d100956d87 Mon Sep 17 00:00:00 2001 From: Dapeng Zhang Date: Thu, 12 Sep 2024 03:57:16 +0800 Subject: [PATCH] Support to change the accessibility of a model and change a model to be struct via customized code (#4347) Fixes #4259 Fixes #4346 I change the structure of test data for those customization code related test cases, now it should be a directory with the name of the test method, in which we could put all of customization code because in quite a few scenarios we might need multiple files there. --------- Co-authored-by: m-nash <64171366+m-nash@users.noreply.github.com> --- .../src/CodeGenTypeAttribute.cs | 2 +- .../src/CodeModelPlugin.cs | 2 +- .../PostProcessing/GeneratedCodeWorkspace.cs | 11 +-- .../src/Providers/ModelProvider.cs | 72 +++++++++++++------ .../src/Providers/NamedTypeSymbolProvider.cs | 40 +++++++++++ .../test/Helpers.cs | 39 ++++++---- .../ModelProviders/ModelCustomizationTests.cs | 71 +++++++++++++++--- .../CanChangeAccessibility/MockInputModel.cs | 7 ++ .../CanChangeModelName/CustomizedModel.cs | 11 +++ .../CustomizedModel.cs | 11 +++ .../MockInputModel.cs | 13 ++++ .../CanChangePropertyName/MockInputModel.cs | 13 ++++ .../CanChangePropertyType/MockInputModel.cs | 13 ++++ .../CanChangeToStruct/MockInputModel.cs | 5 ++ .../NamedTypeSymbolProviderTests.cs | 7 ++ .../TestCustomization_CanChangeModelName.cs | 25 ------- ...mization_CanChangePropertyAccessibility.cs | 27 ------- ...TestCustomization_CanChangePropertyName.cs | 27 ------- ...TestCustomization_CanChangePropertyType.cs | 27 ------- .../ConfigurationTests/DisableDocsForType.cs | 0 .../test/TestHelpers/MockHelpers.cs | 25 +++++-- 21 files changed, 286 insertions(+), 162 deletions(-) create mode 100644 packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/TestData/ModelCustomizationTests/CanChangeAccessibility/MockInputModel.cs create mode 100644 packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/TestData/ModelCustomizationTests/CanChangeModelName/CustomizedModel.cs create mode 100644 packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/TestData/ModelCustomizationTests/CanChangeModelNameAndToStructAtSameTime/CustomizedModel.cs create mode 100644 packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/TestData/ModelCustomizationTests/CanChangePropertyAccessibility/MockInputModel.cs create mode 100644 packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/TestData/ModelCustomizationTests/CanChangePropertyName/MockInputModel.cs create mode 100644 packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/TestData/ModelCustomizationTests/CanChangePropertyType/MockInputModel.cs create mode 100644 packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/TestData/ModelCustomizationTests/CanChangeToStruct/MockInputModel.cs delete mode 100644 packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/TestData/ModelCustomizationTests/TestCustomization_CanChangeModelName.cs delete mode 100644 packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/TestData/ModelCustomizationTests/TestCustomization_CanChangePropertyAccessibility.cs delete mode 100644 packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/TestData/ModelCustomizationTests/TestCustomization_CanChangePropertyName.cs delete mode 100644 packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/TestData/ModelCustomizationTests/TestCustomization_CanChangePropertyType.cs rename packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/{Tests => }/TestData/ConfigurationTests/DisableDocsForType.cs (100%) diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.Customization/src/CodeGenTypeAttribute.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.Customization/src/CodeGenTypeAttribute.cs index ec3ba01fe0..f994f6438c 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.Customization/src/CodeGenTypeAttribute.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp.Customization/src/CodeGenTypeAttribute.cs @@ -7,7 +7,7 @@ namespace Microsoft.Generator.CSharp.Customization { - [AttributeUsage(AttributeTargets.Class)] + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] public class CodeGenTypeAttribute : Attribute { public string? OriginalName { get; } diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/CodeModelPlugin.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/CodeModelPlugin.cs index 2bc8d5ca19..5a13fc6797 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/CodeModelPlugin.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/CodeModelPlugin.cs @@ -78,7 +78,7 @@ public virtual void AddVisitor(LibraryVisitor visitor) private SourceInputModel? _sourceInputModel; internal async Task InitializeSourceInputModelAsync() { - GeneratedCodeWorkspace existingCode = GeneratedCodeWorkspace.CreateExistingCodeProject(Instance.Configuration.ProjectDirectory, Instance.Configuration.ProjectGeneratedDirectory); + GeneratedCodeWorkspace existingCode = GeneratedCodeWorkspace.CreateExistingCodeProject([Instance.Configuration.ProjectDirectory], Instance.Configuration.ProjectGeneratedDirectory); _sourceInputModel = new SourceInputModel(await existingCode.GetCompilationAsync()); } } diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/PostProcessing/GeneratedCodeWorkspace.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/PostProcessing/GeneratedCodeWorkspace.cs index 0bc10cc3ee..386ce30a72 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/PostProcessing/GeneratedCodeWorkspace.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/PostProcessing/GeneratedCodeWorkspace.cs @@ -161,18 +161,19 @@ internal static async Task Create() return new GeneratedCodeWorkspace(generatedCodeProject); } - internal static GeneratedCodeWorkspace CreateExistingCodeProject(string projectDirectory, string generatedDirectory) + internal static GeneratedCodeWorkspace CreateExistingCodeProject(IEnumerable projectDirectories, string generatedDirectory) { var workspace = new AdhocWorkspace(); var newOptionSet = workspace.Options.WithChangedOption(FormattingOptions.NewLine, LanguageNames.CSharp, _newLine); workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(newOptionSet)); Project project = workspace.AddProject("ExistingCode", LanguageNames.CSharp); - if (Path.IsPathRooted(projectDirectory)) + foreach (var projectDirectory in projectDirectories) { - projectDirectory = Path.GetFullPath(projectDirectory); - - project = AddDirectory(project, projectDirectory, skipPredicate: sourceFile => sourceFile.StartsWith(generatedDirectory)); + if (Path.IsPathRooted(projectDirectory)) + { + project = AddDirectory(project, Path.GetFullPath(projectDirectory), skipPredicate: sourceFile => sourceFile.StartsWith(generatedDirectory)); + } } project = project diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Providers/ModelProvider.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Providers/ModelProvider.cs index 5d587fe86f..493a191006 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Providers/ModelProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Providers/ModelProvider.cs @@ -24,8 +24,6 @@ public sealed class ModelProvider : TypeProvider protected override FormattableString Description { get; } - private readonly bool _isStruct; - private readonly TypeSignatureModifiers _declarationModifiers; private readonly CSharpType _privateAdditionalRawDataPropertyType = typeof(IDictionary); private readonly Type _additionalPropsUnknownType = typeof(BinaryData); private readonly Lazy? _baseTypeProvider; @@ -38,27 +36,12 @@ public ModelProvider(InputModelType inputModel) { _inputModel = inputModel; Description = inputModel.Description != null ? FormattableStringHelpers.FromString(inputModel.Description) : $"The {Name}."; - _declarationModifiers = TypeSignatureModifiers.Partial | - (inputModel.ModelAsStruct ? TypeSignatureModifiers.ReadOnly | TypeSignatureModifiers.Struct : TypeSignatureModifiers.Class); - - if (inputModel.Access == "internal") - { - _declarationModifiers |= TypeSignatureModifiers.Internal; - } - - bool isAbstract = inputModel.DiscriminatorProperty is not null && inputModel.DiscriminatorValue is null; - if (isAbstract) - { - _declarationModifiers |= TypeSignatureModifiers.Abstract; - } if (inputModel.BaseModel is not null) { _baseTypeProvider = new(() => CodeModelPlugin.Instance.TypeFactory.CreateModel(inputModel.BaseModel)); DiscriminatorValueExpression = EnsureDiscriminatorValueExpression(); } - - _isStruct = inputModel.ModelAsStruct; } public bool IsUnknownDiscriminatorModel => _inputModel.IsUnknownDiscriminatorModel; @@ -107,7 +90,54 @@ protected override TypeProvider[] BuildSerializationProviders() protected override string BuildName() => _inputModel.Name.ToCleanName(); - protected override TypeSignatureModifiers GetDeclarationModifiers() => _declarationModifiers; + protected override TypeSignatureModifiers GetDeclarationModifiers() + { + var customCodeModifiers = CustomCodeView?.DeclarationModifiers ?? TypeSignatureModifiers.None; + var isStruct = false; + // the information of if this model should be a struct comes from two sources: + // 1. the customied code + // 2. the spec + if (customCodeModifiers.HasFlag(TypeSignatureModifiers.Struct)) + { + isStruct = true; + } + if (_inputModel.ModelAsStruct) + { + isStruct = true; + } + var declarationModifiers = TypeSignatureModifiers.Partial; + + if (isStruct) + { + declarationModifiers |= TypeSignatureModifiers.ReadOnly | TypeSignatureModifiers.Struct; + } + else + { + declarationModifiers |= TypeSignatureModifiers.Class; + } + + if (customCodeModifiers != TypeSignatureModifiers.None) + { + declarationModifiers |= GetAccessibilityModifiers(customCodeModifiers); + } + else if (_inputModel.Access == "internal") + { + declarationModifiers |= TypeSignatureModifiers.Internal; + } + + bool isAbstract = _inputModel.DiscriminatorProperty is not null && _inputModel.DiscriminatorValue is null; + if (isAbstract) + { + declarationModifiers |= TypeSignatureModifiers.Abstract; + } + + return declarationModifiers; + + static TypeSignatureModifiers GetAccessibilityModifiers(TypeSignatureModifiers modifiers) + { + return modifiers & (TypeSignatureModifiers.Public | TypeSignatureModifiers.Internal | TypeSignatureModifiers.Protected | TypeSignatureModifiers.Private); + } + } /// /// Builds the fields for the model by adding the raw data field. @@ -318,7 +348,7 @@ private ConstructorProvider BuildFullConstructor() // add the base parameters, if any foreach (var property in baseProperties) { - AddInitializationParameterForCtor(baseParameters, property, _isStruct, isPrimaryConstructor); + AddInitializationParameterForCtor(baseParameters, property, Type.IsStruct, isPrimaryConstructor); } // construct the initializer using the parameters from base signature @@ -326,7 +356,7 @@ private ConstructorProvider BuildFullConstructor() foreach (var property in Properties) { - AddInitializationParameterForCtor(constructorParameters, property, _isStruct, isPrimaryConstructor); + AddInitializationParameterForCtor(constructorParameters, property, Type.IsStruct, isPrimaryConstructor); } constructorParameters.AddRange(_inputModel.IsUnknownDiscriminatorModel ? baseParameters : baseParameters.Where(p => p.Property is null || !p.Property.IsDiscriminator)); @@ -455,7 +485,7 @@ private MethodBodyStatement GetPropertyInitializers( ValueExpression? initializationValue = null; - if (parameterMap.TryGetValue(property.AsParameter.Name, out var parameter) || _isStruct) + if (parameterMap.TryGetValue(property.AsParameter.Name, out var parameter) || Type.IsStruct) { if (parameter != null) { diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Providers/NamedTypeSymbolProvider.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Providers/NamedTypeSymbolProvider.cs index 2452a2f447..2adea338d0 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Providers/NamedTypeSymbolProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/src/Providers/NamedTypeSymbolProvider.cs @@ -30,6 +30,46 @@ public NamedTypeSymbolProvider(INamedTypeSymbol namedTypeSymbol) protected override string GetNamespace() => GetFullyQualifiedNameFromDisplayString(_namedTypeSymbol.ContainingNamespace); + protected override TypeSignatureModifiers GetDeclarationModifiers() + { + var declaredModifiers = GetAccessModifiers(_namedTypeSymbol.DeclaredAccessibility); + if (_namedTypeSymbol.IsReadOnly) + { + declaredModifiers |= TypeSignatureModifiers.ReadOnly; + } + if (_namedTypeSymbol.IsStatic) + { + declaredModifiers |= TypeSignatureModifiers.Static; + } + switch (_namedTypeSymbol.TypeKind) + { + case TypeKind.Class: + declaredModifiers |= TypeSignatureModifiers.Class; + if (_namedTypeSymbol.IsSealed) + { + declaredModifiers |= TypeSignatureModifiers.Sealed; + } + break; + case TypeKind.Struct: + declaredModifiers |= TypeSignatureModifiers.Struct; + break; + case TypeKind.Interface: + declaredModifiers |= TypeSignatureModifiers.Interface; + break; + } + return declaredModifiers; + + static TypeSignatureModifiers GetAccessModifiers(Accessibility accessibility) => accessibility switch + { + Accessibility.Private => TypeSignatureModifiers.Private, + Accessibility.Protected => TypeSignatureModifiers.Protected, + Accessibility.Internal => TypeSignatureModifiers.Internal, + Accessibility.Public => TypeSignatureModifiers.Public, + Accessibility.ProtectedOrInternal => TypeSignatureModifiers.Protected | TypeSignatureModifiers.Internal, + _ => TypeSignatureModifiers.None + }; + } + protected override FieldProvider[] BuildFields() { List fields = new List(); diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Helpers.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Helpers.cs index 869a0f2aee..744a18cc8d 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Helpers.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Helpers.cs @@ -4,7 +4,8 @@ using System; using System.Diagnostics; using System.IO; -using Microsoft.CodeAnalysis.CSharp; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; using Microsoft.CodeAnalysis; namespace Microsoft.Generator.CSharp.Tests @@ -15,19 +16,24 @@ internal static class Helpers public static string GetExpectedFromFile(string? parameters = null) { - return File.ReadAllText(GetAssetFilePath(parameters)); + return File.ReadAllText(GetAssetFileOrDirectoryPath(true, parameters)); } - private static string GetAssetFilePath(string? parameters = null) + private static string GetAssetFileOrDirectoryPath(bool isFile, string? parameters = null) { var stackTrace = new StackTrace(); var stackFrame = GetRealMethodInvocation(stackTrace); var method = stackFrame.GetMethod(); var callingClass = method!.DeclaringType; var nsSplit = callingClass!.Namespace!.Split('.'); - var ns = nsSplit[^1]; var paramString = parameters is null ? string.Empty : $"({parameters})"; - return Path.Combine(_assemblyLocation, ns, "TestData", callingClass.Name, $"{method.Name}{paramString}.cs"); + var extName = isFile ? ".cs" : string.Empty; + var path = _assemblyLocation; + for (int i = 4; i < nsSplit.Length; i++) + { + path = Path.Combine(path, nsSplit[i]); + } + return Path.Combine(path, "TestData", callingClass.Name, $"{method.Name}{paramString}{extName}"); } private static StackFrame GetRealMethodInvocation(StackTrace stackTrace) @@ -36,7 +42,9 @@ private static StackFrame GetRealMethodInvocation(StackTrace stackTrace) while (i < stackTrace.FrameCount) { var frame = stackTrace.GetFrame(i); - if (frame!.GetMethod()!.DeclaringType != typeof(Helpers)) + var declaringType = frame!.GetMethod()!.DeclaringType!; + // we need to skip those method invocations from this class, or from the async state machine when the caller is an async method + if (declaringType != typeof(Helpers) && declaringType != typeof(MockHelpers) && !IsCompilerGenerated(declaringType)) { return frame; } @@ -44,17 +52,20 @@ private static StackFrame GetRealMethodInvocation(StackTrace stackTrace) } throw new InvalidOperationException($"There is no method invocation outside the {typeof(Helpers)} class in the stack trace"); + + static bool IsCompilerGenerated(Type type) + { + return type.IsDefined(typeof(CompilerGeneratedAttribute), false) || (type.Namespace?.StartsWith("System.Runtime.CompilerServices") ?? false) || + type.Name.StartsWith("<<", StringComparison.Ordinal); + } } - public static Compilation GetCompilationFromFile(string? parameters = null) + public static async Task GetCompilationFromDirectoryAsync(string? parameters = null) { - SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(File.ReadAllText(GetAssetFilePath(parameters))); - CSharpCompilation compilation = CSharpCompilation.Create("ExistingCode") - .WithOptions(new CSharpCompilationOptions(OutputKind.ConsoleApplication)) - .AddReferences(MetadataReference.CreateFromFile(typeof(object).Assembly.Location)) - .AddSyntaxTrees(syntaxTree); - - return compilation; + var directory = GetAssetFileOrDirectoryPath(false, parameters); + var codeGenAttributeFiles = Path.Combine(_assemblyLocation, "..", "..", "..", "..", "..", "Microsoft.Generator.CSharp.Customization", "src"); + var workspace = GeneratedCodeWorkspace.CreateExistingCodeProject([directory, codeGenAttributeFiles], Path.Combine(directory, "Generated")); + return await workspace.GetCompilationAsync(); } } } diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/ModelCustomizationTests.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/ModelCustomizationTests.cs index af9449bad7..2d21efedd5 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/ModelCustomizationTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/ModelCustomizationTests.cs @@ -1,21 +1,22 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System.Threading.Tasks; using Microsoft.Generator.CSharp.Input; using Microsoft.Generator.CSharp.Primitives; using Microsoft.Generator.CSharp.Providers; using Microsoft.Generator.CSharp.Tests.Common; using NUnit.Framework; -namespace Microsoft.Generator.CSharp.Tests.Providers // the namespace here is crucial to get correct test data file. +namespace Microsoft.Generator.CSharp.Tests.Providers.ModelProviders { public class ModelCustomizationTests { // Validates that the property body's setter is correctly set based on the property type [Test] - public void TestCustomization_CanChangeModelName() + public async Task CanChangeModelName() { - MockHelpers.LoadMockPlugin(customization: Helpers.GetCompilationFromFile()); + await MockHelpers.LoadMockPluginAsync(compilation: async () => await Helpers.GetCompilationFromDirectoryAsync()); var props = new[] { @@ -30,9 +31,9 @@ public void TestCustomization_CanChangeModelName() } [Test] - public void TestCustomization_CanChangePropertyName() + public async Task CanChangePropertyName() { - MockHelpers.LoadMockPlugin(customization: Helpers.GetCompilationFromFile()); + await MockHelpers.LoadMockPluginAsync(compilation: async () => await Helpers.GetCompilationFromDirectoryAsync()); var props = new[] { @@ -54,9 +55,9 @@ public void TestCustomization_CanChangePropertyName() } [Test] - public void TestCustomization_CanChangePropertyType() + public async Task CanChangePropertyType() { - MockHelpers.LoadMockPlugin(customization: Helpers.GetCompilationFromFile()); + await MockHelpers.LoadMockPluginAsync(compilation: async () => await Helpers.GetCompilationFromDirectoryAsync()); var props = new[] { @@ -79,9 +80,9 @@ public void TestCustomization_CanChangePropertyType() } [Test] - public void TestCustomization_CanChangePropertyAccessibility() + public async Task CanChangePropertyAccessibility() { - MockHelpers.LoadMockPlugin(customization: Helpers.GetCompilationFromFile()); + await MockHelpers.LoadMockPluginAsync(compilation: async () => await Helpers.GetCompilationFromDirectoryAsync()); var props = new[] { @@ -111,5 +112,57 @@ private static void AssertCommon(TypeProvider? customCodeView, ModelProvider mod Assert.AreEqual(customCodeView?.Name, modelTypeProvider.Type.Name); Assert.AreEqual(customCodeView?.Type.Namespace, modelTypeProvider.Type.Namespace); } + + [TestCase] + public async Task CanChangeToStruct() + { + await MockHelpers.LoadMockPluginAsync(compilation: async () => await Helpers.GetCompilationFromDirectoryAsync()); + + var props = new[] + { + InputFactory.Property("prop1", InputFactory.Array(InputPrimitiveType.String)) + }; + + var inputModel = InputFactory.Model("mockInputModel", properties: props); + var modelTypeProvider = new ModelProvider(inputModel); + + Assert.IsTrue(modelTypeProvider.DeclarationModifiers.HasFlag(TypeSignatureModifiers.Public | TypeSignatureModifiers.Partial | TypeSignatureModifiers.ReadOnly | TypeSignatureModifiers.Struct)); + Assert.IsTrue(modelTypeProvider.Type.IsValueType); + } + + [Test] + public async Task CanChangeModelNameAndToStructAtSameTime() + { + await MockHelpers.LoadMockPluginAsync(compilation: async () => await Helpers.GetCompilationFromDirectoryAsync()); + + var props = new[] + { + InputFactory.Property("prop1", InputFactory.Array(InputPrimitiveType.String)) + }; + + var inputModel = InputFactory.Model("mockInputModel", properties: props); + var modelTypeProvider = new ModelProvider(inputModel); + + Assert.IsTrue(modelTypeProvider.DeclarationModifiers.HasFlag(TypeSignatureModifiers.Public | TypeSignatureModifiers.Partial | TypeSignatureModifiers.ReadOnly | TypeSignatureModifiers.Struct)); + Assert.IsTrue(modelTypeProvider.Type.IsValueType); + Assert.AreEqual("CustomizedModel", modelTypeProvider.Type.Name); + Assert.AreEqual("NewNamespace.Models", modelTypeProvider.Type.Namespace); + } + + [TestCase] + public async Task CanChangeAccessibility() + { + await MockHelpers.LoadMockPluginAsync(compilation: async () => await Helpers.GetCompilationFromDirectoryAsync()); + + var props = new[] { + InputFactory.Property("prop1", InputFactory.Array(InputPrimitiveType.String)) + }; + + var inputModel = InputFactory.Model("mockInputModel", properties: props); + var modelTypeProvider = new ModelProvider(inputModel); + + Assert.IsTrue(modelTypeProvider.DeclarationModifiers.HasFlag(TypeSignatureModifiers.Internal | TypeSignatureModifiers.Partial | TypeSignatureModifiers.Class)); + Assert.IsFalse(modelTypeProvider.Type.IsPublic); + } } } diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/TestData/ModelCustomizationTests/CanChangeAccessibility/MockInputModel.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/TestData/ModelCustomizationTests/CanChangeAccessibility/MockInputModel.cs new file mode 100644 index 0000000000..904c7d5553 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/TestData/ModelCustomizationTests/CanChangeAccessibility/MockInputModel.cs @@ -0,0 +1,7 @@ +#nullable disable + +namespace Sample.Models; + +internal partial class MockInputModel +{ +} diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/TestData/ModelCustomizationTests/CanChangeModelName/CustomizedModel.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/TestData/ModelCustomizationTests/CanChangeModelName/CustomizedModel.cs new file mode 100644 index 0000000000..81fb094851 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/TestData/ModelCustomizationTests/CanChangeModelName/CustomizedModel.cs @@ -0,0 +1,11 @@ +#nullable disable + +using System; +using Microsoft.Generator.CSharp.Customization; + +namespace NewNamespace.Models; + +[CodeGenType("MockInputModel")] +public partial class CustomizedModel +{ +} diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/TestData/ModelCustomizationTests/CanChangeModelNameAndToStructAtSameTime/CustomizedModel.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/TestData/ModelCustomizationTests/CanChangeModelNameAndToStructAtSameTime/CustomizedModel.cs new file mode 100644 index 0000000000..e5fac73872 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/TestData/ModelCustomizationTests/CanChangeModelNameAndToStructAtSameTime/CustomizedModel.cs @@ -0,0 +1,11 @@ +#nullable disable + +using System; +using Microsoft.Generator.CSharp.Customization; + +namespace NewNamespace.Models; + +[CodeGenType("MockInputModel")] +public partial struct CustomizedModel +{ +} diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/TestData/ModelCustomizationTests/CanChangePropertyAccessibility/MockInputModel.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/TestData/ModelCustomizationTests/CanChangePropertyAccessibility/MockInputModel.cs new file mode 100644 index 0000000000..c981891baa --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/TestData/ModelCustomizationTests/CanChangePropertyAccessibility/MockInputModel.cs @@ -0,0 +1,13 @@ +#nullable disable + +using Sample; +using Microsoft.Generator.CSharp.Customization; + +namespace Sample.Models +{ + public partial class MockInputModel + { + [CodeGenMember("Prop1")] + internal string[] Prop2 { get; set; } + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/TestData/ModelCustomizationTests/CanChangePropertyName/MockInputModel.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/TestData/ModelCustomizationTests/CanChangePropertyName/MockInputModel.cs new file mode 100644 index 0000000000..43180579a8 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/TestData/ModelCustomizationTests/CanChangePropertyName/MockInputModel.cs @@ -0,0 +1,13 @@ +#nullable disable + +using Sample; +using Microsoft.Generator.CSharp.Customization; + +namespace Sample.Models +{ + public partial class MockInputModel + { + [CodeGenMember("Prop1")] + public string[] Prop2 { get; set; } + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/TestData/ModelCustomizationTests/CanChangePropertyType/MockInputModel.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/TestData/ModelCustomizationTests/CanChangePropertyType/MockInputModel.cs new file mode 100644 index 0000000000..8f97a16998 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/TestData/ModelCustomizationTests/CanChangePropertyType/MockInputModel.cs @@ -0,0 +1,13 @@ +#nullable disable + +using Sample; +using Microsoft.Generator.CSharp.Customization; + +namespace Sample.Models +{ + public partial class MockInputModel + { + [CodeGenMember("Prop1")] + public int[] Prop2 { get; set; } + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/TestData/ModelCustomizationTests/CanChangeToStruct/MockInputModel.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/TestData/ModelCustomizationTests/CanChangeToStruct/MockInputModel.cs new file mode 100644 index 0000000000..f51d61687a --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/ModelProviders/TestData/ModelCustomizationTests/CanChangeToStruct/MockInputModel.cs @@ -0,0 +1,5 @@ +namespace Sample.Models; + +public partial struct MockInputModel +{ +} diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/NamedTypeSymbolProviders/NamedTypeSymbolProviderTests.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/NamedTypeSymbolProviders/NamedTypeSymbolProviderTests.cs index a98e5919b8..3e526c0533 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/NamedTypeSymbolProviders/NamedTypeSymbolProviderTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/NamedTypeSymbolProviders/NamedTypeSymbolProviderTests.cs @@ -27,6 +27,13 @@ public NamedTypeSymbolProviderTests() _namedSymbol = new NamedSymbol(); } + [Test] + public void ValidateModifiers() + { + var modifiers = _namedTypeSymbolProvider.DeclarationModifiers; + Assert.IsTrue(modifiers.HasFlag(TypeSignatureModifiers.Public | TypeSignatureModifiers.Partial | TypeSignatureModifiers.Class)); + } + [Test] public void ValidateName() { diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/TestData/ModelCustomizationTests/TestCustomization_CanChangeModelName.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/TestData/ModelCustomizationTests/TestCustomization_CanChangeModelName.cs deleted file mode 100644 index dcfbbbf3b9..0000000000 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/TestData/ModelCustomizationTests/TestCustomization_CanChangeModelName.cs +++ /dev/null @@ -1,25 +0,0 @@ -#nullable disable - -using System; - -namespace NewNamespace -{ - // TODO: if we decide to use the public APIs, we do not have to define this attribute here. Tracking: https://github.com/Azure/autorest.csharp/issues/4551 - [AttributeUsage(AttributeTargets.Class)] - internal class CodeGenTypeAttribute : Attribute - { - public string OriginalName { get; } - - public CodeGenTypeAttribute(string originalName) - { - OriginalName = originalName; - } - } -} -namespace NewNamespace.Models -{ - [CodeGenType("MockInputModel")] - public partial class CustomizedModel - { - } -} diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/TestData/ModelCustomizationTests/TestCustomization_CanChangePropertyAccessibility.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/TestData/ModelCustomizationTests/TestCustomization_CanChangePropertyAccessibility.cs deleted file mode 100644 index 825f168817..0000000000 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/TestData/ModelCustomizationTests/TestCustomization_CanChangePropertyAccessibility.cs +++ /dev/null @@ -1,27 +0,0 @@ -#nullable disable - -using System; - -namespace Sample -{ - // TODO: if we decide to use the public APIs, we do not have to define this attribute here. Tracking: https://github.com/Azure/autorest.csharp/issues/4551 - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] - internal class CodeGenMemberAttribute : Attribute - { - public CodeGenMemberAttribute() : base(null) - { - } - - public CodeGenMemberAttribute(string originalName) : base(originalName) - { - } - } -} -namespace Sample.Models -{ - public partial class MockInputModel - { - [CodeGenMember("Prop1")] - internal string[] Prop2 { get; set; } - } -} diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/TestData/ModelCustomizationTests/TestCustomization_CanChangePropertyName.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/TestData/ModelCustomizationTests/TestCustomization_CanChangePropertyName.cs deleted file mode 100644 index a2ea89d564..0000000000 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/TestData/ModelCustomizationTests/TestCustomization_CanChangePropertyName.cs +++ /dev/null @@ -1,27 +0,0 @@ -#nullable disable - -using System; - -namespace Sample -{ - // TODO: if we decide to use the public APIs, we do not have to define this attribute here. Tracking: https://github.com/Azure/autorest.csharp/issues/4551 - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] - internal class CodeGenMemberAttribute : Attribute - { - public CodeGenMemberAttribute() : base(null) - { - } - - public CodeGenMemberAttribute(string originalName) : base(originalName) - { - } - } -} -namespace Sample.Models -{ - public partial class MockInputModel - { - [CodeGenMember("Prop1")] - public string[] Prop2 { get; set; } - } -} diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/TestData/ModelCustomizationTests/TestCustomization_CanChangePropertyType.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/TestData/ModelCustomizationTests/TestCustomization_CanChangePropertyType.cs deleted file mode 100644 index e1d8feb757..0000000000 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Providers/TestData/ModelCustomizationTests/TestCustomization_CanChangePropertyType.cs +++ /dev/null @@ -1,27 +0,0 @@ -#nullable disable - -using System; - -namespace Sample -{ - // TODO: if we decide to use the public APIs, we do not have to define this attribute here. Tracking: https://github.com/Azure/autorest.csharp/issues/4551 - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] - internal class CodeGenMemberAttribute : Attribute - { - public CodeGenMemberAttribute() : base(null) - { - } - - public CodeGenMemberAttribute(string originalName) : base(originalName) - { - } - } -} -namespace Sample.Models -{ - public partial class MockInputModel - { - [CodeGenMember("Prop1")] - public int[] Prop2 { get; set; } - } -} diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Tests/TestData/ConfigurationTests/DisableDocsForType.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/TestData/ConfigurationTests/DisableDocsForType.cs similarity index 100% rename from packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/Tests/TestData/ConfigurationTests/DisableDocsForType.cs rename to packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/TestData/ConfigurationTests/DisableDocsForType.cs diff --git a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/TestHelpers/MockHelpers.cs b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/TestHelpers/MockHelpers.cs index c15c61ab03..621668f267 100644 --- a/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/TestHelpers/MockHelpers.cs +++ b/packages/http-client-csharp/generator/Microsoft.Generator.CSharp/test/TestHelpers/MockHelpers.cs @@ -3,6 +3,7 @@ using System; using System.IO; +using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.Generator.CSharp.Input; using Microsoft.Generator.CSharp.Primitives; @@ -17,12 +18,28 @@ internal static class MockHelpers { public const string TestHelpersFolder = "TestHelpers"; - public static Mock LoadMockPlugin( + public async static Task> LoadMockPluginAsync( Func? createCSharpTypeCore = null, Func? createOutputLibrary = null, string? configuration = null, InputModelType[]? inputModelTypes = null, - Compilation? customization = null) + Func>? compilation = null) + { + var mockPlugin = LoadMockPlugin(createCSharpTypeCore, createOutputLibrary, configuration, inputModelTypes); + + var compilationResult = compilation == null ? null : await compilation(); + + var sourceInputModel = new Mock(() => new SourceInputModel(compilationResult)) { CallBase = true }; + mockPlugin.Setup(p => p.SourceInputModel).Returns(sourceInputModel.Object); + + return mockPlugin; + } + + public static Mock LoadMockPlugin( + Func? createCSharpTypeCore = null, + Func? createOutputLibrary = null, + string? configuration = null, + InputModelType[]? inputModelTypes = null) { var configFilePath = Path.Combine(AppContext.BaseDirectory, TestHelpersFolder); // initialize the singleton instance of the plugin @@ -49,13 +66,11 @@ public static Mock LoadMockPlugin( mockPlugin.SetupGet(p => p.TypeFactory).Returns(mockTypeFactory.Object); - var sourceInputModel = new Mock(() => new SourceInputModel(customization)) { CallBase = true }; - + var sourceInputModel = new Mock(() => new SourceInputModel(null)) { CallBase = true }; mockPlugin.Setup(p => p.SourceInputModel).Returns(sourceInputModel.Object); CodeModelPlugin.Instance = mockPlugin.Object; - return mockPlugin; } }