test_dputhelper.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766
  1. # -*- coding: utf-8; -*-
  2. #
  3. # test/test_dputhelper.py
  4. # Part of ‘dput’, a Debian package upload toolkit.
  5. #
  6. # Copyright © 2015 Ben Finney <ben+python@benfinney.id.au>
  7. #
  8. # This is free software: you may copy, modify, and/or distribute this work
  9. # under the terms of the GNU General Public License as published by the
  10. # Free Software Foundation; version 3 of that license or any later version.
  11. # No warranty expressed or implied. See the file ‘LICENSE.GPL-3’ for details.
  12. """ Unit tests for ‘dput.helper.dputhelper’ module. """
  13. from __future__ import (absolute_import, unicode_literals)
  14. import sys
  15. import os
  16. import collections
  17. import itertools
  18. import tempfile
  19. import doctest
  20. import textwrap
  21. import testtools
  22. import testtools.matchers
  23. import testscenarios
  24. import pkg_resources
  25. __package__ = str("test")
  26. __import__(__package__)
  27. sys.path.insert(1, os.path.dirname(os.path.dirname(__file__)))
  28. from dput.helper import dputhelper
  29. from .helper import (
  30. mock,
  31. StringIO,
  32. patch_sys_argv,
  33. patch_system_interfaces,
  34. patch_time_time,
  35. patch_os_spawnv,
  36. SubprocessDouble,
  37. )
  38. class spawnv_TestCase(
  39. testscenarios.WithScenarios,
  40. testtools.TestCase):
  41. """ Test cases for `spawnv` function. """
  42. default_args = collections.OrderedDict([
  43. ('mode', object()),
  44. ('file', tempfile.mktemp()),
  45. ('args', ["arg-{}".format(n) for n in range(5)]),
  46. ])
  47. scenarios = [
  48. ('success', {
  49. 'test_args': default_args.copy(),
  50. 'os_spawnv_scenario_name': 'success',
  51. }),
  52. ('failure', {
  53. 'test_args': default_args.copy(),
  54. 'os_spawnv_scenario_name': 'failure',
  55. 'expected_output': textwrap.dedent("""\
  56. Warning: The execution of '...' as
  57. '...'
  58. returned a nonzero exit code.
  59. """)
  60. }),
  61. ('not-found', {
  62. 'test_args': default_args.copy(),
  63. 'os_spawnv_scenario_name': 'not_found',
  64. 'expected_output': textwrap.dedent("""\
  65. Error: Failed to execute '...'.
  66. The file may not exist or not be executable.
  67. """)
  68. }),
  69. ]
  70. def setUp(self):
  71. """ Set up test fixtures. """
  72. super(spawnv_TestCase, self).setUp()
  73. patch_system_interfaces(self)
  74. patch_os_spawnv(self)
  75. self.set_subprocess_double()
  76. def set_subprocess_double(self):
  77. """ Set the test double for the subprocess. """
  78. double = SubprocessDouble(
  79. self.test_args['file'],
  80. self.test_args['args'])
  81. double.register_for_testcase(self)
  82. double.set_os_spawnv_scenario(self.os_spawnv_scenario_name)
  83. self.subprocess_double = double
  84. def test_calls_os_spawnv_with_specified_args(self):
  85. """ Should call `os.spawnv` with specified arguments. """
  86. dputhelper.spawnv(*self.test_args.values())
  87. os.spawnv.assert_called_with(*self.test_args.values())
  88. def test_emits_expected_output(self):
  89. """ Should emit the expected output messages. """
  90. if not hasattr(self, 'expected_output'):
  91. self.expected_output = ""
  92. dputhelper.spawnv(*self.test_args.values())
  93. self.assertThat(
  94. sys.stdout.getvalue(),
  95. testtools.matchers.DocTestMatches(
  96. self.expected_output, flags=doctest.ELLIPSIS))
  97. class TimestampFile_TestCase(testtools.TestCase):
  98. """ Base for test cases for the `TimestampFile` class. """
  99. scenarios = NotImplemented
  100. def setUp(self):
  101. """ Set up test fixtures. """
  102. super(TimestampFile_TestCase, self).setUp()
  103. patch_time_time(self, itertools.count(1))
  104. self.test_file = StringIO()
  105. self.instance = dputhelper.TimestampFile(self.test_file)
  106. class TimestampFile_InstanceTestCase(
  107. testscenarios.WithScenarios,
  108. TimestampFile_TestCase):
  109. """ Test cases for `TimestampFile` instance creation. """
  110. scenarios = [
  111. ('default', {}),
  112. ]
  113. def test_has_specified_file(self):
  114. """ Should have specified file object as `f` attribute. """
  115. self.assertIs(self.test_file, self.instance.f)
  116. def test_has_attributes_from_component_file(self):
  117. """ Should have attributes directly from component file. """
  118. attr_names = [
  119. 'b0gUs',
  120. 'mode', 'name', 'encoding',
  121. 'readable', 'seekable', 'writable',
  122. 'read', 'seek', 'tell',
  123. ]
  124. for attr_name in attr_names:
  125. expected_attr_value = getattr(self.test_file, attr_name, None)
  126. self.expectThat(
  127. getattr(self.instance, attr_name, None),
  128. testtools.matchers.Equals(expected_attr_value))
  129. class TimestampFile_write_TestCase(
  130. testscenarios.WithScenarios,
  131. TimestampFile_TestCase):
  132. """ Test cases for `TimestampFile.write` method. """
  133. scenarios = [
  134. ('empty', {
  135. 'test_output': "",
  136. 'expected_lines': [],
  137. }),
  138. ('lines-one', {
  139. 'test_output': textwrap.dedent("""\
  140. Lorem ipsum, dolor sit amet.
  141. """),
  142. 'expected_lines': [
  143. "1: Lorem ipsum, dolor sit amet.",
  144. "",
  145. ],
  146. }),
  147. ('lines-three', {
  148. 'test_output': textwrap.dedent("""\
  149. Lorem ipsum, dolor sit amet,
  150. consectetur adipiscing elit.
  151. Integer non pulvinar risus, sed malesuada diam.
  152. """),
  153. 'expected_lines': [
  154. "1: Lorem ipsum, dolor sit amet,",
  155. "2: consectetur adipiscing elit.",
  156. "3: Integer non pulvinar risus, sed malesuada diam.",
  157. "",
  158. ],
  159. }),
  160. ('lines-two-with-trail', {
  161. 'test_output': textwrap.dedent("""\
  162. Lorem ipsum, dolor sit amet,
  163. consectetur adipiscing elit.
  164. Integer non pulvinar risus"""),
  165. 'expected_lines': [
  166. "1: Lorem ipsum, dolor sit amet,",
  167. "2: consectetur adipiscing elit.",
  168. "3: Integer non pulvinar risus",
  169. ],
  170. }),
  171. ]
  172. def test_has_expected_content_for_output(self):
  173. """ Should have expected content for specified `write` output. """
  174. self.instance.write(self.test_output)
  175. expected_lines = self.expected_lines
  176. if self.expected_lines:
  177. if self.expected_lines[-1]:
  178. # Expecting an unterminated final line.
  179. expected_lines = self.expected_lines[:-1]
  180. expected_lines.append("")
  181. else:
  182. # Expecting no output following newline.
  183. expected_lines = self.expected_lines
  184. expected_content = "\n".join(expected_lines)
  185. self.assertEqual(expected_content, self.instance.f.getvalue())
  186. class TimestampFile_close_TestCase(
  187. testscenarios.WithScenarios,
  188. TimestampFile_TestCase):
  189. """ Test cases for `TimestampFile.write` method. """
  190. scenarios = TimestampFile_write_TestCase.scenarios
  191. @testtools.skip("TimestampFile.close method is broken")
  192. def test_has_expected_final_line(self):
  193. """ Should have expected final line. """
  194. self.instance.write(self.test_output)
  195. self.instance.f.seek(0)
  196. self.instance.close()
  197. expected_content = self.expected_lines[-1]
  198. self.assertEqual(expected_content, self.instance.f.getvalue())
  199. class FileWithProgress_TestCase(
  200. testscenarios.WithScenarios,
  201. testtools.TestCase):
  202. """ Base for test cases for the `FileWithProgress` class. """
  203. default_args = {
  204. 'ptype': 0,
  205. 'progressf': sys.__stdout__,
  206. 'size': -1,
  207. 'step': 1024,
  208. }
  209. def setUp(self):
  210. """ Set up test fixtures. """
  211. super(FileWithProgress_TestCase, self).setUp()
  212. patch_system_interfaces(self)
  213. self.test_file = StringIO(
  214. getattr(self, 'content', ""))
  215. self.set_test_args()
  216. self.make_instance()
  217. def set_test_args(self):
  218. """ Set the arguments for the test instance constructor. """
  219. self.test_args = dict(
  220. f=self.test_file,
  221. )
  222. if hasattr(self, 'test_ptype'):
  223. self.test_args['ptype'] = self.test_ptype
  224. if hasattr(self, 'test_progressf'):
  225. self.test_args['progressf'] = self.test_progressf
  226. if hasattr(self, 'test_size'):
  227. self.test_args['size'] = self.test_size
  228. if hasattr(self, 'test_step'):
  229. self.test_args['step'] = self.test_step
  230. def make_instance(self):
  231. """ Make the test instance of the class. """
  232. self.instance = dputhelper.FileWithProgress(**self.test_args)
  233. class FileWithProgress_ArgsTestCase(FileWithProgress_TestCase):
  234. """ Test cases for constructor arguments for `FileWithProgress` class. """
  235. scenarios = [
  236. ('simple', {}),
  237. ('all args', {
  238. 'test_ptype': 1,
  239. 'test_progressf': StringIO(),
  240. 'test_size': 10,
  241. 'test_step': 2,
  242. }),
  243. ]
  244. def test_has_specified_file(self):
  245. """ Should have specified file object as `f` attribute. """
  246. self.assertIs(self.test_file, self.instance.f)
  247. def test_has_specified_ptype(self):
  248. """ Should have specified progress type value as `ptype` attribute. """
  249. expected_ptype = getattr(
  250. self, 'test_ptype', self.default_args['ptype'])
  251. self.assertEqual(expected_ptype, self.instance.ptype)
  252. def test_has_specified_progressf(self):
  253. """ Should have specified progress file as `progressf` attribute. """
  254. expected_progressf = getattr(
  255. self, 'test_progressf', self.default_args['progressf'])
  256. self.assertEqual(expected_progressf, self.instance.progressf)
  257. def test_has_specified_size(self):
  258. """ Should have specified size value as `size` attribute. """
  259. expected_size = getattr(
  260. self, 'test_size', self.default_args['size'])
  261. self.assertEqual(expected_size, self.instance.size)
  262. def test_has_specified_step(self):
  263. """ Should have specified step value as `step` attribute. """
  264. expected_step = getattr(
  265. self, 'test_step', self.default_args['step'])
  266. self.assertEqual(expected_step, self.instance.step)
  267. def test_has_attributes_from_component_file(self):
  268. """ Should have attributes directly from component file. """
  269. attr_names = [
  270. 'b0gUs',
  271. 'mode', 'name', 'encoding',
  272. 'readable', 'seekable', 'writable',
  273. 'seek', 'tell', 'write',
  274. ]
  275. for attr_name in attr_names:
  276. expected_attr_value = getattr(self.test_file, attr_name, None)
  277. self.expectThat(
  278. getattr(self.instance, attr_name, None),
  279. testtools.matchers.Equals(expected_attr_value))
  280. class FileWithProgress_OutputTestCase(FileWithProgress_TestCase):
  281. """ Test cases for progress output for `FileWithProgress` class. """
  282. content_scenarios = [
  283. ('empty', {
  284. 'content': "",
  285. }),
  286. ('10 000 chars', {
  287. 'content': "0123456789\n" * 1000,
  288. }),
  289. ('10 000 000 chars', {
  290. 'content': "0123456789\n" * 1000000,
  291. }),
  292. ]
  293. ptype_scenarios = [
  294. ('default', {}),
  295. ('ptype 0', {'test_ptype': 0}),
  296. ('ptype 1', {'test_ptype': 1}),
  297. ('ptype 2', {'test_ptype': 2}),
  298. ]
  299. step_scenarios = [
  300. ('default', {}),
  301. ('step 5', {'test_step': 5}),
  302. ('step 500', {'test_step': 500}),
  303. ('step 50 000', {'test_step': 50000}),
  304. ]
  305. scenarios = testscenarios.multiply_scenarios(
  306. content_scenarios, ptype_scenarios, step_scenarios)
  307. def setUp(self):
  308. """ Set up test fixtures. """
  309. super(FileWithProgress_OutputTestCase, self).setUp()
  310. self.test_file = StringIO(self.content)
  311. self.test_size = len(self.content)
  312. self.test_progressf = StringIO()
  313. self.set_test_args()
  314. self.make_instance()
  315. self.set_expected_output()
  316. def set_expected_output(self):
  317. """ Set the expected output for this test case. """
  318. ptype = getattr(self, 'test_ptype', self.default_args['ptype'])
  319. if ptype == 1:
  320. self.expected_output = "/"
  321. elif ptype == 2:
  322. step = getattr(self, 'test_step', 1024)
  323. total_bytes = len(self.content)
  324. total_hunks = int(total_bytes / step)
  325. total_hunks_text = "{size}k".format(size=total_hunks)
  326. total_steps = int(
  327. (total_bytes + step - 1) / step)
  328. total_steps_text = "{size}k".format(size=total_steps)
  329. progress_text = "{hunks}/{steps}".format(
  330. hunks=total_hunks_text, steps=total_steps_text)
  331. self.expected_output = progress_text
  332. else:
  333. # `ptype == 0` specifies no progress output.
  334. self.expected_output = ""
  335. if not self.content:
  336. # No progress output for an empty file.
  337. self.expected_output = ""
  338. def test_emits_expected_output_for_content(self):
  339. """ Should emit expected output for file content. """
  340. self.instance.read()
  341. output_stream_content = self.test_progressf.getvalue()
  342. self.assertEqual(
  343. self.expected_output, output_stream_content)
  344. def test_clears_output_on_close(self):
  345. """ Should clear progress output when closed. """
  346. self.instance.read()
  347. self.instance.close()
  348. expected_output = (
  349. self.expected_output
  350. + len(self.expected_output) * "\b"
  351. + len(self.expected_output) * " "
  352. + len(self.expected_output) * "\b"
  353. )
  354. output_stream_content = self.test_progressf.getvalue()
  355. self.assertEqual(expected_output, output_stream_content)
  356. def patch_filewithprogress(testcase):
  357. """ Patch the `FileWithProgress` class for the test case. """
  358. if not hasattr(testcase, 'fake_filewithprogress'):
  359. testcase.fake_filewithprogress = mock.MagicMock(
  360. spec=dputhelper.FileWithProgress, name="FileWithProgress")
  361. def fake_filewithprogress_factory(
  362. f, ptype=0, progressf=sys.stdout, size=-1, step=1024):
  363. result = testcase.fake_filewithprogress
  364. result.f = f
  365. result.ptype = ptype
  366. result.progressf = progressf
  367. result.size = size
  368. result.step = step
  369. return result
  370. func_patcher = mock.patch.object(
  371. dputhelper, "FileWithProgress",
  372. side_effect=fake_filewithprogress_factory)
  373. func_patcher.start()
  374. testcase.addCleanup(func_patcher.stop)
  375. GetoptResult = collections.namedtuple('GetoptResult', ['optlist', 'args'])
  376. class getopt_SuccessTestCase(
  377. testscenarios.WithScenarios,
  378. testtools.TestCase):
  379. """ Success test cases for `getopt` function. """
  380. scenarios = [
  381. ('empty', {
  382. 'test_argv': [object()],
  383. 'expected_result': GetoptResult(
  384. optlist=[], args=[]),
  385. }),
  386. ('no opts', {
  387. 'test_argv': [object(), "foo", "bar", "baz"],
  388. 'expected_result': GetoptResult(
  389. optlist=[], args=["foo", "bar", "baz"]),
  390. }),
  391. ('only short opts', {
  392. 'test_argv': [object(), "-a", "-b", "-c"],
  393. 'test_shortopts': "axbycz",
  394. 'expected_result': GetoptResult(
  395. optlist=[
  396. ('-a', ""),
  397. ('-b', ""),
  398. ('-c', ""),
  399. ],
  400. args=[]),
  401. }),
  402. ('only long opts', {
  403. 'test_argv': [object(), "--alpha", "--beta", "--gamma"],
  404. 'test_longopts': [
  405. "wibble", "alpha", "wobble",
  406. "beta", "wubble", "gamma",
  407. ],
  408. 'expected_result': GetoptResult(
  409. optlist=[
  410. ('--alpha', ""),
  411. ('--beta', ""),
  412. ('--gamma', ""),
  413. ],
  414. args=[]),
  415. }),
  416. ('long opt prefix', {
  417. 'test_argv': [object(), "--al", "--be", "--ga"],
  418. 'test_longopts': [
  419. "wibble", "alpha", "wobble",
  420. "beta", "wubble", "gamma",
  421. ],
  422. 'expected_result': GetoptResult(
  423. optlist=[
  424. ('--alpha', ""),
  425. ('--beta', ""),
  426. ('--gamma', ""),
  427. ],
  428. args=[]),
  429. }),
  430. ('short opt cluster', {
  431. 'test_argv': [object(), "-abc"],
  432. 'test_shortopts': "abc",
  433. 'expected_result': GetoptResult(
  434. optlist=[
  435. ('-a', ""),
  436. ('-b', ""),
  437. ('-c', ""),
  438. ],
  439. args=[]),
  440. }),
  441. ('short with args', {
  442. 'test_argv': [object(), "-a", "-b", "eggs", "-cbeans"],
  443. 'test_shortopts': "ab:c:",
  444. 'expected_result': GetoptResult(
  445. optlist=[
  446. ('-a', ""),
  447. ('-b', "eggs"),
  448. ('-c', "beans"),
  449. ],
  450. args=[]),
  451. }),
  452. ('long with args', {
  453. 'test_argv': [
  454. object(),
  455. "--alpha",
  456. "--beta=eggs",
  457. "--gamma", "beans"],
  458. 'test_longopts': [
  459. "wibble", "alpha", "wobble",
  460. "beta=", "wubble", "gamma=",
  461. ],
  462. 'expected_result': GetoptResult(
  463. optlist=[
  464. ('--alpha', ""),
  465. ('--beta', "eggs"),
  466. ('--gamma', "beans"),
  467. ],
  468. args=[]),
  469. }),
  470. ('long with optional args', {
  471. 'test_argv': [
  472. object(),
  473. "--alpha",
  474. "--beta=eggs",
  475. "--gamma"],
  476. 'test_longopts': [
  477. "wibble", "alpha", "wobble",
  478. "beta==", "wubble", "gamma==",
  479. ],
  480. 'expected_result': GetoptResult(
  481. optlist=[
  482. ('--alpha', ""),
  483. ('--beta', "eggs"),
  484. ('--gamma', ""),
  485. ],
  486. args=[]),
  487. }),
  488. ('single hyphen arg', {
  489. 'test_argv': [object(), "-a", "-b", "-c", "-"],
  490. 'test_shortopts': "axbycz",
  491. 'expected_result': GetoptResult(
  492. optlist=[
  493. ('-a', ""),
  494. ('-b', ""),
  495. ('-c', ""),
  496. ],
  497. args=["-"]),
  498. }),
  499. ('explicit end of opts', {
  500. 'test_argv': [
  501. object(),
  502. "--alpha",
  503. "--beta",
  504. "--",
  505. "--spam"],
  506. 'test_longopts': [
  507. "wibble", "alpha", "wobble",
  508. "beta", "wubble", "gamma",
  509. ],
  510. 'expected_result': GetoptResult(
  511. optlist=[
  512. ('--alpha', ""),
  513. ('--beta', ""),
  514. ],
  515. args=["--spam"]),
  516. }),
  517. ]
  518. def test_returns_expected_result_for_argv(self):
  519. """ Should return expected result for specified argv. """
  520. shortopts = getattr(self, 'test_shortopts', "")
  521. longopts = getattr(self, 'test_longopts', "")
  522. result = dputhelper.getopt(
  523. self.test_argv[1:], shortopts, longopts)
  524. self.assertEqual(self.expected_result, result)
  525. class getopt_ErrorTestCase(
  526. testscenarios.WithScenarios,
  527. testtools.TestCase):
  528. """ Error test cases for `getopt` function. """
  529. scenarios = [
  530. ('short opt unknown', {
  531. 'test_argv': [object(), "-a", "-b", "-z", "-c"],
  532. 'test_shortopts': "abc",
  533. 'expected_error': dputhelper.DputException,
  534. }),
  535. ('short missing arg', {
  536. 'test_argv': [object(), "-a", "-b", "-c"],
  537. 'test_shortopts': "abc:",
  538. 'expected_error': dputhelper.DputException,
  539. }),
  540. ('long opt unknown', {
  541. 'test_argv': [
  542. object(), "--alpha", "--beta", "--zeta", "--gamma"],
  543. 'test_longopts': [
  544. "alpha", "beta", "gamma"],
  545. 'expected_error': dputhelper.DputException,
  546. }),
  547. ('long ambiguous prefix', {
  548. 'test_argv': [
  549. object(), "--alpha", "--be", "--gamma"],
  550. 'test_longopts': [
  551. "alpha", "beta", "bettong", "bertha", "gamma"],
  552. 'expected_error': dputhelper.DputException,
  553. }),
  554. ('long missing arg', {
  555. 'test_argv': [object(), "--alpha", "--beta", "--gamma"],
  556. 'test_longopts': [
  557. "alpha", "beta", "gamma="],
  558. 'expected_error': dputhelper.DputException,
  559. }),
  560. ('long unexpected arg', {
  561. 'test_argv': [
  562. object(), "--alpha", "--beta=beans", "--gamma"],
  563. 'test_longopts': [
  564. "alpha", "beta", "gamma"],
  565. 'expected_error': dputhelper.DputException,
  566. }),
  567. ]
  568. def test_raises_expected_error_for_argv(self):
  569. """ Should raise expected error for specified argv. """
  570. shortopts = getattr(self, 'test_shortopts', "")
  571. longopts = getattr(self, 'test_longopts', "")
  572. with testtools.ExpectedException(self.expected_error):
  573. dputhelper.getopt(
  574. self.test_argv[1:], shortopts, longopts)
  575. def patch_getopt(testcase):
  576. """ Patch the `getopt` function for the specified test case. """
  577. def fake_getopt(args, shortopts, longopts):
  578. result = (testcase.getopt_opts, testcase.getopt_args)
  579. return result
  580. func_patcher = mock.patch.object(
  581. dputhelper, "getopt", side_effect=fake_getopt)
  582. func_patcher.start()
  583. testcase.addCleanup(func_patcher.stop)
  584. class get_progname_TestCase(
  585. testscenarios.WithScenarios,
  586. testtools.TestCase):
  587. """ Test cases for `get_progname` function. """
  588. command_name_scenarios = [
  589. ('command-simple', {
  590. 'argv_zero': "amet",
  591. 'expected_progname': "amet",
  592. }),
  593. ('command-relative', {
  594. 'argv_zero': "lorem/ipsum/dolor/sit/amet",
  595. 'expected_progname': "amet",
  596. }),
  597. ('command-absolute', {
  598. 'argv_zero': "/lorem/ipsum/dolor/sit/amet",
  599. 'expected_progname': "amet",
  600. }),
  601. ]
  602. subsequent_args_scenarios = [
  603. ('args-empty', {
  604. 'argv_remain': [],
  605. }),
  606. ('args-one-word', {
  607. 'argv_remain': ["spam"],
  608. }),
  609. ('args-three-words', {
  610. 'argv_remain': ["spam", "beans", "eggs"],
  611. }),
  612. ('args-one-option', {
  613. 'argv_remain': ["--spam"],
  614. }),
  615. ]
  616. scenarios = testscenarios.multiply_scenarios(
  617. command_name_scenarios, subsequent_args_scenarios)
  618. def setUp(self):
  619. """ Set up test fixtures. """
  620. super(get_progname_TestCase, self).setUp()
  621. self.test_argv = [self.argv_zero] + self.argv_remain
  622. def test_returns_expected_progname(self):
  623. """ Should return expected progname value for command line. """
  624. result = dputhelper.get_progname(self.test_argv)
  625. self.assertEqual(self.expected_progname, result)
  626. def test_queries_sys_argv_if_argv_unspecified(self):
  627. """ Should query `sys.argv` if no `argv` specified. """
  628. self.sys_argv = self.test_argv
  629. patch_sys_argv(self)
  630. result = dputhelper.get_progname()
  631. self.assertEqual(self.expected_progname, result)
  632. def patch_pkg_resources_get_distribution(testcase):
  633. """ Patch `pkg_resources.get_distribution` for the test case. """
  634. if not hasattr(testcase, 'fake_distribution'):
  635. testcase.fake_distribution = mock.MagicMock(pkg_resources.Distribution)
  636. func_patcher = mock.patch.object(
  637. pkg_resources, 'get_distribution',
  638. return_value=testcase.fake_distribution)
  639. func_patcher.start()
  640. testcase.addCleanup(func_patcher.stop)
  641. class get_distribution_version_TestCase(
  642. testscenarios.WithScenarios,
  643. testtools.TestCase):
  644. """ Test cases for `get_distribution_version` function. """
  645. scenarios = [
  646. ('simple', {
  647. 'fake_distribution': mock.MagicMock(
  648. project_name="lorem", version="42.23"),
  649. }),
  650. ]
  651. def setUp(self):
  652. """ Set up test fixtures. """
  653. super(get_distribution_version_TestCase, self).setUp()
  654. patch_pkg_resources_get_distribution(self)
  655. def test_returns_expected_result(self):
  656. """ Should return expected version for the distribution. """
  657. result = dputhelper.get_distribution_version()
  658. expected_version = self.fake_distribution.version
  659. self.assertEqual(expected_version, result)
  660. # Local variables:
  661. # coding: utf-8
  662. # mode: python
  663. # End:
  664. # vim: fileencoding=utf-8 filetype=python :