Testing Class Material
This commit is contained in:
commit
05b1f6cdd5
85 changed files with 102796 additions and 0 deletions
BIN
extra_slides/code_organization_slides.pptx
Normal file
BIN
extra_slides/code_organization_slides.pptx
Normal file
Binary file not shown.
BIN
extra_slides/mocking.pptx
Normal file
BIN
extra_slides/mocking.pptx
Normal file
Binary file not shown.
30
extra_slides/mocking/code.py
Normal file
30
extra_slides/mocking/code.py
Normal file
|
@ -0,0 +1,30 @@
|
|||
|
||||
>>> from smtplib import SMTP
|
||||
>>> mock_smtp = Mock(spec=SMTP)
|
||||
|
||||
>>> isinstance(mock_smtp, SMTP)
|
||||
True
|
||||
|
||||
>>> mock_smtp.<TAB>
|
||||
mock_smtp.assert_any_call mock_smtp.attach_mock mock_smtp.call_args
|
||||
mock_smtp.assert_called_once_with mock_smtp.auth mock_smtp.call_args_list
|
||||
mock_smtp.assert_called_with mock_smtp.auth_cram_md5 mock_smtp.call_count >
|
||||
mock_smtp.assert_has_calls mock_smtp.auth_login mock_smtp.called
|
||||
mock_smtp.assert_not_called mock_smtp.auth_plain mock_smtp.close
|
||||
|
||||
>>> mock_smtp.bogus
|
||||
---------------------------------------------------------------------------
|
||||
AttributeError Traceback (most recent call last)
|
||||
<ipython-input-17-4856e93b6e10> in <module>()
|
||||
----> 1 mock_smtp.bogus
|
||||
|
||||
/Users/pberkes/miniconda3/envs/gnode/lib/python3.5/unittest/mock.py in __getattr__(self, name)
|
||||
576 elif self._mock_methods is not None:
|
||||
577 if name not in self._mock_methods or name in _all_magics:
|
||||
--> 578 raise AttributeError("Mock object has no attribute %r" % name)
|
||||
579 elif _is_magic(name):
|
||||
580 raise AttributeError(name)
|
||||
|
||||
AttributeError: Mock object has no attribute 'bogus'
|
||||
|
||||
|
64
extra_slides/mocking/demo_Mock.py
Normal file
64
extra_slides/mocking/demo_Mock.py
Normal file
|
@ -0,0 +1,64 @@
|
|||
##### Mock basic
|
||||
m = Mock()
|
||||
m.x = 3
|
||||
m.x
|
||||
m.f(1,2,3)
|
||||
m.whatever(3, key=2)
|
||||
m
|
||||
m.f
|
||||
m.g
|
||||
|
||||
##### special attributes and assert methods
|
||||
mock=Mock()
|
||||
mock.f(2,3)
|
||||
mock.f('a')
|
||||
|
||||
mock.f.called
|
||||
mock.add.called
|
||||
mock.f.called
|
||||
mock.f.call_args
|
||||
mock.f.call_count
|
||||
mock.f.call_args_list
|
||||
mock.f.assert_called_with('a')
|
||||
mock.f.assert_called_once_with('a')
|
||||
mock.f.assert_called_with(2, 3)
|
||||
mock.f.assert_any_call(2, 3)
|
||||
mock.f.assert_has_calls(['a', (2,3)])
|
||||
|
||||
#### return_value and side_effect
|
||||
mock.g.return_value = 7
|
||||
mock.g(32)
|
||||
mock.g('r')
|
||||
|
||||
# useful to simulate file errors or server errors
|
||||
mock.g.side_effect = Exception('Noooo')
|
||||
mock.g(2)
|
||||
mock.g.side_effect = lambda x: x.append(2)
|
||||
a=[1]
|
||||
mock.g(a)
|
||||
a
|
||||
|
||||
mock.g.side_effect = [1, 4, 5]
|
||||
mock.g()
|
||||
mock.g()
|
||||
mock.g()
|
||||
mock.g()
|
||||
|
||||
#####
|
||||
mock = Mock()
|
||||
mock.f(3,4)
|
||||
mock.g('a')
|
||||
mock.f.a()
|
||||
mock.method_calls
|
||||
|
||||
result = m.h(32)
|
||||
result(1)
|
||||
m.mock_calls
|
||||
|
||||
##### spec
|
||||
from chaco.api import Plot
|
||||
m2 = Mock(spec=Plot)
|
||||
isinstance(m2, Plot)
|
||||
m2.add
|
||||
m2.add(12,'asdfasd')
|
||||
m2.aaa
|
41
extra_slides/mocking/report.py
Normal file
41
extra_slides/mocking/report.py
Normal file
|
@ -0,0 +1,41 @@
|
|||
report_template = """
|
||||
Report
|
||||
======
|
||||
|
||||
The experiment was a {judgment}!
|
||||
Let's do this again, with a bigger budget.
|
||||
"""
|
||||
|
||||
|
||||
def send_report(result, smtp):
|
||||
if result > 0.5:
|
||||
judgment = 'big success'
|
||||
else:
|
||||
judgment = 'total failure'
|
||||
report = report_template.format(judgment=judgment)
|
||||
smtp.send_message(
|
||||
report,
|
||||
from_addr='pony@magicpony.com',
|
||||
to_addrs=['ferenc@magicpony.com'],
|
||||
)
|
||||
|
||||
|
||||
from unittest.mock import Mock
|
||||
|
||||
def test_send_report_success():
|
||||
smtp = Mock()
|
||||
|
||||
send_report(0.6, smtp)
|
||||
assert smtp.send_message.call_count == 1
|
||||
pos_args, kw_args = smtp.send_message.call_args
|
||||
message = pos_args[0]
|
||||
assert 'success' in message
|
||||
|
||||
smtp.reset_mock()
|
||||
|
||||
send_report(0.4, smtp)
|
||||
assert smtp.send_message.call_count == 1
|
||||
args, kwargs = smtp.send_message.call_args
|
||||
message = args[0]
|
||||
assert 'failure' in message
|
||||
|
13
extra_slides/mocking/telescope/telescope_driver.py
Normal file
13
extra_slides/mocking/telescope/telescope_driver.py
Normal file
|
@ -0,0 +1,13 @@
|
|||
|
||||
def connect(address):
|
||||
import time
|
||||
time.sleep(5)
|
||||
return '1'
|
||||
|
||||
def get_angle(address):
|
||||
return 0.0
|
||||
|
||||
def set_angle(address, angle):
|
||||
if angle < 0 or angle > 1.40:
|
||||
raise IOError('Telescope jammed -- please call technical support')
|
||||
return True
|
30
extra_slides/mocking/telescope/telescope_model.py
Normal file
30
extra_slides/mocking/telescope/telescope_model.py
Normal file
|
@ -0,0 +1,30 @@
|
|||
import telescope_driver
|
||||
|
||||
|
||||
class TelescopeModel(object):
|
||||
|
||||
# Minimum safe elevation angle (see handbook).
|
||||
MIN_ANGLE = 0.0
|
||||
|
||||
# Maximum safe elevation angle (see handbook).
|
||||
MAX_ANGLE = 80.0
|
||||
|
||||
def __init__(self, address):
|
||||
self.address = address
|
||||
# Connect to telescope
|
||||
self.connection = telescope_driver.connect(address)
|
||||
# Get initial state of telescope.
|
||||
self.current_angle = telescope_driver.get_angle(self.connection)
|
||||
|
||||
def set_elevation_angle(self, angle):
|
||||
""" Set the elevation angle of the telescope (in rad).
|
||||
|
||||
If the angle is outside the range allowed by the manufacturer,
|
||||
raise a ValueError.
|
||||
"""
|
||||
|
||||
if angle < self.MIN_ANGLE or angle > self.MAX_ANGLE:
|
||||
raise ValueError('Unsafe elevation angle: {}'.format(angle))
|
||||
|
||||
telescope_driver.set_angle(self.connection, angle)
|
||||
self.current_angle = angle
|
12
extra_slides/mocking/telescope/test_telescope_model.py
Normal file
12
extra_slides/mocking/telescope/test_telescope_model.py
Normal file
|
@ -0,0 +1,12 @@
|
|||
import numpy as np
|
||||
from py.test import raises
|
||||
|
||||
from telescope_model import TelescopeModel
|
||||
|
||||
|
||||
def test_unsafe_elevation_angle():
|
||||
telescope = TelescopeModel(address='10.2.1.1')
|
||||
elevation_angle = np.pi / 2.0
|
||||
|
||||
with raises(ValueError):
|
||||
telescope.set_elevation_angle(elevation_angle)
|
|
@ -0,0 +1,29 @@
|
|||
from unittest import mock
|
||||
|
||||
import numpy as np
|
||||
from py.test import raises
|
||||
|
||||
from telescope_model import TelescopeModel
|
||||
|
||||
|
||||
def test_unsafe_elevation_angle():
|
||||
with mock.patch('telescope_model.telescope_driver'):
|
||||
telescope = TelescopeModel(address='10.2.1.1')
|
||||
elevation_angle = np.pi / 2.0
|
||||
with raises(ValueError):
|
||||
telescope.set_elevation_angle(elevation_angle)
|
||||
|
||||
|
||||
def test_model_initialization():
|
||||
connection_id = 'bogus_connection'
|
||||
initial_angle = 1.23
|
||||
|
||||
with mock.patch('telescope_model.telescope_driver') as driver:
|
||||
driver.connect.return_value = connection_id
|
||||
driver.get_angle.return_value = initial_angle
|
||||
|
||||
telescope = TelescopeModel(address='10.2.1.1')
|
||||
assert telescope.connection == connection_id
|
||||
assert driver.connect.call_count == 1
|
||||
assert telescope.current_angle == initial_angle
|
||||
|
BIN
extra_slides/packaging.pptx
Normal file
BIN
extra_slides/packaging.pptx
Normal file
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 415 KiB |
22
extra_slides/packaging/noiser_project_final/noiser/main.py
Normal file
22
extra_slides/packaging/noiser_project_final/noiser/main.py
Normal file
|
@ -0,0 +1,22 @@
|
|||
import os.path
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
from scipy.ndimage import imread
|
||||
from pkg_resources import resource_filename
|
||||
|
||||
from noiser.noise import white_noise
|
||||
from noiser.utils import copy_image
|
||||
|
||||
|
||||
def main():
|
||||
path = resource_filename('noiser', os.path.join('images', 'baboon_kandinsky.png'))
|
||||
print(path)
|
||||
img = imread(path)
|
||||
noisy = copy_image(white_noise(img, 20))
|
||||
plt.imshow(noisy)
|
||||
plt.draw()
|
||||
plt.show()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -0,0 +1,7 @@
|
|||
import numpy as np
|
||||
|
||||
|
||||
def white_noise(image, std):
|
||||
noise = np.random.normal(scale=std, size=image.shape).astype(image.dtype)
|
||||
noisy = image + noise
|
||||
return noisy
|
|
@ -0,0 +1,8 @@
|
|||
from setuptools import setup, find_packages
|
||||
|
||||
setup(
|
||||
name='Noiser',
|
||||
version='1.0',
|
||||
packages=find_packages(),
|
||||
)
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
import numpy as np
|
||||
from numpy.testing import assert_allclose
|
||||
|
||||
from noiser.noise import white_noise
|
||||
|
||||
|
||||
def test_white_noise():
|
||||
n_images, height, width = 201, 101, 102
|
||||
dtype = np.float32
|
||||
|
||||
# Create ``n_images`` identical image.
|
||||
base_image = np.random.rand(1, height, width, 3).astype(dtype) - 0.5
|
||||
images = np.repeat(base_image, n_images, axis=0)
|
||||
|
||||
std = 0.13
|
||||
noisy = white_noise(images, std=std)
|
||||
|
||||
# dtype and shape are preserved.
|
||||
assert noisy.dtype == dtype
|
||||
assert noisy.shape == images.shape
|
||||
|
||||
# Mean and std of noisy image match expectations.
|
||||
assert_allclose(images.mean(0), base_image[0], atol=1e-4)
|
||||
assert np.isclose((noisy - images).std(), std, atol=1e-4)
|
|
@ -0,0 +1,14 @@
|
|||
import numpy as np
|
||||
from numpy.testing import assert_array_equal
|
||||
|
||||
from noiser.utils import copy_image
|
||||
|
||||
|
||||
def test_copy_image():
|
||||
height, width = 101, 102
|
||||
dtype = np.float32
|
||||
|
||||
image = np.random.rand(height, width, 3).astype(dtype)
|
||||
copy = copy_image(image)
|
||||
assert_array_equal(copy, image)
|
||||
|
15
extra_slides/packaging/noiser_project_final/noiser/utils.pyx
Normal file
15
extra_slides/packaging/noiser_project_final/noiser/utils.pyx
Normal file
|
@ -0,0 +1,15 @@
|
|||
import numpy as np
|
||||
cimport numpy as np
|
||||
|
||||
|
||||
def copy_image(np.ndarray img):
|
||||
cdef int h = img.shape[0]
|
||||
cdef int w = img.shape[1]
|
||||
cdef int c = img.shape[2]
|
||||
cdef np.ndarray copy = np.empty([h, w, c], dtype=img.dtype)
|
||||
|
||||
for i in range(h):
|
||||
for j in range(w):
|
||||
for k in range(c):
|
||||
copy[i, j, k] = img[i, j, k]
|
||||
return copy
|
29
extra_slides/packaging/noiser_project_final/setup.py
Normal file
29
extra_slides/packaging/noiser_project_final/setup.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
from Cython.Build import cythonize
|
||||
import numpy
|
||||
from setuptools import setup, find_packages
|
||||
from setuptools.extension import Extension
|
||||
|
||||
|
||||
extensions = [
|
||||
Extension(
|
||||
'noiser.utils',
|
||||
["noiser/utils.pyx"],
|
||||
include_dirs=[numpy.get_include()],
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
setup(
|
||||
name='Noiser',
|
||||
version='1.0',
|
||||
packages=find_packages(),
|
||||
ext_modules=cythonize(extensions),
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
'baboon=noiser.main:main',
|
||||
]
|
||||
},
|
||||
package_data={
|
||||
'noiser': ['images/*.png'],
|
||||
}
|
||||
)
|
31
extra_slides/profiling/residuals.py
Normal file
31
extra_slides/profiling/residuals.py
Normal file
|
@ -0,0 +1,31 @@
|
|||
import numpy as np
|
||||
import theano
|
||||
from theano import tensor as T
|
||||
|
||||
|
||||
SLOPE = 3.1
|
||||
INTERCEPT = -1.2
|
||||
|
||||
|
||||
def residual_stats_theano(x, y):
|
||||
expected = SLOPE * x + INTERCEPT
|
||||
residuals = y - expected
|
||||
return residuals.mean(), residuals.std()
|
||||
|
||||
|
||||
x_var = T.vector()
|
||||
y_var = T.vector()
|
||||
|
||||
residual_stats = theano.function(
|
||||
inputs=[x_var, y_var],
|
||||
outputs=residual_stats_theano(x_var, y_var),
|
||||
allow_input_downcast=True,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
x = np.linspace(-10, 10, 1000)
|
||||
y = SLOPE * x + INTERCEPT
|
||||
y += np.random.normal(loc=0.1, scale=0.5, size=x.shape)
|
||||
mn, std = residual_stats(x, y)
|
||||
print('Residual mean=', mn, ', std=', std)
|
35
extra_slides/profiling/residuals_profile.py
Normal file
35
extra_slides/profiling/residuals_profile.py
Normal file
|
@ -0,0 +1,35 @@
|
|||
import numpy as np
|
||||
import theano
|
||||
from theano import tensor as T
|
||||
|
||||
theano.config.profile_memory = True
|
||||
theano.config.profile = True
|
||||
|
||||
|
||||
SLOPE = 3.1
|
||||
INTERCEPT = -1.2
|
||||
|
||||
|
||||
def residual_stats_theano(x, y):
|
||||
expected = SLOPE * x + INTERCEPT
|
||||
residuals = y - expected
|
||||
return residuals.mean(), residuals.std()
|
||||
|
||||
|
||||
x_var = T.vector()
|
||||
y_var = T.vector()
|
||||
|
||||
residual_stats = theano.function(
|
||||
inputs=[x_var, y_var],
|
||||
outputs=residual_stats_theano(x_var, y_var),
|
||||
allow_input_downcast=True,
|
||||
profile=True,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
x = np.linspace(-10, 10, 1000)
|
||||
y = SLOPE * x + INTERCEPT
|
||||
y += np.random.normal(loc=0.1, scale=0.5, size=x.shape)
|
||||
mn, std = residual_stats(x, y)
|
||||
print('Residual mean=', mn, ', std=', std)
|
Loading…
Add table
Add a link
Reference in a new issue