split_penalty_test.py 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. # Copyright 2015 Google Inc. All Rights Reserved.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. """Tests for yapf.split_penalty."""
  15. import sys
  16. import textwrap
  17. import unittest
  18. from yapf_third_party._ylib2to3 import pytree
  19. from yapf.pytree import pytree_utils
  20. from yapf.pytree import pytree_visitor
  21. from yapf.pytree import split_penalty
  22. from yapf.yapflib import style
  23. from yapftests import yapf_test_helper
  24. UNBREAKABLE = split_penalty.UNBREAKABLE
  25. VERY_STRONGLY_CONNECTED = split_penalty.VERY_STRONGLY_CONNECTED
  26. DOTTED_NAME = split_penalty.DOTTED_NAME
  27. STRONGLY_CONNECTED = split_penalty.STRONGLY_CONNECTED
  28. class SplitPenaltyTest(yapf_test_helper.YAPFTest):
  29. @classmethod
  30. def setUpClass(cls):
  31. style.SetGlobalStyle(style.CreateYapfStyle())
  32. def _ParseAndComputePenalties(self, code, dumptree=False):
  33. """Parses the code and computes split penalties.
  34. Arguments:
  35. code: code to parse as a string
  36. dumptree: if True, the parsed pytree (after penalty assignment) is dumped
  37. to stderr. Useful for debugging.
  38. Returns:
  39. Parse tree.
  40. """
  41. tree = pytree_utils.ParseCodeToTree(code)
  42. split_penalty.ComputeSplitPenalties(tree)
  43. if dumptree:
  44. pytree_visitor.DumpPyTree(tree, target_stream=sys.stderr)
  45. return tree
  46. def _CheckPenalties(self, tree, list_of_expected):
  47. """Check that the tokens in the tree have the correct penalties.
  48. Args:
  49. tree: the pytree.
  50. list_of_expected: list of (name, penalty) pairs. Non-semantic tokens are
  51. filtered out from the expected values.
  52. """
  53. def FlattenRec(tree):
  54. if pytree_utils.NodeName(tree) in pytree_utils.NONSEMANTIC_TOKENS:
  55. return []
  56. if isinstance(tree, pytree.Leaf):
  57. return [(tree.value,
  58. pytree_utils.GetNodeAnnotation(
  59. tree, pytree_utils.Annotation.SPLIT_PENALTY))]
  60. nodes = []
  61. for node in tree.children:
  62. nodes += FlattenRec(node)
  63. return nodes
  64. self.assertEqual(list_of_expected, FlattenRec(tree))
  65. def testUnbreakable(self):
  66. # Test function definitions.
  67. code = textwrap.dedent(r"""
  68. def foo(x):
  69. pass
  70. """)
  71. tree = self._ParseAndComputePenalties(code)
  72. self._CheckPenalties(tree, [
  73. ('def', None),
  74. ('foo', UNBREAKABLE),
  75. ('(', UNBREAKABLE),
  76. ('x', None),
  77. (')', STRONGLY_CONNECTED),
  78. (':', UNBREAKABLE),
  79. ('pass', None),
  80. ])
  81. # Test function definition with trailing comment.
  82. code = textwrap.dedent(r"""
  83. def foo(x): # trailing comment
  84. pass
  85. """)
  86. tree = self._ParseAndComputePenalties(code)
  87. self._CheckPenalties(tree, [
  88. ('def', None),
  89. ('foo', UNBREAKABLE),
  90. ('(', UNBREAKABLE),
  91. ('x', None),
  92. (')', STRONGLY_CONNECTED),
  93. (':', UNBREAKABLE),
  94. ('pass', None),
  95. ])
  96. # Test class definitions.
  97. code = textwrap.dedent(r"""
  98. class A:
  99. pass
  100. class B(A):
  101. pass
  102. """)
  103. tree = self._ParseAndComputePenalties(code)
  104. self._CheckPenalties(tree, [
  105. ('class', None),
  106. ('A', UNBREAKABLE),
  107. (':', UNBREAKABLE),
  108. ('pass', None),
  109. ('class', None),
  110. ('B', UNBREAKABLE),
  111. ('(', UNBREAKABLE),
  112. ('A', None),
  113. (')', None),
  114. (':', UNBREAKABLE),
  115. ('pass', None),
  116. ])
  117. # Test lambda definitions.
  118. code = textwrap.dedent(r"""
  119. lambda a, b: None
  120. """)
  121. tree = self._ParseAndComputePenalties(code)
  122. self._CheckPenalties(tree, [
  123. ('lambda', None),
  124. ('a', VERY_STRONGLY_CONNECTED),
  125. (',', VERY_STRONGLY_CONNECTED),
  126. ('b', VERY_STRONGLY_CONNECTED),
  127. (':', VERY_STRONGLY_CONNECTED),
  128. ('None', VERY_STRONGLY_CONNECTED),
  129. ])
  130. # Test dotted names.
  131. code = textwrap.dedent(r"""
  132. import a.b.c
  133. """)
  134. tree = self._ParseAndComputePenalties(code)
  135. self._CheckPenalties(tree, [
  136. ('import', None),
  137. ('a', None),
  138. ('.', UNBREAKABLE),
  139. ('b', UNBREAKABLE),
  140. ('.', UNBREAKABLE),
  141. ('c', UNBREAKABLE),
  142. ])
  143. def testStronglyConnected(self):
  144. # Test dictionary keys.
  145. code = textwrap.dedent(r"""
  146. a = {
  147. 'x': 42,
  148. y(lambda a: 23): 37,
  149. }
  150. """)
  151. tree = self._ParseAndComputePenalties(code)
  152. self._CheckPenalties(tree, [
  153. ('a', None),
  154. ('=', None),
  155. ('{', None),
  156. ("'x'", None),
  157. (':', STRONGLY_CONNECTED),
  158. ('42', None),
  159. (',', None),
  160. ('y', None),
  161. ('(', UNBREAKABLE),
  162. ('lambda', STRONGLY_CONNECTED),
  163. ('a', VERY_STRONGLY_CONNECTED),
  164. (':', VERY_STRONGLY_CONNECTED),
  165. ('23', VERY_STRONGLY_CONNECTED),
  166. (')', VERY_STRONGLY_CONNECTED),
  167. (':', STRONGLY_CONNECTED),
  168. ('37', None),
  169. (',', None),
  170. ('}', None),
  171. ])
  172. # Test list comprehension.
  173. code = textwrap.dedent(r"""
  174. [a for a in foo if a.x == 37]
  175. """)
  176. tree = self._ParseAndComputePenalties(code)
  177. self._CheckPenalties(tree, [
  178. ('[', None),
  179. ('a', None),
  180. ('for', 0),
  181. ('a', STRONGLY_CONNECTED),
  182. ('in', STRONGLY_CONNECTED),
  183. ('foo', STRONGLY_CONNECTED),
  184. ('if', 0),
  185. ('a', STRONGLY_CONNECTED),
  186. ('.', VERY_STRONGLY_CONNECTED),
  187. ('x', DOTTED_NAME),
  188. ('==', STRONGLY_CONNECTED),
  189. ('37', STRONGLY_CONNECTED),
  190. (']', None),
  191. ])
  192. def testFuncCalls(self):
  193. code = 'foo(1, 2, 3)\n'
  194. tree = self._ParseAndComputePenalties(code)
  195. self._CheckPenalties(tree, [
  196. ('foo', None),
  197. ('(', UNBREAKABLE),
  198. ('1', None),
  199. (',', UNBREAKABLE),
  200. ('2', None),
  201. (',', UNBREAKABLE),
  202. ('3', None),
  203. (')', VERY_STRONGLY_CONNECTED),
  204. ])
  205. # Now a method call, which has more than one trailer
  206. code = 'foo.bar.baz(1, 2, 3)\n'
  207. tree = self._ParseAndComputePenalties(code)
  208. self._CheckPenalties(tree, [
  209. ('foo', None),
  210. ('.', VERY_STRONGLY_CONNECTED),
  211. ('bar', DOTTED_NAME),
  212. ('.', VERY_STRONGLY_CONNECTED),
  213. ('baz', DOTTED_NAME),
  214. ('(', STRONGLY_CONNECTED),
  215. ('1', None),
  216. (',', UNBREAKABLE),
  217. ('2', None),
  218. (',', UNBREAKABLE),
  219. ('3', None),
  220. (')', VERY_STRONGLY_CONNECTED),
  221. ])
  222. # Test single generator argument.
  223. code = 'max(i for i in xrange(10))\n'
  224. tree = self._ParseAndComputePenalties(code)
  225. self._CheckPenalties(tree, [
  226. ('max', None),
  227. ('(', UNBREAKABLE),
  228. ('i', 0),
  229. ('for', 0),
  230. ('i', STRONGLY_CONNECTED),
  231. ('in', STRONGLY_CONNECTED),
  232. ('xrange', STRONGLY_CONNECTED),
  233. ('(', UNBREAKABLE),
  234. ('10', STRONGLY_CONNECTED),
  235. (')', VERY_STRONGLY_CONNECTED),
  236. (')', VERY_STRONGLY_CONNECTED),
  237. ])
  238. if __name__ == '__main__':
  239. unittest.main()