Skip to content

Commit

Permalink
Change XML comments to be tested at run-time
Browse files Browse the repository at this point in the history
Signed-off-by: Dimitar Dobrev <dpldobrev@protonmail.com>
  • Loading branch information
ddobrev committed Jan 2, 2021
1 parent 8683546 commit 8451cef
Show file tree
Hide file tree
Showing 5 changed files with 207 additions and 139 deletions.
71 changes: 1 addition & 70 deletions src/Generator.Tests/AST/TestAST.cs
Original file line number Diff line number Diff line change
Expand Up @@ -402,75 +402,6 @@ public void TestFunctionInstantiatedFrom()
classTemplate.TemplatedClass.Constructors.First(c => !c.IsCopyConstructor && !c.IsMoveConstructor));
}

[Test]
public void TestComments()
{
var @class = AstContext.FindCompleteClass("TestComments");
var textGenerator = new TextGenerator();
textGenerator.Print(@class.Comment.FullComment, CommentKind.BCPLSlash);
Assert.AreEqual(@"/// <summary>
/// <para>Hash set/map base class.</para>
/// <para>Note that to prevent extra memory use due to vtable pointer, %HashBase intentionally does not declare a virtual destructor</para>
/// <para>and therefore %HashBase pointers should never be used.</para>
/// </summary>
".Replace("\r", string.Empty), textGenerator.StringBuilder.Replace("\r", string.Empty).ToString());

var method = @class.Methods.First(m => m.Name == "GetIOHandlerControlSequence");
textGenerator.StringBuilder.Clear();
textGenerator.Print(method.Comment.FullComment, CommentKind.BCPL);
Assert.AreEqual(@"// <summary>
// <para>Get the string that needs to be written to the debugger stdin file</para>
// <para>handle when a control character is typed.</para>
// </summary>
// <param name=""ch"">The character that was typed along with the control key</param>
// <returns>
// <para>The string that should be written into the file handle that is</para>
// <para>feeding the input stream for the debugger, or NULL if there is</para>
// <para>no string for this control key.</para>
// </returns>
// <remarks>
// <para>Some GUI programs will intercept &quot;control + char&quot; sequences and want</para>
// <para>to have them do what normally would happen when using a real</para>
// <para>terminal, so this function allows GUI programs to emulate this</para>
// <para>functionality.</para>
// </remarks>
".Replace("\r", string.Empty), textGenerator.StringBuilder.Replace("\r", string.Empty).ToString());

var methodTestDoxygen = @class.Methods.First(m => m.Name == "SBAttachInfo");
textGenerator.StringBuilder.Clear();
textGenerator.Print(methodTestDoxygen.Comment.FullComment, CommentKind.BCPLSlash);
Assert.AreEqual(@"/// <summary>Attach to a process by name.</summary>
/// <param name=""path"">A full or partial name for the process to attach to.</param>
/// <param name=""wait_for"">
/// <para>If <c>false,</c> attach to an existing process whose name matches.</para>
/// <para>If <c>true,</c> then wait for the next process whose name matches.</para>
/// </param>
/// <remarks>
/// <para>This function implies that a future call to SBTarget::Attach(...)</para>
/// <para>will be synchronous.</para>
/// </remarks>
".Replace("\r", string.Empty), textGenerator.StringBuilder.Replace("\r", string.Empty).ToString());

var methodDoxygenCustomTags = @class.Methods.First(m => m.Name == "glfwDestroyWindow");
new CleanCommentsPass().VisitFull(methodDoxygenCustomTags.Comment.FullComment);
textGenerator.StringBuilder.Clear();
textGenerator.Print(methodDoxygenCustomTags.Comment.FullComment, CommentKind.BCPLSlash);
Assert.AreEqual(@"/// <summary>Destroys the specified window and its context.</summary>
/// <param name=""window"">The window to destroy.</param>
/// <remarks>
/// <para>This function destroys the specified window and its context. On calling</para>
/// <para>this function, no further callbacks will be called for that window.</para>
/// <para>If the context of the specified window is current on the main thread, it is</para>
/// <para>detached before being destroyed.</para>
/// <para>The context of the specified window must not be current on any other</para>
/// <para>thread when this function is called.</para>
/// <para>This function must not be called from a callback.</para>
/// <para>This function must only be called from the main thread.</para>
/// <para>Added in version 3.0. Replaces `glfwCloseWindow`.</para>
/// </remarks>
".Replace("\r", string.Empty), textGenerator.StringBuilder.Replace("\r", string.Empty).ToString());
}

[Test]
public void TestCompletionOfClassTemplates()
{
Expand Down Expand Up @@ -515,7 +446,7 @@ public void TestPrintingSpecializationWithConstValue()
[Test]
public void TestLayoutBase()
{
var @class = AstContext.FindCompleteClass("TestComments");
var @class = AstContext.FindCompleteClass("HasConstFunction");
Assert.That(@class.Layout.Bases.Count, Is.EqualTo(0));
}

Expand Down
1 change: 1 addition & 0 deletions tests/NamespacesDerived/NamespacesDerived.CSharp.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<EnableGeneratorCompileItems>false</EnableGeneratorCompileItems>
<DocumentationFile>NamespacesDerived.CSharp.xml</DocumentationFile>
</PropertyGroup>

<ItemGroup>
Expand Down
138 changes: 137 additions & 1 deletion tests/NamespacesDerived/NamespacesDerived.Tests.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
using NamespacesDerived;
using System.IO;
using System.Xml.Linq;
using System.Linq;
using System;
using System.Reflection;
using NUnit.Framework;
using NamespacesDerived;

[TestFixture]
public class NamespaceDerivedTests
Expand Down Expand Up @@ -39,6 +44,137 @@ public void TestOverrideMethodFromDependency()
}
}

[Test]
public void TestComments()
{
Type testCommentsType = typeof(TestComments);
Assembly assembly = testCommentsType.Assembly;
string dir = Path.GetDirectoryName(assembly.Location);
string xml = Path.ChangeExtension(Path.GetFileName(assembly.Location), ".xml");
XDocument xmlDoc = XDocument.Load(Path.Combine(dir, xml));
XElement members = xmlDoc.Root.Element("members");

TestClassComment(testCommentsType, members);
TestGetIOHandlerControlSequence(testCommentsType, members);
TestSBAttachInfo(testCommentsType, members);
TestGlfwDestroyWindow(testCommentsType, members);
}

private static void TestClassComment(Type testCommentsType, XElement members)
{
string testCommentsName = $"T:{testCommentsType.FullName}";
XElement testComments = members.Elements().Single(
m => m.Attribute("name").Value == testCommentsName);
Assert.That(testComments.Element("summary").Elements().Select(p => p.Value), Is.EquivalentTo(
new[]
{
"Hash set/map base class.",
"Note that to prevent extra memory use due to vtable pointer, %HashBase intentionally does not declare a virtual destructor",
"and therefore %HashBase pointers should never be used."
}));
}

private static void TestGetIOHandlerControlSequence(Type testCommentsType, XElement members)
{
const string name = "GetIOHandlerControlSequence";
XElement method = FindMethod(testCommentsType, members, name);
Assert.That(method.Element("summary").Elements("para").Select(p => p.Value),
Is.EquivalentTo(
new[]
{
"Get the string that needs to be written to the debugger stdin file",
"handle when a control character is typed."
}));
Assert.That(method.Element("returns").Elements("para").Select(p => p.Value),
Is.EquivalentTo(
new[]
{
"The string that should be written into the file handle that is",
"feeding the input stream for the debugger, or NULL if there is",
"no string for this control key."
}));
Assert.That(method.Element("remarks").Elements("para").Select(p => p.Value),
Is.EquivalentTo(
new[]
{
"Some GUI programs will intercept \"control + char\" sequences and want",
"to have them do what normally would happen when using a real",
"terminal, so this function allows GUI programs to emulate this",
"functionality."
}));
XElement ch = method.Element("param");
Assert.That(ch.Attribute("name").Value,
Is.EqualTo(testCommentsType.GetMethod(name).GetParameters()[0].Name));
Assert.That(ch.Value, Is.EqualTo("The character that was typed along with the control key"));
}

private static void TestSBAttachInfo(Type testCommentsType, XElement members)
{
const string name = "SBAttachInfo";
XElement method = FindMethod(testCommentsType, members, name);
Assert.That(method.Element("remarks").Elements("para").Select(p => p.Value),
Is.EquivalentTo(
new[]
{
"This function implies that a future call to SBTarget::Attach(...)",
"will be synchronous."
}));

ParameterInfo[] @params = testCommentsType.GetMethod(name).GetParameters();

XElement path = method.Element("param");
Assert.That(path.Attribute("name").Value,
Is.EqualTo(@params[0].Name));
Assert.That(path.Value, Is.EqualTo("A full or partial name for the process to attach to."));

XElement wait_for = (XElement) path.NextNode;
Assert.That(wait_for.Attribute("name").Value, Is.EqualTo(@params[1].Name));
Assert.That(wait_for.Elements("para").Select(p => string.Concat(p.Nodes())),
Is.EquivalentTo(
new[]
{
"If <c>false,</c> attach to an existing process whose name matches.",
"If <c>true,</c> then wait for the next process whose name matches."
}));
}

private static void TestGlfwDestroyWindow(Type testCommentsType, XElement members)
{
const string name = "GlfwDestroyWindow";
XElement method = FindMethod(testCommentsType, members, name);
Assert.That(method.Element("summary").Value,
Is.EqualTo("Destroys the specified window and its context."));
Assert.That(method.Element("remarks").Elements("para").Select(p => p.Value),
Is.EquivalentTo(
new[]
{
"This function destroys the specified window and its context. On calling",
"this function, no further callbacks will be called for that window.",
"If the context of the specified window is current on the main thread, it is",
"detached before being destroyed.",
"The context of the specified window must not be current on any other",
"thread when this function is called.",
"This function must not be called from a callback.",
"_safety This function must only be called from the main thread.",
"Added in version 3.0. Replaces `glfwCloseWindow`."
}));
XElement window = method.Element("param");
Assert.That(window.Attribute("name").Value,
Is.EqualTo(testCommentsType.GetMethod(name).GetParameters()[0].Name));
Assert.That(window.Value, Is.EqualTo("The window to destroy."));
}

private static XElement FindMethod(Type testCommentsType, XElement members, string name)
{
string fullName = $"M:{testCommentsType.FullName}.{name}";
return members.Elements().Single(
m =>
{
string name = m.Attribute("name").Value;
return name.Substring(0, Math.Max(name.IndexOf('('), 0)) == fullName;
});
}

private class OverrideMethodFromDependency : HasVirtualInDependency
{
public override int VirtualInCore(int parameter) => 2;
Expand Down
68 changes: 68 additions & 0 deletions tests/NamespacesDerived/NamespacesDerived.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,71 @@ namespace NamespacesBase
Base base;
};
}

/// Hash set/map base class.
/** Note that to prevent extra memory use due to vtable pointer, %HashBase intentionally does not declare a virtual destructor
and therefore %HashBase pointers should never be used.
*/
class TestComments
{
public:
//----------------------------------------------------------------------
/// Get the string that needs to be written to the debugger stdin file
/// handle when a control character is typed.
///
/// Some GUI programs will intercept "control + char" sequences and want
/// to have them do what normally would happen when using a real
/// terminal, so this function allows GUI programs to emulate this
/// functionality.
///
/// @param[in] ch
/// The character that was typed along with the control key
///
/// @return
/// The string that should be written into the file handle that is
/// feeding the input stream for the debugger, or NULL if there is
/// no string for this control key.
//----------------------------------------------------------------------
const char* GetIOHandlerControlSequence(char ch);

//------------------------------------------------------------------
/// Attach to a process by name.
///
/// This function implies that a future call to SBTarget::Attach(...)
/// will be synchronous.
///
/// @param[in] path
/// A full or partial name for the process to attach to.
///
/// @param[in] wait_for
/// If \b false, attach to an existing process whose name matches.
/// If \b true, then wait for the next process whose name matches.
//------------------------------------------------------------------
int SBAttachInfo(const char* path, bool wait_for);

/*! @brief Destroys the specified window and its context.
*
* This function destroys the specified window and its context. On calling
* this function, no further callbacks will be called for that window.
*
* If the context of the specified window is current on the main thread, it is
* detached before being destroyed.
*
* @param[in] window The window to destroy.
*
* @note The context of the specified window must not be current on any other
* thread when this function is called.
*
* @reentrancy This function must not be called from a callback.
*
* @thread_safety This function must only be called from the main thread.
*
* @since Added in version 3.0. Replaces `glfwCloseWindow`.
*/
void glfwDestroyWindow(int* window);

/**
* <sip:alice@example.net>
*/
class LinphoneAddress {};
};
68 changes: 0 additions & 68 deletions tests/Native/AST.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,74 +128,6 @@ void instantiatesTemplate(TestSpecializationArguments<int> i, TestSpecialization
{
}

/// Hash set/map base class.
/** Note that to prevent extra memory use due to vtable pointer, %HashBase intentionally does not declare a virtual destructor
and therefore %HashBase pointers should never be used.
*/
class TestComments
{
public:
//----------------------------------------------------------------------
/// Get the string that needs to be written to the debugger stdin file
/// handle when a control character is typed.
///
/// Some GUI programs will intercept "control + char" sequences and want
/// to have them do what normally would happen when using a real
/// terminal, so this function allows GUI programs to emulate this
/// functionality.
///
/// @param[in] ch
/// The character that was typed along with the control key
///
/// @return
/// The string that should be written into the file handle that is
/// feeding the input stream for the debugger, or NULL if there is
/// no string for this control key.
//----------------------------------------------------------------------
const char * GetIOHandlerControlSequence(char ch);

//------------------------------------------------------------------
/// Attach to a process by name.
///
/// This function implies that a future call to SBTarget::Attach(...)
/// will be synchronous.
///
/// @param[in] path
/// A full or partial name for the process to attach to.
///
/// @param[in] wait_for
/// If \b false, attach to an existing process whose name matches.
/// If \b true, then wait for the next process whose name matches.
//------------------------------------------------------------------
int SBAttachInfo(const char *path, bool wait_for);

/*! @brief Destroys the specified window and its context.
*
* This function destroys the specified window and its context. On calling
* this function, no further callbacks will be called for that window.
*
* If the context of the specified window is current on the main thread, it is
* detached before being destroyed.
*
* @param[in] window The window to destroy.
*
* @note The context of the specified window must not be current on any other
* thread when this function is called.
*
* @reentrancy This function must not be called from a callback.
*
* @thread_safety This function must only be called from the main thread.
*
* @since Added in version 3.0. Replaces `glfwCloseWindow`.
*/
void glfwDestroyWindow(int* window);

/**
* <sip:alice@example.net>
*/
class LinphoneAddress {};
};

template <typename T>
class ForwardedTemplate;

Expand Down

0 comments on commit 8451cef

Please sign in to comment.