test_urlpath.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. # -*- test-case-name: twisted.python.test.test_urlpath -*-
  2. # Copyright (c) Twisted Matrix Laboratories.
  3. # See LICENSE for details.
  4. """
  5. Tests for L{twisted.python.urlpath}.
  6. """
  7. from twisted.trial import unittest
  8. from twisted.python import urlpath
  9. from twisted.python.compat import _PY3
  10. class _BaseURLPathTests(object):
  11. """
  12. Tests for instantiated L{urlpath.URLPath}s.
  13. """
  14. def test_partsAreBytes(self):
  15. """
  16. All of the attributes of L{urlpath.URLPath} should be L{bytes}.
  17. """
  18. self.assertIsInstance(self.path.scheme, bytes)
  19. self.assertIsInstance(self.path.netloc, bytes)
  20. self.assertIsInstance(self.path.path, bytes)
  21. self.assertIsInstance(self.path.query, bytes)
  22. self.assertIsInstance(self.path.fragment, bytes)
  23. def test_strReturnsStr(self):
  24. """
  25. Calling C{str()} with a L{URLPath} will always return a L{str}.
  26. """
  27. self.assertEqual(type(self.path.__str__()), str)
  28. def test_mutabilityWithText(self, stringType=type(u"")):
  29. """
  30. Setting attributes on L{urlpath.URLPath} should change the value
  31. returned by L{str}.
  32. @param stringType: a callable to parameterize this test for different
  33. text types.
  34. @type stringType: 1-argument callable taking L{unicode} and returning
  35. L{str} or L{bytes}.
  36. """
  37. self.path.scheme = stringType(u"https")
  38. self.assertEqual(
  39. str(self.path),
  40. "https://example.com/foo/bar?yes=no&no=yes#footer")
  41. self.path.netloc = stringType(u"another.example.invalid")
  42. self.assertEqual(
  43. str(self.path),
  44. "https://another.example.invalid/foo/bar?yes=no&no=yes#footer")
  45. self.path.path = stringType(u"/hello")
  46. self.assertEqual(
  47. str(self.path),
  48. "https://another.example.invalid/hello?yes=no&no=yes#footer")
  49. self.path.query = stringType(u"alpha=omega&opposites=same")
  50. self.assertEqual(
  51. str(self.path),
  52. "https://another.example.invalid/hello?alpha=omega&opposites=same"
  53. "#footer")
  54. self.path.fragment = stringType(u"header")
  55. self.assertEqual(
  56. str(self.path),
  57. "https://another.example.invalid/hello?alpha=omega&opposites=same"
  58. "#header")
  59. def test_mutabilityWithBytes(self):
  60. """
  61. Same as L{test_mutabilityWithText} but for bytes.
  62. """
  63. self.test_mutabilityWithText(lambda x: x.encode("ascii"))
  64. def test_allAttributesAreBytes(self):
  65. """
  66. A created L{URLPath} has bytes attributes.
  67. """
  68. self.assertIsInstance(self.path.scheme, bytes)
  69. self.assertIsInstance(self.path.netloc, bytes)
  70. self.assertIsInstance(self.path.path, bytes)
  71. self.assertIsInstance(self.path.query, bytes)
  72. self.assertIsInstance(self.path.fragment, bytes)
  73. def test_stringConversion(self):
  74. """
  75. Calling C{str()} with a L{URLPath} will return the same URL that it was
  76. constructed with.
  77. """
  78. self.assertEqual(str(self.path),
  79. "http://example.com/foo/bar?yes=no&no=yes#footer")
  80. def test_childString(self):
  81. """
  82. Calling C{str()} with a C{URLPath.child()} will return a URL which is
  83. the child of the URL it was instantiated with.
  84. """
  85. self.assertEqual(str(self.path.child(b'hello')),
  86. "http://example.com/foo/bar/hello")
  87. self.assertEqual(str(self.path.child(b'hello').child(b'')),
  88. "http://example.com/foo/bar/hello/")
  89. self.assertEqual(str(self.path.child(b'hello', keepQuery=True)),
  90. "http://example.com/foo/bar/hello?yes=no&no=yes")
  91. def test_siblingString(self):
  92. """
  93. Calling C{str()} with a C{URLPath.sibling()} will return a URL which is
  94. the sibling of the URL it was instantiated with.
  95. """
  96. self.assertEqual(str(self.path.sibling(b'baz')),
  97. 'http://example.com/foo/baz')
  98. self.assertEqual(str(self.path.sibling(b'baz', keepQuery=True)),
  99. "http://example.com/foo/baz?yes=no&no=yes")
  100. # The sibling of http://example.com/foo/bar/
  101. # is http://example.comf/foo/bar/baz
  102. # because really we are constructing a sibling of
  103. # http://example.com/foo/bar/index.html
  104. self.assertEqual(str(self.path.child(b'').sibling(b'baz')),
  105. 'http://example.com/foo/bar/baz')
  106. def test_parentString(self):
  107. """
  108. Calling C{str()} with a C{URLPath.parent()} will return a URL which is
  109. the parent of the URL it was instantiated with.
  110. """
  111. # .parent() should be equivalent to '..'
  112. # 'foo' is the current directory, '/' is the parent directory
  113. self.assertEqual(str(self.path.parent()),
  114. 'http://example.com/')
  115. self.assertEqual(str(self.path.parent(keepQuery=True)),
  116. 'http://example.com/?yes=no&no=yes')
  117. self.assertEqual(str(self.path.child(b'').parent()),
  118. 'http://example.com/foo/')
  119. self.assertEqual(str(self.path.child(b'baz').parent()),
  120. 'http://example.com/foo/')
  121. self.assertEqual(
  122. str(self.path.parent().parent().parent().parent().parent()),
  123. 'http://example.com/')
  124. def test_hereString(self):
  125. """
  126. Calling C{str()} with a C{URLPath.here()} will return a URL which is
  127. the URL that it was instantiated with, without any file, query, or
  128. fragment.
  129. """
  130. # .here() should be equivalent to '.'
  131. self.assertEqual(str(self.path.here()), 'http://example.com/foo/')
  132. self.assertEqual(str(self.path.here(keepQuery=True)),
  133. 'http://example.com/foo/?yes=no&no=yes')
  134. self.assertEqual(str(self.path.child(b'').here()),
  135. 'http://example.com/foo/bar/')
  136. def test_doubleSlash(self):
  137. """
  138. Calling L{urlpath.URLPath.click} on a L{urlpath.URLPath} with a
  139. trailing slash with a relative URL containing a leading slash will
  140. result in a URL with a single slash at the start of the path portion.
  141. """
  142. self.assertEqual(
  143. str(self.path.click(b"/hello/world")).encode("ascii"),
  144. b"http://example.com/hello/world"
  145. )
  146. def test_pathList(self):
  147. """
  148. L{urlpath.URLPath.pathList} returns a L{list} of L{bytes}.
  149. """
  150. self.assertEqual(
  151. self.path.child(b"%00%01%02").pathList(),
  152. [b"", b"foo", b"bar", b"%00%01%02"]
  153. )
  154. # Just testing that the 'copy' argument exists for compatibility; it
  155. # was originally provided for performance reasons, and its behavioral
  156. # contract is kind of nonsense (where is the state shared? who with?)
  157. # so it doesn't actually *do* anything any more.
  158. self.assertEqual(
  159. self.path.child(b"%00%01%02").pathList(copy=False),
  160. [b"", b"foo", b"bar", b"%00%01%02"]
  161. )
  162. self.assertEqual(
  163. self.path.child(b"%00%01%02").pathList(unquote=True),
  164. [b"", b"foo", b"bar", b"\x00\x01\x02"]
  165. )
  166. class BytesURLPathTests(_BaseURLPathTests, unittest.TestCase):
  167. """
  168. Tests for interacting with a L{URLPath} created with C{fromBytes}.
  169. """
  170. def setUp(self):
  171. self.path = urlpath.URLPath.fromBytes(
  172. b"http://example.com/foo/bar?yes=no&no=yes#footer")
  173. def test_mustBeBytes(self):
  174. """
  175. L{URLPath.fromBytes} must take a L{bytes} argument.
  176. """
  177. with self.assertRaises(ValueError):
  178. urlpath.URLPath.fromBytes(None)
  179. with self.assertRaises(ValueError):
  180. urlpath.URLPath.fromBytes(u"someurl")
  181. def test_withoutArguments(self):
  182. """
  183. An instantiation with no arguments creates a usable L{URLPath} with
  184. default arguments.
  185. """
  186. url = urlpath.URLPath()
  187. self.assertEqual(str(url), "http://localhost/")
  188. def test_partialArguments(self):
  189. """
  190. Leaving some optional arguments unfilled makes a L{URLPath} with those
  191. optional arguments filled with defaults.
  192. """
  193. # Not a "full" URL given to fromBytes, no /
  194. # / is filled in
  195. url = urlpath.URLPath.fromBytes(b"http://google.com")
  196. self.assertEqual(url.scheme, b"http")
  197. self.assertEqual(url.netloc, b"google.com")
  198. self.assertEqual(url.path, b"/")
  199. self.assertEqual(url.fragment, b"")
  200. self.assertEqual(url.query, b"")
  201. self.assertEqual(str(url), "http://google.com/")
  202. def test_nonASCIIBytes(self):
  203. """
  204. L{URLPath.fromBytes} can interpret non-ASCII bytes as percent-encoded
  205. """
  206. url = urlpath.URLPath.fromBytes(b"http://example.com/\xff\x00")
  207. self.assertEqual(str(url), "http://example.com/%FF%00")
  208. class StringURLPathTests(_BaseURLPathTests, unittest.TestCase):
  209. """
  210. Tests for interacting with a L{URLPath} created with C{fromString} and a
  211. L{str} argument.
  212. """
  213. def setUp(self):
  214. self.path = urlpath.URLPath.fromString(
  215. "http://example.com/foo/bar?yes=no&no=yes#footer")
  216. def test_mustBeStr(self):
  217. """
  218. C{URLPath.fromString} must take a L{str} or L{unicode} argument.
  219. """
  220. with self.assertRaises(ValueError):
  221. urlpath.URLPath.fromString(None)
  222. if _PY3:
  223. with self.assertRaises(ValueError):
  224. urlpath.URLPath.fromString(b"someurl")
  225. class UnicodeURLPathTests(_BaseURLPathTests, unittest.TestCase):
  226. """
  227. Tests for interacting with a L{URLPath} created with C{fromString} and a
  228. L{unicode} argument.
  229. """
  230. def setUp(self):
  231. self.path = urlpath.URLPath.fromString(
  232. u"http://example.com/foo/bar?yes=no&no=yes#footer")
  233. def test_nonASCIICharacters(self):
  234. """
  235. L{URLPath.fromString} can load non-ASCII characters.
  236. """
  237. url = urlpath.URLPath.fromString(u"http://example.com/\xff\x00")
  238. self.assertEqual(str(url), "http://example.com/%C3%BF%00")