XSpec Validation

Are you missing a feature? Request its implementation here.
scottbdr
Posts: 58
Joined: Tue Jul 21, 2009 1:48 am

XSpec Validation

Post by scottbdr »

Hi. I'm about to leave my job where I use Schematron/XSpec and wanted to pass something on that I think has real value: a schematron (SQF) that identifies problems in your XSpec tests and also provides a "coverage report" that shows whether you have unit tests (expect, expect-not) for all of the rules in your Schematron. The problems it identifies are small, but I can tell you from hard experience, especially when you are just learning XSpec, are easy to make and can really drive you crazy when your tests start failing:
- using the wrong expect for the rule you have (i.e. using expect-report when you should be using expect-assert)
- using @id references that don't appear in the Schematron

You might consider rolling at least the two above errors into Oxygen's XSpec validation - they are simple and will save a lot of hair pulling experiences. :D Otherwise, it's there for anyone who needs it! BTW, thanks for the great support I've received on this forum over the years!!!

Here it is:

Code: Select all

<?xml version="1.0" encoding="UTF-8"?>
<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron" queryBinding="xslt2"
    xmlns:sqf="http://www.schematron-quickfix.com/validator/process" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:x="http://www.jenitennison.com/xslt/xspec">
    
    <sch:ns uri="http://purl.oclc.org/dsdl/schematron" prefix="sch"/>
    <sch:ns uri="http://www.jenitennison.com/xslt/xspec" prefix="x"/>
    <sch:ns uri="http://www.w3.org/1999/XSL/Transform" prefix="xsl"/>
    
    <sch:p>
        This Schematron Quick Fix validates business rules and assists in authoring of XSpec test modules.
              
        Apply the validation scenario to validate any XSpec.       
    </sch:p>
    
    <sch:let name="sch-file-basename" value="substring-before(tokenize(/x:description/@schematron,'/')[last()],'.sch')" />
    <sch:let name="default-label" value="if (boolean($sch-file-basename/normalize-space()))then $sch-file-basename else 'default label (fix me!)'"></sch:let>
    <sch:let name="schematronPath" value="resolve-uri(/x:description/@schematron, base-uri(.))" />
    <sch:let name="schematronXML" value="doc($schematronPath)" />
    <sch:let name="sch-rule-ids" value="$schematronXML//(sch:report|sch:assert)/@id" />
    
    <sch:pattern id="sqf-xpec-description">
        <sch:rule context="x:description">
            <sch:assert test="@run-as = 'external'" sqf:fix="add-run-as" id="xspec-00" role="warn">
                <sch:name/> SHOULD have @run-as='external' set unless your are SURE it should not be. This may break your test!</sch:assert>

            <sqf:fix id="add-run-as">
                <sqf:description>
                    <sqf:title>Add/fix x:description/@run-as to be 'external'</sqf:title>
                </sqf:description>
                <sqf:add node-type="attribute" target="run-as" select="'external'"/>
            </sqf:fix>
            
            <sch:assert test="doc-available($schematronPath)" id="xspec-01" role="error">
                <sch:name/> @schematron MUST point to a Schematron file. Can't find <sch:value-of select="$schematronPath"/>
            </sch:assert>
                    
            <sch:let name="expect-rule-ids" value="//node()[name(.) = ('x:expect-assert','x:expect-report')]/@id" />
            <sch:let name="not-expect-rule-ids" value="//node()[name(.) = ('x:expect-not-assert','x:expect-not-report')]/@id" />
            <sch:let name="expect-not-found" value="$sch-rule-ids[not(. = $expect-rule-ids)]" />
            <sch:let name="not-expect-not-found" value="$sch-rule-ids[not(. = $not-expect-rule-ids)]" />
            <sch:assert test="count(($expect-not-found,$not-expect-not-found)) = 0" id="xspec-10" role="info">
                COVERAGE REPORT:
                >> MISSING x:expect: <sch:value-of select="if ($expect-not-found) then string-join($expect-not-found,', ') else 'None'"/>
                >> MISSING x:expect-not: <sch:value-of select="if ($not-expect-not-found) then string-join($not-expect-not-found,', ') else 'None'"/>
            </sch:assert>
        </sch:rule>
        <sch:rule context="x:scenario">
            <sch:assert test="boolean(@label/normalize-space())" id="xspec-02" sqf:fix="add-label" role="warn">
                <sch:name /> @label should be non-null</sch:assert>
            <sqf:fix id="add-label">
                <sqf:description>
                    <sqf:title>Add @label attribute</sqf:title>
                </sqf:description>
                <sqf:user-entry name="label-input" default="'my scenario label'">
                    <sqf:description>
                        <sqf:title>Input the desired label text:</sqf:title>
                    </sqf:description>
                </sqf:user-entry>
                <sqf:add node-type="attribute" target="label" select="$label-input"/>
            </sqf:fix>
        </sch:rule>
    </sch:pattern>
    <sch:pattern id="sqf-rule-ids">
        <sch:rule context="x:expect-assert|x:expect-not-assert|x:expect-report|x:expect-not-report">
            <sch:let name="id-ref" value="normalize-space(@id)" />
            <sch:let name="rule-type" value="if(contains(name(),'assert')) then 'assert' else 'report'" />
            <sch:let name="target-rule" value="$schematronXML//node()[@id=$id-ref]" />
            <sch:let name="target-rule-type" value="if(contains($target-rule/name(.),'assert')) then 'assert' else 'report'" />
            <sch:let name="updated-el-name" value="replace(name(.),$rule-type, $target-rule-type)" />
                      
            <sch:assert test="$target-rule" sqf:fix="add-idref" id="xspec-03"><sch:name /> @id value '<sch:value-of select="@id"/>' 
                does not appear to be a valid id from the Schematron being tested. One of <sch:value-of select="string-join($sch-rule-ids,', ')"/>
            </sch:assert>
            
            <sqf:fix id="add-idref" use-for-each="$sch-rule-ids">
                <sch:let name="new-id" value="$sqf:current"/>
                <sqf:description>
                    <sqf:title>Set @id to <sch:value-of select="$sqf:current"/></sqf:title>
                </sqf:description>
                <sqf:replace node-type="element" target="{name(.)}">
                    <sqf:copy-of select="@* except @id"/>
                    <xsl:attribute name="id" select="$sqf:current" />
                </sqf:replace>
            </sqf:fix>
            
            <sch:assert test="$target-rule-type = $rule-type" role="error" sqf:fix="fix-rule-type">
                <sch:name/> does not match the type of rule identified by @id <sch:value-of select="@id"/>: <sch:value-of select="$target-rule/name(.)"/>
            </sch:assert>
            
            <sqf:fix id="fix-rule-type">
                <sqf:description>
                    <sqf:title>Change expect statement to <sch:value-of select="replace(name(),$rule-type, $target-rule-type)"/></sqf:title>
                </sqf:description>
                <sqf:replace target="{$updated-el-name}" node-type="element">
                    <sqf:copy-of select="@*"/>
                </sqf:replace>                
            </sqf:fix>
        </sch:rule>
    </sch:pattern>
</sch:schema>
tavy
Posts: 398
Joined: Thu Jul 01, 2004 12:29 pm

Re: XSpec Validation

Post by tavy »

Hi Scott,

Thank you for sharing your experience and for providing these Schematron rules.
I added an issue on our issue tracker to integrate the Schematron rules in the Oxygen XSpec framework.

Best Regards,
Octavian
Octavian Nadolu
<oXygen/> XML Editor
http://www.oxygenxml.com
Post Reply