XslTransformTests.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. //
  2. // System.Xml.Xsl.XslTransformTests.cs
  3. //
  4. // Author:
  5. // Atsushi Enomoto <[email protected]>
  6. //
  7. // (C) 2002 Atsushi Enomoto
  8. //
  9. using System;
  10. using System.Globalization;
  11. using System.IO;
  12. using System.Text;
  13. using System.Xml;
  14. using System.Xml.XPath;
  15. using System.Xml.Xsl;
  16. using NUnit.Framework;
  17. namespace MonoTests.System.Xml.Xsl
  18. {
  19. [TestFixture]
  20. public class XslTransformTests
  21. {
  22. XmlDocument doc;
  23. XslTransform xslt;
  24. XmlDocument result;
  25. [SetUp]
  26. public void GetReady()
  27. {
  28. doc = new XmlDocument ();
  29. xslt = new XslTransform ();
  30. result = new XmlDocument ();
  31. }
  32. [Test]
  33. public void TestBasicTransform ()
  34. {
  35. doc.LoadXml ("<root/>");
  36. xslt.Load ("Test/XmlFiles/xsl/empty.xsl");
  37. xslt.Transform ("Test/XmlFiles/xsl/empty.xsl", "Test/XmlFiles/xsl/result.xml");
  38. result.Load ("Test/XmlFiles/xsl/result.xml");
  39. Assert.AreEqual (2, result.ChildNodes.Count, "count");
  40. }
  41. [Test]
  42. [ExpectedException (typeof (XsltCompileException))]
  43. public void InvalidStylesheet ()
  44. {
  45. XmlDocument doc = new XmlDocument ();
  46. doc.LoadXml ("<xsl:element xmlns:xsl='http://www.w3.org/1999/XSL/Transform' />");
  47. XslTransform t = new XslTransform ();
  48. t.Load (doc);
  49. }
  50. [Test]
  51. [ExpectedException (typeof (XsltCompileException))]
  52. public void EmptyStylesheet ()
  53. {
  54. XmlDocument doc = new XmlDocument ();
  55. XslTransform t = new XslTransform ();
  56. t.Load (doc);
  57. }
  58. [Test]
  59. [ExpectedException (typeof (XsltCompileException))]
  60. public void InvalidStylesheet2 ()
  61. {
  62. string xsl = @"<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='1.0'>
  63. <xsl:template match='/root'>
  64. <xsl:call-template name='foo'>
  65. <xsl:with-param name='name' value='text()' />
  66. </xsl:call-template>
  67. </xsl:template>
  68. <xsl:template name='foo'>
  69. <xsl:param name='name' />
  70. <result>
  71. <xsl:if test='1'>
  72. <xsl:variable name='last' value='text()' />
  73. <xsl:value-of select='$last' />
  74. </xsl:if>
  75. </result>
  76. </xsl:template>
  77. </xsl:stylesheet>
  78. ";
  79. XslTransform xslt = new XslTransform ();
  80. xslt.Load (new XPathDocument (new XmlTextReader (xsl, XmlNodeType.Document, null)));
  81. }
  82. [Test()]
  83. [Category ("NotWorking")] // it depends on "mcs" existence
  84. public void MsxslTest() {
  85. string _styleSheet = @"
  86. <xslt:stylesheet xmlns:xslt=""http://www.w3.org/1999/XSL/Transform"" version=""1.0""
  87. xmlns:msxsl=""urn:schemas-microsoft-com:xslt"" xmlns:stringutils=""urn:schemas-sourceforge.net-blah"">
  88. <xslt:output method=""text"" />
  89. <msxsl:script language=""C#"" implements-prefix=""stringutils"">
  90. <![CDATA[
  91. string PadRight( string str, int padding) {
  92. return str.PadRight(padding);
  93. }
  94. ]]>
  95. </msxsl:script>
  96. <xslt:template match=""test"">
  97. <xslt:value-of select=""stringutils:PadRight(@name, 20)"" />
  98. </xslt:template>
  99. </xslt:stylesheet>";
  100. StringReader stringReader = new StringReader(_styleSheet);
  101. XslTransform transform = new XslTransform();
  102. XmlTextReader reader = new XmlTextReader(stringReader);
  103. transform.Load(reader, new XmlUrlResolver(), AppDomain.CurrentDomain.Evidence);
  104. StringBuilder sb = new StringBuilder();
  105. StringWriter writer = new StringWriter(sb, CultureInfo.InvariantCulture);
  106. XsltArgumentList arguments = new XsltArgumentList();
  107. XmlDocument xmlDoc = new XmlDocument();
  108. xmlDoc.LoadXml("<test name=\"test\" />");
  109. // Do transformation
  110. transform.Transform(xmlDoc, new XsltArgumentList(), writer, new XmlUrlResolver());
  111. Assert.AreEqual ("test".PadRight(20), sb.ToString());
  112. }
  113. [Test]
  114. public void MSXslNodeSet ()
  115. {
  116. string xsl = @"<xsl:stylesheet version='1.0'
  117. xmlns:xsl='http://www.w3.org/1999/XSL/Transform' xmlns:msxsl='urn:schemas-microsoft-com:xslt'>
  118. <xsl:template match='/'>
  119. <root>
  120. <xsl:variable name='var'>
  121. <xsl:copy-of select='root/foo' />
  122. </xsl:variable>
  123. <xsl:for-each select='msxsl:node-set($var)/foo'>
  124. <xsl:value-of select='name(.)' />: <xsl:value-of select='@attr' />
  125. </xsl:for-each>
  126. </root>
  127. </xsl:template>
  128. </xsl:stylesheet>";
  129. StringWriter sw = new StringWriter ();
  130. XslTransform t = new XslTransform ();
  131. t.Load (new XPathDocument (new StringReader (xsl)));
  132. t.Transform (new XPathDocument (new XmlTextReader (new StringReader ("<root><foo attr='A'/><foo attr='B'/><foo attr='C'/></root>"))), null, sw);
  133. Assert.AreEqual (@"<?xml version=""1.0"" encoding=""utf-16""?><root xmlns:msxsl=""urn:schemas-microsoft-com:xslt"">foo: Afoo: Bfoo: C</root>", sw.ToString ());
  134. }
  135. [Test]
  136. [Category ("NotDotNet")]
  137. // Actually MS.NET here throws XsltException, but Mono returns
  138. // XPathException (since XPath evaluation engine generally
  139. // catches (should catch) static error. It is implementation
  140. // dependent matter.
  141. [ExpectedException (typeof (XPathException))]
  142. public void MSXslNodeSetRejectsNodeSet ()
  143. {
  144. string xsl = @"<xsl:stylesheet version='1.0'
  145. xmlns:xsl='http://www.w3.org/1999/XSL/Transform' xmlns:msxsl='urn:schemas-microsoft-com:xslt'>
  146. <xsl:template match='/'>
  147. <root>
  148. <!-- msxsl:node-set() does not accept a node set -->
  149. <xsl:for-each select='msxsl:node-set(root/foo)'>
  150. <xsl:value-of select='name(.)' />: <xsl:value-of select='@attr' />
  151. </xsl:for-each>
  152. </root>
  153. </xsl:template>
  154. </xsl:stylesheet>";
  155. StringWriter sw = new StringWriter ();
  156. XslTransform t = new XslTransform ();
  157. t.Load (new XPathDocument (new StringReader (xsl)));
  158. t.Transform (new XPathDocument (new XmlTextReader (new StringReader ("<root><foo attr='A'/><foo attr='B'/><foo attr='C'/></root>"))), null, sw);
  159. }
  160. [Test]
  161. public void EvaluateEmptyVariableAsBoolean ()
  162. {
  163. string xsl = @"<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='1.0'>
  164. <xsl:template match='/'>
  165. <xsl:variable name='var'><empty /></xsl:variable>
  166. <root><xsl:if test='$var'>true</xsl:if></root>
  167. </xsl:template>
  168. </xsl:stylesheet>";
  169. XslTransform t = new XslTransform ();
  170. t.Load (new XPathDocument (new StringReader (xsl)));
  171. StringWriter sw = new StringWriter ();
  172. t.Transform (
  173. new XPathDocument (new StringReader ("<root/>")),
  174. null,
  175. sw);
  176. Assert.IsTrue (sw.ToString ().IndexOf ("true") > 0);
  177. }
  178. [Test]
  179. [ExpectedException (typeof (XsltCompileException))]
  180. public void NotAllowedPatternAxis ()
  181. {
  182. string xsl = @"<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='1.0'>
  183. <xsl:template match='/descendant-or-self::node()/elem'>
  184. <ERROR/>
  185. </xsl:template>
  186. </xsl:stylesheet>";
  187. new XslTransform ().Load (new XPathDocument (
  188. new StringReader (xsl)));
  189. }
  190. [Test]
  191. [ExpectedException (typeof (XsltCompileException))]
  192. public void ImportIncorrectlyLocated ()
  193. {
  194. string xsl = @"<xsl:transform xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='1.0'>
  195. <xsl:template match='/'></xsl:template>
  196. <xsl:import href='dummy.xsl' />
  197. </xsl:transform>";
  198. new XslTransform ().Load (new XPathDocument (
  199. new StringReader (xsl)));
  200. }
  201. private WeakReference StylesheetLoad (XslTransform t, string xsl)
  202. {
  203. XPathDocument doc = new XPathDocument (
  204. new StringReader (xsl));
  205. WeakReference wr = new WeakReference (doc);
  206. t.Load (doc);
  207. return wr;
  208. }
  209. private WeakReference StylesheetTransform (XslTransform t, string xml)
  210. {
  211. XPathDocument doc = new XPathDocument (
  212. new StringReader (xml));
  213. WeakReference wr = new WeakReference (doc);
  214. t.Transform (doc, null, TextWriter.Null, null);
  215. return wr;
  216. }
  217. [Test]
  218. // bug #75663.
  219. public void ErrorOnDocumentResolution ()
  220. {
  221. // XslTransform recovers from errors on document resolution.
  222. string xslText = @"<xsl:stylesheet
  223. xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
  224. version='1.0'>
  225. <xsl:variable name='n'
  226. select='document(""notexist.xml"")' />
  227. <xsl:template match='/'>xx</xsl:template>
  228. </xsl:stylesheet>";
  229. string xmlText = @"<root />";
  230. XslTransform transform = new XslTransform ();
  231. XPathDocument doc = new XPathDocument (
  232. new XmlTextReader ("a.xsl", new StringReader (xslText)));
  233. transform.Load (doc);
  234. XPathDocument xmlDocument = new XPathDocument (new StringReader (xmlText));
  235. transform.Transform (xmlDocument, null, TextWriter.Null);
  236. }
  237. // bug #76046
  238. [Test]
  239. public void LoadStyleFromNonRoot ()
  240. {
  241. XmlDocument doc = new XmlDocument ();
  242. XslTransform xslt = new XslTransform ();
  243. doc.LoadXml ("<root><dummy /><xsl:transform xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='1.0' /></root>");
  244. XmlNode node = doc.ChildNodes [0].ChildNodes [1];
  245. xslt.Load (node, null, null);
  246. }
  247. [Test]
  248. public void ReturnEmptyResultsAsXmlReader ()
  249. {
  250. // bug #76115
  251. XmlDocument doc = new XmlDocument ();
  252. doc.LoadXml ("<xsl:transform xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='1.0' />");
  253. XslTransform xslt = new XslTransform ();
  254. xslt.Load (doc, null, null);
  255. XmlReader reader = xslt.Transform(doc, null, new XmlUrlResolver ());
  256. reader.Read ();
  257. // another case - with xsl:output standalone='yes'
  258. doc.LoadXml ("<xsl:transform xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='1.0' ><xsl:output standalone='yes' indent='no'/><xsl:template match='/'><foo/></xsl:template></xsl:transform>");
  259. xslt = new XslTransform ();
  260. xslt.Load (doc, null, null);
  261. reader = xslt.Transform (doc, null, new XmlUrlResolver ());
  262. while (!reader.EOF)
  263. reader.Read (); // btw no XMLdecl output.
  264. }
  265. [Test] // bug #76530
  266. // http://www.w3.org/TR/xslt#section-Creating-Elements-with-xsl:element
  267. // "If the namespace attribute is not present then the QName
  268. // is expanded into an expanded-name using the namespace
  269. // declarations in effect for the xsl:element element,
  270. // including any default namespace declaration."
  271. public void LREDefaultNamespace ()
  272. {
  273. string xsl = @"<xsl:stylesheet version='1.0' xmlns='urn:foo' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
  274. <xsl:template match='/*'>
  275. <xsl:element name='{local-name()}' />
  276. </xsl:template>
  277. </xsl:stylesheet>";
  278. string xml = "<root/>";
  279. XslTransform t = new XslTransform ();
  280. t.Load (new XPathDocument (new StringReader (xsl)));
  281. StringWriter sw = new StringWriter ();
  282. XmlTextWriter xw = new XmlTextWriter (sw);
  283. t.Transform (
  284. new XPathDocument (new StringReader (xml)),
  285. null, xw);
  286. Assert.AreEqual ("<root xmlns=\"urn:foo\" />",
  287. sw.ToString ());
  288. string xsl2 = @"<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform' xmlns='urn:foo'>
  289. <xsl:template match='/*'>
  290. <root>
  291. <xsl:element name='{local-name()}' />
  292. </root>
  293. </xsl:template>
  294. </xsl:stylesheet>";
  295. string xml2 = "<page/>";
  296. t.Load (new XPathDocument (new StringReader (xsl2)));
  297. sw = new StringWriter ();
  298. xw = new XmlTextWriter (sw);
  299. t.Transform (
  300. new XPathDocument (new StringReader (xml2)),
  301. null, xw);
  302. Assert.AreEqual ("<root xmlns=\"urn:foo\"><page /></root>",
  303. sw.ToString ());
  304. }
  305. [Test]
  306. // http://lists.ximian.com/pipermail/mono-devel-list/2005-November/015812.html
  307. public void WhitespaceHandling ()
  308. {
  309. string ref_out = @"XML
  310. Extensible Markup language
  311. Great stuffs
  312. XSLT
  313. Extensible Markup language
  314. Great stuffs
  315. XPATH
  316. Extensible Markup language
  317. Great stuffs
  318. XSD
  319. Extensible Markup language
  320. Great stuffs
  321. ";
  322. XmlDocument d = new XmlDocument ();
  323. d.Load ("Test/XmlFiles/xsl/91834.xml");
  324. XslTransform t = new XslTransform ();
  325. t.Load ("Test/XmlFiles/xsl/91834.xsl");
  326. StringWriter sw_raw = new StringWriter ();
  327. t.Transform (d, null, sw_raw);
  328. Assert.AreEqual (ref_out, sw_raw.ToString ().Replace ("\r\n", "\n"));
  329. }
  330. // http://support.microsoft.com/default.aspx?scid=kb;en-us;829014
  331. [Test]
  332. public void EmptyNodeSetSort ()
  333. {
  334. string xmlFragment = @"<?xml version=""1.0"" encoding=""utf-8""?>
  335. <EMPLOYEES>
  336. <EMPLOYEE>
  337. <NAME>Steve</NAME>
  338. <DEPT>IT</DEPT>
  339. <SKILL>C++</SKILL>
  340. <SKILL>C#</SKILL>
  341. </EMPLOYEE>
  342. <EMPLOYEE>
  343. <NAME>John</NAME>
  344. <DEPT>IT</DEPT>
  345. <SKILL>VB.NET</SKILL>
  346. <SKILL>SQl Server</SKILL>
  347. </EMPLOYEE>
  348. </EMPLOYEES>";
  349. string xsltFragment = @"<?xml version=""1.0""?>
  350. <xsl:stylesheet version=""1.0"" xmlns:xsl=""http://www.w3.org/1999/XSL/Transform"">
  351. <xsl:output omit-xml-declaration=""yes"" />
  352. <xsl:preserve-space elements=""*"" />
  353. <xsl:template match=""/EMPLOYEES"">
  354. <xsl:for-each select=""EMPLOYEE[DEPT='Finance']"">
  355. <xsl:sort select=""NAME""/>
  356. <xsl:value-of select=""NAME""/>
  357. </xsl:for-each>
  358. </xsl:template>
  359. </xsl:stylesheet>";
  360. XmlTextReader xmlRdr = new XmlTextReader (new StringReader (xmlFragment));
  361. XmlTextReader xsltRdr = new XmlTextReader (new StringReader (xsltFragment));
  362. XslTransform stylesheet = new XslTransform ();
  363. stylesheet.Load (xsltRdr, new XmlUrlResolver (), AppDomain.CurrentDomain.Evidence);
  364. StringWriter sw = new StringWriter ();
  365. stylesheet.Transform (new XPathDocument (xmlRdr), new XsltArgumentList (),
  366. sw, new XmlUrlResolver ());
  367. Assert.AreEqual (0, sw.ToString ().Length);
  368. }
  369. // http://support.microsoft.com/default.aspx?scid=kb;en-us;834667
  370. [Test]
  371. public void LocalParameter ()
  372. {
  373. string xsltFragment = @"<?xml version=""1.0"" encoding=""UTF-8"" ?>
  374. <xsl:stylesheet xmlns:xsl=""http://www.w3.org/1999/XSL/Transform"" version=""1.0"">
  375. <xsl:param name=""param1"" select=""'global-param1-default'"" />
  376. <xsl:param name=""param2"" select=""'global-param2-default'"" />
  377. <xsl:output method=""text"" encoding=""ascii"" />
  378. <xsl:template match=""/"">
  379. <xsl:call-template name=""Test"">
  380. <xsl:with-param name=""param1"" select=""'local-param1-arg'"" />
  381. <xsl:with-param name=""param2"" select=""'local-param2-arg'"" />
  382. </xsl:call-template>
  383. </xsl:template>
  384. <xsl:template name=""Test"">
  385. <xsl:param name=""param1"" select=""'local-param1-default'"" />
  386. <xsl:param name=""param2"" select=""'local-param2-default'"" />
  387. <xsl:value-of select=""$param1"" /><xsl:text>/</xsl:text><xsl:value-of select=""$param2"" />
  388. </xsl:template>
  389. </xsl:stylesheet>";
  390. XmlDocument xmlDoc = new XmlDocument ();
  391. xmlDoc.LoadXml ("<dummy />");
  392. XslTransform xsltProcessor = new XslTransform ();
  393. xsltProcessor.Load (new XmlTextReader (new StringReader (xsltFragment)),
  394. new XmlUrlResolver (), AppDomain.CurrentDomain.Evidence);
  395. StringWriter sw = new StringWriter ();
  396. XsltArgumentList xsltArgs = new XsltArgumentList ();
  397. xsltArgs.AddParam ("param1", string.Empty, "global-param1-arg");
  398. xsltProcessor.Transform (xmlDoc, xsltArgs, sw, new XmlUrlResolver ());
  399. Assert.AreEqual ("local-param1-arg/local-param2-arg", sw.ToString ());
  400. }
  401. }
  402. }