test_pattern.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592
  1. import io
  2. import sys
  3. import pytest
  4. from flexmock import flexmock
  5. from borgmatic.actions import pattern as module
  6. from borgmatic.borg.pattern import Pattern, Pattern_source, Pattern_style, Pattern_type
  7. @pytest.mark.parametrize(
  8. 'pattern_line,expected_pattern',
  9. (
  10. ('R /foo', Pattern('/foo', source=Pattern_source.CONFIG)),
  11. ('P sh', Pattern('sh', Pattern_type.PATTERN_STYLE, source=Pattern_source.CONFIG)),
  12. ('+ /foo*', Pattern('/foo*', Pattern_type.INCLUDE, source=Pattern_source.CONFIG)),
  13. (
  14. '+ sh:/foo*',
  15. Pattern(
  16. '/foo*',
  17. Pattern_type.INCLUDE,
  18. Pattern_style.SHELL,
  19. source=Pattern_source.CONFIG,
  20. ),
  21. ),
  22. ),
  23. )
  24. def test_parse_pattern_transforms_pattern_line_to_instance(pattern_line, expected_pattern):
  25. assert module.parse_pattern(pattern_line) == expected_pattern
  26. def test_parse_pattern_with_invalid_pattern_line_errors():
  27. with pytest.raises(ValueError):
  28. module.parse_pattern('/foo')
  29. def test_collect_patterns_converts_source_directories():
  30. assert module.collect_patterns({'source_directories': ['/foo', '/bar']}) == (
  31. Pattern('/foo', source=Pattern_source.CONFIG),
  32. Pattern('/bar', source=Pattern_source.CONFIG),
  33. )
  34. def test_collect_patterns_parses_config_patterns():
  35. flexmock(module).should_receive('parse_pattern').with_args('R /foo').and_return(Pattern('/foo'))
  36. flexmock(module).should_receive('parse_pattern').with_args('# comment').never()
  37. flexmock(module).should_receive('parse_pattern').with_args('').never()
  38. flexmock(module).should_receive('parse_pattern').with_args(' ').never()
  39. flexmock(module).should_receive('parse_pattern').with_args('R /bar').and_return(Pattern('/bar'))
  40. assert module.collect_patterns({'patterns': ['R /foo', '# comment', '', ' ', 'R /bar']}) == (
  41. Pattern('/foo'),
  42. Pattern('/bar'),
  43. )
  44. def test_collect_patterns_converts_exclude_patterns():
  45. assert module.collect_patterns({'exclude_patterns': ['/foo', '/bar', 'sh:**/baz']}) == (
  46. Pattern(
  47. '/foo',
  48. Pattern_type.NO_RECURSE,
  49. Pattern_style.FNMATCH,
  50. source=Pattern_source.CONFIG,
  51. ),
  52. Pattern(
  53. '/bar',
  54. Pattern_type.NO_RECURSE,
  55. Pattern_style.FNMATCH,
  56. source=Pattern_source.CONFIG,
  57. ),
  58. Pattern(
  59. '**/baz',
  60. Pattern_type.NO_RECURSE,
  61. Pattern_style.SHELL,
  62. source=Pattern_source.CONFIG,
  63. ),
  64. )
  65. def test_collect_patterns_reads_config_patterns_from_file():
  66. builtins = flexmock(sys.modules['builtins'])
  67. builtins.should_receive('open').with_args('file1.txt', encoding='utf-8').and_return(
  68. io.StringIO('R /foo')
  69. )
  70. builtins.should_receive('open').with_args('file2.txt', encoding='utf-8').and_return(
  71. io.StringIO('R /bar\n# comment\n\n \nR /baz'),
  72. )
  73. flexmock(module).should_receive('parse_pattern').with_args('R /foo').and_return(Pattern('/foo'))
  74. flexmock(module).should_receive('parse_pattern').with_args('# comment').never()
  75. flexmock(module).should_receive('parse_pattern').with_args('').never()
  76. flexmock(module).should_receive('parse_pattern').with_args(' ').never()
  77. flexmock(module).should_receive('parse_pattern').with_args('R /bar').and_return(Pattern('/bar'))
  78. flexmock(module).should_receive('parse_pattern').with_args('R /baz').and_return(Pattern('/baz'))
  79. assert module.collect_patterns({'patterns_from': ['file1.txt', 'file2.txt']}) == (
  80. Pattern('/foo'),
  81. Pattern('/bar'),
  82. Pattern('/baz'),
  83. )
  84. def test_collect_patterns_errors_on_missing_config_patterns_from_file():
  85. builtins = flexmock(sys.modules['builtins'])
  86. builtins.should_receive('open').with_args('file1.txt', encoding='utf-8').and_raise(
  87. FileNotFoundError
  88. )
  89. flexmock(module).should_receive('parse_pattern').never()
  90. with pytest.raises(ValueError):
  91. module.collect_patterns({'patterns_from': ['file1.txt', 'file2.txt']})
  92. def test_collect_patterns_reads_config_exclude_from_file():
  93. builtins = flexmock(sys.modules['builtins'])
  94. builtins.should_receive('open').with_args('file1.txt', encoding='utf-8').and_return(
  95. io.StringIO('/foo')
  96. )
  97. builtins.should_receive('open').with_args('file2.txt', encoding='utf-8').and_return(
  98. io.StringIO('/bar\n# comment\n\n \n/baz'),
  99. )
  100. flexmock(module).should_receive('parse_pattern').with_args(
  101. '! /foo',
  102. default_style=Pattern_style.FNMATCH,
  103. ).and_return(Pattern('/foo', Pattern_type.NO_RECURSE, Pattern_style.FNMATCH))
  104. flexmock(module).should_receive('parse_pattern').with_args(
  105. '! /bar',
  106. default_style=Pattern_style.FNMATCH,
  107. ).and_return(Pattern('/bar', Pattern_type.NO_RECURSE, Pattern_style.FNMATCH))
  108. flexmock(module).should_receive('parse_pattern').with_args('# comment').never()
  109. flexmock(module).should_receive('parse_pattern').with_args('').never()
  110. flexmock(module).should_receive('parse_pattern').with_args(' ').never()
  111. flexmock(module).should_receive('parse_pattern').with_args(
  112. '! /baz',
  113. default_style=Pattern_style.FNMATCH,
  114. ).and_return(Pattern('/baz', Pattern_type.NO_RECURSE, Pattern_style.FNMATCH))
  115. assert module.collect_patterns({'exclude_from': ['file1.txt', 'file2.txt']}) == (
  116. Pattern('/foo', Pattern_type.NO_RECURSE, Pattern_style.FNMATCH),
  117. Pattern('/bar', Pattern_type.NO_RECURSE, Pattern_style.FNMATCH),
  118. Pattern('/baz', Pattern_type.NO_RECURSE, Pattern_style.FNMATCH),
  119. )
  120. def test_collect_patterns_errors_on_missing_config_exclude_from_file():
  121. builtins = flexmock(sys.modules['builtins'])
  122. builtins.should_receive('open').with_args('file1.txt', encoding='utf-8').and_raise(OSError)
  123. flexmock(module).should_receive('parse_pattern').never()
  124. with pytest.raises(ValueError):
  125. module.collect_patterns({'exclude_from': ['file1.txt', 'file2.txt']})
  126. def test_expand_directory_with_basic_path_passes_it_through():
  127. flexmock(module.os.path).should_receive('expanduser').and_return('foo')
  128. flexmock(module.glob).should_receive('glob').and_return([])
  129. paths = module.expand_directory('foo', None)
  130. assert paths == ['foo']
  131. def test_expand_directory_with_glob_expands():
  132. flexmock(module.os.path).should_receive('expanduser').and_return('foo*')
  133. flexmock(module.glob).should_receive('glob').and_return(['foo', 'food'])
  134. paths = module.expand_directory('foo*', None)
  135. assert paths == ['foo', 'food']
  136. def test_expand_directory_strips_off_working_directory():
  137. flexmock(module.os.path).should_receive('expanduser').and_return('foo')
  138. flexmock(module.glob).should_receive('glob').with_args('/working/dir/foo').and_return([]).once()
  139. paths = module.expand_directory('foo', working_directory='/working/dir')
  140. assert paths == ['foo']
  141. def test_expand_directory_globs_working_directory_and_strips_it_off():
  142. flexmock(module.os.path).should_receive('expanduser').and_return('foo*')
  143. flexmock(module.glob).should_receive('glob').with_args('/working/dir/foo*').and_return(
  144. ['/working/dir/foo', '/working/dir/food'],
  145. ).once()
  146. paths = module.expand_directory('foo*', working_directory='/working/dir')
  147. assert paths == ['foo', 'food']
  148. def test_expand_directory_with_slashdot_hack_globs_working_directory_and_strips_it_off():
  149. flexmock(module.os.path).should_receive('expanduser').and_return('./foo*')
  150. flexmock(module.glob).should_receive('glob').with_args('/working/dir/./foo*').and_return(
  151. ['/working/dir/./foo', '/working/dir/./food'],
  152. ).once()
  153. paths = module.expand_directory('./foo*', working_directory='/working/dir')
  154. assert paths == ['./foo', './food']
  155. def test_expand_directory_with_working_directory_matching_start_of_directory_does_not_strip_it_off():
  156. flexmock(module.os.path).should_receive('expanduser').and_return('/working/dir/foo')
  157. flexmock(module.glob).should_receive('glob').with_args('/working/dir/foo').and_return(
  158. ['/working/dir/foo'],
  159. ).once()
  160. paths = module.expand_directory('/working/dir/foo', working_directory='/working/dir')
  161. assert paths == ['/working/dir/foo']
  162. def test_expand_patterns_flattens_expanded_directories():
  163. flexmock(module).should_receive('expand_directory').with_args('~/foo', None).and_return(
  164. ['/root/foo'],
  165. )
  166. flexmock(module).should_receive('expand_directory').with_args('bar*', None).and_return(
  167. ['bar', 'barf'],
  168. )
  169. paths = module.expand_patterns((Pattern('~/foo'), Pattern('bar*')))
  170. assert paths == (Pattern('/root/foo'), Pattern('bar'), Pattern('barf'))
  171. def test_expand_patterns_with_working_directory_passes_it_through():
  172. flexmock(module).should_receive('expand_directory').with_args('foo', '/working/dir').and_return(
  173. ['/working/dir/foo'],
  174. )
  175. patterns = module.expand_patterns((Pattern('foo'),), working_directory='/working/dir')
  176. assert patterns == (Pattern('/working/dir/foo'),)
  177. def test_expand_patterns_does_not_expand_skip_paths():
  178. flexmock(module).should_receive('expand_directory').with_args('/foo', None).and_return(['/foo'])
  179. flexmock(module).should_receive('expand_directory').with_args('/bar*', None).never()
  180. patterns = module.expand_patterns((Pattern('/foo'), Pattern('/bar*')), skip_paths=('/bar*',))
  181. assert patterns == (Pattern('/foo'), Pattern('/bar*'))
  182. def test_expand_patterns_considers_none_as_no_patterns():
  183. assert module.expand_patterns(None) == ()
  184. def test_expand_patterns_expands_tildes_and_globs_in_root_patterns():
  185. flexmock(module.os.path).should_receive('expanduser').never()
  186. flexmock(module).should_receive('expand_directory').and_return(
  187. ['/root/foo/one', '/root/foo/two'],
  188. )
  189. paths = module.expand_patterns((Pattern('~/foo/*'),))
  190. assert paths == (Pattern('/root/foo/one'), Pattern('/root/foo/two'))
  191. def test_expand_patterns_expands_only_tildes_in_non_root_patterns():
  192. flexmock(module).should_receive('expand_directory').never()
  193. flexmock(module.os.path).should_receive('expanduser').and_return('/root/bar/*')
  194. paths = module.expand_patterns((Pattern('~/bar/*', Pattern_type.INCLUDE),))
  195. assert paths == (Pattern('/root/bar/*', Pattern_type.INCLUDE),)
  196. def test_get_existent_path_or_parent_passes_through_existent_path():
  197. flexmock(module.os.path).should_receive('exists').and_return(True)
  198. assert module.get_existent_path_or_parent('/foo/bar/baz') == '/foo/bar/baz'
  199. def test_get_existent_path_or_parent_with_non_existent_path_returns_none():
  200. flexmock(module.os.path).should_receive('exists').and_return(False)
  201. assert module.get_existent_path_or_parent('/foo/bar/baz') is None
  202. def test_get_existent_path_or_parent_with_non_existent_path_returns_existent_parent():
  203. flexmock(module.os.path).should_receive('exists').with_args('/foo/bar/baz*').and_return(False)
  204. flexmock(module.os.path).should_receive('exists').with_args('/foo/bar').and_return(True)
  205. flexmock(module.os.path).should_receive('exists').with_args('/foo').never()
  206. flexmock(module.os.path).should_receive('exists').with_args('/').never()
  207. assert module.get_existent_path_or_parent('/foo/bar/baz*') == '/foo/bar'
  208. def test_get_existent_path_or_parent_with_non_existent_path_returns_existent_grandparent():
  209. flexmock(module.os.path).should_receive('exists').with_args('/foo/bar/baz*').and_return(False)
  210. flexmock(module.os.path).should_receive('exists').with_args('/foo/bar').and_return(False)
  211. flexmock(module.os.path).should_receive('exists').with_args('/foo').and_return(True)
  212. flexmock(module.os.path).should_receive('exists').with_args('/').never()
  213. assert module.get_existent_path_or_parent('/foo/bar/baz*') == '/foo'
  214. def test_get_existent_path_or_parent_with_end_to_end_test_prefix_returns_none():
  215. flexmock(module.os.path).should_receive('exists').never()
  216. assert module.get_existent_path_or_parent('/e2e/foo/bar/baz') is None
  217. def test_device_map_patterns_gives_device_id_per_path():
  218. flexmock(module).should_receive('get_existent_path_or_parent').replace_with(lambda path: path)
  219. flexmock(module.os).should_receive('stat').with_args('/foo').and_return(flexmock(st_dev=55))
  220. flexmock(module.os).should_receive('stat').with_args('/bar').and_return(flexmock(st_dev=66))
  221. device_map = module.device_map_patterns(
  222. (
  223. Pattern('/foo'),
  224. Pattern('^/bar', type=Pattern_type.INCLUDE, style=Pattern_style.REGULAR_EXPRESSION),
  225. ),
  226. )
  227. assert device_map == (
  228. Pattern('/foo', device=55),
  229. Pattern(
  230. '^/bar',
  231. type=Pattern_type.INCLUDE,
  232. style=Pattern_style.REGULAR_EXPRESSION,
  233. device=66,
  234. ),
  235. )
  236. def test_device_map_patterns_with_missing_path_does_not_error():
  237. flexmock(module).should_receive('get_existent_path_or_parent').with_args('/foo').and_return(
  238. '/foo',
  239. )
  240. flexmock(module).should_receive('get_existent_path_or_parent').with_args('/bar').and_return(
  241. None,
  242. )
  243. flexmock(module.os).should_receive('stat').with_args('/foo').and_return(flexmock(st_dev=55))
  244. flexmock(module.os).should_receive('stat').with_args('/bar').never()
  245. device_map = module.device_map_patterns((Pattern('/foo'), Pattern('/bar')))
  246. assert device_map == (
  247. Pattern('/foo', device=55),
  248. Pattern('/bar'),
  249. )
  250. def test_device_map_patterns_uses_working_directory_to_construct_path():
  251. flexmock(module).should_receive('get_existent_path_or_parent').replace_with(lambda path: path)
  252. flexmock(module.os).should_receive('stat').with_args('/foo').and_return(flexmock(st_dev=55))
  253. flexmock(module.os).should_receive('stat').with_args('/working/dir/bar').and_return(
  254. flexmock(st_dev=66),
  255. )
  256. device_map = module.device_map_patterns(
  257. (Pattern('/foo'), Pattern('bar')),
  258. working_directory='/working/dir',
  259. )
  260. assert device_map == (
  261. Pattern('/foo', device=55),
  262. Pattern('bar', device=66),
  263. )
  264. def test_device_map_patterns_with_existing_device_id_does_not_overwrite_it():
  265. flexmock(module).should_receive('get_existent_path_or_parent').replace_with(lambda path: path)
  266. flexmock(module.os).should_receive('stat').with_args('/foo').and_return(flexmock(st_dev=55))
  267. flexmock(module.os).should_receive('stat').with_args('/bar').and_return(flexmock(st_dev=100))
  268. device_map = module.device_map_patterns((Pattern('/foo'), Pattern('/bar', device=66)))
  269. assert device_map == (
  270. Pattern('/foo', device=55),
  271. Pattern('/bar', device=66),
  272. )
  273. @pytest.mark.parametrize(
  274. 'patterns,expected_patterns,one_file_system',
  275. (
  276. ((Pattern('/', device=1), Pattern('/root', device=1)), (Pattern('/', device=1),), False),
  277. ((Pattern('/', device=1), Pattern('/root/', device=1)), (Pattern('/', device=1),), False),
  278. (
  279. (Pattern('/', device=1), Pattern('/root', device=2)),
  280. (Pattern('/', device=1),),
  281. False,
  282. ),
  283. ((Pattern('/root', device=1), Pattern('/', device=1)), (Pattern('/', device=1),), False),
  284. (
  285. (Pattern('/root', device=1), Pattern('/root/foo', device=1)),
  286. (Pattern('/root', device=1),),
  287. False,
  288. ),
  289. (
  290. (Pattern('/root/', device=1), Pattern('/root/foo', device=1)),
  291. (Pattern('/root/', device=1),),
  292. False,
  293. ),
  294. (
  295. (Pattern('/root', device=1), Pattern('/root/foo/', device=1)),
  296. (Pattern('/root', device=1),),
  297. False,
  298. ),
  299. (
  300. (Pattern('/root', device=1), Pattern('/root/foo', device=2)),
  301. (Pattern('/root', device=1),),
  302. False,
  303. ),
  304. (
  305. (Pattern('/root/foo', device=1), Pattern('/root', device=1)),
  306. (Pattern('/root', device=1),),
  307. False,
  308. ),
  309. (
  310. (Pattern('/root', device=None), Pattern('/root/foo', device=None)),
  311. (Pattern('/root'), Pattern('/root/foo')),
  312. False,
  313. ),
  314. (
  315. (
  316. Pattern('/root', device=1),
  317. Pattern('/etc', device=1),
  318. Pattern('/root/foo/bar', device=1),
  319. ),
  320. (Pattern('/root', device=1), Pattern('/etc', device=1)),
  321. False,
  322. ),
  323. (
  324. (
  325. Pattern('/root', device=1),
  326. Pattern('/root/foo', device=1),
  327. Pattern('/root/foo/bar', device=1),
  328. ),
  329. (Pattern('/root', device=1),),
  330. False,
  331. ),
  332. (
  333. (Pattern('/dup', device=1), Pattern('/dup', device=1)),
  334. (Pattern('/dup', device=1),),
  335. False,
  336. ),
  337. (
  338. (Pattern('/foo', device=1), Pattern('/bar', device=1)),
  339. (Pattern('/foo', device=1), Pattern('/bar', device=1)),
  340. False,
  341. ),
  342. (
  343. (Pattern('/foo', device=1), Pattern('/bar', device=2)),
  344. (Pattern('/foo', device=1), Pattern('/bar', device=2)),
  345. False,
  346. ),
  347. ((Pattern('/root/foo', device=1),), (Pattern('/root/foo', device=1),), False),
  348. (
  349. (Pattern('/', device=1), Pattern('/root', Pattern_type.INCLUDE, device=1)),
  350. (Pattern('/', device=1), Pattern('/root', Pattern_type.INCLUDE, device=1)),
  351. False,
  352. ),
  353. (
  354. (Pattern('/root', Pattern_type.INCLUDE, device=1), Pattern('/', device=1)),
  355. (Pattern('/root', Pattern_type.INCLUDE, device=1), Pattern('/', device=1)),
  356. False,
  357. ),
  358. ((Pattern('/', device=1), Pattern('/root', device=1)), (Pattern('/', device=1),), True),
  359. ((Pattern('/', device=1), Pattern('/root/', device=1)), (Pattern('/', device=1),), True),
  360. (
  361. (Pattern('/', device=1), Pattern('/root', device=2)),
  362. (Pattern('/', device=1), Pattern('/root', device=2)),
  363. True,
  364. ),
  365. ((Pattern('/root', device=1), Pattern('/', device=1)), (Pattern('/', device=1),), True),
  366. (
  367. (Pattern('/root', device=1), Pattern('/root/foo', device=1)),
  368. (Pattern('/root', device=1),),
  369. True,
  370. ),
  371. (
  372. (Pattern('/root/', device=1), Pattern('/root/foo', device=1)),
  373. (Pattern('/root/', device=1),),
  374. True,
  375. ),
  376. (
  377. (Pattern('/root', device=1), Pattern('/root/foo/', device=1)),
  378. (Pattern('/root', device=1),),
  379. True,
  380. ),
  381. (
  382. (Pattern('/root', device=1), Pattern('/root/foo', device=2)),
  383. (Pattern('/root', device=1), Pattern('/root/foo', device=2)),
  384. True,
  385. ),
  386. (
  387. (Pattern('/root/foo', device=1), Pattern('/root', device=1)),
  388. (Pattern('/root', device=1),),
  389. True,
  390. ),
  391. (
  392. (Pattern('/root', device=None), Pattern('/root/foo', device=None)),
  393. (Pattern('/root'), Pattern('/root/foo')),
  394. True,
  395. ),
  396. (
  397. (
  398. Pattern('/root', device=1),
  399. Pattern('/etc', device=1),
  400. Pattern('/root/foo/bar', device=1),
  401. ),
  402. (Pattern('/root', device=1), Pattern('/etc', device=1)),
  403. True,
  404. ),
  405. (
  406. (
  407. Pattern('/root', device=1),
  408. Pattern('/root/foo', device=1),
  409. Pattern('/root/foo/bar', device=1),
  410. ),
  411. (Pattern('/root', device=1),),
  412. True,
  413. ),
  414. (
  415. (Pattern('/dup', device=1), Pattern('/dup', device=1)),
  416. (Pattern('/dup', device=1),),
  417. True,
  418. ),
  419. (
  420. (Pattern('/foo', device=1), Pattern('/bar', device=1)),
  421. (Pattern('/foo', device=1), Pattern('/bar', device=1)),
  422. True,
  423. ),
  424. (
  425. (Pattern('/foo', device=1), Pattern('/bar', device=2)),
  426. (Pattern('/foo', device=1), Pattern('/bar', device=2)),
  427. True,
  428. ),
  429. ((Pattern('/root/foo', device=1),), (Pattern('/root/foo', device=1),), True),
  430. (
  431. (Pattern('/', device=1), Pattern('/root', Pattern_type.INCLUDE, device=1)),
  432. (Pattern('/', device=1), Pattern('/root', Pattern_type.INCLUDE, device=1)),
  433. True,
  434. ),
  435. (
  436. (Pattern('/root', Pattern_type.INCLUDE, device=1), Pattern('/', device=1)),
  437. (Pattern('/root', Pattern_type.INCLUDE, device=1), Pattern('/', device=1)),
  438. True,
  439. ),
  440. ),
  441. )
  442. def test_deduplicate_patterns_omits_child_paths_based_on_device_and_one_file_system(
  443. patterns,
  444. expected_patterns,
  445. one_file_system,
  446. ):
  447. assert (
  448. module.deduplicate_patterns(patterns, {'one_file_system': one_file_system})
  449. == expected_patterns
  450. )
  451. def test_process_patterns_includes_patterns():
  452. flexmock(module).should_receive('deduplicate_patterns').and_return(
  453. (Pattern('foo'), Pattern('bar')),
  454. )
  455. flexmock(module).should_receive('device_map_patterns').and_return({})
  456. flexmock(module).should_receive('expand_patterns').with_args(
  457. (Pattern('foo'), Pattern('bar')),
  458. working_directory='/working',
  459. skip_paths=set(),
  460. ).and_return(()).once()
  461. assert module.process_patterns(
  462. (Pattern('foo'), Pattern('bar')),
  463. config={},
  464. working_directory='/working',
  465. ) == [Pattern('foo'), Pattern('bar')]
  466. def test_process_patterns_skips_expand_for_requested_paths():
  467. skip_paths = {flexmock()}
  468. flexmock(module).should_receive('deduplicate_patterns').and_return(
  469. (Pattern('foo'), Pattern('bar')),
  470. )
  471. flexmock(module).should_receive('device_map_patterns').and_return({})
  472. flexmock(module).should_receive('expand_patterns').with_args(
  473. (Pattern('foo'), Pattern('bar')),
  474. working_directory='/working',
  475. skip_paths=skip_paths,
  476. ).and_return(()).once()
  477. assert module.process_patterns(
  478. (Pattern('foo'), Pattern('bar')),
  479. config={},
  480. working_directory='/working',
  481. skip_expand_paths=skip_paths,
  482. ) == [Pattern('foo'), Pattern('bar')]