diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 103b9a1d891596561e3bd96cdcb4da7243655913..76884141dfef4c844b083f2958b0ec8027cbbe04 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,14 +1,29 @@ -tests: - stage: test - script: python setup.py test +image: continuumio/miniconda3 + +build-job: + stage: build + script: + - conda install conda-build + - conda build -c conda-forge FieldTools + - conda build purge + - conda build -c local -c conda-forge ./ + - conda build purge + # To some reason setting CONDA_BLD_PATH=conda_bld does not work + - mv /opt/conda/conda-bld ./conda_bld + artifacts: + paths: + - conda_bld -pages: - stage: deploy +test-job: + stage: test script: - - python3 setup.py build_sphinx - - mv build/sphinx/html public + - export CONDA_BLD_PATH=conda_bld # Received packages as artifacts + - conda install -c local -c conda-forge pymagglobal packaging + - python tests/run_tests.py artifacts: paths: - - public - only: - - master + - ./docs/pic_mst.png + - ./docs/pic_map.png + - ./docs/pic_dip.png + - ./docs/pic_cfe.png + - ./docs/pic_cfs.png diff --git a/pymagglobal/_commands.py b/pymagglobal/_commands.py index bebd8c950b3d1ec6853ac7ad76cfcd4f303567ea..3b179ab5a721808a5ae74530e18bd1f8b84944ee 100644 --- a/pymagglobal/_commands.py +++ b/pymagglobal/_commands.py @@ -458,9 +458,9 @@ def maps(args): f'{args.epoch} {args.t_unit}') for it in range(3): - axs[it].tripcolor(plt_lat, plt_lon, field[it], - vmin=vmins[it], vmax=vmaxs[it], - rasterized=True, cmap=cmaps[it]) + mappable = axs[it].tripcolor(plt_lat, plt_lon, field[it], + vmin=vmins[it], vmax=vmaxs[it], + rasterized=True, cmap=cmaps[it]) axs[it].coastlines(alpha=0.8, lw=0.5) axs[it].set_global() axs[it].set_title(f'{utils._names[args.type][it]} [{units[it]}]') @@ -470,9 +470,7 @@ def maps(args): bnds[1]-0.1-cbar_hght, bnds[2], cbar_hght]) - norm = colors.Normalize(vmin=vmins[it], - vmax=vmaxs[it]) - fig.colorbar(cm.ScalarMappable(norm=norm, cmap=cmaps[it]), + fig.colorbar(mappable, cax=colax, orientation='horizontal') return fig diff --git a/pymagglobal/tests/__init__.py b/pymagglobal/tests/__init__.py deleted file mode 100644 index 7678ea4795c6e5de75d07823e5d9e2e4981c552d..0000000000000000000000000000000000000000 --- a/pymagglobal/tests/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -import unittest -import doctest -from pathlib import PurePath - -from pymagglobal import utils -from pymagglobal import _commands - -def test_suite(): - '''To be called in setup.py - XXX Deprecated since 3.8''' - tests_path = PurePath(__file__).parent - # By convention discovers TestCases in test_*.py - suite = unittest.TestLoader().discover(tests_path) - # Add doc-test manually - suite.addTest(doctest.DocTestSuite(utils)) - suite.addTest(doctest.DocTestSuite(_commands)) - return suite - diff --git a/pymagglobal/tests/test__commands.py b/pymagglobal/tests/test__commands.py deleted file mode 100644 index 2882242f334f2286036754bacb63192a19d7a0c6..0000000000000000000000000000000000000000 --- a/pymagglobal/tests/test__commands.py +++ /dev/null @@ -1,31 +0,0 @@ -import unittest -import io -import sys - -from matplotlib import pyplot as plt -from pymagglobal import models -from test_argument_parser import _SetUpCls - - -class Commands(_SetUpCls): - - @unittest.skip('not yet implemented') - def test_usage_ex_master(self): - from pymagglobal.__main__ import mst_ex_ofn, mst_ex_cmd - nsp = self.parser.parse_args(mst_ex_cmd.split()[1:]) - #nsp.func(nsp) - #plt.savefig('./docs/' + mst_ex_ofn) - - @unittest.skip('not yet implemented') - def test_usage_ex_dipole(self): - from pymagglobal.__main__ import dip_ex_ofn, dip_ex_cmd - nsp = self.parser.parse_args(dip_ex_cmd.split()[1:]) - #nsp.func(nsp) - #plt.savefig('./docs/' + mst_ex_ofn) - - @unittest.skip('not yet implemented') - def test_usage_ex_map(self): - from pymagglobal.__main__ import map_ex_ofn, map_ex_cmd - nsp = self.parser.parse_args(map_ex_cmd.split()[1:]) - #nsp.func(nsp) - #plt.savefig('./docs/' + mst_ex_ofn) diff --git a/setup.py b/setup.py index 2f06e9bb3345610f14ca8bfafaf9e940702240d8..4046eca4b993ab3b905001bc3be46ed2b610493e 100644 --- a/setup.py +++ b/setup.py @@ -105,5 +105,4 @@ setup( f'pymagglobal.__main__:main']}, cmdclass=cmdclass, command_options=command_options, - test_suite='pymagglobal.tests.test_suite', ) diff --git a/tests/run_tests.py b/tests/run_tests.py new file mode 100644 index 0000000000000000000000000000000000000000..72849e718ab126aff593f90c37a1ea71df2e538b --- /dev/null +++ b/tests/run_tests.py @@ -0,0 +1,28 @@ +import sys +from pathlib import PurePath + +import unittest +import doctest + +import matplotlib +matplotlib.use('Agg') # Select a headless backend + +from pymagglobal import utils +from pymagglobal import _commands + +# Fetch relative path +tests_path = PurePath(__file__).parent +# By convention the TestLoader discovers TestCases in test_*.py +suite = unittest.TestLoader().discover(tests_path) + +# Add all doc-tests manually +suite.addTest(doctest.DocTestSuite(utils)) +suite.addTest(doctest.DocTestSuite(_commands)) + +if __name__ == '__main__': + # Set up a test-runner + runner = unittest.TextTestRunner(verbosity=2) + # Collect test results + result = runner.run(suite) + # If not successful return 1 + sys.exit(not result.wasSuccessful()) diff --git a/pymagglobal/tests/test_argument_parser.py b/tests/test_argument_parser.py similarity index 68% rename from pymagglobal/tests/test_argument_parser.py rename to tests/test_argument_parser.py index 6cf855198bac75b2949afe5a7968a11f271f320c..687dc293a0939c72b72789f3fd07874ac9c1afa3 100644 --- a/pymagglobal/tests/test_argument_parser.py +++ b/tests/test_argument_parser.py @@ -35,6 +35,7 @@ class ParserTestGlobal(_SetUpCls): '''Check if `--list-models` terminates successfully''' with self.assertRaises(SystemExit) as cm: # Redirect stdout not to pollute output the terminal + # FIXME: stdout remains redirected and suppresses potential output sys.stdout = io.StringIO() # Pass list since sys.argv splits at white space (filename # stripped) @@ -46,12 +47,6 @@ class ParserTestGlobal(_SetUpCls): class ParserTestMaster(_SetUpCls): '''Test-cases from the `master` sub-command''' - def test_master_valid(self): - '''Check if a syntactically proper string is parsed without error''' - # TODO: Grab that command from 'example of use' - cmd = 'pymagglobal master --no-show 12 12 gufm1' - nsp = self.parser.parse_args(cmd.split()[1:]) - def test_master_exclusive_res_every(self): '''Raise if both, --res and --every are specified''' cmd = 'pymagglobal master --no-show --res=20 --every=12 12 12 gufm1' @@ -69,23 +64,42 @@ class ParserTestMaster(_SetUpCls): def test_command_run(self): ''' Check if the example runs flawlessly ''' - from pymagglobal.__main__ import mst_ex_cmd as cmd + from pymagglobal.__main__ import mst_ex_cmd as cmd, mst_ex_ofn as ofn nsp = self.parser.parse_args(cmd.split()[1:]) - nsp.func(nsp) + fig = nsp.func(nsp) + # TODO: use --no-show together with --savefig + fig.savefig(f'./docs/{ofn}', bbox_inches='tight') + class ParserTestMap(_SetUpCls): '''Test-cases from the `map` sub-command''' def test_custom_model(self): - cmd = 'map 1500 ./pymagglobal/dat/arhimag1k' + '''Check whether a file-name passed is treated as a custom model''' + from pymagglobal import models + cmd = 'map 1500 ' + models['arhimag1k'] nsp = self.parser.parse_args(cmd.split()) self.assertEqual(nsp.model.name, 'custom model') + def test_custom_model_raises(self): + '''Check if a wrong file-name throws FileNotFoundError''' + cmd = 'map 1500 ./non_existent_file' + self.assertRaises(FileNotFoundError, + self.parser.parse_args, cmd.split()) + def test_usage_valid(self): '''Check if the example of us is parsed without error''' from pymagglobal.__main__ import map_ex_cmd as cmd nsp = self.parser.parse_args(cmd.split()[1:]) + def test_command_run(self): + ''' Check if the example runs flawlessly ''' + from pymagglobal.__main__ import map_ex_cmd as cmd, map_ex_ofn as ofn + nsp = self.parser.parse_args(cmd.split()[1:]) + fig = nsp.func(nsp) + # TODO: use --no-show together with --savefig + fig.savefig(f'./docs/{ofn}', bbox_inches='tight') + class ParserTestDipole(_SetUpCls): '''Test-cases from the `dipole` sub-command''' @@ -95,7 +109,15 @@ class ParserTestDipole(_SetUpCls): from pymagglobal.__main__ import dip_ex_cmd as cmd nsp = self.parser.parse_args(cmd.split()[1:]) - def test_longerm(self): + def test_command_run(self): + ''' Check if the example runs flawlessly ''' + from pymagglobal.__main__ import dip_ex_cmd as cmd, dip_ex_ofn as ofn + nsp = self.parser.parse_args(cmd.split()[1:]) + fig = nsp.func(nsp) + # TODO: use --no-show together with --savefig + fig.savefig(f'./docs/{ofn}', bbox_inches='tight') + + def test_longterm(self): cmd = 'dipole --longterm LSMOD.2' nsp = self.parser.parse_args(cmd.split()) self.assertEqual(nsp.t_unit, 'ka') @@ -116,6 +138,15 @@ class ParserTestCoefficientsEpoch(_SetUpCls): from pymagglobal.__main__ import cfe_ex_cmd as cmd nsp = self.parser.parse_args(cmd.split()[1:]) + def test_command_run(self): + ''' Check if the example runs flawlessly ''' + from pymagglobal.__main__ import cfe_ex_cmd as cmd, cfe_ex_ofn as ofn + nsp = self.parser.parse_args(cmd.split()[1:]) + fig = nsp.func(nsp) + # TODO: use --no-show together with --savefig + fig.savefig(f'./docs/{ofn}', bbox_inches='tight') + + class ParserTestCoefficientSeries(_SetUpCls): '''Test-cases from the `coeff-series` sub-command''' @@ -126,6 +157,8 @@ class ParserTestCoefficientSeries(_SetUpCls): def test_command_run(self): ''' Check if the example runs flawlessly ''' - from pymagglobal.__main__ import cfs_ex_cmd as cmd + from pymagglobal.__main__ import cfs_ex_cmd as cmd, cfs_ex_ofn as ofn nsp = self.parser.parse_args(cmd.split()[1:]) - nsp.func(nsp) + fig = nsp.func(nsp) + # TODO: use --no-show together with --savefig + fig.savefig(f'./docs/{ofn}', bbox_inches='tight') diff --git a/pymagglobal/tests/test_pymagglobal.py b/tests/test_pymagglobal.py similarity index 92% rename from pymagglobal/tests/test_pymagglobal.py rename to tests/test_pymagglobal.py index 72974fa9b7b4acbe1b4ec48b00f539b157d15eba..aa7f5223bb7e7c3fdfb5c6d25896b984820b8bb1 100644 --- a/pymagglobal/tests/test_pymagglobal.py +++ b/tests/test_pymagglobal.py @@ -8,6 +8,7 @@ import pymagglobal # Not a test-case since `import *` is only allowed at module level from pymagglobal import * + class ParserTestPackage(unittest.TestCase): '''Unit-tests for the package i.e. __init__.py''' @@ -17,6 +18,6 @@ class ParserTestPackage(unittest.TestCase): def test_models(self): '''Check if built-in model-files exist''' - for model_fn in models.values(): + for model_fn in pymagglobal.models.values(): with self.subTest(msg=model_fn): self.assertTrue(Path(model_fn).is_file())