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"); } }