Merge pull request #105 from codecop/master

Port to XSLT
This commit is contained in:
Emily Bache 2018-12-08 12:12:25 +01:00 committed by GitHub
commit 702220f151
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 570 additions and 0 deletions

3
xslt/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*.html
*.test_result.xml
*.next_day.xml

19
xslt/README.md Normal file
View File

@ -0,0 +1,19 @@
# XSLT port of the Gilded-Rose Kata
This is a XSLT 1.0 port of the *Gilded-Rose-Kata*.
## Building and Running
* [Apache Ant's XSLT task](https://ant.apache.org/manual/Tasks/style.html) is used to run the transformations.
* `update_quality.xsl` contains the Gilded Rose logic.
* Run Ant in the current folder to transform all files.
* `texttest_fixture.xml` is transformed into `texttest_fixture.next_day.xml` with updated values.
## Unit Test
[xsltunit](http://xsltunit.org/) is a implementation of xUnit in XSLT.
* `tst_update_quality.xsl`is the the test (stylesheet).
* Run Ant in the current folder to run the tests.
* `update_quality.test_result.xml` contains the results and
`update_quality.test_result.html` is a readable report.

76
xslt/build.xml Normal file
View File

@ -0,0 +1,76 @@
<?xml version='1.0'?>
<project name="GildedRoseXslt" default="run" basedir=".">
<property name="style" value="update_quality" />
<property name="src" location="." />
<property name="test_prefix" value="tst_" />
<property name="test_process_suffix" value=".next_day.xml" />
<property name="test_result_suffix" value=".test_result.xml" />
<target name="validateXml">
<!-- for lenient="false" all XML must define a DTD, else we get an error -->
<xmlvalidate warn="true" lenient="true">
<fileset dir="${src}">
<include name="**/*.xml" />
<include name="**/*.xsl" />
<exclude name="**/build.xml" />
</fileset>
</xmlvalidate>
<xmlvalidate warn="true" lenient="false">
<fileset dir="${src}">
<include name="*.xml" />
<exclude name="*${test_process_suffix}" />
<exclude name="*${test_result_suffix}" />
<exclude name="build.xml" />
</fileset>
</xmlvalidate>
</target>
<target name="test" depends="-testSheet,-verifyResult" />
<target name="-testSheet">
<dirname property="suite" file="${style}.xsl" />
<!-- apply style to XMLs in suite -->
<xslt basedir="${suite}" destdir="${suite}" style="${suite}/${style}.xsl" extension="${test_process_suffix}">
<include name="*.xml" />
<exclude name="*${test_process_suffix}" />
<exclude name="*${test_result_suffix}" />
<exclude name="build.xml" />
</xslt>
<!-- apply test(s) -->
<xslt basedir="${suite}" destdir="${suite}" style="${suite}/${test_prefix}${style}.xsl" extension="${test_result_suffix}">
<include name="*.xsl" />
<exclude name="${test_prefix}*.xsl" />
</xslt>
<!-- create readable html test report -->
<xslt basedir="${suite}" destdir="${suite}" style="${basedir}/xsltunit-0.2/xsltunit_report.xsl">
<include name="*${test_result_suffix}" />
<param name="testname" expression="${style}.xsl" />
</xslt>
</target>
<target name="-verifyResult">
<loadfile property="test_failed" srcFile="${style}${test_result_suffix}" />
<fail message="Test ${suite} failed!">
<condition>
<contains string="${test_failed}" substring="outcome=&quot;failed&quot;" />
</condition>
</fail>
</target>
<target name="clean">
<delete dir="${src}">
<include name="**/*${test_process_suffix}" />
<include name="**/*${test_result_suffix}" />
<include name="**/*.html" />
</delete>
</target>
<target name="run" depends="validateXml,test" />
</project>

8
xslt/gilded_rose.dtd Normal file
View File

@ -0,0 +1,8 @@
<!ELEMENT gildedrose (greeting?, day?, items) >
<!ELEMENT greeting (#PCDATA) >
<!ELEMENT day (#PCDATA) >
<!ELEMENT items (item*) >
<!ELEMENT item EMPTY >
<!ATTLIST item name CDATA #REQUIRED
sellin CDATA #REQUIRED
quality CDATA #REQUIRED>

18
xslt/texttest_fixture.xml Normal file
View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<!DOCTYPE gildedrose SYSTEM "gilded_rose.dtd">
<gildedrose>
<greeting>OMGHAI!</greeting>
<day>0</day>
<items>
<item name="+5 Dexterity Vest" sellin="10" quality="20" />
<item name="Aged Brie" sellin="2" quality="0" />
<item name="Elixir of the Mongoose" sellin="5" quality="7" />
<item name="Sulfuras, Hand of Ragnaros" sellin="0" quality="80" />
<item name="Sulfuras, Hand of Ragnaros" sellin="-1" quality="80" />
<item name="Backstage passes to a TAFKAL80ETC concert" sellin="15" quality="20" />
<item name="Backstage passes to a TAFKAL80ETC concert" sellin="10" quality="49" />
<item name="Backstage passes to a TAFKAL80ETC concert" sellin="5" quality="49" />
<!-- this conjured item does not work properly yet -->
<item name="Conjured Mana Cake" sellin="3" quality="6" />
</items>
</gildedrose>

View File

@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl" xmlns:xsltu="http://xsltunit.org/0/" exclude-result-prefixes="exsl" xmlns:xalan="http://xml.apache.org/xalan">
<xsl:import href="update_quality.xsl" />
<xsl:import href="xsltunit-0.2/xsltunit.xsl" />
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" />
<xsl:template match="/">
<xsltu:tests>
<xsltu:test id="test-top-level-adds-greeting">
<xsl:variable name="source">
<gildedrose><items></items></gildedrose>
</xsl:variable>
<xsl:variable name="applied">
<xsl:apply-templates select="exsl:node-set($source)/gildedrose" />
</xsl:variable>
<xsl:call-template name="xsltu:assertEqual">
<xsl:with-param name="id" select="'template shows greeting'" />
<xsl:with-param name="nodes1">
<xsl:value-of select="exsl:node-set($applied)/gildedrose/greeting" />
</xsl:with-param>
<xsl:with-param name="nodes2">OMGHAI!</xsl:with-param>
</xsl:call-template>
</xsltu:test>
<xsltu:test id="test-top-level-keeps-greeting">
<xsl:variable name="source">
<gildedrose><greeting>OMGHAI!</greeting><items></items></gildedrose>
</xsl:variable>
<xsl:variable name="applied">
<xsl:apply-templates select="exsl:node-set($source)/gildedrose" />
</xsl:variable>
<xsl:call-template name="xsltu:assertEqual">
<xsl:with-param name="id" select="'template shows greeting'" />
<xsl:with-param name="nodes1">
<xsl:value-of select="exsl:node-set($applied)/gildedrose/greeting" />
</xsl:with-param>
<xsl:with-param name="nodes2">OMGHAI!</xsl:with-param>
</xsl:call-template>
</xsltu:test>
<xsltu:test id="test-day-increases-day">
<xsl:variable name="source">
<gildedrose><day>12</day><items></items></gildedrose>
</xsl:variable>
<xsl:variable name="applied">
<xsl:apply-templates select="exsl:node-set($source)/gildedrose" />
</xsl:variable>
<xsl:call-template name="xsltu:assertEqual">
<xsl:with-param name="id" select="'template shows day'" />
<xsl:with-param name="nodes1">
<xsl:value-of select="exsl:node-set($applied)/gildedrose/day" />
</xsl:with-param>
<xsl:with-param name="nodes2">13</xsl:with-param>
</xsl:call-template>
</xsltu:test>
<xsltu:test id="test-top-level-resets-day">
<xsl:variable name="source">
<gildedrose><items></items></gildedrose>
</xsl:variable>
<xsl:variable name="applied">
<xsl:apply-templates select="exsl:node-set($source)/gildedrose" />
</xsl:variable>
<xsl:call-template name="xsltu:assertEqual">
<xsl:with-param name="id" select="'template shows day'" />
<xsl:with-param name="nodes1">
<xsl:value-of select="exsl:node-set($applied)/gildedrose/day" />
</xsl:with-param>
<xsl:with-param name="nodes2">0</xsl:with-param>
</xsl:call-template>
</xsltu:test>
<xsltu:test id="test-item-foo">
<xsl:variable name="source">
<gildedrose><items><item name="foo" sellin="0" quality="0" /></items></gildedrose>
</xsl:variable>
<xsl:variable name="applied">
<xsl:apply-templates select="exsl:node-set($source)/gildedrose" />
</xsl:variable>
<xsl:call-template name="xsltu:assertEqual">
<xsl:with-param name="id" select="'name'" />
<xsl:with-param name="nodes1">
<xsl:value-of select="exsl:node-set($applied)/gildedrose/items/item[1]/@name" />
</xsl:with-param>
<xsl:with-param name="nodes2">fixme</xsl:with-param>
</xsl:call-template>
</xsltu:test>
</xsltu:tests>
</xsl:template>
</xsl:stylesheet>

141
xslt/update_quality.xsl Normal file
View File

@ -0,0 +1,141 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" encoding="utf-8" indent="yes" />
<xsl:template match="/gildedrose">
<xsl:text disable-output-escaping='yes'>
&lt;!DOCTYPE gildedrose SYSTEM "gilded_rose.dtd"&gt;
</xsl:text>
<xsl:element name="gildedrose">
<xsl:element name="greeting">
<xsl:text>OMGHAI!</xsl:text>
</xsl:element>
<xsl:choose>
<xsl:when test="day">
<xsl:apply-templates select="day" />
</xsl:when>
<xsl:otherwise>
<xsl:element name="day">
<xsl:text>0</xsl:text>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
<xsl:element name="items">
<xsl:for-each select="items/item">
<xsl:element name="item">
<xsl:attribute name="name">
<xsl:value-of select="@name" />
</xsl:attribute>
<xsl:variable name="qualityDelta1">
<xsl:choose>
<xsl:when test="not(@name = 'Aged Brie') and not(@name = 'Backstage passes to a TAFKAL80ETC concert')">
<xsl:if test="@quality &gt; 0">
<xsl:if test="not(@name = 'Sulfuras, Hand of Ragnaros')">
<xsl:text>-</xsl:text>
</xsl:if>
</xsl:if>
</xsl:when>
<xsl:otherwise>
<xsl:if test="@quality &lt; 50">
<xsl:text>-0+</xsl:text>
<xsl:if test="@name = 'Backstage passes to a TAFKAL80ETC concert'">
<xsl:if test="@sellin &lt; 11">
<xsl:if test="@quality &lt; 49"> <!-- hack for XSLT -->
<xsl:text>+</xsl:text>
</xsl:if>
</xsl:if>
<xsl:if test="@sellin &lt; 6">
<xsl:if test="@quality &lt; 48"> <!-- hack for XSLT -->
<xsl:text>+</xsl:text>
</xsl:if>
</xsl:if>
</xsl:if>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="quality1">
<xsl:choose>
<xsl:when test="string-length($qualityDelta1)">
<xsl:value-of select="@quality + string-length($qualityDelta1) - 2" /> <!-- hack for XSLT -->
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="@quality" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="sellin">
<xsl:choose>
<xsl:when test="not(@name = 'Sulfuras, Hand of Ragnaros')">
<xsl:value-of select="@sellin - 1" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="@sellin" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:attribute name="sellin">
<xsl:value-of select="$sellin" />
</xsl:attribute>
<xsl:variable name="qualityDelta2">
<xsl:if test="$sellin &lt; 0">
<xsl:choose>
<xsl:when test="not(@name = 'Aged Brie')">
<xsl:choose>
<xsl:when test="not(@name = 'Backstage passes to a TAFKAL80ETC concert')">
<xsl:if test="$quality1 &gt; 0">
<xsl:if test="not(@name = 'Sulfuras, Hand of Ragnaros')">
<xsl:text>-1</xsl:text>
</xsl:if>
</xsl:if>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="-$quality1" />
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise>
<xsl:if test="$quality1 &lt; 50">
<xsl:text>1</xsl:text>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:variable>
<xsl:attribute name="quality">
<xsl:choose>
<xsl:when test="string-length($qualityDelta2)">
<xsl:value-of select="$quality1 + $qualityDelta2" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$quality1" />
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:element>
</xsl:template>
<xsl:template match="day">
<xsl:element name="day">
<xsl:variable name="day-before">
<xsl:value-of select="." />
</xsl:variable>
<xsl:value-of select="$day-before + 1" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>

View File

@ -0,0 +1,158 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl"
xmlns:xsltu="http://xsltunit.org/0/"
exclude-result-prefixes="exsl">
<xsl:template name="xsltu:assertEqual">
<xsl:param name="id"/>
<xsl:param name="nodes1"/>
<xsl:param name="nodes2"/>
<xsl:variable name="result">
<xsl:call-template name="xsltu:diff">
<xsl:with-param name="nodes1" select="exsl:node-set($nodes1)"/>
<xsl:with-param name="nodes2" select="exsl:node-set($nodes2)"/>
</xsl:call-template>
</xsl:variable>
<xsl:call-template name="xsltu:assert">
<xsl:with-param name="id" select="$id"/>
<xsl:with-param name="test" select="not(exsl:node-set($result)//xsltu:no-match)"/>
<xsl:with-param name="message" select="exsl:node-set($result)"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="xsltu:assertNotEqual">
<xsl:param name="id"/>
<xsl:param name="nodes1"/>
<xsl:param name="nodes2"/>
<xsl:variable name="result">
<xsl:call-template name="xsltu:diff">
<xsl:with-param name="nodes1" select="exsl:node-set($nodes1)"/>
<xsl:with-param name="nodes2" select="exsl:node-set($nodes2)"/>
</xsl:call-template>
</xsl:variable>
<xsl:call-template name="xsltu:assert">
<xsl:with-param name="id" select="$id"/>
<xsl:with-param name="test" select="exsl:node-set($result)//xsltu:no-match"/>
<xsl:with-param name="message">Should have been different!</xsl:with-param>
</xsl:call-template>
</xsl:template>
<xsl:template name="xsltu:assert">
<xsl:param name="id"/>
<xsl:param name="test"/>
<xsl:param name="message"/>
<xsltu:assert id="{$id}">
<xsl:choose>
<xsl:when test="$test">
<xsl:attribute name="outcome">passed</xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:attribute name="outcome">failed</xsl:attribute>
<xsltu:message>
<xsl:copy-of select="$message"/>
</xsltu:message>
</xsl:otherwise>
</xsl:choose>
</xsltu:assert>
</xsl:template>
<xsl:template name="xsltu:diff">
<xsl:param name="nodes1"/>
<xsl:param name="nodes2"/>
<xsltu:diff name="{name($nodes1)}">
<xsl:choose>
<xsl:when test="self::* and (local-name($nodes1) != local-name($nodes2) or namespace-uri($nodes1) != namespace-uri($nodes2))">
<xsltu:no-match diff="names">
<xsltu:node>
<xsl:copy-of select="$nodes1"/>
</xsltu:node>
<xsltu:node>
<xsl:copy-of select="$nodes2"/>
</xsltu:node>
</xsltu:no-match>
</xsl:when>
<xsl:when test="count($nodes1/@*) != count($nodes2/@*)">
<xsltu:no-match diff="number of children attributes ({count($nodes1/@*)} versus {count($nodes2/@*)} )">
<xsltu:node>
<xsl:copy-of select="$nodes1"/>
</xsltu:node>
<xsltu:node>
<xsl:copy-of select="$nodes2"/>
</xsltu:node>
</xsltu:no-match>
</xsl:when>
<xsl:when test="count($nodes1/*) != count($nodes2/*)">
<xsltu:no-match diff="number of children elements ({count($nodes1/*)} versus {count($nodes2/*)} )">
<xsltu:node>
<xsl:copy-of select="$nodes1"/>
</xsltu:node>
<xsltu:node>
<xsl:copy-of select="$nodes2"/>
</xsltu:node>
</xsltu:no-match>
</xsl:when>
<xsl:when test="count($nodes1/text()) != count($nodes2/text())">
<xsltu:no-match diff="number of children text nodes ({count($nodes1/text())} versus {count($nodes2/text())} )">
<xsltu:node>
<xsl:copy-of select="$nodes1"/>
</xsltu:node>
<xsltu:node>
<xsl:copy-of select="$nodes2"/>
</xsltu:node>
</xsltu:no-match>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="$nodes1/@*" mode="xsltu:diff">
<xsl:with-param name="nodes2" select="$nodes2"/>
</xsl:apply-templates>
<xsl:apply-templates select="$nodes1/*" mode="xsltu:diff">
<xsl:with-param name="nodes2" select="$nodes2"/>
</xsl:apply-templates>
<xsl:apply-templates select="$nodes1/text()" mode="xsltu:diff">
<xsl:with-param name="nodes2" select="$nodes2"/>
</xsl:apply-templates>
</xsl:otherwise>
</xsl:choose>
</xsltu:diff>
</xsl:template>
<xsl:template match="*" mode="xsltu:diff">
<xsl:param name="pos" select="position()"/>
<xsl:param name="nodes2"/>
<xsl:param name="node2" select="$nodes2/*[position()=$pos]"/>
<xsl:call-template name="xsltu:diff">
<xsl:with-param name="nodes1" select="."/>
<xsl:with-param name="nodes2" select="$node2"/>
</xsl:call-template>
</xsl:template>
<xsl:template match="text()" mode="xsltu:diff">
<xsl:param name="current" select="."/>
<xsl:param name="pos" select="position()"/>
<xsl:param name="nodes2"/>
<xsl:param name="node2" select="$nodes2/text()[position()=$pos]"/>
<xsl:if test="not(. = $node2)">
<xsltu:no-match>
<xsltu:node>
<xsl:copy-of select="."/>
</xsltu:node>
<xsltu:node>
<xsl:copy-of select="$node2"/>
</xsltu:node>
</xsltu:no-match>
</xsl:if>
</xsl:template>
<xsl:template match="@*" mode="xsltu:diff">
<xsl:param name="current" select="."/>
<xsl:param name="nodes2"/>
<xsl:param name="node2" select="$nodes2/@*[local-name() = local-name(current()) and namespace-uri() = namespace-uri(current())]"/>
<xsl:if test="not(. = $node2)">
<xsltu:no-match>
<xsltu:node>
<xsl:copy-of select="."/>
</xsltu:node>
<xsltu:node>
<xsl:copy-of select="$node2"/>
</xsltu:node>
</xsltu:no-match>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

View File

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsltu="http://xsltunit.org/0/" version="1.0">
<xsl:param name="testname" />
<xsl:template match="xsltu:tests">
<html>
<head>
<title>All Tests</title>
</head>
<body>
<h2>Tests Stylesheet <code><xsl:value-of select="$testname" /></code></h2>
<xsl:apply-templates />
</body>
</html>
</xsl:template>
<xsl:template match="xsltu:test">
<h3>Test <code><xsl:value-of select="@id" /></code></h3>
<ul>
<xsl:apply-templates />
</ul>
</xsl:template>
<xsl:template match="xsltu:assert">
<li>
Assert <code><xsl:value-of select="@id" /></code><xsl:text> ... </xsl:text>
<xsl:choose>
<xsl:when test="@outcome = 'passed'">
<span style="color:green">Passed</span>
</xsl:when>
<xsl:otherwise>
<span style="color:red">Failed</span>
<xsl:apply-templates />
</xsl:otherwise>
</xsl:choose>
</li>
</xsl:template>
<xsl:template match="text()">
<xsl:value-of select="normalize-space(translate(., '&#9;&#10;&#13;', ''))" />
</xsl:template>
<xsl:template match="xsltu:no-match">
<table>
<tr>
<td>Expected:</td><td><code><xsl:value-of select="node[2]" /></code></td>
</tr>
<tr>
<td>Actual:</td><td><code><xsl:value-of select="node[1]" /></code></td>
</tr>
</table>
</xsl:template>
</xsl:stylesheet>