Merge ~larsks/cloud-init:feature/move-base-testcase into cloud-init:master

Proposed by Lars Kellogg-Stedman on 2017-08-26
Status: Merged
Merged at revision: a3649e03206a3596131413956ea7ecc18790ec73
Proposed branch: ~larsks/cloud-init:feature/move-base-testcase
Merge into: cloud-init:master
Prerequisite: ~larsks/cloud-init:bug/ec2-tests
Diff against target: 2043 lines (+491/-535)
87 files modified
cloudinit/analyze/tests/test_dump.py (+1/-1)
cloudinit/net/tests/test_dhcp.py (+1/-1)
cloudinit/net/tests/test_init.py (+1/-1)
cloudinit/tests/helpers.py (+395/-0)
cloudinit/tests/test_url_helper.py (+1/-1)
cloudinit/url_helper.py (+0/-13)
dev/null (+0/-391)
tests/unittests/test__init__.py (+1/-1)
tests/unittests/test_atomic_helper.py (+1/-1)
tests/unittests/test_builtin_handlers.py (+1/-1)
tests/unittests/test_cli.py (+1/-1)
tests/unittests/test_cs_util.py (+1/-1)
tests/unittests/test_data.py (+1/-1)
tests/unittests/test_datasource/test_aliyun.py (+1/-1)
tests/unittests/test_datasource/test_altcloud.py (+1/-1)
tests/unittests/test_datasource/test_azure.py (+4/-2)
tests/unittests/test_datasource/test_azure_helper.py (+1/-1)
tests/unittests/test_datasource/test_cloudsigma.py (+1/-1)
tests/unittests/test_datasource/test_cloudstack.py (+1/-1)
tests/unittests/test_datasource/test_common.py (+1/-1)
tests/unittests/test_datasource/test_configdrive.py (+1/-1)
tests/unittests/test_datasource/test_digitalocean.py (+1/-1)
tests/unittests/test_datasource/test_ec2.py (+1/-41)
tests/unittests/test_datasource/test_gce.py (+1/-1)
tests/unittests/test_datasource/test_maas.py (+1/-1)
tests/unittests/test_datasource/test_nocloud.py (+1/-1)
tests/unittests/test_datasource/test_opennebula.py (+1/-1)
tests/unittests/test_datasource/test_openstack.py (+1/-1)
tests/unittests/test_datasource/test_ovf.py (+1/-1)
tests/unittests/test_datasource/test_scaleway.py (+1/-1)
tests/unittests/test_datasource/test_smartos.py (+1/-1)
tests/unittests/test_distros/test_arch.py (+1/-1)
tests/unittests/test_distros/test_create_users.py (+1/-1)
tests/unittests/test_distros/test_debian.py (+1/-1)
tests/unittests/test_distros/test_generic.py (+1/-1)
tests/unittests/test_distros/test_netconfig.py (+1/-1)
tests/unittests/test_distros/test_opensuse.py (+1/-1)
tests/unittests/test_distros/test_resolv.py (+1/-1)
tests/unittests/test_distros/test_sles.py (+1/-1)
tests/unittests/test_distros/test_sysconfig.py (+1/-1)
tests/unittests/test_distros/test_user_data_normalize.py (+1/-1)
tests/unittests/test_ds_identify.py (+2/-1)
tests/unittests/test_ec2_util.py (+1/-1)
tests/unittests/test_filters/test_launch_index.py (+1/-1)
tests/unittests/test_handler/test_handler_apt_conf_v1.py (+1/-1)
tests/unittests/test_handler/test_handler_apt_configure_sources_list_v1.py (+1/-1)
tests/unittests/test_handler/test_handler_apt_configure_sources_list_v3.py (+1/-1)
tests/unittests/test_handler/test_handler_apt_source_v1.py (+1/-1)
tests/unittests/test_handler/test_handler_apt_source_v3.py (+1/-1)
tests/unittests/test_handler/test_handler_ca_certs.py (+1/-1)
tests/unittests/test_handler/test_handler_chef.py (+1/-1)
tests/unittests/test_handler/test_handler_debug.py (+1/-1)
tests/unittests/test_handler/test_handler_disk_setup.py (+1/-1)
tests/unittests/test_handler/test_handler_growpart.py (+1/-1)
tests/unittests/test_handler/test_handler_landscape.py (+3/-2)
tests/unittests/test_handler/test_handler_locale.py (+1/-1)
tests/unittests/test_handler/test_handler_lxd.py (+1/-1)
tests/unittests/test_handler/test_handler_mcollective.py (+1/-1)
tests/unittests/test_handler/test_handler_mounts.py (+1/-1)
tests/unittests/test_handler/test_handler_ntp.py (+1/-1)
tests/unittests/test_handler/test_handler_power_state.py (+2/-2)
tests/unittests/test_handler/test_handler_puppet.py (+1/-1)
tests/unittests/test_handler/test_handler_rsyslog.py (+1/-1)
tests/unittests/test_handler/test_handler_runcmd.py (+1/-1)
tests/unittests/test_handler/test_handler_seed_random.py (+1/-1)
tests/unittests/test_handler/test_handler_set_hostname.py (+1/-1)
tests/unittests/test_handler/test_handler_snappy.py (+2/-2)
tests/unittests/test_handler/test_handler_spacewalk.py (+1/-1)
tests/unittests/test_handler/test_handler_timezone.py (+1/-1)
tests/unittests/test_handler/test_handler_write_files.py (+1/-1)
tests/unittests/test_handler/test_handler_yum_add_repo.py (+1/-1)
tests/unittests/test_handler/test_schema.py (+1/-1)
tests/unittests/test_helpers.py (+1/-1)
tests/unittests/test_log.py (+1/-1)
tests/unittests/test_merging.py (+1/-1)
tests/unittests/test_net.py (+4/-4)
tests/unittests/test_pathprefix2dict.py (+1/-1)
tests/unittests/test_registry.py (+1/-1)
tests/unittests/test_reporting.py (+1/-1)
tests/unittests/test_rh_subscription.py (+1/-1)
tests/unittests/test_runs/test_merge_run.py (+1/-1)
tests/unittests/test_runs/test_simple_run.py (+1/-1)
tests/unittests/test_sshutil.py (+2/-1)
tests/unittests/test_templating.py (+1/-1)
tests/unittests/test_util.py (+1/-1)
tests/unittests/test_version.py (+1/-1)
tests/unittests/test_vmware_config_file.py (+1/-1)
Reviewer Review Type Date Requested Status
Chad Smith 2017-08-26 Approve on 2017-09-05
Lars Kellogg-Stedman (community) Disapprove on 2017-09-04
Server Team CI bot continuous-integration 2017-08-26 Approve on 2017-09-01
Review via email: mp+329661@code.launchpad.net

Description of the Change

relocate tests/unittests/helpers.py to cloudinit/tests

This moves the base test case classes into into cloudinit/tests (and
updates all the corresponding imports)

To post a comment you must log in.

FAILED: Continuous integration, rev:346228a1a98537d1181cc3a416d44408aace3060
https://jenkins.ubuntu.com/server/job/cloud-init-ci/216/
Executed test runs:
    SUCCESS: Checkout
    FAILED: Unit & Style Tests

Click here to trigger a rebuild:
https://jenkins.ubuntu.com/server/job/cloud-init-ci/216/rebuild

review: Needs Fixing (continuous-integration)

FAILED: Continuous integration, rev:7e39ee3abe906f788b99e0beae84b77943f370db
https://jenkins.ubuntu.com/server/job/cloud-init-ci/217/
Executed test runs:
    SUCCESS: Checkout
    FAILED: Unit & Style Tests

Click here to trigger a rebuild:
https://jenkins.ubuntu.com/server/job/cloud-init-ci/217/rebuild

review: Needs Fixing (continuous-integration)

FAILED: Continuous integration, rev:1bb128313ee3f09c95ba703ce8d466fe86e162c3
https://jenkins.ubuntu.com/server/job/cloud-init-ci/218/
Executed test runs:
    SUCCESS: Checkout
    FAILED: Unit & Style Tests

Click here to trigger a rebuild:
https://jenkins.ubuntu.com/server/job/cloud-init-ci/218/rebuild

review: Needs Fixing (continuous-integration)

PASSED: Continuous integration, rev:965ec2a2d400db3dded28f12b2b5d2cd273d9bc4
https://jenkins.ubuntu.com/server/job/cloud-init-ci/219/
Executed test runs:
    SUCCESS: Checkout
    SUCCESS: Unit & Style Tests
    SUCCESS: Ubuntu LTS: Build
    SUCCESS: Ubuntu LTS: Integration
    SUCCESS: MAAS Compatability Testing
    IN_PROGRESS: Declarative: Post Actions

Click here to trigger a rebuild:
https://jenkins.ubuntu.com/server/job/cloud-init-ci/219/rebuild

review: Approve (continuous-integration)

FAILED: Continuous integration, rev:4a8856b41b2b0fe4ce254a9a4c55f0433c0a7fea
https://jenkins.ubuntu.com/server/job/cloud-init-ci/245/
Executed test runs:
    SUCCESS: Checkout
    FAILED: Unit & Style Tests

Click here to trigger a rebuild:
https://jenkins.ubuntu.com/server/job/cloud-init-ci/245/rebuild

review: Needs Fixing (continuous-integration)

FAILED: Continuous integration, rev:f2f5ffe22ee177b4a207ac4f688922a9ad2275f0
https://jenkins.ubuntu.com/server/job/cloud-init-ci/246/
Executed test runs:
    SUCCESS: Checkout
    FAILED: Unit & Style Tests

Click here to trigger a rebuild:
https://jenkins.ubuntu.com/server/job/cloud-init-ci/246/rebuild

review: Needs Fixing (continuous-integration)

PASSED: Continuous integration, rev:f5f64de3e6dd90394eafc39b4455cccb44b4f4e4
https://jenkins.ubuntu.com/server/job/cloud-init-ci/247/
Executed test runs:
    SUCCESS: Checkout
    SUCCESS: Unit & Style Tests
    SUCCESS: Ubuntu LTS: Build
    SUCCESS: Ubuntu LTS: Integration
    SUCCESS: MAAS Compatability Testing
    IN_PROGRESS: Declarative: Post Actions

Click here to trigger a rebuild:
https://jenkins.ubuntu.com/server/job/cloud-init-ci/247/rebuild

review: Approve (continuous-integration)
Chad Smith (chad.smith) wrote :

Good work Lars. I moved this over to https://code.launchpad.net/~chad.smith/cloud-init/+git/cloud-init/+merge/330090 as there were some unit tests creeping into trunk that needed an import refresh.

Thanks for this, marking this branch as rejected as the followup (which you'll get credit for) will land.

review: Approve
Lars Kellogg-Stedman (larsks) wrote :

I've updated the original merge proposal so that applies cleanly and passes with current master. Please use that one, and let me fix it if it needs additional changes. Thanks!

review: Disapprove
Lars Kellogg-Stedman (larsks) wrote :

Ugh, sorry, too many windows. *This* is the original merge proposal :).

Chad Smith (chad.smith) wrote :

@Lars, welcome back. Can you click approve or abstain so I don't see that red? :) I'll merge this away in either case.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/cloudinit/analyze/tests/test_dump.py b/cloudinit/analyze/tests/test_dump.py
2index 2c0885d..f4c4284 100644
3--- a/cloudinit/analyze/tests/test_dump.py
4+++ b/cloudinit/analyze/tests/test_dump.py
5@@ -6,7 +6,7 @@ from textwrap import dedent
6 from cloudinit.analyze.dump import (
7 dump_events, parse_ci_logline, parse_timestamp)
8 from cloudinit.util import subp, write_file
9-from tests.unittests.helpers import CiTestCase
10+from cloudinit.tests.helpers import CiTestCase
11
12
13 class TestParseTimestamp(CiTestCase):
14diff --git a/cloudinit/net/tests/test_dhcp.py b/cloudinit/net/tests/test_dhcp.py
15index 47d8d46..4a37e98 100644
16--- a/cloudinit/net/tests/test_dhcp.py
17+++ b/cloudinit/net/tests/test_dhcp.py
18@@ -8,7 +8,7 @@ from cloudinit.net.dhcp import (
19 InvalidDHCPLeaseFileError, maybe_perform_dhcp_discovery,
20 parse_dhcp_lease_file, dhcp_discovery)
21 from cloudinit.util import ensure_file, write_file
22-from tests.unittests.helpers import CiTestCase
23+from cloudinit.tests.helpers import CiTestCase
24
25
26 class TestParseDHCPLeasesFile(CiTestCase):
27diff --git a/cloudinit/net/tests/test_init.py b/cloudinit/net/tests/test_init.py
28index cc052a7..8cb4114 100644
29--- a/cloudinit/net/tests/test_init.py
30+++ b/cloudinit/net/tests/test_init.py
31@@ -7,7 +7,7 @@ import os
32
33 import cloudinit.net as net
34 from cloudinit.util import ensure_file, write_file, ProcessExecutionError
35-from tests.unittests.helpers import CiTestCase
36+from cloudinit.tests.helpers import CiTestCase
37
38
39 class TestSysDevPath(CiTestCase):
40diff --git a/cloudinit/tests/helpers.py b/cloudinit/tests/helpers.py
41new file mode 100644
42index 0000000..28e2662
43--- /dev/null
44+++ b/cloudinit/tests/helpers.py
45@@ -0,0 +1,395 @@
46+# This file is part of cloud-init. See LICENSE file for license information.
47+
48+from __future__ import print_function
49+
50+import functools
51+import json
52+import logging
53+import os
54+import shutil
55+import sys
56+import tempfile
57+import unittest
58+
59+import mock
60+import six
61+import unittest2
62+
63+try:
64+ from contextlib import ExitStack
65+except ImportError:
66+ from contextlib2 import ExitStack
67+
68+from cloudinit import helpers as ch
69+from cloudinit import util
70+
71+# Used for skipping tests
72+SkipTest = unittest2.SkipTest
73+
74+# Used for detecting different python versions
75+PY2 = False
76+PY26 = False
77+PY27 = False
78+PY3 = False
79+
80+_PY_VER = sys.version_info
81+_PY_MAJOR, _PY_MINOR, _PY_MICRO = _PY_VER[0:3]
82+if (_PY_MAJOR, _PY_MINOR) <= (2, 6):
83+ if (_PY_MAJOR, _PY_MINOR) == (2, 6):
84+ PY26 = True
85+ if (_PY_MAJOR, _PY_MINOR) >= (2, 0):
86+ PY2 = True
87+else:
88+ if (_PY_MAJOR, _PY_MINOR) == (2, 7):
89+ PY27 = True
90+ PY2 = True
91+ if (_PY_MAJOR, _PY_MINOR) >= (3, 0):
92+ PY3 = True
93+
94+
95+# Makes the old path start
96+# with new base instead of whatever
97+# it previously had
98+def rebase_path(old_path, new_base):
99+ if old_path.startswith(new_base):
100+ # Already handled...
101+ return old_path
102+ # Retarget the base of that path
103+ # to the new base instead of the
104+ # old one...
105+ path = os.path.join(new_base, old_path.lstrip("/"))
106+ path = os.path.abspath(path)
107+ return path
108+
109+
110+# Can work on anything that takes a path as arguments
111+def retarget_many_wrapper(new_base, am, old_func):
112+ def wrapper(*args, **kwds):
113+ n_args = list(args)
114+ nam = am
115+ if am == -1:
116+ nam = len(n_args)
117+ for i in range(0, nam):
118+ path = args[i]
119+ # patchOS() wraps various os and os.path functions, however in
120+ # Python 3 some of these now accept file-descriptors (integers).
121+ # That breaks rebase_path() so in lieu of a better solution, just
122+ # don't rebase if we get a fd.
123+ if isinstance(path, six.string_types):
124+ n_args[i] = rebase_path(path, new_base)
125+ return old_func(*n_args, **kwds)
126+ return wrapper
127+
128+
129+class TestCase(unittest2.TestCase):
130+
131+ def reset_global_state(self):
132+ """Reset any global state to its original settings.
133+
134+ cloudinit caches some values in cloudinit.util. Unit tests that
135+ involved those cached paths were then subject to failure if the order
136+ of invocation changed (LP: #1703697).
137+
138+ This function resets any of these global state variables to their
139+ initial state.
140+
141+ In the future this should really be done with some registry that
142+ can then be cleaned in a more obvious way.
143+ """
144+ util.PROC_CMDLINE = None
145+ util._DNS_REDIRECT_IP = None
146+ util._LSB_RELEASE = {}
147+
148+ def setUp(self):
149+ super(TestCase, self).setUp()
150+ self.reset_global_state()
151+
152+
153+class CiTestCase(TestCase):
154+ """This is the preferred test case base class unless user
155+ needs other test case classes below."""
156+
157+ # Subclass overrides for specific test behavior
158+ # Whether or not a unit test needs logfile setup
159+ with_logs = False
160+
161+ def setUp(self):
162+ super(CiTestCase, self).setUp()
163+ if self.with_logs:
164+ # Create a log handler so unit tests can search expected logs.
165+ self.logger = logging.getLogger()
166+ self.logs = six.StringIO()
167+ formatter = logging.Formatter('%(levelname)s: %(message)s')
168+ handler = logging.StreamHandler(self.logs)
169+ handler.setFormatter(formatter)
170+ self.old_handlers = self.logger.handlers
171+ self.logger.handlers = [handler]
172+
173+ def tearDown(self):
174+ if self.with_logs:
175+ # Remove the handler we setup
176+ logging.getLogger().handlers = self.old_handlers
177+ super(CiTestCase, self).tearDown()
178+
179+ def tmp_dir(self, dir=None, cleanup=True):
180+ # return a full path to a temporary directory that will be cleaned up.
181+ if dir is None:
182+ tmpd = tempfile.mkdtemp(
183+ prefix="ci-%s." % self.__class__.__name__)
184+ else:
185+ tmpd = tempfile.mkdtemp(dir=dir)
186+ self.addCleanup(functools.partial(shutil.rmtree, tmpd))
187+ return tmpd
188+
189+ def tmp_path(self, path, dir=None):
190+ # return an absolute path to 'path' under dir.
191+ # if dir is None, one will be created with tmp_dir()
192+ # the file is not created or modified.
193+ if dir is None:
194+ dir = self.tmp_dir()
195+ return os.path.normpath(os.path.abspath(os.path.join(dir, path)))
196+
197+
198+class ResourceUsingTestCase(CiTestCase):
199+
200+ def setUp(self):
201+ super(ResourceUsingTestCase, self).setUp()
202+ self.resource_path = None
203+
204+ def resourceLocation(self, subname=None):
205+ if self.resource_path is None:
206+ paths = [
207+ os.path.join('tests', 'data'),
208+ os.path.join('data'),
209+ os.path.join(os.pardir, 'tests', 'data'),
210+ os.path.join(os.pardir, 'data'),
211+ ]
212+ for p in paths:
213+ if os.path.isdir(p):
214+ self.resource_path = p
215+ break
216+ self.assertTrue((self.resource_path and
217+ os.path.isdir(self.resource_path)),
218+ msg="Unable to locate test resource data path!")
219+ if not subname:
220+ return self.resource_path
221+ return os.path.join(self.resource_path, subname)
222+
223+ def readResource(self, name):
224+ where = self.resourceLocation(name)
225+ with open(where, 'r') as fh:
226+ return fh.read()
227+
228+ def getCloudPaths(self, ds=None):
229+ tmpdir = tempfile.mkdtemp()
230+ self.addCleanup(shutil.rmtree, tmpdir)
231+ cp = ch.Paths({'cloud_dir': tmpdir,
232+ 'templates_dir': self.resourceLocation()},
233+ ds=ds)
234+ return cp
235+
236+
237+class FilesystemMockingTestCase(ResourceUsingTestCase):
238+
239+ def setUp(self):
240+ super(FilesystemMockingTestCase, self).setUp()
241+ self.patched_funcs = ExitStack()
242+
243+ def tearDown(self):
244+ self.patched_funcs.close()
245+ ResourceUsingTestCase.tearDown(self)
246+
247+ def replicateTestRoot(self, example_root, target_root):
248+ real_root = self.resourceLocation()
249+ real_root = os.path.join(real_root, 'roots', example_root)
250+ for (dir_path, _dirnames, filenames) in os.walk(real_root):
251+ real_path = dir_path
252+ make_path = rebase_path(real_path[len(real_root):], target_root)
253+ util.ensure_dir(make_path)
254+ for f in filenames:
255+ real_path = util.abs_join(real_path, f)
256+ make_path = util.abs_join(make_path, f)
257+ shutil.copy(real_path, make_path)
258+
259+ def patchUtils(self, new_root):
260+ patch_funcs = {
261+ util: [('write_file', 1),
262+ ('append_file', 1),
263+ ('load_file', 1),
264+ ('ensure_dir', 1),
265+ ('chmod', 1),
266+ ('delete_dir_contents', 1),
267+ ('del_file', 1),
268+ ('sym_link', -1),
269+ ('copy', -1)],
270+ }
271+ for (mod, funcs) in patch_funcs.items():
272+ for (f, am) in funcs:
273+ func = getattr(mod, f)
274+ trap_func = retarget_many_wrapper(new_root, am, func)
275+ self.patched_funcs.enter_context(
276+ mock.patch.object(mod, f, trap_func))
277+
278+ # Handle subprocess calls
279+ func = getattr(util, 'subp')
280+
281+ def nsubp(*_args, **_kwargs):
282+ return ('', '')
283+
284+ self.patched_funcs.enter_context(
285+ mock.patch.object(util, 'subp', nsubp))
286+
287+ def null_func(*_args, **_kwargs):
288+ return None
289+
290+ for f in ['chownbyid', 'chownbyname']:
291+ self.patched_funcs.enter_context(
292+ mock.patch.object(util, f, null_func))
293+
294+ def patchOS(self, new_root):
295+ patch_funcs = {
296+ os.path: [('isfile', 1), ('exists', 1),
297+ ('islink', 1), ('isdir', 1)],
298+ os: [('listdir', 1), ('mkdir', 1),
299+ ('lstat', 1), ('symlink', 2)],
300+ }
301+ for (mod, funcs) in patch_funcs.items():
302+ for f, nargs in funcs:
303+ func = getattr(mod, f)
304+ trap_func = retarget_many_wrapper(new_root, nargs, func)
305+ self.patched_funcs.enter_context(
306+ mock.patch.object(mod, f, trap_func))
307+
308+ def patchOpen(self, new_root):
309+ trap_func = retarget_many_wrapper(new_root, 1, open)
310+ name = 'builtins.open' if PY3 else '__builtin__.open'
311+ self.patched_funcs.enter_context(mock.patch(name, trap_func))
312+
313+ def patchStdoutAndStderr(self, stdout=None, stderr=None):
314+ if stdout is not None:
315+ self.patched_funcs.enter_context(
316+ mock.patch.object(sys, 'stdout', stdout))
317+ if stderr is not None:
318+ self.patched_funcs.enter_context(
319+ mock.patch.object(sys, 'stderr', stderr))
320+
321+ def reRoot(self, root=None):
322+ if root is None:
323+ root = self.tmp_dir()
324+ self.patchUtils(root)
325+ self.patchOS(root)
326+ return root
327+
328+
329+class HttprettyTestCase(CiTestCase):
330+ # necessary as http_proxy gets in the way of httpretty
331+ # https://github.com/gabrielfalcao/HTTPretty/issues/122
332+
333+ def setUp(self):
334+ self.restore_proxy = os.environ.get('http_proxy')
335+ if self.restore_proxy is not None:
336+ del os.environ['http_proxy']
337+ super(HttprettyTestCase, self).setUp()
338+
339+ def tearDown(self):
340+ if self.restore_proxy:
341+ os.environ['http_proxy'] = self.restore_proxy
342+ super(HttprettyTestCase, self).tearDown()
343+
344+
345+def populate_dir(path, files):
346+ if not os.path.exists(path):
347+ os.makedirs(path)
348+ ret = []
349+ for (name, content) in files.items():
350+ p = os.path.sep.join([path, name])
351+ util.ensure_dir(os.path.dirname(p))
352+ with open(p, "wb") as fp:
353+ if isinstance(content, six.binary_type):
354+ fp.write(content)
355+ else:
356+ fp.write(content.encode('utf-8'))
357+ fp.close()
358+ ret.append(p)
359+
360+ return ret
361+
362+
363+def dir2dict(startdir, prefix=None):
364+ flist = {}
365+ if prefix is None:
366+ prefix = startdir
367+ for root, dirs, files in os.walk(startdir):
368+ for fname in files:
369+ fpath = os.path.join(root, fname)
370+ key = fpath[len(prefix):]
371+ flist[key] = util.load_file(fpath)
372+ return flist
373+
374+
375+def json_dumps(data):
376+ # print data in nicely formatted json.
377+ return json.dumps(data, indent=1, sort_keys=True,
378+ separators=(',', ': '))
379+
380+
381+def wrap_and_call(prefix, mocks, func, *args, **kwargs):
382+ """
383+ call func(args, **kwargs) with mocks applied, then unapplies mocks
384+ nicer to read than repeating dectorators on each function
385+
386+ prefix: prefix for mock names (e.g. 'cloudinit.stages.util') or None
387+ mocks: dictionary of names (under 'prefix') to mock and either
388+ a return value or a dictionary to pass to the mock.patch call
389+ func: function to call with mocks applied
390+ *args,**kwargs: arguments for 'func'
391+
392+ return_value: return from 'func'
393+ """
394+ delim = '.'
395+ if prefix is None:
396+ prefix = ''
397+ prefix = prefix.rstrip(delim)
398+ unwraps = []
399+ for fname, kw in mocks.items():
400+ if prefix:
401+ fname = delim.join((prefix, fname))
402+ if not isinstance(kw, dict):
403+ kw = {'return_value': kw}
404+ p = mock.patch(fname, **kw)
405+ p.start()
406+ unwraps.append(p)
407+ try:
408+ return func(*args, **kwargs)
409+ finally:
410+ for p in unwraps:
411+ p.stop()
412+
413+
414+try:
415+ skipIf = unittest.skipIf
416+except AttributeError:
417+ # Python 2.6. Doesn't have to be high fidelity.
418+ def skipIf(condition, reason):
419+ def decorator(func):
420+ def wrapper(*args, **kws):
421+ if condition:
422+ return func(*args, **kws)
423+ else:
424+ print(reason, file=sys.stderr)
425+ return wrapper
426+ return decorator
427+
428+
429+# older versions of mock do not have the useful 'assert_not_called'
430+if not hasattr(mock.Mock, 'assert_not_called'):
431+ def __mock_assert_not_called(mmock):
432+ if mmock.call_count != 0:
433+ msg = ("[citest] Expected '%s' to not have been called. "
434+ "Called %s times." %
435+ (mmock._mock_name or 'mock', mmock.call_count))
436+ raise AssertionError(msg)
437+ mock.Mock.assert_not_called = __mock_assert_not_called
438+
439+
440+# vi: ts=4 expandtab
441diff --git a/cloudinit/tests/test_url_helper.py b/cloudinit/tests/test_url_helper.py
442index 349110d..b778a3a 100644
443--- a/cloudinit/tests/test_url_helper.py
444+++ b/cloudinit/tests/test_url_helper.py
445@@ -1,7 +1,7 @@
446 # This file is part of cloud-init. See LICENSE file for license information.
447
448 from cloudinit.url_helper import oauth_headers
449-from tests.unittests.helpers import CiTestCase, mock, skipIf
450+from cloudinit.tests.helpers import CiTestCase, mock, skipIf
451
452
453 try:
454diff --git a/cloudinit/url_helper.py b/cloudinit/url_helper.py
455index 660bcc6..0e0f5b4 100644
456--- a/cloudinit/url_helper.py
457+++ b/cloudinit/url_helper.py
458@@ -17,14 +17,6 @@ import time
459 from email.utils import parsedate
460 from functools import partial
461
462-<<<<<<< cloudinit/url_helper.py
463-=======
464-try:
465- import oauthlib.oauth1 as oauth1
466-except ImportError:
467- oauth1 = None
468-
469->>>>>>> cloudinit/url_helper.py
470 from requests import exceptions
471
472 from six.moves.urllib.parse import (
473@@ -495,14 +487,9 @@ class OauthUrlHelper(object):
474
475 def oauth_headers(url, consumer_key, token_key, token_secret, consumer_secret,
476 timestamp=None):
477-<<<<<<< cloudinit/url_helper.py
478 try:
479 import oauthlib.oauth1 as oauth1
480 except ImportError:
481-=======
482-
483- if oauth1 is None:
484->>>>>>> cloudinit/url_helper.py
485 raise NotImplementedError('oauth support is not available')
486
487 if timestamp:
488diff --git a/tests/unittests/helpers.py b/tests/unittests/helpers.py
489deleted file mode 100644
490index bf1dc5d..0000000
491--- a/tests/unittests/helpers.py
492+++ /dev/null
493@@ -1,391 +0,0 @@
494-# This file is part of cloud-init. See LICENSE file for license information.
495-
496-from __future__ import print_function
497-
498-import functools
499-import json
500-import logging
501-import os
502-import shutil
503-import sys
504-import tempfile
505-import unittest
506-
507-import mock
508-import six
509-import unittest2
510-
511-try:
512- from contextlib import ExitStack
513-except ImportError:
514- from contextlib2 import ExitStack
515-
516-from cloudinit import helpers as ch
517-from cloudinit import util
518-
519-# Used for skipping tests
520-SkipTest = unittest2.SkipTest
521-
522-# Used for detecting different python versions
523-PY2 = False
524-PY26 = False
525-PY27 = False
526-PY3 = False
527-
528-_PY_VER = sys.version_info
529-_PY_MAJOR, _PY_MINOR, _PY_MICRO = _PY_VER[0:3]
530-if (_PY_MAJOR, _PY_MINOR) <= (2, 6):
531- if (_PY_MAJOR, _PY_MINOR) == (2, 6):
532- PY26 = True
533- if (_PY_MAJOR, _PY_MINOR) >= (2, 0):
534- PY2 = True
535-else:
536- if (_PY_MAJOR, _PY_MINOR) == (2, 7):
537- PY27 = True
538- PY2 = True
539- if (_PY_MAJOR, _PY_MINOR) >= (3, 0):
540- PY3 = True
541-
542-
543-# Makes the old path start
544-# with new base instead of whatever
545-# it previously had
546-def rebase_path(old_path, new_base):
547- if old_path.startswith(new_base):
548- # Already handled...
549- return old_path
550- # Retarget the base of that path
551- # to the new base instead of the
552- # old one...
553- path = os.path.join(new_base, old_path.lstrip("/"))
554- path = os.path.abspath(path)
555- return path
556-
557-
558-# Can work on anything that takes a path as arguments
559-def retarget_many_wrapper(new_base, am, old_func):
560- def wrapper(*args, **kwds):
561- n_args = list(args)
562- nam = am
563- if am == -1:
564- nam = len(n_args)
565- for i in range(0, nam):
566- path = args[i]
567- # patchOS() wraps various os and os.path functions, however in
568- # Python 3 some of these now accept file-descriptors (integers).
569- # That breaks rebase_path() so in lieu of a better solution, just
570- # don't rebase if we get a fd.
571- if isinstance(path, six.string_types):
572- n_args[i] = rebase_path(path, new_base)
573- return old_func(*n_args, **kwds)
574- return wrapper
575-
576-
577-class TestCase(unittest2.TestCase):
578- def reset_global_state(self):
579- """Reset any global state to its original settings.
580-
581- cloudinit caches some values in cloudinit.util. Unit tests that
582- involved those cached paths were then subject to failure if the order
583- of invocation changed (LP: #1703697).
584-
585- This function resets any of these global state variables to their
586- initial state.
587-
588- In the future this should really be done with some registry that
589- can then be cleaned in a more obvious way.
590- """
591- util.PROC_CMDLINE = None
592- util._DNS_REDIRECT_IP = None
593- util._LSB_RELEASE = {}
594-
595- def setUp(self):
596- super(unittest2.TestCase, self).setUp()
597- self.reset_global_state()
598-
599-
600-class CiTestCase(TestCase):
601- """This is the preferred test case base class unless user
602- needs other test case classes below."""
603-
604- # Subclass overrides for specific test behavior
605- # Whether or not a unit test needs logfile setup
606- with_logs = False
607-
608- def setUp(self):
609- super(CiTestCase, self).setUp()
610- if self.with_logs:
611- # Create a log handler so unit tests can search expected logs.
612- self.logger = logging.getLogger()
613- self.logs = six.StringIO()
614- formatter = logging.Formatter('%(levelname)s: %(message)s')
615- handler = logging.StreamHandler(self.logs)
616- handler.setFormatter(formatter)
617- self.old_handlers = self.logger.handlers
618- self.logger.handlers = [handler]
619-
620- def tearDown(self):
621- if self.with_logs:
622- # Remove the handler we setup
623- logging.getLogger().handlers = self.old_handlers
624- super(CiTestCase, self).tearDown()
625-
626- def tmp_dir(self, dir=None, cleanup=True):
627- # return a full path to a temporary directory that will be cleaned up.
628- if dir is None:
629- tmpd = tempfile.mkdtemp(
630- prefix="ci-%s." % self.__class__.__name__)
631- else:
632- tmpd = tempfile.mkdtemp(dir=dir)
633- self.addCleanup(functools.partial(shutil.rmtree, tmpd))
634- return tmpd
635-
636- def tmp_path(self, path, dir=None):
637- # return an absolute path to 'path' under dir.
638- # if dir is None, one will be created with tmp_dir()
639- # the file is not created or modified.
640- if dir is None:
641- dir = self.tmp_dir()
642- return os.path.normpath(os.path.abspath(os.path.join(dir, path)))
643-
644-
645-class ResourceUsingTestCase(CiTestCase):
646- def setUp(self):
647- super(ResourceUsingTestCase, self).setUp()
648- self.resource_path = None
649-
650- def resourceLocation(self, subname=None):
651- if self.resource_path is None:
652- paths = [
653- os.path.join('tests', 'data'),
654- os.path.join('data'),
655- os.path.join(os.pardir, 'tests', 'data'),
656- os.path.join(os.pardir, 'data'),
657- ]
658- for p in paths:
659- if os.path.isdir(p):
660- self.resource_path = p
661- break
662- self.assertTrue((self.resource_path and
663- os.path.isdir(self.resource_path)),
664- msg="Unable to locate test resource data path!")
665- if not subname:
666- return self.resource_path
667- return os.path.join(self.resource_path, subname)
668-
669- def readResource(self, name):
670- where = self.resourceLocation(name)
671- with open(where, 'r') as fh:
672- return fh.read()
673-
674- def getCloudPaths(self, ds=None):
675- tmpdir = tempfile.mkdtemp()
676- self.addCleanup(shutil.rmtree, tmpdir)
677- cp = ch.Paths({'cloud_dir': tmpdir,
678- 'templates_dir': self.resourceLocation()},
679- ds=ds)
680- return cp
681-
682-
683-class FilesystemMockingTestCase(ResourceUsingTestCase):
684- def setUp(self):
685- super(FilesystemMockingTestCase, self).setUp()
686- self.patched_funcs = ExitStack()
687-
688- def tearDown(self):
689- self.patched_funcs.close()
690- ResourceUsingTestCase.tearDown(self)
691-
692- def replicateTestRoot(self, example_root, target_root):
693- real_root = self.resourceLocation()
694- real_root = os.path.join(real_root, 'roots', example_root)
695- for (dir_path, _dirnames, filenames) in os.walk(real_root):
696- real_path = dir_path
697- make_path = rebase_path(real_path[len(real_root):], target_root)
698- util.ensure_dir(make_path)
699- for f in filenames:
700- real_path = util.abs_join(real_path, f)
701- make_path = util.abs_join(make_path, f)
702- shutil.copy(real_path, make_path)
703-
704- def patchUtils(self, new_root):
705- patch_funcs = {
706- util: [('write_file', 1),
707- ('append_file', 1),
708- ('load_file', 1),
709- ('ensure_dir', 1),
710- ('chmod', 1),
711- ('delete_dir_contents', 1),
712- ('del_file', 1),
713- ('sym_link', -1),
714- ('copy', -1)],
715- }
716- for (mod, funcs) in patch_funcs.items():
717- for (f, am) in funcs:
718- func = getattr(mod, f)
719- trap_func = retarget_many_wrapper(new_root, am, func)
720- self.patched_funcs.enter_context(
721- mock.patch.object(mod, f, trap_func))
722-
723- # Handle subprocess calls
724- func = getattr(util, 'subp')
725-
726- def nsubp(*_args, **_kwargs):
727- return ('', '')
728-
729- self.patched_funcs.enter_context(
730- mock.patch.object(util, 'subp', nsubp))
731-
732- def null_func(*_args, **_kwargs):
733- return None
734-
735- for f in ['chownbyid', 'chownbyname']:
736- self.patched_funcs.enter_context(
737- mock.patch.object(util, f, null_func))
738-
739- def patchOS(self, new_root):
740- patch_funcs = {
741- os.path: [('isfile', 1), ('exists', 1),
742- ('islink', 1), ('isdir', 1)],
743- os: [('listdir', 1), ('mkdir', 1),
744- ('lstat', 1), ('symlink', 2)],
745- }
746- for (mod, funcs) in patch_funcs.items():
747- for f, nargs in funcs:
748- func = getattr(mod, f)
749- trap_func = retarget_many_wrapper(new_root, nargs, func)
750- self.patched_funcs.enter_context(
751- mock.patch.object(mod, f, trap_func))
752-
753- def patchOpen(self, new_root):
754- trap_func = retarget_many_wrapper(new_root, 1, open)
755- name = 'builtins.open' if PY3 else '__builtin__.open'
756- self.patched_funcs.enter_context(mock.patch(name, trap_func))
757-
758- def patchStdoutAndStderr(self, stdout=None, stderr=None):
759- if stdout is not None:
760- self.patched_funcs.enter_context(
761- mock.patch.object(sys, 'stdout', stdout))
762- if stderr is not None:
763- self.patched_funcs.enter_context(
764- mock.patch.object(sys, 'stderr', stderr))
765-
766- def reRoot(self, root=None):
767- if root is None:
768- root = self.tmp_dir()
769- self.patchUtils(root)
770- self.patchOS(root)
771- return root
772-
773-
774-class HttprettyTestCase(CiTestCase):
775- # necessary as http_proxy gets in the way of httpretty
776- # https://github.com/gabrielfalcao/HTTPretty/issues/122
777- def setUp(self):
778- self.restore_proxy = os.environ.get('http_proxy')
779- if self.restore_proxy is not None:
780- del os.environ['http_proxy']
781- super(HttprettyTestCase, self).setUp()
782-
783- def tearDown(self):
784- if self.restore_proxy:
785- os.environ['http_proxy'] = self.restore_proxy
786- super(HttprettyTestCase, self).tearDown()
787-
788-
789-def populate_dir(path, files):
790- if not os.path.exists(path):
791- os.makedirs(path)
792- ret = []
793- for (name, content) in files.items():
794- p = os.path.sep.join([path, name])
795- util.ensure_dir(os.path.dirname(p))
796- with open(p, "wb") as fp:
797- if isinstance(content, six.binary_type):
798- fp.write(content)
799- else:
800- fp.write(content.encode('utf-8'))
801- fp.close()
802- ret.append(p)
803-
804- return ret
805-
806-
807-def dir2dict(startdir, prefix=None):
808- flist = {}
809- if prefix is None:
810- prefix = startdir
811- for root, dirs, files in os.walk(startdir):
812- for fname in files:
813- fpath = os.path.join(root, fname)
814- key = fpath[len(prefix):]
815- flist[key] = util.load_file(fpath)
816- return flist
817-
818-
819-def json_dumps(data):
820- # print data in nicely formatted json.
821- return json.dumps(data, indent=1, sort_keys=True,
822- separators=(',', ': '))
823-
824-
825-def wrap_and_call(prefix, mocks, func, *args, **kwargs):
826- """
827- call func(args, **kwargs) with mocks applied, then unapplies mocks
828- nicer to read than repeating dectorators on each function
829-
830- prefix: prefix for mock names (e.g. 'cloudinit.stages.util') or None
831- mocks: dictionary of names (under 'prefix') to mock and either
832- a return value or a dictionary to pass to the mock.patch call
833- func: function to call with mocks applied
834- *args,**kwargs: arguments for 'func'
835-
836- return_value: return from 'func'
837- """
838- delim = '.'
839- if prefix is None:
840- prefix = ''
841- prefix = prefix.rstrip(delim)
842- unwraps = []
843- for fname, kw in mocks.items():
844- if prefix:
845- fname = delim.join((prefix, fname))
846- if not isinstance(kw, dict):
847- kw = {'return_value': kw}
848- p = mock.patch(fname, **kw)
849- p.start()
850- unwraps.append(p)
851- try:
852- return func(*args, **kwargs)
853- finally:
854- for p in unwraps:
855- p.stop()
856-
857-
858-try:
859- skipIf = unittest.skipIf
860-except AttributeError:
861- # Python 2.6. Doesn't have to be high fidelity.
862- def skipIf(condition, reason):
863- def decorator(func):
864- def wrapper(*args, **kws):
865- if condition:
866- return func(*args, **kws)
867- else:
868- print(reason, file=sys.stderr)
869- return wrapper
870- return decorator
871-
872-
873-# older versions of mock do not have the useful 'assert_not_called'
874-if not hasattr(mock.Mock, 'assert_not_called'):
875- def __mock_assert_not_called(mmock):
876- if mmock.call_count != 0:
877- msg = ("[citest] Expected '%s' to not have been called. "
878- "Called %s times." %
879- (mmock._mock_name or 'mock', mmock.call_count))
880- raise AssertionError(msg)
881- mock.Mock.assert_not_called = __mock_assert_not_called
882-
883-
884-# vi: ts=4 expandtab
885diff --git a/tests/unittests/test__init__.py b/tests/unittests/test__init__.py
886index 781f6d5..25878d7 100644
887--- a/tests/unittests/test__init__.py
888+++ b/tests/unittests/test__init__.py
889@@ -12,7 +12,7 @@ from cloudinit import settings
890 from cloudinit import url_helper
891 from cloudinit import util
892
893-from .helpers import TestCase, CiTestCase, ExitStack, mock
894+from cloudinit.tests.helpers import TestCase, CiTestCase, ExitStack, mock
895
896
897 class FakeModule(handlers.Handler):
898diff --git a/tests/unittests/test_atomic_helper.py b/tests/unittests/test_atomic_helper.py
899index 515919d..0101b0e 100644
900--- a/tests/unittests/test_atomic_helper.py
901+++ b/tests/unittests/test_atomic_helper.py
902@@ -6,7 +6,7 @@ import stat
903
904 from cloudinit import atomic_helper
905
906-from .helpers import CiTestCase
907+from cloudinit.tests.helpers import CiTestCase
908
909
910 class TestAtomicHelper(CiTestCase):
911diff --git a/tests/unittests/test_builtin_handlers.py b/tests/unittests/test_builtin_handlers.py
912index dd9d035..9751ed9 100644
913--- a/tests/unittests/test_builtin_handlers.py
914+++ b/tests/unittests/test_builtin_handlers.py
915@@ -11,7 +11,7 @@ try:
916 except ImportError:
917 import mock
918
919-from . import helpers as test_helpers
920+from cloudinit.tests import helpers as test_helpers
921
922 from cloudinit import handlers
923 from cloudinit import helpers
924diff --git a/tests/unittests/test_cli.py b/tests/unittests/test_cli.py
925index 12f0185..495bdc9 100644
926--- a/tests/unittests/test_cli.py
927+++ b/tests/unittests/test_cli.py
928@@ -2,7 +2,7 @@
929
930 import six
931
932-from . import helpers as test_helpers
933+from cloudinit.tests import helpers as test_helpers
934
935 from cloudinit.cmd import main as cli
936
937diff --git a/tests/unittests/test_cs_util.py b/tests/unittests/test_cs_util.py
938index b8f5031..ee88520 100644
939--- a/tests/unittests/test_cs_util.py
940+++ b/tests/unittests/test_cs_util.py
941@@ -2,7 +2,7 @@
942
943 from __future__ import print_function
944
945-from . import helpers as test_helpers
946+from cloudinit.tests import helpers as test_helpers
947
948 from cloudinit.cs_utils import Cepko
949
950diff --git a/tests/unittests/test_data.py b/tests/unittests/test_data.py
951index 4ad86bb..6d621d2 100644
952--- a/tests/unittests/test_data.py
953+++ b/tests/unittests/test_data.py
954@@ -27,7 +27,7 @@ from cloudinit import stages
955 from cloudinit import user_data as ud
956 from cloudinit import util
957
958-from . import helpers
959+from cloudinit.tests import helpers
960
961
962 INSTANCE_ID = "i-testing"
963diff --git a/tests/unittests/test_datasource/test_aliyun.py b/tests/unittests/test_datasource/test_aliyun.py
964index 996560e..82ee971 100644
965--- a/tests/unittests/test_datasource/test_aliyun.py
966+++ b/tests/unittests/test_datasource/test_aliyun.py
967@@ -5,9 +5,9 @@ import httpretty
968 import mock
969 import os
970
971-from .. import helpers as test_helpers
972 from cloudinit import helpers
973 from cloudinit.sources import DataSourceAliYun as ay
974+from cloudinit.tests import helpers as test_helpers
975
976 DEFAULT_METADATA = {
977 'instance-id': 'aliyun-test-vm-00',
978diff --git a/tests/unittests/test_datasource/test_altcloud.py b/tests/unittests/test_datasource/test_altcloud.py
979index 9c46abc..3b274d9 100644
980--- a/tests/unittests/test_datasource/test_altcloud.py
981+++ b/tests/unittests/test_datasource/test_altcloud.py
982@@ -18,7 +18,7 @@ import tempfile
983 from cloudinit import helpers
984 from cloudinit import util
985
986-from ..helpers import TestCase
987+from cloudinit.tests.helpers import TestCase
988
989 import cloudinit.sources.DataSourceAltCloud as dsac
990
991diff --git a/tests/unittests/test_datasource/test_azure.py b/tests/unittests/test_datasource/test_azure.py
992index 20e70fb..0a11777 100644
993--- a/tests/unittests/test_datasource/test_azure.py
994+++ b/tests/unittests/test_datasource/test_azure.py
995@@ -6,8 +6,8 @@ from cloudinit.sources import DataSourceAzure as dsaz
996 from cloudinit.util import find_freebsd_part
997 from cloudinit.util import get_path_dev_freebsd
998
999-from ..helpers import (CiTestCase, TestCase, populate_dir, mock,
1000- ExitStack, PY26, SkipTest)
1001+from cloudinit.tests.helpers import (CiTestCase, TestCase, populate_dir, mock,
1002+ ExitStack, PY26, SkipTest)
1003
1004 import crypt
1005 import os
1006@@ -871,6 +871,7 @@ class TestLoadAzureDsDir(CiTestCase):
1007
1008
1009 class TestReadAzureOvf(TestCase):
1010+
1011 def test_invalid_xml_raises_non_azure_ds(self):
1012 invalid_xml = "<foo>" + construct_valid_ovf_env(data={})
1013 self.assertRaises(dsaz.BrokenAzureDataSource,
1014@@ -1079,6 +1080,7 @@ class TestCanDevBeReformatted(CiTestCase):
1015
1016
1017 class TestAzureNetExists(CiTestCase):
1018+
1019 def test_azure_net_must_exist_for_legacy_objpkl(self):
1020 """DataSourceAzureNet must exist for old obj.pkl files
1021 that reference it."""
1022diff --git a/tests/unittests/test_datasource/test_azure_helper.py b/tests/unittests/test_datasource/test_azure_helper.py
1023index b2d2971..80ce003 100644
1024--- a/tests/unittests/test_datasource/test_azure_helper.py
1025+++ b/tests/unittests/test_datasource/test_azure_helper.py
1026@@ -3,7 +3,7 @@
1027 import os
1028
1029 from cloudinit.sources.helpers import azure as azure_helper
1030-from ..helpers import ExitStack, mock, TestCase
1031+from cloudinit.tests.helpers import ExitStack, mock, TestCase
1032
1033
1034 GOAL_STATE_TEMPLATE = """\
1035diff --git a/tests/unittests/test_datasource/test_cloudsigma.py b/tests/unittests/test_datasource/test_cloudsigma.py
1036index 5997102..e4c5990 100644
1037--- a/tests/unittests/test_datasource/test_cloudsigma.py
1038+++ b/tests/unittests/test_datasource/test_cloudsigma.py
1039@@ -6,7 +6,7 @@ from cloudinit.cs_utils import Cepko
1040 from cloudinit import sources
1041 from cloudinit.sources import DataSourceCloudSigma
1042
1043-from .. import helpers as test_helpers
1044+from cloudinit.tests import helpers as test_helpers
1045
1046 SERVER_CONTEXT = {
1047 "cpu": 1000,
1048diff --git a/tests/unittests/test_datasource/test_cloudstack.py b/tests/unittests/test_datasource/test_cloudstack.py
1049index e94aad6..2dc9030 100644
1050--- a/tests/unittests/test_datasource/test_cloudstack.py
1051+++ b/tests/unittests/test_datasource/test_cloudstack.py
1052@@ -3,7 +3,7 @@
1053 from cloudinit import helpers
1054 from cloudinit.sources.DataSourceCloudStack import DataSourceCloudStack
1055
1056-from ..helpers import TestCase, mock, ExitStack
1057+from cloudinit.tests.helpers import TestCase, mock, ExitStack
1058
1059
1060 class TestCloudStackPasswordFetching(TestCase):
1061diff --git a/tests/unittests/test_datasource/test_common.py b/tests/unittests/test_datasource/test_common.py
1062index 4802f10..80b9c65 100644
1063--- a/tests/unittests/test_datasource/test_common.py
1064+++ b/tests/unittests/test_datasource/test_common.py
1065@@ -24,7 +24,7 @@ from cloudinit.sources import (
1066 )
1067 from cloudinit.sources import DataSourceNone as DSNone
1068
1069-from .. import helpers as test_helpers
1070+from cloudinit.tests import helpers as test_helpers
1071
1072 DEFAULT_LOCAL = [
1073 Azure.DataSourceAzure,
1074diff --git a/tests/unittests/test_datasource/test_configdrive.py b/tests/unittests/test_datasource/test_configdrive.py
1075index 337be66..237c189 100644
1076--- a/tests/unittests/test_datasource/test_configdrive.py
1077+++ b/tests/unittests/test_datasource/test_configdrive.py
1078@@ -15,7 +15,7 @@ from cloudinit.sources import DataSourceConfigDrive as ds
1079 from cloudinit.sources.helpers import openstack
1080 from cloudinit import util
1081
1082-from ..helpers import TestCase, ExitStack, mock
1083+from cloudinit.tests.helpers import TestCase, ExitStack, mock
1084
1085
1086 PUBKEY = u'ssh-rsa AAAAB3NzaC1....sIkJhq8wdX+4I3A4cYbYP ubuntu@server-460\n'
1087diff --git a/tests/unittests/test_datasource/test_digitalocean.py b/tests/unittests/test_datasource/test_digitalocean.py
1088index e97a679..f264f36 100644
1089--- a/tests/unittests/test_datasource/test_digitalocean.py
1090+++ b/tests/unittests/test_datasource/test_digitalocean.py
1091@@ -13,7 +13,7 @@ from cloudinit import settings
1092 from cloudinit.sources import DataSourceDigitalOcean
1093 from cloudinit.sources.helpers import digitalocean
1094
1095-from ..helpers import mock, TestCase
1096+from cloudinit.tests.helpers import mock, TestCase
1097
1098 DO_MULTIPLE_KEYS = ["ssh-rsa AAAAB3NzaC1yc2EAAAA... test1@do.co",
1099 "ssh-rsa AAAAB3NzaC1yc2EAAAA... test2@do.co"]
1100diff --git a/tests/unittests/test_datasource/test_ec2.py b/tests/unittests/test_datasource/test_ec2.py
1101index e533668..9fb9048 100644
1102--- a/tests/unittests/test_datasource/test_ec2.py
1103+++ b/tests/unittests/test_datasource/test_ec2.py
1104@@ -4,9 +4,9 @@ import copy
1105 import httpretty
1106 import mock
1107
1108-from .. import helpers as test_helpers
1109 from cloudinit import helpers
1110 from cloudinit.sources import DataSourceEc2 as ec2
1111+from cloudinit.tests import helpers as test_helpers
1112
1113
1114 # collected from api version 2016-09-02/ with
1115@@ -164,48 +164,16 @@ class TestEc2(test_helpers.HttprettyTestCase):
1116 self.datasource = ec2.DataSourceEc2
1117 self.metadata_addr = self.datasource.metadata_urls[0]
1118
1119-<<<<<<< tests/unittests/test_datasource/test_ec2.py
1120 def data_url(self, version):
1121 """Return a metadata url based on the version provided."""
1122 return '/'.join([self.metadata_addr, version, 'meta-data', ''])
1123-=======
1124- @property
1125- def metadata_url(self):
1126- return '/'.join([
1127- self.metadata_addr,
1128- self.datasource.min_metadata_version, 'meta-data', ''])
1129-
1130- @property
1131- def metadata_urls(self):
1132- return ['/'.join([self.metadata_addr, version, 'meta-data'])
1133- for version in
1134- [self.datasource.min_metadata_version] +
1135- self.datasource.extended_metadata_versions]
1136-
1137- @property
1138- def userdata_url(self):
1139- return '/'.join([
1140- self.metadata_addr,
1141- self.datasource.min_metadata_version, 'user-data'])
1142->>>>>>> tests/unittests/test_datasource/test_ec2.py
1143-
1144- @property
1145- def userdata_urls(self):
1146- return ['/'.join([self.metadata_addr, version, 'user-data'])
1147- for version in
1148- [self.datasource.min_metadata_version] +
1149- self.datasource.extended_metadata_versions]
1150
1151 def _patch_add_cleanup(self, mpath, *args, **kwargs):
1152 p = mock.patch(mpath, *args, **kwargs)
1153 p.start()
1154 self.addCleanup(p.stop)
1155
1156-<<<<<<< tests/unittests/test_datasource/test_ec2.py
1157 def _setup_ds(self, sys_cfg, platform_data, md, md_version=None):
1158-=======
1159- def _setup_ds(self, sys_cfg, platform_data, md, ud=None):
1160->>>>>>> tests/unittests/test_datasource/test_ec2.py
1161 self.uris = []
1162 distro = {}
1163 paths = helpers.Paths({})
1164@@ -220,7 +188,6 @@ class TestEc2(test_helpers.HttprettyTestCase):
1165 return_value=platform_data)
1166
1167 if md:
1168-<<<<<<< tests/unittests/test_datasource/test_ec2.py
1169 httpretty.HTTPretty.allow_net_connect = False
1170 all_versions = (
1171 [ds.min_metadata_version] + ds.extended_metadata_versions)
1172@@ -238,13 +205,6 @@ class TestEc2(test_helpers.HttprettyTestCase):
1173 else:
1174 # Register 404s for all unrequested extended versions
1175 register_mock_metaserver(instance_id_url, None)
1176-=======
1177- for url in self.metadata_urls:
1178- register_mock_metaserver(url, md)
1179- for url in self.userdata_urls:
1180- register_mock_metaserver(url, ud)
1181-
1182->>>>>>> tests/unittests/test_datasource/test_ec2.py
1183 return ds
1184
1185 @httpretty.activate
1186diff --git a/tests/unittests/test_datasource/test_gce.py b/tests/unittests/test_datasource/test_gce.py
1187index ad608be..50e49a1 100644
1188--- a/tests/unittests/test_datasource/test_gce.py
1189+++ b/tests/unittests/test_datasource/test_gce.py
1190@@ -15,7 +15,7 @@ from cloudinit import helpers
1191 from cloudinit import settings
1192 from cloudinit.sources import DataSourceGCE
1193
1194-from .. import helpers as test_helpers
1195+from cloudinit.tests import helpers as test_helpers
1196
1197
1198 GCE_META = {
1199diff --git a/tests/unittests/test_datasource/test_maas.py b/tests/unittests/test_datasource/test_maas.py
1200index c1911bf..289c6a4 100644
1201--- a/tests/unittests/test_datasource/test_maas.py
1202+++ b/tests/unittests/test_datasource/test_maas.py
1203@@ -8,7 +8,7 @@ import yaml
1204
1205 from cloudinit.sources import DataSourceMAAS
1206 from cloudinit import url_helper
1207-from ..helpers import TestCase, populate_dir
1208+from cloudinit.tests.helpers import TestCase, populate_dir
1209
1210 try:
1211 from unittest import mock
1212diff --git a/tests/unittests/test_datasource/test_nocloud.py b/tests/unittests/test_datasource/test_nocloud.py
1213index ff29439..fea9156 100644
1214--- a/tests/unittests/test_datasource/test_nocloud.py
1215+++ b/tests/unittests/test_datasource/test_nocloud.py
1216@@ -3,7 +3,7 @@
1217 from cloudinit import helpers
1218 from cloudinit.sources import DataSourceNoCloud
1219 from cloudinit import util
1220-from ..helpers import TestCase, populate_dir, mock, ExitStack
1221+from cloudinit.tests.helpers import TestCase, populate_dir, mock, ExitStack
1222
1223 import os
1224 import shutil
1225diff --git a/tests/unittests/test_datasource/test_opennebula.py b/tests/unittests/test_datasource/test_opennebula.py
1226index b0f8e43..e7d5569 100644
1227--- a/tests/unittests/test_datasource/test_opennebula.py
1228+++ b/tests/unittests/test_datasource/test_opennebula.py
1229@@ -3,7 +3,7 @@
1230 from cloudinit import helpers
1231 from cloudinit.sources import DataSourceOpenNebula as ds
1232 from cloudinit import util
1233-from ..helpers import mock, populate_dir, TestCase
1234+from cloudinit.tests.helpers import mock, populate_dir, TestCase
1235
1236 import os
1237 import pwd
1238diff --git a/tests/unittests/test_datasource/test_openstack.py b/tests/unittests/test_datasource/test_openstack.py
1239index c2905d1..177e980 100644
1240--- a/tests/unittests/test_datasource/test_openstack.py
1241+++ b/tests/unittests/test_datasource/test_openstack.py
1242@@ -9,7 +9,7 @@ import httpretty as hp
1243 import json
1244 import re
1245
1246-from .. import helpers as test_helpers
1247+from cloudinit.tests import helpers as test_helpers
1248
1249 from six.moves.urllib.parse import urlparse
1250 from six import StringIO
1251diff --git a/tests/unittests/test_datasource/test_ovf.py b/tests/unittests/test_datasource/test_ovf.py
1252index 477cf8e..9dbf4dd 100644
1253--- a/tests/unittests/test_datasource/test_ovf.py
1254+++ b/tests/unittests/test_datasource/test_ovf.py
1255@@ -6,7 +6,7 @@
1256
1257 import base64
1258
1259-from .. import helpers as test_helpers
1260+from cloudinit.tests import helpers as test_helpers
1261
1262 from cloudinit.sources import DataSourceOVF as dsovf
1263
1264diff --git a/tests/unittests/test_datasource/test_scaleway.py b/tests/unittests/test_datasource/test_scaleway.py
1265index 65d83ad..436df9e 100644
1266--- a/tests/unittests/test_datasource/test_scaleway.py
1267+++ b/tests/unittests/test_datasource/test_scaleway.py
1268@@ -9,7 +9,7 @@ from cloudinit import helpers
1269 from cloudinit import settings
1270 from cloudinit.sources import DataSourceScaleway
1271
1272-from ..helpers import mock, HttprettyTestCase, TestCase
1273+from cloudinit.tests.helpers import mock, HttprettyTestCase, TestCase
1274
1275
1276 class DataResponses(object):
1277diff --git a/tests/unittests/test_datasource/test_smartos.py b/tests/unittests/test_datasource/test_smartos.py
1278index e3c99bb..933d5b6 100644
1279--- a/tests/unittests/test_datasource/test_smartos.py
1280+++ b/tests/unittests/test_datasource/test_smartos.py
1281@@ -33,7 +33,7 @@ import six
1282 from cloudinit import helpers as c_helpers
1283 from cloudinit.util import b64e
1284
1285-from ..helpers import mock, FilesystemMockingTestCase, TestCase
1286+from cloudinit.tests.helpers import mock, FilesystemMockingTestCase, TestCase
1287
1288 SDC_NICS = json.loads("""
1289 [
1290diff --git a/tests/unittests/test_distros/test_arch.py b/tests/unittests/test_distros/test_arch.py
1291index 3d4c9a7..a95ba3b 100644
1292--- a/tests/unittests/test_distros/test_arch.py
1293+++ b/tests/unittests/test_distros/test_arch.py
1294@@ -3,7 +3,7 @@
1295 from cloudinit.distros.arch import _render_network
1296 from cloudinit import util
1297
1298-from ..helpers import (CiTestCase, dir2dict)
1299+from cloudinit.tests.helpers import (CiTestCase, dir2dict)
1300
1301 from . import _get_distro
1302
1303diff --git a/tests/unittests/test_distros/test_create_users.py b/tests/unittests/test_distros/test_create_users.py
1304index 1d02f7b..aa13670 100644
1305--- a/tests/unittests/test_distros/test_create_users.py
1306+++ b/tests/unittests/test_distros/test_create_users.py
1307@@ -1,7 +1,7 @@
1308 # This file is part of cloud-init. See LICENSE file for license information.
1309
1310 from cloudinit import distros
1311-from ..helpers import (TestCase, mock)
1312+from cloudinit.tests.helpers import (TestCase, mock)
1313
1314
1315 class MyBaseDistro(distros.Distro):
1316diff --git a/tests/unittests/test_distros/test_debian.py b/tests/unittests/test_distros/test_debian.py
1317index 72d3aad..da16a79 100644
1318--- a/tests/unittests/test_distros/test_debian.py
1319+++ b/tests/unittests/test_distros/test_debian.py
1320@@ -2,7 +2,7 @@
1321
1322 from cloudinit import distros
1323 from cloudinit import util
1324-from ..helpers import (FilesystemMockingTestCase, mock)
1325+from cloudinit.tests.helpers import (FilesystemMockingTestCase, mock)
1326
1327
1328 @mock.patch("cloudinit.distros.debian.util.subp")
1329diff --git a/tests/unittests/test_distros/test_generic.py b/tests/unittests/test_distros/test_generic.py
1330index b355a19..791fe61 100644
1331--- a/tests/unittests/test_distros/test_generic.py
1332+++ b/tests/unittests/test_distros/test_generic.py
1333@@ -3,7 +3,7 @@
1334 from cloudinit import distros
1335 from cloudinit import util
1336
1337-from .. import helpers
1338+from cloudinit.tests import helpers
1339
1340 import os
1341 import shutil
1342diff --git a/tests/unittests/test_distros/test_netconfig.py b/tests/unittests/test_distros/test_netconfig.py
1343index 6d89dba..c4bd11b 100644
1344--- a/tests/unittests/test_distros/test_netconfig.py
1345+++ b/tests/unittests/test_distros/test_netconfig.py
1346@@ -12,7 +12,7 @@ try:
1347 except ImportError:
1348 from contextlib2 import ExitStack
1349
1350-from ..helpers import TestCase
1351+from cloudinit.tests.helpers import TestCase
1352
1353 from cloudinit import distros
1354 from cloudinit.distros.parsers.sys_conf import SysConf
1355diff --git a/tests/unittests/test_distros/test_opensuse.py b/tests/unittests/test_distros/test_opensuse.py
1356index bdb1d63..b9bb9b3 100644
1357--- a/tests/unittests/test_distros/test_opensuse.py
1358+++ b/tests/unittests/test_distros/test_opensuse.py
1359@@ -1,6 +1,6 @@
1360 # This file is part of cloud-init. See LICENSE file for license information.
1361
1362-from ..helpers import CiTestCase
1363+from cloudinit.tests.helpers import CiTestCase
1364
1365 from . import _get_distro
1366
1367diff --git a/tests/unittests/test_distros/test_resolv.py b/tests/unittests/test_distros/test_resolv.py
1368index 97168cf..68ea008 100644
1369--- a/tests/unittests/test_distros/test_resolv.py
1370+++ b/tests/unittests/test_distros/test_resolv.py
1371@@ -3,7 +3,7 @@
1372 from cloudinit.distros.parsers import resolv_conf
1373 from cloudinit.distros import rhel_util
1374
1375-from ..helpers import TestCase
1376+from cloudinit.tests.helpers import TestCase
1377
1378 import re
1379 import tempfile
1380diff --git a/tests/unittests/test_distros/test_sles.py b/tests/unittests/test_distros/test_sles.py
1381index c656aac..33e3c45 100644
1382--- a/tests/unittests/test_distros/test_sles.py
1383+++ b/tests/unittests/test_distros/test_sles.py
1384@@ -1,6 +1,6 @@
1385 # This file is part of cloud-init. See LICENSE file for license information.
1386
1387-from ..helpers import CiTestCase
1388+from cloudinit.tests.helpers import CiTestCase
1389
1390 from . import _get_distro
1391
1392diff --git a/tests/unittests/test_distros/test_sysconfig.py b/tests/unittests/test_distros/test_sysconfig.py
1393index 235eceb..c1d5b69 100644
1394--- a/tests/unittests/test_distros/test_sysconfig.py
1395+++ b/tests/unittests/test_distros/test_sysconfig.py
1396@@ -4,7 +4,7 @@ import re
1397
1398 from cloudinit.distros.parsers.sys_conf import SysConf
1399
1400-from ..helpers import TestCase
1401+from cloudinit.tests.helpers import TestCase
1402
1403
1404 # Lots of good examples @
1405diff --git a/tests/unittests/test_distros/test_user_data_normalize.py b/tests/unittests/test_distros/test_user_data_normalize.py
1406index 88746e0..0fa9cdb 100644
1407--- a/tests/unittests/test_distros/test_user_data_normalize.py
1408+++ b/tests/unittests/test_distros/test_user_data_normalize.py
1409@@ -5,7 +5,7 @@ from cloudinit.distros import ug_util
1410 from cloudinit import helpers
1411 from cloudinit import settings
1412
1413-from ..helpers import TestCase
1414+from cloudinit.tests.helpers import TestCase
1415 import mock
1416
1417
1418diff --git a/tests/unittests/test_ds_identify.py b/tests/unittests/test_ds_identify.py
1419index 8ccfe55..1a81a89 100644
1420--- a/tests/unittests/test_ds_identify.py
1421+++ b/tests/unittests/test_ds_identify.py
1422@@ -6,7 +6,8 @@ from uuid import uuid4
1423
1424 from cloudinit import safeyaml
1425 from cloudinit import util
1426-from .helpers import CiTestCase, dir2dict, json_dumps, populate_dir
1427+from cloudinit.tests.helpers import (
1428+ CiTestCase, dir2dict, json_dumps, populate_dir)
1429
1430 UNAME_MYSYS = ("Linux bart 4.4.0-62-generic #83-Ubuntu "
1431 "SMP Wed Jan 18 14:10:15 UTC 2017 x86_64 GNU/Linux")
1432diff --git a/tests/unittests/test_ec2_util.py b/tests/unittests/test_ec2_util.py
1433index 65fdb51..af78997 100644
1434--- a/tests/unittests/test_ec2_util.py
1435+++ b/tests/unittests/test_ec2_util.py
1436@@ -2,7 +2,7 @@
1437
1438 import httpretty as hp
1439
1440-from . import helpers
1441+from cloudinit.tests import helpers
1442
1443 from cloudinit import ec2_utils as eu
1444 from cloudinit import url_helper as uh
1445diff --git a/tests/unittests/test_filters/test_launch_index.py b/tests/unittests/test_filters/test_launch_index.py
1446index 13137f6..6364d38 100644
1447--- a/tests/unittests/test_filters/test_launch_index.py
1448+++ b/tests/unittests/test_filters/test_launch_index.py
1449@@ -2,7 +2,7 @@
1450
1451 import copy
1452
1453-from .. import helpers
1454+from cloudinit.tests import helpers
1455
1456 from six.moves import filterfalse
1457
1458diff --git a/tests/unittests/test_handler/test_handler_apt_conf_v1.py b/tests/unittests/test_handler/test_handler_apt_conf_v1.py
1459index 554277f..83f962a 100644
1460--- a/tests/unittests/test_handler/test_handler_apt_conf_v1.py
1461+++ b/tests/unittests/test_handler/test_handler_apt_conf_v1.py
1462@@ -3,7 +3,7 @@
1463 from cloudinit.config import cc_apt_configure
1464 from cloudinit import util
1465
1466-from ..helpers import TestCase
1467+from cloudinit.tests.helpers import TestCase
1468
1469 import copy
1470 import os
1471diff --git a/tests/unittests/test_handler/test_handler_apt_configure_sources_list_v1.py b/tests/unittests/test_handler/test_handler_apt_configure_sources_list_v1.py
1472index f53ddbb..d2b96f0 100644
1473--- a/tests/unittests/test_handler/test_handler_apt_configure_sources_list_v1.py
1474+++ b/tests/unittests/test_handler/test_handler_apt_configure_sources_list_v1.py
1475@@ -24,7 +24,7 @@ from cloudinit.sources import DataSourceNone
1476
1477 from cloudinit.distros.debian import Distro
1478
1479-from .. import helpers as t_help
1480+from cloudinit.tests import helpers as t_help
1481
1482 LOG = logging.getLogger(__name__)
1483
1484diff --git a/tests/unittests/test_handler/test_handler_apt_configure_sources_list_v3.py b/tests/unittests/test_handler/test_handler_apt_configure_sources_list_v3.py
1485index 1ca915b..f7608c2 100644
1486--- a/tests/unittests/test_handler/test_handler_apt_configure_sources_list_v3.py
1487+++ b/tests/unittests/test_handler/test_handler_apt_configure_sources_list_v3.py
1488@@ -24,7 +24,7 @@ from cloudinit.sources import DataSourceNone
1489
1490 from cloudinit.distros.debian import Distro
1491
1492-from .. import helpers as t_help
1493+from cloudinit.tests import helpers as t_help
1494
1495 LOG = logging.getLogger(__name__)
1496
1497diff --git a/tests/unittests/test_handler/test_handler_apt_source_v1.py b/tests/unittests/test_handler/test_handler_apt_source_v1.py
1498index 12502d0..3a3f95c 100644
1499--- a/tests/unittests/test_handler/test_handler_apt_source_v1.py
1500+++ b/tests/unittests/test_handler/test_handler_apt_source_v1.py
1501@@ -20,7 +20,7 @@ from cloudinit.config import cc_apt_configure
1502 from cloudinit import gpg
1503 from cloudinit import util
1504
1505-from ..helpers import TestCase
1506+from cloudinit.tests.helpers import TestCase
1507
1508 EXPECTEDKEY = """-----BEGIN PGP PUBLIC KEY BLOCK-----
1509 Version: GnuPG v1
1510diff --git a/tests/unittests/test_handler/test_handler_apt_source_v3.py b/tests/unittests/test_handler/test_handler_apt_source_v3.py
1511index 292d3f5..7bb1b7c 100644
1512--- a/tests/unittests/test_handler/test_handler_apt_source_v3.py
1513+++ b/tests/unittests/test_handler/test_handler_apt_source_v3.py
1514@@ -28,7 +28,7 @@ from cloudinit import util
1515 from cloudinit.config import cc_apt_configure
1516 from cloudinit.sources import DataSourceNone
1517
1518-from .. import helpers as t_help
1519+from cloudinit.tests import helpers as t_help
1520
1521 EXPECTEDKEY = u"""-----BEGIN PGP PUBLIC KEY BLOCK-----
1522 Version: GnuPG v1
1523diff --git a/tests/unittests/test_handler/test_handler_ca_certs.py b/tests/unittests/test_handler/test_handler_ca_certs.py
1524index 7cee2c3..06e14db 100644
1525--- a/tests/unittests/test_handler/test_handler_ca_certs.py
1526+++ b/tests/unittests/test_handler/test_handler_ca_certs.py
1527@@ -5,7 +5,7 @@ from cloudinit.config import cc_ca_certs
1528 from cloudinit import helpers
1529 from cloudinit import util
1530
1531-from ..helpers import TestCase
1532+from cloudinit.tests.helpers import TestCase
1533
1534 import logging
1535 import shutil
1536diff --git a/tests/unittests/test_handler/test_handler_chef.py b/tests/unittests/test_handler/test_handler_chef.py
1537index 6a152ea..e5785cf 100644
1538--- a/tests/unittests/test_handler/test_handler_chef.py
1539+++ b/tests/unittests/test_handler/test_handler_chef.py
1540@@ -14,7 +14,7 @@ from cloudinit import helpers
1541 from cloudinit.sources import DataSourceNone
1542 from cloudinit import util
1543
1544-from .. import helpers as t_help
1545+from cloudinit.tests import helpers as t_help
1546
1547 LOG = logging.getLogger(__name__)
1548
1549diff --git a/tests/unittests/test_handler/test_handler_debug.py b/tests/unittests/test_handler/test_handler_debug.py
1550index 1873c3e..787ba35 100644
1551--- a/tests/unittests/test_handler/test_handler_debug.py
1552+++ b/tests/unittests/test_handler/test_handler_debug.py
1553@@ -11,7 +11,7 @@ from cloudinit import util
1554
1555 from cloudinit.sources import DataSourceNone
1556
1557-from ..helpers import (FilesystemMockingTestCase, mock)
1558+from cloudinit.tests.helpers import (FilesystemMockingTestCase, mock)
1559
1560 import logging
1561 import shutil
1562diff --git a/tests/unittests/test_handler/test_handler_disk_setup.py b/tests/unittests/test_handler/test_handler_disk_setup.py
1563index 8a6d49e..5afcaca 100644
1564--- a/tests/unittests/test_handler/test_handler_disk_setup.py
1565+++ b/tests/unittests/test_handler/test_handler_disk_setup.py
1566@@ -3,7 +3,7 @@
1567 import random
1568
1569 from cloudinit.config import cc_disk_setup
1570-from ..helpers import CiTestCase, ExitStack, mock, TestCase
1571+from cloudinit.tests.helpers import CiTestCase, ExitStack, mock, TestCase
1572
1573
1574 class TestIsDiskUsed(TestCase):
1575diff --git a/tests/unittests/test_handler/test_handler_growpart.py b/tests/unittests/test_handler/test_handler_growpart.py
1576index c5fc8c9..a3e4635 100644
1577--- a/tests/unittests/test_handler/test_handler_growpart.py
1578+++ b/tests/unittests/test_handler/test_handler_growpart.py
1579@@ -4,7 +4,7 @@ from cloudinit import cloud
1580 from cloudinit.config import cc_growpart
1581 from cloudinit import util
1582
1583-from ..helpers import TestCase
1584+from cloudinit.tests.helpers import TestCase
1585
1586 import errno
1587 import logging
1588diff --git a/tests/unittests/test_handler/test_handler_landscape.py b/tests/unittests/test_handler/test_handler_landscape.py
1589index 7c247fa..db92a7e 100644
1590--- a/tests/unittests/test_handler/test_handler_landscape.py
1591+++ b/tests/unittests/test_handler/test_handler_landscape.py
1592@@ -1,9 +1,10 @@
1593 # This file is part of cloud-init. See LICENSE file for license information.
1594
1595 from cloudinit.config import cc_landscape
1596-from cloudinit.sources import DataSourceNone
1597 from cloudinit import (distros, helpers, cloud, util)
1598-from ..helpers import FilesystemMockingTestCase, mock, wrap_and_call
1599+from cloudinit.sources import DataSourceNone
1600+from cloudinit.tests.helpers import (FilesystemMockingTestCase, mock,
1601+ wrap_and_call)
1602
1603 from configobj import ConfigObj
1604 import logging
1605diff --git a/tests/unittests/test_handler/test_handler_locale.py b/tests/unittests/test_handler/test_handler_locale.py
1606index a789db3..e29a06f 100644
1607--- a/tests/unittests/test_handler/test_handler_locale.py
1608+++ b/tests/unittests/test_handler/test_handler_locale.py
1609@@ -13,7 +13,7 @@ from cloudinit import util
1610
1611 from cloudinit.sources import DataSourceNoCloud
1612
1613-from .. import helpers as t_help
1614+from cloudinit.tests import helpers as t_help
1615
1616 from configobj import ConfigObj
1617
1618diff --git a/tests/unittests/test_handler/test_handler_lxd.py b/tests/unittests/test_handler/test_handler_lxd.py
1619index 351226b..f132a77 100644
1620--- a/tests/unittests/test_handler/test_handler_lxd.py
1621+++ b/tests/unittests/test_handler/test_handler_lxd.py
1622@@ -3,7 +3,7 @@
1623 from cloudinit.config import cc_lxd
1624 from cloudinit.sources import DataSourceNoCloud
1625 from cloudinit import (distros, helpers, cloud)
1626-from .. import helpers as t_help
1627+from cloudinit.tests import helpers as t_help
1628
1629 import logging
1630
1631diff --git a/tests/unittests/test_handler/test_handler_mcollective.py b/tests/unittests/test_handler/test_handler_mcollective.py
1632index 2a9f382..7eec735 100644
1633--- a/tests/unittests/test_handler/test_handler_mcollective.py
1634+++ b/tests/unittests/test_handler/test_handler_mcollective.py
1635@@ -4,7 +4,7 @@ from cloudinit import (cloud, distros, helpers, util)
1636 from cloudinit.config import cc_mcollective
1637 from cloudinit.sources import DataSourceNoCloud
1638
1639-from .. import helpers as t_help
1640+from cloudinit.tests import helpers as t_help
1641
1642 import configobj
1643 import logging
1644diff --git a/tests/unittests/test_handler/test_handler_mounts.py b/tests/unittests/test_handler/test_handler_mounts.py
1645index 650ca0e..fe492d4 100644
1646--- a/tests/unittests/test_handler/test_handler_mounts.py
1647+++ b/tests/unittests/test_handler/test_handler_mounts.py
1648@@ -6,7 +6,7 @@ import tempfile
1649
1650 from cloudinit.config import cc_mounts
1651
1652-from .. import helpers as test_helpers
1653+from cloudinit.tests import helpers as test_helpers
1654
1655 try:
1656 from unittest import mock
1657diff --git a/tests/unittests/test_handler/test_handler_ntp.py b/tests/unittests/test_handler/test_handler_ntp.py
1658index 83d5faa..4f29124 100644
1659--- a/tests/unittests/test_handler/test_handler_ntp.py
1660+++ b/tests/unittests/test_handler/test_handler_ntp.py
1661@@ -3,7 +3,7 @@
1662 from cloudinit.config import cc_ntp
1663 from cloudinit.sources import DataSourceNone
1664 from cloudinit import (distros, helpers, cloud, util)
1665-from ..helpers import FilesystemMockingTestCase, mock, skipIf
1666+from cloudinit.tests.helpers import FilesystemMockingTestCase, mock, skipIf
1667
1668
1669 import os
1670diff --git a/tests/unittests/test_handler/test_handler_power_state.py b/tests/unittests/test_handler/test_handler_power_state.py
1671index e382210..85a0fe0 100644
1672--- a/tests/unittests/test_handler/test_handler_power_state.py
1673+++ b/tests/unittests/test_handler/test_handler_power_state.py
1674@@ -4,8 +4,8 @@ import sys
1675
1676 from cloudinit.config import cc_power_state_change as psc
1677
1678-from .. import helpers as t_help
1679-from ..helpers import mock
1680+from cloudinit.tests import helpers as t_help
1681+from cloudinit.tests.helpers import mock
1682
1683
1684 class TestLoadPowerState(t_help.TestCase):
1685diff --git a/tests/unittests/test_handler/test_handler_puppet.py b/tests/unittests/test_handler/test_handler_puppet.py
1686index 805c76b..0b6e3b5 100644
1687--- a/tests/unittests/test_handler/test_handler_puppet.py
1688+++ b/tests/unittests/test_handler/test_handler_puppet.py
1689@@ -3,7 +3,7 @@
1690 from cloudinit.config import cc_puppet
1691 from cloudinit.sources import DataSourceNone
1692 from cloudinit import (distros, helpers, cloud, util)
1693-from ..helpers import CiTestCase, mock
1694+from cloudinit.tests.helpers import CiTestCase, mock
1695
1696 import logging
1697
1698diff --git a/tests/unittests/test_handler/test_handler_rsyslog.py b/tests/unittests/test_handler/test_handler_rsyslog.py
1699index cca0667..8c8e283 100644
1700--- a/tests/unittests/test_handler/test_handler_rsyslog.py
1701+++ b/tests/unittests/test_handler/test_handler_rsyslog.py
1702@@ -9,7 +9,7 @@ from cloudinit.config.cc_rsyslog import (
1703 parse_remotes_line, remotes_to_rsyslog_cfg)
1704 from cloudinit import util
1705
1706-from .. import helpers as t_help
1707+from cloudinit.tests import helpers as t_help
1708
1709
1710 class TestLoadConfig(t_help.TestCase):
1711diff --git a/tests/unittests/test_handler/test_handler_runcmd.py b/tests/unittests/test_handler/test_handler_runcmd.py
1712index 7880ee7..374c1d3 100644
1713--- a/tests/unittests/test_handler/test_handler_runcmd.py
1714+++ b/tests/unittests/test_handler/test_handler_runcmd.py
1715@@ -3,7 +3,7 @@
1716 from cloudinit.config import cc_runcmd
1717 from cloudinit.sources import DataSourceNone
1718 from cloudinit import (distros, helpers, cloud, util)
1719-from ..helpers import FilesystemMockingTestCase, skipIf
1720+from cloudinit.tests.helpers import FilesystemMockingTestCase, skipIf
1721
1722 import logging
1723 import os
1724diff --git a/tests/unittests/test_handler/test_handler_seed_random.py b/tests/unittests/test_handler/test_handler_seed_random.py
1725index e5e607f..f60dedc 100644
1726--- a/tests/unittests/test_handler/test_handler_seed_random.py
1727+++ b/tests/unittests/test_handler/test_handler_seed_random.py
1728@@ -22,7 +22,7 @@ from cloudinit import util
1729
1730 from cloudinit.sources import DataSourceNone
1731
1732-from .. import helpers as t_help
1733+from cloudinit.tests import helpers as t_help
1734
1735 import logging
1736
1737diff --git a/tests/unittests/test_handler/test_handler_set_hostname.py b/tests/unittests/test_handler/test_handler_set_hostname.py
1738index 8165bf9..abdc17e 100644
1739--- a/tests/unittests/test_handler/test_handler_set_hostname.py
1740+++ b/tests/unittests/test_handler/test_handler_set_hostname.py
1741@@ -7,7 +7,7 @@ from cloudinit import distros
1742 from cloudinit import helpers
1743 from cloudinit import util
1744
1745-from .. import helpers as t_help
1746+from cloudinit.tests import helpers as t_help
1747
1748 from configobj import ConfigObj
1749 import logging
1750diff --git a/tests/unittests/test_handler/test_handler_snappy.py b/tests/unittests/test_handler/test_handler_snappy.py
1751index e4d0762..76b79c2 100644
1752--- a/tests/unittests/test_handler/test_handler_snappy.py
1753+++ b/tests/unittests/test_handler/test_handler_snappy.py
1754@@ -7,9 +7,9 @@ from cloudinit.config.cc_snap_config import (
1755 from cloudinit import (distros, helpers, cloud, util)
1756 from cloudinit.config.cc_snap_config import handle as snap_handle
1757 from cloudinit.sources import DataSourceNone
1758-from ..helpers import FilesystemMockingTestCase, mock
1759+from cloudinit.tests.helpers import FilesystemMockingTestCase, mock
1760
1761-from .. import helpers as t_help
1762+from cloudinit.tests import helpers as t_help
1763
1764 import logging
1765 import os
1766diff --git a/tests/unittests/test_handler/test_handler_spacewalk.py b/tests/unittests/test_handler/test_handler_spacewalk.py
1767index 28b5892..ddbf4a7 100644
1768--- a/tests/unittests/test_handler/test_handler_spacewalk.py
1769+++ b/tests/unittests/test_handler/test_handler_spacewalk.py
1770@@ -3,7 +3,7 @@
1771 from cloudinit.config import cc_spacewalk
1772 from cloudinit import util
1773
1774-from .. import helpers
1775+from cloudinit.tests import helpers
1776
1777 import logging
1778
1779diff --git a/tests/unittests/test_handler/test_handler_timezone.py b/tests/unittests/test_handler/test_handler_timezone.py
1780index c30fbdf..27eedde 100644
1781--- a/tests/unittests/test_handler/test_handler_timezone.py
1782+++ b/tests/unittests/test_handler/test_handler_timezone.py
1783@@ -13,7 +13,7 @@ from cloudinit import util
1784
1785 from cloudinit.sources import DataSourceNoCloud
1786
1787-from .. import helpers as t_help
1788+from cloudinit.tests import helpers as t_help
1789
1790 from configobj import ConfigObj
1791 import logging
1792diff --git a/tests/unittests/test_handler/test_handler_write_files.py b/tests/unittests/test_handler/test_handler_write_files.py
1793index 1129e77..7fa8fd2 100644
1794--- a/tests/unittests/test_handler/test_handler_write_files.py
1795+++ b/tests/unittests/test_handler/test_handler_write_files.py
1796@@ -4,7 +4,7 @@ from cloudinit.config.cc_write_files import write_files, decode_perms
1797 from cloudinit import log as logging
1798 from cloudinit import util
1799
1800-from ..helpers import CiTestCase, FilesystemMockingTestCase
1801+from cloudinit.tests.helpers import CiTestCase, FilesystemMockingTestCase
1802
1803 import base64
1804 import gzip
1805diff --git a/tests/unittests/test_handler/test_handler_yum_add_repo.py b/tests/unittests/test_handler/test_handler_yum_add_repo.py
1806index c4396df..b7adbe5 100644
1807--- a/tests/unittests/test_handler/test_handler_yum_add_repo.py
1808+++ b/tests/unittests/test_handler/test_handler_yum_add_repo.py
1809@@ -3,7 +3,7 @@
1810 from cloudinit.config import cc_yum_add_repo
1811 from cloudinit import util
1812
1813-from .. import helpers
1814+from cloudinit.tests import helpers
1815
1816 try:
1817 from configparser import ConfigParser
1818diff --git a/tests/unittests/test_handler/test_schema.py b/tests/unittests/test_handler/test_schema.py
1819index 640f11d..6137e3c 100644
1820--- a/tests/unittests/test_handler/test_schema.py
1821+++ b/tests/unittests/test_handler/test_schema.py
1822@@ -6,7 +6,7 @@ from cloudinit.config.schema import (
1823 validate_cloudconfig_schema, main)
1824 from cloudinit.util import write_file
1825
1826-from ..helpers import CiTestCase, mock, skipIf
1827+from cloudinit.tests.helpers import CiTestCase, mock, skipIf
1828
1829 from copy import copy
1830 from six import StringIO
1831diff --git a/tests/unittests/test_helpers.py b/tests/unittests/test_helpers.py
1832index f1979e8..2e4582a 100644
1833--- a/tests/unittests/test_helpers.py
1834+++ b/tests/unittests/test_helpers.py
1835@@ -4,7 +4,7 @@
1836
1837 import os
1838
1839-from . import helpers as test_helpers
1840+from cloudinit.tests import helpers as test_helpers
1841
1842 from cloudinit import sources
1843
1844diff --git a/tests/unittests/test_log.py b/tests/unittests/test_log.py
1845index 68fb4b8..cd6296d 100644
1846--- a/tests/unittests/test_log.py
1847+++ b/tests/unittests/test_log.py
1848@@ -2,9 +2,9 @@
1849
1850 """Tests for cloudinit.log """
1851
1852-from .helpers import CiTestCase
1853 from cloudinit.analyze.dump import CLOUD_INIT_ASCTIME_FMT
1854 from cloudinit import log as ci_logging
1855+from cloudinit.tests.helpers import CiTestCase
1856 import datetime
1857 import logging
1858 import six
1859diff --git a/tests/unittests/test_merging.py b/tests/unittests/test_merging.py
1860index 0658b6b..f51358d 100644
1861--- a/tests/unittests/test_merging.py
1862+++ b/tests/unittests/test_merging.py
1863@@ -1,6 +1,6 @@
1864 # This file is part of cloud-init. See LICENSE file for license information.
1865
1866-from . import helpers
1867+from cloudinit.tests import helpers
1868
1869 from cloudinit.handlers import cloud_config
1870 from cloudinit.handlers import (CONTENT_START, CONTENT_END)
1871diff --git a/tests/unittests/test_net.py b/tests/unittests/test_net.py
1872index f251024..c10ef90 100644
1873--- a/tests/unittests/test_net.py
1874+++ b/tests/unittests/test_net.py
1875@@ -11,10 +11,10 @@ from cloudinit.net import sysconfig
1876 from cloudinit.sources.helpers import openstack
1877 from cloudinit import util
1878
1879-from .helpers import CiTestCase
1880-from .helpers import dir2dict
1881-from .helpers import mock
1882-from .helpers import populate_dir
1883+from cloudinit.tests.helpers import CiTestCase
1884+from cloudinit.tests.helpers import dir2dict
1885+from cloudinit.tests.helpers import mock
1886+from cloudinit.tests.helpers import populate_dir
1887
1888 import base64
1889 import copy
1890diff --git a/tests/unittests/test_pathprefix2dict.py b/tests/unittests/test_pathprefix2dict.py
1891index a4ae284..abbb29b 100644
1892--- a/tests/unittests/test_pathprefix2dict.py
1893+++ b/tests/unittests/test_pathprefix2dict.py
1894@@ -2,7 +2,7 @@
1895
1896 from cloudinit import util
1897
1898-from .helpers import TestCase, populate_dir
1899+from cloudinit.tests.helpers import TestCase, populate_dir
1900
1901 import shutil
1902 import tempfile
1903diff --git a/tests/unittests/test_registry.py b/tests/unittests/test_registry.py
1904index acf0bf4..2b62502 100644
1905--- a/tests/unittests/test_registry.py
1906+++ b/tests/unittests/test_registry.py
1907@@ -2,7 +2,7 @@
1908
1909 from cloudinit.registry import DictRegistry
1910
1911-from .helpers import (mock, TestCase)
1912+from cloudinit.tests.helpers import (mock, TestCase)
1913
1914
1915 class TestDictRegistry(TestCase):
1916diff --git a/tests/unittests/test_reporting.py b/tests/unittests/test_reporting.py
1917index f3b8f99..571420e 100644
1918--- a/tests/unittests/test_reporting.py
1919+++ b/tests/unittests/test_reporting.py
1920@@ -8,7 +8,7 @@ from cloudinit.reporting import handlers
1921
1922 import mock
1923
1924-from .helpers import TestCase
1925+from cloudinit.tests.helpers import TestCase
1926
1927
1928 def _fake_registry():
1929diff --git a/tests/unittests/test_rh_subscription.py b/tests/unittests/test_rh_subscription.py
1930index ca14cd4..e9d5702 100644
1931--- a/tests/unittests/test_rh_subscription.py
1932+++ b/tests/unittests/test_rh_subscription.py
1933@@ -7,7 +7,7 @@ import logging
1934 from cloudinit.config import cc_rh_subscription
1935 from cloudinit import util
1936
1937-from .helpers import TestCase, mock
1938+from cloudinit.tests.helpers import TestCase, mock
1939
1940
1941 class GoodTests(TestCase):
1942diff --git a/tests/unittests/test_runs/test_merge_run.py b/tests/unittests/test_runs/test_merge_run.py
1943index 6589527..add9365 100644
1944--- a/tests/unittests/test_runs/test_merge_run.py
1945+++ b/tests/unittests/test_runs/test_merge_run.py
1946@@ -4,7 +4,7 @@ import os
1947 import shutil
1948 import tempfile
1949
1950-from .. import helpers
1951+from cloudinit.tests import helpers
1952
1953 from cloudinit.settings import PER_INSTANCE
1954 from cloudinit import stages
1955diff --git a/tests/unittests/test_runs/test_simple_run.py b/tests/unittests/test_runs/test_simple_run.py
1956index 55f15b5..5cf666f 100644
1957--- a/tests/unittests/test_runs/test_simple_run.py
1958+++ b/tests/unittests/test_runs/test_simple_run.py
1959@@ -4,7 +4,7 @@ import os
1960 import shutil
1961 import tempfile
1962
1963-from .. import helpers
1964+from cloudinit.tests import helpers
1965
1966 from cloudinit.settings import PER_INSTANCE
1967 from cloudinit import stages
1968diff --git a/tests/unittests/test_sshutil.py b/tests/unittests/test_sshutil.py
1969index 991f45a..2a8e6ab 100644
1970--- a/tests/unittests/test_sshutil.py
1971+++ b/tests/unittests/test_sshutil.py
1972@@ -2,8 +2,8 @@
1973
1974 from mock import patch
1975
1976-from . import helpers as test_helpers
1977 from cloudinit import ssh_util
1978+from cloudinit.tests import helpers as test_helpers
1979
1980
1981 VALID_CONTENT = {
1982@@ -57,6 +57,7 @@ TEST_OPTIONS = (
1983
1984
1985 class TestAuthKeyLineParser(test_helpers.TestCase):
1986+
1987 def test_simple_parse(self):
1988 # test key line with common 3 fields (keytype, base64, comment)
1989 parser = ssh_util.AuthKeyLineParser()
1990diff --git a/tests/unittests/test_templating.py b/tests/unittests/test_templating.py
1991index 4e62782..b911d92 100644
1992--- a/tests/unittests/test_templating.py
1993+++ b/tests/unittests/test_templating.py
1994@@ -6,7 +6,7 @@
1995
1996 from __future__ import print_function
1997
1998-from . import helpers as test_helpers
1999+from cloudinit.tests import helpers as test_helpers
2000 import textwrap
2001
2002 from cloudinit import templater
2003diff --git a/tests/unittests/test_util.py b/tests/unittests/test_util.py
2004index 5f11c88..3e4154c 100644
2005--- a/tests/unittests/test_util.py
2006+++ b/tests/unittests/test_util.py
2007@@ -12,7 +12,7 @@ import six
2008 import yaml
2009
2010 from cloudinit import importer, util
2011-from . import helpers
2012+from cloudinit.tests import helpers
2013
2014 try:
2015 from unittest import mock
2016diff --git a/tests/unittests/test_version.py b/tests/unittests/test_version.py
2017index 1662ce0..d012f69 100644
2018--- a/tests/unittests/test_version.py
2019+++ b/tests/unittests/test_version.py
2020@@ -1,6 +1,6 @@
2021 # This file is part of cloud-init. See LICENSE file for license information.
2022
2023-from .helpers import CiTestCase
2024+from cloudinit.tests.helpers import CiTestCase
2025 from cloudinit import version
2026
2027
2028diff --git a/tests/unittests/test_vmware_config_file.py b/tests/unittests/test_vmware_config_file.py
2029index 03b36d3..d865107 100644
2030--- a/tests/unittests/test_vmware_config_file.py
2031+++ b/tests/unittests/test_vmware_config_file.py
2032@@ -8,10 +8,10 @@
2033 import logging
2034 import sys
2035
2036-from .helpers import CiTestCase
2037 from cloudinit.sources.helpers.vmware.imc.boot_proto import BootProtoEnum
2038 from cloudinit.sources.helpers.vmware.imc.config import Config
2039 from cloudinit.sources.helpers.vmware.imc.config_file import ConfigFile
2040+from cloudinit.tests.helpers import CiTestCase
2041
2042 logging.basicConfig(level=logging.DEBUG, stream=sys.stdout)
2043 logger = logging.getLogger(__name__)

Subscribers

People subscribed via source and target branches