diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/EnumProvider/SerializationCustomizationTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/EnumProvider/SerializationCustomizationTests.cs index 89ee081a6a3..6a9c628c2e3 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/EnumProvider/SerializationCustomizationTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/EnumProvider/SerializationCustomizationTests.cs @@ -69,5 +69,31 @@ public async Task CanReplaceMethod() Assert.AreEqual(1, customMethods.Count); Assert.AreEqual("ToSerialString", customMethods[0].Signature.Name); } + + [Test] + public async Task BackCompat_FixedEnumSerializationUsesPreservedUnderscores() + { + var inputEnum = InputFactory.Int32Enum( + "mockInputEnum", + [ + ("ExistingValue", 0), + ("Other", 1) + ]); + await MockHelpers.LoadMockGeneratorAsync( + inputEnums: () => [inputEnum], + lastContractCompilation: async () => await Helpers.GetCompilationFromDirectoryAsync()); + + var enumProvider = ScmCodeModelGenerator.Instance.TypeFactory.CreateEnum(inputEnum); + Assert.IsNotNull(enumProvider); + + var serializationProvider = enumProvider!.SerializationProviders.Single(); + _ = serializationProvider.Methods; + enumProvider.EnsureBuilt(); + enumProvider.ProcessTypeForBackCompatibility(); + + var file = new TypeProviderWriter(serializationProvider).Write(); + StringAssert.Contains("MockInputEnum.Existing_Value", file.Content); + StringAssert.DoesNotContain("MockInputEnum.ExistingValue", file.Content); + } } } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/EnumProvider/TestData/SerializationCustomizationTests/BackCompat_FixedEnumSerializationUsesPreservedUnderscores/MockInputEnum.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/EnumProvider/TestData/SerializationCustomizationTests/BackCompat_FixedEnumSerializationUsesPreservedUnderscores/MockInputEnum.cs new file mode 100644 index 00000000000..ba99f9670c2 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/test/Providers/EnumProvider/TestData/SerializationCustomizationTests/BackCompat_FixedEnumSerializationUsesPreservedUnderscores/MockInputEnum.cs @@ -0,0 +1,10 @@ +#nullable disable + +namespace Sample.Models +{ + public enum MockInputEnum + { + Existing_Value = 0, + Other = 1, + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/PostProcessing/GeneratedCodeWorkspace.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/PostProcessing/GeneratedCodeWorkspace.cs index c36686f637f..c1c2678e1d3 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/PostProcessing/GeneratedCodeWorkspace.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/PostProcessing/GeneratedCodeWorkspace.cs @@ -375,7 +375,7 @@ internal static async Task AddPackageReferencesFromProject() /// internal static ApiCompatBaseline LoadApiCompatBaseline() { - var packageName = CodeModelGenerator.Instance.TypeFactory.PrimaryNamespace; + var packageName = CodeModelGenerator.Instance.Configuration.PackageName; var directory = new DirectoryInfo(CodeModelGenerator.Instance.Configuration.ProjectDirectory); while (directory != null) @@ -395,7 +395,7 @@ internal static ApiCompatBaseline LoadApiCompatBaseline() internal static async Task LoadBaselineContract() { - var packageName = CodeModelGenerator.Instance.TypeFactory.PrimaryNamespace; + var packageName = CodeModelGenerator.Instance.Configuration.PackageName; string projectFilePath = Path.GetFullPath(Path.Combine(CodeModelGenerator.Instance.Configuration.ProjectDirectory, $"{packageName}.csproj")); if (!File.Exists(projectFilePath)) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/EnumProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/EnumProvider.cs index f15a2741167..e164342ed20 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/EnumProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/EnumProvider.cs @@ -2,7 +2,9 @@ // Licensed under the MIT License. using System; +using System.Collections.Generic; using System.IO; +using System.Linq; using Microsoft.TypeSpec.Generator.Input; using Microsoft.TypeSpec.Generator.Input.Extensions; using Microsoft.TypeSpec.Generator.Primitives; @@ -63,6 +65,36 @@ protected override string BuildNamespace() => string.IsNullOrEmpty(_inputType?.N CodeModelGenerator.Instance.TypeFactory.PrimaryNamespace : // we default to this model namespace when the namespace is empty CodeModelGenerator.Instance.TypeFactory.GetCleanNameSpace(_inputType.Namespace); + protected static string RemoveUnderscores(string name) => name.Replace("_", string.Empty); + + private protected static string GetBackCompatibleName( + string generatedName, + IReadOnlyList generatedNames, + IReadOnlyList lastContractNames) + { + if (lastContractNames.Any(n => n.Equals(generatedName, StringComparison.OrdinalIgnoreCase))) + { + return generatedName; + } + + var normalizedName = RemoveUnderscores(generatedName); + // A normalized match ignores underscores and casing. Preserve the last-contract name only + // when exactly one current member and one last-contract member have the same normalized name; + // multiple matches are ambiguous. Only two matches are needed to distinguish those cases. + var matchingCurrentNames = generatedNames + .Where(n => RemoveUnderscores(n).Equals(normalizedName, StringComparison.OrdinalIgnoreCase)) + .Take(2) + .ToArray(); + var matchingLastContractNames = lastContractNames + .Where(n => RemoveUnderscores(n).Equals(normalizedName, StringComparison.OrdinalIgnoreCase)) + .Take(2) + .ToArray(); + + return matchingCurrentNames.Length == 1 && matchingLastContractNames.Length == 1 + ? matchingLastContractNames[0] + : generatedName; + } + protected override bool GetIsEnum() => true; protected override CSharpType BuildEnumUnderlyingType() => CodeModelGenerator.Instance.TypeFactory.CreateCSharpType(_inputType!.ValueType) ?? throw new InvalidOperationException($"Failed to create CSharpType for {_inputType.ValueType}"); } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ExtensibleEnumProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ExtensibleEnumProvider.cs index a4ab1bf292d..c849dcf2a1b 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ExtensibleEnumProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ExtensibleEnumProvider.cs @@ -48,6 +48,10 @@ internal ExtensibleEnumProvider(InputEnumType input, TypeProvider? declaringType protected override IReadOnlyList BuildEnumValues() { + var generatedNames = _allowedValues + .Select(v => v.IsExactName ? v.Name : v.Name.ToIdentifierName()) + .ToArray(); + var lastContractNames = LastContractView?.Properties.Select(p => p.Name).ToArray() ?? []; var values = new EnumTypeMember[_allowedValues.Count]; for (int i = 0; i < _allowedValues.Count; i++) @@ -56,7 +60,7 @@ protected override IReadOnlyList BuildEnumValues() // build the field var modifiers = FieldModifiers.Private | FieldModifiers.Const; // the fields for extensible enums are private and const, storing the underlying values, therefore we need to append the word `Value` to the name - var valueName = inputValue.IsExactName ? inputValue.Name : inputValue.Name.ToIdentifierName(); + var valueName = GetBackCompatibleName(generatedNames[i], generatedNames, lastContractNames); var name = $"{valueName}Value"; // for initializationValue, if the enum is extensible, we always need it var initializationValue = Literal(inputValue.Value); diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/FixedEnumProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/FixedEnumProvider.cs index 854ea4a6b6a..3cea4cc508f 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/FixedEnumProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/FixedEnumProvider.cs @@ -73,6 +73,11 @@ protected override TypeProvider[] BuildSerializationProviders() protected override IReadOnlyList BuildEnumValues() { var customMembers = new HashSet(CustomCodeView?.Fields ?? []); + var generatedNames = AllowedValues + .Select(v => v.IsExactName ? v.Name : v.Name.ToIdentifierName()) + .ToArray(); + var lastContractFields = LastContractView?.Fields ?? []; + var lastContractNames = lastContractFields.Select(f => f.Name).ToArray(); var values = new EnumTypeMember[AllowedValues.Count]; @@ -81,13 +86,13 @@ protected override IReadOnlyList BuildEnumValues() var inputValue = AllowedValues[i]; var modifiers = FieldModifiers.Public | FieldModifiers.Static; // the fields for fixed enums are just its members (we use fields to represent the values in a system `enum` type), we just use the name for this field - var name = inputValue.IsExactName ? inputValue.Name : inputValue.Name.ToIdentifierName(); + var name = GetBackCompatibleName(generatedNames[i], generatedNames, lastContractNames); // check if the enum member was renamed in custom code string? customMemberName = null; foreach (var customMember in customMembers) { - if (customMember.OriginalName == name) + if (customMember.OriginalName == generatedNames[i]) { customMemberName = customMember.Name; } @@ -124,10 +129,12 @@ protected override IReadOnlyList BuildEnumValues() var currentLookup = currentValues.ToDictionary(v => v.Name, StringComparer.OrdinalIgnoreCase); var allMembers = new List(currentValues.Count); + var processedNames = new HashSet(StringComparer.OrdinalIgnoreCase); + var customMemberLastContractNames = GetCustomMemberLastContractNames(lastContractFields); foreach (var field in lastContractFields) { - if (currentLookup.TryGetValue(field.Name, out var existingMember)) + if (currentLookup.TryGetValue(field.Name, out var existingMember) && processedNames.Add(existingMember.Name)) { // By default, preserve the last contract's explicit value for integer enums so // members keep their exact values. If the baseline accepts a value change for this @@ -151,6 +158,10 @@ protected override IReadOnlyList BuildEnumValues() initializationValue); allMembers.Add(new EnumTypeMember(existingMember.Name, updatedField, memberValue)); } + else if (customMemberLastContractNames.Contains(field.Name)) + { + continue; + } else if (CodeModelGenerator.Instance.SourceInputModel?.ApiCompatBaseline.IsMemberSuppressed(Type.FullyQualifiedName, field.Name, 0) == true) { CodeModelGenerator.Instance.Emitter.Debug( @@ -167,7 +178,13 @@ protected override IReadOnlyList BuildEnumValues() } // Then, add new members that weren't in the last contract (in their original input order). - AppendMembersNotInLastContract(currentValues, lastContractFields, allMembers); + foreach (var current in currentValues) + { + if (!processedNames.Contains(current.Name)) + { + allMembers.Add(current); + } + } // Report a reordering only when the relative order of members present in BOTH the // current values and the resulting set was actually altered. @@ -181,6 +198,31 @@ protected override IReadOnlyList BuildEnumValues() return allMembers; } + private HashSet GetCustomMemberLastContractNames(IReadOnlyList lastContractFields) + { + var customOriginalNames = new HashSet( + CustomCodeView?.Fields + .Where(f => f.OriginalName != null) + .Select(f => f.OriginalName!) ?? [], + StringComparer.Ordinal); + var generatedNames = AllowedValues + .Select(v => v.IsExactName ? v.Name : v.Name.ToIdentifierName()) + .ToArray(); + var lastContractNames = lastContractFields.Select(f => f.Name).ToArray(); + var customMemberLastContractNames = new HashSet(StringComparer.OrdinalIgnoreCase); + + for (int i = 0; i < generatedNames.Length; i++) + { + if (customOriginalNames.Contains(generatedNames[i])) + { + customMemberLastContractNames.Add( + GetBackCompatibleName(generatedNames[i], generatedNames, lastContractNames)); + } + } + + return customMemberLastContractNames; + } + private bool TryResurrectRemovedMember(FieldProvider lastContractField, [NotNullWhen(true)] out EnumTypeMember? member) { member = null; @@ -225,19 +267,22 @@ private static bool SharedMemberOrderChanged( var currentNames = new HashSet(currentValues.Count, StringComparer.Ordinal); foreach (var member in currentValues) { - currentNames.Add(member.Name); + currentNames.Add(RemoveUnderscores(member.Name)); } var index = 0; foreach (var member in result) { - if (!currentNames.Contains(member.Name)) + if (!currentNames.Contains(RemoveUnderscores(member.Name))) { continue; } if (index >= currentValues.Count - || !string.Equals(member.Name, currentValues[index].Name, StringComparison.Ordinal)) + || !string.Equals( + RemoveUnderscores(member.Name), + RemoveUnderscores(currentValues[index].Name), + StringComparison.OrdinalIgnoreCase)) { return true; } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/GeneratedCodeWorkspaceTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/GeneratedCodeWorkspaceTests.cs index 480c052a0bc..66ac281be15 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/GeneratedCodeWorkspaceTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/GeneratedCodeWorkspaceTests.cs @@ -97,6 +97,30 @@ await MockHelpers.LoadMockGeneratorAsync( Assert.NotNull(fooMethod, "Foo method should be found in the SimpleType"); } + [TestCase(Category = EvaluatedFrameworkTestCategory)] + public async Task TestLoadBaselineContractUsesPackageNameWhenNamespaceDiffers() + { + const string ns = "Service.Namespace"; + const string packageName = "Service.Package"; + var nugetCacheDir = Path.Combine(_tempDirectory!, "NuGetCache"); + CreateTestAssemblyAndProjectFile( + nugetCacheDir, + "TestNamespace.csproj", + packageName, + ns, + $"{packageName}.csproj"); + + await MockHelpers.LoadMockGeneratorAsync( + inputNamespaceName: ns, + outputPath: _projectDir, + configuration: $"{{\"package-name\": \"{packageName}\"}}"); + + var compilation = await GeneratedCodeWorkspace.LoadBaselineContract(); + + Assert.NotNull(compilation, "Compilation should not be null"); + Assert.NotNull(compilation!.GetTypeByMetadataName($"{ns}.SimpleType")); + } + [Test] public async Task AddPackageReferencesFromProject_AddsReferencesFromCsproj() { @@ -336,11 +360,18 @@ public class Placeholder {{ }} return dllPath; } - private void CreateTestAssemblyAndProjectFile(string nugetCacheDir, string csProjectFileName) + private void CreateTestAssemblyAndProjectFile( + string nugetCacheDir, + string csProjectFileName, + string? packageName = null, + string? namespaceName = null, + string? destinationProjectFileName = null) { - var ns = csProjectFileName.StartsWith("TestNamespaceUnevaluatedFrameworkValue") + var ns = namespaceName ?? (csProjectFileName.StartsWith("TestNamespaceUnevaluatedFrameworkValue") ? "TestNamespaceUnevaluatedFrameworkValue" - : "TestNamespace"; + : "TestNamespace"); + packageName ??= ns; + destinationProjectFileName ??= csProjectFileName; var syntaxTree = CSharpSyntaxTree.ParseText($@" namespace {ns} @@ -377,19 +408,19 @@ public void Foo(string p1) {{ }} Assert.Fail("Failed to open test project file."); } - var csProjDestination = Path.Combine(_projectDir!, "src", csProjectFileName); + var csProjDestination = Path.Combine(_projectDir!, "src", destinationProjectFileName); projectRoot!.Save(csProjDestination); var compilation = CSharpCompilation.Create( - ns, + packageName, [syntaxTree], references, new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); - var nugetPackageDir = Path.Combine(nugetCacheDir, ns.ToLowerInvariant(), version, "lib", "netstandard2.0"); + var nugetPackageDir = Path.Combine(nugetCacheDir, packageName.ToLowerInvariant(), version, "lib", "netstandard2.0"); Directory.CreateDirectory(nugetPackageDir); - var dllPath = Path.Combine(nugetPackageDir, $"{ns}.dll"); + var dllPath = Path.Combine(nugetPackageDir, $"{packageName}.dll"); var emitResult = compilation.Emit(dllPath); Assert.IsTrue(emitResult.Success, $"Failed to emit test assembly: ${string.Join(", ", emitResult.Diagnostics)}"); } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/EnumProviderTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/EnumProviderTests.cs index fe18ff059a3..4fd64c9b4c6 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/EnumProviderTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/EnumProviderTests.cs @@ -4,6 +4,7 @@ // cspell:ignore readded using System; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.TypeSpec.Generator.Expressions; @@ -751,6 +752,97 @@ await MockHelpers.LoadMockGeneratorAsync( Assert.AreEqual("Default", fields[1].Name); } + [Test] + public async Task BackCompat_FixedEnumUnderscoresPreserved() + { + await MockHelpers.LoadMockGeneratorAsync( + createCSharpTypeCore: (inputType) => typeof(int), + lastContractCompilation: async () => await Helpers.GetCompilationFromDirectoryAsync()); + + var input = InputFactory.Int32Enum("mockInputEnum", [ + ("ExistingValue", 0), + ("Other", 1), + ]); + + var enumType = EnumProvider.Create(input); + enumType.EnsureBuilt(); + enumType.ProcessTypeForBackCompatibility(); + + var fields = enumType.Fields; + Assert.AreEqual(2, fields.Count); + Assert.AreEqual("Existing_Value", fields[0].Name); + Assert.AreEqual("Other", fields[1].Name); + } + + [Test] + public async Task BackCompat_FixedEnumCustomizationTakesPrecedenceOverPreservedUnderscores() + { + await MockHelpers.LoadMockGeneratorAsync( + createCSharpTypeCore: (inputType) => typeof(int), + compilation: async () => await Helpers.GetCompilationFromDirectoryAsync(parameters: "Custom"), + lastContractCompilation: async () => await Helpers.GetCompilationFromDirectoryAsync(parameters: "Last")); + + var input = InputFactory.Int32Enum("mockInputEnum", [("ExistingValue", 0)]); + + var enumType = EnumProvider.Create(input); + Assert.IsNotNull(enumType.CustomCodeView); + Assert.AreEqual("ExistingValue", enumType.CustomCodeView!.Fields.Single().OriginalName); + Assert.AreEqual("Customized", enumType.CustomCodeView.Fields.Single().Name); + enumType.EnsureBuilt(); + enumType.Update( + enumType.Methods, + enumType.Constructors, + enumType.Properties, + enumType.Fields); + enumType.ProcessTypeForBackCompatibility(); + + Assert.AreEqual(0, enumType.Fields.Count); + } + + [Test] + public async Task BackCompat_FixedEnumAmbiguousUnderscoreMatchNotApplied() + { + await MockHelpers.LoadMockGeneratorAsync( + createCSharpTypeCore: (inputType) => typeof(int), + lastContractCompilation: async () => await Helpers.GetCompilationFromDirectoryAsync()); + + var enumValues = new List(); + var input = InputFactory.Enum("mockInputEnum", InputPrimitiveType.Int32, enumValues); + enumValues.Add(InputFactory.EnumMember.Int32("ExistingValue", 0, input)); + enumValues.Add(InputFactory.EnumMember.Int32("Existing_Value", 1, input, isExactName: true)); + + var enumType = EnumProvider.Create(input); + enumType.EnsureBuilt(); + enumType.ProcessTypeForBackCompatibility(); + + var fields = enumType.Fields; + Assert.AreEqual(3, fields.Count); + Assert.AreEqual("Existing__Value", fields[0].Name); + Assert.AreEqual("ExistingValue", fields[1].Name); + Assert.AreEqual("Existing_Value", fields[2].Name); + } + + [Test] + public async Task BackCompat_ExtensibleEnumUnderscoresPreserved() + { + await MockHelpers.LoadMockGeneratorAsync( + createCSharpTypeCore: (inputType) => typeof(string), + lastContractCompilation: async () => await Helpers.GetCompilationFromDirectoryAsync()); + + var input = InputFactory.StringEnum("mockInputEnum", [ + ("ExistingValue", "existing"), + ("Other", "other"), + ], isExtensible: true); + + var enumType = EnumProvider.Create(input); + + Assert.AreEqual("_value", enumType.Fields[0].Name); + Assert.AreEqual("Existing_ValueValue", enumType.Fields[1].Name); + Assert.AreEqual("OtherValue", enumType.Fields[2].Name); + Assert.AreEqual("Existing_Value", enumType.Properties[0].Name); + Assert.AreEqual("Other", enumType.Properties[1].Name); + } + // Verifies that back-compat does NOT re-introduce enum values that have been suppressed // via [CodeGenSuppress] or that already exist in user-provided custom code. Without // filtering in ProcessTypeForBackCompatibility, the back-compat code would rebuild the diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/BackCompat_ExtensibleEnumUnderscoresPreserved/MockInputEnum.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/BackCompat_ExtensibleEnumUnderscoresPreserved/MockInputEnum.cs new file mode 100644 index 00000000000..5df3fb5b33a --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/BackCompat_ExtensibleEnumUnderscoresPreserved/MockInputEnum.cs @@ -0,0 +1,10 @@ +#nullable disable + +namespace Sample.Models +{ + public readonly partial struct MockInputEnum + { + public static MockInputEnum Existing_Value { get; } + public static MockInputEnum Other { get; } + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/BackCompat_FixedEnumAmbiguousUnderscoreMatchNotApplied/MockInputEnum.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/BackCompat_FixedEnumAmbiguousUnderscoreMatchNotApplied/MockInputEnum.cs new file mode 100644 index 00000000000..d4ea71be54b --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/BackCompat_FixedEnumAmbiguousUnderscoreMatchNotApplied/MockInputEnum.cs @@ -0,0 +1,9 @@ +#nullable disable + +namespace Sample.Models +{ + public enum MockInputEnum + { + Existing__Value = 0, + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/BackCompat_FixedEnumCustomizationTakesPrecedenceOverPreservedUnderscores(Custom)/MockInputEnum.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/BackCompat_FixedEnumCustomizationTakesPrecedenceOverPreservedUnderscores(Custom)/MockInputEnum.cs new file mode 100644 index 00000000000..10fecf72e12 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/BackCompat_FixedEnumCustomizationTakesPrecedenceOverPreservedUnderscores(Custom)/MockInputEnum.cs @@ -0,0 +1,12 @@ +#nullable disable + +using Microsoft.TypeSpec.Generator.Customizations; + +namespace Sample.Models +{ + public enum MockInputEnum + { + [CodeGenMember("ExistingValue")] + Customized + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/BackCompat_FixedEnumCustomizationTakesPrecedenceOverPreservedUnderscores(Last)/MockInputEnum.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/BackCompat_FixedEnumCustomizationTakesPrecedenceOverPreservedUnderscores(Last)/MockInputEnum.cs new file mode 100644 index 00000000000..ae1454faa2b --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/BackCompat_FixedEnumCustomizationTakesPrecedenceOverPreservedUnderscores(Last)/MockInputEnum.cs @@ -0,0 +1,9 @@ +#nullable disable + +namespace Sample.Models +{ + public enum MockInputEnum + { + Existing_Value = 0 + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/BackCompat_FixedEnumUnderscoresPreserved/MockInputEnum.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/BackCompat_FixedEnumUnderscoresPreserved/MockInputEnum.cs new file mode 100644 index 00000000000..ba99f9670c2 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/BackCompat_FixedEnumUnderscoresPreserved/MockInputEnum.cs @@ -0,0 +1,10 @@ +#nullable disable + +namespace Sample.Models +{ + public enum MockInputEnum + { + Existing_Value = 0, + Other = 1, + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/TestHelpers/MockHelpers.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/TestHelpers/MockHelpers.cs index 924b643ee08..df4e9ab513f 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/TestHelpers/MockHelpers.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/TestHelpers/MockHelpers.cs @@ -90,9 +90,9 @@ public static Mock LoadMockGenerator( ResetCache(); outputPath = outputPath ?? Path.Combine(AppContext.BaseDirectory, TestHelpersFolder); - if (includeXmlDocs) + if (includeXmlDocs && configuration == null) { - configuration = "{\"disable-xml-docs\": false, \"package-name\": \"Sample.Namespace\"}"; + configuration = $"{{\"disable-xml-docs\": false, \"package-name\": \"{inputNamespaceName ?? "Sample.Namespace"}\"}}"; } // initialize the singleton instance of the generator var mockGenerator = new Mock(new GeneratorContext(Configuration.Load(outputPath, configuration))) { CallBase = true };