Skip to content

Commit e69fc1a

Browse files
committed
Move _tf_close_coeff back to testing realm and make better use of assertion messages
1 parent 92bd703 commit e69fc1a

5 files changed

Lines changed: 93 additions & 103 deletions

File tree

control/tests/bdalg_test.py

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,21 @@
1-
"""bdalg_test.py - test suite for block diagram algebra
1+
"""bdalg_test.py - test suite for block diagram algebra.
22
33
RMM, 30 Mar 2011 (based on TestBDAlg from v0.4a)
44
"""
55

6+
import control as ctrl
67
import numpy as np
7-
from numpy import sort
88
import pytest
9-
10-
import control as ctrl
11-
from control.xferfcn import TransferFunction, _tf_close_coeff
9+
from control.bdalg import _ensure_tf, append, connect, feedback
10+
from control.lti import poles, zeros
1211
from control.statesp import StateSpace
13-
from control.bdalg import feedback, append, connect
14-
from control.lti import zeros, poles
15-
from control.bdalg import _ensure_tf
12+
from control.tests.conftest import assert_tf_close_coeff
13+
from control.xferfcn import TransferFunction
14+
from numpy import sort
1615

1716

1817
class TestFeedback:
19-
"""These are tests for the feedback function in bdalg.py. Currently, some
20-
of the tests are not implemented, or are not working properly. TODO: these
21-
need to be fixed."""
18+
"""Tests for the feedback function in bdalg.py."""
2219

2320
@pytest.fixture
2421
def tsys(self):
@@ -180,7 +177,7 @@ def testTFTF(self, tsys):
180177
[[[1., 4., 9., 8., 5.]]])
181178

182179
def testLists(self, tsys):
183-
"""Make sure that lists of various lengths work for operations"""
180+
"""Make sure that lists of various lengths work for operations."""
184181
sys1 = ctrl.tf([1, 1], [1, 2])
185182
sys2 = ctrl.tf([1, 3], [1, 4])
186183
sys3 = ctrl.tf([1, 5], [1, 6])
@@ -237,7 +234,7 @@ def testLists(self, tsys):
237234
sort(zeros(sys1 + sys2 + sys3 + sys4 + sys5)))
238235

239236
def testMimoSeries(self, tsys):
240-
"""regression: bdalg.series reverses order of arguments"""
237+
"""regression: bdalg.series reverses order of arguments."""
241238
g1 = ctrl.ss([], [], [], [[1, 2], [0, 3]])
242239
g2 = ctrl.ss([], [], [], [[1, 0], [2, 3]])
243240
ref = g2 * g1
@@ -430,9 +427,9 @@ class TestEnsureTf:
430427
],
431428
)
432429
def test_ensure(self, arraylike_or_tf, dt, tf):
433-
"""Test nominal cases"""
430+
"""Test nominal cases."""
434431
ensured_tf = _ensure_tf(arraylike_or_tf, dt)
435-
assert _tf_close_coeff(tf, ensured_tf)
432+
assert_tf_close_coeff(tf, ensured_tf)
436433

437434
@pytest.mark.parametrize(
438435
"arraylike_or_tf, dt, exception",
@@ -460,7 +457,7 @@ def test_ensure(self, arraylike_or_tf, dt, tf):
460457
],
461458
)
462459
def test_error_ensure(self, arraylike_or_tf, dt, exception):
463-
"""Test error cases"""
460+
"""Test error cases."""
464461
with pytest.raises(exception):
465462
_ensure_tf(arraylike_or_tf, dt)
466463

@@ -624,7 +621,7 @@ class TestTfCombineSplit:
624621
def test_combine_tf(self, tf_array, tf):
625622
"""Test combining transfer functions."""
626623
tf_combined = ctrl.combine_tf(tf_array)
627-
assert _tf_close_coeff(tf_combined, tf)
624+
assert_tf_close_coeff(tf_combined, tf)
628625

629626
@pytest.mark.parametrize(
630627
"tf_array, tf",
@@ -712,12 +709,12 @@ def test_split_tf(self, tf_array, tf):
712709
# Test entry-by-entry
713710
for i in range(tf_split.shape[0]):
714711
for j in range(tf_split.shape[1]):
715-
assert _tf_close_coeff(
712+
assert_tf_close_coeff(
716713
tf_split[i, j],
717714
tf_array[i, j],
718715
)
719716
# Test combined
720-
assert _tf_close_coeff(
717+
assert_tf_close_coeff(
721718
ctrl.combine_tf(tf_split),
722719
ctrl.combine_tf(tf_array),
723720
)

control/tests/conftest.py

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""conftest.py - pytest local plugins and fixtures"""
1+
"""conftest.py - pytest local plugins, fixtures, marks and functions."""
22

33
import os
44
from contextlib import contextmanager
@@ -9,6 +9,7 @@
99

1010
import control
1111

12+
1213
# some common pytest marks. These can be used as test decorators or in
1314
# pytest.param(marks=)
1415
slycotonly = pytest.mark.skipif(
@@ -61,7 +62,7 @@ def mplcleanup():
6162

6263
@pytest.fixture(scope="function")
6364
def legacy_plot_signature():
64-
"""Turn off warnings for calls to plotting functions with old signatures"""
65+
"""Turn off warnings for calls to plotting functions with old signatures."""
6566
import warnings
6667
warnings.filterwarnings(
6768
'ignore', message='passing systems .* is deprecated',
@@ -75,14 +76,53 @@ def legacy_plot_signature():
7576

7677
@pytest.fixture(scope="function")
7778
def ignore_future_warning():
78-
"""Turn off warnings for functions that generate FutureWarning"""
79+
"""Turn off warnings for functions that generate FutureWarning."""
7980
import warnings
8081
warnings.filterwarnings(
8182
'ignore', message='.*deprecated', category=FutureWarning)
8283
yield
8384
warnings.resetwarnings()
84-
8585

86-
# Allow pytest.mark.slow to mark slow tests (skip with pytest -m "not slow")
86+
8787
def pytest_configure(config):
88+
"""Allow pytest.mark.slow to mark slow tests.
89+
90+
skip with pytest -m "not slow"
91+
"""
8892
config.addinivalue_line("markers", "slow: mark test as slow to run")
93+
94+
95+
def assert_tf_close_coeff(tf_a, tf_b, rtol=1e-5, atol=1e-8):
96+
"""Check if two transfer functions have close coefficients.
97+
98+
Parameters
99+
----------
100+
tf_a : TransferFunction
101+
First transfer function.
102+
tf_b : TransferFunction
103+
Second transfer function.
104+
rtol : float
105+
Relative tolerance for ``np.testing.assert_allclose``.
106+
atol : float
107+
Absolute tolerance for ``np.testing.assert_allclose``.
108+
109+
Raises
110+
------
111+
AssertionError
112+
"""
113+
# Check number of outputs and inputs
114+
assert tf_a.noutputs == tf_b.noutputs
115+
assert tf_a.ninputs == tf_b.ninputs
116+
# Check timestep
117+
assert tf_a.dt == tf_b.dt
118+
# Check coefficient arrays
119+
for i in range(tf_a.noutputs):
120+
for j in range(tf_a.ninputs):
121+
np.testing.assert_allclose(
122+
tf_a.num[i][j],
123+
tf_b.num[i][j],
124+
rtol=rtol, atol=atol)
125+
np.testing.assert_allclose(
126+
tf_a.den[i][j],
127+
tf_b.den[i][j],
128+
rtol=rtol, atol=atol)

control/tests/statesp_test.py

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,22 @@
77
convert to pytest
88
"""
99

10-
import numpy as np
11-
from numpy.testing import assert_array_almost_equal
12-
import pytest
1310
import operator
14-
from numpy.linalg import solve
15-
from scipy.linalg import block_diag, eigvals
1611

1712
import control as ct
13+
import numpy as np
14+
import pytest
1815
from control.config import defaults
1916
from control.dtime import sample_system
2017
from control.lti import evalfr
21-
from control.statesp import StateSpace, _convert_to_statespace, tf2ss, \
22-
_statesp_defaults, _rss_generate, linfnorm, ss, rss, drss
23-
from control.xferfcn import TransferFunction, ss2tf, _tf_close_coeff
18+
from control.statesp import (StateSpace, _convert_to_statespace, _rss_generate,
19+
_statesp_defaults, drss, linfnorm, rss, ss, tf2ss)
20+
from control.xferfcn import TransferFunction, ss2tf
21+
from numpy.linalg import solve
22+
from numpy.testing import assert_array_almost_equal
23+
from scipy.linalg import block_diag, eigvals
2424

25-
from .conftest import editsdefaults, slycotonly
25+
from .conftest import assert_tf_close_coeff, editsdefaults, slycotonly
2626

2727

2828
class TestStateSpace:
@@ -384,7 +384,7 @@ def test_add_sub_mimo_siso(self):
384384
(StateSpace.__rsub__, -expected_sub),
385385
]:
386386
result = op(ss_mimo, ss_siso)
387-
assert _tf_close_coeff(
387+
assert_tf_close_coeff(
388388
expected.minreal(),
389389
ss2tf(result).minreal(),
390390
)
@@ -404,7 +404,7 @@ def test_add_sub_mimo_siso(self):
404404
(StateSpace.__rsub__, -expected_sub),
405405
]:
406406
result = op(ss_siso, np.eye(2))
407-
assert _tf_close_coeff(
407+
assert_tf_close_coeff(
408408
expected.minreal(),
409409
ss2tf(result).minreal(),
410410
)
@@ -479,7 +479,7 @@ def test_add_sub_mimo_siso(self):
479479
)
480480
def test_mul_mimo_siso(self, left, right, expected):
481481
result = tf2ss(left).__mul__(right)
482-
assert _tf_close_coeff(
482+
assert_tf_close_coeff(
483483
expected.minreal(),
484484
ss2tf(result).minreal(),
485485
)
@@ -554,7 +554,7 @@ def test_mul_mimo_siso(self, left, right, expected):
554554
)
555555
def test_rmul_mimo_siso(self, left, right, expected):
556556
result = tf2ss(right).__rmul__(left)
557-
assert _tf_close_coeff(
557+
assert_tf_close_coeff(
558558
expected.minreal(),
559559
ss2tf(result).minreal(),
560560
)
@@ -584,13 +584,13 @@ def test_pow(self, sys222, sys322):
584584
# matrices instead.
585585
result = (sys * sys**-1).minreal()
586586
expected = StateSpace([], [], [], np.eye(2), dt=0)
587-
assert _tf_close_coeff(
587+
assert_tf_close_coeff(
588588
ss2tf(expected).minreal(),
589589
ss2tf(result).minreal(),
590590
)
591591
result = (sys**-1 * sys).minreal()
592592
expected = StateSpace([], [], [], np.eye(2), dt=0)
593-
assert _tf_close_coeff(
593+
assert_tf_close_coeff(
594594
ss2tf(expected).minreal(),
595595
ss2tf(result).minreal(),
596596
)
@@ -616,14 +616,14 @@ def test_truediv(self, sys222, sys322):
616616
# Divide by self
617617
result = (sys.__truediv__(sys)).minreal()
618618
expected = StateSpace([], [], [], np.eye(2), dt=0)
619-
assert _tf_close_coeff(
619+
assert_tf_close_coeff(
620620
ss2tf(expected).minreal(),
621621
ss2tf(result).minreal(),
622622
)
623623
# Divide by TF
624624
result = sys.__truediv__(TransferFunction.s)
625625
expected = ss2tf(sys) / TransferFunction.s
626-
assert _tf_close_coeff(
626+
assert_tf_close_coeff(
627627
expected.minreal(),
628628
ss2tf(result).minreal(),
629629
)
@@ -634,22 +634,22 @@ def test_rtruediv(self, sys222, sys322):
634634
for sys in [sys222, sys322]:
635635
result = (sys.__rtruediv__(sys)).minreal()
636636
expected = StateSpace([], [], [], np.eye(2), dt=0)
637-
assert _tf_close_coeff(
637+
assert_tf_close_coeff(
638638
ss2tf(expected).minreal(),
639639
ss2tf(result).minreal(),
640640
)
641641
# Divide TF by SS
642642
result = sys.__rtruediv__(TransferFunction.s)
643643
expected = TransferFunction.s / sys
644-
assert _tf_close_coeff(
644+
assert_tf_close_coeff(
645645
expected.minreal(),
646646
result.minreal(),
647647
)
648648
# Divide array by SS
649649
sys = tf2ss(TransferFunction([1, 2], [2, 1]))
650650
result = sys.__rtruediv__(np.eye(2))
651651
expected = TransferFunction([2, 1], [1, 2]) * np.eye(2)
652-
assert _tf_close_coeff(
652+
assert_tf_close_coeff(
653653
expected.minreal(),
654654
ss2tf(result).minreal(),
655655
)

control/tests/xferfcn_test.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@
1010
import pytest
1111

1212
import control as ct
13-
from control import StateSpace, TransferFunction, defaults, evalfr, isctime, \
14-
isdtime, reset_defaults, rss, sample_system, set_defaults, ss, ss2tf, tf, \
15-
tf2ss, zpk
13+
from control import (StateSpace, TransferFunction, defaults, evalfr, isctime,
14+
isdtime, reset_defaults, rss, sample_system, set_defaults,
15+
ss, ss2tf, tf, tf2ss, zpk)
1616
from control.statesp import _convert_to_statespace
17-
from control.tests.conftest import slycotonly
18-
from control.xferfcn import _convert_to_transfer_function, _tf_close_coeff
17+
from control.tests.conftest import assert_tf_close_coeff, slycotonly
18+
from control.xferfcn import _convert_to_transfer_function
1919

2020

2121
class TestXferFcn:
@@ -424,7 +424,7 @@ def test_add_sub_mimo_siso(self):
424424
[op(tf_arr[1, 0], tf_siso), op(tf_arr[1, 1], tf_siso)],
425425
])
426426
result = op(tf_mimo, tf_siso)
427-
assert _tf_close_coeff(expected.minreal(), result.minreal())
427+
assert_tf_close_coeff(expected.minreal(), result.minreal())
428428

429429
@pytest.mark.parametrize(
430430
"left, right, expected",
@@ -496,7 +496,7 @@ def test_add_sub_mimo_siso(self):
496496
def test_mul_mimo_siso(self, left, right, expected):
497497
"""Test multiplication of a MIMO and a SISO system."""
498498
result = left.__mul__(right)
499-
assert _tf_close_coeff(expected.minreal(), result.minreal())
499+
assert_tf_close_coeff(expected.minreal(), result.minreal())
500500

501501
@pytest.mark.parametrize(
502502
"left, right, expected",
@@ -568,7 +568,7 @@ def test_mul_mimo_siso(self, left, right, expected):
568568
def test_rmul_mimo_siso(self, left, right, expected):
569569
"""Test right multiplication of a MIMO and a SISO system."""
570570
result = right.__rmul__(left)
571-
assert _tf_close_coeff(expected.minreal(), result.minreal())
571+
assert_tf_close_coeff(expected.minreal(), result.minreal())
572572

573573
@pytest.mark.parametrize(
574574
"left, right, expected",
@@ -605,7 +605,7 @@ def test_rmul_mimo_siso(self, left, right, expected):
605605
def test_truediv_mimo_siso(self, left, right, expected):
606606
"""Test true division of a MIMO and a SISO system."""
607607
result = left.__truediv__(right)
608-
assert _tf_close_coeff(expected.minreal(), result.minreal())
608+
assert_tf_close_coeff(expected.minreal(), result.minreal())
609609

610610
@pytest.mark.parametrize(
611611
"left, right, expected",
@@ -631,7 +631,7 @@ def test_truediv_mimo_siso(self, left, right, expected):
631631
def test_rtruediv_mimo_siso(self, left, right, expected):
632632
"""Test right true division of a MIMO and a SISO system."""
633633
result = right.__rtruediv__(left)
634-
assert _tf_close_coeff(expected.minreal(), result.minreal())
634+
assert_tf_close_coeff(expected.minreal(), result.minreal())
635635

636636
@pytest.mark.parametrize("named", [False, True])
637637
def test_slice(self, named):
@@ -925,9 +925,9 @@ def test_append(self):
925925
],
926926
)
927927
tf_appended_1 = tf1.append(tf2)
928-
assert _tf_close_coeff(tf_exp_1, tf_appended_1)
928+
assert_tf_close_coeff(tf_exp_1, tf_appended_1)
929929
tf_appended_2 = tf1.append(tf2).append(tf3)
930-
assert _tf_close_coeff(tf_exp_2, tf_appended_2)
930+
assert_tf_close_coeff(tf_exp_2, tf_appended_2)
931931

932932
def test_convert_to_transfer_function(self):
933933
"""Test for correct state space to transfer function conversion."""

0 commit comments

Comments
 (0)