using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Xsl;
using umbraco;
using Umbraco.Core;
using Umbraco.Core.Xml;
using Umbraco.Core.Xml.XPath;
using NUnit.Framework;
namespace Umbraco.Tests.CoreXml
{
[TestFixture]
public class NavigableNavigatorTests
{
[Test]
public void NewNavigatorIsAtRoot()
{
const string xml = @"";
var doc = XmlHelper.CreateXPathDocument(xml);
var nav = doc.CreateNavigator();
Assert.AreEqual(XPathNodeType.Root, nav.NodeType);
Assert.AreEqual(string.Empty, nav.Name);
var source = new TestSource5();
nav = new NavigableNavigator(source);
Assert.AreEqual(XPathNodeType.Root, nav.NodeType);
Assert.AreEqual(string.Empty, nav.Name);
}
[Test]
public void NativeXmlValues()
{
const string xml = @"blah
blah
bam";
var doc = XmlHelper.CreateXPathDocument(xml);
var nav = doc.CreateNavigator();
NavigatorValues(nav, true);
}
[Test]
public void NavigableXmlValues()
{
var source = new TestSource6();
var nav = new NavigableNavigator(source);
NavigatorValues(nav, false);
}
static void NavigatorValues(XPathNavigator nav, bool native)
{
// in non-native we can't have Value dump everything, else
// we'd dump the entire database? Makes not much sense.
Assert.AreEqual(native ? "\n blah\n blah\n bam\n " : string.Empty, nav.Value.Lf()); // !!
Assert.IsTrue(nav.MoveToFirstChild());
Assert.AreEqual("root", nav.Name);
Assert.AreEqual(native ? "\n blah\n blah\n bam\n " : string.Empty, nav.Value.Lf()); // !!
Assert.IsTrue(nav.MoveToFirstChild());
Assert.AreEqual("wrap", nav.Name);
Assert.AreEqual(native ? "\n blah\n blah\n bam\n " : string.Empty, nav.Value.Lf()); // !!
Assert.IsTrue(nav.MoveToFirstChild());
Assert.AreEqual("item1", nav.Name);
Assert.AreEqual(string.Empty, nav.Value);
Assert.IsFalse(nav.MoveToFirstChild());
Assert.IsTrue(nav.MoveToNext());
Assert.AreEqual("item2", nav.Name);
Assert.AreEqual(string.Empty, nav.Value);
Assert.IsFalse(nav.MoveToFirstChild()); // !!
Assert.IsTrue(nav.MoveToNext());
Assert.AreEqual("item2a", nav.Name);
Assert.AreEqual(string.Empty, nav.Value);
Assert.IsFalse(nav.MoveToFirstChild()); // !!
Assert.IsTrue(nav.MoveToNext());
Assert.AreEqual("item2b", nav.Name);
Assert.AreEqual(string.Empty, nav.Value);
Assert.IsFalse(nav.MoveToFirstChild()); // !!
// we have no way to tell the navigable that a value is CDATA
// so the rule is, if it's null it's not there, anything else is there
// and the filtering has to be done when building the content
Assert.IsTrue(nav.MoveToNext());
Assert.AreEqual("item2c", nav.Name);
Assert.AreEqual("\n ", nav.Value.Lf()); // ok since it's a property
Assert.IsTrue(nav.MoveToFirstChild());
Assert.AreEqual(XPathNodeType.Text, nav.NodeType);
Assert.AreEqual(string.Empty, nav.Name);
Assert.AreEqual("\n ", nav.Value.Lf());
Assert.IsTrue(nav.MoveToParent());
Assert.IsTrue(nav.MoveToNext());
Assert.AreEqual("item3", nav.Name);
Assert.AreEqual("blah", nav.Value); // ok since it's a property
Assert.IsTrue(nav.MoveToFirstChild());
Assert.AreEqual(XPathNodeType.Text, nav.NodeType);
Assert.AreEqual(string.Empty, nav.Name);
Assert.AreEqual("blah", nav.Value);
Assert.IsTrue(nav.MoveToParent());
Assert.IsTrue(nav.MoveToNext());
Assert.AreEqual("item3a", nav.Name);
Assert.AreEqual("\n blah\n ", nav.Value.Lf()); // ok since it's a property
Assert.IsTrue(nav.MoveToFirstChild());
Assert.AreEqual(XPathNodeType.Text, nav.NodeType);
Assert.AreEqual(string.Empty, nav.Name);
Assert.AreEqual("\n blah\n ", nav.Value.Lf());
Assert.IsTrue(nav.MoveToParent());
Assert.IsTrue(nav.MoveToNext());
Assert.AreEqual("item4", nav.Name);
Assert.AreEqual("bam", nav.Value); // ok since it's a property
Assert.IsTrue(nav.MoveToFirstChild());
Assert.AreEqual("subitem", nav.Name);
Assert.AreEqual("bam", nav.Value); // ok since we're in a fragment
Assert.IsTrue(nav.MoveToFirstChild());
Assert.AreEqual(XPathNodeType.Text, nav.NodeType);
Assert.AreEqual(string.Empty, nav.Name);
Assert.AreEqual("bam", nav.Value);
Assert.IsFalse(nav.MoveToNext());
Assert.IsTrue(nav.MoveToParent());
Assert.AreEqual("subitem", nav.Name);
Assert.IsFalse(nav.MoveToNext());
Assert.IsTrue(nav.MoveToParent());
Assert.AreEqual("item4", nav.Name);
Assert.IsTrue(nav.MoveToNext());
Assert.AreEqual("item5", nav.Name);
Assert.AreEqual("\n ", nav.Value.Lf());
Assert.IsTrue(nav.MoveToFirstChild());
Assert.AreEqual(XPathNodeType.Text, nav.NodeType);
Assert.AreEqual("\n ", nav.Value.Lf());
}
[Test]
public void Navigate()
{
var source = new TestSource1();
var nav = new NavigableNavigator(source);
nav.MoveToRoot();
Assert.AreEqual("", nav.Name); // because we're at root
nav.MoveToFirstChild();
Assert.AreEqual("root", nav.Name);
nav.MoveToFirstChild();
Assert.AreEqual("type1", nav.Name); // our first content
nav.MoveToFirstAttribute();
Assert.AreEqual("id", nav.Name);
Assert.AreEqual("1", nav.Value);
nav.MoveToNextAttribute();
Assert.AreEqual("prop1", nav.Name);
Assert.AreEqual("1:p1", nav.Value);
nav.MoveToNextAttribute();
Assert.AreEqual("prop2", nav.Name);
Assert.AreEqual("1:p2", nav.Value);
Assert.IsFalse(nav.MoveToNextAttribute());
nav.MoveToParent();
nav.MoveToFirstChild();
Assert.AreEqual("prop3", nav.Name);
Assert.AreEqual("1:p3", nav.Value);
Assert.IsFalse(nav.MoveToNext());
}
[Test]
public void NavigateMixed()
{
var source = new TestSource2();
var nav = new NavigableNavigator(source);
nav.MoveToRoot();
nav.MoveToFirstChild();
Assert.AreEqual("root", nav.Name);
nav.MoveToFirstChild();
Assert.AreEqual("type1", nav.Name); // our content
nav.MoveToFirstChild();
Assert.AreEqual("prop1", nav.Name); // our property
Assert.AreEqual(XPathNodeType.Element, nav.NodeType);
nav.MoveToFirstChild();
// "poo"
Assert.AreEqual(XPathNodeType.Element, nav.NodeType);
Assert.AreEqual("data", nav.Name);
nav.MoveToFirstChild();
Assert.AreEqual(XPathNodeType.Element, nav.NodeType);
Assert.AreEqual("item1", nav.Name);
nav.MoveToNext();
Assert.AreEqual(XPathNodeType.Element, nav.NodeType);
Assert.AreEqual("item2", nav.Name);
nav.MoveToParent();
Assert.AreEqual(XPathNodeType.Element, nav.NodeType);
Assert.AreEqual("data", nav.Name);
nav.MoveToParent();
Assert.AreEqual(XPathNodeType.Element, nav.NodeType);
Assert.AreEqual("prop1", nav.Name);
}
[Test]
public void OuterXmlBasic()
{
const string xml = @"";
var doc = XmlHelper.CreateXPathDocument(xml);
var nnav = doc.CreateNavigator();
Assert.AreEqual(xml, nnav.OuterXml);
var source = new TestSource0();
var nav = new NavigableNavigator(source);
Assert.AreEqual(xml, nav.OuterXml);
}
[Test]
public void OuterXml()
{
var source = new TestSource1();
var nav = new NavigableNavigator(source);
const string xml = @"1:p3";
Assert.AreEqual(xml.Lf(), nav.OuterXml.Lf());
}
[Test]
public void OuterXmlMixed()
{
var source = new TestSource2();
var nav = new NavigableNavigator(source);
nav.MoveToRoot();
const string outerXml = @"poo";
Assert.AreEqual(outerXml.Lf(), nav.OuterXml.Lf());
}
[Test]
public void Query()
{
var source = new TestSource1();
var nav = new NavigableNavigator(source);
var iterator = nav.Select("//type1");
Assert.AreEqual(1, iterator.Count);
iterator.MoveNext();
Assert.AreEqual("type1", iterator.Current.Name);
iterator = nav.Select("//* [@prop1='1:p1']");
Assert.AreEqual(1, iterator.Count);
iterator.MoveNext();
Assert.AreEqual("type1", iterator.Current.Name);
}
[Test]
public void QueryMixed()
{
var source = new TestSource2();
var nav = new NavigableNavigator(source);
var doc = XmlHelper.CreateXPathDocument("poo");
var docNav = doc.CreateNavigator();
var docIter = docNav.Select("//item2 [@xx=33]");
Assert.AreEqual(1, docIter.Count);
Assert.AreEqual("", docIter.Current.Name);
docIter.MoveNext();
Assert.AreEqual("item2", docIter.Current.Name);
var iterator = nav.Select("//item2 [@xx=33]");
Assert.AreEqual(1, iterator.Count);
Assert.AreEqual("", iterator.Current.Name);
iterator.MoveNext();
Assert.AreEqual("item2", iterator.Current.Name);
}
[Test]
public void QueryWithVariables()
{
var source = new TestSource1();
var nav = new NavigableNavigator(source);
var iterator = nav.Select("//* [@prop1=$var]", new XPathVariable("var", "1:p1"));
Assert.AreEqual(1, iterator.Count);
iterator.MoveNext();
Assert.AreEqual("type1", iterator.Current.Name);
}
[Test]
public void QueryMixedWithVariables()
{
var source = new TestSource2();
var nav = new NavigableNavigator(source);
var iterator = nav.Select("//item2 [@xx=$var]", new XPathVariable("var", "33"));
Assert.AreEqual(1, iterator.Count);
iterator.MoveNext();
Assert.AreEqual("item2", iterator.Current.Name);
}
[Test]
public void MixedWithNoValue()
{
var source = new TestSource4();
var nav = new NavigableNavigator(source);
var doc = XmlHelper.CreateXPathDocument(@"dang");
var docNav = doc.CreateNavigator();
docNav.MoveToRoot();
Assert.IsTrue(docNav.MoveToFirstChild());
Assert.AreEqual("root", docNav.Name);
Assert.IsTrue(docNav.MoveToFirstChild());
Assert.AreEqual("type1", docNav.Name);
Assert.IsTrue(docNav.MoveToNext());
Assert.AreEqual("type1", docNav.Name);
Assert.IsTrue(docNav.MoveToNext());
Assert.AreEqual("type1", docNav.Name);
Assert.IsFalse(docNav.MoveToNext());
docNav.MoveToRoot();
var docOuter = docNav.OuterXml;
nav.MoveToRoot();
Assert.IsTrue(nav.MoveToFirstChild());
Assert.AreEqual("root", nav.Name);
Assert.IsTrue(nav.MoveToFirstChild());
Assert.AreEqual("type1", nav.Name);
Assert.IsTrue(nav.MoveToNext());
Assert.AreEqual("type1", nav.Name);
Assert.IsTrue(nav.MoveToNext());
Assert.AreEqual("type1", nav.Name);
Assert.IsFalse(nav.MoveToNext());
nav.MoveToRoot();
var outer = nav.OuterXml;
Assert.AreEqual(docOuter, outer);
}
[Test]
[Ignore("NavigableNavigator does not implement IHasXmlNode.")]
public void XmlNodeList()
{
var source = new TestSource1();
var nav = new NavigableNavigator(source);
var iterator = nav.Select("/*");
// but, that requires that the underlying navigator implements IHasXmlNode
// so it is possible to obtain nodes from the navigator - not possible yet
var nodes = XmlNodeListFactory.CreateNodeList(iterator);
Assert.AreEqual(nodes.Count, 1);
var node = nodes[0];
Assert.AreEqual(3, node.Attributes.Count);
Assert.AreEqual("1", node.Attributes["id"].Value);
Assert.AreEqual("1:p1", node.Attributes["prop1"].Value);
Assert.AreEqual("1:p2", node.Attributes["prop2"].Value);
Assert.AreEqual(1, node.ChildNodes.Count);
Assert.AreEqual("prop3", node.FirstChild.Name);
Assert.AreEqual("1:p3", node.FirstChild.Value);
}
[Test]
public void CloneIsSafe()
{
var source = new TestSource5();
var nav = new NavigableNavigator(source);
TestContent content;
Assert.AreEqual(NavigableNavigator.StatePosition.Root, nav.InternalState.Position);
Assert.IsTrue(nav.MoveToFirstChild());
Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position);
Assert.AreEqual("root", nav.Name); // at -1
Assert.IsTrue(nav.MoveToFirstChild());
Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position);
Assert.AreEqual(1, (nav.UnderlyingObject as TestContent).Id);
Assert.IsTrue(nav.MoveToFirstChild());
Assert.AreEqual(NavigableNavigator.StatePosition.PropertyElement, nav.InternalState.Position);
Assert.AreEqual("prop1", nav.Name);
Assert.IsTrue(nav.MoveToNext());
Assert.AreEqual(NavigableNavigator.StatePosition.PropertyElement, nav.InternalState.Position);
Assert.AreEqual("prop2", nav.Name);
Assert.IsTrue(nav.MoveToNext());
Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position);
Assert.AreEqual(3, (nav.UnderlyingObject as TestContent).Id);
// at that point nav is at /root/1/3
var clone = nav.Clone() as NavigableNavigator;
// move nav to /root/1/5 and ensure that clone stays at /root/1/3
Assert.IsTrue(nav.MoveToNext());
Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position);
Assert.AreEqual(5, (nav.UnderlyingObject as TestContent).Id);
Assert.AreEqual(NavigableNavigator.StatePosition.Element, clone.InternalState.Position);
Assert.AreEqual(3, (clone.UnderlyingObject as TestContent).Id);
// move nav to /root/2
Assert.IsTrue(nav.MoveToParent());
Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position);
Assert.AreEqual(1, (nav.UnderlyingObject as TestContent).Id);
Assert.IsTrue(nav.MoveToNext());
Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position);
Assert.AreEqual(2, (nav.UnderlyingObject as TestContent).Id);
Assert.IsTrue(nav.MoveToFirstChild());
Assert.AreEqual(NavigableNavigator.StatePosition.PropertyElement, nav.InternalState.Position);
Assert.AreEqual("prop1", nav.Name);
Assert.AreEqual("p21", nav.Value);
// move clone to .. /root/1
Assert.IsTrue(clone.MoveToParent());
// clone has not been corrupted by nav
Assert.AreEqual(NavigableNavigator.StatePosition.Element, clone.InternalState.Position);
Assert.AreEqual(1, (clone.UnderlyingObject as TestContent).Id);
}
[TestCase(1)]
[TestCase(2)]
[TestCase(3)]
[TestCase(4)]
[TestCase(5)]
[TestCase(6)]
public void SelectById(int id)
{
var source = new TestSource5();
var nav = new NavigableNavigator(source);
var iter = nav.Select(string.Format("//* [@id={0}]", id));
Assert.IsTrue(iter.MoveNext());
var current = iter.Current as NavigableNavigator;
Assert.AreEqual(NavigableNavigator.StatePosition.Element, current.InternalState.Position);
Assert.AreEqual(id, (current.UnderlyingObject as TestContent).Id);
}
[TestCase(1)]
[TestCase(2)]
[TestCase(3)]
[TestCase(4)]
[TestCase(5)]
[TestCase(6)]
public void SelectByIdWithVariable(int id)
{
var source = new TestSource5();
var nav = new NavigableNavigator(source);
var iter = nav.Select("//* [@id=$id]", new XPathVariable("id", id.ToString(CultureInfo.InvariantCulture)));
Assert.IsTrue(iter.MoveNext());
var current = iter.Current as NavigableNavigator;
Assert.AreEqual(NavigableNavigator.StatePosition.Element, current.InternalState.Position);
Assert.AreEqual(id, (current.UnderlyingObject as TestContent).Id);
}
[Test]
public void MoveToId()
{
var source = new TestSource5();
var nav = new NavigableNavigator(source);
// move to /root/1/3
Assert.IsTrue(nav.MoveToId("3"));
Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position);
Assert.AreEqual(3, (nav.UnderlyingObject as TestContent).Id);
// move to /root/1
Assert.IsTrue(nav.MoveToParent());
Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position);
Assert.AreEqual(1, (nav.UnderlyingObject as TestContent).Id);
// move to /root
Assert.IsTrue(nav.MoveToParent());
Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position);
Assert.AreEqual(-1, (nav.UnderlyingObject as TestContent).Id);
// move up
Assert.IsTrue(nav.MoveToParent());
Assert.AreEqual(NavigableNavigator.StatePosition.Root, nav.InternalState.Position);
Assert.IsFalse(nav.MoveToParent());
// move to /root/1
Assert.IsTrue(nav.MoveToId("1"));
Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position);
Assert.AreEqual(1, (nav.UnderlyingObject as TestContent).Id);
// move to /root
Assert.IsTrue(nav.MoveToParent());
Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position);
Assert.AreEqual(-1, (nav.UnderlyingObject as TestContent).Id);
// move up
Assert.IsTrue(nav.MoveToParent());
Assert.AreEqual(NavigableNavigator.StatePosition.Root, nav.InternalState.Position);
Assert.IsFalse(nav.MoveToParent());
// move to /root
Assert.IsTrue(nav.MoveToId("-1"));
Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position);
Assert.AreEqual(-1, (nav.UnderlyingObject as TestContent).Id);
// move up
Assert.IsTrue(nav.MoveToParent());
Assert.AreEqual(NavigableNavigator.StatePosition.Root, nav.InternalState.Position);
Assert.IsFalse(nav.MoveToParent());
// get lost
Assert.IsFalse(nav.MoveToId("666"));
}
[Test]
public void RootedNavigator()
{
var source = new TestSource5();
var nav = new NavigableNavigator(source, source.Get(1));
// go to (/root) /1
Assert.IsTrue(nav.MoveToFirstChild());
Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position);
Assert.AreEqual(1, (nav.UnderlyingObject as TestContent).Id);
// go to (/root) /1/prop1
Assert.IsTrue(nav.MoveToFirstChild());
// go to (/root) /1/prop2
Assert.IsTrue(nav.MoveToNext());
// go to (/root) /1/3
Assert.IsTrue(nav.MoveToNext());
Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position);
Assert.AreEqual(3, (nav.UnderlyingObject as TestContent).Id);
// go to (/root) /1
Assert.IsTrue(nav.MoveToParent());
Assert.AreEqual(NavigableNavigator.StatePosition.Element, nav.InternalState.Position);
Assert.AreEqual(1, (nav.UnderlyingObject as TestContent).Id);
// go to (/root) ie root
Assert.IsTrue(nav.MoveToParent());
Assert.AreEqual(NavigableNavigator.StatePosition.Root, nav.InternalState.Position);
Assert.IsFalse(nav.MoveToParent());
// can't go there
Assert.IsFalse(nav.MoveToId("2"));
}
[TestCase(true, true)]
[TestCase(true, false)]
[TestCase(false, true)]
[TestCase(false, false)]
public void XsltDebugModeAndSortOrder(bool native, bool debug)
{
const string xml = @"
title-1title-3title-7title-8title-5title-2title-4title-6
";
const string xslt = @"
]>
! !! !!!
";
const string expected = @"! title-1
!! title-3
!!! title-7
!!! title-8
!! title-5
! title-2
!! title-4
!! title-6
";
// see http://www.onenaught.com/posts/352/xslt-performance-tip-dont-indent-output
// why aren't we using an XmlWriter here?
var transform = new XslCompiledTransform(debug);
var xmlReader = new XmlTextReader(new StringReader(xslt))
{
EntityHandling = EntityHandling.ExpandEntities
};
var xslResolver = new XmlUrlResolver
{
Credentials = CredentialCache.DefaultCredentials
};
var args = new XsltArgumentList();
// .Default is more restrictive than .TrustedXslt
transform.Load(xmlReader, XsltSettings.Default, xslResolver);
XPathNavigator macro;
if (!native)
{
var source = new TestSource7();
var nav = new NavigableNavigator(source);
//args.AddParam("currentPage", string.Empty, nav.Clone());
var x = new XmlDocument();
x.LoadXml(xml);
macro = new MacroNavigator(new[]
{
// it even fails like that => macro nav. issue?
new MacroNavigator.MacroParameter("nav", x.CreateNavigator()) // nav.Clone())
}
);
}
else
{
var doc = new XmlDocument();
doc.LoadXml("");
var nav = doc.CreateElement("nav");
doc.DocumentElement.AppendChild(nav);
var x = new XmlDocument();
x.LoadXml(xml);
nav.AppendChild(doc.ImportNode(x.DocumentElement, true));
macro = doc.CreateNavigator();
}
var writer = new StringWriter();
transform.Transform(macro, args, writer);
// this was working with native, debug and non-debug
// this was working with macro nav, non-debug
// but was NOT working (changing the order of nodes) with macro nav, debug
// was due to an issue with macro nav IsSamePosition, fixed
//Console.WriteLine("--------");
//Console.WriteLine(writer.ToString());
Assert.AreEqual(expected.Lf(), writer.ToString().Lf());
}
[Test]
public void WhiteSpacesAndEmptyValues()
{
// "When Microsoft’s DOM builder receives a text node from the parser
// that contains only white space, it is thrown away." - so if it's ONLY
// spaces, it's nothing, but spaces are NOT trimmed.
// For attributes, spaces are preserved even when there's only spaces.
var doc = XmlHelper.CreateXPathDocument(@" ooo ooo
");
var docNav = doc.CreateNavigator();
Assert.AreEqual(@" ooo ooo
".Lf(), docNav.OuterXml.Lf());
docNav.MoveToRoot();
Assert.IsTrue(docNav.MoveToFirstChild());
Assert.IsTrue(docNav.MoveToFirstChild());
Assert.IsTrue(docNav.MoveToFirstChild()); // prop
Assert.IsTrue(docNav.IsEmptyElement);
Assert.IsTrue(docNav.MoveToParent());
Assert.IsTrue(docNav.MoveToNext());
Assert.IsTrue(docNav.MoveToFirstChild()); // prop
Assert.IsFalse(docNav.IsEmptyElement);
Assert.AreEqual("", docNav.Value); // contains an empty text node
Assert.IsTrue(docNav.MoveToParent());
Assert.IsTrue(docNav.MoveToNext());
Assert.IsTrue(docNav.MoveToFirstChild()); // prop
Assert.IsFalse(docNav.IsEmptyElement);
Assert.AreEqual("", docNav.Value); // contains an empty text node
var source = new TestSource8();
var nav = new NavigableNavigator(source);
// shows how whitespaces are handled by NavigableNavigator
Assert.AreEqual(@" ooo ".Lf(), nav.OuterXml.Lf());
}
}
#region Navigable implementation
class TestPropertyType : INavigableFieldType
{
public TestPropertyType(string name, bool isXmlContent = false, Func