diff --git a/.travis.yml b/.travis.yml index ddde6f906..62333ead8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,7 +28,7 @@ env: - SCIPY=scipy SLYCOT= # default, w/out slycot - SCIPY="scipy==0.19.1" SLYCOT= # legacy support, w/out slycot -# Add optional builds that test against latest version of slycot +# Add optional builds that test against latest version of slycot, python jobs: include: - name: "linux, Python 2.7, slycot=source" @@ -43,8 +43,13 @@ jobs: services: xvfb python: "3.7" env: SCIPY=scipy SLYCOT=source + - name: "linux, Python 3.8, slycot=source" + os: linux + dist: xenial + services: xvfb + python: "3.8" + env: SCIPY=scipy SLYCOT=source -matrix: # Exclude combinations that are very unlikely (and don't work) exclude: - python: "3.7" # python3.7 should use latest scipy @@ -63,6 +68,12 @@ matrix: services: xvfb python: "3.7" env: SCIPY=scipy SLYCOT=source + - name: "linux, Python 3.8, slycot=source" + os: linux + dist: xenial + services: xvfb + python: "3.8" + env: SCIPY=scipy SLYCOT=source # install required system libraries before_install: @@ -97,6 +108,7 @@ before_install: fi # Make sure to look in the right place for python libraries (for slycot) - export LIBRARY_PATH="$HOME/miniconda/envs/test-environment/lib" + - conda install pytest # coveralls not in conda repos => install via pip instead - pip install coveralls @@ -118,7 +130,7 @@ install: # command to run tests script: - 'if [ $SLYCOT != "" ]; then python -c "import slycot"; fi' - - coverage run setup.py test + - coverage run -m pytest --disable-warnings control/tests # only run examples if Slycot is install # set PYTHONPATH for examples diff --git a/control/__init__.py b/control/__init__.py index 3dec2c12f..7daa39b3e 100644 --- a/control/__init__.py +++ b/control/__init__.py @@ -79,11 +79,5 @@ except ImportError: __version__ = "dev" -# The following is to use Numpy's testing framework -# Tests go under directory tests/, benchmarks under directory benchmarks/ -from numpy.testing import Tester -test = Tester().test -bench = Tester().bench - # Initialize default parameter values reset_defaults() diff --git a/control/tests/bdalg_test.py b/control/tests/bdalg_test.py index ae687df35..fde503052 100644 --- a/control/tests/bdalg_test.py +++ b/control/tests/bdalg_test.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# bdalg_test.py - test suit for block diagram algebra +# bdalg_test.py - test suite for block diagram algebra # RMM, 30 Mar 2011 (based on TestBDAlg from v0.4a) import unittest @@ -271,9 +271,5 @@ def test_feedback_args(self): self.assertTrue(isinstance(sys, ctrl.FRD)) -def suite(): - return unittest.TestLoader().loadTestsFromTestCase(TestFeedback) - - if __name__ == "__main__": unittest.main() diff --git a/control/tests/canonical_test.py b/control/tests/canonical_test.py index 8f0248dc7..3172f13b7 100644 --- a/control/tests/canonical_test.py +++ b/control/tests/canonical_test.py @@ -288,9 +288,6 @@ def test_similarity(self): np.testing.assert_array_almost_equal(mimo_new.C, mimo_ini.C) np.testing.assert_array_almost_equal(mimo_new.D, mimo_ini.D) -def suite(): - return unittest.TestLoader().loadTestsFromTestCase(TestFeedback) - if __name__ == "__main__": unittest.main() diff --git a/control/tests/config_test.py b/control/tests/config_test.py index c0fc9755b..1aeb9d290 100644 --- a/control/tests/config_test.py +++ b/control/tests/config_test.py @@ -218,9 +218,6 @@ def tearDown(self): # Reset the configuration defaults ct.config.reset_defaults() -def suite(): - return unittest.TestLoader().loadTestsFromTestCase(TestTimeresp) - if __name__ == '__main__': unittest.main() diff --git a/control/tests/convert_test.py b/control/tests/convert_test.py index 0340fa718..92254b49c 100644 --- a/control/tests/convert_test.py +++ b/control/tests/convert_test.py @@ -268,8 +268,6 @@ def test_tf2ss_robustness(self): np.testing.assert_array_almost_equal(np.sort(sys2tf.pole()), np.sort(sys2ss.pole())) -def suite(): - return unittest.TestLoader().loadTestsFromTestCase(TestConvert) if __name__ == "__main__": unittest.main() diff --git a/control/tests/ctrlutil_test.py b/control/tests/ctrlutil_test.py index 6e0d221f9..03a347154 100644 --- a/control/tests/ctrlutil_test.py +++ b/control/tests/ctrlutil_test.py @@ -58,8 +58,5 @@ def test_mag2db_array(self): np.testing.assert_array_almost_equal(db_array, self.db) -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(TestUtils) - if __name__ == "__main__": unittest.main() diff --git a/control/tests/discrete_test.py b/control/tests/discrete_test.py index f08a5fa5e..6598e3a81 100644 --- a/control/tests/discrete_test.py +++ b/control/tests/discrete_test.py @@ -5,7 +5,9 @@ import unittest import numpy as np -from control import * +from control import StateSpace, TransferFunction, feedback, step_response, \ + isdtime, timebase, isctime, sample_system, bode, impulse_response, \ + timebaseEqual, forced_response from control import matlab class TestDiscrete(unittest.TestCase): @@ -382,9 +384,6 @@ def test_discrete_bode(self): np.testing.assert_array_almost_equal(mag_out, np.absolute(H_z)) np.testing.assert_array_almost_equal(phase_out, np.angle(H_z)) -def suite(): - return unittest.TestLoader().loadTestsFromTestCase(TestDiscrete) - if __name__ == "__main__": unittest.main() diff --git a/control/tests/flatsys_test.py b/control/tests/flatsys_test.py index 040d7365a..0c1d0c92c 100644 --- a/control/tests/flatsys_test.py +++ b/control/tests/flatsys_test.py @@ -127,9 +127,5 @@ def tearDown(self): ct.reset_defaults() -def suite(): - return unittest.TestLoader().loadTestsFromTestCase(TestFlatSys) - - if __name__ == '__main__': unittest.main() diff --git a/control/tests/frd_test.py b/control/tests/frd_test.py index 1a6a263f3..629d488ea 100644 --- a/control/tests/frd_test.py +++ b/control/tests/frd_test.py @@ -415,8 +415,5 @@ def test_evalfr_deprecated(self): self.assertRaises(PendingDeprecationWarning, frd_tf.evalfr, 1.) -def suite(): - return unittest.TestLoader().loadTestsFromTestCase(TestFRD) - if __name__ == "__main__": unittest.main() diff --git a/control/tests/freqresp_test.py b/control/tests/freqresp_test.py index 9c1382d8a..7e803a9e6 100644 --- a/control/tests/freqresp_test.py +++ b/control/tests/freqresp_test.py @@ -235,8 +235,5 @@ def test_options(self): ctrl.config.reset_defaults() -def suite(): - return unittest.TestLoader().loadTestsFromTestCase(TestTimeresp) - if __name__ == '__main__': unittest.main() diff --git a/control/tests/iosys_test.py b/control/tests/iosys_test.py index 9fdac09cf..27651de71 100644 --- a/control/tests/iosys_test.py +++ b/control/tests/iosys_test.py @@ -911,10 +911,6 @@ def test_duplicates(self): self.assertEqual(len(warnval), 0) -def suite(): - return unittest.TestLoader().loadTestsFromTestCase(TestTimeresp) - - # Predator prey dynamics def predprey(t, x, u, params={}): r = params.get('r', 2) diff --git a/control/tests/lti_test.py b/control/tests/lti_test.py index 65023302a..ed832fb05 100644 --- a/control/tests/lti_test.py +++ b/control/tests/lti_test.py @@ -70,8 +70,6 @@ def test_dcgain(self): np.testing.assert_equal(sys.dcgain(), 42) np.testing.assert_equal(dcgain(sys), 42) -def suite(): - return unittest.TestLoader().loadTestsFromTestCase(TestUtils) if __name__ == "__main__": unittest.main() diff --git a/control/tests/margin_test.py b/control/tests/margin_test.py index 5162d30bb..85404b449 100644 --- a/control/tests/margin_test.py +++ b/control/tests/margin_test.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# margin_test.py - test suit for stability margin commands +# margin_test.py - test suite for stability margin commands # RMM, 15 Jul 2011 from __future__ import print_function @@ -310,8 +310,6 @@ def test_zmore_margin(self): assert_array_almost_equal( res, test['result'], test['digits']) -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(TestMargin) if __name__ == "__main__": unittest.main() diff --git a/control/tests/mateqn_test.py b/control/tests/mateqn_test.py index a5b609067..29f31c853 100644 --- a/control/tests/mateqn_test.py +++ b/control/tests/mateqn_test.py @@ -299,8 +299,5 @@ def test_raise(self): assert_raises(ControlArgument, cdare, A, B, Q, R, S) -def suite(): - return unittest.TestLoader().loadTestsFromTestCase(TestMatrixEquations) - if __name__ == "__main__": unittest.main() diff --git a/control/tests/matlab_test.py b/control/tests/matlab_test.py index 0e7060bea..ad4b5b120 100644 --- a/control/tests/matlab_test.py +++ b/control/tests/matlab_test.py @@ -688,8 +688,6 @@ def test_tf_string_args(self): # for i in range(len(tfdata)): # np.testing.assert_array_almost_equal(tfdata_1[i], tfdata_2[i]) -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(TestMatlab) if __name__ == '__main__': unittest.main() diff --git a/control/tests/minreal_test.py b/control/tests/minreal_test.py index 9c20ab5e0..595bb08b0 100644 --- a/control/tests/minreal_test.py +++ b/control/tests/minreal_test.py @@ -108,9 +108,6 @@ def testMinrealtf(self): np.testing.assert_array_almost_equal(hm.num[0][0], hr.num[0][0]) np.testing.assert_array_almost_equal(hm.den[0][0], hr.den[0][0]) -def suite(): - return unittest.TestLoader().loadTestsFromTestCase(TestMinreal) - if __name__ == "__main__": unittest.main() diff --git a/control/tests/modelsimp_array_test.py b/control/tests/modelsimp_array_test.py index f56f492a8..4a6f591e6 100644 --- a/control/tests/modelsimp_array_test.py +++ b/control/tests/modelsimp_array_test.py @@ -169,9 +169,6 @@ def tearDown(self): # Reset configuration variables to their original settings control.config.reset_defaults() -def suite(): - return unittest.TestLoader().loadTestsFromTestCase(TestModelsimp) - if __name__ == '__main__': unittest.main() diff --git a/control/tests/modelsimp_test.py b/control/tests/modelsimp_test.py index f79a86357..2368bd92f 100644 --- a/control/tests/modelsimp_test.py +++ b/control/tests/modelsimp_test.py @@ -130,9 +130,6 @@ def testBalredMatchDC(self): np.testing.assert_array_almost_equal(rsys.C, Crtrue,decimal=4) np.testing.assert_array_almost_equal(rsys.D, Drtrue,decimal=4) -def suite(): - return unittest.TestLoader().loadTestsFromTestCase(TestModelsimp) - if __name__ == '__main__': unittest.main() diff --git a/control/tests/nichols_test.py b/control/tests/nichols_test.py index 297c63f2d..9cf15ae44 100644 --- a/control/tests/nichols_test.py +++ b/control/tests/nichols_test.py @@ -29,9 +29,6 @@ def testNgrid(self): nichols(self.sys, grid=False) ngrid() -def suite(): - return unittest.TestLoader().loadTestsFromTestCase(TestStateSpace) - if __name__ == "__main__": unittest.main() diff --git a/control/tests/phaseplot_test.py b/control/tests/phaseplot_test.py index 4f93e6d97..a911c1ec1 100644 --- a/control/tests/phaseplot_test.py +++ b/control/tests/phaseplot_test.py @@ -77,8 +77,6 @@ def invpend_ode(self, x, t, m=1., l=1., b=0, g=9.8): def oscillator_ode(self, x, t, m=1., b=1, k=1, extra=None): return (x[1], -k/m*x[0] - b/m*x[1]) -def suite(): - return unittest.TestLoader().loadTestsFromTestCase(TestPhasePlot) if __name__ == '__main__': unittest.main() diff --git a/control/tests/rlocus_test.py b/control/tests/rlocus_test.py index 464f04066..1208609c7 100644 --- a/control/tests/rlocus_test.py +++ b/control/tests/rlocus_test.py @@ -68,8 +68,6 @@ def test_root_locus_zoom(self): assert_array_almost_equal(zoom_x,zoom_x_valid) assert_array_almost_equal(zoom_y,zoom_y_valid) -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(TestRootLocus) if __name__ == "__main__": unittest.main() diff --git a/control/tests/robust_array_test.py b/control/tests/robust_array_test.py index 62cf8c6c5..beb44d2de 100644 --- a/control/tests/robust_array_test.py +++ b/control/tests/robust_array_test.py @@ -388,5 +388,6 @@ def testSiso(self): def tearDown(self): control.config.reset_defaults() + if __name__ == "__main__": unittest.main() diff --git a/control/tests/run_all.py b/control/tests/run_all.py deleted file mode 100755 index b21248432..000000000 --- a/control/tests/run_all.py +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env python -# -# test_all.py - test suit for python-control -# RMM, 30 Mar 2011 - -from __future__ import print_function -import unittest # unit test module -import re # regular expressions -import os # operating system commands - -def test_all(verbosity=0): - """ Runs all tests written for python-control. - """ - try: # autodiscovery (python 2.7+) - start_dir = './' - pattern = '*_test.py' - top_level_dir = '../' - testModules = \ - unittest.defaultTestLoader.discover(start_dir, pattern=pattern, \ - top_level_dir=top_level_dir) - - for mod in test_mods: - print('Running tests in', mod) - tests = unittest.defaultTestLoader.loadTestFromModule(mod) - t = unittest.TextTestRunner() - t.run(tests) - print('Completed tests in', mod) - - except: - testModules = findTests('./tests/') - - # Now go through each module and run all of its tests. - for mod in testModules: - print('Running tests in', mod) - suiteList=[] # list of unittest.TestSuite objects - exec('import '+mod+' as currentModule') - - try: - currentSuite = currentModule.suite() - if isinstance(currentSuite, unittest.TestSuite): - suiteList.append(currentModule.suite()) - else: - print(mod + '.suite() doesn\'t return a TestSuite') - except: - print('The test module '+mod+' doesnt have ' + \ - 'a proper suite() function') - - t=unittest.TextTestRunner(verbosity=verbosity) - t.run(unittest.TestSuite(unittest.TestSuite(suiteList))) - print('Completed tests in', mod) - -def findTests(testdir = './', pattern = "[^.#]*_test.py$"): - """Since python <2.7 doesn't have test discovery, this finds tests in the - provided directory. The default is to check the current directory. Any files - that match test* or Test* are considered unittest modules and checked for - a module.suite() function (in tests()).""" - - # Get list of files in test directory - fileList = os.listdir(testdir) - - # Go through the files and look for anything that matches the pattern - testModules= [] - for fileName in fileList: - if (re.match(pattern, fileName)): - testModules.append(fileName[:-len('.py')]) - - # Return all of the modules that we find - return testModules - -if __name__=='__main__': - test_all() diff --git a/control/tests/sisotool_test.py b/control/tests/sisotool_test.py index 40ef0f966..f2cdf9106 100644 --- a/control/tests/sisotool_test.py +++ b/control/tests/sisotool_test.py @@ -62,8 +62,6 @@ def test_sisotool(self): step_response_moved = np.array([[ 0., 0.02458187, 0.16529784 , 0.46602716 , 0.91012035 , 1.43364313, 1.93996334 , 2.3190105 , 2.47041552 , 2.32724853] ]) assert_array_almost_equal(ax_step.lines[0].get_data()[1][:10],step_response_moved) -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(TestSisotool) if __name__ == "__main__": unittest.main() diff --git a/control/tests/slycot_convert_test.py b/control/tests/slycot_convert_test.py index eab178954..677cac4ba 100644 --- a/control/tests/slycot_convert_test.py +++ b/control/tests/slycot_convert_test.py @@ -192,10 +192,6 @@ def testFreqResp(self): decimal=2) -# These are here for once the above is made into a unittest. -def suite(): - return unittest.TestLoader().loadTestsFromTestCase(TestSlycot) - if __name__ == '__main__': unittest.main() diff --git a/control/tests/statefbk_array_test.py b/control/tests/statefbk_array_test.py index 941488978..10f450186 100644 --- a/control/tests/statefbk_array_test.py +++ b/control/tests/statefbk_array_test.py @@ -409,11 +409,5 @@ def tearDown(self): reset_defaults() -def test_suite(): - - status1 = unittest.TestLoader().loadTestsFromTestCase(TestStatefbk) - status2 = unittest.TestLoader().loadTestsFromTestCase(TestStatefbk) - return status1 and status2 - if __name__ == '__main__': unittest.main() diff --git a/control/tests/statefbk_test.py b/control/tests/statefbk_test.py index 133631232..fc0ffeffa 100644 --- a/control/tests/statefbk_test.py +++ b/control/tests/statefbk_test.py @@ -344,8 +344,5 @@ def test_dare(self): assert np.all(np.abs(L) > 1) -def test_suite(): - return unittest.TestLoader().loadTestsFromTestCase(TestStatefbk) - if __name__ == '__main__': unittest.main() diff --git a/control/tests/statesp_array_test.py b/control/tests/statesp_array_test.py index a45e008bc..a2d034075 100644 --- a/control/tests/statesp_array_test.py +++ b/control/tests/statesp_array_test.py @@ -629,9 +629,6 @@ def test_copy_constructor(self): def tearDown(self): reset_defaults() # reset configuration defaults -def suite(): - return unittest.TestLoader().loadTestsFromTestCase(TestStateSpace) - if __name__ == "__main__": unittest.main() diff --git a/control/tests/statesp_test.py b/control/tests/statesp_test.py index 191271da4..9273877af 100644 --- a/control/tests/statesp_test.py +++ b/control/tests/statesp_test.py @@ -612,9 +612,5 @@ def test_copy_constructor(self): np.testing.assert_array_equal(cpysys.A, [[-1]]) # original value -def suite(): - return unittest.TestLoader().loadTestsFromTestCase(TestStateSpace) - - if __name__ == "__main__": unittest.main() diff --git a/control/tests/timeresp_test.py b/control/tests/timeresp_test.py index 4087f530f..5c58f4d67 100644 --- a/control/tests/timeresp_test.py +++ b/control/tests/timeresp_test.py @@ -562,8 +562,5 @@ def test_time_series_data_convention(self): self.assertTrue(len(t) == len(y)) # Allows direct plotting of output -def suite(): - return unittest.TestLoader().loadTestsFromTestCase(TestTimeresp) - if __name__ == '__main__': unittest.main() diff --git a/control/tests/xferfcn_input_test.py b/control/tests/xferfcn_input_test.py index 0d6ca56fe..52fb85c29 100644 --- a/control/tests/xferfcn_input_test.py +++ b/control/tests/xferfcn_input_test.py @@ -255,9 +255,5 @@ def test_clean_part_list_list_arrays(self): np.testing.assert_array_equal(num_[1][1], array([4.0, 4.0], dtype=float)) -def suite(): - return unittest.TestLoader().loadTestsFromTestCase(TestXferFcnInput) - - if __name__ == "__main__": unittest.main() diff --git a/control/tests/xferfcn_test.py b/control/tests/xferfcn_test.py index 0a1778d1d..4a1f8e09d 100644 --- a/control/tests/xferfcn_test.py +++ b/control/tests/xferfcn_test.py @@ -827,9 +827,5 @@ def test_latex_repr(self): self.assertEqual(H._repr_latex_(), ref) -def suite(): - return unittest.TestLoader().loadTestsFromTestCase(TestXferFcn) - - if __name__ == "__main__": unittest.main()