Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 69 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
69
Dung lượng
137,59 KB
Nội dung
node: /employee[1]/employee[3]/employee[2]/employee[1] stack: /1/2/2/2 (match="employee[employee]") node: /employee[1]/employee[3]/employee[2]/employee[1]/employee[1] stack: /1/2/2/2/3 (match="employee") node: /employee[1]/employee[3]/employee[2]/employee[1] stack: /1/2/2/2/reportsTo node: /employee[1]/employee[3]/employee[2]/employee[1] stack: /1/2/2/2/HimHer node: /employee[1]/employee[3]/employee[2]/employee[2] stack: /1/2/2/2 (match="employee[employee]") node: /employee[1]/employee[3]/employee[2]/employee[2]/employee[1] stack: /1/2/2/2/3 (match="employee") node: /employee[1]/employee[3]/employee[2]/employee[2]/employee[2] stack: /1/2/2/2/3 (match="employee") node: /employee[1]/employee[3]/employee[2]/employee[2] stack: /1/2/2/2/reportsTo node: /employee[1]/employee[3]/employee[2]/employee[2] stack: /1/2/2/2/HimHer node: /employee[1]/employee[3]/employee[2]/employee[3] stack: /1/2/2/2 (match="employee[employee]") node: /employee[1]/employee[3]/employee[2]/employee[3]/employee[1] stack: /1/2/2/2/3 (match="employee") node: /employee[1]/employee[3]/employee[2]/employee[3] stack: /1/2/2/2/reportsTo node: /employee[1]/employee[3]/employee[2]/employee[3] stack: /1/2/2/2/HimHer node: /employee[1]/employee[3]/employee[2] stack: /1/2/2/reportsTo node: /employee[1]/employee[3]/employee[2] stack: /1/2/2/HimHer node: /employee[1]/employee[3] stack: /1/2/reportsTo node: /employee[1]/employee[3] stack: /1/2/HimHer node: /employee[1] stack: /1/reportsTo node: /employee[1] stack: /1/HimHer The modified stylesheet outputs trace messages via the xsl:message mechanism. The format for every processed node is as follows: node: [XPath to this node] stack: [call stack of the templates invoked] param: name="[parameter name]" value="[parameter value]" more parameters variable: name="[variable name]" value="[variable value]" more variables The call stack takes the form of a path (with / as separator) and includes all passed templates. If a template has a name attribute, then this name is used. Otherwise, the number (position) of the template appears within the stack. If the current template does not have a name, the match attribute is displayed. If a mode attribute is specified, its value is displayed. One known problem is that the output for parameters or variables is their string value (produced with xsl:value-of). That's not reasonable for node sets and result-tree fragments. However, using xsl:copy-of results in an error if the variable contains attribute or namespace nodes without parents. 13.3.4 See Also The trace.xslt source and further examples can be found at http://www.informatik.hu- berlin.de/~obecker/XSLT/#trace. 13.4 Including Embedded Unit Test Data in Utility Stylesheets 13.4.1 Problem You want to package tests with your utility stylesheets so they can verified at any time. 13.4.2 Solution The following stylesheet is meant to be included as a utility. However, this example provides the capability of testing the stylesheet by executing it as its own input document: <! math.max.xslt > <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:math="http://www.exslt.org/math" exclude-result- prefixes="math" xmlns:test="http://www.ora.com/XSLTCookbook/test" id="math:math.max"> <xsl:template name="math:max"> <xsl:param name="nodes" select="/ "/> <xsl:param name="max"/> <xsl:variable name="count" select="count($nodes)"/> <xsl:variable name="aNode" select="$nodes[ceiling($count div 2)]"/> <xsl:choose> <xsl:when test="not($count)"> <xsl:value-of select="number($max)"/> </xsl:when> <xsl:when test="number($aNode) != number($aNode)"> <xsl:value-of select="number($aNode)"/> </xsl:when> <xsl:otherwise> <xsl:call-template name="math:max"> <xsl:with-param name="nodes" select="$nodes[not(. <= number($aNode))]"/> <xsl:with-param name="max"> <xsl:choose> <xsl:when test="not($max) or $aNode > $max"> <xsl:value-of select="$aNode"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="$max"/> </xsl:otherwise> </xsl:choose> </xsl:with-param> </xsl:call-template> </xsl:otherwise> </xsl:choose> </xsl:template> <! TEST CODE: DO NOT REMOVE! > <xsl:template match="/xsl:stylesheet[@id='math:math.max'] | xsl:include[@href='math. max.xslt'] " priority="-1000"> <xsl:message> TESTING math.max </xsl:message> <xsl:for-each select="document('')/*/test:test"> <xsl:variable name="ans"> <xsl:call-template name="math:max"> <xsl:with-param name="nodes" select="test:data"/> </xsl:call-template> </xsl:variable> <xsl:if test="$ans != @ans"> <xsl:message> math:max TEST <xsl:value-of select="@num"/> FAILED [<xsl:value-of select="$ans"/>] </xsl:message> </xsl:if> </xsl:for-each> <! Test with Infinity > <xsl:variable name="ans1"> <xsl:call-template name="math:max"> <xsl:with-param name="nodes" select="document('')/*/test:test[@num=1]/test: data"/> <xsl:with-param name="max" select="1 div 0"/> </xsl:call-template> </xsl:variable> <xsl:if test="$ans1 != Infinity"> <xsl:message> math:max Infinity Test FAILED [<xsl:value-of select="$ans1"/>] </xsl:message> </xsl:if> <! Test with -Infinity > <xsl:variable name="ans2"> <xsl:call-template name="math:max"> <xsl:with-param name="nodes" select="document('')/*/test:test[@num=1]/test: data"/> <xsl:with-param name="max" select="-1 div 0"/> </xsl:call-template> </xsl:variable> <xsl:if test="$ans2 != document('')/*/test:test[@num=1]/@ans"> <xsl:message> math:max -Infinity Test FAILED [<xsl:value-of select="$ans2"/>] </xsl:message> </xsl:if> </xsl:template> <test:test num="1" ans="9" xmlns="http://www.ora.com/XSLTCookbook/test"> <data>9</data> <data>8</data> <data>7</data> <data>6</data> <data>5</data> <data>4</data> <data>3</data> <data>2</data> <data>1</data> </test:test> <test:test num="2" ans="1" xmlns="http://www.ora.com/XSLTCookbook/test"> <data>1</data> </test:test> <test:test num="3" ans="1" xmlns="http://www.ora.com/XSLTCookbook/test"> <data>-1</data> <data>1</data> </test:test> <test:test num="4" ans="0" xmlns="http://www.ora.com/XSLTCookbook/test"> <data>0</data> <data>0</data> </test:test> <test:test num="5" ans="NaN" xmlns="http://www.ora.com/XSLTCookbook/test"> <data>foo</data> <data>1</data> </test:test> <test:test num="6" ans="NaN" xmlns="http://www.ora.com/XSLTCookbook/test"> <data>1</data> <data>foo</data> </test:test> <test:test num="7" ans="NaN" xmlns="http://www.ora.com/XSLTCookbook/test"> </test:test> </xsl:stylesheet> 13.4.3 Discussion The xsl:stylesheet element has an optional attribute called id. This attribute idenfities stylesheets that are embedded in larger documents. However, here the ID is used for testing purposes. You want to package test code with the stylesheet but make reasonably certain that this test code does not interfere with the normal usage of the stylesheet. Do this by creating a template that will match only when the stylesheet processes itself: <xsl:template match="/xsl:stylesheet[@id='math:math.max'] | xsl:include[@href='math.max.xslt']"> This explains the /xsl:stylesheet[@id='math:math.max'], but what about the xsl:include[@href='math.max.xslt'] part? To see the value of this, here is a stylesheet that packages all your math utilities into a single file for easy inclusion. You would like an easy way to test the entire package too: <! math.xslt > <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:math="http://exslt.org/math" extension-element-prefixes="math" id="math:math"> <xsl:include href="math.abs.xslt"/> <xsl:include href="math.constant.xslt"/> <xsl:include href="math.exp.xslt"/> <xsl:include href="math.highest.xslt"/> <xsl:include href="math.log.xslt"/> <xsl:include href="math.lowest.xslt"/> <xsl:include href="math.max.xslt"/> <xsl:include href="math.min.xslt"/> <xsl:include href="math.power.xslt"/> <xsl:include href="math.sqrt.xslt"/> <! TEST CODE > <xsl:template match="xsl:stylesheet[@id='math:math'] | xsl:include[@href='math. xslt']"> <xsl:message> TESTING math </xsl:message> <xsl:for-each select="document('')/*/xsl:include"> <xsl:apply-templates select="."/> </xsl:for-each> </xsl:template> <xsl:template match="xsl:include" priority="-10"> <xsl:message> WARNING: <xsl:value-of select="@href"/> has no test code. </xsl:message> </xsl:template> </xsl:stylesheet> Here you see that the test code for a package simply loops over all its xsl:include elements and applies templates to them. This step causes each included stylesheet tests to be exercised due to the aforementioned xsl:include[@href='filename'] part of the match. Notice the template <xsl:template match="xsl:include" priority="- 10">. This template causes emission of a warning if an included file does not contain test code. This concept is important for quality control, since forgetting to create tests is easy. If you object to packaging the tests with the actual code, you can achieve the same effect by creating separate test files for each utility. In this case, there is no need to use the id attribute of the stylesheet; simply match against the root: <! math.max.test.xslt > <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:math="http://www.exslt.org/math" exclude-result- prefixes="math" xmlns:test="http://www.ora.com/XSLTCookbook/test"> <xsl:include href=" /math/math.max.xslt"/> <! TEST CODE: DO NOT REMOVE! > <xsl:template match="/ | xsl:include[@href='math.max.test.xslt']"> <xsl:message> TESTING math.max </xsl:message> <xsl:for-each select="document('')/*/test:test"> <xsl:variable name="ans"> <xsl:call-template name="math:max"> <xsl:with-param name="nodes" select="test:data"/> </xsl:call-template> </xsl:variable> <xsl:if test="$ans != @ans"> <xsl:message> math:max TEST <xsl:value-of select="@num"/> FAILED [<xsl:value-of select="$ans"/>] </xsl:message> </xsl:if> </xsl:for-each> <! Same as math.max.xslt above > </xsl:stylesheet> You would then create separate test packages: <! math.test.xslt > <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:math="http://exslt.org/math" extension-element-prefixes="math"> <xsl:include href="math.max.test.xslt"/> <xsl:include href="math.min.test.xslt"/> <! Same as math.xslt, above > </xsl:stylesheet> If you separate your tests in this way, be sure to ship the test code with the actual implementations. Doing so allows your clients to verify tests for themselves. The test code also doubles as an example of how to use the templates. 13.5 Structuring Unit Tests 13.5.1 Problem You want to structure tests to simplify testing. 13.5.2 Solution Notice how Recipe 13.4 embedded test data in the test stylesheets. Each test element contains a test num attribute and the correct result in the form of an ans attribute. The test driver then extracts these test elements from the stylesheet, executes the test, and compares the expected answer against the actual answer. The most important aspect of the test driver is that it produces no output when the test succeeds. 13.5.3 Discussion Some of the best advice on automating testing is in Brian W. Kernighan's and Rob Pike's The Practice of Programming (Addison Wesley, 1999). The authors state that test programs should produce output only when tests fail. Why? Who wants to wade through pages of test output to look for cases where the test fail? If you expect test code to produce no output, you will quickly notice failures when there is output. Of course, you should test your test code to make sure it actually executes before relying on this testing technique. The method that stores the answer as an attribute in the test element works for simple tests that produce a primitive result. However, some templates produce node sets. In this case, you might need to store the correct answer as child elements in the tests. You can then use the value set operations of Recipe 7.2 to compare results. However, sometimes you can test node-set producing templates more simply. Consider the test driver for the math:lowest template. Recall that math:lowest returns a node set consisting of all instances of the lowest number in an input node set: <xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns: math="http://www.exslt.org/math" exclude-result- prefixes="math test" xmlns: test="http://www.ora.com/XSLTCookbook/test" id="math:math.lowest"> <xsl:import href="math.min.xslt"/> <xsl:template name="math:lowest"> <xsl:param name="nodes" select="/ "/> <xsl:variable name="min"> <xsl:call-template name="math:min"> <xsl:with-param name="nodes" select="$nodes"/> </xsl:call-template> </xsl:variable> <xsl:choose> <xsl:when test="number($min) = $min"> <xsl:copy-of select="$nodes[. = $min]"/> </xsl:when> <xsl:otherwise/> </xsl:choose> </xsl:template> <! TEST CODE: DO NOT REMOVE! > <xsl:template match="xsl:stylesheet[@id='math:math.lowest'] | xsl:include[@href='math.lowest.xslt'] " xmlns:exsl="http://exslt.org/common"> <xsl:message> TESTING math.lowest </xsl:message> <xsl:choose> <xsl:when test="function-available('exsl:node-set')"> <xsl:for-each select="document('')/*/test:test"> <xsl:variable name="ans"> <xsl:call-template name="math:lowest"> <xsl:with-param name="nodes" select="test:data"/> </xsl:call-template> </xsl:variable> <xsl:variable name="$ans-ns" select=" exsl:node- set($ans)"/> <xsl:if test="not($ans-ns/* != test:data[. = current( )/@ans]) and count($ans-ns/*) != count(test:data[. = current( )/@ans])"> <xsl:message> math:lowest TEST <xsl:value-of select="@num"/> FAILED [<xsl:copy-of select="$ans-ns"/>] [<xsl:copy-of select="test:data[. = current( )/@ans]"/>] </xsl:message> </xsl:if> </xsl:for-each> </xsl:when> <xsl:otherwise> <xsl:message> WARNING math.lowest test code requires exsl:node- set THIS VERSION=[<xsl:value-of select="system- property('xsl:version')"/>] VENDOR=[<xsl:value-of select="system- property('xsl:vendor')"/>] </xsl:message> </xsl:otherwise> </xsl:choose> </xsl:template> <test:test num="1" ans="1" xmlns="http://www.ora.com/XSLTCookbook/test"> <data>9</data> [...]... thus somewhat more challenging Dimitre has an XSLT library called FXSL - an XSLT functional programming library that can be downloaded from http://topxml.com/xsl/articles/dice 14.2 Creating Polymorphic XSLT 14.2.1 Problem You want to create XSLT that performs the same function on disparate data 14.2.2 Solution There are two kinds of polymorphic behavior in XSLT The first form is reminiscent of overloading,... > Copyright All rights reserved. 14.2.3 Discussion Calling XSLT an object-oriented language would stretch... of XSLT Fortunately, you can achieve this goal by using matching instead of naming The trick is to define a template that can match only one particular piece of data That piece of data is called a template tag, and by convention, you define the tag directly above the template it matches: ... first, $data1-public-data, is unique to this stylesheet The second, $data, is defined in terms of the first, but can be overridden: .xslt > XSLT toplevel element works with respect to xsl:import in Michael Kay's XSLT Programmer's Reference (Wrox, 2001) This chapter takes advantage of the ability to combine a global variable's contents defined in an imported stylesheet with one... generic programming 14.1.3 See Also Dimitre Novatchev was the first person, to my knowledge, to discover techniques for generic and functional programming in XSLT He wrote several articles on the topic See http://topxml.com/members/profile.asp?id=i1005 The generic programming recipes in this chapter were developed before I discovered Dimitre's work and vary in some ways from Dimitre's approach I would . xmlns="http://www.ora.com/XSLTCookbook/test"> <data>1</data> </test:test> <test:test num="3" ans="1" xmlns="http://www.ora.com/XSLTCookbook/test">. href="math.abs .xslt& quot;/> <xsl:include href="math.constant .xslt& quot;/> <xsl:include href="math.exp .xslt& quot;/> <xsl:include href="math.highest .xslt& quot;/>. href="math.log .xslt& quot;/> <xsl:include href="math.lowest .xslt& quot;/> <xsl:include href="math.max .xslt& quot;/> <xsl:include href="math.min .xslt& quot;/>