diff --git a/spring-test-mvc/src/main/java/org/springframework/test/util/XpathExpectationsHelper.java b/spring-test-mvc/src/main/java/org/springframework/test/util/XpathExpectationsHelper.java index 1989a80889..a00b7b31d0 100644 --- a/spring-test-mvc/src/main/java/org/springframework/test/util/XpathExpectationsHelper.java +++ b/spring-test-mvc/src/main/java/org/springframework/test/util/XpathExpectationsHelper.java @@ -16,9 +16,6 @@ package org.springframework.test.util; -import static org.springframework.test.util.AssertionErrors.*; -import static org.springframework.test.util.MatcherAssertionErrors.*; - import java.io.StringReader; import java.util.Collections; import java.util.Map; @@ -33,12 +30,16 @@ import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import org.hamcrest.Matcher; +import org.springframework.util.CollectionUtils; import org.springframework.util.xml.SimpleNamespaceContext; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; +import static org.springframework.test.util.AssertionErrors.*; +import static org.springframework.test.util.MatcherAssertionErrors.*; + /** * A helper class for applying assertions via XPath expressions. * @@ -51,6 +52,8 @@ public class XpathExpectationsHelper { private final XPathExpression xpathExpression; + private final boolean hasNamespaces; + /** * Class constructor. @@ -66,6 +69,7 @@ public class XpathExpectationsHelper { this.expression = String.format(expression, args); this.xpathExpression = compileXpathExpression(this.expression, namespaces); + this.hasNamespaces = !CollectionUtils.isEmpty(namespaces); } private XPathExpression compileXpathExpression(String expression, Map namespaces) @@ -104,7 +108,7 @@ public class XpathExpectationsHelper { */ protected Document parseXmlString(String xml) throws Exception { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - factory.setNamespaceAware(true); + factory.setNamespaceAware(this.hasNamespaces); DocumentBuilder documentBuilder = factory.newDocumentBuilder(); InputSource inputSource = new InputSource(new StringReader(xml)); Document document = documentBuilder.parse(inputSource); diff --git a/spring-test-mvc/src/test/java/org/springframework/test/web/servlet/samples/standalone/resultmatchers/XpathAssertionTests.java b/spring-test-mvc/src/test/java/org/springframework/test/web/servlet/samples/standalone/resultmatchers/XpathAssertionTests.java index 7c941d652b..34ef829bf9 100644 --- a/spring-test-mvc/src/test/java/org/springframework/test/web/servlet/samples/standalone/resultmatchers/XpathAssertionTests.java +++ b/spring-test-mvc/src/test/java/org/springframework/test/web/servlet/samples/standalone/resultmatchers/XpathAssertionTests.java @@ -16,17 +16,6 @@ package org.springframework.test.web.servlet.samples.standalone.resultmatchers; -import static org.hamcrest.Matchers.closeTo; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.notNullValue; -import static org.hamcrest.Matchers.nullValue; -import static org.hamcrest.Matchers.startsWith; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.xpath; -import static org.springframework.test.web.servlet.setup.MockMvcBuilders.standaloneSetup; - import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -47,6 +36,12 @@ import org.springframework.test.web.servlet.MockMvc; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; +import static org.hamcrest.Matchers.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; +import static org.springframework.test.web.servlet.setup.MockMvcBuilders.*; +import static org.springframework.web.bind.annotation.RequestMethod.*; + /** * Examples of expectations on XML response content with XPath expressions. * @@ -57,7 +52,7 @@ import org.springframework.web.bind.annotation.ResponseBody; */ public class XpathAssertionTests { - private static final Map NS = + private static final Map musicNamespace = Collections.singletonMap("ns", "http://example.org/music/people"); private MockMvc mockMvc; @@ -78,13 +73,13 @@ public class XpathAssertionTests { String performer = "/ns:people/performers/performer[%s]"; this.mockMvc.perform(get("/music/people")) - .andExpect(xpath(composer, NS, 1).exists()) - .andExpect(xpath(composer, NS, 2).exists()) - .andExpect(xpath(composer, NS, 3).exists()) - .andExpect(xpath(composer, NS, 4).exists()) - .andExpect(xpath(performer, NS, 1).exists()) - .andExpect(xpath(performer, NS, 2).exists()) - .andExpect(xpath(composer, NS, 1).node(notNullValue())); + .andExpect(xpath(composer, musicNamespace, 1).exists()) + .andExpect(xpath(composer, musicNamespace, 2).exists()) + .andExpect(xpath(composer, musicNamespace, 3).exists()) + .andExpect(xpath(composer, musicNamespace, 4).exists()) + .andExpect(xpath(performer, musicNamespace, 1).exists()) + .andExpect(xpath(performer, musicNamespace, 2).exists()) + .andExpect(xpath(composer, musicNamespace, 1).node(notNullValue())); } @Test @@ -94,11 +89,11 @@ public class XpathAssertionTests { String performer = "/ns:people/performers/performer[%s]"; this.mockMvc.perform(get("/music/people")) - .andExpect(xpath(composer, NS, 0).doesNotExist()) - .andExpect(xpath(composer, NS, 5).doesNotExist()) - .andExpect(xpath(performer, NS, 0).doesNotExist()) - .andExpect(xpath(performer, NS, 3).doesNotExist()) - .andExpect(xpath(composer, NS, 0).node(nullValue())); + .andExpect(xpath(composer, musicNamespace, 0).doesNotExist()) + .andExpect(xpath(composer, musicNamespace, 5).doesNotExist()) + .andExpect(xpath(performer, musicNamespace, 0).doesNotExist()) + .andExpect(xpath(performer, musicNamespace, 3).doesNotExist()) + .andExpect(xpath(composer, musicNamespace, 0).node(nullValue())); } @Test @@ -108,15 +103,15 @@ public class XpathAssertionTests { String performerName = "/ns:people/performers/performer[%s]/name"; this.mockMvc.perform(get("/music/people")) - .andExpect(xpath(composerName, NS, 1).string("Johann Sebastian Bach")) - .andExpect(xpath(composerName, NS, 2).string("Johannes Brahms")) - .andExpect(xpath(composerName, NS, 3).string("Edvard Grieg")) - .andExpect(xpath(composerName, NS, 4).string("Robert Schumann")) - .andExpect(xpath(performerName, NS, 1).string("Vladimir Ashkenazy")) - .andExpect(xpath(performerName, NS, 2).string("Yehudi Menuhin")) - .andExpect(xpath(composerName, NS, 1).string(equalTo("Johann Sebastian Bach"))) // Hamcrest.. - .andExpect(xpath(composerName, NS, 1).string(startsWith("Johann"))) - .andExpect(xpath(composerName, NS, 1).string(notNullValue())); + .andExpect(xpath(composerName, musicNamespace, 1).string("Johann Sebastian Bach")) + .andExpect(xpath(composerName, musicNamespace, 2).string("Johannes Brahms")) + .andExpect(xpath(composerName, musicNamespace, 3).string("Edvard Grieg")) + .andExpect(xpath(composerName, musicNamespace, 4).string("Robert Schumann")) + .andExpect(xpath(performerName, musicNamespace, 1).string("Vladimir Ashkenazy")) + .andExpect(xpath(performerName, musicNamespace, 2).string("Yehudi Menuhin")) + .andExpect(xpath(composerName, musicNamespace, 1).string(equalTo("Johann Sebastian Bach"))) // Hamcrest.. + .andExpect(xpath(composerName, musicNamespace, 1).string(startsWith("Johann"))) + .andExpect(xpath(composerName, musicNamespace, 1).string(notNullValue())); } @Test @@ -125,12 +120,12 @@ public class XpathAssertionTests { String composerDouble = "/ns:people/composers/composer[%s]/someDouble"; this.mockMvc.perform(get("/music/people")) - .andExpect(xpath(composerDouble, NS, 1).number(21d)) - .andExpect(xpath(composerDouble, NS, 2).number(.0025)) - .andExpect(xpath(composerDouble, NS, 3).number(1.6035)) - .andExpect(xpath(composerDouble, NS, 4).number(Double.NaN)) - .andExpect(xpath(composerDouble, NS, 1).number(equalTo(21d))) // Hamcrest.. - .andExpect(xpath(composerDouble, NS, 3).number(closeTo(1.6, .01))); + .andExpect(xpath(composerDouble, musicNamespace, 1).number(21d)) + .andExpect(xpath(composerDouble, musicNamespace, 2).number(.0025)) + .andExpect(xpath(composerDouble, musicNamespace, 3).number(1.6035)) + .andExpect(xpath(composerDouble, musicNamespace, 4).number(Double.NaN)) + .andExpect(xpath(composerDouble, musicNamespace, 1).number(equalTo(21d))) // Hamcrest.. + .andExpect(xpath(composerDouble, musicNamespace, 3).number(closeTo(1.6, .01))); } @Test @@ -139,20 +134,36 @@ public class XpathAssertionTests { String performerBooleanValue = "/ns:people/performers/performer[%s]/someBoolean"; this.mockMvc.perform(get("/music/people")) - .andExpect(xpath(performerBooleanValue, NS, 1).booleanValue(false)) - .andExpect(xpath(performerBooleanValue, NS, 2).booleanValue(true)); + .andExpect(xpath(performerBooleanValue, musicNamespace, 1).booleanValue(false)) + .andExpect(xpath(performerBooleanValue, musicNamespace, 2).booleanValue(true)); } @Test public void testNodeCount() throws Exception { this.mockMvc.perform(get("/music/people")) - .andExpect(xpath("/ns:people/composers/composer", NS).nodeCount(4)) - .andExpect(xpath("/ns:people/performers/performer", NS).nodeCount(2)) - .andExpect(xpath("/ns:people/composers/composer", NS).nodeCount(equalTo(4))) // Hamcrest.. - .andExpect(xpath("/ns:people/performers/performer", NS).nodeCount(equalTo(2))); + .andExpect(xpath("/ns:people/composers/composer", musicNamespace).nodeCount(4)) + .andExpect(xpath("/ns:people/performers/performer", musicNamespace).nodeCount(2)) + .andExpect(xpath("/ns:people/composers/composer", musicNamespace).nodeCount(equalTo(4))) // Hamcrest.. + .andExpect(xpath("/ns:people/performers/performer", musicNamespace).nodeCount(equalTo(2))); + } + + // SPR-10704 + + @Test + public void testFeedWithLinefeedChars() throws Exception { + +// Map namespace = Collections.singletonMap("ns", ""); + + standaloneSetup(new BlogFeedController()).build() + .perform(get("/blog.atom").accept(MediaType.APPLICATION_ATOM_XML)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_ATOM_XML)) + .andExpect(xpath("//feed/title").string("Test Feed")) + .andExpect(xpath("//feed/icon").string("http://www.example.com/favicon.ico")); } + @Controller private static class MusicController { @@ -203,4 +214,19 @@ public class XpathAssertionTests { } } + + @Controller + public class BlogFeedController { + + @RequestMapping(value="/blog.atom", method = { GET, HEAD }) + @ResponseBody + public String listPublishedPosts() { + return "\r\n" + + "\r\n" + + " Test Feed\r\n" + + " http://www.example.com/favicon.ico\r\n" + + "\r\n\r\n"; + } + } + }