re2_test.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. # Copyright 2019 The RE2 Authors. All Rights Reserved.
  2. # Use of this source code is governed by a BSD-style
  3. # license that can be found in the LICENSE file.
  4. """Tests for google3.third_party.re2.python.re2."""
  5. import collections
  6. import pickle
  7. import re
  8. from absl.testing import absltest
  9. from absl.testing import parameterized
  10. import re2
  11. class OptionsTest(parameterized.TestCase):
  12. @parameterized.parameters(*re2.Options.NAMES)
  13. def test_option(self, name):
  14. options = re2.Options()
  15. value = getattr(options, name)
  16. if isinstance(value, re2.Options.Encoding):
  17. value = next(v for v in type(value).__members__.values() if v != value)
  18. elif isinstance(value, bool):
  19. value = not value
  20. elif isinstance(value, int):
  21. value = value + 1
  22. else:
  23. raise TypeError('option {!r}: {!r} {!r}'.format(name, type(value), value))
  24. setattr(options, name, value)
  25. self.assertEqual(value, getattr(options, name))
  26. class Re2CompileTest(parameterized.TestCase):
  27. """Contains tests that apply to the re2 module only.
  28. We disagree with Python on the string types of group names,
  29. so there is no point attempting to verify consistency.
  30. """
  31. @parameterized.parameters(
  32. (u'(foo*)(?P<bar>qux+)', 2, [(u'bar', 2)]),
  33. (b'(foo*)(?P<bar>qux+)', 2, [(b'bar', 2)]),
  34. (u'(foo*)(?P<中文>qux+)', 2, [(u'中文', 2)]),
  35. )
  36. def test_compile(self, pattern, expected_groups, expected_groupindex):
  37. regexp = re2.compile(pattern)
  38. self.assertIs(regexp, re2.compile(pattern)) # cached
  39. self.assertIs(regexp, re2.compile(regexp)) # cached
  40. with self.assertRaisesRegex(re2.error,
  41. ('pattern is already compiled, so '
  42. 'options may not be specified')):
  43. options = re2.Options()
  44. options.log_errors = not options.log_errors
  45. re2.compile(regexp, options=options)
  46. self.assertIsNotNone(regexp.options)
  47. self.assertEqual(expected_groups, regexp.groups)
  48. self.assertDictEqual(dict(expected_groupindex), regexp.groupindex)
  49. def test_compile_with_options(self):
  50. options = re2.Options()
  51. options.max_mem = 100
  52. with self.assertRaisesRegex(re2.error, 'pattern too large'):
  53. re2.compile('.{1000}', options=options)
  54. def test_programsize_reverseprogramsize(self):
  55. regexp = re2.compile('a+b')
  56. self.assertEqual(7, regexp.programsize)
  57. self.assertEqual(7, regexp.reverseprogramsize)
  58. def test_programfanout_reverseprogramfanout(self):
  59. regexp = re2.compile('a+b')
  60. self.assertListEqual([1, 1], regexp.programfanout)
  61. self.assertListEqual([3], regexp.reverseprogramfanout)
  62. @parameterized.parameters(
  63. (u'abc', 0, None),
  64. (b'abc', 0, None),
  65. (u'abc', 10, (b'abc', b'abc')),
  66. (b'abc', 10, (b'abc', b'abc')),
  67. (u'ab*c', 10, (b'ab', b'ac')),
  68. (b'ab*c', 10, (b'ab', b'ac')),
  69. (u'ab+c', 10, (b'abb', b'abc')),
  70. (b'ab+c', 10, (b'abb', b'abc')),
  71. (u'ab?c', 10, (b'abc', b'ac')),
  72. (b'ab?c', 10, (b'abc', b'ac')),
  73. (u'.*', 10, (b'', b'\xf4\xbf\xbf\xc0')),
  74. (b'.*', 10, None),
  75. (u'\\C*', 10, None),
  76. (b'\\C*', 10, None),
  77. )
  78. def test_possiblematchrange(self, pattern, maxlen, expected_min_max):
  79. # For brevity, the string type of pattern determines the encoding.
  80. # It would otherwise be possible to have bytes with UTF8, but as per
  81. # the module docstring, it isn't permitted to have str with LATIN1.
  82. options = re2.Options()
  83. if isinstance(pattern, str):
  84. options.encoding = re2.Options.Encoding.UTF8
  85. else:
  86. options.encoding = re2.Options.Encoding.LATIN1
  87. regexp = re2.compile(pattern, options=options)
  88. if expected_min_max:
  89. self.assertEqual(expected_min_max, regexp.possiblematchrange(maxlen))
  90. else:
  91. with self.assertRaisesRegex(re2.error, 'failed to compute match range'):
  92. regexp.possiblematchrange(maxlen)
  93. Params = collections.namedtuple(
  94. 'Params', ('pattern', 'text', 'spans', 'search', 'match', 'fullmatch'))
  95. PARAMS = [
  96. Params(u'\\d+', u'Hello, world.', None, False, False, False),
  97. Params(b'\\d+', b'Hello, world.', None, False, False, False),
  98. Params(u'\\s+', u'Hello, world.', [(6, 7)], True, False, False),
  99. Params(b'\\s+', b'Hello, world.', [(6, 7)], True, False, False),
  100. Params(u'\\w+', u'Hello, world.', [(0, 5)], True, True, False),
  101. Params(b'\\w+', b'Hello, world.', [(0, 5)], True, True, False),
  102. Params(u'(\\d+)?', u'Hello, world.', [(0, 0), (-1, -1)], True, True, False),
  103. Params(b'(\\d+)?', b'Hello, world.', [(0, 0), (-1, -1)], True, True, False),
  104. Params(u'youtube(_device|_md|_gaia|_multiday|_multiday_gaia)?',
  105. u'youtube_ads', [(0, 7), (-1, -1)], True, True, False),
  106. Params(b'youtube(_device|_md|_gaia|_multiday|_multiday_gaia)?',
  107. b'youtube_ads', [(0, 7), (-1, -1)], True, True, False),
  108. ]
  109. def upper(match):
  110. return match.group().upper()
  111. class ReRegexpTest(parameterized.TestCase):
  112. """Contains tests that apply to the re and re2 modules."""
  113. MODULE = re
  114. @parameterized.parameters((p.pattern,) for p in PARAMS)
  115. def test_pickle(self, pattern):
  116. regexp = self.MODULE.compile(pattern)
  117. rick = pickle.loads(pickle.dumps(regexp))
  118. self.assertEqual(regexp.pattern, rick.pattern)
  119. @parameterized.parameters(
  120. (p.pattern, p.text, (p.spans if p.search else None)) for p in PARAMS)
  121. def test_search(self, pattern, text, expected_spans):
  122. match = self.MODULE.search(pattern, text)
  123. if expected_spans is None:
  124. self.assertIsNone(match)
  125. else:
  126. spans = [match.span(group) for group in range(match.re.groups + 1)]
  127. self.assertListEqual(expected_spans, spans)
  128. def test_search_with_pos_and_endpos(self):
  129. regexp = self.MODULE.compile(u'.+') # empty string NOT allowed
  130. text = u'I \u2665 RE2!'
  131. # Note that len(text) is the position of the empty string at the end of
  132. # text, so range() stops at len(text) + 1 in order to include len(text).
  133. for pos in range(len(text) + 1):
  134. for endpos in range(pos, len(text) + 1):
  135. match = regexp.search(text, pos=pos, endpos=endpos)
  136. if pos == endpos:
  137. self.assertIsNone(match)
  138. else:
  139. self.assertEqual(pos, match.pos)
  140. self.assertEqual(endpos, match.endpos)
  141. self.assertEqual(pos, match.start())
  142. self.assertEqual(endpos, match.end())
  143. self.assertTupleEqual((pos, endpos), match.span())
  144. def test_search_with_bogus_pos_and_endpos(self):
  145. regexp = self.MODULE.compile(u'.*') # empty string allowed
  146. text = u'I \u2665 RE2!'
  147. match = regexp.search(text, pos=-100)
  148. self.assertEqual(0, match.pos)
  149. match = regexp.search(text, pos=100)
  150. self.assertEqual(8, match.pos)
  151. match = regexp.search(text, endpos=-100)
  152. self.assertEqual(0, match.endpos)
  153. match = regexp.search(text, endpos=100)
  154. self.assertEqual(8, match.endpos)
  155. match = regexp.search(text, pos=100, endpos=-100)
  156. self.assertIsNone(match)
  157. @parameterized.parameters(
  158. (p.pattern, p.text, (p.spans if p.match else None)) for p in PARAMS)
  159. def test_match(self, pattern, text, expected_spans):
  160. match = self.MODULE.match(pattern, text)
  161. if expected_spans is None:
  162. self.assertIsNone(match)
  163. else:
  164. spans = [match.span(group) for group in range(match.re.groups + 1)]
  165. self.assertListEqual(expected_spans, spans)
  166. @parameterized.parameters(
  167. (p.pattern, p.text, (p.spans if p.fullmatch else None)) for p in PARAMS)
  168. def test_fullmatch(self, pattern, text, expected_spans):
  169. match = self.MODULE.fullmatch(pattern, text)
  170. if expected_spans is None:
  171. self.assertIsNone(match)
  172. else:
  173. spans = [match.span(group) for group in range(match.re.groups + 1)]
  174. self.assertListEqual(expected_spans, spans)
  175. @parameterized.parameters(
  176. (u'', u'', [(0, 0)]),
  177. (b'', b'', [(0, 0)]),
  178. (u'', u'x', [(0, 0), (1, 1)]),
  179. (b'', b'x', [(0, 0), (1, 1)]),
  180. (u'', u'xy', [(0, 0), (1, 1), (2, 2)]),
  181. (b'', b'xy', [(0, 0), (1, 1), (2, 2)]),
  182. (u'.', u'xy', [(0, 1), (1, 2)]),
  183. (b'.', b'xy', [(0, 1), (1, 2)]),
  184. (u'x', u'xy', [(0, 1)]),
  185. (b'x', b'xy', [(0, 1)]),
  186. (u'y', u'xy', [(1, 2)]),
  187. (b'y', b'xy', [(1, 2)]),
  188. (u'z', u'xy', []),
  189. (b'z', b'xy', []),
  190. (u'\\w*', u'Hello, world.', [(0, 5), (5, 5), (6, 6), (7, 12), (12, 12),
  191. (13, 13)]),
  192. (b'\\w*', b'Hello, world.', [(0, 5), (5, 5), (6, 6), (7, 12), (12, 12),
  193. (13, 13)]),
  194. )
  195. def test_finditer(self, pattern, text, expected_matches):
  196. matches = [match.span() for match in self.MODULE.finditer(pattern, text)]
  197. self.assertListEqual(expected_matches, matches)
  198. @parameterized.parameters(
  199. (u'\\w\\w+', u'Hello, world.', [u'Hello', u'world']),
  200. (b'\\w\\w+', b'Hello, world.', [b'Hello', b'world']),
  201. (u'(\\w)\\w+', u'Hello, world.', [u'H', u'w']),
  202. (b'(\\w)\\w+', b'Hello, world.', [b'H', b'w']),
  203. (u'(\\w)(\\w+)', u'Hello, world.', [(u'H', u'ello'), (u'w', u'orld')]),
  204. (b'(\\w)(\\w+)', b'Hello, world.', [(b'H', b'ello'), (b'w', b'orld')]),
  205. (u'(\\w)(\\w+)?', u'Hello, w.', [(u'H', u'ello'), (u'w', u'')]),
  206. (b'(\\w)(\\w+)?', b'Hello, w.', [(b'H', b'ello'), (b'w', b'')]),
  207. )
  208. def test_findall(self, pattern, text, expected_matches):
  209. matches = self.MODULE.findall(pattern, text)
  210. self.assertListEqual(expected_matches, matches)
  211. @parameterized.parameters(
  212. (u'\\W+', u'Hello, world.', -1, [u'Hello, world.']),
  213. (b'\\W+', b'Hello, world.', -1, [b'Hello, world.']),
  214. (u'\\W+', u'Hello, world.', 0, [u'Hello', u'world', u'']),
  215. (b'\\W+', b'Hello, world.', 0, [b'Hello', b'world', b'']),
  216. (u'\\W+', u'Hello, world.', 1, [u'Hello', u'world.']),
  217. (b'\\W+', b'Hello, world.', 1, [b'Hello', b'world.']),
  218. (u'(\\W+)', u'Hello, world.', -1, [u'Hello, world.']),
  219. (b'(\\W+)', b'Hello, world.', -1, [b'Hello, world.']),
  220. (u'(\\W+)', u'Hello, world.', 0, [u'Hello', u', ', u'world', u'.', u'']),
  221. (b'(\\W+)', b'Hello, world.', 0, [b'Hello', b', ', b'world', b'.', b'']),
  222. (u'(\\W+)', u'Hello, world.', 1, [u'Hello', u', ', u'world.']),
  223. (b'(\\W+)', b'Hello, world.', 1, [b'Hello', b', ', b'world.']),
  224. )
  225. def test_split(self, pattern, text, maxsplit, expected_pieces):
  226. pieces = self.MODULE.split(pattern, text, maxsplit)
  227. self.assertListEqual(expected_pieces, pieces)
  228. @parameterized.parameters(
  229. (u'\\w+', upper, u'Hello, world.', -1, u'Hello, world.', 0),
  230. (b'\\w+', upper, b'Hello, world.', -1, b'Hello, world.', 0),
  231. (u'\\w+', upper, u'Hello, world.', 0, u'HELLO, WORLD.', 2),
  232. (b'\\w+', upper, b'Hello, world.', 0, b'HELLO, WORLD.', 2),
  233. (u'\\w+', upper, u'Hello, world.', 1, u'HELLO, world.', 1),
  234. (b'\\w+', upper, b'Hello, world.', 1, b'HELLO, world.', 1),
  235. (u'\\w+', u'MEEP', u'Hello, world.', -1, u'Hello, world.', 0),
  236. (b'\\w+', b'MEEP', b'Hello, world.', -1, b'Hello, world.', 0),
  237. (u'\\w+', u'MEEP', u'Hello, world.', 0, u'MEEP, MEEP.', 2),
  238. (b'\\w+', b'MEEP', b'Hello, world.', 0, b'MEEP, MEEP.', 2),
  239. (u'\\w+', u'MEEP', u'Hello, world.', 1, u'MEEP, world.', 1),
  240. (b'\\w+', b'MEEP', b'Hello, world.', 1, b'MEEP, world.', 1),
  241. (u'\\\\', u'\\\\\\\\', u'Hello,\\world.', 0, u'Hello,\\\\world.', 1),
  242. (b'\\\\', b'\\\\\\\\', b'Hello,\\world.', 0, b'Hello,\\\\world.', 1),
  243. )
  244. def test_subn_sub(self, pattern, repl, text, count, expected_joined_pieces,
  245. expected_numsplit):
  246. joined_pieces, numsplit = self.MODULE.subn(pattern, repl, text, count)
  247. self.assertEqual(expected_joined_pieces, joined_pieces)
  248. self.assertEqual(expected_numsplit, numsplit)
  249. joined_pieces = self.MODULE.sub(pattern, repl, text, count)
  250. self.assertEqual(expected_joined_pieces, joined_pieces)
  251. class Re2RegexpTest(ReRegexpTest):
  252. """Contains tests that apply to the re2 module only."""
  253. MODULE = re2
  254. def test_compile_with_latin1_encoding(self):
  255. options = re2.Options()
  256. options.encoding = re2.Options.Encoding.LATIN1
  257. with self.assertRaisesRegex(re2.error,
  258. ('string type of pattern is str, but '
  259. 'encoding specified in options is LATIN1')):
  260. re2.compile(u'.?', options=options)
  261. # ... whereas this is fine, of course.
  262. re2.compile(b'.?', options=options)
  263. @parameterized.parameters(
  264. (u'\\p{Lo}', u'\u0ca0_\u0ca0', [(0, 1), (2, 3)]),
  265. (b'\\p{Lo}', b'\xe0\xb2\xa0_\xe0\xb2\xa0', [(0, 3), (4, 7)]),
  266. )
  267. def test_finditer_with_utf8(self, pattern, text, expected_matches):
  268. matches = [match.span() for match in self.MODULE.finditer(pattern, text)]
  269. self.assertListEqual(expected_matches, matches)
  270. def test_purge(self):
  271. re2.compile('Goodbye, world.')
  272. self.assertGreater(re2._Regexp._make.cache_info().currsize, 0)
  273. re2.purge()
  274. self.assertEqual(re2._Regexp._make.cache_info().currsize, 0)
  275. class Re2EscapeTest(parameterized.TestCase):
  276. """Contains tests that apply to the re2 module only.
  277. We disagree with Python on the escaping of some characters,
  278. so there is no point attempting to verify consistency.
  279. """
  280. @parameterized.parameters(
  281. (u'a*b+c?', u'a\\*b\\+c\\?'),
  282. (b'a*b+c?', b'a\\*b\\+c\\?'),
  283. )
  284. def test_escape(self, pattern, expected_escaped):
  285. escaped = re2.escape(pattern)
  286. self.assertEqual(expected_escaped, escaped)
  287. class ReMatchTest(parameterized.TestCase):
  288. """Contains tests that apply to the re and re2 modules."""
  289. MODULE = re
  290. def test_expand(self):
  291. pattern = u'(?P<S>[\u2600-\u26ff]+).*?(?P<P>[^\\s\\w]+)'
  292. text = u'I \u2665 RE2!\n'
  293. match = self.MODULE.search(pattern, text)
  294. self.assertEqual(u'\u2665\n!', match.expand(u'\\1\\n\\2'))
  295. self.assertEqual(u'\u2665\n!', match.expand(u'\\g<1>\\n\\g<2>'))
  296. self.assertEqual(u'\u2665\n!', match.expand(u'\\g<S>\\n\\g<P>'))
  297. self.assertEqual(u'\\1\\2\n\u2665!', match.expand(u'\\\\1\\\\2\\n\\1\\2'))
  298. def test_expand_with_octal(self):
  299. pattern = u'()()()()()()()()()(\\w+)'
  300. text = u'Hello, world.'
  301. match = self.MODULE.search(pattern, text)
  302. self.assertEqual(u'Hello\n', match.expand(u'\\g<0>\\n'))
  303. self.assertEqual(u'Hello\n', match.expand(u'\\g<10>\\n'))
  304. self.assertEqual(u'\x00\n', match.expand(u'\\0\\n'))
  305. self.assertEqual(u'\x00\n', match.expand(u'\\00\\n'))
  306. self.assertEqual(u'\x00\n', match.expand(u'\\000\\n'))
  307. self.assertEqual(u'\x000\n', match.expand(u'\\0000\\n'))
  308. self.assertEqual(u'\n', match.expand(u'\\1\\n'))
  309. self.assertEqual(u'Hello\n', match.expand(u'\\10\\n'))
  310. self.assertEqual(u'@\n', match.expand(u'\\100\\n'))
  311. self.assertEqual(u'@0\n', match.expand(u'\\1000\\n'))
  312. def test_getitem_group_groups_groupdict(self):
  313. pattern = u'(?P<S>[\u2600-\u26ff]+).*?(?P<P>[^\\s\\w]+)'
  314. text = u'Hello, world.\nI \u2665 RE2!\nGoodbye, world.\n'
  315. match = self.MODULE.search(pattern, text)
  316. self.assertEqual(u'\u2665 RE2!', match[0])
  317. self.assertEqual(u'\u2665', match[1])
  318. self.assertEqual(u'!', match[2])
  319. self.assertEqual(u'\u2665', match[u'S'])
  320. self.assertEqual(u'!', match[u'P'])
  321. self.assertEqual(u'\u2665 RE2!', match.group())
  322. self.assertEqual(u'\u2665 RE2!', match.group(0))
  323. self.assertEqual(u'\u2665', match.group(1))
  324. self.assertEqual(u'!', match.group(2))
  325. self.assertEqual(u'\u2665', match.group(u'S'))
  326. self.assertEqual(u'!', match.group(u'P'))
  327. self.assertTupleEqual((u'\u2665', u'!'), match.group(1, 2))
  328. self.assertTupleEqual((u'\u2665', u'!'), match.group(u'S', u'P'))
  329. self.assertTupleEqual((u'\u2665', u'!'), match.groups())
  330. self.assertDictEqual({u'S': u'\u2665', u'P': u'!'}, match.groupdict())
  331. def test_bogus_group_start_end_and_span(self):
  332. pattern = u'(?P<S>[\u2600-\u26ff]+).*?(?P<P>[^\\s\\w]+)'
  333. text = u'I \u2665 RE2!\n'
  334. match = self.MODULE.search(pattern, text)
  335. self.assertRaises(IndexError, match.group, -1)
  336. self.assertRaises(IndexError, match.group, 3)
  337. self.assertRaises(IndexError, match.group, 'X')
  338. self.assertRaises(IndexError, match.start, -1)
  339. self.assertRaises(IndexError, match.start, 3)
  340. self.assertRaises(IndexError, match.end, -1)
  341. self.assertRaises(IndexError, match.end, 3)
  342. self.assertRaises(IndexError, match.span, -1)
  343. self.assertRaises(IndexError, match.span, 3)
  344. @parameterized.parameters(
  345. (u'((a)(b))((c)(d))', u'foo bar qux', None, None),
  346. (u'(?P<one>(a)(b))((c)(d))', u'foo abcd qux', 4, None),
  347. (u'(?P<one>(a)(b))(?P<four>(c)(d))', u'foo abcd qux', 4, 'four'),
  348. )
  349. def test_lastindex_lastgroup(self, pattern, text, expected_lastindex,
  350. expected_lastgroup):
  351. match = self.MODULE.search(pattern, text)
  352. if expected_lastindex is None:
  353. self.assertIsNone(match)
  354. else:
  355. self.assertEqual(expected_lastindex, match.lastindex)
  356. self.assertEqual(expected_lastgroup, match.lastgroup)
  357. class Re2MatchTest(ReMatchTest):
  358. """Contains tests that apply to the re2 module only."""
  359. MODULE = re2
  360. class SetTest(absltest.TestCase):
  361. def test_search(self):
  362. s = re2.Set.SearchSet()
  363. self.assertEqual(0, s.Add('\\d+'))
  364. self.assertEqual(1, s.Add('\\s+'))
  365. self.assertEqual(2, s.Add('\\w+'))
  366. self.assertRaises(re2.error, s.Add, '(MEEP')
  367. s.Compile()
  368. self.assertItemsEqual([1, 2], s.Match('Hello, world.'))
  369. def test_match(self):
  370. s = re2.Set.MatchSet()
  371. self.assertEqual(0, s.Add('\\d+'))
  372. self.assertEqual(1, s.Add('\\s+'))
  373. self.assertEqual(2, s.Add('\\w+'))
  374. self.assertRaises(re2.error, s.Add, '(MEEP')
  375. s.Compile()
  376. self.assertItemsEqual([2], s.Match('Hello, world.'))
  377. def test_fullmatch(self):
  378. s = re2.Set.FullMatchSet()
  379. self.assertEqual(0, s.Add('\\d+'))
  380. self.assertEqual(1, s.Add('\\s+'))
  381. self.assertEqual(2, s.Add('\\w+'))
  382. self.assertRaises(re2.error, s.Add, '(MEEP')
  383. s.Compile()
  384. self.assertIsNone(s.Match('Hello, world.'))
  385. class FilterTest(absltest.TestCase):
  386. def test_match(self):
  387. f = re2.Filter()
  388. self.assertEqual(0, f.Add('Hello, \\w+\\.'))
  389. self.assertEqual(1, f.Add('\\w+, world\\.'))
  390. self.assertEqual(2, f.Add('Goodbye, \\w+\\.'))
  391. self.assertRaises(re2.error, f.Add, '(MEEP')
  392. f.Compile()
  393. self.assertItemsEqual([0, 1], f.Match('Hello, world.', potential=True))
  394. self.assertItemsEqual([0, 1], f.Match('HELLO, WORLD.', potential=True))
  395. self.assertItemsEqual([0, 1], f.Match('Hello, world.'))
  396. self.assertIsNone(f.Match('HELLO, WORLD.'))
  397. self.assertRaises(IndexError, f.re, -1)
  398. self.assertRaises(IndexError, f.re, 3)
  399. self.assertEqual('Goodbye, \\w+\\.', f.re(2).pattern)
  400. # Verify whether the underlying RE2 object is usable.
  401. self.assertEqual(0, f.re(2).groups)
  402. def test_issue_484(self):
  403. # Previously, the shim would dereference a null pointer and crash.
  404. f = re2.Filter()
  405. with self.assertRaisesRegex(re2.error,
  406. r'Match\(\) called before compiling'):
  407. f.Match('')
  408. if __name__ == '__main__':
  409. absltest.main()