using System;
using System.Xml;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
/// Class used to check XML documentation content generated
/// by C# compiler for Doxygen tests.
/// Methods allow to look for the documentation of a specific object (method, type, variable)
/// and to check its content.
public class doxygen_checker{
private XmlElement _root;
private string _namespace_prefix;
/// Enum to identify the different types of objects in XML
public enum CodeType
{
/// member of a class/type
MEMBER,
/// class/type itself
TYPE
};
/// Constructor
///
/// The name of namespace to consider
/// The name of the class inside the namespace
/// The file path where the XML to parse is located
public doxygen_checker(string namespaceName, string className, string xmlFilePath)
{
XmlDocument doc = new XmlDocument();
string fileContent;
try{
fileContent = File.ReadAllText(xmlFilePath);
}
catch (Exception e)
{
throw new ApplicationException("Error: " + e.Message);
}
doc.LoadXml(fileContent);
_root = doc.DocumentElement; //get the root element.
_namespace_prefix = namespaceName +"Namespace." + className + ".";
}
/// Check general content (not included in a tag) of an object
///
/// The type of object to check
/// The name of the object (function mostly) to check
/// The content of the documentation for the given object
/// When object or expected content is not found
public void checkText(CodeType codeType, string objectName, string expectedContent)
{
string completeObjectName = _createObjectName(codeType, objectName);
bool found = false;
_root.SelectNodes("members/member").Cast().ToList().ForEach(member =>
{
if (member.Attributes["name"].Value == completeObjectName)
{
found = true;
// parse all children of member until #text is found
string valMember = member.ChildNodes.Cast().ToList().Find(node => node.Name == "#text").InnerText;
if (uniformizeString(expectedContent) != uniformizeString(valMember))
{
throw new ApplicationException("Error: " + objectName + " contains '" + valMember + "', expected '" + expectedContent + "'");
}
return;
}
});
if (!found)
throw new ApplicationException("Object '" + objectName + "' not found");
}
/// Check field content from an object
///
/// The type of object to check
/// The name of object (function mostly) to check
/// The name of the XML tag (summary, param,...) of the object to check
/// The index of the field (0 for the first one, 1 for the second, etc) to check
/// The content of the documentation for the given object
/// When object, field, or expected content is not found
public void checkObject(CodeType codeType, string objectName, string fieldName, int itemIndex, string expectedContent)
{
string completeObjectName = _createObjectName(codeType, objectName);
bool found = false;
_root.SelectNodes("members/member").Cast().ToList().ForEach(member =>
{
if (member.Attributes["name"].Value == completeObjectName)
{
found = true;
var field = member.SelectNodes(fieldName);
if (field == null)
throw new ApplicationException("Field " + fieldName + " is not found");
if (itemIndex >= field.Count)
{
throw new ApplicationException("Error: " + objectName + " has " + field.Count + " fields, "+ itemIndex +"th value does not exist");
}
else
{
var valField = field.Item(itemIndex).InnerText;
if (uniformizeString(expectedContent) != uniformizeString(valField))
{
throw new ApplicationException("Error: " + objectName + " field '" + fieldName + "' is '" + valField + "', expected '" + expectedContent + "'");
}
}
return;
}
});
if(!found)
throw new ApplicationException("Object '" + objectName + "' not found" );
}
private string _createObjectName(CodeType codeType, string objectName)
{
if(codeType == CodeType.MEMBER)
return "M:" + _namespace_prefix + objectName;
else
return "T:" + _namespace_prefix + objectName;
}
/// Returns a string without break lines so as to avoid issues during string comparison
private string uniformizeString(string str)
{
// remove line breaks in string
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
return Regex.Replace(str, @"\t|\n|\r", "");
else
return Regex.Replace(str, @"\s|\n|\r", "");
}
/// Check field attribute from an object
///
/// The type of object to check
/// The name of object (function mostly) to check
/// The name of the XML tag (summary, param,...) of the object to check
/// The index of the field (0 for the first one, 1 for the second, etc) to check
/// The XML attribute to consider
/// The content of the documentation for the given object
/// When object, field, attribute or expected content is not found
public void checkObjectAttribute(CodeType codeType, string objectName, string fieldName, int itemIndex, string attributeName, string expectedContent)
{
string completeObjectName = _createObjectName(codeType, objectName);
bool found = false;
_root.SelectNodes("members/member").Cast().ToList().ForEach(member =>
{
if (member.Attributes["name"].Value == completeObjectName)
{
found = true;
var field = member.SelectNodes(fieldName);
if (field == null)
throw new ApplicationException("Field " + fieldName + " is not found");
if(field.Count == 0)
throw new ApplicationException("Field " + fieldName + " is empty");
// read attribute of first field's item
string valItem = field.Item(itemIndex).Attributes[attributeName].Value;
if(uniformizeString(valItem) != uniformizeString(expectedContent))
throw new ApplicationException("Error: " + objectName + " field " + fieldName + " is '" + valItem + "', expected '" + expectedContent + "'");
return;
}
});
if (!found)
throw new ApplicationException("Object '" + objectName + "' not found");
}
}