From 1443bc1664b4637f246bbc1f5ff1f9125b9eddc4 Mon Sep 17 00:00:00 2001 From: Nico Verwer Date: Fri, 3 Jun 2016 11:23:15 +0200 Subject: [PATCH 1/2] Implemented schematron . Moved message formatting into FormatterBase class. Added error message for abstract rules that are not found. --- src/Schematron/Formatters/FormatterBase.cs | 70 +++++++++++ src/Schematron/Formatters/FormattingUtils.cs | 2 +- src/Schematron/Formatters/LogFormatter.cs | 47 +------- src/Schematron/Formatters/SimpleFormatter.cs | 44 +------ src/Schematron/Formatters/XmlFormatter.cs | 119 +++++++------------ src/Schematron/SchemaLoader.cs | 8 +- src/Schematron/TagExpressions.cs | 8 +- src/Schematron/Test.cs | 112 ++++++++++------- 8 files changed, 194 insertions(+), 216 deletions(-) diff --git a/src/Schematron/Formatters/FormatterBase.cs b/src/Schematron/Formatters/FormatterBase.cs index b102228..b252d8e 100644 --- a/src/Schematron/Formatters/FormatterBase.cs +++ b/src/Schematron/Formatters/FormatterBase.cs @@ -85,5 +85,75 @@ public virtual void Format(SchemaCollection schemas, StringBuilder output) public virtual void Format(StringBuilder output) { } + + protected static StringBuilder FormatMessage(Test source, XPathNavigator context, string msg) + { + StringBuilder sb = new StringBuilder(); + XPathExpression nameExpr; + XPathExpression selectExpr; + + // As we move on, we have to append starting from the last point, + // skipping the and expressions: Substring(offset, name.Index - offset). + int offset = 0; + + for (int i = 0; i < source.NameValueOfExpressions.Count; i++) + { + System.Text.RegularExpressions.Match name = source.NameValueOfExpressions[i]; + nameExpr = source.NamePaths[i]; + selectExpr = source.ValueOfSelects[i]; + + // Append the text without the expression. + sb.Append(msg.Substring(offset, name.Index - offset)); + + // Does the name element have a path attribute? + if (nameExpr != null) + { + nameExpr.SetContext(source.GetContext()); + + string result = null; + if (nameExpr.ReturnType == XPathResultType.NodeSet) + { + // It the result of the expression is a nodeset, we only get the element + // name of the first node, which is compatible with XSLT implementation. + XPathNodeIterator nodes = (XPathNodeIterator)context.Evaluate(nameExpr); + if (nodes.MoveNext()) + result = nodes.Current.Name; + } + else + result = context.Evaluate(nameExpr) as string; + + if (result != null) + sb.Append(result); + } + // Does the value-of element have a select attribute? + else if (selectExpr != null) + { + selectExpr.SetContext(source.GetContext()); + + string result = null; + if (selectExpr.ReturnType == XPathResultType.NodeSet) + { + XPathNodeIterator nodes = (XPathNodeIterator)context.Evaluate(selectExpr); + result = String.Empty; + while (nodes.MoveNext()) + result += nodes.Current.Value; + } + else + result = context.Evaluate(selectExpr) as string; + + if (result != null) + sb.Append(result); + } + // If there is no path or select expression, there is an empty element. + else + sb.Append(context.Name); + + offset = name.Index + name.Length; + } + + sb.Append(msg.Substring(offset)); + return sb; + } + } } \ No newline at end of file diff --git a/src/Schematron/Formatters/FormattingUtils.cs b/src/Schematron/Formatters/FormattingUtils.cs index fa2f271..541f331 100644 --- a/src/Schematron/Formatters/FormattingUtils.cs +++ b/src/Schematron/Formatters/FormattingUtils.cs @@ -201,7 +201,7 @@ public static string GetNamespaceSummary(XPathNavigator context, Hashtable names /// public static string NormalizeString(string input) { - // Account for enconded strings, such as < (<) and > (>). + // Account for encoded strings, such as < (<) and > (>). return System.Web.HttpUtility.HtmlDecode( _normalize.Replace(input, " ").Trim()); } diff --git a/src/Schematron/Formatters/LogFormatter.cs b/src/Schematron/Formatters/LogFormatter.cs index 763cf95..e442bbc 100644 --- a/src/Schematron/Formatters/LogFormatter.cs +++ b/src/Schematron/Formatters/LogFormatter.cs @@ -23,51 +23,8 @@ public LogFormatter() /// public override void Format(Test source, XPathNavigator context, StringBuilder output) { - string msg = source.Message; - StringBuilder sb = new StringBuilder(); - XPathExpression expr; - - // As we move on, we have to append starting from the last point, - // skipping the expression itself: Substring(offset, name.Index - offset). - int offset = 0; - - for (int i = 0; i < source.NameExpressions.Count; i++) - { - Match name = source.NameExpressions[i]; - expr = source.NamePaths[i]; - - // Append the text without the expression. - sb.Append(msg.Substring(offset, name.Index - offset)); - - // Does the name element have a path attribute? - if (expr != null) - { - expr.SetContext(source.GetContext()); - - string result = null; - if (expr.ReturnType == XPathResultType.NodeSet) - { - // It the result of the expression is a nodeset, we only get the element - // name of the first node, which is compatible with XSLT implementation. - XPathNodeIterator nodes = (XPathNodeIterator)context.Evaluate(expr); - if (nodes.MoveNext()) - result = nodes.Current.Name; - } - else - result = context.Evaluate(expr) as string; - - if (result != null) - sb.Append(result); - } - else - sb.Append(context.Name); - - offset = name.Index + name.Length; - } - - sb.Append(msg.Substring(offset)); - - // Finally remove any non-name schematron tag in the message. + StringBuilder sb = FormatMessage(source, context, source.Message); + // Finally remove any non-name schematron tag in the message. string res = TagExpressions.AllSchematron.Replace(sb.ToString(), String.Empty); sb = new StringBuilder(); if (source is Assert) diff --git a/src/Schematron/Formatters/SimpleFormatter.cs b/src/Schematron/Formatters/SimpleFormatter.cs index 8be3bff..9cd63c4 100644 --- a/src/Schematron/Formatters/SimpleFormatter.cs +++ b/src/Schematron/Formatters/SimpleFormatter.cs @@ -27,49 +27,7 @@ public SimpleFormatter() /// public override void Format(Test source, XPathNavigator context, StringBuilder output) { - string msg = source.Message; - StringBuilder sb = new StringBuilder(); - XPathExpression expr; - - // As we move on, we have to append starting from the last point, - // skipping the expression itself: Substring(offset, name.Index - offset). - int offset = 0; - - for (int i = 0; i < source.NameExpressions.Count; i++) - { - Match name = source.NameExpressions[i]; - expr = source.NamePaths[i]; - - // Append the text without the expression. - sb.Append(msg.Substring(offset, name.Index - offset)); - - // Does the name element have a path attribute? - if (expr != null) - { - expr.SetContext(source.GetContext()); - - string result = null; - if (expr.ReturnType == XPathResultType.NodeSet) - { - // It the result of the expression is a nodeset, we only get the element - // name of the first node, which is compatible with XSLT implementation. - XPathNodeIterator nodes = (XPathNodeIterator)context.Evaluate(expr); - if (nodes.MoveNext()) - result = nodes.Current.Name; - } - else - result = context.Evaluate(expr) as string; - - if (result != null) - sb.Append(result); - } - else - sb.Append(context.Name); - - offset = name.Index + name.Length; - } - - sb.Append(msg.Substring(offset)); + StringBuilder sb = FormatMessage(source, context, source.Message); if (source is Assert) sb.Insert(0, "\tAssert fails: "); diff --git a/src/Schematron/Formatters/XmlFormatter.cs b/src/Schematron/Formatters/XmlFormatter.cs index fc2aba3..577e806 100644 --- a/src/Schematron/Formatters/XmlFormatter.cs +++ b/src/Schematron/Formatters/XmlFormatter.cs @@ -28,91 +28,52 @@ public XmlFormatter() /// Look at documentation. /// public override void Format(Test source, XPathNavigator context, StringBuilder output) - { - string msg = source.Message; - XmlTextWriter writer = new XmlTextWriter(new StringWriter(output)); - //Temporary disable namespace support. - writer.Namespaces = false; - StringBuilder sb = new StringBuilder(); - XPathExpression expr; - - // Start element declaration. - writer.WriteStartElement("message"); - - // As we move on, we have to append starting from the last point, - // skipping the expression itself: Substring(offset, name.Index - offset). - int offset = 0; + { + string msg = source.Message; + XmlTextWriter writer = new XmlTextWriter(new StringWriter(output)); + //Temporary disable namespace support. + writer.Namespaces = false; - for (int i = 0; i < source.NameExpressions.Count; i++) - { - Match name = source.NameExpressions[i]; - expr = source.NamePaths[i]; - - // Append the text without the expression. - sb.Append(msg.Substring(offset, name.Index - offset)); + // Start element declaration. + writer.WriteStartElement("message"); - // Does the name element have a path attribute? - if (expr != null) - { - expr.SetContext(source.GetContext()); + msg = FormatMessage(source, context, msg).ToString(); - string result = null; - if (expr.ReturnType == XPathResultType.NodeSet) - { - // It the result of the expression is a nodeset, we only get the element - // name of the first node, which is compatible with XSLT implementation. - XPathNodeIterator nodes = (XPathNodeIterator)context.Evaluate(expr); - if (nodes.MoveNext()) - result = nodes.Current.Name; - } - else - result = context.Evaluate(expr) as string; + // Finally remove any non-name schematron tag in the message. + string res = TagExpressions.AllSchematron.Replace(msg, String.Empty); - if (result != null) - sb.Append(result); - } - else - sb.Append(context.Name); - - offset = name.Index + name.Length; - } - - sb.Append(msg.Substring(offset)); - // Finally remove any non-name schematron tag in the message. - string res = TagExpressions.AllSchematron.Replace(sb.ToString(), String.Empty); - - //Accumulate namespaces found during traversal of node for its position. - Hashtable ns = new Hashtable(); + //Accumulate namespaces found during traversal of node for its position. + Hashtable ns = new Hashtable(); // Write element. - writer.WriteElementString("text", res); - // Write element. - writer.WriteElementString("path", FormattingUtils.GetFullNodePosition(context.Clone(), String.Empty, source, ns)); - // Write element. - //writer.WriteElementString("summary", FormattingUtils.GetNodeSummary(context, ns, String.Empty)); - writer.WriteStartElement("summary"); - writer.WriteRaw(FormattingUtils.GetNodeSummary(context, ns, String.Empty)); - writer.WriteEndElement(); - - // Write element. - if (context is IXmlLineInfo) - { - writer.WriteStartElement("position"); - IXmlLineInfo info = (IXmlLineInfo) context; - writer.WriteAttributeString("line", info.LineNumber.ToString()); - writer.WriteAttributeString("column", info.LinePosition.ToString()); - writer.WriteEndElement(); - } - - // Close element. - writer.WriteEndElement(); - writer.Flush(); - } - - /// - /// Look at documentation. - /// - public override void Format(Rule source, XPathNavigator context, StringBuilder output) + writer.WriteElementString("text", res); + // Write element. + writer.WriteElementString("path", FormattingUtils.GetFullNodePosition(context.Clone(), String.Empty, source, ns)); + // Write element. + //writer.WriteElementString("summary", FormattingUtils.GetNodeSummary(context, ns, String.Empty)); + writer.WriteStartElement("summary"); + writer.WriteRaw(FormattingUtils.GetNodeSummary(context, ns, String.Empty)); + writer.WriteEndElement(); + + // Write element. + if (context is IXmlLineInfo) + { + writer.WriteStartElement("position"); + IXmlLineInfo info = (IXmlLineInfo)context; + writer.WriteAttributeString("line", info.LineNumber.ToString()); + writer.WriteAttributeString("column", info.LinePosition.ToString()); + writer.WriteEndElement(); + } + + // Close element. + writer.WriteEndElement(); + writer.Flush(); + } + + /// + /// Look at documentation. + /// + public override void Format(Rule source, XPathNavigator context, StringBuilder output) { string res = " - /// The compiled regular expression to replace the special name tag inside a message. + /// The compiled regular expression to replace the special name and value tags inside a message. /// /// - /// Replaces each instance of name tags with the value un the current context element. + /// Replaces each instance of name and valuetags with the value in the current context element. /// - public static Regex Name; + public static Regex NameValueOf; public static Regex Emph; public static Regex Dir; @@ -25,7 +25,7 @@ internal class TagExpressions static TagExpressions() { // The element declarations can contain the namespace if expanded in a loaded document. - Name = new Regex(@"<[^\s>]*\bname\b[^>]*/>", RegexOptions.Compiled); + NameValueOf = new Regex(@"<[^\s>]*\b(name|value-of)\b[^>]*/>", RegexOptions.Compiled); Emph = new Regex(@"<[^\s>]*\bemph\b[^>]*>", RegexOptions.Compiled); Dir = new Regex(@"<[^\s]*\bdir\b[^>]*>", RegexOptions.Compiled); Span = new Regex(@"<[^\s]*\bspan\b[^>]*>", RegexOptions.Compiled); diff --git a/src/Schematron/Test.cs b/src/Schematron/Test.cs index ad9b4ff..36c38e4 100644 --- a/src/Schematron/Test.cs +++ b/src/Schematron/Test.cs @@ -15,61 +15,83 @@ public abstract class Test : EvaluableExpression protected string _msg; /// - protected MatchCollection _names; + protected MatchCollection _name_valueofs; /// protected XPathExpression[] _paths; - /// - /// - /// - public Test(string test, string message) : base(test) - { - if (ReturnType != XPathResultType.Boolean && - ReturnType != XPathResultType.NodeSet) - throw new InvalidExpressionException("Test expression doesn't evaluate to a boolean or nodeset result: " + test); - - Message = Formatters.FormattingUtils.NormalizeString(message); - - // Save tags in the message and their paths in their compiled form. + /// + protected XPathExpression[] _selects; + + /// + /// + /// + public Test(string test, string message) : base(test) + { + if (ReturnType != XPathResultType.Boolean && + ReturnType != XPathResultType.NodeSet) + throw new InvalidExpressionException("Test expression doesn't evaluate to a boolean or nodeset result: " + test); + + Message = Formatters.FormattingUtils.NormalizeString(message); + + // Save and tags in the message and their paths / selects in their compiled form. // TODO: see if we can work with the XML in the message, instead of using RE. - _names = TagExpressions.Name.Matches(Message); - int nc = _names.Count; - _paths = new XPathExpression[nc]; + // TODO: Check the correct usahe of path and select attributes. + _name_valueofs = TagExpressions.NameValueOf.Matches(Message); + int nc = _name_valueofs.Count; + _paths = new XPathExpression[nc]; + _selects = new XPathExpression[nc]; - for (int i = 0; i < nc; i++) - { - // Locate the path attribute and compile it with the DefaultNavigator. - Match name = _names[i]; - int start = name.Value.IndexOf("path="); - // Does it have a path attribute? - if (start > 0) - { - // Skip the path=" string. - start += 6; - // If the namespace element is present, end the expression there. - int end = name.Value.LastIndexOf("xmlns") - 2; - if (end < 0) - end = name.Value.LastIndexOf('"'); - string xpath = name.Value.Substring(start, end - start); - _paths[i] = Config.DefaultNavigator.Compile(xpath); - } - else - _paths[i] = null; - } - } + for (int i = 0; i < nc; i++) + { + // Locate the path attribute and compile it with the DefaultNavigator. + Match name_valueof = _name_valueofs[i]; + int start = name_valueof.Value.IndexOf("path="); + // Does it have a path attribute? + if (start > 0) + { + // Skip the path=" string. + start += 6; + // If the namespace element is present, end the expression there. + int end = name_valueof.Value.LastIndexOf("xmlns") - 2; + if (end < 0) + end = name_valueof.Value.LastIndexOf('"'); + string xpath = name_valueof.Value.Substring(start, end - start); + _paths[i] = Config.DefaultNavigator.Compile(xpath); + _selects[i] = null; - /// - public string Message + } + else if ((start = name_valueof.Value.IndexOf("select=")) > 0) + { + // Skip the select=" string. + start += 8; + // If the namespace element is present, end the expression there. + int end = name_valueof.Value.LastIndexOf("xmlns") - 2; + if (end < 0) + end = name_valueof.Value.LastIndexOf('"'); + string xpath = name_valueof.Value.Substring(start, end - start); + _selects[i] = Config.DefaultNavigator.Compile(xpath); + _paths[i] = null; + } + else + { + _paths[i] = null; + _selects[i] = null; + } + } + } + + /// + public string Message { get { return _msg; } set { _msg = value; } } /// - public MatchCollection NameExpressions + public MatchCollection NameValueOfExpressions { - get { return _names; } + get { return _name_valueofs; } } /// @@ -77,5 +99,11 @@ public XPathExpression[] NamePaths { get { return _paths; } } - } + + /// + public XPathExpression[] ValueOfSelects + { + get { return _selects; } + } + } } From 0fc02c235783035c439e471d0d7f7f3b91dca56a Mon Sep 17 00:00:00 2001 From: Nico Verwer Date: Sun, 5 Jun 2016 20:41:15 +0200 Subject: [PATCH 2/2] Unit tests for and added. --- src/Schematron.Tests/Content/po-schema.xsd | 2 +- src/Schematron.Tests/Schematron.Tests.csproj | 3 + src/Schematron.Tests/ValidatorTests.cs | 262 ++++++++++--------- 3 files changed, 149 insertions(+), 118 deletions(-) diff --git a/src/Schematron.Tests/Content/po-schema.xsd b/src/Schematron.Tests/Content/po-schema.xsd index a7bfb9c..7b8fbcb 100644 --- a/src/Schematron.Tests/Content/po-schema.xsd +++ b/src/Schematron.Tests/Content/po-schema.xsd @@ -9,7 +9,7 @@ - Attributes sex and title must have compatible values on element . + Attributes sex () and title () must have compatible values on element . Both customerId and partnerId can't be present on element . diff --git a/src/Schematron.Tests/Schematron.Tests.csproj b/src/Schematron.Tests/Schematron.Tests.csproj index 89be735..e264af2 100644 --- a/src/Schematron.Tests/Schematron.Tests.csproj +++ b/src/Schematron.Tests/Schematron.Tests.csproj @@ -86,6 +86,9 @@ Schematron + + + diff --git a/src/Schematron.Tests/ValidatorTests.cs b/src/Schematron.Tests/ValidatorTests.cs index 8a086cb..dcb4375 100644 --- a/src/Schematron.Tests/ValidatorTests.cs +++ b/src/Schematron.Tests/ValidatorTests.cs @@ -8,121 +8,149 @@ using Xunit; namespace Schematron.Tests -{ - public class ValidatorTests - { - const string XsdLocation = "./Content/po-schema.xsd"; - const string XsdWithPartialSchemaLocation = "./Content/po-schema-with-schema-import.xsd"; - const string XmlContentLocation = "./Content/po-instance.xml"; - const string TargetNamespace = "http://example.com/po-schematron"; - - [Fact] - public void NewAddSchemaSignatureShouldNotBreakCode() - { - var validatorA = new Validator(OutputFormatting.XML); - validatorA.AddSchema(XmlReader.Create(XsdLocation)); - - var validatorB = new Validator(OutputFormatting.XML); - validatorB.AddSchema(TargetNamespace, XsdLocation); - - var resultA = default(string); - var resultB = default(string); - - try - { - var result = validatorA.Validate(XmlReader.Create(XmlContentLocation)); - } - catch (ValidationException ex) - { - resultA = ex.Message; - - System.Diagnostics.Debug.WriteLine(ex.Message); - } - - try - { - var result = validatorB.Validate(XmlReader.Create(XmlContentLocation)); - } - catch (ValidationException ex) - { - resultB = ex.Message; - - Xunit.Assert.True(resultA == resultB); - - System.Diagnostics.Debug.WriteLine(ex.Message); - } - - } - - [Fact] - public void ValidateShouldReturnSchematronValidationResultWhenSchematronConstraintsAreNotMet() - { - //Arrange - var validator = new Validator(OutputFormatting.XML); - - //Act - validator.AddSchema(TargetNamespace, XsdLocation); - - using (var doc = XmlReader.Create(XmlContentLocation)) - { - var result = default(IXPathNavigable); - - try - { - result = validator.Validate(doc); - } - catch (ValidationException ex) - { - System.Diagnostics.Debug.WriteLine(ex.Message); - - var serializer = new XmlSerializer(typeof(Schematron.Serialization.SchematronValidationResultTempObjectModel.output)); - - using (var stream = new MemoryStream(System.Text.Encoding.Unicode.GetBytes(ex.Message))) - using (var reader = XmlReader.Create(stream)) - { - var obj = (Schematron.Serialization.SchematronValidationResultTempObjectModel.output)serializer.Deserialize(reader); - - // Assert - - - Xunit.Assert.NotNull(obj); - Xunit.Assert.NotNull(obj.xml); - Xunit.Assert.NotNull(obj.schematron); - } - } - } - } - - [Fact] - public void WhenUsingTheXmlReaderApproach_ToSupplyASchema_TypesFromImportsAreNotResolved() - { - // arrange - var validator = new Schematron.Validator(); - - // act, (assert) - Xunit.Assert.Throws(() => validator.AddSchema(XmlReader.Create(XsdWithPartialSchemaLocation))); - } - - [Fact] - public void WhenUsingTheXmlSchemaSetBasedApproach_ToSupplyASchema_TypesFromImportsAreResolved() - { - // arrange - var validator = new Schematron.Validator(); - - var count = validator.XmlSchemas != null ? validator.XmlSchemas.Count : 0; - - // act, (assert) - validator.AddSchema(TargetNamespace, XsdWithPartialSchemaLocation); - - Xunit.Assert.True(validator.Schemas.Count == count + 1); - - //var res = validator.Validate(XmlContentLocation); - } - - //[Fact] - public void DoTheRawXmlValidation() - { - throw new NotImplementedException(); - } - } +{ + public class ValidatorTests + { + const string XsdLocation = "./Content/po-schema.xsd"; + const string XsdWithPartialSchemaLocation = "./Content/po-schema-with-schema-import.xsd"; + const string XmlContentLocation = "./Content/po-instance.xml"; + const string TargetNamespace = "http://example.com/po-schematron"; + + [Fact] + public void NewAddSchemaSignatureShouldNotBreakCode() + { + var validatorA = new Validator(OutputFormatting.XML); + validatorA.AddSchema(XmlReader.Create(XsdLocation)); + + var validatorB = new Validator(OutputFormatting.XML); + validatorB.AddSchema(TargetNamespace, XsdLocation); + + var resultA = default(string); + var resultB = default(string); + + try + { + var result = validatorA.Validate(XmlReader.Create(XmlContentLocation)); + } + catch (ValidationException ex) + { + resultA = ex.Message; + + System.Diagnostics.Debug.WriteLine(ex.Message); + } + + try + { + var result = validatorB.Validate(XmlReader.Create(XmlContentLocation)); + } + catch (ValidationException ex) + { + resultB = ex.Message; + + Xunit.Assert.True(resultA == resultB); + + System.Diagnostics.Debug.WriteLine(ex.Message); + } + + } + + [Fact] + public void ValidateShouldReturnSchematronValidationResultWhenSchematronConstraintsAreNotMet() + { + //Arrange + var validator = new Validator(OutputFormatting.XML); + + //Act + validator.AddSchema(TargetNamespace, XsdLocation); + + using (var doc = XmlReader.Create(XmlContentLocation)) + { + var result = default(IXPathNavigable); + + try + { + result = validator.Validate(doc); + } + catch (ValidationException ex) + { + System.Diagnostics.Debug.WriteLine(ex.Message); + + var serializer = new XmlSerializer(typeof(Schematron.Serialization.SchematronValidationResultTempObjectModel.output)); + + using (var stream = new MemoryStream(System.Text.Encoding.Unicode.GetBytes(ex.Message))) + using (var reader = XmlReader.Create(stream)) + { + var obj = (Schematron.Serialization.SchematronValidationResultTempObjectModel.output)serializer.Deserialize(reader); + + // Assert + + + Xunit.Assert.NotNull(obj); + Xunit.Assert.NotNull(obj.xml); + Xunit.Assert.NotNull(obj.schematron); + } + } + } + } + + [Fact] + public void WhenUsingTheXmlReaderApproach_ToSupplyASchema_TypesFromImportsAreNotResolved() + { + // arrange + var validator = new Schematron.Validator(); + + // act, (assert) + Xunit.Assert.Throws(() => validator.AddSchema(XmlReader.Create(XsdWithPartialSchemaLocation))); + } + + [Fact] + public void WhenUsingTheXmlSchemaSetBasedApproach_ToSupplyASchema_TypesFromImportsAreResolved() + { + // arrange + var validator = new Schematron.Validator(); + + var count = validator.XmlSchemas != null ? validator.XmlSchemas.Count : 0; + + // act, (assert) + validator.AddSchema(TargetNamespace, XsdWithPartialSchemaLocation); + + Xunit.Assert.True(validator.Schemas.Count == count + 1); + + //var res = validator.Validate(XmlContentLocation); + } + + //[Fact] + public void DoTheRawXmlValidation() + { + throw new NotImplementedException(); + } + + [Fact] + public void SchematronValidationResultIncludesExpandedValueElements() + { + //Arrange + var validator = new Validator(OutputFormatting.XML); + + //Act + validator.AddSchema(TargetNamespace, XsdLocation); + + using (var doc = XmlReader.Create(XmlContentLocation)) + { + var result = default(IXPathNavigable); + + try + { + result = validator.Validate(doc); + } + catch (ValidationException ex) + { + System.Diagnostics.Debug.WriteLine(ex.Message); + string expectedMessage = "Attributes sex (Female) and title (Mr) must have compatible values on element customer."; + Xunit.Assert.True(ex.Message.Contains(expectedMessage)); + } + Xunit.Assert.Null(result); + } + } + + } }