Merge pull request #20 from gnuoy/prep-for-opendev
Prepare for more to opendev
This commit is contained in:
25
LICENSE
25
LICENSE
@@ -175,3 +175,28 @@
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
16
copyright
16
copyright
@@ -1,16 +0,0 @@
|
||||
Format: http://dep.debian.net/deps/dep5/
|
||||
|
||||
Files: *
|
||||
Copyright: Copyright 2015, Canonical Ltd., All Rights Reserved.
|
||||
License: Apache License 2.0
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
.
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
.
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
53
perf.py
53
perf.py
@@ -1,53 +0,0 @@
|
||||
import sys
|
||||
import subprocess
|
||||
import threading
|
||||
|
||||
class Iperf():
|
||||
"""
|
||||
Install and start a server automatically
|
||||
"""
|
||||
try:
|
||||
if not nextline:
|
||||
mtu = None
|
||||
except NameError:
|
||||
mtu = None
|
||||
# The following is a mess - since I'm installing iperf3 in the function
|
||||
# Surely there is another easier way to get this into the charm?
|
||||
def __init__(self):
|
||||
#try:
|
||||
# subprocess.check_call(['pgrep', 'iperf'], stderr=subprocess.STDOUT)
|
||||
# if a:
|
||||
thread = threading.Thread(target=self.start_server, args=())
|
||||
thread.start()
|
||||
#except:
|
||||
# pass
|
||||
#hookenv.log(sys.exc_info()[0], 'INFO')
|
||||
|
||||
def start_server(self):
|
||||
process = subprocess.Popen(['iperf', '-s', '-m'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
while True:
|
||||
nextline = process.stdout.readline()
|
||||
nextline = nextline.decode("utf-8")
|
||||
if nextline == '' and process.poll() is not None:
|
||||
break
|
||||
if "bits" in nextline:
|
||||
self.speed = nextline.rsplit(' ', 2)[1]
|
||||
sys.stdout.write(self.speed)
|
||||
sys.stdout.write("\n")
|
||||
if "MTU" in nextline:
|
||||
self.mtu = nextline.rsplit(' ', 4)[1]
|
||||
sys.stdout.write(self.mtu)
|
||||
sys.stdout.flush()
|
||||
#output = process.communicate()[0]
|
||||
#exitCode = process.returncode
|
||||
#
|
||||
#output = exitCode
|
||||
|
||||
#if (exitCode == 0):
|
||||
# pass
|
||||
#elif exitCode:
|
||||
# raise Exception(command, exitCode, output)
|
||||
|
||||
|
||||
perf = Iperf()
|
||||
#print (perf.mtu)
|
||||
@@ -1,3 +1,11 @@
|
||||
charm-tools
|
||||
# This file is managed centrally by release-tools and should not be modified
|
||||
# within individual charm repos. See the 'global' dir contents for available
|
||||
# choices of *requirements.txt files for OpenStack Charms:
|
||||
# https://github.com/openstack-charmers/release-tools
|
||||
#
|
||||
setuptools<50.0.0 # https://github.com/pypa/setuptools/commit/04e3df22df840c6bb244e9b27bc56750c44b7c85
|
||||
# Build requirements
|
||||
charm-tools>=2.4.4
|
||||
# importlib-resources 1.1.0 removed Python 3.5 support
|
||||
importlib-resources<1.1.0
|
||||
simplejson
|
||||
flake8>=2.2.4,<=2.4.1
|
||||
|
||||
25
src/LICENSE
25
src/LICENSE
@@ -175,3 +175,28 @@
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
@@ -1,4 +1,16 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright 2020 Canonical Ltd
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
@@ -536,7 +548,7 @@ def check_dns(nodes):
|
||||
if unit_id in nofwd:
|
||||
nofwd.remove(unit_id)
|
||||
if ip != forward:
|
||||
mstr = '(r\"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$"'
|
||||
mstr = r'(r\"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$"'
|
||||
if not re.match(mstr, forward):
|
||||
forward = "Can not resolve hostname to IP {}"\
|
||||
.format(repr(forward))
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
# Copyright 2020 Canonical Ltd
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
from charms.reactive import when, when_not, set_state, remove_state
|
||||
from charmhelpers.core import hookenv
|
||||
@@ -64,9 +78,9 @@ def check_check_state(magpie):
|
||||
Servers should only update their status after iperf has checked them
|
||||
'''
|
||||
if magpie.get_iperf_checked():
|
||||
for units in magpie.get_iperf_checked():
|
||||
if units and hookenv.local_unit() in units:
|
||||
set_state('iperf.checked')
|
||||
for units in magpie.get_iperf_checked():
|
||||
if units and hookenv.local_unit() in units:
|
||||
set_state('iperf.checked')
|
||||
|
||||
|
||||
@when('magpie.joined', 'leadership.is_leader')
|
||||
|
||||
8
src/test-requirements.txt
Normal file
8
src/test-requirements.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
# This file is managed centrally by release-tools and should not be modified
|
||||
# within individual charm repos. See the 'global' dir contents for available
|
||||
# choices of *requirements.txt files for OpenStack Charms:
|
||||
# https://github.com/openstack-charmers/release-tools
|
||||
#
|
||||
# Functional Test Requirements (let Zaza's dependencies solve all dependencies here!)
|
||||
git+https://github.com/openstack-charmers/zaza.git#egg=zaza
|
||||
git+https://github.com/openstack-charmers/zaza-openstack-tests.git#egg=zaza.openstack
|
||||
@@ -1,21 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import unittest
|
||||
import amulet
|
||||
|
||||
|
||||
class TestDeploy(unittest.TestCase):
|
||||
"""
|
||||
Trivial deployment test for Magpie
|
||||
"""
|
||||
|
||||
def test_deploy(self):
|
||||
self.d = amulet.Deployment(series='xenial')
|
||||
self.d.add('magpie', charm='~admcleod/magpie')
|
||||
self.d.setup(timeout=900)
|
||||
self.d.sentry.wait_for_messages({'magpie': 'Waiting for peers...'},
|
||||
timeout=3600)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -1,83 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import re
|
||||
import unittest
|
||||
import amulet
|
||||
|
||||
|
||||
class TestDeploy(unittest.TestCase):
|
||||
"""
|
||||
Deploy 2 peers and make sure their status messages contain "or" or "failed"
|
||||
This does not test the substrate - only that the charms deploy and relate.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.d = amulet.Deployment(series='xenial')
|
||||
cls.d.add('magpie', charm='~admcleod/magpie', units=2)
|
||||
cls.d.setup(timeout=900)
|
||||
cls.magpie_0 = cls.d.sentry['magpie'][0]
|
||||
cls.magpie_1 = cls.d.sentry['magpie'][1]
|
||||
|
||||
def test_deploy(self):
|
||||
self.d.sentry.wait_for_messages({'magpie': re.compile('ok|failed')},
|
||||
timeout=60)
|
||||
|
||||
# following test is commented out until this bug is resolved;
|
||||
# https://bugs.launchpad.net/juju/+bug/1623480
|
||||
# def test_check_local_hostname(self):
|
||||
# self.d.sentry.wait_for_messages({'magpie': {re.compile('.*hostname
|
||||
# ok.*'}}, timeout=60)
|
||||
|
||||
def test_break_dns_single(self):
|
||||
print('Test break dns single...')
|
||||
"""
|
||||
Break DNS on one unit, make sure DNS check fails, fix DNS, toggle
|
||||
back
|
||||
"""
|
||||
self.d.sentry.wait_for_messages({'magpie': 'icmp ok, dns ok'},
|
||||
timeout=60)
|
||||
self.magpie_0.run("sudo mv /etc/resolv.conf /etc/resolv.conf.bak")
|
||||
self.magpie_0.run("hooks/update-status")
|
||||
self.d.sentry.wait_for_messages({'magpie':
|
||||
{re.compile('.*dns failed*')}},
|
||||
timeout=60)
|
||||
self.magpie_0.run("sudo mv /etc/resolv.conf.bak /etc/resolv.conf")
|
||||
self.magpie_0.run("hooks/update-status")
|
||||
self.d.sentry.wait_for_messages({'magpie': 'icmp ok, dns ok'},
|
||||
timeout=60)
|
||||
|
||||
def test_break_dns_all(self):
|
||||
print('Test break dns all...')
|
||||
"""
|
||||
Set DNS with action to 255.255.255.255 - All units should fail DNS.
|
||||
"""
|
||||
self.d.configure('magpie', {'dns_server': '255.255.255.255'})
|
||||
self.magpie_0.run("hooks/update-status")
|
||||
self.magpie_1.run("hooks/update-status")
|
||||
self.d.sentry.wait_for_messages({'magpie':
|
||||
re.compile('icmp ok,.*dns failed.*')})
|
||||
self.d.configure('magpie', {'dns_server': ''})
|
||||
self.magpie_0.run("hooks/update-status")
|
||||
self.magpie_1.run("hooks/update-status")
|
||||
self.d.sentry.wait_for_messages({'magpie': 'icmp ok, dns ok'})
|
||||
|
||||
def test_break_ping_single(self):
|
||||
print('Test break ping single')
|
||||
"""
|
||||
Take primary interface down and make sure ICMP fails.
|
||||
"""
|
||||
stoprestart = "(sudo service networking stop; sleep 60; service \
|
||||
networking start) & "
|
||||
self.magpie_1.run(stoprestart)
|
||||
self.magpie_1.run("hooks/update-status")
|
||||
self.d.sentry.wait_for_messages({'magpie':
|
||||
{re.compile('icmp failed.*')}},
|
||||
timeout=120)
|
||||
self.magpie_1.run("hooks/update-status")
|
||||
self.d.sentry.wait_for_messages({'magpie': {re.compile('icmp ok.*')}},
|
||||
timeout=120)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
5
src/tests/bundles/bionic.yaml
Normal file
5
src/tests/bundles/bionic.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
series: bionic
|
||||
applications:
|
||||
magpie:
|
||||
num_units: 3
|
||||
charm: magpie
|
||||
5
src/tests/bundles/focal.yaml
Normal file
5
src/tests/bundles/focal.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
series: focal
|
||||
applications:
|
||||
magpie:
|
||||
num_units: 3
|
||||
charm: magpie
|
||||
5
src/tests/bundles/groovy.yaml
Normal file
5
src/tests/bundles/groovy.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
series: groovy
|
||||
applications:
|
||||
magpie:
|
||||
num_units: 3
|
||||
charm: magpie
|
||||
@@ -1,3 +1,15 @@
|
||||
reset: false
|
||||
packages:
|
||||
- amulet
|
||||
charm_name: magpie
|
||||
gate_bundles:
|
||||
- bionic
|
||||
- focal
|
||||
- groovy
|
||||
smoke_bundles:
|
||||
- focal
|
||||
target_deploy_status:
|
||||
magpie:
|
||||
workload-status-message: "icmp ok"
|
||||
tests:
|
||||
- zaza.openstack.charm_tests.magpie.tests.MagpieTest
|
||||
tests_options:
|
||||
force_deploy:
|
||||
- groovy
|
||||
|
||||
50
src/tox.ini
Normal file
50
src/tox.ini
Normal file
@@ -0,0 +1,50 @@
|
||||
# Source charm (with zaza): ./src/tox.ini
|
||||
# This file is managed centrally by release-tools and should not be modified
|
||||
# within individual charm repos. See the 'global' dir contents for available
|
||||
# choices of tox.ini for OpenStack Charms:
|
||||
# https://github.com/openstack-charmers/release-tools
|
||||
|
||||
[tox]
|
||||
envlist = pep8
|
||||
skipsdist = True
|
||||
# NOTE: Avoid build/test env pollution by not enabling sitepackages.
|
||||
sitepackages = False
|
||||
# NOTE: Avoid false positives by not skipping missing interpreters.
|
||||
skip_missing_interpreters = False
|
||||
|
||||
[testenv]
|
||||
setenv = VIRTUAL_ENV={envdir}
|
||||
PYTHONHASHSEED=0
|
||||
whitelist_externals = juju
|
||||
passenv = HOME TERM CS_* OS_* TEST_*
|
||||
deps = -r{toxinidir}/test-requirements.txt
|
||||
install_command =
|
||||
pip install {opts} {packages}
|
||||
|
||||
[testenv:pep8]
|
||||
basepython = python3
|
||||
deps=charm-tools
|
||||
commands = charm-proof
|
||||
|
||||
[testenv:func-noop]
|
||||
basepython = python3
|
||||
commands =
|
||||
functest-run-suite --help
|
||||
|
||||
[testenv:func]
|
||||
basepython = python3
|
||||
commands =
|
||||
functest-run-suite --keep-model
|
||||
|
||||
[testenv:func-smoke]
|
||||
basepython = python3
|
||||
commands =
|
||||
functest-run-suite --keep-model --smoke
|
||||
|
||||
[testenv:func-target]
|
||||
basepython = python3
|
||||
commands =
|
||||
functest-run-suite --keep-model --bundle {posargs}
|
||||
|
||||
[testenv:venv]
|
||||
commands = {posargs}
|
||||
@@ -1,7 +1,24 @@
|
||||
# Unit test requirements
|
||||
flake8>=2.2.4,<=2.4.1
|
||||
os-testr>=0.4.1
|
||||
# This file is managed centrally by release-tools and should not be modified
|
||||
# within individual charm repos. See the 'global' dir contents for available
|
||||
# choices of *requirements.txt files for OpenStack Charms:
|
||||
# https://github.com/openstack-charmers/release-tools
|
||||
#
|
||||
setuptools<50.0.0 # https://github.com/pypa/setuptools/commit/04e3df22df840c6bb244e9b27bc56750c44b7c85
|
||||
# Lint and unit test requirements
|
||||
flake8>=2.2.4
|
||||
stestr>=2.2.0
|
||||
requests>=2.18.4
|
||||
charms.reactive
|
||||
mock>=1.2
|
||||
nose>=1.3.7
|
||||
coverage>=3.6
|
||||
git+https://github.com/openstack/charms.openstack#egg=charms.openstack
|
||||
git+https://github.com/openstack/charms.openstack.git#egg=charms.openstack
|
||||
#
|
||||
# Revisit for removal / mock improvement:
|
||||
netifaces # vault
|
||||
psycopg2-binary # vault
|
||||
tenacity # vault
|
||||
pbr # vault
|
||||
cryptography # vault, keystone-saml-mellon
|
||||
lxml # keystone-saml-mellon
|
||||
hvac # vault, barbican-vault
|
||||
|
||||
97
tox.ini
Normal file
97
tox.ini
Normal file
@@ -0,0 +1,97 @@
|
||||
# Source charm: ./tox.ini
|
||||
# This file is managed centrally by release-tools and should not be modified
|
||||
# within individual charm repos. See the 'global' dir contents for available
|
||||
# choices of tox.ini for OpenStack Charms:
|
||||
# https://github.com/openstack-charmers/release-tools
|
||||
|
||||
[tox]
|
||||
skipsdist = True
|
||||
envlist = pep8,py3
|
||||
# NOTE: Avoid build/test env pollution by not enabling sitepackages.
|
||||
sitepackages = False
|
||||
# NOTE: Avoid false positives by not skipping missing interpreters.
|
||||
skip_missing_interpreters = False
|
||||
|
||||
[testenv]
|
||||
setenv = VIRTUAL_ENV={envdir}
|
||||
PYTHONHASHSEED=0
|
||||
TERM=linux
|
||||
LAYER_PATH={toxinidir}/layers
|
||||
INTERFACE_PATH={toxinidir}/interfaces
|
||||
JUJU_REPOSITORY={toxinidir}/build
|
||||
passenv = http_proxy https_proxy INTERFACE_PATH LAYER_PATH JUJU_REPOSITORY
|
||||
install_command =
|
||||
pip install {opts} {packages}
|
||||
deps =
|
||||
-r{toxinidir}/requirements.txt
|
||||
|
||||
[testenv:build]
|
||||
basepython = python3
|
||||
commands =
|
||||
charm-build --log-level DEBUG -o {toxinidir}/build src {posargs}
|
||||
|
||||
[testenv:py3]
|
||||
basepython = python3
|
||||
deps = -r{toxinidir}/test-requirements.txt
|
||||
commands = stestr run --slowest {posargs}
|
||||
|
||||
[testenv:py35]
|
||||
basepython = python3.5
|
||||
deps = -r{toxinidir}/test-requirements.txt
|
||||
commands = stestr run --slowest {posargs}
|
||||
|
||||
[testenv:py36]
|
||||
basepython = python3.6
|
||||
deps = -r{toxinidir}/test-requirements.txt
|
||||
commands = stestr run --slowest {posargs}
|
||||
|
||||
[testenv:py37]
|
||||
basepython = python3.7
|
||||
deps = -r{toxinidir}/test-requirements.txt
|
||||
commands = stestr run --slowest {posargs}
|
||||
|
||||
[testenv:py38]
|
||||
basepython = python3.8
|
||||
deps = -r{toxinidir}/test-requirements.txt
|
||||
commands = stestr run --slowest {posargs}
|
||||
|
||||
[testenv:pep8]
|
||||
basepython = python3
|
||||
deps = -r{toxinidir}/test-requirements.txt
|
||||
commands = flake8 {posargs} src unit_tests
|
||||
|
||||
[testenv:cover]
|
||||
# Technique based heavily upon
|
||||
# https://github.com/openstack/nova/blob/master/tox.ini
|
||||
basepython = python3
|
||||
deps = -r{toxinidir}/requirements.txt
|
||||
-r{toxinidir}/test-requirements.txt
|
||||
setenv =
|
||||
{[testenv]setenv}
|
||||
PYTHON=coverage run
|
||||
commands =
|
||||
coverage erase
|
||||
stestr run --slowest {posargs}
|
||||
coverage combine
|
||||
coverage html -d cover
|
||||
coverage xml -o cover/coverage.xml
|
||||
coverage report
|
||||
|
||||
[coverage:run]
|
||||
branch = True
|
||||
concurrency = multiprocessing
|
||||
parallel = True
|
||||
source =
|
||||
.
|
||||
omit =
|
||||
.tox/*
|
||||
*/charmhelpers/*
|
||||
unit_tests/*
|
||||
|
||||
[testenv:venv]
|
||||
basepython = python3
|
||||
commands = {posargs}
|
||||
|
||||
[flake8]
|
||||
# E402 ignore necessary for path append before sys module import in actions
|
||||
ignore = E402,W503,W504
|
||||
24
unit_tests/test_magpie_tools.py
Normal file
24
unit_tests/test_magpie_tools.py
Normal file
@@ -0,0 +1,24 @@
|
||||
import lib.charms.layer.magpie_tools as magpie_tools
|
||||
import unit_tests.test_utils
|
||||
|
||||
|
||||
class TestMagpieTools(unit_tests.test_utils.CharmTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestMagpieTools, self).setUp()
|
||||
self.obj = self.tools = magpie_tools
|
||||
self.patches = [
|
||||
'hookenv']
|
||||
self.patch_all()
|
||||
|
||||
def test_safe_status(self):
|
||||
self.hookenv.config.return_value = {
|
||||
'supress_status': False}
|
||||
self.tools.safe_status('active', 'awesome')
|
||||
self.hookenv.status_set.assert_called_once_with(
|
||||
'active', 'awesome')
|
||||
self.hookenv.status_set.reset_mock()
|
||||
self.hookenv.config.return_value = {
|
||||
'supress_status': True}
|
||||
self.tools.safe_status('active', 'awesome')
|
||||
self.assertFalse(self.hookenv.status_set.called)
|
||||
55
unit_tests/test_utils.py
Normal file
55
unit_tests/test_utils.py
Normal file
@@ -0,0 +1,55 @@
|
||||
import mock
|
||||
import unittest
|
||||
import unittest.mock
|
||||
|
||||
|
||||
class CharmTestCase(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self._patches = {}
|
||||
self._patches_start = {}
|
||||
|
||||
def tearDown(self):
|
||||
for k, v in self._patches.items():
|
||||
v.stop()
|
||||
setattr(self, k, None)
|
||||
self._patches = None
|
||||
self._patches_start = None
|
||||
|
||||
def _patch(self, method):
|
||||
_m = unittest.mock.patch.object(self.obj, method)
|
||||
mock = _m.start()
|
||||
self.addCleanup(_m.stop)
|
||||
return mock
|
||||
|
||||
def patch_all(self):
|
||||
for method in self.patches:
|
||||
setattr(self, method, self._patch(method))
|
||||
|
||||
def patch_object(self, obj, attr, return_value=None, name=None, new=None,
|
||||
**kwargs):
|
||||
if name is None:
|
||||
name = attr
|
||||
if new is not None:
|
||||
mocked = mock.patch.object(obj, attr, new=new, **kwargs)
|
||||
else:
|
||||
mocked = mock.patch.object(obj, attr, **kwargs)
|
||||
self._patches[name] = mocked
|
||||
started = mocked.start()
|
||||
if new is None:
|
||||
started.return_value = return_value
|
||||
self._patches_start[name] = started
|
||||
setattr(self, name, started)
|
||||
|
||||
def patch(self, item, return_value=None, name=None, new=None, **kwargs):
|
||||
if name is None:
|
||||
raise RuntimeError("Must pass 'name' to .patch()")
|
||||
if new is not None:
|
||||
mocked = mock.patch(item, new=new, **kwargs)
|
||||
else:
|
||||
mocked = mock.patch(item, **kwargs)
|
||||
self._patches[name] = mocked
|
||||
started = mocked.start()
|
||||
if new is None:
|
||||
started.return_value = return_value
|
||||
self._patches_start[name] = started
|
||||
Reference in New Issue
Block a user