Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 101 additions & 0 deletions docgenerator/SDKDocGenerator.UnitTests/MemberSignatureTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
using System;
using Xunit;
using SDKDocGenerator.PlatformMap;

namespace SDKDocGenerator.UnitTests
{
public class MemberSignatureTests
{
#region GetMemberType

[Theory]
[InlineData("M:Namespace.Type.Method(Params)", "M")]
[InlineData("T:Amazon.S3.Model.GetObjectRequest", "T")]
[InlineData("P:Amazon.Runtime.ClientConfig.Timeout", "P")]
[InlineData("F:Amazon.S3.Model.Region.USEast1", "F")]
[InlineData("E:Amazon.S3.Transfer.TransferUtility.UploadProgressEvent", "E")]
public void GetMemberType_ReturnsCorrectPrefix(string signature, string expected)
{
Assert.Equal(expected, MemberSignature.GetMemberType(signature));
}

[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData("X")]
[InlineData("MissingColon")]
public void GetMemberType_ThrowsOnInvalidSignature(string signature)
{
Assert.Throws<ArgumentException>(() => MemberSignature.GetMemberType(signature));
}

#endregion

#region GetMemberName

[Theory]
[InlineData("M:Amazon.S3.AmazonS3Client.GetObject(Amazon.S3.Model.GetObjectRequest)", "Amazon.S3.AmazonS3Client.GetObject")]
[InlineData("M:Amazon.S3.AmazonS3Client.GetObject", "Amazon.S3.AmazonS3Client.GetObject")]
[InlineData("T:Amazon.S3.Model.GetObjectRequest", "Amazon.S3.Model.GetObjectRequest")]
[InlineData("P:Amazon.Runtime.ClientConfig.Timeout", "Amazon.Runtime.ClientConfig.Timeout")]
[InlineData("M:Amazon.S3.AmazonS3Client.#ctor(System.String)", "Amazon.S3.AmazonS3Client.#ctor")]
public void GetMemberName_StripsParameterList(string signature, string expected)
{
Assert.Equal(expected, MemberSignature.GetMemberName(signature));
}

#endregion

#region GetDeclaringTypeName

[Theory]
[InlineData("M:Amazon.S3.AmazonS3Client.GetObject(Amazon.S3.Model.GetObjectRequest)", "Amazon.S3.AmazonS3Client")]
[InlineData("P:Amazon.Runtime.ClientConfig.ReadWriteTimeout", "Amazon.Runtime.ClientConfig")]
[InlineData("F:Amazon.S3.Model.Region.USEast1", "Amazon.S3.Model.Region")]
[InlineData("E:Amazon.S3.Transfer.TransferUtility.UploadProgressEvent", "Amazon.S3.Transfer.TransferUtility")]
[InlineData("M:Amazon.S3.AmazonS3Client.#ctor(System.String)", "Amazon.S3.AmazonS3Client")]
public void GetDeclaringTypeName_ExtractsTypeFromMember(string signature, string expected)
{
Assert.Equal(expected, MemberSignature.GetDeclaringTypeName(signature));
}

[Fact]
public void GetDeclaringTypeName_ReturnsFullName_ForTypeSignature()
{
// Type signatures have no member name after last dot, so this returns the namespace
var result = MemberSignature.GetDeclaringTypeName("T:Amazon.S3.Model.GetObjectRequest");
Assert.Equal("Amazon.S3.Model", result);
}

#endregion

#region ExtractMethodName

[Theory]
[InlineData("M:Amazon.S3.AmazonS3Client.GetObject(Amazon.S3.Model.GetObjectRequest)", "GetObject")]
[InlineData("M:Amazon.S3.AmazonS3Client.#ctor(System.String)", "#ctor")]
[InlineData("P:Amazon.Runtime.ClientConfig.Timeout", "Timeout")]
[InlineData("M:Amazon.TranscribeStreaming.AmazonTranscribeStreamingClient.StartTranscription(Amazon.TranscribeStreaming.Model.StartTranscriptionRequest)", "StartTranscription")]
public void ExtractMethodName_ReturnsMethodNameOnly(string signature, string expected)
{
Assert.Equal(expected, MemberSignature.ExtractMethodName(signature));
}

#endregion

#region Delegation Consistency

[Fact]
public void ForMethod_ProducesSameOutputAsNDocUtilities()
{
// Verify that MemberSignature.ForMethod and NDocUtilities.DetermineNDocNameLookupSignature
// produce the same result for the same input
var methodInfo = typeof(TestMethods).GetMethod("Query", new Type[] { typeof(string) });
var directResult = NDocUtilities.DetermineNDocNameLookupSignature(methodInfo, "");
var wrapperResult = MemberSignature.ForMethod(new MethodInfoWrapper(methodInfo, ""));
Assert.Equal(directResult, wrapperResult);
}

#endregion
}
}
65 changes: 65 additions & 0 deletions docgenerator/SDKDocGenerator.UnitTests/NDocMethodSignatureTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,48 @@ public void CollectionOutTestMethod()
signature);
}

[Fact]
public void ArrayParameterMethod()
{
var methodInfo = typeof(TestMethods).GetMethods().First(x => x.Name == "ArrayTest");
var signature = NDocUtilities.DetermineNDocNameLookupSignature(methodInfo, "");
Assert.Equal(
"M:SDKDocGenerator.UnitTests.TestMethods.ArrayTest(System.String[])",
signature);
}

[Fact]
public void NestedGenericParameterMethod()
{
var methodInfo = typeof(TestMethods).GetMethods().First(x => x.Name == "NestedGenericTest");
var signature = NDocUtilities.DetermineNDocNameLookupSignature(methodInfo, "");
Assert.Equal(
"M:SDKDocGenerator.UnitTests.TestMethods.NestedGenericTest(System.Collections.Generic.Dictionary{System.String,System.Collections.Generic.List{System.Int32}})",
signature);
}

[Fact]
public void ConstructorSignature_Parameterless()
{
var ctor = typeof(TestMethods).GetConstructor(Type.EmptyTypes);
var wrapper = new ConstructorInfoWrapper(ctor, "");
var signature = NDocUtilities.DetermineConstructorSignature(wrapper);
Assert.Equal(
"M:SDKDocGenerator.UnitTests.TestMethods.#ctor",
signature);
}

[Fact]
public void ConstructorSignature_WithParameters()
{
var ctor = typeof(TestCtorClass).GetConstructor(new[] { typeof(string), typeof(int) });
var wrapper = new ConstructorInfoWrapper(ctor, "");
var signature = NDocUtilities.DetermineConstructorSignature(wrapper);
Assert.Equal(
"M:SDKDocGenerator.UnitTests.TestCtorClass.#ctor(System.String,System.Int32)",
signature);
}

}

public class TestMethods
Expand Down Expand Up @@ -94,5 +136,28 @@ public void CollectionOutTest(out IList<string> param)
param = new List<string>();
}

/// <summary>
/// Test Method
/// </summary>
/// <param name="items"></param>
public void ArrayTest(string[] items)
{
}

/// <summary>
/// Test Method
/// </summary>
/// <param name="param"></param>
public void NestedGenericTest(Dictionary<string, List<int>> param)
{
}

}

public class TestCtorClass
{
public TestCtorClass(string name, int value)
{
}
}
}
216 changes: 216 additions & 0 deletions docgenerator/SDKDocGenerator.UnitTests/PlatformAvailabilityMapTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
using System;
using System.Collections.Generic;
using Xunit;
using SDKDocGenerator.PlatformMap;

namespace SDKDocGenerator.UnitTests
{
public class PlatformAvailabilityMapTests
{
#region Test Helpers

/// <summary>
/// Creates a PlatformAvailabilityMap with hand-crafted entries for testing
/// query behavior without loading any real SDK assemblies.
/// </summary>
private static PlatformAvailabilityMap CreateTestMap(
Dictionary<string, PlatformMemberEntry> memberIndex,
HashSet<string> allPlatforms,
string primaryPlatform = "net472",
string serviceName = "TestService")
{
return new PlatformAvailabilityMap(
serviceName,
primaryPlatform,
memberIndex,
allPlatforms,
new List<PlatformAssemblyContext>());
}

private static PlatformMemberEntry CreateEntry(string signature, string declaringType, params string[] platforms)
{
var entry = new PlatformMemberEntry(signature, declaringType);
foreach (var p in platforms)
entry.Platforms.Add(p);
return entry;
}

#endregion

#region Universal Member Queries

[Fact]
public void UniversalMember_IsAvailableOnAllPlatforms()
{
var index = new Dictionary<string, PlatformMemberEntry>(StringComparer.Ordinal)
{
["M:Ns.Type.Method()"] = CreateEntry("M:Ns.Type.Method()", "Ns.Type", "net472", "net8.0")
};
var platforms = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "net472", "net8.0" };

using var map = CreateTestMap(index, platforms);

Assert.True(map.IsMemberAvailableOnAllPlatforms("M:Ns.Type.Method()"));
Assert.False(map.IsMemberPlatformRestricted("M:Ns.Type.Method()"));
Assert.True(map.IsMemberAvailableOnPlatform("M:Ns.Type.Method()", "net472"));
Assert.True(map.IsMemberAvailableOnPlatform("M:Ns.Type.Method()", "net8.0"));
}

#endregion

#region Exclusive Member Queries (Core Feature)

[Fact]
public void ExclusiveMember_AvailableOnSupplemental_NotOnPrimary()
{
var index = new Dictionary<string, PlatformMemberEntry>(StringComparer.Ordinal)
{
["M:Ns.Client.StartTranscription(Ns.Request)"] = CreateEntry(
"M:Ns.Client.StartTranscription(Ns.Request)", "Ns.Client", "net8.0")
};
var platforms = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "net472", "net8.0" };

using var map = CreateTestMap(index, platforms);

Assert.True(map.IsMemberAvailableOnPlatform("M:Ns.Client.StartTranscription(Ns.Request)", "net8.0"));
Assert.False(map.IsMemberAvailableOnPlatform("M:Ns.Client.StartTranscription(Ns.Request)", "net472"));
Assert.False(map.IsMemberAvailableOnAllPlatforms("M:Ns.Client.StartTranscription(Ns.Request)"));
Assert.True(map.IsMemberPlatformRestricted("M:Ns.Client.StartTranscription(Ns.Request)"));
}

#endregion

#region Statistics

[Fact]
public void Statistics_CorrectlyCountUniversalAndRestricted()
{
var universal = CreateEntry("M:Ns.Type.Both()", "Ns.Type", "net472", "net8.0");
var restricted = CreateEntry("M:Ns.Type.OnlyNet8()", "Ns.Type", "net8.0");

var index = new Dictionary<string, PlatformMemberEntry>(StringComparer.Ordinal)
{
[universal.Signature] = universal,
[restricted.Signature] = restricted
};
var platforms = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "net472", "net8.0" };

using var map = CreateTestMap(index, platforms);

Assert.Equal(2, map.MemberCount);
Assert.Equal(1, map.UniversalMemberCount);
Assert.Equal(1, map.PlatformRestrictedMemberCount);
}

[Fact]
public void GetExclusiveMemberCount_CountsMembersOnOnlyOnePlatform()
{
var exclusive = CreateEntry("M:Ns.Type.OnlyNet8()", "Ns.Type", "net8.0");
var shared = CreateEntry("M:Ns.Type.Both()", "Ns.Type", "net472", "net8.0");

var index = new Dictionary<string, PlatformMemberEntry>(StringComparer.Ordinal)
{
[exclusive.Signature] = exclusive,
[shared.Signature] = shared
};
var platforms = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "net472", "net8.0" };

using var map = CreateTestMap(index, platforms);

Assert.Equal(1, map.GetExclusiveMemberCount("net8.0"));
Assert.Equal(0, map.GetExclusiveMemberCount("net472"));
}

#endregion

#region Platform Queries

[Fact]
public void GetMembersForPlatform_ReturnsMembersOnThatPlatform()
{
var both = CreateEntry("M:Ns.Type.Both()", "Ns.Type", "net472", "net8.0");
var net8Only = CreateEntry("M:Ns.Type.Net8Only()", "Ns.Type", "net8.0");

var index = new Dictionary<string, PlatformMemberEntry>(StringComparer.Ordinal)
{
[both.Signature] = both,
[net8Only.Signature] = net8Only
};
var platforms = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "net472", "net8.0" };

using var map = CreateTestMap(index, platforms);

var net472Members = map.GetMembersForPlatform("net472");
var net8Members = map.GetMembersForPlatform("net8.0");

Assert.Single(net472Members);
Assert.Equal(2, net8Members.Count);
}

[Fact]
public void Queries_ReturnEmpty_WhenNotFound()
{
var index = new Dictionary<string, PlatformMemberEntry>(StringComparer.Ordinal);
var platforms = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "net472" };

using var map = CreateTestMap(index, platforms);

Assert.Empty(map.GetPlatformsForMember("M:Ns.Type.DoesNotExist()"));
Assert.Empty(map.GetMembersForPlatform("netstandard2.0"));
}

#endregion

#region Platform Case Insensitivity

[Fact]
public void PlatformQueries_AreCaseInsensitive()
{
var entry = CreateEntry("M:Ns.Type.Method()", "Ns.Type", "net8.0");

var index = new Dictionary<string, PlatformMemberEntry>(StringComparer.Ordinal)
{
[entry.Signature] = entry
};
var platforms = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "net8.0" };

using var map = CreateTestMap(index, platforms);

// Query with different casing should work due to OrdinalIgnoreCase
Assert.True(map.IsMemberAvailableOnPlatform("M:Ns.Type.Method()", "NET8.0"));
Assert.True(map.IsMemberAvailableOnPlatform("M:Ns.Type.Method()", "Net8.0"));
}

#endregion

#region Disposal

[Fact]
public void ThrowsObjectDisposedException_AfterDispose()
{
var index = new Dictionary<string, PlatformMemberEntry>(StringComparer.Ordinal);
var platforms = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "net472" };

var map = CreateTestMap(index, platforms);
map.Dispose();

Assert.Throws<ObjectDisposedException>(() => map.GetPlatformsForMember("M:Ns.Type.Method()"));
Assert.Throws<ObjectDisposedException>(() => map.IsMemberAvailableOnPlatform("M:Ns.Type.Method()", "net472"));
Assert.Throws<ObjectDisposedException>(() => map.IsMemberAvailableOnAllPlatforms("M:Ns.Type.Method()"));
Assert.Throws<ObjectDisposedException>(() => map.GetMembersForPlatform("net472"));
}

[Fact]
public void DoubleDispose_DoesNotThrow()
{
var index = new Dictionary<string, PlatformMemberEntry>(StringComparer.Ordinal);
var platforms = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "net472" };

var map = CreateTestMap(index, platforms);
map.Dispose();
map.Dispose(); // Should not throw
}

#endregion
}
}
Loading
Loading