|
2 | 2 | // Licensed under the MIT License. |
3 | 3 |
|
4 | 4 | using System; |
| 5 | +using System.Collections.Generic; |
5 | 6 | using System.Linq; |
| 7 | +using System.Reflection; |
6 | 8 | using System.Threading.Tasks; |
7 | 9 | using Microsoft.TypeSpec.Generator.ClientModel.Providers; |
8 | 10 | using Microsoft.TypeSpec.Generator.Input; |
9 | 11 | using Microsoft.TypeSpec.Generator.Primitives; |
10 | 12 | using Microsoft.TypeSpec.Generator.Providers; |
| 13 | +using Microsoft.TypeSpec.Generator.Statements; |
11 | 14 | using Microsoft.TypeSpec.Generator.Tests.Common; |
12 | 15 | using NUnit.Framework; |
13 | 16 |
|
@@ -909,6 +912,95 @@ public void TestSettingsConstructor_WithUrlEndpoint() |
909 | 912 | Assert.AreEqual(Helpers.GetExpectedFromFile(), file.Content); |
910 | 913 | } |
911 | 914 |
|
| 915 | + [Test] |
| 916 | + public async Task TestBindCoreMethod_WithCustomStructParam() |
| 917 | + { |
| 918 | + // A custom struct with a string constructor should use string binding |
| 919 | + var singletonField = typeof(ClientOptionsProvider).GetField("_singletonInstance", |
| 920 | + BindingFlags.Static | BindingFlags.NonPublic); |
| 921 | + singletonField?.SetValue(null, null); |
| 922 | + |
| 923 | + await MockHelpers.LoadMockGeneratorAsync( |
| 924 | + compilation: async () => await Helpers.GetCompilationFromDirectoryAsync()); |
| 925 | + |
| 926 | + var typeProvider = CodeModelGenerator.Instance.SourceInputModel |
| 927 | + .FindForTypeInCustomization("SampleNamespace", "CustomAudience"); |
| 928 | + Assert.IsNotNull(typeProvider, "CustomAudience should be found in custom code"); |
| 929 | + |
| 930 | + var body = new List<MethodBodyStatement>(); |
| 931 | + var sectionParam = new ParameterProvider( |
| 932 | + "section", |
| 933 | + $"The configuration section.", |
| 934 | + ClientSettingsProvider.IConfigurationSectionType); |
| 935 | + |
| 936 | + ClientSettingsProvider.AppendBindingForProperty(body, sectionParam, "Audience", "audience", typeProvider!.Type); |
| 937 | + |
| 938 | + var bodyString = string.Join("\n", body.Select(s => s.ToDisplayString())); |
| 939 | + Assert.IsTrue(bodyString.Contains("is string"), |
| 940 | + "Should use 'is string' pattern for custom struct with string constructor"); |
| 941 | + Assert.IsFalse(bodyString.Contains("GetSection"), |
| 942 | + "Should NOT use GetSection for custom struct with string constructor"); |
| 943 | + } |
| 944 | + |
| 945 | + [Test] |
| 946 | + public async Task TestBindCoreMethod_WithCustomIntStructParam() |
| 947 | + { |
| 948 | + // A custom struct with an int constructor should use int.TryParse binding |
| 949 | + var singletonField = typeof(ClientOptionsProvider).GetField("_singletonInstance", |
| 950 | + BindingFlags.Static | BindingFlags.NonPublic); |
| 951 | + singletonField?.SetValue(null, null); |
| 952 | + |
| 953 | + await MockHelpers.LoadMockGeneratorAsync( |
| 954 | + compilation: async () => await Helpers.GetCompilationFromDirectoryAsync()); |
| 955 | + |
| 956 | + var typeProvider = CodeModelGenerator.Instance.SourceInputModel |
| 957 | + .FindForTypeInCustomization("SampleNamespace", "CustomPriority"); |
| 958 | + Assert.IsNotNull(typeProvider, "CustomPriority should be found in custom code"); |
| 959 | + |
| 960 | + var body = new List<MethodBodyStatement>(); |
| 961 | + var sectionParam = new ParameterProvider( |
| 962 | + "section", |
| 963 | + $"The configuration section.", |
| 964 | + ClientSettingsProvider.IConfigurationSectionType); |
| 965 | + |
| 966 | + ClientSettingsProvider.AppendBindingForProperty(body, sectionParam, "Priority", "priority", typeProvider!.Type); |
| 967 | + |
| 968 | + var bodyString = string.Join("\n", body.Select(s => s.ToDisplayString())); |
| 969 | + Assert.IsTrue(bodyString.Contains("int.TryParse"), |
| 970 | + "Should use int.TryParse for custom struct with int constructor"); |
| 971 | + Assert.IsFalse(bodyString.Contains("GetSection"), |
| 972 | + "Should NOT use GetSection for custom struct with int constructor"); |
| 973 | + } |
| 974 | + |
| 975 | + [Test] |
| 976 | + public async Task TestBindCoreMethod_WithCustomStructParam_FallsBackToComplexObject() |
| 977 | + { |
| 978 | + // A custom struct with no single-parameter framework-type constructor |
| 979 | + // should fall back to complex object binding |
| 980 | + var singletonField = typeof(ClientOptionsProvider).GetField("_singletonInstance", |
| 981 | + BindingFlags.Static | BindingFlags.NonPublic); |
| 982 | + singletonField?.SetValue(null, null); |
| 983 | + |
| 984 | + await MockHelpers.LoadMockGeneratorAsync( |
| 985 | + compilation: async () => await Helpers.GetCompilationFromDirectoryAsync()); |
| 986 | + |
| 987 | + var typeProvider = CodeModelGenerator.Instance.SourceInputModel |
| 988 | + .FindForTypeInCustomization("SampleNamespace", "CustomComplex"); |
| 989 | + Assert.IsNotNull(typeProvider, "CustomComplex should be found in custom code"); |
| 990 | + |
| 991 | + var body = new List<MethodBodyStatement>(); |
| 992 | + var sectionParam = new ParameterProvider( |
| 993 | + "section", |
| 994 | + $"The configuration section.", |
| 995 | + ClientSettingsProvider.IConfigurationSectionType); |
| 996 | + |
| 997 | + ClientSettingsProvider.AppendBindingForProperty(body, sectionParam, "Complex", "complex", typeProvider!.Type); |
| 998 | + |
| 999 | + var bodyString = string.Join("\n", body.Select(s => s.ToDisplayString())); |
| 1000 | + Assert.IsTrue(bodyString.Contains("GetSection"), |
| 1001 | + "Should fall back to GetSection for struct with no single-parameter framework-type constructor"); |
| 1002 | + } |
| 1003 | + |
912 | 1004 | private static bool IsSettingsConstructor(ConstructorProvider c) => |
913 | 1005 | c.Signature?.Initializer != null && |
914 | 1006 | c.Signature?.Modifiers == MethodSignatureModifiers.Public && |
|
0 commit comments