Jo Shields a575963da9 Imported Upstream version 3.6.0
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
2014-08-13 10:39:27 +01:00

302 lines
9.2 KiB
C#

//
// Authors:
// Rafael Mizrahi <rafim@mainsoft.com>
// Erez Lotan <erezl@mainsoft.com>
// Vladimir Krasnov <vladimirk@mainsoft.com>
//
//
// Copyright (c) 2002-2005 Mainsoft Corporation.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.IO;
using System.Xml;
using System.Net;
using System.Text;
using System.Collections;
using System.Reflection;
using NUnit.Framework;
namespace MonoTests.stand_alone.WebHarness
{
public class HtmlDiff
{
public const string BEGIN_TAG = "begint";
public const string END_TAG = "endt";
private XmlDocument _xmlIgnoreList = null;
private string _compareStatus = "";
private static string _compareActual = "";
private static string _compareExpect = "";
private string _ignoreListFile = "";
public HtmlDiff()
{
}
public string IgnoreListFile
{
get {return _ignoreListFile;}
set {_ignoreListFile = value;}
}
public string CompareStatus
{
get {return _compareStatus.ToString();}
}
public static string GetControlFromPageHtml (string str)
{
return GetControlFromPageHtml (str, BEGIN_TAG, END_TAG);
}
public static string GetControlFromPageHtml (string str, string beginTag, string endTag)
{
if (str == null || str.Length == 0)
throw new ArgumentException ("internal error: str is null or empty");
if (beginTag == null || beginTag.Length == 0)
throw new ArgumentNullException ("beginTag");
if (endTag == null || endTag.Length == 0)
throw new ArgumentNullException ("endTag");
int beginPos = str.IndexOf (beginTag);
int endPos = str.IndexOf (endTag);
if (beginPos == -1)
throw new InvalidOperationException (String.Format ("internal error: begin tag ('{0}') is missing. Full source: {1}", beginTag, str));
if (endPos == -1)
throw new InvalidOperationException (String.Format ("internal error: end tag ('{0}') is missing. Full source: {1}", endTag, str));
StringBuilder sb = new StringBuilder ();
sb.Append (str.Substring (beginPos + beginTag.Length, endPos - beginPos - beginTag.Length));
return sb.ToString ();
}
public static void AssertAreEqual (string origin, string derived, string msg)
{
bool test = false;
try {
test = HtmlComparer (origin, derived);
}
catch (Exception e) {
//swallow e when there is XML error and fallback
//to the text comparison
Assert.AreEqual (origin, derived, msg);
}
if (!test) {
Assert.AreEqual (_compareExpect, _compareActual, msg);
}
}
private static bool HtmlComparer (string origin, string derived)
{
XmlDocument or = new XmlDocument ();
MonoTests.stand_alone.WebHarness.HtmlDiff helper = new MonoTests.stand_alone.WebHarness.HtmlDiff ();
or.LoadXml (helper.HtmltoXml (origin));
XmlDocument dr = new XmlDocument ();
dr.LoadXml (helper.HtmltoXml (derived));
return helper.XmlCompare (or, dr, false);
}
private bool XmlCompare(XmlDocument expected, XmlDocument actual, bool ignoreAlmost)
{
XmlComparer comparer = new XmlComparer();
if (ignoreAlmost == false)
{
DoAlmost(expected);
DoAlmost(actual);
}
bool c = comparer.AreEqual(expected, actual);
_compareStatus = comparer.LastCompare;
_compareActual = comparer.Actual;
_compareExpect = comparer.Expected;
return c;
}
public string HtmltoXml (string html) //throws XmlException
{
HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument ();
doc.LoadHtml (html.Trim (new char[] { '\r', '\n', ' ' })); // bug in HtmlAgilityPack
StringBuilder fixedxml = new StringBuilder ();
StringWriter sw = new StringWriter (fixedxml);
StringBuilder tempxml = new StringBuilder ();
StringWriter tsw = new StringWriter (tempxml);
doc.OptionOutputAsXml = true;
doc.Save (tsw);
// fix style attribute
// the reason is that style attribute name-value pairs come in different order
// in .NET and GH
// Here I will sort the values of style attribute
XmlDocument tempDoc = new XmlDocument ();
tempDoc.LoadXml (tempxml.ToString ());
XmlNodeList allNodes = tempDoc.SelectNodes ("//*");
foreach (XmlNode n in allNodes) {
if (n.Attributes["style"] != null) {
string att = n.Attributes["style"].Value;
string[] style = att.Trim (new char[] { ' ', ';' }).Split (';');
for (int styleIndex = 0; styleIndex < style.Length; styleIndex++) {
style[styleIndex] = FixStyleNameValue (style[styleIndex]);
}
Array.Sort (style);
n.Attributes["style"].Value = string.Join (";", style);
}
}
tempDoc.Save (sw);
return fixedxml.ToString ();
}
private string FixStyleNameValue(string nameValue)
{
string [] nv = nameValue.Split(':');
// value may contain spaces in case of
// multiple values for one key
string [] nvalue = nv[1].Trim().Split(' ');
Array.Sort(nvalue);
nv[1] = string.Join(" ", nvalue);
return nv[0].Trim().ToLower() + ":" + nv[1].Trim().ToLower();
}
private void DoAlmost(XmlDocument xmlDocument)
{
XmlNode XmlIgnoreNode;
IEnumerator xmlIgnoreEnum;
if (_xmlIgnoreList == null)
{
_xmlIgnoreList = new XmlDocument();
string xml;
Stream source = Assembly.GetExecutingAssembly ()
.GetManifestResourceStream ("HtmlCompare.nunitweb_config.xml");
if (source == null) {
source = Assembly.GetExecutingAssembly ()
.GetManifestResourceStream ("nunitweb_config.xml");
}
try {
using (StreamReader sr = new StreamReader (source))
xml = sr.ReadToEnd ();
}
finally {
source.Close ();
}
_xmlIgnoreList.LoadXml (xml);
}
// Remove by Id or Name
// search by tag and if id or name match, remove all attributes
// must be the first almost since the following almost delete the id and name
xmlIgnoreEnum = _xmlIgnoreList.SelectSingleNode("Almost/RemoveById").GetEnumerator();
while (xmlIgnoreEnum.MoveNext())
{
XmlNodeList DocNodeList;
XmlIgnoreNode = (XmlNode)xmlIgnoreEnum.Current;
DocNodeList = xmlDocument.GetElementsByTagName("*");
if (DocNodeList != null)
{
foreach (XmlElement tmpXmlElement in DocNodeList)
{
foreach (XmlAttribute tmpIgnoreAttr in XmlIgnoreNode.Attributes)
{
if (tmpXmlElement.Name.ToLower() == XmlIgnoreNode.Name.ToLower())
{
if (tmpXmlElement.Attributes[tmpIgnoreAttr.Name] != null )
{
if (tmpXmlElement.Attributes[tmpIgnoreAttr.Name].Value.ToLower() == tmpIgnoreAttr.Value.ToLower())
{
tmpXmlElement.RemoveAllAttributes();
}
}
}
}
}
}
}
// remove ignored attributes
// search for tag and remove it's attributes
xmlIgnoreEnum = _xmlIgnoreList.SelectSingleNode("Almost/IgnoreList").GetEnumerator(); //FirstChild.GetEnumerator
while (xmlIgnoreEnum.MoveNext())
{
XmlIgnoreNode = (XmlNode)xmlIgnoreEnum.Current;
XmlNodeList DocNodeList;
//clean specific element
DocNodeList = xmlDocument.GetElementsByTagName("*");
if (DocNodeList != null)
{
foreach (XmlElement tmpXmlElement in DocNodeList)
{
if (tmpXmlElement.Name.ToLower() == XmlIgnoreNode.Name.ToLower())
{
foreach (XmlAttribute tmpIgnoreAttr in XmlIgnoreNode.Attributes)
{
tmpXmlElement.RemoveAttribute(tmpIgnoreAttr.Name);
}
}
}
}
}
// clean javascript attribute value
xmlIgnoreEnum = _xmlIgnoreList.SelectSingleNode("Almost/CleanJavaScriptValueList").GetEnumerator(); //FirstChild.GetEnumerator
while (xmlIgnoreEnum.MoveNext())
{
XmlIgnoreNode = (XmlNode)xmlIgnoreEnum.Current;
XmlNodeList DocNodeList;
//clean Java Script attribute values
DocNodeList = xmlDocument.GetElementsByTagName("*");
if (DocNodeList != null)
{
foreach (XmlElement tmpXmlElement in DocNodeList)
{
if (tmpXmlElement.Name.ToLower() == XmlIgnoreNode.Name.ToLower())
{
foreach (XmlAttribute tmpIgnoreAttr in XmlIgnoreNode.Attributes)
{
if (tmpXmlElement.Attributes[tmpIgnoreAttr.Name] != null )
{
if (tmpXmlElement.Attributes[tmpIgnoreAttr.Name].Value.ToLower().IndexOf("javascript") >= 0 )
{
tmpXmlElement.SetAttribute(tmpIgnoreAttr.Name, "");
}
}
}
}
}
}
}
}
}
}