test_qrcode.py 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. import warnings
  2. import six
  3. import sys
  4. import qrcode
  5. import qrcode.util
  6. import qrcode.image.svg
  7. try:
  8. import qrcode.image.pure
  9. import pymaging_png # ensure that PNG support is installed
  10. except ImportError: # pragma: no cover
  11. pymaging_png = None
  12. import qrcode
  13. from qrcode.image.base import BaseImage
  14. from qrcode.exceptions import DataOverflowError
  15. from qrcode.util import (
  16. QRData, MODE_NUMBER, MODE_ALPHA_NUM, MODE_8BIT_BYTE)
  17. from qrcode.tests.svg import SvgImageWhite
  18. try:
  19. import unittest2 as unittest
  20. except ImportError:
  21. import unittest
  22. try:
  23. from unittest import mock
  24. except ImportError:
  25. import mock
  26. UNICODE_TEXT = u'\u03b1\u03b2\u03b3'
  27. class QRCodeTests(unittest.TestCase):
  28. def test_basic(self):
  29. qr = qrcode.QRCode(version=1)
  30. qr.add_data('a')
  31. qr.make(fit=False)
  32. def test_large(self):
  33. qr = qrcode.QRCode(version=27)
  34. qr.add_data('a')
  35. qr.make(fit=False)
  36. def test_invalid_version(self):
  37. qr = qrcode.QRCode(version=41)
  38. self.assertRaises(ValueError, qr.make, fit=False)
  39. def test_overflow(self):
  40. qr = qrcode.QRCode(version=1)
  41. qr.add_data('abcdefghijklmno')
  42. self.assertRaises(DataOverflowError, qr.make, fit=False)
  43. def test_add_qrdata(self):
  44. qr = qrcode.QRCode(version=1)
  45. data = QRData('a')
  46. qr.add_data(data)
  47. qr.make(fit=False)
  48. def test_fit(self):
  49. qr = qrcode.QRCode()
  50. qr.add_data('a')
  51. qr.make()
  52. self.assertEqual(qr.version, 1)
  53. qr.add_data('bcdefghijklmno')
  54. qr.make()
  55. self.assertEqual(qr.version, 2)
  56. def test_mode_number(self):
  57. qr = qrcode.QRCode()
  58. qr.add_data('1234567890123456789012345678901234', optimize=0)
  59. qr.make()
  60. self.assertEqual(qr.version, 1)
  61. self.assertEqual(qr.data_list[0].mode, MODE_NUMBER)
  62. def test_mode_alpha(self):
  63. qr = qrcode.QRCode()
  64. qr.add_data('ABCDEFGHIJ1234567890', optimize=0)
  65. qr.make()
  66. self.assertEqual(qr.version, 1)
  67. self.assertEqual(qr.data_list[0].mode, MODE_ALPHA_NUM)
  68. def test_regression_mode_comma(self):
  69. qr = qrcode.QRCode()
  70. qr.add_data(',', optimize=0)
  71. qr.make()
  72. self.assertEqual(qr.data_list[0].mode, MODE_8BIT_BYTE)
  73. def test_mode_8bit(self):
  74. qr = qrcode.QRCode()
  75. qr.add_data(u'abcABC' + UNICODE_TEXT, optimize=0)
  76. qr.make()
  77. self.assertEqual(qr.version, 1)
  78. self.assertEqual(qr.data_list[0].mode, MODE_8BIT_BYTE)
  79. def test_mode_8bit_newline(self):
  80. qr = qrcode.QRCode()
  81. qr.add_data('ABCDEFGHIJ1234567890\n', optimize=0)
  82. qr.make()
  83. self.assertEqual(qr.data_list[0].mode, MODE_8BIT_BYTE)
  84. def test_render_pil(self):
  85. qr = qrcode.QRCode()
  86. qr.add_data(UNICODE_TEXT)
  87. img = qr.make_image()
  88. img.save(six.BytesIO())
  89. def test_qrcode_bad_factory(self):
  90. self.assertRaises(
  91. TypeError, qrcode.QRCode, image_factory='not_BaseImage')
  92. self.assertRaises(
  93. AssertionError, qrcode.QRCode, image_factory=dict)
  94. def test_qrcode_factory(self):
  95. class MockFactory(BaseImage):
  96. drawrect = mock.Mock()
  97. qr = qrcode.QRCode(image_factory=MockFactory)
  98. qr.add_data(UNICODE_TEXT)
  99. qr.make_image()
  100. self.assertTrue(MockFactory.drawrect.called)
  101. def test_render_svg(self):
  102. qr = qrcode.QRCode()
  103. qr.add_data(UNICODE_TEXT)
  104. img = qr.make_image(image_factory=qrcode.image.svg.SvgImage)
  105. img.save(six.BytesIO())
  106. def test_render_svg_path(self):
  107. qr = qrcode.QRCode()
  108. qr.add_data(UNICODE_TEXT)
  109. img = qr.make_image(image_factory=qrcode.image.svg.SvgPathImage)
  110. img.save(six.BytesIO())
  111. def test_render_svg_fragment(self):
  112. qr = qrcode.QRCode()
  113. qr.add_data(UNICODE_TEXT)
  114. img = qr.make_image(image_factory=qrcode.image.svg.SvgFragmentImage)
  115. img.save(six.BytesIO())
  116. def test_render_svg_with_background(self):
  117. qr = qrcode.QRCode()
  118. qr.add_data(UNICODE_TEXT)
  119. img = qr.make_image(image_factory=SvgImageWhite)
  120. img.save(six.BytesIO())
  121. @unittest.skipIf(not pymaging_png, "Requires pymaging with PNG support")
  122. def test_render_pymaging_png(self):
  123. qr = qrcode.QRCode()
  124. qr.add_data(UNICODE_TEXT)
  125. img = qr.make_image(image_factory=qrcode.image.pure.PymagingImage)
  126. with warnings.catch_warnings():
  127. if six.PY3:
  128. warnings.simplefilter('ignore', DeprecationWarning)
  129. img.save(six.BytesIO())
  130. @unittest.skipIf(not pymaging_png, "Requires pymaging")
  131. def test_render_pymaging_png_bad_kind(self):
  132. qr = qrcode.QRCode()
  133. qr.add_data(UNICODE_TEXT)
  134. img = qr.make_image(image_factory=qrcode.image.pure.PymagingImage)
  135. self.assertRaises(ValueError, img.save, six.BytesIO(), kind='FISH')
  136. def test_optimize(self):
  137. qr = qrcode.QRCode()
  138. text = 'A1abc12345def1HELLOa'
  139. qr.add_data(text, optimize=4)
  140. qr.make()
  141. self.assertEqual(
  142. [d.mode for d in qr.data_list],
  143. [
  144. MODE_8BIT_BYTE, MODE_NUMBER, MODE_8BIT_BYTE, MODE_ALPHA_NUM,
  145. MODE_8BIT_BYTE
  146. ]
  147. )
  148. self.assertEqual(qr.version, 2)
  149. def test_optimize_short(self):
  150. qr = qrcode.QRCode()
  151. text = 'A1abc1234567def1HELLOa'
  152. qr.add_data(text, optimize=7)
  153. qr.make()
  154. self.assertEqual(len(qr.data_list), 3)
  155. self.assertEqual(
  156. [d.mode for d in qr.data_list],
  157. [MODE_8BIT_BYTE, MODE_NUMBER, MODE_8BIT_BYTE]
  158. )
  159. self.assertEqual(qr.version, 2)
  160. def test_optimize_size(self):
  161. text = 'A1abc12345123451234512345def1HELLOHELLOHELLOHELLOa' * 5
  162. qr = qrcode.QRCode()
  163. qr.add_data(text)
  164. qr.make()
  165. self.assertEqual(qr.version, 10)
  166. qr = qrcode.QRCode()
  167. qr.add_data(text, optimize=0)
  168. qr.make()
  169. self.assertEqual(qr.version, 11)
  170. def test_qrdata_repr(self):
  171. data = b'hello'
  172. data_obj = qrcode.util.QRData(data)
  173. self.assertEqual(repr(data_obj), repr(data))
  174. def test_print_ascii_stdout(self):
  175. qr = qrcode.QRCode()
  176. stdout_encoding = sys.stdout.encoding
  177. with mock.patch('sys.stdout') as fake_stdout:
  178. # Python 2.6 needs sys.stdout.encoding to be a real string.
  179. sys.stdout.encoding = stdout_encoding
  180. fake_stdout.isatty.return_value = None
  181. self.assertRaises(OSError, qr.print_ascii, tty=True)
  182. self.assertTrue(fake_stdout.isatty.called)
  183. def test_print_ascii(self):
  184. qr = qrcode.QRCode(border=0)
  185. f = six.StringIO()
  186. qr.print_ascii(out=f)
  187. printed = f.getvalue()
  188. f.close()
  189. expected = u'\u2588\u2580\u2580\u2580\u2580\u2580\u2588'
  190. self.assertEqual(printed[:len(expected)], expected)
  191. f = six.StringIO()
  192. f.isatty = lambda: True
  193. qr.print_ascii(out=f, tty=True)
  194. printed = f.getvalue()
  195. f.close()
  196. expected = (
  197. u'\x1b[48;5;232m\x1b[38;5;255m' +
  198. u'\xa0\u2584\u2584\u2584\u2584\u2584\xa0')
  199. self.assertEqual(printed[:len(expected)], expected)
  200. def test_print_tty_stdout(self):
  201. qr = qrcode.QRCode()
  202. with mock.patch('sys.stdout') as fake_stdout:
  203. fake_stdout.isatty.return_value = None
  204. self.assertRaises(OSError, qr.print_tty)
  205. self.assertTrue(fake_stdout.isatty.called)
  206. def test_print_tty(self):
  207. qr = qrcode.QRCode()
  208. f = six.StringIO()
  209. f.isatty = lambda: True
  210. qr.print_tty(out=f)
  211. printed = f.getvalue()
  212. f.close()
  213. BOLD_WHITE_BG = '\x1b[1;47m'
  214. BLACK_BG = '\x1b[40m'
  215. WHITE_BLOCK = BOLD_WHITE_BG + ' ' + BLACK_BG
  216. EOL = '\x1b[0m\n'
  217. expected = (
  218. BOLD_WHITE_BG + ' '*23 + EOL +
  219. WHITE_BLOCK + ' '*7 + WHITE_BLOCK)
  220. self.assertEqual(printed[:len(expected)], expected)
  221. def test_get_matrix(self):
  222. qr = qrcode.QRCode(border=0)
  223. qr.add_data('1')
  224. self.assertEqual(qr.get_matrix(), qr.modules)
  225. def test_get_matrix_border(self):
  226. qr = qrcode.QRCode(border=1)
  227. qr.add_data('1')
  228. matrix = [row[1:-1] for row in qr.get_matrix()[1:-1]]
  229. self.assertEqual(matrix, qr.modules)
  230. def test_negative_size_at_construction(self):
  231. self.assertRaises(ValueError, qrcode.QRCode, box_size=-1)
  232. def test_negative_size_at_usage(self):
  233. qr = qrcode.QRCode()
  234. qr.box_size = -1
  235. self.assertRaises(ValueError, qr.make_image)
  236. class ShortcutTest(unittest.TestCase):
  237. def runTest(self):
  238. qrcode.make('image')