Merge ~raharper/curtin:fix/unittest-prevent-subp-by-default into curtin:master

Proposed by Ryan Harper
Status: Merged
Approved by: Ryan Harper
Approved revision: 56df4215c9d7b4671f435ca84284f1bd52f89705
Merge reported by: Server Team CI bot
Merged at revision: not available
Proposed branch: ~raharper/curtin:fix/unittest-prevent-subp-by-default
Merge into: curtin:master
Diff against target: 457 lines (+165/-77)
10 files modified
tests/unittests/helpers.py (+52/-3)
tests/unittests/test_apt_custom_sources_list.py (+2/-0)
tests/unittests/test_apt_source.py (+2/-0)
tests/unittests/test_block.py (+11/-7)
tests/unittests/test_block_zfs.py (+24/-20)
tests/unittests/test_clear_holders.py (+5/-2)
tests/unittests/test_commands_block_meta.py (+4/-0)
tests/unittests/test_commands_collect_logs.py (+2/-0)
tests/unittests/test_gpg.py (+50/-44)
tests/unittests/test_util.py (+13/-1)
Reviewer Review Type Date Requested Status
Server Team CI bot continuous-integration Approve
Dan Watkins (community) Approve
Review via email: mp+382604@code.launchpad.net

Commit message

unittest: do not allow util.subp by default

Bring over our util.subp mock from cloud-init. Rework unittest to
enable subp as needed.

LP: #1873913

To post a comment you must log in.
Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Dan Watkins (oddbloke) wrote :

Good thinking to pull this over, I have some comments inline.

review: Needs Fixing
Revision history for this message
Ryan Harper (raharper) wrote :

Let me drop the logging bits; I replied in line; I think the per-test enablement with a tearDown() is reasonable. Let me see if I can sub-class the main test class and put the subp_enabled ones within that; hoping to avoid duplicating code from one class to another when all we need is to enable/disable subp.

Revision history for this message
Dan Watkins (oddbloke) wrote :

On Mon, Apr 20, 2020 at 08:27:59PM -0000, Ryan Harper wrote:
> Let me drop the logging bits

Thanks!

> > diff --git a/tests/unittests/test_block_zfs.py b/tests/unittests/test_block_zfs.py
> > index 3508d4b..2be7c41 100644
> > --- a/tests/unittests/test_block_zfs.py
> > +++ b/tests/unittests/test_block_zfs.py
> > @@ -425,6 +425,9 @@ class TestAssertZfsSupported(CiTestCase):
> > def setUp(self):
> > super(TestAssertZfsSupported, self).setUp()
> >
> > + def tearDown(self):
> > + self.allowed_subp = False
>
> I think this is reasonable safe. It's off by default. The test-cases
> that know they need it set it at the start of their test_*; this just
> retains the default off unless you need it and I think help reduce the
> scope of when subp is enabled.

Yep, I think the case I was worried about isn't applicable here, so
let's stick with this.

Revision history for this message
Dan Watkins (oddbloke) wrote :

Sorry, noticed one more thing! Other than that, this looks good now, thanks for the refactors!

Revision history for this message
Ryan Harper (raharper) wrote :

Fixing

Revision history for this message
Dan Watkins (oddbloke) wrote :

Thanks!

review: Approve
Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Server Team CI bot (server-team-bot) wrote :

Commit message lints:
- Line #2 has 15 too many characters. Line starts with: "Bring over our util.subp"...

review: Needs Fixing
Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Needs Fixing (continuous-integration)
56df421... by Ryan Harper

Fix new calls of util.subp and multipath.is_mpath_member

Revision history for this message
Ryan Harper (raharper) wrote :

wait for CI to say yes, then mark approve

Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Server Team CI bot (server-team-bot) wrote :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/tests/unittests/helpers.py b/tests/unittests/helpers.py
index 9514745..2f5e51a 100644
--- a/tests/unittests/helpers.py
+++ b/tests/unittests/helpers.py
@@ -1,6 +1,5 @@
1# This file is part of curtin. See LICENSE file for copyright and license info.1# This file is part of curtin. See LICENSE file for copyright and license info.
22
3import contextlib
4import imp3import imp
5import importlib4import importlib
6import mock5import mock
@@ -10,9 +9,11 @@ import shutil
10import string9import string
11import tempfile10import tempfile
12from unittest import TestCase, skipIf11from unittest import TestCase, skipIf
1312from contextlib import contextmanager
14from curtin import util13from curtin import util
1514
15_real_subp = util.subp
16
1617
17def builtin_module_name():18def builtin_module_name():
18 options = ('builtins', '__builtin__')19 options = ('builtins', '__builtin__')
@@ -27,7 +28,7 @@ def builtin_module_name():
27 return name28 return name
2829
2930
30@contextlib.contextmanager31@contextmanager
31def simple_mocked_open(content=None):32def simple_mocked_open(content=None):
32 if not content:33 if not content:
33 content = ''34 content = ''
@@ -54,6 +55,54 @@ def skipUnlessJsonSchema():
54class CiTestCase(TestCase):55class CiTestCase(TestCase):
55 """Common testing class which all curtin unit tests subclass."""56 """Common testing class which all curtin unit tests subclass."""
5657
58 allowed_subp = False
59 SUBP_SHELL_TRUE = "shell=True"
60
61 @contextmanager
62 def allow_subp(self, allowed_subp):
63 orig = self.allowed_subp
64 try:
65 self.allowed_subp = allowed_subp
66 yield
67 finally:
68 self.allowed_subp = orig
69
70 def setUp(self):
71 super(CiTestCase, self).setUp()
72 if self.allowed_subp is True:
73 util.subp = _real_subp
74 else:
75 util.subp = self._fake_subp
76
77 def _fake_subp(self, *args, **kwargs):
78 if 'args' in kwargs:
79 cmd = kwargs['args']
80 else:
81 cmd = args[0]
82
83 if not isinstance(cmd, str):
84 cmd = cmd[0]
85 pass_through = False
86 if not isinstance(self.allowed_subp, (list, bool)):
87 raise TypeError("self.allowed_subp supports list or bool.")
88 if isinstance(self.allowed_subp, bool):
89 pass_through = self.allowed_subp
90 else:
91 pass_through = (
92 (cmd in self.allowed_subp) or
93 (self.SUBP_SHELL_TRUE in self.allowed_subp and
94 kwargs.get('shell')))
95 if pass_through:
96 return _real_subp(*args, **kwargs)
97 raise Exception(
98 "called subp. set self.allowed_subp=True to allow\n subp(%s)" %
99 ', '.join([str(repr(a)) for a in args] +
100 ["%s=%s" % (k, repr(v)) for k, v in kwargs.items()]))
101
102 def tearDown(self):
103 util.subp = _real_subp
104 super(CiTestCase, self).tearDown()
105
57 def add_patch(self, target, attr, **kwargs):106 def add_patch(self, target, attr, **kwargs):
58 """Patches specified target object and sets it as attr on test107 """Patches specified target object and sets it as attr on test
59 instance also schedules cleanup"""108 instance also schedules cleanup"""
diff --git a/tests/unittests/test_apt_custom_sources_list.py b/tests/unittests/test_apt_custom_sources_list.py
index fb6eb0c..bf004b1 100644
--- a/tests/unittests/test_apt_custom_sources_list.py
+++ b/tests/unittests/test_apt_custom_sources_list.py
@@ -93,7 +93,9 @@ class TestAptSourceConfigSourceList(CiTestCase):
93 def setUp(self):93 def setUp(self):
94 super(TestAptSourceConfigSourceList, self).setUp()94 super(TestAptSourceConfigSourceList, self).setUp()
95 self.new_root = self.tmp_dir()95 self.new_root = self.tmp_dir()
96 self.add_patch('curtin.util.subp', 'm_subp')
96 # self.patchUtils(self.new_root)97 # self.patchUtils(self.new_root)
98 self.m_subp.return_value = ("amd64", "")
9799
98 def _apt_source_list(self, cfg, expected):100 def _apt_source_list(self, cfg, expected):
99 "_apt_source_list - Test rendering from template (generic)"101 "_apt_source_list - Test rendering from template (generic)"
diff --git a/tests/unittests/test_apt_source.py b/tests/unittests/test_apt_source.py
index 353cdf8..6ae5579 100644
--- a/tests/unittests/test_apt_source.py
+++ b/tests/unittests/test_apt_source.py
@@ -75,6 +75,8 @@ class TestAptSourceConfig(CiTestCase):
75 self.aptlistfile3 = os.path.join(self.tmp, "single-deb3.list")75 self.aptlistfile3 = os.path.join(self.tmp, "single-deb3.list")
76 self.join = os.path.join76 self.join = os.path.join
77 self.matcher = re.compile(ADD_APT_REPO_MATCH).search77 self.matcher = re.compile(ADD_APT_REPO_MATCH).search
78 self.add_patch('curtin.util.subp', 'm_subp')
79 self.m_subp.return_value = ('s390x', '')
7880
79 @staticmethod81 @staticmethod
80 def _add_apt_sources(*args, **kwargs):82 def _add_apt_sources(*args, **kwargs):
diff --git a/tests/unittests/test_block.py b/tests/unittests/test_block.py
index 707386f..dcdcf51 100644
--- a/tests/unittests/test_block.py
+++ b/tests/unittests/test_block.py
@@ -522,6 +522,12 @@ class TestPartTableSignature(CiTestCase):
522 gpt_content_4k = b'\x00' * 0x800 + b'EFI PART' + b'\x00' * (0x800 - 8)522 gpt_content_4k = b'\x00' * 0x800 + b'EFI PART' + b'\x00' * (0x800 - 8)
523 null_content = b'\x00' * 0xf00523 null_content = b'\x00' * 0xf00
524524
525 def setUp(self):
526 super(TestPartTableSignature, self).setUp()
527 self.add_patch('curtin.util.subp', 'm_subp')
528 self.m_subp.side_effect = iter([
529 util.ProcessExecutionError(stdout="", stderr="", exit_code=1)])
530
525 def _test_util_load_file(self, content, device, read_len, offset, decode):531 def _test_util_load_file(self, content, device, read_len, offset, decode):
526 return (bytes if not decode else str)(content[offset:offset+read_len])532 return (bytes if not decode else str)(content[offset:offset+read_len])
527533
@@ -577,15 +583,13 @@ class TestPartTableSignature(CiTestCase):
577 (self.assertTrue if expected else self.assertFalse)(583 (self.assertTrue if expected else self.assertFalse)(
578 block.check_efi_signature(self.blockdev))584 block.check_efi_signature(self.blockdev))
579585
580 @mock.patch('curtin.block.util.subp')586 def test_check_vtoc_signature_finds_vtoc_returns_true(self):
581 def test_check_vtoc_signature_finds_vtoc_returns_true(self, mock_subp):587 self.m_subp.side_effect = iter([("vtoc.....ok", "")])
582 mock_subp.return_value = ("vtoc.....ok", "")
583 self.assertTrue(block.check_vtoc_signature(self.blockdev))588 self.assertTrue(block.check_vtoc_signature(self.blockdev))
584589
585 @mock.patch('curtin.block.util.subp')590 def test_check_vtoc_signature_returns_false_with_no_sig(self):
586 def test_check_vtoc_signature_returns_false_with_no_sig(self, mock_subp):591 self.m_subp.side_effect = iter([
587 mock_subp.side_effect = [592 util.ProcessExecutionError(stdout="", stderr="", exit_code=1)])
588 util.ProcessExecutionError(stdout="", stderr="", exit_code=1)]
589 self.assertFalse(block.check_vtoc_signature(self.blockdev))593 self.assertFalse(block.check_vtoc_signature(self.blockdev))
590594
591595
diff --git a/tests/unittests/test_block_zfs.py b/tests/unittests/test_block_zfs.py
index 3508d4b..e392000 100644
--- a/tests/unittests/test_block_zfs.py
+++ b/tests/unittests/test_block_zfs.py
@@ -468,6 +468,30 @@ class TestAssertZfsSupported(CiTestCase):
468 with self.assertRaises(RuntimeError):468 with self.assertRaises(RuntimeError):
469 zfs.zfs_assert_supported()469 zfs.zfs_assert_supported()
470470
471 @mock.patch('curtin.block.zfs.get_supported_filesystems')
472 @mock.patch('curtin.block.zfs.util.lsb_release')
473 @mock.patch('curtin.block.zfs.util.get_platform_arch')
474 @mock.patch('curtin.block.zfs.util')
475 def test_zfs_assert_supported_raises_exc_on_missing_binaries(self,
476 mock_util,
477 m_arch,
478 m_release,
479 m_supfs):
480 """zfs_assert_supported raises RuntimeError if no zpool or zfs tools"""
481 mock_util.get_platform_arch.return_value = 'amd64'
482 mock_util.lsb_release.return_value = {'codename': 'bionic'}
483 mock_util.subp.return_value = ("", "")
484 m_supfs.return_value = ['zfs']
485 mock_util.which.return_value = None
486
487 with self.assertRaises(RuntimeError):
488 zfs.zfs_assert_supported()
489
490
491class TestAssertZfsSupportedSubp(TestAssertZfsSupported):
492
493 allowed_subp = True
494
471 @mock.patch('curtin.block.zfs.util.subprocess.Popen')495 @mock.patch('curtin.block.zfs.util.subprocess.Popen')
472 @mock.patch('curtin.block.zfs.util.is_kmod_loaded')496 @mock.patch('curtin.block.zfs.util.is_kmod_loaded')
473 @mock.patch('curtin.block.zfs.get_supported_filesystems')497 @mock.patch('curtin.block.zfs.get_supported_filesystems')
@@ -481,7 +505,6 @@ class TestAssertZfsSupported(CiTestCase):
481 m_popen,505 m_popen,
482 ):506 ):
483 """zfs_assert_supported raises RuntimeError modprobe zfs error"""507 """zfs_assert_supported raises RuntimeError modprobe zfs error"""
484
485 m_arch.return_value = 'amd64'508 m_arch.return_value = 'amd64'
486 m_release.return_value = {'codename': 'bionic'}509 m_release.return_value = {'codename': 'bionic'}
487 m_supfs.return_value = ['ext4']510 m_supfs.return_value = ['ext4']
@@ -497,25 +520,6 @@ class TestAssertZfsSupported(CiTestCase):
497 with self.assertRaises(RuntimeError):520 with self.assertRaises(RuntimeError):
498 zfs.zfs_assert_supported()521 zfs.zfs_assert_supported()
499522
500 @mock.patch('curtin.block.zfs.get_supported_filesystems')
501 @mock.patch('curtin.block.zfs.util.lsb_release')
502 @mock.patch('curtin.block.zfs.util.get_platform_arch')
503 @mock.patch('curtin.block.zfs.util')
504 def test_zfs_assert_supported_raises_exc_on_missing_binaries(self,
505 mock_util,
506 m_arch,
507 m_release,
508 m_supfs):
509 """zfs_assert_supported raises RuntimeError if no zpool or zfs tools"""
510 mock_util.get_platform_arch.return_value = 'amd64'
511 mock_util.lsb_release.return_value = {'codename': 'bionic'}
512 mock_util.subp.return_value = ("", "")
513 m_supfs.return_value = ['zfs']
514 mock_util.which.return_value = None
515
516 with self.assertRaises(RuntimeError):
517 zfs.zfs_assert_supported()
518
519523
520class TestZfsSupported(CiTestCase):524class TestZfsSupported(CiTestCase):
521525
diff --git a/tests/unittests/test_clear_holders.py b/tests/unittests/test_clear_holders.py
index f73b49d..82b481a 100644
--- a/tests/unittests/test_clear_holders.py
+++ b/tests/unittests/test_clear_holders.py
@@ -701,13 +701,14 @@ class TestClearHolders(CiTestCase):
701 mock_gen_holders_tree.return_value = self.example_holders_trees[1][1]701 mock_gen_holders_tree.return_value = self.example_holders_trees[1][1]
702 clear_holders.assert_clear(device)702 clear_holders.assert_clear(device)
703703
704 @mock.patch('curtin.block.clear_holders.udev')
704 @mock.patch('curtin.block.clear_holders.multipath')705 @mock.patch('curtin.block.clear_holders.multipath')
705 @mock.patch('curtin.block.clear_holders.lvm')706 @mock.patch('curtin.block.clear_holders.lvm')
706 @mock.patch('curtin.block.clear_holders.zfs')707 @mock.patch('curtin.block.clear_holders.zfs')
707 @mock.patch('curtin.block.clear_holders.mdadm')708 @mock.patch('curtin.block.clear_holders.mdadm')
708 @mock.patch('curtin.block.clear_holders.util')709 @mock.patch('curtin.block.clear_holders.util')
709 def test_start_clear_holders_deps(self, mock_util, mock_mdadm, mock_zfs,710 def test_start_clear_holders_deps(self, mock_util, mock_mdadm, mock_zfs,
710 mock_lvm, mock_mp):711 mock_lvm, mock_mp, mock_udev):
711 mock_zfs.zfs_supported.return_value = True712 mock_zfs.zfs_supported.return_value = True
712 clear_holders.start_clear_holders_deps()713 clear_holders.start_clear_holders_deps()
713 mock_mdadm.mdadm_assemble.assert_called_with(714 mock_mdadm.mdadm_assemble.assert_called_with(
@@ -715,13 +716,15 @@ class TestClearHolders(CiTestCase):
715 mock_util.load_kernel_module.assert_has_calls([716 mock_util.load_kernel_module.assert_has_calls([
716 mock.call('bcache')])717 mock.call('bcache')])
717718
719 @mock.patch('curtin.block.clear_holders.udev')
718 @mock.patch('curtin.block.clear_holders.multipath')720 @mock.patch('curtin.block.clear_holders.multipath')
719 @mock.patch('curtin.block.clear_holders.lvm')721 @mock.patch('curtin.block.clear_holders.lvm')
720 @mock.patch('curtin.block.clear_holders.zfs')722 @mock.patch('curtin.block.clear_holders.zfs')
721 @mock.patch('curtin.block.clear_holders.mdadm')723 @mock.patch('curtin.block.clear_holders.mdadm')
722 @mock.patch('curtin.block.clear_holders.util')724 @mock.patch('curtin.block.clear_holders.util')
723 def test_start_clear_holders_deps_nozfs(self, mock_util, mock_mdadm,725 def test_start_clear_holders_deps_nozfs(self, mock_util, mock_mdadm,
724 mock_zfs, mock_lvm, mock_mp):726 mock_zfs, mock_lvm, mock_mp,
727 mock_udev):
725 """test that we skip zfs modprobe on unsupported platforms"""728 """test that we skip zfs modprobe on unsupported platforms"""
726 mock_zfs.zfs_supported.return_value = False729 mock_zfs.zfs_supported.return_value = False
727 clear_holders.start_clear_holders_deps()730 clear_holders.start_clear_holders_deps()
diff --git a/tests/unittests/test_commands_block_meta.py b/tests/unittests/test_commands_block_meta.py
index aac1a9e..6da6412 100644
--- a/tests/unittests/test_commands_block_meta.py
+++ b/tests/unittests/test_commands_block_meta.py
@@ -19,6 +19,8 @@ class TestGetPathToStorageVolume(CiTestCase):
19 self.add_patch(basepath + 'os.path.exists', 'm_exists')19 self.add_patch(basepath + 'os.path.exists', 'm_exists')
20 self.add_patch(basepath + 'block.lookup_disk', 'm_lookup')20 self.add_patch(basepath + 'block.lookup_disk', 'm_lookup')
21 self.add_patch(basepath + 'devsync', 'm_devsync')21 self.add_patch(basepath + 'devsync', 'm_devsync')
22 self.add_patch(basepath + 'util.subp', 'm_subp')
23 self.add_patch(basepath + 'multipath.is_mpath_member', 'm_mp')
2224
23 def test_block_lookup_called_with_disk_wwn(self):25 def test_block_lookup_called_with_disk_wwn(self):
24 volume = 'mydisk'26 volume = 'mydisk'
@@ -93,6 +95,8 @@ class TestGetPathToStorageVolume(CiTestCase):
93 ValueError('Error'), ValueError('Error')])95 ValueError('Error'), ValueError('Error')])
94 # no path96 # no path
95 self.m_exists.return_value = False97 self.m_exists.return_value = False
98 # not multipath
99 self.m_mp.return_value = False
96100
97 with self.assertRaises(ValueError):101 with self.assertRaises(ValueError):
98 block_meta.get_path_to_storage_volume(volume, s_cfg)102 block_meta.get_path_to_storage_volume(volume, s_cfg)
diff --git a/tests/unittests/test_commands_collect_logs.py b/tests/unittests/test_commands_collect_logs.py
index 1feba18..b5df902 100644
--- a/tests/unittests/test_commands_collect_logs.py
+++ b/tests/unittests/test_commands_collect_logs.py
@@ -288,6 +288,7 @@ class TestCreateTar(CiTestCase):
288288
289 Configured log_file or post_files which don't exist are ignored.289 Configured log_file or post_files which don't exist are ignored.
290 """290 """
291 self.add_patch('curtin.util.subp', 'mock_subp')
291 tarfile = self.tmp_path('my.tar', _dir=self.new_root)292 tarfile = self.tmp_path('my.tar', _dir=self.new_root)
292 log1 = self.tmp_path('some.log', _dir=self.new_root)293 log1 = self.tmp_path('some.log', _dir=self.new_root)
293 write_file(log1, 'log content')294 write_file(log1, 'log content')
@@ -314,6 +315,7 @@ class TestCreateTar(CiTestCase):
314315
315 def test_create_log_tarfile_redacts_maas_credentials(self):316 def test_create_log_tarfile_redacts_maas_credentials(self):
316 """create_log_tarfile redacts sensitive maas credentials configured."""317 """create_log_tarfile redacts sensitive maas credentials configured."""
318 self.add_patch('curtin.util.subp', 'mock_subp')
317 tarfile = self.tmp_path('my.tar', _dir=self.new_root)319 tarfile = self.tmp_path('my.tar', _dir=self.new_root)
318 self.add_patch(320 self.add_patch(
319 'curtin.commands.collect_logs._redact_sensitive_information',321 'curtin.commands.collect_logs._redact_sensitive_information',
diff --git a/tests/unittests/test_gpg.py b/tests/unittests/test_gpg.py
index 2c83ae3..42ff8c4 100644
--- a/tests/unittests/test_gpg.py
+++ b/tests/unittests/test_gpg.py
@@ -48,50 +48,6 @@ class TestCurtinGpg(CiTestCase):
48 "--recv", key], capture=True,48 "--recv", key], capture=True,
49 retries=None)49 retries=None)
5050
51 @patch('time.sleep')
52 @patch('curtin.util._subp')
53 def test_recv_key_retry_raises(self, mock_under_subp, mock_sleep):
54 key = 'DEADBEEF'
55 keyserver = 'keyserver.ubuntu.com'
56 retries = (1, 2, 5, 10)
57 nr_calls = 5
58 mock_under_subp.side_effect = iter([
59 util.ProcessExecutionError()] * nr_calls)
60
61 with self.assertRaises(ValueError):
62 gpg.recv_key(key, keyserver, retries=retries)
63
64 print("_subp calls: %s" % mock_under_subp.call_args_list)
65 print("sleep calls: %s" % mock_sleep.call_args_list)
66 expected_calls = nr_calls * [
67 call(["gpg", "--keyserver", keyserver, "--recv", key],
68 capture=True)]
69 mock_under_subp.assert_has_calls(expected_calls)
70
71 expected_calls = [call(1), call(2), call(5), call(10)]
72 mock_sleep.assert_has_calls(expected_calls)
73
74 @patch('time.sleep')
75 @patch('curtin.util._subp')
76 def test_recv_key_retry_works(self, mock_under_subp, mock_sleep):
77 key = 'DEADBEEF'
78 keyserver = 'keyserver.ubuntu.com'
79 nr_calls = 2
80 mock_under_subp.side_effect = iter([
81 util.ProcessExecutionError(), # 1
82 ("", ""),
83 ])
84
85 gpg.recv_key(key, keyserver, retries=[1])
86
87 print("_subp calls: %s" % mock_under_subp.call_args_list)
88 print("sleep calls: %s" % mock_sleep.call_args_list)
89 expected_calls = nr_calls * [
90 call(["gpg", "--keyserver", keyserver, "--recv", key],
91 capture=True)]
92 mock_under_subp.assert_has_calls(expected_calls)
93 mock_sleep.assert_has_calls([call(1)])
94
95 @patch('curtin.util.subp')51 @patch('curtin.util.subp')
96 def test_delete_key(self, mock_subp):52 def test_delete_key(self, mock_subp):
97 key = 'DEADBEEF'53 key = 'DEADBEEF'
@@ -160,4 +116,54 @@ class TestCurtinGpg(CiTestCase):
160 call(key, keyserver=keyserver, retries=None)])116 call(key, keyserver=keyserver, retries=None)])
161 mock_del.assert_has_calls([call(key)])117 mock_del.assert_has_calls([call(key)])
162118
119
120class TestCurtinGpgSubp(TestCurtinGpg):
121
122 allowed_subp = True
123
124 @patch('time.sleep')
125 @patch('curtin.util._subp')
126 def test_recv_key_retry_raises(self, mock_under_subp, mock_sleep):
127 key = 'DEADBEEF'
128 keyserver = 'keyserver.ubuntu.com'
129 retries = (1, 2, 5, 10)
130 nr_calls = 5
131 mock_under_subp.side_effect = iter([
132 util.ProcessExecutionError()] * nr_calls)
133
134 with self.assertRaises(ValueError):
135 gpg.recv_key(key, keyserver, retries=retries)
136
137 print("_subp calls: %s" % mock_under_subp.call_args_list)
138 print("sleep calls: %s" % mock_sleep.call_args_list)
139 expected_calls = nr_calls * [
140 call(["gpg", "--keyserver", keyserver, "--recv", key],
141 capture=True)]
142 mock_under_subp.assert_has_calls(expected_calls)
143
144 expected_calls = [call(1), call(2), call(5), call(10)]
145 mock_sleep.assert_has_calls(expected_calls)
146
147 @patch('time.sleep')
148 @patch('curtin.util._subp')
149 def test_recv_key_retry_works(self, mock_under_subp, mock_sleep):
150 key = 'DEADBEEF'
151 keyserver = 'keyserver.ubuntu.com'
152 nr_calls = 2
153 mock_under_subp.side_effect = iter([
154 util.ProcessExecutionError(), # 1
155 ("", ""),
156 ])
157
158 gpg.recv_key(key, keyserver, retries=[1])
159
160 print("_subp calls: %s" % mock_under_subp.call_args_list)
161 print("sleep calls: %s" % mock_sleep.call_args_list)
162 expected_calls = nr_calls * [
163 call(["gpg", "--keyserver", keyserver, "--recv", key],
164 capture=True)]
165 mock_under_subp.assert_has_calls(expected_calls)
166 mock_sleep.assert_has_calls([call(1)])
167
168
163# vi: ts=4 expandtab syntax=python169# vi: ts=4 expandtab syntax=python
diff --git a/tests/unittests/test_util.py b/tests/unittests/test_util.py
index 438f7ee..0377357 100644
--- a/tests/unittests/test_util.py
+++ b/tests/unittests/test_util.py
@@ -113,6 +113,7 @@ class TestSubp(CiTestCase):
113 utf8_invalid = b'ab\xaadef'113 utf8_invalid = b'ab\xaadef'
114 utf8_valid = b'start \xc3\xa9 end'114 utf8_valid = b'start \xc3\xa9 end'
115 utf8_valid_2 = b'd\xc3\xa9j\xc8\xa7'115 utf8_valid_2 = b'd\xc3\xa9j\xc8\xa7'
116 allowed_subp = True
116117
117 try:118 try:
118 decode_type = unicode119 decode_type = unicode
@@ -552,7 +553,7 @@ class TestTargetPath(CiTestCase):
552 paths.target_path("/target/", "///my/path/"))553 paths.target_path("/target/", "///my/path/"))
553554
554555
555class TestRunInChroot(CiTestCase):556class TestRunInChrootTestSubp(CiTestCase):
556 """Test the legacy 'RunInChroot'.557 """Test the legacy 'RunInChroot'.
557558
558 The test works by mocking ChrootableTarget's __enter__ to do nothing.559 The test works by mocking ChrootableTarget's __enter__ to do nothing.
@@ -560,6 +561,7 @@ class TestRunInChroot(CiTestCase):
560 a.) RunInChroot is a subclass of ChrootableTarget561 a.) RunInChroot is a subclass of ChrootableTarget
561 b.) ChrootableTarget's __exit__ only un-does work that its __enter__562 b.) ChrootableTarget's __exit__ only un-does work that its __enter__
562 did. Meaning for our mocked case, it does nothing."""563 did. Meaning for our mocked case, it does nothing."""
564 allowed_subp = True
563565
564 @mock.patch.object(util.ChrootableTarget, "__enter__", new=lambda a: a)566 @mock.patch.object(util.ChrootableTarget, "__enter__", new=lambda a: a)
565 def test_run_in_chroot_with_target_slash(self):567 def test_run_in_chroot_with_target_slash(self):
@@ -567,6 +569,16 @@ class TestRunInChroot(CiTestCase):
567 out, err = i(['echo', 'HI MOM'], capture=True)569 out, err = i(['echo', 'HI MOM'], capture=True)
568 self.assertEqual('HI MOM\n', out)570 self.assertEqual('HI MOM\n', out)
569571
572
573class TestRunInChrootTest(CiTestCase):
574 """Test the legacy 'RunInChroot'.
575
576 The test works by mocking ChrootableTarget's __enter__ to do nothing.
577 The assumptions made are:
578 a.) RunInChroot is a subclass of ChrootableTarget
579 b.) ChrootableTarget's __exit__ only un-does work that its __enter__
580 did. Meaning for our mocked case, it does nothing."""
581
570 @mock.patch.object(util.ChrootableTarget, "__enter__", new=lambda a: a)582 @mock.patch.object(util.ChrootableTarget, "__enter__", new=lambda a: a)
571 @mock.patch("curtin.util.subp")583 @mock.patch("curtin.util.subp")
572 def test_run_in_chroot_with_target(self, m_subp):584 def test_run_in_chroot_with_target(self, m_subp):

Subscribers

People subscribed via source and target branches